/* Social Cut — dithered-paper identity (canonical brand tokens, shared with
   socialcut.io / apps/socialcut-site/src/app/globals.css — keep in sync).
   The app is a cutting table: paper ground, ink print, one hot accent,
   1-bit dithered gear cutouts, boiling paper tooth. Panels are liquid
   glass — frosted paper lifted off the table.
   Anton (poster display) · Schibsted Grotesk (body) · Azeret Mono (readout).
   Glass + motion researched 2026-06; WKWebView (pywebview) + Chrome safe. */

:root {
  /* --- paper palette (paper surfaces + ink print + hot accent) --- */
  --bg: oklch(97.4% 0.0055 92);      /* paper — the table (ambient base) */
  --bg2: oklch(99.5% 0.002 92);      /* paper-2 — raised plane / matte panels */
  --bg3: oklch(94.5% 0.007 90);      /* paper-3 — inputs / hover */
  --ink: oklch(18.5% 0.011 45);      /* ink text */
  --muted: oklch(45.5% 0.014 60);    /* muted — mono labels, secondary */
  --faint: oklch(60% 0.013 64);      /* decorative dim only, never body copy */
  --line: oklch(85.5% 0.011 86);     /* hairline borders */
  --line2: oklch(78% 0.012 80);      /* hairline, lifted (hover/active) */
  --ember: oklch(66% 0.1855 45);     /* THE accent — primary action, progress, active */
  --ember-deep: oklch(56% 0.175 44); /* pressed / small accent TEXT (4.5:1 on paper) */
  --ember-bright: oklch(74% 0.178 50);/* hover-hot fill — never small text on paper */
  --accent:var(--ember);
  --accent-ink:var(--ink);           /* text on ember = ink */
  --ok:#4C6B3C;          /* moss — success / armed (text-safe on paper) */
  --ok-fill:#A9C29C;     /* sage — armed chip fill (ink text on top) */
  --err:#A8432A;         /* fired clay — errors / dirty (text-safe on paper) */

  /* --- liquid glass (frosted paper lifted off the table) --- */
  --glass-fill:color-mix(in srgb, var(--bg2) 42%, transparent);  /* milk tint over the table */
  --glass-floor:rgba(253,252,249,0.50);                          /* contrast safety floor under text */
  --glass-rim:color-mix(in srgb, var(--ink) 13%, transparent);   /* hairline outer rim */
  --glass-top:rgba(255,255,255,0.85);                            /* bright top bevel */

  --r-sm:3px; --r-md:4px; --r-lg:6px;  /* tape-label rectangles — never pills */

  --display:'Anton',Impact,'Arial Narrow',sans-serif;
  --sans:'Schibsted Grotesk',system-ui,sans-serif;
  --mono:'Azeret Mono','Courier New',monospace;

  /* --- motion tokens (M3 + ease-out-expo; researched) --- */
  --ease-standard:cubic-bezier(0.4,0,0.2,1);
  --ease-out:cubic-bezier(0,0,0.2,1);
  --ease-out-expo:cubic-bezier(0.16,1,0.3,1);
  --ease-emph:cubic-bezier(0.2,0,0,1);
  --dur-micro:140ms; --dur-standard:240ms; --dur-expressive:560ms; --dur-reveal:700ms;
  --stagger:60ms;

  color-scheme: light;
  accent-color: var(--ember);   /* native checkboxes/sliders glow amber */
}

* { box-sizing:border-box; }
html { -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility; }
body {
  margin:0; background:var(--bg); color:var(--ink);
  font-family:var(--sans); font-weight:400; font-size:15px; line-height:1.55; letter-spacing:.005em;
  min-height:100vh;
}
::selection { background:var(--ember); color:var(--accent-ink); }
@view-transition { navigation: auto; }   /* cross-document step transitions (enhancement) */

/* --- ambient table: a slow warm accent wash + boiling paper tooth --- */
.ambient { position:fixed; inset:0; z-index:-2; pointer-events:none; background:var(--bg); }
.ambient::before {                                   /* the warm light, off top-left */
  content:""; position:absolute; inset:-15%;
  background:radial-gradient(46% 42% at 16% 10%,
    color-mix(in srgb, var(--ember) 9%, transparent), color-mix(in srgb, var(--ember) 3%, transparent) 30%, transparent 60%);
  animation:expose 40s var(--ease-standard) infinite alternate;
  will-change:transform;
}
@keyframes expose { to { transform:translate3d(40px,28px,0) scale(1.08); } }
body::after {                                        /* paper tooth — multiplies over the page, boils like the site */
  content:""; position:fixed; inset:-140px; z-index:60; pointer-events:none;
  opacity:.16; mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.95' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  animation:tooth 6s steps(5) infinite;
}
@keyframes tooth {
  0%{transform:translate3d(0,0,0)} 20%{transform:translate3d(-11px,7px,0)}
  40%{transform:translate3d(9px,-10px,0)} 60%{transform:translate3d(-7px,12px,0)}
  80%{transform:translate3d(12px,4px,0)} 100%{transform:translate3d(0,0,0)}
}

/* --- liquid glass: frosted paper panes lifted off the table (chrome only, never media) --- */
.glass {
  position:relative;
  background:linear-gradient(var(--glass-floor),var(--glass-floor)), var(--glass-fill);
  -webkit-backdrop-filter:blur(20px) saturate(170%);
          backdrop-filter:blur(20px) saturate(170%);
  border-radius:var(--r-lg);
  box-shadow:
    inset 0 1px 0 0 var(--glass-top),            /* bright top bevel (light above) */
    inset 0 -1px 0 0 rgba(38,32,27,0.08),        /* faint lower refraction line */
    0 0 0 .5px var(--glass-rim),                 /* retina-crisp outer rim */
    0 18px 44px -18px rgba(38,32,27,0.22);       /* soft ink elevation */
  contain:paint;
}
/* chromium sheen — a faint diagonal spectral banding over every glass pane,
   like light raking brushed chrome: cool silver bands with blue/green/magenta
   whispers, soft-light so it reads as reflection, never as a color cast */
.glass::before, .chip-check::before, .steps::before {
  content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background:linear-gradient(115deg,
    rgba(255,255,255,0) 0%, rgba(255,255,255,.30) 12%,
    oklch(0.86 0.03 250 / .16) 26%, rgba(255,255,255,0) 40%,
    oklch(0.88 0.025 150 / .13) 58%, rgba(255,255,255,.22) 72%,
    oklch(0.86 0.03 330 / .13) 86%, rgba(255,255,255,0) 100%);
  mix-blend-mode:soft-light;
}
.steps { position:relative; }
.glass::after {                                  /* per-pane grain to dissolve blur banding */
  content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  opacity:.04; mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:120px 120px;
}
/* engine without backdrop-filter, or user asked to reduce transparency → solid, still readable */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))) {
  .glass { background:color-mix(in srgb, var(--bg2) 96%, transparent); }
}
@media (prefers-reduced-transparency: reduce) {
  .glass, .topbar { -webkit-backdrop-filter:none; backdrop-filter:none; background:color-mix(in srgb, var(--bg2) 97%, transparent); }
}
/* raised glass for the sticky preview/picker rail — deeper blur reads as hovering above */
.glass--raised { -webkit-backdrop-filter:blur(26px) saturate(180%); backdrop-filter:blur(26px) saturate(180%);
  box-shadow:inset 0 1px 0 0 var(--glass-top), inset 0 -1px 0 0 rgba(38,32,27,0.10),
    0 0 0 .5px var(--glass-rim), 0 26px 56px -20px rgba(38,32,27,0.28); }
/* screen-reader-only utility (visually hidden, announced) */
.sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0,0,0,0); white-space:nowrap; border:0; }

/* keyboard focus visible everywhere (ember ring — matches site) */
:focus-visible { outline:2px solid var(--ember); outline-offset:3px; border-radius:2px; }
.filepick:focus-within { outline:2px solid var(--ember); outline-offset:3px; }

/* --- type roles (Anton is single-weight 400 — no stretch, positive tracking) --- */
.display, h1, h2 {
  font-family:var(--display); font-weight:400;
  text-transform:uppercase; line-height:.96; letter-spacing:.008em; margin:0;
}
h3 { font-family:var(--display); font-weight:400; text-transform:uppercase;
     letter-spacing:.012em; font-size:15px; margin:0 0 10px; }
.mono, .kicker, .tag, .meta, .label, .snip, .mono * { font-family:var(--mono); }
.kicker, .tag, .step-n, .label {
  font-family:var(--mono); text-transform:uppercase; letter-spacing:.2em;
  font-size:11px; font-weight:400; color:var(--muted);
}
/* Edge codes (site Reveal.tsx/globals.css): page-top kickers are the dim
   variant (.edge-code-dim — faint, never ember); .burn is the ember accent
   word inside a display heading (site hero "CONTENT OUT.") */
/* --faint is decorative dim (see token block) and lands at 3.0:1 on --bg. The
   kicker carries section identity and is a real heading on the project list,
   so it takes --muted (7.0:1) instead. */
.kicker { color:var(--muted); letter-spacing:.12em; font-size:12px; }
.burn { color:var(--ember); }
time, .tnum, .meta td, .focal-opt, .timecode { font-variant-numeric:tabular-nums; }

/* --- top bar (decluttered: wordmark left, live state right) --- */
.topbar {
  display:flex; justify-content:space-between; align-items:center;
  padding:18px 40px; border-bottom:1px solid var(--line);
  position:sticky; top:0; z-index:20;
  background:linear-gradient(color-mix(in srgb, var(--bg) 66%, transparent),color-mix(in srgb, var(--bg) 66%, transparent));
  -webkit-backdrop-filter:blur(14px) saturate(140%); backdrop-filter:blur(14px) saturate(140%);
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))) {
  .topbar { background:color-mix(in srgb, var(--bg) 97%, transparent); }
}
/* The wordmark is the document h1 (base.html) — this strips the heading's own
   size and spacing so the bar looks exactly as it did. */
.logo-h1 { margin:0; font-size:inherit; line-height:1; letter-spacing:normal; }
.logo {
  display:inline-flex; align-items:center; gap:.3em;
  font-family:var(--display); font-weight:400; text-transform:uppercase;
  font-size:22px; line-height:1; letter-spacing:.02em; color:var(--ink); text-decoration:none;
  padding-block:5px;   /* clears the 24px minimum target height */
}
/* Cue mark — the blinking record dot (site Wordmark) */
.logo .cue { display:inline-block; width:.34em; height:.34em; border-radius:50%;
  background:var(--ember); animation:blink 1.9s var(--ease-out-expo) infinite; }
@keyframes blink { 0%,44%{opacity:1} 56%,100%{opacity:.22} }
.topside { display:flex; align-items:center; gap:18px; }
.topmeta { font-family:var(--mono);
  text-transform:uppercase; letter-spacing:.16em; font-size:10px; color:var(--muted); }
.topnav { display:flex; gap:14px; }
.topnav a { font-family:var(--mono); text-transform:uppercase; letter-spacing:.14em;
  font-size:10px; color:var(--muted); text-decoration:none; padding-block:7px; }
.topnav a:hover { color:var(--ink); }
.logoutform { display:inline; margin:0; }
.logout-btn { background:transparent; color:var(--muted); border:1px solid var(--line);
  box-shadow:none; font-size:10px; padding:7px 13px; letter-spacing:.14em; }
.logout-btn:hover { background:transparent; color:var(--ink); border-color:var(--line2);
  transform:none; }

main { max-width:1080px; margin:0 auto; padding:42px 40px 96px; }

/* --- film-strip progress rail (seated in a smoked-glass track) --- */
/* align-items:center, not the flex default `stretch`: the deliverable picker is a
   sibling of the step chips, so an OPEN <details> would otherwise stretch every
   chip (the active one included) to the panel's full height. */
.steps { display:flex; align-items:center; gap:2px; margin-bottom:40px; padding:5px;
  border-radius:var(--r-lg);
  width:fit-content; background:linear-gradient(rgba(253,252,249,.55),rgba(253,252,249,.55)), var(--glass-fill);
  box-shadow:inset 0 1px 0 0 var(--glass-top), 0 0 0 .5px var(--glass-rim),
    0 10px 26px -14px rgba(38,32,27,0.25);
  -webkit-backdrop-filter:blur(14px) saturate(160%); backdrop-filter:blur(14px) saturate(160%); }
.step {
  display:flex; align-items:center; gap:8px; padding:8px 16px; border-radius:3px;
  text-decoration:none; color:var(--muted);
  transition:color var(--dur-micro) var(--ease-standard), background-color var(--dur-micro) var(--ease-standard);
}
.step:hover { color:var(--ink); }
/* instant click acknowledgement while the boosted fetch is in flight */
.step.htmx-request { opacity:.55; transition:opacity 120ms var(--ease-standard); }
.step-n { font-family:var(--mono); font-size:11px; letter-spacing:.1em; opacity:.7; }
.step-t { font-family:var(--mono); text-transform:uppercase; letter-spacing:.14em; font-size:11px; }
.step.active { color:var(--accent-ink); background:var(--ember); }
.step.active .step-n { color:var(--accent-ink); opacity:.85; }
.step.done { color:var(--ink); }
.step.done .step-n { opacity:1; }
.step.done .step-n::after { content:" ✓"; color:var(--ember-deep); }
/* collapse non-active labels on narrow widths — film-strip stays compact */
@media (max-width:720px){ .step:not(.active) .step-t { display:none; } }
/* Even collapsed to bare numbers, 5 tabs + dividers still edges past 375px
   (measured ~385px). width:fit-content means the rail otherwise just grows
   past its container instead of wrapping, so cap it and let the rail scroll
   internally rather than pushing the whole page wider. */
/* Sticky under 700px: the step chips stay reachable while thumb-scrolling a
   long step (step 3's caption cards, step 4's brand kit) instead of getting
   scrolled away above the fold. overflow-x:auto lives on THIS SAME element
   (for the internal horizontal scroll when the chips are wider than the
   phone) — that's harmless for sticky, which only breaks when an ANCESTOR
   sets overflow:hidden/scroll/auto; verified live in a real browser (main/
   body have no overflow rule at this width, so nothing here defeats it).
   z-index:30 clears .topbar's 20 so the chip rail paints in front, on the
   near-solid backing below (90% --bg, still blurred like every other glass
   pane), rather than content ghosting through as it scrolls up behind it.

   .topbar itself drops out of `position:sticky` at this width (see the rule
   right below): .topbar wraps to a taller 2-line wordmark under ~375px and
   its height isn't fixed, so a second independently-sticky bar sharing the
   same top:0 slot left a sliver of it poking out below `.steps` on real
   devices (verified live) — whatever height didn't fit under `.steps`'s own
   shorter box just kept rendering there, uncovered. One sticky top slot on
   a phone is also simply the right amount of permanently-reserved real
   estate; the step chip already carries "which step, which title", so the
   wordmark bar scrolling away with the rest of the page loses nothing a
   phone user needs mid-step. */
@media (max-width:700px){
  .topbar { position:static; }
  .steps {
    max-width:100%; overflow-x:auto; -webkit-overflow-scrolling:touch;
    position:sticky; top:0; z-index:30;
    background:linear-gradient(color-mix(in srgb, var(--bg) 90%, transparent),color-mix(in srgb, var(--bg) 90%, transparent)), var(--glass-fill);
    -webkit-backdrop-filter:blur(14px) saturate(140%); backdrop-filter:blur(14px) saturate(140%);
  }
}

/* --- step body: the active card sits on smoked glass over the wall --- */
.step-body { padding:30px 32px; }
.step-body.glass { padding:30px 32px; }
.step-head { font-size:clamp(30px,4.4vw,50px); letter-spacing:.008em; margin-bottom:20px;
  overflow-wrap:anywhere; }

/* --- headings + body + states --- */
h2 { font-size:clamp(34px,5vw,58px); margin-bottom:18px; }
.sub { color:var(--muted); max-width:62ch; font-size:14px; }
.kicker { display:block; margin-bottom:18px; }
.ok { color:var(--ok); } .err { color:var(--err); } .stale { color:var(--ember-deep); }
.stub { color:var(--muted); font-style:normal; font-family:var(--mono); font-size:12px; letter-spacing:.04em; }

/* --- buttons: amber primary, ghost secondary, crisp micro-interactions --- */
/* Actions never touch. `.next` usually follows a form or a list and needs its own
   air; side-by-side actions get a gap. Deliberately NOT on `button, .next` itself —
   the flex rows (.export-actions, .kit-save, .photo-row, .pc-nav) already set `gap`,
   and a baked-in margin would double up there. */
.next { margin-top:20px; }
button + button, button + .next, .next + button { margin-left:10px; }
button, .next {
  font-family:var(--mono); text-transform:uppercase; letter-spacing:.14em; font-size:12px;
  font-weight:700; background:var(--ember); color:var(--accent-ink); border:1px solid transparent;
  border-radius:3px; padding:11px 22px; cursor:pointer; text-decoration:none; display:inline-block;
  /* flat tape label (site .btn-burn) — no glow, hover goes white-hot */
  transition:transform var(--dur-micro) var(--ease-standard),
             background-color var(--dur-micro) var(--ease-standard);
}
button:hover, .next:hover { transform:translateY(-1px); background:var(--ember-bright); }
button:active, .next:active { transform:scale(.97); }
/* drawn forward-arrow for buttons/links. Never a font glyph: the mono face
   may lack arrow glyphs, so a text arrow renders from a mismatched fallback. */
.arr { display:inline-block; width:.85em; height:.85em; margin-left:.5em; vertical-align:-.08em;
  background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M1.5 8h12M8.5 2.5 14 8l-5.5 5.5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='square'/%3E%3C/svg%3E") center/contain no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M1.5 8h12M8.5 2.5 14 8l-5.5 5.5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='square'/%3E%3C/svg%3E") center/contain no-repeat; }
.arr-l { transform:rotate(180deg); margin:0 .5em 0 0; }
.btn-ghost, .modes a, .brandchip {
  background:transparent; color:var(--ink); border:1px solid var(--line); box-shadow:none;
}
.btn-ghost:hover { border-color:var(--line2); box-shadow:none; }

/* --- drop / intake (glass enlarger frame; warms on drag) --- */
.drop { padding-top:8px; }
.drop h2 { font-size:clamp(52px,8.5vw,104px); margin-bottom:24px; }
.dropform { display:flex; gap:14px; align-items:center; margin-top:30px; flex-wrap:wrap; }
.filepick { position:relative; display:inline-flex; }
.filepick input[type=file] { position:absolute; inset:0; opacity:0; cursor:pointer; }
.filepick-cta {
  font-family:var(--mono); text-transform:uppercase; letter-spacing:.14em; font-size:12px;
  color:var(--muted); border:1px dashed var(--line2); border-radius:3px; padding:11px 22px;
  transition:color var(--dur-micro) var(--ease-standard), border-color var(--dur-micro) var(--ease-standard);
}
.filepick:hover .filepick-cta { color:var(--ink); border-color:var(--ember); }
/* liquid-glass intake (drag-to-warm pane — no dashed frame, the glass IS the
   affordance; the drag state is an accent ring over the pane's own shadows) */
.dropform { flex-direction:column; align-items:flex-start; gap:16px; }
.intake { position:relative; display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:9px; text-align:center; padding:48px 56px; border-radius:var(--r-lg); cursor:pointer;
  min-width:min(440px,80vw);
  transition:box-shadow var(--dur-standard) var(--ease-out-expo), transform var(--dur-standard) var(--ease-out-expo); }
.intake:hover { transform:translateY(-1px);
  box-shadow:inset 0 1px 0 0 var(--glass-top), inset 0 -1px 0 0 rgba(38,32,27,0.08),
    0 0 0 .5px var(--glass-rim), 0 22px 48px -18px rgba(38,32,27,0.28); }
.intake input[type=file] { position:absolute; inset:0; opacity:0; cursor:pointer; }
.intake-icon { font-size:30px; color:var(--ember); line-height:1; }
.intake-cta { font-family:var(--mono); text-transform:uppercase; letter-spacing:.14em; font-size:12px; color:var(--ink);
  overflow-wrap:anywhere; }
.intake-hint { font-family:var(--mono); font-size:10px; letter-spacing:.12em; color:var(--muted); }
.intake.is-dragging { transform:scale(1.015);
  box-shadow:inset 0 1px 0 0 var(--glass-top),
    0 0 0 1.5px color-mix(in srgb, var(--ember) 60%, transparent),
    0 22px 48px -18px rgba(38,32,27,0.3); }
body.dragging .ambient::before { background:radial-gradient(58% 52% at 50% 32%,
  color-mix(in srgb, var(--ember) 22%, transparent), color-mix(in srgb, var(--ember) 7%, transparent) 42%, transparent 72%); }

/* --- meta readout (one line + optional table) --- */
.meta-line { font-family:var(--mono); font-size:12px; color:var(--muted); letter-spacing:.06em;
  font-variant-numeric:tabular-nums; margin:6px 0 18px; }
.meta-line b { color:var(--ink); font-weight:700; }
.meta { border-collapse:collapse; margin:12px 0 18px; font-family:var(--mono); font-size:12px; }
.meta th { text-align:left; color:var(--muted); text-transform:uppercase; letter-spacing:.14em;
  padding:5px 26px 5px 0; font-weight:400; }
.meta td { color:var(--ink); padding:5px 0; }
details > summary { cursor:pointer; }

/* --- mode tabs + brand chips --- */
.modes { display:flex; gap:8px; margin-bottom:22px; }
.modes a, .brandchip {
  font-family:var(--mono); text-transform:uppercase; letter-spacing:.12em; font-size:11px;
  padding:8px 15px; border:1px solid var(--line); border-radius:3px; color:var(--muted);
  text-decoration:none; transition:color var(--dur-micro) var(--ease-standard), border-color var(--dur-micro) var(--ease-standard);
}
.modes a:hover, .brandchip:hover { color:var(--ink); border-color:var(--line2); }
.modes a.active, .brandchip.active { color:var(--ember-deep); border-color:var(--ember); }

/* --- lists / segments --- */
.cards-list { list-style:none; padding:0; display:flex; flex-direction:column; gap:8px; }
.cards-list .snip { color:var(--muted); font-size:12px; margin-top:4px; font-family:var(--mono); }
/* Transcript segments are buttons, but they are the only prose in the product —
   the user's own words, read to find a moment. The button role above sets
   uppercase/12px/700/.14em tracking, which has to be undone here or the one
   screen whose whole job is reading becomes the hardest thing to read. */
.seg { text-align:left; width:100%; background:var(--bg2); border:1px solid var(--line);
  border-radius:var(--r-md); padding:12px 14px; color:var(--ink); cursor:pointer; font-family:var(--sans);
  text-transform:none; letter-spacing:normal; font-size:15px; font-weight:400; line-height:1.55;
  transition:border-color var(--dur-micro) var(--ease-standard), transform var(--dur-micro) var(--ease-standard); }
.seg:hover { border-color:var(--line2); transform:translateX(2px); }
.manual input { background:var(--bg2); border:1px solid var(--line); color:var(--ink);
  border-radius:var(--r-sm); padding:8px 10px; margin-right:6px; font-family:var(--mono); font-size:12px;
  font-variant-numeric:tabular-nums; }
.manual input.invalid { border-color:var(--err); box-shadow:0 0 0 2px color-mix(in srgb,var(--err) 30%,transparent); }
/* Seek is a sibling of the label now, not a child of it. */
.sugg-row { display:flex; align-items:flex-start; gap:10px; }
.sugg-row > label { flex:1; min-width:0; }
.cards-list li:has(.seg) { display:flex; align-items:center; gap:10px; }
.cards-list li:has(.seg) .seg { flex:1; }
/* Only icon-only control in the app: sized to clear the WCAG 2.2 AA 24x24
   target floor rather than the 31x23 it used to be. */
.seek { font-size:12px; padding:6px 12px; min-width:32px; min-height:28px; border-radius:3px;
  background:var(--bg3); color:var(--ink); border:1px solid var(--line2); margin-left:8px;
  transition:background-color var(--dur-micro) var(--ease-standard); }
.seek:hover { background:var(--ember); color:var(--accent-ink); border-color:var(--ember); }

/* armed-state chip (sage) + one-shot ring pulse confirming in/out */
.armed { font-family:var(--mono); text-transform:uppercase; letter-spacing:.1em; font-size:11px;
  color:#1d241b; background:var(--ok-fill); border-radius:3px; display:inline-block; padding:6px 14px;
  margin-bottom:12px; animation:ring-pulse 1.1s var(--ease-out) 1; }
.armed[hidden] { display:none; }
.armed button { font-size:10px; padding:1px 8px; margin-left:6px; background:transparent;
  border:1px solid #1d241b; color:#1d241b; border-radius:3px; box-shadow:none; }
.seg.armed-row { outline:2px solid var(--ok); outline-offset:2px; }
@keyframes ring-pulse { 0%{box-shadow:0 0 0 0 rgba(169,194,156,.55);} 100%{box-shadow:0 0 0 12px rgba(169,194,156,0);} }

/* Advisory notice: the run did not fail, but something the user is paying for
   (captions) cannot happen. Terracotta rail rather than a full error card — err
   colour to catch the eye, body copy to explain, no alarm styling. */
.notice { border:1px solid color-mix(in srgb,var(--err) 40%,transparent);
  background:color-mix(in srgb,var(--err) 6%,transparent);
  padding:12px 16px; border-radius:var(--r-md); margin:0 0 18px;
  max-width:62ch; line-height:1.5; color:var(--ink); }
.notice strong { color:var(--err); }

/* --- step 2: picking with eyes on (glass-mounted player + list) --- */
.picker-layout { display:grid; grid-template-columns:minmax(260px,380px) 1fr; gap:28px; align-items:start; }
@media (max-width:900px){ .picker-layout { grid-template-columns:1fr; } }
.picker-video { position:sticky; top:84px; padding:10px; border-radius:var(--r-lg); }
.picker-video video { width:100%; max-height:60vh; border-radius:var(--r-md); background:#000;
  display:block; box-shadow:0 0 0 1px rgba(0,0,0,.6), 0 10px 30px -12px #000; }
.picker-video.armed-glint { animation:glint .6s var(--ease-out) 1; }
@keyframes glint { 0%,100%{box-shadow:inset 0 1px 0 var(--glass-top),0 0 0 .5px var(--glass-rim);}
  40%{box-shadow:inset 0 1px 0 var(--glass-top),0 0 0 1.5px color-mix(in srgb, var(--ember) 60%, transparent),0 0 22px -4px var(--ember);} }
.vid-hint { margin-top:10px; font-family:var(--mono); text-transform:uppercase; letter-spacing:.1em;
  font-size:10px; color:var(--muted); }

/* --- wizard two-column: step content + persistent deliverable preview rail --- */
.wizard-grid { display:grid; grid-template-columns:1fr minmax(280px,360px); gap:24px; align-items:start; }
@media (max-width:980px){ .wizard-grid { grid-template-columns:1fr; } }
/* Grid items default to min-width:auto (their own min-content size), so a
   fixed-width descendant (the brand-kit swatch row, the caption-panel swatch
   row) can force this whole column wider than its track — even the single 1fr
   track under 980px won't shrink below that. min-width:0 lets the track's own
   size win, same fix as the flex min-width:0 pattern below. At >=980px the
   track is already wide enough that nothing here needs to shrink, so this is a
   no-op at desktop widths. */
.step-body, .preview { min-width:0; }
.preview { position:sticky; top:84px; padding:16px; display:flex; flex-direction:column; gap:10px; }
@media (max-width:980px){ .preview { position:static; } }
.preview-label { font-family:var(--mono); text-transform:uppercase; letter-spacing:.2em; font-size:10px; color:var(--muted); margin-bottom:8px; }
.preview-media { width:100%; max-height:58vh; border-radius:var(--r-md); background:#000; display:block;
  box-shadow:0 0 0 1px rgba(0,0,0,.6), 0 10px 30px -14px #000; }
/* slides seated like prints on the light table — a graphite mat so white art doesn't float */
.preview-slide { width:100%; border-radius:var(--r-sm); display:block; margin-top:2px;
  box-shadow:0 0 0 1px rgba(0,0,0,.4), inset 0 0 0 1px rgba(0,0,0,.25), 0 8px 20px -10px #000; }
.preview-cap { font-family:var(--mono); text-transform:uppercase; letter-spacing:.12em; font-size:10px; color:var(--muted); text-align:center; margin-top:8px; }
.preview-cap.teal { color:var(--ember-deep); }
.preview-empty { font-family:var(--mono); font-size:11px; color:var(--muted); text-align:center; line-height:1.6;
  padding:44px 14px; border:1px dashed var(--line2); border-radius:var(--r-md); }
.preview.armed-glint { animation:glint .6s var(--ease-out) 1; }

/* --- variant previews: footage matte on true black; 9:16 = teal hero; develops in --- */
.variants { display:flex; flex-wrap:wrap; gap:14px; align-items:flex-start; }
.variants figure { margin:0; }
.variants img { height:230px; background:#000; border:1px solid var(--line);
  border-radius:var(--r-md); display:block; }
.variants figcaption { color:var(--muted); font-family:var(--mono); font-size:11px;
  text-transform:uppercase; letter-spacing:.12em; margin-top:7px; text-align:center; }
.variants figure.hero figcaption { color:var(--ember-deep); }
.variants figure.hero figcaption::after { content:""; display:block; height:2px; width:34px; margin:5px auto 0;
  background:var(--ember); border-radius:2px; }
.clip { border-top:1px solid var(--line); padding-top:18px; margin-top:20px; }
/* carousel slide tiles seated in a graphite mat (white art mustn't float on the wall) */
.slide-grid img { background:var(--bg2);
  box-shadow:0 0 0 1px rgba(0,0,0,.4), inset 0 0 0 1px rgba(0,0,0,.25), 0 8px 22px -12px #000; }
/* the develop reveal — clip resolves in from an overexposed, unfixed print */
.variants video { animation:develop var(--dur-reveal) var(--ease-emph) both; }
.variants figure:nth-child(2) video { animation-delay:120ms; }
.variants figure:nth-child(3) video { animation-delay:240ms; }
.variants figure:nth-child(4) video { animation-delay:360ms; }
.variants figure:nth-child(5) video { animation-delay:480ms; }
@keyframes develop {
  0%   { opacity:0; filter:brightness(1.7) contrast(.55) saturate(.4); transform:translateY(8px) scale(.99); }
  55%  { opacity:1; }
  100% { opacity:1; filter:none; transform:none; }
}

/* --- step 3 render settings (glass disclosure) --- */
.render-settings { padding:6px 18px 18px; margin:14px 0; max-width:660px; border-radius:var(--r-lg); }
.render-settings > summary { cursor:pointer; font-family:var(--mono); text-transform:uppercase;
  letter-spacing:.14em; font-size:11px; color:var(--ink); padding:10px 0; }
.render-summary-line { color:var(--muted); font-family:var(--mono); font-size:11px; letter-spacing:.04em; }
.render-settings fieldset { border:1px solid var(--line); border-radius:var(--r-md); margin:12px 0; padding:10px 14px; }
.render-settings legend { font-family:var(--mono); text-transform:uppercase; letter-spacing:.12em;
  font-size:10px; color:var(--muted); padding:0 6px; }
.aspects { display:flex; flex-direction:column; gap:2px; }
.aspect-opt { display:flex; align-items:baseline; gap:8px; padding:6px 0; font-size:14px; }
.aspect-opt strong { font-family:var(--mono); min-width:54px; }
.render-row { display:flex; gap:18px; flex-wrap:wrap; margin:12px 0; }
.render-row label { display:flex; flex-direction:column; gap:5px; font-family:var(--mono);
  text-transform:uppercase; letter-spacing:.1em; font-size:10px; color:var(--muted); }
.render-row input, .render-row select { width:180px; }
.focals { display:flex; flex-direction:column; gap:8px; }
.focal-opt { display:flex; align-items:center; gap:12px; font-family:var(--mono); font-size:12px; }
.focal-opt input[type=range] { flex:1; max-width:300px; accent-color:var(--ember); }
.style-confirm { margin-left:12px; font-family:var(--mono); font-size:12px; color:var(--ink); }
.style-confirm button { padding:5px 14px; font-size:10px; margin-left:6px; }
.render-actions { display:flex; align-items:center; gap:14px; flex-wrap:wrap; margin-top:6px; }
.render-summary-line { color:var(--muted); }
.stylebar { margin:14px 0; }
.newstyle { margin:20px 0; border:1px solid var(--line); border-radius:var(--r-lg); padding:10px 14px; }
.newstyle summary { cursor:pointer; color:var(--muted); font-family:var(--mono);
  text-transform:uppercase; letter-spacing:.12em; font-size:11px; }
.newstyle form { display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-top:12px; }
/* Labelled kit fields: each control keeps its name above it, so the name is
   still there after you type (a placeholder is not an accessible name). */
.kitform { align-items:flex-end !important; gap:14px !important; }
.kitform .field { display:flex; flex-direction:column; gap:5px; margin:0; }
.kitform .field label { font-family:var(--mono); text-transform:uppercase;
  letter-spacing:.12em; font-size:10px; color:var(--muted); }
.kitform .field input[type=color] { padding:2px; width:52px; height:32px; cursor:pointer; }

/* --- forms --- */
select, textarea, input[type=number], input[type=text], .newstyle input {
  background:var(--bg2); border:1px solid var(--line); color:var(--ink); border-radius:var(--r-sm);
  padding:9px 11px; font-family:var(--mono); font-size:12px;
  transition:border-color var(--dur-micro) var(--ease-standard); }
select:hover, textarea:hover, input:hover { border-color:var(--line2); }

/* --- carousel brand card (matte — keeps focus on the slides) --- */
.brandbar { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin:16px 0 12px; }
.brandcard { background:var(--bg2); border:1px solid var(--line); border-radius:var(--r-lg);
  padding:14px 16px; margin-bottom:20px; }
.swatches { display:flex; gap:6px; margin-bottom:10px; }
.swatches .sw { width:28px; height:28px; border-radius:var(--r-sm); border:1px solid color-mix(in srgb, var(--ink) 14%, transparent); }
.brandmeta strong { margin-right:8px; font-family:var(--display); font-weight:400; text-transform:uppercase; }
.brandprompt { margin-top:12px; }
.brandprompt summary { cursor:pointer; color:var(--muted); font-family:var(--mono); font-size:11px;
  text-transform:uppercase; letter-spacing:.12em; }
.brandprompt pre { white-space:pre-wrap; background:var(--bg); border:1px solid var(--line);
  border-radius:var(--r-sm); padding:12px; font-family:var(--mono); font-size:12px; color:var(--ink);
  margin-top:10px; line-height:1.5; }
.fgrender { display:flex; align-items:center; gap:14px; margin:14px 0; flex-wrap:wrap; }
.fgrender .sub { margin:0; }

/* --- carousel slides + per-slide copy editor + dirty guard --- */
.slides { list-style:none; padding:0; display:flex; flex-direction:column; gap:10px; margin:18px 0; }
.slides li { border:1px solid var(--line); border-radius:var(--r-md); padding:13px 15px; background:var(--bg2); }
.slides .label { display:inline-block; font-family:var(--mono); text-transform:uppercase; letter-spacing:.14em;
  font-size:10px; color:var(--accent-ink); background:var(--ember); padding:2px 8px; border-radius:var(--r-sm); margin-right:8px; }
.slides .snip { color:var(--muted); font-size:13px; margin-top:6px; }
.slides-edit li { display:flex; flex-direction:column; gap:8px; }
.slides-edit label { display:flex; flex-direction:column; gap:4px; font-family:var(--mono);
  text-transform:uppercase; letter-spacing:.12em; font-size:10px; color:var(--muted); }
.slides-edit input, .slides-edit textarea { background:var(--bg); border:1px solid var(--line); color:var(--ink);
  border-radius:var(--r-sm); padding:9px 11px; font-family:var(--sans); font-size:14px; letter-spacing:normal; text-transform:none; }
.slides-edit .label { align-self:flex-start; }
.dirty-chip { font-family:var(--mono); letter-spacing:.12em; font-size:10px; color:var(--accent-ink);
  background:var(--err); border-radius:3px; padding:3px 10px; margin-left:8px; vertical-align:middle; }

/* --- step 5 export (export-only; editable post copy) --- */
.bundle-group { border:1px solid var(--line); border-radius:var(--r-md); padding:10px 14px; margin:8px 0; max-width:560px; }
.bundle-group summary { cursor:pointer; font-family:var(--mono); text-transform:uppercase; letter-spacing:.12em; font-size:11px; color:var(--ink); }
.bundle-group ul { margin-top:10px; font-family:var(--mono); font-size:11px; color:var(--muted); }
.postcopy { display:flex; flex-direction:column; gap:8px; max-width:600px; margin:6px 0 18px; }
.postcopy label { font-family:var(--mono); text-transform:uppercase; letter-spacing:.12em; font-size:10px; color:var(--muted); }
.postcopy textarea { font-family:var(--sans); font-size:14px; min-height:90px; }

/* --- live job status (glass; amber spinner) --- */
.jobstatus { display:flex; flex-direction:column; align-items:center; text-align:center; gap:14px;
  padding:60px 28px; border-radius:var(--r-lg); max-width:560px; margin:8px auto; }
.jobstatus .spinner { width:34px; height:34px; border-radius:50%;
  border:3px solid color-mix(in srgb,var(--ember) 22%,transparent); border-top-color:var(--ember);
  animation:spin .8s linear infinite; }
.jobstatus .joblabel { font-size:18px; color:var(--ink); margin:0; }
.jobstatus .jobhint { font-family:var(--mono); text-transform:uppercase; letter-spacing:.12em;
  font-size:11px; color:var(--muted); margin:0; }
.jobstatus.err { box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--err) 45%,transparent), 0 18px 44px -18px rgba(38,32,27,.22); }
.jobstatus.err .joblabel { color:var(--err); }
/* Stopping was the user's own decision, so it reads as a neutral resting state —
   never the terracotta alarm reserved for something actually going wrong. */
.jobstatus.stopped { box-shadow:inset 0 0 0 1px var(--line2), 0 18px 44px -18px rgba(38,32,27,.22); }
.jobstatus.stopped .joblabel { color:var(--ink); }
.jobstatus .jobelapsed { font-family:var(--mono); font-size:11px; letter-spacing:.1em;
  color:var(--muted); margin:0; }
.jobstatus .joberr-detail { max-width:100%; }
.jobstatus .joberr-detail summary { font-family:var(--mono); text-transform:uppercase;
  letter-spacing:.12em; font-size:10px; color:var(--muted); cursor:pointer; }
.jobstatus .joberr { font-family:var(--mono); font-size:12px; color:var(--muted); background:var(--bg);
  border:1px solid var(--line); border-radius:var(--r-sm); padding:10px 12px; max-width:100%;
  margin-top:10px; overflow:auto; white-space:pre-wrap; text-align:left; }

/* Cancel lives on the persistent shell, outside the swapped region, so keyboard
   focus survives the 1.5s poll. It hides itself once the job reaches a terminal
   state rather than being destroyed and rebuilt. */
.quota-actions { display:flex; flex-wrap:wrap; gap:10px; align-items:center; margin-top:22px; }
/* Final-screen actions — were the last two inline styles in the app, with no
   wrapping behaviour of their own at narrow widths. */
.export-actions { display:flex; flex-wrap:wrap; gap:10px; align-items:center; margin-top:6px; }
.export-form { margin-top:18px; }
.jobshell { display:flex; flex-direction:column; align-items:center; }
.jobcancel { margin:-6px 0 8px; }
.jobshell:has(#jobbody [data-job-done]) .jobcancel { display:none; }
.cancel-job { font-size:10px; padding:6px 14px; background:transparent; color:var(--ink);
  border:1px solid var(--line2); box-shadow:none; }
@keyframes spin { to { transform:rotate(360deg); } }

/* --- home resume list --- */
.resume { margin-top:46px; }
.projlist { list-style:none; padding:0; margin:14px 0 0; display:flex; flex-direction:column; gap:8px; max-width:560px; }
.projlist a { display:flex; justify-content:space-between; align-items:baseline; gap:14px;
  border:1px solid var(--line); border-radius:var(--r-md); padding:12px 15px; background:var(--bg2);
  text-decoration:none; transition:border-color var(--dur-micro) var(--ease-standard), transform var(--dur-micro) var(--ease-standard); }
.projlist a:hover { border-color:var(--line2); transform:translateX(2px); }
.projlist .label { color:var(--muted); font-size:11px; }

hr { border:none; border-top:1px solid var(--line); margin:30px 0; }
a { color:var(--ink); }
code { font-family:var(--mono); font-size:.92em; color:var(--ember-deep); }

/* --- state utils --- */
button[aria-busy="true"], .next[aria-busy="true"] { opacity:.55; pointer-events:none; cursor:progress; }
button:disabled, button.disabled { opacity:.45; cursor:not-allowed; box-shadow:none; }

/* --- step entrance + cross-document view transitions --- */
.step-body, .drop { animation:rise var(--dur-expressive) var(--ease-out-expo) both; }
@keyframes rise { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:none; } }
::view-transition-old(root){ animation:vt-out var(--dur-standard) var(--ease-standard) both; }
::view-transition-new(root){ animation:vt-in var(--dur-expressive) var(--ease-out-expo) both; }
@keyframes vt-out { to { opacity:0; } }
@keyframes vt-in  { from { opacity:0; } }

/* --- liquid step transitions: the chrome MORPHS between wizard pages ------
   Cross-document view transitions are already on (@view-transition at top).
   Naming the persistent chrome gives each piece its own transition group:
   the browser animates position+size from the old page to the new, so the
   active chip glides along the rail, and the glass step pane and preview
   rail flow into place instead of blinking with the root fade. Names must
   be unique per page — each selector below matches exactly one element.
   Enhancement only: engines without VT just navigate; reduced motion skips
   the whole transition (pageswap handler in base.html + the block below). */
/* Scoped to the rail + chip on purpose: the big glass panes are LIVE during
   a transition, so morphing them re-rasterizes their backdrop blur every
   frame — that read as chop, not liquid. The panes crossfade with the root
   instead (their geometry barely changes between steps anyway). */
.topbar { view-transition-name: topbar; }
.steps { view-transition-name: steps-rail; }
.step.active { view-transition-name: step-active; }
::view-transition-group(*) {
  animation-duration: 480ms;
  animation-timing-function: var(--ease-out-expo);
}
/* the always-boiling layers repaint large regions on every tick (the tooth
   is a full-viewport multiply blend; the cutouts are pixelated sprites) —
   a tick landing mid-morph visibly drops frames, so they hold still until
   the transition finishes */
html:active-view-transition body::after,
html:active-view-transition .cutout,
html:active-view-transition .cut,
html:active-view-transition .ambient::before,
html:active-view-transition .logo .cue { animation-play-state: paused !important; }

/* --- editing surfaces: nothing auto-made is beyond reach --- */
/* step 2: in/out points editable in place on the Selected list */
.range-row { display:flex; align-items:center; gap:6px; margin:4px 0; }
.range-edit { width:76px; background:var(--bg2); border:1px solid var(--line); color:var(--ink);
  border-radius:var(--r-sm); padding:6px 8px; font-family:var(--mono); font-size:12px;
  font-variant-numeric:tabular-nums; }
.range-edit.invalid { border-color:var(--err); box-shadow:0 0 0 2px color-mix(in srgb,var(--err) 30%,transparent); }

/* step 3: caption-text editor, anchored to the on-screen CARD (B5) — the words
   that get burned, edited as the phrase group the viewer sees, not the raw
   transcript segment underneath. */
.cap-rows-block { margin:14px 0 0; }
.cap-rows-head { font-family:var(--mono); text-transform:uppercase; letter-spacing:.14em;
  font-size:11px; color:var(--muted); margin:0 0 8px; }
.cap-rows { display:flex; flex-direction:column; gap:8px; }
.cap-row { display:flex; align-items:center; gap:10px; }
.cap-row-time { flex:0 0 auto; background:var(--bg2); border:1px solid var(--line);
  color:var(--muted); border-radius:var(--r-sm); padding:7px 10px; font-family:var(--mono);
  font-size:11px; font-variant-numeric:tabular-nums; cursor:pointer; }
.cap-row-time:hover { color:var(--ink); border-color:var(--line2); }
.cap-row-text { flex:1 1 auto; background:var(--bg); border:1px solid var(--line); color:var(--ink);
  border-radius:var(--r-sm); padding:9px 11px; font-family:var(--sans); font-size:14px;
  width:100%; }
.cap-rows-save { display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin:18px 0; }
.cap-rows-status { font-family:var(--mono); font-size:11px; color:var(--muted); }

/* step 4: per-slide photo swap */
.slide-photos { margin:16px 0; }
.photo-row { display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  border:1px solid var(--line); border-radius:var(--r-md); padding:10px 13px;
  background:var(--bg2); margin:8px 0; }
.photo-row .label { font-family:var(--mono); text-transform:uppercase; letter-spacing:.12em;
  font-size:10px; color:var(--muted); flex:1 1 100%; }
.photo-row input[type="file"] { font-size:12px; color:var(--muted); font-family:var(--mono); }
.photo-thumb { width:64px; height:80px; object-fit:cover; border-radius:var(--r-sm);
  border:1px solid var(--line2); }

/* --- dithered cutout: one piece of gear per entry screen --------------------
   Each PNG is a 3-frame boil sprite (300% wide, from the site); the steps
   animation flips frames so the dither "boils". Fixed behind the content,
   bleeding off an edge like the site's reels. Per-instance image/size/position
   come inline from the template. */
.cutout { position:fixed; z-index:-1; pointer-events:none;
  background-size:300% 100%; background-repeat:no-repeat; image-rendering:pixelated;
  animation:boil .42s steps(1) infinite; }
@keyframes boil {
  0%,33.32%{background-position-x:0%} 33.33%,66.65%{background-position-x:50%}
  66.66%,100%{background-position-x:100%}
}
@media (max-width:900px){ .cutout { opacity:.3; } }  /* never fights content on a phone */

/* --- selection motion: state changes get a quick, springy settle ------------
   Product budget: <=240ms, ease-out-expo, state-conveying only. Selection
   surfaces are step 2's segment list, step 3's caption/aspect pickers,
   step 4's slides, the deliverable checkboxes, and the home resume list. */
.seg, .projlist a {
  transition:border-color var(--dur-micro) var(--ease-standard),
             transform var(--dur-standard) var(--ease-out-expo),
             box-shadow var(--dur-standard) var(--ease-out-expo);
}
.seg:hover, .projlist a:hover { border-color:var(--line2); transform:translateX(3px);
  box-shadow:0 6px 18px -10px rgba(38,32,27,.28); }
.seg:active { transform:translateX(3px) scale(.995); }
.cap-cell { transition:border-color var(--dur-micro) var(--ease-standard),
  background-color var(--dur-micro) var(--ease-standard),
  transform var(--dur-standard) var(--ease-out-expo); }
.cap-cell:has(input:checked) { transform:scale(1.08); }
.dlv-choose label { transition:color var(--dur-micro) var(--ease-standard); }
.dlv-choose label:has(input:checked) { color:var(--ink); }
.dlv-choose label:has(input:not(:checked)) { color:var(--muted); }
.variants figure { transition:transform var(--dur-standard) var(--ease-out-expo); }
.variants figure:hover { transform:translateY(-3px); }
.slides li, .photo-row { transition:border-color var(--dur-micro) var(--ease-standard),
  box-shadow var(--dur-standard) var(--ease-out-expo); }
.slides li:hover, .photo-row:hover { border-color:var(--line2);
  box-shadow:0 6px 18px -12px rgba(38,32,27,.25); }

/* --- reduced motion: keep fades, kill all movement + the develop sweep --- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration:.001ms !important; animation-iteration-count:1 !important;
    transition-duration:.001ms !important; }
  .ambient::before { animation:none !important; }
  body::after, .logo .cue { animation:none !important; }
  .cap-cell:has(input:checked) { transform:none !important; }
  .variants figure:hover { transform:none !important; }
  .variants video { animation:none !important; opacity:1 !important; filter:none !important; transform:none !important; }
  .step-body, .drop { animation:none !important; opacity:1 !important; transform:none !important; }
  .armed { animation:none !important; }
  .seg:hover, .projlist a:hover { transform:none !important; }
  .jobstatus .spinner { animation:none !important; }
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*){ animation:none !important; }
}

/* --- brand kit editor ------------------------------------------------------ */
.brandkit { margin: 18px 0 24px; }
.kit-palette, .kit-type { border: 0; margin: 14px 0; padding: 0; }
.kit-swatch { display: flex; align-items: center; gap: 10px; margin: 6px 0; }
.kit-swatch > label:first-child { min-width: 92px; font-family: var(--mono);
  text-transform: uppercase; letter-spacing: .12em; font-size: 11px; }
.kit-swatch input[type=color] { width: 44px; height: 30px; padding: 0; border: 0;
  background: none; cursor: pointer; }
.kit-swatch .hexin { width: 92px; font-family: var(--mono); font-size: 12px;
  text-transform: uppercase; }
.kit-lock { display: inline-flex; align-items: center; gap: 5px; font-size: 12px;
  color: var(--muted); }
.kit-contrast { font-variant-numeric: tabular-nums; min-width: 130px; }
.kit-contrast.warn { color: #E2231A; }
/* advisory, not an error: a low accent/secondary ratio is often the design */
.kit-contrast.soft { color: var(--muted); font-style: italic; }
.kit-actions { display: flex; align-items: center; gap: 14px; margin-top: 12px; }
.kit-mode { font-size: 12px; color: var(--muted); }
/* padding/font-size are clamp()ed rather than fixed: at desktop viewports the
   preferred branch (vw) always exceeds the max bound, so clamp resolves to the
   same 34px/42px as before (no-op there) — only under ~700px does the vw
   branch fall inside the clamp range and actually shrink. */
.kit-specimen { margin: 18px 0; padding: clamp(16px, 5vw, 34px); max-width: 100%;
  border-radius: 10px; transition: background .18s ease, color .18s ease; }
.spec-kick { text-transform: uppercase; letter-spacing: .30em; font-size: 12px;
  font-weight: 600; }
.spec-display { font-size: clamp(24px, 7vw, 42px); line-height: 1.02; margin: 14px 0 18px; }
.spec-rule { height: 1px; opacity: .6; margin: 0 0 16px; }
.spec-body { font-size: 15px; line-height: 1.45; max-width: 52ch; }
.spec-mono { font-size: 13px; letter-spacing: .04em; margin-top: 14px; }
.kit-save { display: flex; gap: 10px; align-items: center; }
.kit-delete { margin-top: 12px; }
.kit-delete .danger { background: none; border: 1px solid #E2231A; color: #E2231A; }

/* Phone containment: the brand-kit swatch row (label + colour + hex + lock +
   contrast readout, unwrapped) is the specific ~450px-wide root cause of the
   step-4 overflow the mobile audit measured. .capkit's own swatch row already
   wraps unconditionally (fewer cells, no lock/contrast); this brings the
   wider brand-kit row in line under the phone breakpoint without touching it
   above 700px, where the unwrapped row is the intended desktop layout. */
@media (max-width:700px) {
  .kit-swatch { flex-wrap: wrap; }
  .kit-swatch .kit-contrast { flex: 1 1 100%; }
  .kitform select, .kitform input { max-width: 100%; }
}

/* --- deliverable rail: the carousel as a 4:5 deck, not a cover still -------- */
.preview-carousel { margin-top:4px; }
.preview-carousel:focus-visible { outline:2px solid var(--ember); outline-offset:3px; }
/* 4:5 is the Instagram portrait frame the slides are rendered at (1080x1350) */
.pc-frame { position:relative; width:100%; aspect-ratio:4 / 5; background:var(--bg2);
  border-radius:var(--r-sm); overflow:hidden;
  box-shadow:0 0 0 1px rgba(0,0,0,.4), inset 0 0 0 1px rgba(0,0,0,.25), 0 8px 20px -10px #000; }
.pc-slide { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block; }
.pc-nav { display:flex; align-items:center; justify-content:center; gap:12px; margin-top:8px; }
.pc-nav button { padding:3px 12px; font-size:15px; line-height:1.1; margin:0; }
.pc-count { font-family:var(--mono); font-size:10px; letter-spacing:.14em; color:var(--muted);
  font-variant-numeric:tabular-nums; min-width:54px; text-align:center; }

/* --- live caption preview stage (Spec B) ------------------------------------ */
/* container-type:size makes cqh/cqw resolve against the video box, so the same
   geometry numbers hold at any preview size and any aspect — BUT size containment
   means this box can no longer take its size FROM its content (the video): it
   needs an explicit, content-independent size of its own, or it (and everything
   sized in cqh/cqw inside it) collapses to 0. height is explicit; width comes
   from aspect-ratio (also explicit, not content-derived) keyed off data-aspect. */
.cap-stage { container-type: size; position: relative; display: inline-block;
  height: 230px; background: #000; border: 1px solid var(--line); border-radius: var(--r-md);
  overflow: hidden; }
.variants figure.hero .cap-stage { height: 368px;
  border-color: color-mix(in srgb, var(--ember) 55%, var(--line));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--ember) 35%, transparent), 0 10px 26px -12px color-mix(in srgb, var(--ember) 45%, transparent); }
.cap-stage[data-aspect="9:16"] { aspect-ratio: 9 / 16; }
.cap-stage[data-aspect="4:5"] { aspect-ratio: 4 / 5; }
.cap-stage[data-aspect="1:1"] { aspect-ratio: 1 / 1; }
.cap-stage[data-aspect="16:9"] { aspect-ratio: 16 / 9; }
.cap-stage[data-aspect="1.91:1"] { aspect-ratio: 1.91 / 1; }
.cap-stage video { display: block; width: 100%; height: 100%; object-fit: cover;
  animation: develop var(--dur-reveal) var(--ease-emph) both; }
.caption-overlay { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }

/* "Burn a proof" — the real burner on demand, vs. the CSS-approximate overlay above */
.cap-proof { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 6px; }
.cap-proof-btn { font-size: 11px; padding: 4px 10px; }
.cap-proof-note { font-family: var(--mono); font-size: 10px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ember-deep); }
.cap-proof-back { font-family: var(--mono); font-size: 10px; letter-spacing: .04em;
  color: var(--muted); text-decoration: underline; }

/* the caption card itself — geometry (position/size/colour) is set inline from
   the server payload by captionpreview.js; these rules are purely structural */
/* word gap in em, not cqw: the burner separates words with real space glyphs,
   which scale with the FONT (~0.25em in most sans faces), not the container —
   at 0.35cqw the gap was ~1px at any stage size and words visibly ran together
   once D-3 gave the stage real estate. */
.cap-line { position: absolute; display: flex; flex-wrap: wrap; gap: 0.25em;
  line-height: 1.25; margin: 0; }
.cap-line[data-align-h="center"] { justify-content: center; text-align: center; }
.cap-line[data-align-h="left"] { justify-content: flex-start; text-align: left; }
.cap-line[data-align-h="right"] { justify-content: flex-end; text-align: right; }
.cap-word { display: inline-block; }
/* pill highlight: text takes the OUTLINE colour, the pill itself the highlight
   colour — mirrors build_ass's pill branch (\1c = outline_color, \3c = highlight
   as the pill fill), so the preview and the burn agree on which colour goes where */
.cap-word.cap-pill { border-radius: 0.35em; padding: 0.04em 0.32em; }

/* --- caption style editor (Spec B control panel) ----------------------------- */
.capkit { margin: 18px 0 24px; }
.capkit .kit-swatch { flex-wrap: wrap; }
.cap-check { display: inline-flex; align-items: center; gap: 6px; font-family: var(--mono);
  text-transform: uppercase; letter-spacing: .1em; font-size: 11px; color: var(--muted);
  margin: 10px 16px 0 0; }
.cap-highlight, .cap-position, .cap-weight { border: 0; margin: 14px 0; padding: 0; }
.cap-highlight legend, .cap-position legend, .cap-weight legend { font-family: var(--mono);
  text-transform: uppercase; letter-spacing: .12em; font-size: 10px; color: var(--muted);
  padding: 0 0 6px; }
.cap-radio { display: inline-flex; align-items: center; gap: 6px; font-family: var(--mono);
  font-size: 12px; margin: 0 16px 6px 0; }
.cap-grid { display: grid; grid-template-columns: repeat(3, 32px); grid-template-rows: repeat(3, 32px);
  gap: 6px; margin: 4px 0 14px; }
.cap-cell { display: block; width: 32px; height: 32px; border: 1px solid var(--line);
  border-radius: var(--r-sm); cursor: pointer; position: relative; background: var(--bg2); }
.cap-cell input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.cap-cell:has(input:checked) { border-color: var(--ember); background: color-mix(in srgb, var(--ember) 20%, transparent); }
.cap-cell:has(input:focus-visible) { outline: 2px solid var(--ember); outline-offset: 2px; }
.cap-weight .field { display: inline-flex; flex-direction: column; gap: 5px; margin-right: 18px; }
.cap-timing { margin-top: 14px; border: 1px solid var(--line); border-radius: var(--r-md); padding: 8px 14px 14px; }
.cap-timing summary { cursor: pointer; font-family: var(--mono); text-transform: uppercase;
  letter-spacing: .12em; font-size: 10px; color: var(--muted); padding: 6px 0; }
.cap-timing .field { margin: 10px 0; max-width: 260px; }

/* --- coarse-pointer touch targets (Spec D, D-2) ------------------------------
   Every checkbox/radio/lock/colour-input reaches a >=44x44px hit area on a
   touch screen, without growing anything a mouse/trackpad session sees: the
   whole block is gated on (pointer: coarse), and the label fixes use a
   `::before` with a negative `inset` rather than padding+negative-margin —
   the pseudo-element overflows the label's own box to enlarge only the
   CLICKABLE region (a label forwards clicks from any descendant, including an
   absolutely-positioned one that paints outside its own border box), so it
   never fights the label's existing margin/spacing rules the way stacking
   padding and a compensating negative margin on the same box would. */
@media (pointer: coarse) {
  input[type=checkbox], input[type=radio] { min-width: 28px; min-height: 28px; }

  .kit-lock, .cap-check, .cap-radio { position: relative; }
  .kit-lock::before, .cap-check::before, .cap-radio::before {
    content: ""; position: absolute; inset: -14px;
  }

  /* .aspect-opt rows sit in a tight 2px-gap column (.aspects) — an invisible
     ::before overflow there would overlap the NEXT row's real content, so
     this grows the row's own (real) padding instead of borrowing space from
     a neighbour. */
  .aspect-opt { padding: 9px 0; }

  /* the 3x3 alignment grid: cells resize for real (they're small solid
     buttons, not inline labels next to running text) rather than via an
     invisible overflow. */
  .cap-grid { grid-template-columns: repeat(3, 44px); grid-template-rows: repeat(3, 44px); }
  .cap-cell { width: 44px; height: 44px; }

  .kit-swatch input[type=color] { min-height: 44px; }
  .hexin { min-height: 40px; }
  .focal-opt input[type=range] { min-height: 44px; }
}

/* --- Spec D, D-3: the hero caption stage earns real estate on a phone -------
   Under 700px the 9:16 hero sizes from WIDTH (min(86vw,420px)) with an
   aspect-ratio, not the desktop fixed height:368px set above — the B4 lesson
   applies again here: container-type:size needs BOTH dimensions determinate,
   and width + aspect-ratio supplies exactly that (height resolves from the
   ratio, so it's still content-independent — cqh/cqw keep resolving for the
   caption overlay's inline geometry, same as at any other stage size). The
   hero video already fills its stage via the unconditional
   `.cap-stage video { width:100%; height:100%; object-fit:cover }` rule
   above — nothing extra needed here. Non-hero variants become a horizontal
   snap-scroll strip so they stop competing with the hero for row width;
   `.variants` itself is the scroll container (its scrollWidth may exceed the
   viewport), never the page.

   The hero must be FULLY visible at scroll position 0 — a caption preview you
   have to swipe to finish seeing defeats the point of D-3. Desktop's 40px+32px
   horizontal padding (main + .step-body) leaves only ~231px of column on a
   375px phone, so both shrink here, and the hero sizes from the COLUMN
   (min(100%, 420px)) rather than the viewport — it can never overhang its
   container, and the other variants peek in from the right edge as the swipe
   affordance. */
@media (max-width: 700px) {
  main { padding-left: 16px; padding-right: 16px; }
  .step-body, .step-body.glass { padding: 22px 14px; }
  .variants { flex-wrap: nowrap; overflow-x: auto; scroll-snap-type: x mandatory; }
  .variants figure { scroll-snap-align: start; flex: none; }
  /* the figure carries the explicit width (a flex:none item never sizes from
     a width:100% child — that resolution is circular); the stage just fills it */
  .variants figure.hero { width: 100%; max-width: 420px; }
  .variants figure.hero .cap-stage { width: 100%; height: auto; aspect-ratio: 9 / 16; }
}

/* --- Spec D, D-4: the deliverable rail becomes a bottom sheet on a phone ----
   Under 980px `.preview` docks fixed to the viewport bottom. Collapsed, it's
   just the 56px `.sheet-handle` (max-height:56px + overflow:hidden clips the
   rest); `.open` (toggled by sheet.js) expands it to a scrollable 75vh sheet
   showing the SAME rail markup/ids — slideshow.js and the preview video need
   no changes. `.sheet-handle` stays hidden above 980px unconditionally (the
   bare rule below, unguarded by any media query) so desktop never sees it. */
.sheet-handle { display: none; }
@media (max-width: 980px) {
  body { padding-bottom: 64px; }
  .preview {
    position: fixed; top: auto; left: 0; right: 0; bottom: 0; z-index: 40;
    max-height: 56px; overflow: hidden; padding: 0;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
  }
  .preview.open { max-height: 75vh; overflow-y: auto; }
  /* .preview is display:flex/flex-direction:column (base rule, unconditional) —
     without flex:none the handle's own height:56px is just a flex-basis that
     shrinks to fit alongside .sheet-body's much taller natural content, so it
     visibly crushes to a sliver. flex:none pins it at its real size; the body
     is what's allowed to shrink/scroll instead. Sticky keeps the handle
     reachable (to collapse again) even once the sheet is open and scrolled. */
  .sheet-handle {
    display: flex; flex: none; position: sticky; top: 0; z-index: 1;
    align-items: center; justify-content: center; gap: 8px;
    width: 100%; height: 56px; padding: 0 16px; margin: 0;
    background: var(--bg2); border: 0;
    color: var(--ink); font-family: var(--mono); text-transform: uppercase;
    letter-spacing: .12em; font-size: 11px; cursor: pointer;
  }
  .sheet-chevron { display: inline-block; transition: transform var(--dur-micro) var(--ease-standard); }
  .preview.open .sheet-chevron { transform: rotate(180deg); }
  .sheet-body { flex: 1 1 auto; min-height: 0; padding: 0 16px 16px; }
}

/* --- Spec D, D-5: the one action that drives the step forward earns the
   thumb's full reach on a phone ------------------------------------------
   `.btn-primary` marks ONLY the wizard's single forward-driving action per
   step — Re-render (step 3), Save caption text (step 3), Render carousel
   (step 4), Bundle for export/Re-bundle (step 5) — not every button. A
   step's secondary/inline controls (Burn a proof, Save as new kit, Add
   range, the palette generator, seek buttons, per-slide photo swap) keep
   their natural inline size; several of those sit right next to a save
   button they must not visually outrank. Scoped to `.step-body` so it can
   never touch anything in the deliverable rail (`.pc-nav` buttons etc). */
@media (max-width: 700px) {
  .step-body .btn-primary { display: block; width: 100%; }
}

/* --- step 1 deliverable chips: liquid-glass toggles ------------------------
   The "what do you want out of it?" question, asked once the video is in.
   Each chip is a glass tile wrapping a real checkbox (native control kept);
   checked chips take the accent rim and full-ink title. */
.dlv-pick { margin: 26px 0 8px; }
.dlv-chips { border: 0; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 10px; }
.dlv-chips legend { font-family: var(--mono); text-transform: uppercase;
  letter-spacing: .16em; font-size: 11px; color: var(--muted); margin-bottom: 12px; }
.chip-check { display: grid; grid-template-columns: auto 1fr; column-gap: 11px;
  align-items: center; padding: 13px 18px 13px 15px; border-radius: var(--r-lg);
  cursor: pointer; position: relative;
  background: linear-gradient(var(--glass-floor), var(--glass-floor)), var(--glass-fill);
  -webkit-backdrop-filter: blur(14px) saturate(160%); backdrop-filter: blur(14px) saturate(160%);
  box-shadow: inset 0 1px 0 0 var(--glass-top), 0 0 0 .5px var(--glass-rim),
    0 8px 22px -14px rgba(38,32,27,.22);
  transition: transform var(--dur-standard) var(--ease-out-expo),
              box-shadow var(--dur-standard) var(--ease-out-expo); }
.chip-check:hover { transform: translateY(-1px); }
.chip-check input { grid-row: 1 / span 2; margin: 0; }
.chip-check .t { font-weight: 600; font-size: 14px; color: var(--muted); line-height: 1.3;
  transition: color var(--dur-micro) var(--ease-standard); }
.chip-check .d { grid-column: 2; font-family: var(--mono); font-size: 10px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--muted-2); margin-top: 2px; }
.chip-check:has(input:checked) {
  box-shadow: inset 0 1px 0 0 var(--glass-top),
    0 0 0 1.5px color-mix(in srgb, var(--ember) 55%, transparent),
    0 10px 26px -14px rgba(38,32,27,.3); }
.chip-check:has(input:checked) .t { color: var(--ink); }
.chip-check:has(input:focus-visible) { outline: 2px solid var(--ember); outline-offset: 3px; }
.chip-check:active { transform: scale(.985); }
/* the author `button{display:inline-block}` beats the UA [hidden] rule —
   same lesson as .armed[hidden] above */
.dlv-save { margin-top: 12px; }
.dlv-save[hidden] { display: none; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .chip-check { background: color-mix(in srgb, var(--bg2) 96%, transparent); }
}

/* Deliverable picker (drop form + wizard nav) */
/* Explicit even though `.steps` centres its children — the picker must never be
   the item that sets (or is stretched to) the rail's height. */
.dlv { align-self: center; }
.dlv-choose { border: 0; padding: 0; margin: 14px 0 4px; display: flex; flex-wrap: wrap; gap: 10px 18px; }
.dlv-choose legend { margin-bottom: 8px; }
.dlv-choose label, .dlv-form label { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; }
.dlv-form { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; padding: 10px 0 2px; }

.inline-form { display: inline; margin-left: 8px; }
