/* Shotink — dark studio theme, an ink-and-optics palette.

   Three layers, in this order, and nothing skips a layer:
     1. raw palette   ink / amber / verdigris / rust / paper ramps  (hex, no meaning)
     2. channels      --*-rgb, so an alpha tint never re-states the hue
     3. semantic      --bg/--surface/--brand/--on-brand/...         (what you use)

   Rule that is easy to get wrong: amber is a LIGHT colour. Anything filled with
   --brand takes --on-brand (near-black) for its text, never var(--paper) — white on
   amber lands around 2.2:1 and fails WCAG AA outright. */
:root{
  /* ---- 1. raw palette --------------------------------------------- */
  /* Neutral black chrome: no blue cast, no navy tint. */
  --ink-900:#050505; --ink-850:#0A0A0B; --ink-800:#101011;
  --ink-700:#171718; --ink-600:#262628;
  /* Pastel accents stay small and intentional; stage art carries the colour. */
  --amber-300:#E8D8FF; --amber-400:#D8C0FF; --amber-500:#C7A6FF;
  --amber-600:#A984E8; --amber-700:#8063BC;
  --verd-400:#D9FFB0; --verd-500:#C4E996; --verd-600:#8EAF68;
  --rust-400:#FFB9C8; --rust-500:#E995A8; --rust-600:#B9687B;
  --paper:#F4F3EF; --paper-2:#B6B6B8; --paper-dim:#858589;
  /* Deeper than --bg, for wells the studio sits INSIDE: a media stage, a
     scroll trough, the strip behind a filmstrip. It was var(--ink-950) hardcoded in
     seventeen places, which is a token that had not been written down. */
  --ink-950:var(--ink-950);
  /* True black, and true white, both DELIBERATE and both for art surfaces only.
     A mask preview has to be judged against real black or a soft edge reads as
     opaque; a paint canvas is paper, and warm paper would tint the artwork the
     user is matching colours against. Neither belongs in chrome — the rule
     above still holds everywhere else. */
  --void:#000;
  --canvas-paper:#FFF;
  /* THE MIXER NEEDS FOUR LANES AND THE BRAND HAS THREE HUES.
     Picture, voice, effects and music have to be told apart at a glance in a
     26px track, which amber/verdigris/rust cannot do. So the fourth is declared
     rather than smuggled in: these were rose/teal/amber/purple straight out of
     a utility palette, off-system and unnamed. Pulled toward the brand where a
     hue existed — picture takes rust, voice takes verdigris, effects take
     amber — and plum is the one genuinely new colour in the system.
     Channels as well as hex, because every lane pairs a fill tint with it. */
  --lane-pic:var(--rust-400);      --lane-pic-rgb:224,112,90;
  --lane-voice:var(--verd-400);    --lane-voice-rgb:79,194,174;
  --lane-sfx:var(--amber-400);     --lane-sfx-rgb:245,195,104;
  --lane-music:#A98CD8;            --lane-music-rgb:169,140,216;

  /* ---- 2. channels, so alpha tints don't hardcode the hue ---------- */
  --bg-rgb:10,10,12;
  --brand-rgb:199,166,255;
  --accent-rgb:196,233,150;
  --danger-rgb:233,149,168;
  --paper-rgb:244,240,255;

  /* ---- 3. semantic ------------------------------------------------- */
  --bg:var(--ink-900);
  --surface:var(--ink-800);
  --surface-2:var(--ink-700);
  --surface-3:var(--ink-600);
  --line:rgba(var(--paper-rgb),.10);
  --line-strong:rgba(var(--paper-rgb),.18);
  --ink:var(--paper);
  --muted:var(--paper-dim);
  --brand:var(--amber-500);
  --brand-2:var(--amber-600);
  --on-brand:#12151E;
  --accent:var(--verd-500);
  --danger:var(--rust-500);
  --warn:var(--amber-400);
  --info:var(--verd-400);
  /* was a second amber, which now collides with --brand; parchment instead so
     script/link accents still read as a separate thing. */
  --gold:var(--paper-2);

  /* ---- type -------------------------------------------------------- */
  --font-disp:'Newsreader',Georgia,'Times New Roman',serif;
  --font-body:'Manrope',system-ui,sans-serif;
  --font-mono:'DM Mono',ui-monospace,'SF Mono',Menlo,Consolas,monospace;

  /* ---- form -------------------------------------------------------- */
  --radius:12px; --radius-sm:9px; --radius-lg:18px;
  --shadow:0 18px 50px -28px rgba(0,0,0,.8);
  --shadow-sm:0 6px 18px -10px rgba(0,0,0,.7);
  --ring:0 0 0 3px rgba(var(--brand-rgb),.32);
}
*{margin:0;padding:0;box-sizing:border-box}
/* One focus treatment for the whole app. There was none before: keyboard
   users got the UA default, which is invisible on this background. */
:focus-visible{outline:2px solid var(--brand);outline-offset:2px;border-radius:4px}
.btn:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:none;box-shadow:var(--ring)}
body{font-family:var(--font-body);background:var(--bg);color:var(--ink);line-height:1.55}
h1,h2,h3,.disp{font-family:var(--font-disp);font-weight:500;letter-spacing:-.02em;line-height:1.12}
a{color:inherit;text-decoration:none}
img{max-width:100%;display:block}
.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
.muted{color:var(--muted)}
.row{display:flex;gap:12px;align-items:center}
.col{display:flex;flex-direction:column;gap:12px}
.spread{display:flex;justify-content:space-between;align-items:center;gap:12px}
.grow{flex:1}.right{text-align:right}.hide{display:none!important}
[v-cloak]{display:none}

/* buttons */
.btn{display:inline-flex;align-items:center;gap:8px;border:none;cursor:pointer;font-family:inherit;
  font-weight:700;font-size:.88rem;padding:10px 17px;border-radius:11px;
  background:linear-gradient(135deg,var(--amber-400),var(--brand));color:var(--on-brand);transition:.15s}
.btn:hover{filter:brightness(1.08);transform:translateY(-1px)}
.btn.ghost{background:transparent;color:var(--ink);border:1.5px solid var(--line)}
.btn.ghost:hover{background:var(--surface-2);transform:none;filter:none}
.btn.sm{padding:6px 12px;font-size:.78rem;border-radius:9px}
.btn.block{width:100%;justify-content:center}
.btn.danger{background:var(--rust-600);color:var(--paper)}
.btn:disabled{opacity:.45;cursor:not-allowed;transform:none}

/* header */
header{position:sticky;top:0;z-index:40;background:rgba(var(--bg-rgb),.85);backdrop-filter:blur(12px);border-bottom:1px solid var(--line)}
.nav{display:flex;align-items:center;gap:22px;padding:13px 0}
.brand{font-family:var(--font-disp);font-weight:600;color:var(--ink);font-size:1.12rem;margin-right:auto;
  display:flex;flex-direction:row;align-items:center;gap:10px;line-height:1}
.brand>span{display:flex;flex-direction:column}
.brand .mk,.lp-logo .mk{width:30px;height:30px;flex:0 0 auto}
.brand small{font-family:var(--font-body);font-size:.56rem;letter-spacing:.3em;text-transform:uppercase;color:var(--brand);font-weight:800}
.nav a.link{font-weight:600;font-size:.85rem;color:var(--muted);display:inline-flex;align-items:center;gap:6px}
.nav a.link:hover,.nav a.link.active{color:var(--ink)}
.proj-badge{font-size:.74rem;font-weight:700;color:var(--accent);background:rgba(var(--accent-rgb),.12);
  border:1px solid rgba(var(--accent-rgb),.25);padding:5px 10px;border-radius:9px}

/* hero / page heads */
.pagehead{padding:34px 0 10px}
.pagehead h1{font-size:clamp(1.6rem,3.5vw,2.4rem)}
.pill{display:inline-block;background:var(--surface-2);color:var(--brand);font-weight:700;font-size:.7rem;
  letter-spacing:.08em;text-transform:uppercase;padding:6px 12px;border-radius:30px;margin-bottom:14px;border:1px solid var(--line)}

/* cards / tool grid */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:16px}
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;
  display:flex;flex-direction:column;transition:.15s;cursor:pointer}
.card:hover{border-color:var(--brand);transform:translateY(-2px);box-shadow:var(--shadow)}
.card .ic{font-size:1.5rem;width:46px;height:46px;border-radius:11px;display:grid;place-items:center;
  background:linear-gradient(135deg,rgba(var(--brand-rgb),.22),rgba(var(--brand-rgb),.10));color:var(--brand);margin-bottom:10px}
.card .body{padding:16px;display:flex;flex-direction:column;gap:6px;flex:1}
.card h3{font-size:1rem}
.card .desc{font-size:.82rem;color:var(--muted);flex:1}
.kindtag{display:inline-flex;gap:5px;align-items:center;font-size:.66rem;font-weight:800;text-transform:uppercase;
  letter-spacing:.05em;color:var(--muted);margin-top:8px}
.kindtag b{color:var(--accent)}
.chip{display:inline-block;background:var(--surface-2);font-size:.66rem;font-weight:700;padding:3px 9px;border-radius:20px;color:var(--muted);border:1px solid var(--line)}
.chip.tool{color:var(--brand)}.chip.script{color:var(--gold)}

/* section tabs */
.tabs{display:flex;gap:4px;border-bottom:1px solid var(--line);margin:14px 0 22px;flex-wrap:wrap}
.tabs button{background:none;border:none;padding:10px 14px;font-family:inherit;font-weight:700;font-size:.88rem;
  color:var(--muted);cursor:pointer;border-bottom:3px solid transparent}
.tabs button.active{color:var(--ink);border-bottom-color:var(--brand)}

/* panels / forms */
.panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:18px;margin-bottom:16px}
label{display:block;font-size:.76rem;font-weight:700;margin:12px 0 5px;color:var(--muted)}
input,textarea,select{width:100%;padding:10px 12px;border:1.5px solid var(--line);border-radius:10px;font-family:inherit;
  font-size:.9rem;background:var(--surface-2);color:var(--ink)}
input:focus,textarea:focus,select:focus{outline:none;border-color:var(--brand)}
textarea{resize:vertical;min-height:78px}
.note{font-size:.76rem;color:var(--muted)}
.err{color:var(--danger);font-size:.82rem;margin-top:8px}

/* ===== Editor layout: canvas + AI panel + job tray ===== */
.editor{display:grid;grid-template-columns:1fr 340px;gap:0;height:calc(100vh - 56px)}
.canvas{padding:20px;overflow:auto;background:
  radial-gradient(circle at 1px 1px, rgba(255,255,255,.05) 1px, transparent 0) 0 0/22px 22px,var(--bg)}
.sidebar{border-left:1px solid var(--line);background:var(--surface);display:flex;flex-direction:column;overflow:hidden}
.sidebar .scroll{overflow:auto;padding:16px}
.canvas-empty{display:grid;place-items:center;height:100%;text-align:center;color:var(--muted)}
.canvas-empty .big{font-size:2.4rem;margin-bottom:10px;opacity:.5}

/* asset gallery / selection */
.gallery{display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:12px}
.tile{position:relative;border:2px solid var(--line);border-radius:12px;overflow:hidden;cursor:pointer;background:var(--surface)}
.tile.sel{border-color:var(--brand);box-shadow:var(--ring)}
.tile .thumb{aspect-ratio:1/1;background:var(--surface-2) center/cover;display:grid;place-items:center;color:var(--muted)}
.tile .cap{padding:7px 9px;font-size:.72rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tile .src{position:absolute;top:6px;left:6px;font-size:.6rem;font-weight:800;text-transform:uppercase;
  background:rgba(0,0,0,.55);padding:2px 6px;border-radius:6px}
.tile .src.job{color:var(--accent)}
.txt-tile{padding:12px;font-size:.78rem;color:var(--ink);min-height:120px;white-space:pre-wrap;overflow:hidden}

/* AI panel param fields */
.ai-head{padding:14px 16px;border-bottom:1px solid var(--line)}
.ai-head h3{font-size:1rem}
.ai-head .sub{font-size:.76rem;color:var(--muted);margin-top:2px}
.toolpick{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
.toolpick button{font-size:.74rem;font-weight:700;padding:6px 10px;border-radius:9px;border:1px solid var(--line);
  background:var(--surface-2);color:var(--muted);cursor:pointer}
.toolpick button.active{border-color:var(--brand);color:var(--ink)}
.range-row{display:flex;align-items:center;gap:10px}
.range-row input[type=range]{flex:1}
.range-row .val{font-size:.78rem;font-weight:700;min-width:42px;text-align:right;color:var(--ink)}

/* job tray */
.tray{border-top:1px solid var(--line);max-height:42%;display:flex;flex-direction:column}
.tray h4{font-size:.74rem;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);padding:12px 16px 6px}
.tray .scroll{overflow:auto;padding:0 16px 14px}
.job{background:var(--surface-2);border:1px solid var(--line);border-radius:11px;padding:10px 12px;margin-bottom:9px}
.job .spread{margin-bottom:7px}
.job .name{font-size:.82rem;font-weight:700}
.bar{height:6px;background:rgba(255,255,255,.08);border-radius:4px;overflow:hidden}
.bar > i{display:block;height:100%;background:linear-gradient(90deg,var(--brand),var(--brand-2));transition:width .4s}
.job .msg{font-size:.72rem;color:var(--muted);margin-top:5px}
.status{font-size:.66rem;font-weight:800;padding:3px 8px;border-radius:20px;text-transform:capitalize}
.s-queued{background:rgba(var(--brand-rgb),.15);color:var(--warn)}
.s-provisioning,.s-running{background:rgba(var(--accent-rgb),.15);color:var(--info)}
.s-completed{background:rgba(var(--accent-rgb),.15);color:var(--accent)}
.s-failed,.s-stopped{background:rgba(var(--danger-rgb),.15);color:var(--danger)}

/* audio player */
.audioplayer{display:flex;align-items:center;gap:10px;background:var(--surface-2);border:1px solid var(--line);
  border-radius:11px;padding:8px 10px}
.audioplayer audio{width:100%}

/* pagination */
.pager{display:flex;gap:6px;justify-content:center;margin:18px 0}
.pager button{min-width:34px;height:34px;border-radius:9px;border:1px solid var(--line);background:var(--surface-2);
  color:var(--muted);font-weight:700;cursor:pointer}
.pager button.active{border-color:var(--brand);color:var(--ink)}
.pager button:disabled{opacity:.4;cursor:default}

/* toasts */
.toasts{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);z-index:90;display:flex;flex-direction:column;gap:8px}
.toast{background:var(--surface-2);border:1px solid var(--line);color:var(--ink);padding:10px 16px;border-radius:11px;
  font-size:.84rem;font-weight:600;box-shadow:var(--shadow);
  /* A structured refusal is a sentence, not a word: let it wrap rather than run
     off both edges of a narrow window. */
  max-width:min(560px,calc(100vw - 36px));line-height:1.45}
.toast.success{border-color:var(--accent)}.toast.error{border-color:var(--danger)}

/* modal */
.scrim{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:60}
.modal{position:fixed;inset:0;display:grid;place-items:center;z-index:61;padding:18px}
.modal .box{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);max-width:460px;width:100%;padding:22px;box-shadow:var(--shadow)}

.foot-site{margin-top:50px;padding:26px 0;border-top:1px solid var(--line);color:var(--muted);font-size:.82rem}
.empty{text-align:center;color:var(--muted);padding:50px 0}
@media(max-width:820px){.editor{grid-template-columns:1fr;height:auto}.sidebar{border-left:none;border-top:1px solid var(--line)}}

/* mask editor */
.tile-mask{position:absolute;bottom:30px;right:6px;font-size:.62rem;font-weight:800;border:none;cursor:pointer;
  background:rgba(var(--brand-rgb),.9);color:var(--on-brand);padding:3px 7px;border-radius:7px}
.tile-mask:hover{background:var(--brand)}
.maskwrap{position:relative;display:inline-block;max-width:100%;border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--void)}
.maskwrap canvas{display:block;max-width:100%}
#maskDraw{position:absolute;top:0;left:0;cursor:crosshair;touch-action:none}

/* ---- public top bar (landing + auth pages: login/register) ---- */
.lp-bar{position:sticky;top:0;z-index:50;backdrop-filter:blur(12px);
  background:rgba(var(--bg-rgb),.72);border-bottom:1px solid var(--line)}
.lp-bar .wrap{display:flex;align-items:center;gap:20px;padding:15px 22px}
.lp-logo{font-family:var(--font-disp);font-weight:600;color:var(--ink);font-size:1.08rem;
  display:flex;flex-direction:row;align-items:center;gap:10px;line-height:1;margin-right:auto}
.lp-logo>span{display:flex;flex-direction:column}
.lp-logo small{font-family:var(--font-body);font-size:.54rem;letter-spacing:.32em;
  text-transform:uppercase;color:var(--brand);font-weight:800;margin-top:5px}
.lp-bar nav{display:flex;align-items:center;gap:22px}
.lp-bar nav a{font-weight:600;font-size:.86rem;color:var(--muted)}
.lp-bar nav a:hover{color:var(--ink)}
.lp-bar nav a.cta{color:var(--on-brand)}
.lp-bar nav a.profile .btn{padding-left:5px}
.lp-bar nav a.profile .avatar{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;background:rgba(10,10,11,.22);font-size:.72rem;font-weight:800}
.lp-menu-btn{display:none;background:none;border:1px solid var(--line);border-radius:9px;
  color:var(--ink);font-size:1.2rem;width:40px;height:40px;cursor:pointer}
@media (max-width:880px){
  .lp-bar nav{position:absolute;top:64px;right:22px;left:22px;flex-direction:column;
    align-items:stretch;gap:6px;background:var(--surface);border:1px solid var(--line);
    border-radius:14px;padding:12px;display:none}
  .lp-bar nav.open{display:flex}
  .lp-bar nav a{padding:10px 12px;border-radius:9px}
  .lp-bar nav a:hover{background:var(--surface-2)}
  .lp-menu-btn{display:block}
}

/* =====================================================================
   STUDIO COCKPIT (workflow-stage shell)
   A full-bleed production cockpit. Signature element: the stage bar — the
   pipeline reading left-to-right with the current stage lit.
   ===================================================================== */
.studio{position:fixed;inset:0;display:flex;flex-direction:column;background:var(--bg);color:var(--ink);z-index:1;font-family:var(--font-body)}

/* menu bar */
.menubar{display:flex;align-items:center;gap:14px;height:var(--bar-h,48px);padding:0 14px;position:relative;
  background:linear-gradient(180deg,var(--surface-2),var(--surface));border-bottom:1px solid var(--line)}
.mb-left,.mb-center,.mb-right{display:flex;align-items:center;gap:10px}
.mb-center{flex:1;justify-content:center}
.mb-logo{display:flex;align-items:center;text-decoration:none;opacity:.9}.mb-logo:hover{opacity:1}.mb-logo img{width:22px;height:22px;display:block}
.mb-proj{display:flex;align-items:center;gap:6px;background:transparent;border:1px solid var(--line);
  color:var(--ink);padding:5px 12px;border-radius:9px;cursor:pointer;font-weight:600}
.mb-proj:hover{border-color:var(--brand)}
.mb-proj-caret{opacity:.5;font-size:.7rem}
.mb-btn{background:transparent;border:1px solid transparent;color:var(--muted);padding:5px 10px;
  border-radius:8px;cursor:pointer;font-size:.82rem;font-weight:600}
.mb-btn:hover:not(:disabled){color:var(--ink);background:rgba(255,255,255,.05)}
.mb-btn:disabled{opacity:.35;cursor:default}
.mb-btn.on{color:var(--brand);background:rgba(var(--brand-rgb),.12)}
.mb-btn.ghost{border-color:var(--line)}

.mb-step{font-size:.74rem;color:var(--muted);font-style:italic;max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mb-jobs{font-size:.76rem;color:var(--gold);font-weight:600}

/* main: work area | rundock. The manual tools used to own a third column on the
   left of every stage; they are editor furniture and live in the editor sheet
   now (.canvas-tools below). */
.studio-main{flex:1;display:grid;grid-template-columns:1fr var(--dock-w,344px);min-height:0}

/* work area */
.workarea{display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--bg)}
/* No `.wa-tabs` and no `.wa-kind`. The generic sub-tab strip is gone — the work
   area is decided by the selected feature chip, and every stage that still has
   sub-tabs carries its own `.srctab` bar inside its board. `stage.tabs` in
   stages.py stays: studio.js still seeds `subTab` from it. `.wa-spacer` stays
   because the Inspector drawer's header uses it.
   See .docs/ui/feature-first-work-area.md. */
.wa-spacer{flex:1}
.wa-body{flex:1;overflow:auto;padding:16px}
.wa-empty{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:10px;color:var(--muted);text-align:center}
.wa-empty-ic{font-size:2.6rem;opacity:.4}
.wa-empty p{max-width:360px;font-size:.9rem}
.wa-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:12px}
.cell{position:relative;background:var(--surface);border:1px solid var(--line);border-radius:12px;
  overflow:hidden;cursor:pointer;transition:border-color .12s,transform .12s}
.cell:hover{border-color:var(--brand-2);transform:translateY(-2px)}
.cell.sel{border-color:var(--brand);box-shadow:0 0 0 1px var(--brand)}
/* SQUARE, WHATEVER IS IN IT.
   `aspect-ratio:1` was already here and was being overruled. `.wa-grid.rich .cell`
   is a COLUMN FLEX container, so this is a flex item, and a flex item's automatic
   minimum size is its content's min-content size — for a replaced element with an
   intrinsic ratio that is width x (h/w). A 720x4338 page in a 163px column has a
   min-content height of 979px, min-height beats aspect-ratio, and because the
   grid's rows are implicit EVERY card in that row grew with it: measured 265px
   tall throughout, 1082px on the row holding one comic page. `min-height:0` is
   the whole fix — it lets the ratio decide, which is what it was written to do. */
.cell-media{aspect-ratio:1;min-height:0;min-width:0;
  display:flex;align-items:center;justify-content:center;background:var(--ink-950)}
.cell-media img,.cell-media video{width:100%;height:100%;object-fit:cover}
/* AND A LONG IMAGE IS SHOWN WHOLE. `cover` fills the tile and crops, which is
   right for a panel and useless for a page: the middle 163px of a 4338px-tall
   scan is a band of nothing, and every such card looks identical. Past roughly
   2.2:1 the image is CONTAINED and centred instead — smaller, letterboxed, and
   recognisably the thing it is. Tagged on load by the delegated listener in
   studio.js, because CSS cannot ask what shape the file is. */
.cell-media img.fitin,.cell-media video.fitin,
.src-media img.fitin{object-fit:contain;background:var(--ink-950)}
.cell-ph{font-size:1.8rem;color:var(--muted)}
.cell-ph.txt{font-size:1.4rem}.cell-ph.sm{font-size:1rem}
.cell figcaption{padding:6px 8px;font-size:.7rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* inspector */
.inspector{display:flex;flex-direction:column;background:var(--surface);border-left:1px solid var(--line);min-height:0}
.muted.sm{font-size:.76rem;color:var(--muted);line-height:1.5}

/* features */
.feat-list{display:flex;flex-direction:column;gap:6px;margin-bottom:14px}
.feat{display:flex;align-items:center;gap:10px;background:var(--surface-2);border:1px solid var(--line);
  border-radius:10px;padding:9px 11px;cursor:pointer;text-align:left}
.feat:hover{border-color:var(--brand-2)}
.feat.on{border-color:var(--brand);background:rgba(var(--brand-rgb),.1)}
.feat-ic{font-size:1.1rem}
.feat-tx{display:flex;flex-direction:column}
.feat-tx b{font-size:.86rem}
.feat-tx small{color:var(--muted);font-size:.68rem}
.feat-panel{border-top:1px solid var(--line);padding-top:12px}
.fp-desc{font-size:.78rem;color:var(--muted);line-height:1.5;margin-bottom:12px}
.fp-desc .fp-readmore{display:inline-flex;align-items:center;gap:5px;margin-left:6px;color:var(--brand);font-weight:700;white-space:nowrap}.fp-desc .fp-readmore i{font-size:.62rem}.fp-desc .fp-readmore:hover{text-decoration:underline}
.fld{display:block;font-size:.74rem;font-weight:600;color:var(--ink);margin:10px 0 4px}
.inj{color:var(--brand);margin-left:5px;font-size:.7rem}
.ctl{width:100%;background:var(--bg);border:1px solid var(--line);border-radius:8px;color:var(--ink);
  padding:7px 9px;font:inherit;font-size:.82rem}
.ctl:focus{outline:none;border-color:var(--brand)}
.ctl.mono{font-family:ui-monospace,monospace;font-size:.74rem}
textarea.ctl{resize:vertical}
.chk{display:flex;align-items:center;gap:7px;font-size:.8rem;color:var(--muted);margin-top:8px}
.chk input{width:auto}
.run{width:100%;margin-top:14px;background:linear-gradient(180deg,var(--amber-400),var(--brand));border:none;
  color:var(--on-brand);padding:11px;border-radius:10px;font-weight:700;cursor:pointer}
.run:hover:not(:disabled){filter:brightness(1.08)}
.run:disabled{opacity:.5;cursor:default}

/* gallery folders */
.fold{border:1px solid var(--line);border-radius:10px;margin-bottom:8px;overflow:hidden}
.mini{background:var(--surface-2);border:1px solid var(--line);color:var(--ink);padding:6px 11px;
  border-radius:8px;cursor:pointer;font-size:.76rem;font-weight:600}
.mini:hover{border-color:var(--brand)}
.mini.ghost{background:transparent;color:var(--muted)}
.asst-card{margin-top:10px}
/* "this reading belongs to an ancestor" — a note, not a warning. */
.asst-inherit{display:flex;gap:7px;align-items:flex-start;margin:0 0 10px;
  padding:7px 9px;border-radius:8px;line-height:1.45;
  background:var(--surface);border:1px solid var(--line)}
.asst-inherit i{margin-top:2px;color:var(--muted)}
.asst-sec{margin-bottom:10px}
.asst-sec label{display:block;font-size:.72rem;font-weight:600;margin-bottom:4px}
.asst-actions{display:flex;gap:7px;margin-top:6px}

/* jobs */
.job{background:var(--surface-2);border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-bottom:8px}
.job.completed{border-color:rgba(var(--accent-rgb),.4)}
.job.failed{border-color:rgba(var(--danger-rgb),.5)}
/* label | pod time | status. The label takes the slack and truncates, so the two
   readings on the right keep their place instead of drifting with its length. */
.job-top{display:flex;align-items:center;gap:8px}
.job-top b{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.job-time{font-size:.68rem;color:var(--faint);font-variant-numeric:tabular-nums;flex-shrink:0}
.job-time i{margin-right:3px}
.job-st{font-size:.68rem;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);flex-shrink:0}
.job-bar{height:4px;background:var(--bg);border-radius:3px;margin:7px 0;overflow:hidden}
.job-bar i{display:block;height:100%;background:var(--brand);transition:width .3s}
/* INDETERMINATE. A job that is provisioning has no percentage to show and never
   did — the bar sat at 0% for the whole cold-volume bootstrap (19 min, measured
   2026-08-24) and read as a hang. A sweep says "working, duration unknown", which
   is the truth; a made-up percentage would not be. */
@keyframes indet{0%{transform:translateX(-100%)}100%{transform:translateX(350%)}}
.job-bar.indet,.bar.indet,.mb-prep-bar.indet{position:relative}
.job-bar.indet>i,.bar.indet>i,.mb-prep-bar.indet>i{
  width:30%!important;transition:none;animation:indet 1.5s ease-in-out infinite}
/* Reduced motion still needs to SAY something, so it keeps the stripe and drops
   only the movement — an empty bar would be the 0% problem again. */
@media (prefers-reduced-motion: reduce){
  .job-bar.indet>i,.bar.indet>i,.mb-prep-bar.indet>i{animation:none;width:30%!important}
}
.job-msg{font-size:.72rem;color:var(--muted)}
.job-acts{display:flex;gap:6px;margin-top:6px}
/* CLEAR ALL, above the list. It sits on the same line as the count of what is
   still running, which is the reassurance the button needs: what it takes and
   what it leaves, both readable without pressing it. */
.job-clear{display:flex;align-items:center;gap:8px;margin:0 0 9px}
.job-clear-n{font-size:.7rem;padding:0 6px;border-radius:999px;
  background:var(--surface-2);border:1px solid var(--line);color:var(--faint)}
.job-clear .muted{margin-left:auto}

/* STAGE BAR — the signature pipeline */
.stagebar{display:flex;align-items:stretch;height:66px;background:linear-gradient(180deg,var(--surface),var(--surface-2));
  border-top:1px solid var(--line)}
.stage{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  background:transparent;border:none;border-right:1px solid var(--line);color:var(--muted);cursor:pointer;
  position:relative;transition:color .15s,background .15s}
.stage:last-child{border-right:none}
.stage:hover{color:var(--ink);background:rgba(255,255,255,.03)}
.stage.on{color:var(--ink);background:rgba(var(--brand-rgb),.1)}
.stage.on::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:var(--brand)}
.stage-no{font-size:.62rem;color:var(--muted);letter-spacing:.1em}
.stage.on .stage-no{color:var(--brand)}
.stage-ic{font-size:1.15rem;line-height:1}
.stage-lb{font-size:.74rem;font-weight:700;letter-spacing:.01em}

/* responsive: collapse rails on narrow screens */
@media(max-width:880px){
  .studio-main{grid-template-columns:1fr}
  /* DEAD, and left as it was rather than "fixed". Nothing carries `class="inspector"`
     any more — the Inspector became the `.insp-veil` / `.insp-drawer` modal, which
     covers the whole viewport and is unaffected by the bar wrapping. Changing a
     rule nothing renders would only make it look maintained. It reads `top:48px`
     as a literal, which WOULD be the same bug the dock had if anything used it. */
  .inspector{position:fixed;right:0;top:48px;bottom:66px;width:90%;max-width:360px;z-index:5;box-shadow:var(--shadow)}
  .tool-lb{display:none}
}
.cell:hover .cell-edit{opacity:1}
.canvaswrap{display:flex;flex-direction:column;height:100%;gap:10px}
/* Padded on its own. It used to inherit `.wa-body`'s 16px and then sat inside a
   sheet that had none, so it was flush against the edge; it owns its gutter now
   and lines up with the tool rail above it and the status strip below. */
.canvas-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 12px}
/* Sliders in the bar are a control, not a layout element: unconstrained, the
   brush slider took a whole row to itself and wrapped the bar into three. */
.canvas-bar input[type=range]{width:110px;flex:0 0 auto}
.canvas-name{font-size:.82rem;font-weight:600}
/* "3 / 12" beside the prev/next arrows. Tabular figures so the width does not
   jitter as you step through a folder. */
.canvas-pos{font-size:.74rem;color:var(--muted);font-variant-numeric:tabular-nums;
  min-width:3.2em;text-align:center}
.canvas-stage{flex:1;display:flex;background:var(--ink-950);
  border:1px solid var(--line);border-radius:12px;overflow:auto;padding:14px}
.canvas-stage canvas{box-shadow:var(--shadow);background:var(--canvas-paper)}
.canvas-stage canvas.upper-canvas{background:transparent}
.canvas-stage>.canvas-container{margin:auto}
.opt{font-size:.74rem;color:var(--muted)}
.opt-in{background:var(--bg);border:1px solid var(--line);border-radius:7px;color:var(--ink);padding:5px 8px;font-size:.8rem;width:130px}

/* Story editor + beat board */
.docwrap{display:flex;flex-direction:column;height:100%;gap:10px}
/* `.beat-mv` outlived the beat board it was named for: it is the small square
   move/insert/delete control, and the script page uses it in every cue's gutter.
   The board itself (a `.md` narration split into draggable blocks) went with the
   second editor on 2026-08-27 — one format, one editor, and the cues ARE the
   beats. */
.beat-mv{background:transparent;border:1px solid var(--line);color:var(--muted);border-radius:6px;
  width:24px;height:22px;cursor:pointer;font-size:.7rem}
.beat-mv:hover{color:var(--ink);border-color:var(--brand)}
.beat-mv:disabled{opacity:.35;cursor:default}
/* Still used by the storyboard and the timeline, which are the other two ordered
   lists in the app. */
.beat-n{width:22px;height:22px;border-radius:50%;background:var(--surface-2);display:flex;align-items:center;
  justify-content:center;font-size:.7rem;color:var(--muted)}
.beat-drag{margin-left:auto;cursor:grab;color:var(--muted);font-size:1.1rem;user-select:none}
.sortable-ghost{opacity:.4}

/* Animation stage */
.anim{display:flex;flex-direction:column;height:100%;gap:10px;min-height:0}
.anim-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.opt-in.sm{width:120px}.opt-in.xs{width:64px}
.shots{flex:1;overflow:auto;display:flex;flex-direction:column;gap:8px;padding-right:4px}
.shot{display:flex;gap:10px;background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:8px}
.shot-thumb{width:104px;height:104px;object-fit:cover;border-radius:8px;background:var(--ink-950);flex-shrink:0}
.shot-fields{flex:1;min-width:0;display:flex;flex-direction:column;gap:6px}
.shot-h{display:flex;align-items:center;gap:8px}
.shot-name{font-size:.78rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.shot-drag{margin-left:auto}
.shot-row{display:flex;gap:6px;flex-wrap:wrap}
.animatic{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;background:var(--ink-950);border:1px solid var(--line);border-radius:12px}
.animatic img{max-width:70%;max-height:72%;object-fit:contain;box-shadow:var(--shadow)}
.animatic-cap{font-size:.8rem;color:var(--muted)}
.track{flex:1;overflow-x:auto;display:flex;gap:8px;align-items:flex-start;padding-bottom:8px}
.clip{flex-shrink:0;width:180px;background:var(--surface);border:1px solid var(--line);border-radius:10px;overflow:hidden}
.clip-v{width:100%;height:104px;object-fit:cover;background:var(--ink-950);display:block}
.clip-foot{display:flex;align-items:center;gap:5px;padding:6px;justify-content:space-between}

/* Sound waveform + Deliver */
.wave-stage{flex:1;display:flex;align-items:center;justify-content:center;background:var(--ink-950);
  border:1px solid var(--line);border-radius:12px;padding:18px}
.wave-stage #waveform{width:100%}
.deliver{display:grid;grid-template-columns:352px 1fr;gap:14px;height:100%;min-height:0}
.deliver-panel{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:16px;overflow:auto}
.deliver-panel h3{font-size:1rem;margin-bottom:6px}
.deliver-out{overflow:auto}
.deliver-out h3{font-size:.9rem;color:var(--muted);margin-bottom:10px}

/* ============================================================= */
/*  STUDIO v3 — professional creative-tool design system          */
/*  Neutral chrome, one calm accent, splash-image stage pills.    */
/*  Inspiration: Figma canvas, ZBrush/Photoshop panel chrome.     */
/* ============================================================= */
/* Adds only what the studio shell needs on top of the palette at the top of this
   file. Re-declaring --bg/--surface/--ink/--muted/--line/--radius here would win
   by being second and turn that palette into dead code. */
:root{
  --line-2:rgba(var(--paper-rgb),.16);
  --faint:#6B6659;
  --ac:var(--brand);           /* the single accent */
  --ac-soft:rgba(var(--brand-rgb),.14);
  --ac-line:rgba(var(--brand-rgb),.45);
  --ok:var(--accent);
  --ease-spring:cubic-bezier(.34,1.4,.5,1);
  --dot:radial-gradient(circle, rgba(var(--paper-rgb),.05) 1px, transparent 1px);
}

/* calm neutral ambience, not a light show */
body::before{
  content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(70% 55% at 50% -10%,rgba(var(--brand-rgb),.08),transparent 70%);
}

/* NO `transform:translateY(0)` in the `to` keyframe. These animations run with
   `both`, so the final keyframe stays applied for the life of the element — and a
   transform other than `none` makes that element a stacking context AND the
   containing block for its `position:fixed` descendants. `.workarea` carries this
   animation (below), so the editor sheet inside it was being sized to the work area
   rather than the viewport and painted UNDER the unpositioned menubar, whatever
   z-index it asked for. translateY(0) is the default; leaving it out changes
   nothing on screen and un-traps .workarea, .inspector and
   .history-window alike. */
@keyframes fadeSlideUp{from{opacity:0;transform:translateY(12px)}to{opacity:1}}
@keyframes scaleIn{from{opacity:0;transform:scale(.96)}to{opacity:1}}   /* same reason as above */
@keyframes floaty{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}

/* ---- buttons: solid, quiet, one accent (NO multicolor gradients) ---- */
.btn{background:var(--ac);color:var(--on-brand);border:1px solid transparent;border-radius:10px;
  font-weight:600;transition:transform .16s var(--ease-spring),background .16s,box-shadow .16s}
.btn:hover{background:var(--amber-400);transform:translateY(-1px);box-shadow:0 6px 18px -8px rgba(var(--brand-rgb),.6);filter:none}
.btn:active{transform:translateY(0)}
.btn.ghost{background:var(--surface-2);color:var(--ink);border:1px solid var(--line-2)}
.btn.ghost:hover{background:var(--surface-3);box-shadow:none}
.btn.danger{background:var(--rust-600);color:var(--paper)}
.btn.lg{padding:13px 24px;font-size:.96rem;border-radius:12px}
.run{background:var(--ac)!important;color:var(--on-brand);border:none;border-radius:11px;font-weight:600;
  padding:12px 18px;cursor:pointer;animation:none;transition:transform .16s var(--ease-spring),background .16s}
.run:hover{background:var(--amber-400);transform:translateY(-1px)}
.run:disabled{background:var(--surface-3)!important;color:var(--faint);cursor:not-allowed;transform:none}

/* ---- Font Awesome sizing in chrome ---- */
/* ---- Font Awesome sizing in chrome ---- */
.nav .link i,.mb-btn i{font-size:.92em;opacity:.85}
/* Tool-rail icons are Font Awesome now (stages.py `fa`), not emoji. They render a
   touch smaller than the glyphs they replaced, so size them up and drop the .85
   dimming — at this size it read as disabled. */
.tool i{font-size:1.02em;opacity:1}
.tool-ic{display:inline-flex;align-items:center;justify-content:center;min-width:1.25em}
.nav .link{display:inline-flex;align-items:center;gap:7px}

/* =========================================================== */
/*  STAGE BAR — workflow tabs with per-stage splash image fade   */
/* =========================================================== */
.stagebar{display:flex;gap:0;align-items:stretch;padding:0;height:60px;
  background:var(--surface);border-top:1px solid var(--line);overflow-x:auto}
.stagebar .stage{
  position:relative;display:flex;align-items:center;justify-content:center;gap:9px;flex:1;min-width:130px;
  border:none;border-right:1px solid var(--line);border-radius:0;padding:0 18px;cursor:pointer;
  color:rgba(255,255,255,.7);background-color:var(--surface-2);background-size:cover;
  background-position:right center;background-repeat:no-repeat;overflow:hidden;
  transition:filter .2s,color .2s,box-shadow .2s;
  filter:saturate(.5) brightness(.7)}
.stagebar .stage:last-child{border-right:none}
.stagebar .stage:hover{filter:saturate(.8) brightness(.86);color:var(--paper)}
.stagebar .stage.on{
  filter:none;color:var(--paper);
  box-shadow:inset 0 -3px 0 var(--paper),0 1px 0 rgba(255,255,255,.05) inset}
.stagebar .stage-no{font-size:.62rem;font-weight:800;opacity:.7;text-align:center}
.stagebar .stage-ic{font-size:1rem;display:flex}
.stagebar .stage-lb{font-weight:700;letter-spacing:.04em;text-transform:uppercase;font-size:.72rem;white-space:nowrap}

/* =========================================================== */
/*  WORK AREA — Figma-style dotted canvas + clean empty states  */
/* =========================================================== */
.wa-body{background-color:var(--bg);background-image:var(--dot);background-size:22px 22px}
.wa-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;
  min-height:340px;text-align:center;color:var(--muted);padding:40px}
.wa-empty .wa-empty-ic,.wa-empty .fa-empty{
  font-size:2.6rem;color:var(--faint);width:78px;height:78px;border-radius:20px;
  display:flex;align-items:center;justify-content:center;background:var(--surface);
  border:1px solid var(--line);box-shadow:0 10px 30px -16px rgba(0,0,0,.8);animation:floaty 5s ease-in-out infinite}
.wa-empty p{max-width:430px}
.wa-empty b{color:var(--ink)}

/* ---- editor toolbar: the manual tools, along the top of the canvas ---- */
.canvas-tools{display:flex;align-items:center;gap:4px;flex-wrap:wrap;padding:8px 12px;
  border-bottom:1px solid var(--line);background:var(--surface-2)}
.ctool{display:flex;align-items:center;gap:7px;padding:6px 11px;background:transparent;
  border:1px solid transparent;border-radius:9px;color:var(--muted);cursor:pointer;
  font-size:.78rem;font-weight:600;white-space:nowrap}
.ctool:hover{background:var(--surface-3);color:var(--ink)}
.ctool.on{background:var(--ac-soft);color:var(--paper);border-color:var(--ac-line)}
.ctool-ic{font-size:.95rem;line-height:1}
.ctool-lb{letter-spacing:.01em}
/* `.ctool-sep` / `.ctool-more` retired 2026-08-29 with the Advanced disclosure.
   A rail of three tools has nothing to hide behind a chevron. */
@media(max-width:640px){ .ctool-lb{display:none} .ctool{padding:6px 9px} }


/* ---- glassy panels + subtle card hover (no rainbow) ---- */
/* ---- glassy panels + subtle card hover (no rainbow) ---- */
.card,.cell,.shot,.clip,.deliver-panel,.proj-card{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  transition:transform .22s var(--ease-spring),border-color .18s,box-shadow .22s}
.cell:hover,.shot:hover,.proj-card:hover{
  transform:translateY(-3px);border-color:var(--line-2);box-shadow:0 16px 36px -22px rgba(0,0,0,.85)}
.cell.sel{border-color:var(--ac);box-shadow:0 0 0 2px var(--ac-soft)}

/* entrance motion */
.workarea,.inspector{animation:fadeSlideUp .45s var(--ease-spring) both}
.wa-grid .cell{animation:scaleIn .35s var(--ease-spring) both}

/* ---- dropzone (neutral, single accent on drag) ---- */
.dropzone{border:2px dashed var(--line-2);margin-top: 16px;margin-bottom: 16px;border-radius:16px;padding:30px 22px;text-align:center;
  background:var(--surface);transition:all .18s var(--ease-spring);cursor:pointer;color:var(--muted)}
.dropzone:hover{border-color:var(--ac-line);color:var(--ink)}
.dropzone.drag{border-color:var(--ac);color:var(--ink);background:var(--ac-soft);transform:scale(1.005)}
.dropzone .dz-ic{font-size:2rem;display:block;margin-bottom:10px;color:var(--ac);animation:floaty 4s ease-in-out infinite}
.dz-bar{height:6px;border-radius:999px;background:var(--surface-3);overflow:hidden;margin-top:14px}
.dz-bar i{display:block;height:100%;width:0;background:var(--ac);transition:width .25s ease}
.dz-files{margin-top:12px;display:flex;flex-direction:column;gap:6px;text-align:left}
.dz-file{display:flex;align-items:center;gap:8px;font-size:.8rem;background:var(--surface-2);
  border:1px solid var(--line);border-radius:9px;padding:7px 10px}
.dz-file .ok{color:var(--ok);margin-left:auto}
.dz-file .bad{color:var(--danger);margin-left:auto;font-size:.72rem}

/* =========================================================== */
/*  ADMIN tables + page heads                                   */
/* =========================================================== */
.pagehead{display:flex;align-items:center;justify-content:space-between;gap:14px;margin:26px 0 18px}
.pagehead h1{font-size:1.5rem}
.table-card{background:var(--surface);border:1px solid var(--line);border-radius:14px;overflow:hidden}
table.tbl{width:100%;border-collapse:collapse;font-size:.85rem}
table.tbl th{text-align:left;padding:13px 16px;color:var(--muted);font-weight:600;
  font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;border-bottom:1px solid var(--line);
  background:var(--surface-2)}
table.tbl td{padding:12px 16px;border-bottom:1px solid var(--line);vertical-align:middle}
table.tbl tr:last-child td{border-bottom:none}
table.tbl tbody tr:hover{background:rgba(255,255,255,.025)}
.badge{display:inline-flex;align-items:center;gap:5px;font-size:.7rem;font-weight:700;padding:3px 9px;
  border-radius:999px;background:var(--surface-3);color:var(--muted)}
.badge.ok{background:rgba(var(--accent-rgb),.14);color:var(--ok)}
.badge.ac{background:var(--ac-soft);color:var(--amber-300)}

/* tidy scrollbars */
::-webkit-scrollbar{width:11px;height:11px}
::-webkit-scrollbar-thumb{background:rgba(255,255,255,.10);border-radius:999px;border:3px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.18);background-clip:content-box}

/* admin + page tabs share the pill treatment */
.tabs{display:flex;gap:6px;border:none;margin-bottom:16px;flex-wrap:wrap}
.tabs button{border-radius:999px;padding:7px 15px;font-weight:600;background:transparent;
  color:var(--muted);border:1px solid transparent;cursor:pointer;transition:all .15s var(--ease-spring)}
.tabs button:hover{color:var(--ink);background:var(--surface-2)}
.tabs button.active{color:var(--paper);background:var(--ac-soft);border-color:var(--ac-line)}
.admin .chip,.chip{display:inline-flex;align-items:center;font-size:.7rem;font-weight:700;padding:3px 9px;
  border-radius:999px;background:var(--surface-3);color:var(--muted)}
.chip.tool{background:var(--ac-soft);color:var(--amber-300)}
.status{font-size:.72rem;font-weight:700;padding:3px 9px;border-radius:999px;background:var(--surface-3);color:var(--muted)}
.status.s-completed{background:rgba(var(--accent-rgb),.14);color:var(--ok)}
.status.s-running,.status.s-pending{background:var(--ac-soft);color:var(--amber-300)}
.status.s-failed{background:rgba(var(--danger-rgb),.16);color:var(--danger)}

/* panels (projects/admin/scripts) + project cards */
.panel{background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:18px;margin-bottom:16px}
.panel label{display:block;font-size:.74rem;font-weight:600;color:var(--muted);margin:10px 0 6px}
.panel input,.panel textarea,.panel select{width:100%;background:var(--bg);border:1px solid var(--line-2);
  border-radius:10px;color:var(--ink);padding:10px 12px;font:inherit;font-size:.88rem;transition:border-color .15s}
.panel input:focus,.panel textarea:focus,.panel select:focus{outline:none;border-color:var(--ac-line);box-shadow:0 0 0 3px var(--ac-soft)}
.proj-card{transition:transform .2s var(--ease-spring),border-color .18s,box-shadow .2s}
.proj-card:hover{transform:translateY(-2px);border-color:var(--line-2);box-shadow:0 16px 36px -24px rgba(0,0,0,.85)}
.proj-ic{width:42px;height:42px;border-radius:12px;display:flex;align-items:center;justify-content:center;
  background:var(--ac-soft);color:var(--amber-300);font-size:1.05rem;flex-shrink:0}
.proj-badge{font-size:.66rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--ac);
  background:var(--ac-soft);border:1px solid var(--ac-line);padding:4px 10px;border-radius:999px}

/* permanent delete + assistant ask box */
.mini.danger{color:var(--danger);border-color:rgba(var(--danger-rgb),.4)}
.mini.danger:hover{background:rgba(var(--danger-rgb),.12);color:var(--rust-400)}
.row.gap{display:flex;gap:8px;flex-wrap:wrap;margin-top:8px}
/* The ask sits under Shot details now, so its rule is a separator BELOW it as
   well as above — it is the middle of the panel rather than the end of it. */
.asst-ask{margin:12px 0;padding:12px 0;border-top:1px solid var(--line);
  border-bottom:1px solid var(--line);display:flex;flex-direction:column;gap:7px}
.asst-ask label{font-size:.72rem;font-weight:700;color:var(--ac)}

/* search box + results (Gallery tab) */
.search-box{display:flex;gap:6px;margin-bottom:10px}
.search-box .ctl{flex:1}
.search-results{display:flex;flex-direction:column;gap:6px;margin-bottom:14px}
.sr{display:flex;flex-direction:column;gap:3px;text-align:left;padding:8px 10px;border:1px solid var(--line);
  border-radius:9px;background:var(--surface-2);cursor:pointer;transition:border-color .15s,background .15s}
.sr:hover{border-color:var(--ac);background:rgba(var(--brand-rgb),.06)}
.sr-top{font-size:.74rem;font-weight:700;color:var(--ink);display:flex;gap:7px;align-items:center}
.sr-snip{font-size:.68rem;color:var(--muted);line-height:1.35;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical}

/* assistant chat */
.asst-chat{margin-top:16px;padding-top:12px;border-top:1px solid var(--line)}
.asst-chat-log{display:flex;flex-direction:column;gap:7px;max-height:240px;overflow-y:auto;margin-bottom:8px}
.acm{padding:7px 10px;border-radius:10px;font-size:.78rem;line-height:1.4;max-width:90%}
.acm.user{align-self:flex-end;background:var(--ac);color:var(--on-brand);border-bottom-right-radius:3px}
.acm.assistant{align-self:flex-start;background:var(--surface-2);border:1px solid var(--line);border-bottom-left-radius:3px}
.asst-chat-in{display:flex;gap:6px}
.asst-chat-in .ctl{flex:1}
.link-mini{background:none;border:none;color:var(--ac);font-size:.68rem;font-weight:600;cursor:pointer;
  padding:2px 0 8px;display:inline-flex;gap:5px;align-items:center}
.link-mini:hover{text-decoration:underline}
.link-mini:disabled{opacity:.5;cursor:default}

/* =========================================================== */
/*  STUDIO v4 — Editor/Inspector views · History window ·       */
/*  contextual Run Dock (empty vessel + mounted panel partials) */
/* =========================================================== */

/* --- menubar: view toggle + history --- */
.mb-sep{width:1px;height:22px;background:var(--line-2);margin:0 2px}
.hist-wrap{position:relative}
.viewtoggle{display:inline-flex;background:var(--surface);border:1px solid var(--line-2);border-radius:999px;padding:2px;gap:2px}
.viewtoggle button{background:transparent;border:none;color:var(--muted);padding:5px 13px;border-radius:999px;
  cursor:pointer;font-size:.8rem;font-weight:600;display:inline-flex;align-items:center;gap:6px;transition:all .15s var(--ease-spring)}
.viewtoggle button:hover:not(:disabled){color:var(--ink)}
.viewtoggle button.on{background:var(--ac-soft);color:var(--paper);box-shadow:inset 0 0 0 1px var(--ac-line)}
.viewtoggle button:disabled{opacity:.4;cursor:not-allowed}

/* --- history window (action stack popover) --- */
.history-window{position:absolute;top:calc(100% + 8px);left:0;width:330px;max-height:62vh;overflow:auto;
  background:var(--surface-2);border:1px solid var(--line-2);border-radius:12px;
  box-shadow:0 24px 60px -22px rgba(0,0,0,.9);z-index:40;animation:fadeSlideUp .2s var(--ease-spring) both}
.hw-head{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid var(--line);
  position:sticky;top:0;background:var(--surface-2);z-index:1}
.hw-head b{font-size:.84rem;display:flex;align-items:center;gap:7px}
.hw-kind{font-size:.66rem;color:var(--muted);text-transform:capitalize;background:var(--surface-3);padding:2px 8px;border-radius:999px}
.hw-head .rd-x{margin-left:auto}
.hw-list{padding:6px}
.hw-item{display:flex;align-items:center;gap:10px;width:100%;text-align:left;background:transparent;border:none;
  color:var(--ink);padding:8px 10px;border-radius:9px;cursor:pointer;transition:background .12s,opacity .12s}
.hw-item:hover{background:var(--surface-3)}
.hw-item.future{opacity:.42}
.hw-item.future:hover{opacity:.85}
.hw-item.cur{background:var(--ac-soft);box-shadow:inset 0 0 0 1px var(--ac-line)}
.hw-ic{width:26px;height:26px;border-radius:7px;background:var(--surface);display:flex;align-items:center;
  justify-content:center;color:var(--muted);font-size:.76rem;flex-shrink:0}
.hw-item.cur .hw-ic{color:var(--on-brand);background:var(--ac)}
.hw-tx{display:flex;flex-direction:column;min-width:0}
.hw-tx b{font-size:.8rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hw-tx small{color:var(--muted);font-size:.66rem;text-transform:capitalize}
.hw-here{margin-left:auto;font-size:.64rem;color:var(--ac);font-weight:800;white-space:nowrap}
.hw-base{border-top:1px dashed var(--line);margin-top:4px}
.ed-dot{width:6px;height:6px;border-radius:50%;background:var(--warn)}

/* --- inspector view --- */
.inspview{display:flex;flex-direction:column;gap:12px}
.cell:hover .cell-chat{opacity:1}

/* --- run dock: empty vessel + mounted panels --- */
.rundock{display:flex;flex-direction:column;background:var(--surface);border-left:1px solid var(--line);
  min-height:0;overflow:auto;animation:fadeSlideUp .45s var(--ease-spring) both}
.rd-panel{padding:14px}
.rd-head{display:flex;align-items:center;gap:8px;margin-bottom:12px}
.rd-head b{font-size:.9rem;display:flex;align-items:center;gap:8px;text-transform:capitalize}
.rd-x{margin-left:auto;background:transparent;border:none;color:var(--muted);cursor:pointer;
  width:26px;height:26px;border-radius:7px;display:flex;align-items:center;justify-content:center}
.rd-x:hover{background:var(--surface-3);color:var(--ink)}
.rd-empty{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;
  text-align:center;padding:30px 20px;color:var(--muted)}
.rd-empty-ic{width:64px;height:64px;border-radius:18px;display:flex;align-items:center;justify-content:center;
  background:var(--surface-2);border:1px solid var(--line);font-size:1.5rem;color:var(--faint);animation:floaty 5s ease-in-out infinite}
.rd-featbar{display:flex;align-items:center;gap:8px;margin-bottom:8px;font-size:.9rem}
.rd-filecard{width:100%;aspect-ratio:16/10;background:var(--ink-950);border:1px solid var(--line);border-radius:12px;
  display:flex;align-items:center;justify-content:center;overflow:hidden;margin-bottom:8px}
.rd-filecard img,.rd-filecard video{width:100%;height:100%;object-fit:contain}
.rd-name{font-size:.84rem;font-weight:600;margin-bottom:10px;word-break:break-word}
.rd-props{display:flex;flex-direction:column;gap:4px;margin-bottom:10px}
.rd-prop{display:flex;justify-content:space-between;gap:10px;font-size:.76rem;padding:6px 9px;
  background:var(--surface-2);border:1px solid var(--line);border-radius:8px}
.rd-prop span{color:var(--muted)}
.rd-prop b{text-transform:capitalize;text-align:right;word-break:break-word}

/* ---- a board's own three halves: its panels, its story, its prompt --------
   A board is N related panels, the beats they carry and the document those two
   compose into. The dock used to show only the anchor plate and the motion
   fields, so a three-panel board looked like a one-panel board and the prompt
   the money is spent on was not visible anywhere in the app. */
.rd-plates{display:flex;gap:6px;overflow-x:auto;padding-bottom:4px;margin-bottom:8px}
.rd-plate{flex:0 0 auto;width:112px;margin:0;cursor:pointer}
.rd-plate img{width:100%;aspect-ratio:16/10;object-fit:contain;background:var(--ink-950);
  border:1px solid var(--line);border-radius:10px;display:block}
.rd-plate figcaption{font-size:.66rem;color:var(--muted);text-align:center;padding-top:3px}
.rd-plate:hover img{border-color:var(--accent)}

.rd-story{margin-bottom:12px}
.rd-beat,.rd-lettering{background:var(--surface-2);border:1px solid var(--line);
  border-radius:8px;padding:7px 9px;margin-bottom:4px}
.rd-beat-where{display:block;font-size:.64rem;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);margin-bottom:3px}
.rd-beat p,.rd-lettering p{margin:0 0 3px;font-size:.78rem;line-height:1.45}
.rd-lettering p:last-child{margin-bottom:0}
.rd-lettering b{color:var(--muted);font-weight:600}

.rd-prompt{margin-bottom:12px}
.rd-promptbox{max-height:340px;overflow:auto;white-space:pre-wrap;word-break:break-word;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.7rem;line-height:1.5;
  background:var(--surface-2);border:1px solid var(--line);border-radius:8px;
  padding:10px 11px;margin:0 0 6px}
/* Animatable verdict. Verdigris while the frame is in the film, rust once it is
   dropped — the same good/warning pair the rest of the studio uses, so a skipped
   frame reads as skipped from across the dock. */
/* THE PANEL'S OWN ACTION ROW. `.row.gap` is a FORM row — 12px gutter, 8px top
   margin — and under a property table it read as a gap somebody forgot to close.
   Tighter, and it wraps, because "Re-run with notes" is a wide button in a
   344px dock. `.tight` drops the wrap and pushes the last child right, which is
   what the Animatable strip wants. */
.rd-acts{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 10px}
.rd-acts.tight{flex-wrap:nowrap;align-items:center;margin:0}
.rd-acts.tight > :last-child{margin-left:auto}
.rd-animatable .rd-acts b{display:flex;align-items:center;gap:6px}

.rd-animatable{border:1px solid var(--line);border-left:3px solid var(--verd-500);
  border-radius:10px;padding:8px 9px;margin-bottom:10px;background:var(--surface-2);font-size:.78rem}
.rd-animatable.off{border-left-color:var(--rust-500)}
.rd-animatable b{display:flex;align-items:center;gap:6px}
.rd-animatable .row{justify-content:space-between}
.rd-animatable p{margin:6px 0 0}
.rd-context{display:flex;align-items:center;gap:10px;background:var(--surface-2);border:1px solid var(--line);border-radius:10px;padding:8px}
.rd-context img{width:40px;height:40px;object-fit:cover;border-radius:8px}
.rd-context-tx{display:flex;flex-direction:column;min-width:0}
.rd-context-tx b{font-size:.8rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rd-context .rd-x{margin-left:auto}
/* The feature panel's two param tiers. A flex COLUMN with explicit `order` is
   what lets one v-for render both (feature-panel.html): basics 1, the
   disclosure 2, advanced 3. */
.fp-params{display:flex;flex-direction:column}
.fp-adv{display:flex;align-items:center;gap:8px;width:100%;margin:4px 0 10px;padding:7px 9px;
  background:transparent;border:1px dashed var(--line-2);border-radius:9px;
  color:var(--muted);cursor:pointer;font-size:.78rem;font-weight:600;letter-spacing:.02em}
.fp-adv:hover{color:var(--ink);border-color:var(--ac-line);background:rgba(255,255,255,.03)}
.fp-adv i{font-size:.7rem;width:10px}
.fp-adv-n{margin-left:auto;font-size:.7rem;padding:1px 7px;border-radius:999px;
  background:var(--surface-2);border:1px solid var(--line);color:var(--faint)}
.fp-noparams{margin:2px 0 10px}
.tp-sec{display:flex;flex-direction:column;gap:8px}
/* GEN FILL'S NEGATIVE PRESETS. A wrapping row of toggles, sized to read as
   "press these" rather than as a second toolbar: the field below is still where
   the answer lives, and the chips are a way of filling it in. `on` borrows the
   accent the rest of the studio uses for a live setting. */
.gf-negchips{display:flex;flex-wrap:wrap;gap:5px}
.gf-negchip{display:inline-flex;align-items:center;gap:5px;padding:4px 9px;border-radius:999px;
  background:var(--surface-2);border:1px solid var(--line);color:var(--muted);
  font-size:.72rem;font-weight:600;cursor:pointer;transition:color .12s,border-color .12s,background .12s}
.gf-negchip i{font-size:.62rem}
.gf-negchip:hover{color:var(--ink);border-color:var(--ac-line)}
.gf-negchip.on{color:var(--ac);border-color:var(--ac-line);background:var(--ac-soft)}
.tp-range{width:100%;accent-color:var(--ac)}

/* CLONE STAMP: the source read-out, which is also the button that re-picks it.
   Full width and at the top of the panel because "where am I copying from" is
   the only question this tool raises, and while it is ARMED it is an instruction
   rather than a read-out — so it borrows the accent to say the picture is waiting
   for a click. */
.clone-src{display:flex;align-items:center;gap:8px;width:100%;padding:8px 10px;
  background:var(--surface-2);border:1px solid var(--line);border-radius:9px;
  color:var(--muted);cursor:pointer;font-size:.74rem;font-weight:600;text-align:left}
.clone-src:hover{color:var(--ink);border-color:var(--ac-line)}
.clone-src.armed{color:var(--ac);border-color:var(--ac-line);background:var(--ac-soft);
  border-style:dashed}
.clone-src i{font-size:.72rem;width:12px}

/* --- responsive: dock + history collapse gracefully --- */
@media(max-width:880px){
  .rundock{position:fixed;right:0;top:48px;bottom:60px;width:90%;max-width:360px;z-index:5;
    box-shadow:0 20px 60px -20px rgba(0,0,0,.9)}
  .history-window{width:88vw;left:auto;right:0}
  .viewtoggle button span{font-size:.74rem}
}

/* =========================================================== */
/*  SOURCE stage — ordering board + leave gate (slice 1)        */
/* =========================================================== */
.srcboard{display:flex;flex-direction:column;gap:12px}
.srcboard-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.srcboard-tabs button.on{background:var(--ac-soft);color:var(--paper);box-shadow:inset 0 0 0 1px var(--ac-line)}
.srcboard-dirty{font-size:.72rem;font-weight:700;color:var(--warn);display:inline-flex;align-items:center;gap:6px}
.srcboard-dirty i{font-size:.5rem}

.srcgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:12px}
.srccell{position:relative;background:var(--surface);border:1px solid var(--line);border-radius:12px;overflow:hidden;
  animation:scaleIn .3s var(--ease-spring) both}
.srccell.sortable-ghost{opacity:.35}
.srccell.sortable-chosen{border-color:var(--ac);box-shadow:0 0 0 2px var(--ac-soft)}
.src-order{position:absolute;top:6px;left:6px;z-index:2;min-width:24px;height:24px;padding:0 6px;border-radius:8px;
  background:var(--ac);color:var(--on-brand);font-size:.78rem;font-weight:800;display:flex;align-items:center;justify-content:center;
  cursor:pointer;box-shadow:0 2px 8px -2px rgba(0,0,0,.6)}
.src-order:hover{background:var(--amber-400)}
.src-drag{position:absolute;top:6px;right:6px;z-index:2;width:24px;height:24px;border-radius:8px;background:rgba(0,0,0,.55);
  color:var(--paper);display:flex;align-items:center;justify-content:center;cursor:grab;font-size:.72rem}
.src-drag:active{cursor:grabbing}
.src-media{aspect-ratio:1;min-height:0;min-width:0;
  display:flex;align-items:center;justify-content:center;background:var(--ink-950);cursor:pointer}
.src-media img{width:100%;height:100%;object-fit:cover}
.srccell figcaption{padding:6px 8px;font-size:.7rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* modal / confirmation gate */
.modal-overlay{position:fixed;inset:0;z-index:60;background:rgba(var(--bg-rgb),.62);backdrop-filter:blur(2px);
  display:flex;align-items:center;justify-content:center;padding:24px;animation:fadeSlideUp .18s var(--ease-spring) both}
.modal{background:var(--surface-2);border:1px solid var(--line-2);border-radius:16px;padding:22px;max-width:460px;width:100%;
  box-shadow:0 30px 80px -30px rgba(0,0,0,.9)}
.modal h3{font-size:1.02rem;display:flex;align-items:center;gap:9px;margin-bottom:10px}
.modal h3 i{color:var(--warn)}
.modal-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:18px;flex-wrap:wrap}
/* An earlier .modal rule (the confirmation gate) is position:fixed;inset:0, so a
   .modal INSIDE an overlay was pinned to the top-left corner instead of being
   centred by the overlay's flex. The overlay does the positioning here. */
.modal-overlay>.modal{position:relative;inset:auto;display:block;place-items:unset}

/* Assistant: capabilities disclosure + human-checkpoint proposals (slice 2) */
.asst-caps{margin-top:10px;border:1px solid var(--line);border-radius:10px;background:var(--surface);padding:8px 12px}
.asst-caps summary{cursor:pointer;font-size:.8rem;font-weight:700;color:var(--ink)}
.asst-caps ul{margin:8px 0 2px;padding-left:18px}
.asst-caps li{font-size:.78rem;color:var(--muted);line-height:1.5;margin:2px 0}
.asst-proposals{margin:10px 0;display:flex;flex-direction:column;gap:6px}
.asst-prop{display:flex;align-items:center;justify-content:space-between;gap:10px;background:var(--ac-soft);
  border:1px solid var(--ac-line);border-radius:10px;padding:8px 10px}
.asst-prop-tx{font-size:.8rem;font-weight:600;color:var(--ink);display:inline-flex;align-items:center;gap:7px}
.asst-prop-tx i{color:var(--ac)}

/* The JUDGE block was here — score tags, the judge bar, the score dock.
   Removed 2026-08-15 with the feature (services/gemini.py has the reasoning).
   Nothing else used .sc-good/.sc-ok/.sc-bad, .cell-score, .judge-* or .jc*;
   verified by grep over templates/ and static/js/ before deleting. */

/* =========================================================== */
/*  EDITOR UI POLISH (slice 4)                                  */
/* =========================================================== */
/* Feature panel: shot-generator hero preview with an animated sheen */
@keyframes pzazSheen{0%{background-position:0% 50%}100%{background-position:200% 50%}}
.fp-hero{margin:12px 0}
.fp-hero-frame{position:relative;border-radius:12px;padding:2px;background:linear-gradient(110deg,var(--ac),transparent 30%,transparent 70%,var(--ac));
  background-size:200% 100%;animation:pzazSheen 3.4s linear infinite}
.fp-hero-frame>img{display:block;width:100%;max-height:220px;object-fit:cover;border-radius:10px;background:var(--ink-950)}
.fp-hero-badge{position:absolute;top:10px;left:10px;display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:8px;
  background:rgba(var(--bg-rgb),.72);color:var(--paper);font-size:.74rem;font-weight:800;backdrop-filter:blur(3px)}
.fp-hero-badge i{color:var(--ac)}
.fp-hero-count{position:absolute;bottom:10px;right:10px;padding:3px 9px;border-radius:8px;background:rgba(var(--bg-rgb),.72);
  color:var(--paper);font-size:.72rem;font-weight:700}
.fp-hero-strip{display:flex;gap:6px;margin-top:6px}
.fp-hero-strip img{width:44px;height:44px;object-fit:cover;border-radius:7px;border:1px solid var(--line)}
.fp-more{display:flex;align-items:center;justify-content:center;min-width:44px;height:44px;border-radius:7px;
  background:var(--surface);border:1px solid var(--line);color:var(--muted);font-size:.72rem;font-weight:800}

/* File insights: structured "shot details" */
.shot-details{background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-bottom:10px}
.sd-title{font-size:.76rem;font-weight:800;color:var(--ink);display:flex;align-items:center;gap:7px;margin-bottom:8px}
.sd-title i{color:var(--ac)}
.sd-row{display:flex;justify-content:space-between;gap:12px;padding:3px 0;font-size:.78rem;border-top:1px solid var(--line)}
.sd-row:first-of-type{border-top:none}
.sd-row>span{color:var(--muted);text-transform:capitalize;flex:0 0 auto}
.sd-row>b{color:var(--ink);text-align:right;font-weight:600}

/* Assistant: "AI director" suggestion card */
.director-card{display:flex;align-items:center;gap:11px;width:100%;margin-top:10px;padding:11px 13px;text-align:left;cursor:pointer;
  border:1px solid var(--ac-line);border-radius:12px;background:var(--ac-soft);color:var(--ink);transition:filter .15s}
.director-card:hover:not(:disabled){filter:brightness(1.08)}
.director-card:disabled{opacity:.55;cursor:default}
.dc-ic{width:34px;height:34px;border-radius:9px;background:var(--ac);color:var(--on-brand);display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.dc-tx{display:flex;flex-direction:column;line-height:1.25}
.dc-tx b{font-size:.86rem}
.dc-tx small{color:var(--muted);font-size:.74rem}
.dc-go{margin-left:auto;color:var(--ac)}

/* =========================================================== */
/*  UI UPGRADE PASS — canvas chrome, clip region, gallery,      */
/*  richer inspector cards, assistant chips, polish             */
/* =========================================================== */

/* #2 canvas chrome: zoom, rulers, status */
.canvas-zoom{display:inline-flex;align-items:center;gap:4px}
.zbtn{width:26px;height:24px;border-radius:6px;border:1px solid var(--line);background:var(--surface);
  color:var(--ink);cursor:pointer;font-size:.78rem;display:inline-flex;align-items:center;justify-content:center}
.zbtn:hover{border-color:var(--ac-line);color:var(--ac)}
.zval{min-width:54px;font-weight:700}
.canvas-frame{flex:1;min-height:0;display:grid;grid-template-columns:22px 1fr;grid-template-rows:22px 1fr;
  position:relative}
/* Floating canvas controls: back top-left, X top-right, prev/next at the vertical
   middle of each side.
   --gut is .canvas-frame's ruler gutter (22px, set on the grid above); --edge is
   the inset from the stage's own edge. Offsetting by the gutter is what keeps
   these ON the artwork instead of on a ruler.
   z-index clears the Fabric canvas, which paints into .canvas-stage. */
.canvas-frame{--gut:22px;--edge:10px}
.canvas-fab{position:absolute;z-index:6;
  width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;
  border-radius:9px;border:1px solid var(--line);background:rgba(var(--bg-rgb),.72);
  backdrop-filter:blur(6px);color:var(--muted);cursor:pointer;font-size:1rem;line-height:1;
  transition:color .12s,border-color .12s,background .12s,opacity .12s}
.canvas-fab:hover:not(:disabled){color:var(--ink);border-color:var(--ac-line);
  background:rgba(var(--bg-rgb),.92)}
.canvas-fab:focus-visible{outline:2px solid var(--ac);outline-offset:2px}
/* Kept in the layout rather than hidden: the arrows must not jump position at the
   first and last file of a folder. */
.canvas-fab:disabled{opacity:.25;cursor:default}
.cf-back{top:calc(var(--gut) + var(--edge));left:calc(var(--gut) + var(--edge))}
.cf-x   {top:calc(var(--gut) + var(--edge));right:var(--edge)}
.cf-prev{top:50%;transform:translateY(-50%);left:calc(var(--gut) + var(--edge))}
.cf-next{top:50%;transform:translateY(-50%);right:var(--edge)}
@media (max-width:900px){.canvas-frame{--edge:6px}.canvas-fab{width:28px;height:28px}}
.canvas-frame>.canvas-stage{grid-column:2;grid-row:2;border-radius:0}
.ruler{background:var(--ink-950)}
.ruler-t{grid-column:2;grid-row:1;border-bottom:1px solid var(--line);
  background-image:repeating-linear-gradient(90deg,var(--line-2) 0 1px,transparent 1px calc(48px * var(--zx,1)))}
.ruler-l{grid-column:1;grid-row:2;border-right:1px solid var(--line);
  background-image:repeating-linear-gradient(0deg,var(--line-2) 0 1px,transparent 1px calc(48px * var(--zx,1)))}
.canvas-status{display:flex;align-items:center;gap:14px;padding:5px 10px;font-size:.72rem;
  color:var(--muted);border-top:1px solid var(--line)}
.canvas-status i{opacity:.7}

/* #3 clip region (audio + video) */
.video-stage{flex:1;display:flex;align-items:center;justify-content:center;background:var(--ink-950);
  border-radius:12px;overflow:hidden;min-height:0}
.video-stage video{max-width:100%;max-height:100%}
.clip-track{position:relative;height:58px;margin-top:8px;background:var(--surface);
  border:1px solid var(--line);border-radius:8px;user-select:none;overflow:hidden}
.cr-ruler{position:absolute;inset:0 0 auto 0;height:16px;border-bottom:1px solid var(--line)}
.cr-tick{position:absolute;top:2px;font-size:.62rem;color:var(--muted);transform:translateX(-50%);white-space:nowrap}
.cr-tick:first-child{transform:none}
.cr-region{position:absolute;top:16px;bottom:0;background:var(--ac-soft);
  border-left:2px solid var(--ac);border-right:2px solid var(--ac)}
.cr-h{position:absolute;top:0;bottom:0;width:12px;cursor:ew-resize;background:var(--ac);opacity:.85}
.cr-h-l{left:-2px}.cr-h-r{right:-2px}
.cr-h:hover{opacity:1}
.cr-head{position:absolute;top:16px;bottom:0;width:2px;background:var(--paper);pointer-events:none}
/* A seekable track says so: the whole strip is a target, the handles keep
   their own resize cursor, and the playhead must not eat the click. */
.cr-seekable{cursor:pointer}
.cr-seekable .cr-region{cursor:pointer}
.cr-seekable .cr-h{cursor:ew-resize}
.cr-seekable .cr-head{width:3px;box-shadow:0 0 0 1px rgba(0,0,0,.45)}
/* ---- THE A-B SELECTION -----------------------------------------------------
   Raj, 2026-09-03: the cutting UI "is currently not intuitive… you just move a
   special a<->b seek selection that appears on seekbar when you open cut."

   So on a track that is attached DISARMED (the clip editor's) there is no
   selection until a panel asks for one, and the track is a plain seek bar until
   then. The sound editor attaches armed, as it always did, and is unaffected.

   The handles carry their letters. A 12px bar with no label is not something a
   user can be told to move; "drag A and B" is. */
.cr:not(.cr-armed) .cr-region{display:none}
/* The selected range has to READ as selected against an unselected track — the
   fill alone is a shade nobody notices on a 42px strip. */
.cr-armed .cr-region{background:var(--ac-soft);box-shadow:inset 0 0 0 1px var(--ac)}
.cr-h::after{content:attr(data-ab);position:absolute;top:3px;left:50%;transform:translateX(-50%);
  font-size:.6rem;font-weight:800;color:var(--on-brand);letter-spacing:0;pointer-events:none}
.cr-armed .cr-h{width:14px;box-shadow:0 0 0 1px rgba(0,0,0,.35)}
.cr-armed .cr-h-l{left:-3px}.cr-armed .cr-h-r{right:-3px}
/* The clip track while a panel owns the selection: the strip says it is in a
   mode, so the handles are not mistaken for part of the ruler. */
.clip-track.cr-armed{border-color:var(--ac-line,var(--ac))}

/* ---- THE BEAT LANE ---------------------------------------------------------
   The shot's animation beats on the same axis as the footage they were written
   for, so "did beat 3 deliver what beat 3 asked for" is a thing you look at.

   It sits BETWEEN the ruler and the region, not over them: the A-B selection has
   to stay readable while the beats are shown, because picking a beat is how the
   selection gets armed in the first place (studio.js `pickBeat`). 14px leaves
   the region its full height on a 58px track.

   A track with no beats keeps the geometry it always had — `cr-has-beats` is
   what pushes the region down, so a clip with no board behind it is unchanged. */
.cr-beats{position:absolute;top:16px;left:0;right:0;height:14px;pointer-events:none}
.cr-has-beats .cr-beats{pointer-events:auto}
.cr-has-beats .cr-region,
.cr-has-beats .cr-head{top:30px}
.cr-beat{position:absolute;top:0;height:14px;box-sizing:border-box;
  background:var(--surface-2);border:1px solid var(--line);
  border-radius:3px;font-size:.58rem;line-height:12px;text-align:center;
  color:var(--muted);cursor:pointer;overflow:hidden;white-space:nowrap}
.cr-beat:hover{border-color:var(--ac);color:var(--ink)}
/* THE ACTIVE BEAT, which is the whole point of the lane. It has to be legible
   at a glance while the clip plays — a border change is not enough at 14px. */
.cr-beat.is-on{background:var(--ac);border-color:var(--ac);color:var(--on-brand);font-weight:700}
/* A beat that begins on a hard cut is a different KIND of boundary from a beat
   that continues the take, and the composer already treats them differently
   ([Shot N] versus a ranged label). The lane says so too. */
.cr-beat-cut{border-left:2px solid var(--warn)}
/* A beat whose span runs past the end of the clip: the shot was re-rendered
   shorter than it was planned. Drawn clamped and marked rather than hidden, so
   the board cannot disagree with the footage in silence. */
.cr-beat-over{border-right:2px dashed var(--warn)}

/* THE SHOT LIST, under the clip track — Filmera's layout, our data.
   It replaced a single grey line describing only the active beat: the lane can
   carry a number at 14px and nothing else, and "did it do that" is a question
   about the whole sheet. A row per beat, the row under the playhead lit, click
   a row to seek — the same control as clicking its block in the lane.

   THE QUIET IS THE DESIGN. Only ACTION is at full contrast; the number, the
   duration, the framing and the sound are all muted, so a sheet of five rows
   reads as five actions with their details beside them rather than as a wall.
   That is what makes the lit row visible without shouting. */
/* A BOARD MEMBER THAT WILL NOT BE SENT. It is in the board because its story is
   — a credits page skipped in Art still happened between the two panels either
   side of it — so it is drawn, greyed, and says why rather than vanishing. A
   panel that disappears between the grouping and the reference strip is a panel
   nobody can tell was considered. */
.rd-ref.is-out img{filter:grayscale(1);opacity:.4}
.rd-ref.is-out figcaption{color:var(--muted);opacity:.75}
.rd-ref-out{font-style:italic;text-transform:none;letter-spacing:0}

.cr-sheet{margin:8px 0 0;border:1px solid var(--line);border-radius:11px;
  background:var(--surface-2);overflow:hidden}
.cr-sheet-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;
  padding:8px 12px;border-bottom:1px solid var(--line);font-size:.72rem;color:var(--muted)}
.cr-kicker{font-size:.58rem;letter-spacing:.14em;text-transform:uppercase;color:var(--ac);
  font-weight:800}
.cr-sheet-head b{color:var(--ink);font-weight:700}
.cr-sheet-dur{font-family:var(--mono,ui-monospace,SFMono-Regular,Menlo,monospace)}
/* The two fields that are one value for the WHOLE clip, said once. Per-row they
   would be the same string five times; in the rows is where they go when a board
   becomes one generation of several shots (plan.txt Sprint 5.2). */
.cr-sheet-frame{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  flex:1 1 220px}
.cr-sheet-frame i{margin-right:5px;opacity:.7}
.cr-ledger{padding:1px 7px;border-radius:999px;font-size:.62rem;font-weight:700;
  background:var(--ink-800);color:var(--muted);
  border:1px solid var(--line);white-space:nowrap}

/* THREE ROWS TALL, AND IT SCROLLS. A sheet of twelve beats is taller than the
   video it belongs to. What is wanted while something plays is the beat before,
   the one on screen and the one coming — so that is the window, the rest is a
   scroll, and `scrollBeatIntoView` keeps the lit row centred as the clip runs.
   The height itself is set inline by `fitBeatWindow` — three rows is not a
   number CSS can know when an action wraps at one width and not another; the
   value here is the fallback until it has measured. */
.cr-rows{max-height:120px;overflow-y:auto;overscroll-behavior:contain;position:relative}
.cr-rows::-webkit-scrollbar{width:8px}
.cr-rows::-webkit-scrollbar-thumb{background:var(--line);border-radius:4px}
.cr-row{display:grid;grid-template-columns:26px 40px minmax(110px,1fr) minmax(200px,3fr) minmax(100px,1.2fr);
  gap:10px;align-items:start;padding:7px 12px 7px 9px;font-size:.74rem;color:var(--muted);
  border-left:3px solid transparent;cursor:pointer}
.cr-row + .cr-row{border-top:1px solid color-mix(in srgb, var(--line) 55%, transparent)}
.cr-row:hover{background:color-mix(in srgb, var(--ac) 7%, transparent)}
/* THE HEAD is a row so the columns cannot drift apart. Not clickable. */
.cr-row-head{cursor:default;color:var(--muted);opacity:.62;font-size:.56rem;
  letter-spacing:.12em;text-transform:uppercase;padding-top:9px;padding-bottom:7px}
.cr-row-head:hover{background:none}
/* THE LIT ROW. A left bar in the accent plus a tint — the same pairing the lane
   uses for `.cr-beat.is-on`, so the block and the row read as one thing. */
.cr-row.is-on{background:color-mix(in srgb, var(--ac) 13%, transparent);
  border-left-color:var(--ac)}
.cr-row.is-on .cr-c-n{color:var(--ac);font-weight:800}
/* A beat that opens on a hard cut, marked the way the lane marks it. */
.cr-row.is-cut{border-left-color:var(--warn)}
.cr-row.is-cut.is-on{background:color-mix(in srgb, var(--warn) 13%, transparent)}
.cr-c-n,.cr-c-d{font-family:var(--mono,ui-monospace,SFMono-Regular,Menlo,monospace);
  font-size:.68rem}
.cr-c-d{text-align:right}
.cr-c-s{min-width:0;overflow:hidden;text-overflow:ellipsis}
/* ACTION is the only column at full contrast — it is what the beat asked for. */
.cr-c-a{color:var(--ink);line-height:1.36;min-width:0}
/* FIX CLIP — the same rows as a form.
   THE FIELDS DO NOT LOOK LIKE FIELDS until hovered or focused: a five-row
   sheet that turns into five text boxes is a form, and this is still the shot
   list. Transparent, then a hairline, then the accent when it has the caret. */
.cr-edit{background:transparent;border:1px solid color-mix(in srgb, var(--line) 60%, transparent);
  border-radius:6px;padding:2px 6px;margin:-3px 0 0 -6px;font:inherit;color:var(--ink);resize:none;
  width:100%;overflow:hidden;field-sizing:content;min-height:1.36em;line-height:1.36}
input.cr-edit{color:var(--muted)}
.cr-edit:hover{border-color:var(--line)}
.cr-edit:focus{outline:none;border-color:var(--ac);background:var(--ink-800);color:var(--ink)}
.cr-edit::placeholder{color:color-mix(in srgb, var(--muted) 55%, transparent)}
/* THE TICK sits where the number was — same column, same width. */
.cr-box{font-size:.9rem;color:var(--muted);vertical-align:-1px}
.cr-row.is-sel .cr-box{color:var(--ac)}
.cr-row.is-sel{background:color-mix(in srgb, var(--ac) 6%, transparent)}
/* EDITED wears amber on the left edge, the same edge the lit row uses for the
   accent, so "this is where the clip is" and "this is what I changed" read as
   the same kind of mark on the same rail. */
.cr-row.is-edited{border-left-color:var(--warn)}
.cr-row.is-edited .cr-c-n .cr-box{color:var(--warn)}
.cr-row.is-on.is-edited{border-left-color:var(--ac)}
.cr-sheet.is-fixing .cr-row{cursor:default}
.cr-sheet.is-fixing .cr-c-n,.cr-sheet.is-fixing .cr-c-d,.cr-sheet.is-fixing .cr-c-s{cursor:pointer}
/* THE HEAD IN FIX: icon buttons for undo/redo, what will be sent, the switch. */
.cr-ico{width:28px;padding:0;justify-content:center}
.cr-sheet-sel{font-size:.7rem;color:var(--ink);white-space:nowrap;margin:0 4px}
.cr-sheet-sel.dim{color:var(--muted);font-style:italic}
/* THE POLISH SWITCH. Anchored right, and quiet: one glyph that is lit when the
   pass is on. It is a preference a person sets once, not a control per render,
   so it does not get a label — the title carries what it does. */
.cr-polish{display:inline-flex;align-items:center;justify-content:center;width:26px;height:24px;
  border-radius:7px;border:1px solid transparent;color:color-mix(in srgb, var(--muted) 60%, transparent);
  cursor:pointer;margin-left:2px}
.cr-polish input{position:absolute;opacity:0;width:0;height:0;pointer-events:none}
.cr-polish:hover{border-color:var(--line)}
.cr-polish.on{color:var(--ac);border-color:color-mix(in srgb, var(--ac) 40%, transparent)}
.cr-c-k{min-width:0;line-height:1.36}
/* A THIN BEAT wears its seconds in amber: the span is the problem, not the words. */
.cr-c-d.is-thin{color:var(--warn)}
.cr-c-d.is-thin i{font-size:.6rem;margin-right:3px;vertical-align:1px}

/* A PHONE CANNOT HOLD FIVE COLUMNS. Below 760px the row becomes two lines —
   number, duration and action on the first, framing and sound indented under
   it — which keeps every field rather than hiding the two that are hardest to
   fit. The head row goes, because it would be labelling a shape it no longer
   describes. */
@media (max-width:760px){
  .editor{grid-template-columns:1fr;height:auto;min-height:calc(100vh - 56px)}
  .sidebar{border-left:none;border-top:1px solid var(--line);min-height:420px}
  .wrap{padding:0 15px}
  .nav{gap:12px}.nav a.link{font-size:.78rem}.brand small{display:none}
  .pagehead{padding-top:24px}
}

/* Shotink visual system: neutral black editorial cockpit */
:root{
  --ink-950:#070d1b; --ink-900:#0c1526; --ink-850:#111d31; --ink-800:#172640;
  --ink-700:#223554; --ink-600:#2c4162;
  --silver-100:#f2f4f7; --silver-200:#d7dee8; --silver-300:#aeb9ca; --silver-400:#8290a5;
  --blue-300:#9ebbe5; --blue-400:#6f94ca; --blue-500:#4e73a9; --blue-600:#385987;
  --bg:var(--ink-900); --surface:var(--ink-850); --surface-2:var(--ink-800); --surface-3:var(--ink-700);
  --line:rgba(196,211,232,.13); --line-strong:rgba(196,211,232,.24);
  --ink:var(--silver-100); --muted:var(--silver-400); --brand:var(--blue-300); --brand-2:var(--blue-500);
  --accent:var(--silver-200); --gold:var(--silver-300); --paper:var(--silver-100); --paper-2:var(--silver-200); --paper-dim:var(--silver-400);
  --bg-rgb:12,21,38; --brand-rgb:158,187,229; --accent-rgb:215,222,232; --paper-rgb:242,244,247;
  --font-disp:'Cormorant Garamond', Georgia, serif; --font-body:'DM Sans', system-ui, sans-serif;
  --radius:14px; --radius-sm:10px; --radius-lg:22px;
  --shadow:0 24px 80px -34px rgba(0,0,0,.85); --shadow-sm:0 10px 28px -18px rgba(0,0,0,.9);
  --ring:0 0 0 3px rgba(158,187,229,.24);
}
body{background:radial-gradient(900px 520px at 78% -12%,rgba(78,115,169,.16),transparent 68%),var(--bg);letter-spacing:.005em}
body:before{content:"";position:fixed;inset:0;pointer-events:none;opacity:.16;background-image:linear-gradient(rgba(255,255,255,.025) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.025) 1px,transparent 1px);background-size:56px 56px;mask-image:linear-gradient(to bottom,black,transparent 80%)}
h1,h2,h3,.disp{font-weight:500;letter-spacing:-.035em}
.btn{background:linear-gradient(135deg,var(--blue-300),var(--blue-500));box-shadow:0 10px 24px -16px rgba(158,187,229,.8);border-radius:10px;transition:transform .25s cubic-bezier(.2,.8,.2,1),box-shadow .25s,filter .25s}
.btn:hover{transform:translateY(-2px);box-shadow:0 16px 32px -16px rgba(158,187,229,.7)}
header{background:rgba(12,21,38,.82);backdrop-filter:blur(18px);border-bottom-color:var(--line-strong)}
.card,.panel,.lp-step{background:linear-gradient(145deg,rgba(23,38,64,.96),rgba(17,29,49,.96));border-color:var(--line);box-shadow:var(--shadow-sm)}
.card{transition:transform .35s cubic-bezier(.2,.8,.2,1),border-color .35s,box-shadow .35s}
.card:hover{transform:translateY(-5px);border-color:rgba(158,187,229,.55);box-shadow:var(--shadow)}
.editor{background:var(--bg)}
.canvas{background:radial-gradient(circle at 1px 1px,rgba(215,222,232,.08) 1px,transparent 0) 0 0/24px 24px,var(--ink-950)}
.sidebar{background:linear-gradient(180deg,#111e33,#0e182a);border-left-color:var(--line-strong)}
.menubar{background:rgba(12,21,38,.9);border-bottom:1px solid var(--line-strong);box-shadow:0 12px 40px -28px #000}
@media (prefers-reduced-motion:no-preference){.card,.btn,.tile,.lp-feature{animation:rise-in .6s both}@keyframes rise-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}}
/* Phones: the app nav keeps every link but drops the words — icons only. */
@media (max-width:820px){.nav{gap:14px}.nav a.link span{display:none}.nav a.link i{font-size:1rem}.brand small{display:none}.proj-badge{max-width:110px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}

/* Stage navigation: a quiet, expandable editorial rail */
.cockpit-sidebar{position:fixed;z-index:20;top:0;bottom:0;left:0;width:248px;padding:28px 18px 22px;background:linear-gradient(180deg,#101d32 0%,#0b1424 100%);border-right:1px solid var(--line-strong);box-shadow:18px 0 60px -50px #000;overflow:auto}
.cockpit-project,.cockpit-nav button,.cockpit-help{display:flex;align-items:center}.cockpit-project small{display:block;color:var(--silver-400);font-size:10px;letter-spacing:.08em;margin-top:3px}.project-avatar{display:grid;place-items:center;border-radius:50%;background:var(--silver-200);color:var(--ink-900);font:600 15px var(--font-disp)}.cockpit-project{gap:11px;padding:12px 10px;margin-bottom:27px;border:1px solid var(--line);border-radius:10px;color:var(--silver-100);text-decoration:none;transition:border-color .2s,background .2s}.cockpit-project:hover{border-color:var(--line-strong);background:rgba(255,255,255,.04)}.cockpit-project b{font-size:12px}.cockpit-project i{margin-left:auto;color:var(--muted);font-size:11px}.project-avatar{width:30px;height:30px;background:var(--blue-300)}.cockpit-kicker{margin:21px 12px 9px;color:var(--silver-400);font:600 9px var(--font-body);letter-spacing:.18em;text-transform:uppercase}.cockpit-nav{display:grid;gap:3px}.cockpit-nav button,.cockpit-help{width:100%;gap:14px;padding:12px 13px;background:transparent;border:0;border-left:2px solid transparent;color:var(--silver-400);font:500 12px var(--font-body);text-align:left;border-radius:9px;cursor:pointer;transition:color .25s,background .25s,transform .25s}.cockpit-nav button i,.cockpit-help i{width:14px;text-align:center;font-size:11px}.cockpit-nav button:hover,.cockpit-help:hover{color:var(--silver-100);background:rgba(158,187,229,.08);transform:translateX(2px)}.cockpit-nav button.active{color:var(--silver-100);background:rgba(111,148,202,.23);border-left-color:var(--blue-300)}.cockpit-create{padding-bottom:24px;border-bottom:1px solid var(--line)}.cockpit-help{margin-top:16px;text-decoration:none}.studio{padding-left:248px}.studio-main{min-height:calc(100vh - var(--bar-h,48px))}.menubar{left:248px;position:fixed;right:0;top:0;z-index:19}
@media (max-width:760px){.cockpit-sidebar{top:auto;right:0;width:auto;height:68px;padding:8px 12px;display:flex;align-items:center;gap:5px;overflow:hidden;border-right:0;border-top:1px solid var(--line-strong)}.cockpit-project,.cockpit-kicker,.cockpit-help,.cockpit-toggle{display:none}.cockpit-nav{display:flex;width:100%;justify-content:space-around}.cockpit-nav button{width:auto;min-width:44px;justify-content:center;flex-direction:column;gap:4px;padding:8px;color:var(--silver-400);font-size:9px}.cockpit-nav button span{display:block;max-width:55px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cockpit-nav button i{font-size:14px}.studio{padding-left:0;padding-bottom:68px}.menubar{left:0;position:sticky}.studio-main{min-height:calc(100vh - 116px)}}

/* Stage rail: the former bottom stagebar now lives in the cockpit sidebar. */
.cockpit-toggle{width:100%;display:flex;align-items:center;justify-content:center;gap:9px;margin:0 0 18px;padding:11px 12px;border:1px solid var(--line-strong);border-radius:10px;background:rgba(255,255,255,.045);color:var(--silver-200);font:700 10px var(--font-body);letter-spacing:.08em;text-transform:uppercase;cursor:pointer;transition:background .2s,border-color .2s,color .2s}.cockpit-toggle:hover{background:rgba(158,187,229,.13);border-color:rgba(158,187,229,.45);color:var(--silver-100)}
/* Collapsed: the rail is gone, the work area takes its width, and .cockpit-fab
   (a fixed round button, bottom-left, clear of the menubar) brings it back. */
.cockpit-sidebar.collapsed{display:none}
.studio:has(.cockpit-sidebar.collapsed){padding-left:0}.studio:has(.cockpit-sidebar.collapsed) .menubar{left:0}
.cockpit-fab{position:fixed;left:14px;bottom:18px;z-index:21;width:42px;height:42px;border-radius:50%;display:grid;place-items:center;
  background:var(--surface-2);color:var(--ink);border:1px solid var(--line-strong);box-shadow:0 12px 30px -12px rgba(0,0,0,.9);cursor:pointer;font-size:.85rem;
  transition:background .2s,border-color .2s,transform .2s}
.cockpit-fab:hover{background:rgba(199,166,255,.16);border-color:rgba(199,166,255,.5);transform:translateY(-1px)}
.stage-navigation{gap:7px}.stage-sidebar{position:relative!important;display:grid!important;grid-template-columns:24px 24px 1fr;align-items:center!important;min-height:62px;padding:9px 12px!important;border-left:2px solid transparent!important;border-radius:11px!important;background-color:var(--surface-2)!important;background-size:auto 100%!important;background-repeat:no-repeat!important;background-position:right center!important;overflow:hidden;filter:saturate(.65) brightness(.72);text-shadow:0 1px 10px rgba(0,0,0,.7);transition:filter .25s,transform .25s,box-shadow .25s!important}.stage-sidebar::after{content:'';position:absolute;inset:0;background:linear-gradient(90deg,rgba(10,10,12,.88),rgba(10,10,12,.52),rgba(10,10,12,.18));pointer-events:none}.stage-sidebar>*{position:relative;z-index:1}.stage-sidebar:hover{filter:saturate(.95) brightness(.9);transform:translateX(2px)!important}.stage-sidebar.on{filter:none;box-shadow:inset 3px 0 var(--paper),0 8px 18px -14px #000!important}.stage-sidebar .stage-no{color:var(--silver-400)}.stage-sidebar .stage-ic{display:flex;justify-content:center;color:var(--ink)}.stage-sidebar .stage-lb{font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;color:var(--ink);white-space:nowrap}

:root{
  --ink-950:#000000; --ink-900:#050505; --ink-850:#0A0A0B; --ink-800:#101011;
  --ink-700:#171718; --ink-600:#262628;
  --bg:#050505; --surface:#0A0A0B; --surface-2:#101011; --surface-3:#171718;
  --bg-rgb:5,5,5; --paper-rgb:244,244,242;
  --line:rgba(244,244,242,.11); --line-strong:rgba(244,244,242,.20);
  --ink:#F4F4F2; --muted:#858589; --on-brand:#0A0A0B;
  /* The brand is the pastel lavender the buttons and the studio's active
     states already use; the moonlight-blue block above left --brand on blue
     while .btn had moved on, so a link and a button disagreed. Lime stays
     the small accent. Fonts are the ones base.html and landing.html load. */
  --brand:#C7A6FF; --brand-2:#A984E8; --brand-rgb:199,166,255;
  --accent:#C4E996; --accent-rgb:196,233,150;
  --ring:0 0 0 3px rgba(199,166,255,.28);
  --font-disp:'Newsreader',Georgia,'Times New Roman',serif;
  --font-body:'Manrope',system-ui,sans-serif;
  --font-mono:'DM Mono',ui-monospace,'SF Mono',Menlo,Consolas,monospace;
}
body{background:var(--bg)}
.btn{background:linear-gradient(135deg,#D8C0FF,#C7A6FF);box-shadow:0 10px 24px -16px rgba(199,166,255,.55)}
header,.menubar{background:rgba(5,5,5,.90)}
.card,.panel,.lp-step{background:linear-gradient(145deg,#111112,#0A0A0B);border-color:var(--line)}
.cockpit-sidebar{background:linear-gradient(180deg,#0B0B0C 0%,#050505 100%);border-right-color:var(--line-strong)}
.sidebar{background:#0A0A0B}
.cockpit-nav button:hover,.cockpit-help:hover{background:rgba(255,255,255,.055)}
.cockpit-nav button.active{background:rgba(199,166,255,.12);border-left-color:#C7A6FF}
.cockpit-toggle:hover{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.35)}

/* THE STAGE RAIL ON A PHONE. The rail is a bottom bar under 760px (above), but
   the .stage-sidebar rules that follow it are written with !important for the
   desktop rail — 62px tall, three grid columns, a splash image with a gradient
   over it — so on a phone six of those sat in a 68px strip, started 116px in
   because the toggle was still drawn, and ran off the right edge. Same
   specificity, later in the file, so these win. */
@media (max-width:760px){
  .cockpit-toggle{display:none!important}
  .cockpit-sidebar,.cockpit-sidebar.collapsed{width:auto;height:64px;padding:6px 8px;gap:0}
  .cockpit-nav.stage-navigation{display:flex;width:100%;gap:4px;justify-content:space-between}
  /* The same tile as the desktop rail — art, tint and vignette — scaled to a
     59×48 button: the splash is sized to the button's height and anchored
     right, the gradient runs bottom-up instead of left-right so the label
     stays readable over it, and the "on" mark is the bottom edge. */
  .stage-sidebar{display:flex!important;flex:1 1 0;min-width:0;min-height:0;flex-direction:column;justify-content:center;gap:3px;
    padding:6px 2px!important;border:0!important;border-radius:9px!important;transform:none!important}
  .stage-sidebar::after{background:linear-gradient(0deg,rgba(10,10,12,.9),rgba(10,10,12,.45) 60%,rgba(10,10,12,.2))}
  .stage-sidebar:hover{transform:none!important}
  .stage-sidebar .stage-no{display:none}
  .stage-sidebar .stage-ic{font-size:1rem}
  .stage-sidebar .stage-lb{font-size:.56rem;letter-spacing:.02em;max-width:100%;overflow:hidden;text-overflow:ellipsis}
  .stage-sidebar.on{box-shadow:inset 0 -3px var(--paper)!important}
  .cockpit-sidebar.collapsed{display:flex}
  .cockpit-fab{display:none}
}

/* ---- the dashboard (/projects) -------------------------------------------
   Where a login lands. Stats, the active project with a door per stage, the
   project cards, and the asset drawer under them. */
.dash{padding-bottom:80px}
.dash-head{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;padding:34px 0 18px}
.dash-hi{color:var(--muted);font-size:.86rem;margin-bottom:4px}
.dash-head h1{font-size:clamp(1.7rem,3.5vw,2.4rem);letter-spacing:-.03em}
.dash-create-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 16px}
.dash-create-grid .span2{grid-column:1/-1}
.dash-create-grid label{margin-top:6px}
.dash-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:8px 0 26px}
.dash-stats .stat{padding:16px 18px;border:1px solid var(--line);border-radius:14px;background:var(--surface);display:flex;flex-direction:column;gap:4px;min-width:0}
.dash-stats .k{font-size:.66rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.dash-stats b{font-family:var(--font-disp);font-weight:500;font-size:2rem;line-height:1;letter-spacing:-.03em}
.dash-stats b.txt{font-size:1.25rem;font-family:var(--font-body);font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dash-stats small{font-size:.76rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dash-avatar{display:grid;place-items:center;width:44px;height:44px;border-radius:12px;font:600 1.2rem var(--font-disp);color:var(--on-brand);
  background:linear-gradient(135deg,#D8C0FF,#C7A6FF);flex:0 0 auto}
.dash-avatar.big{width:56px;height:56px;font-size:1.5rem;border-radius:14px}
.dash-continue{border:1px solid rgba(var(--brand-rgb),.35);border-radius:16px;padding:20px;margin-bottom:30px;
  background:linear-gradient(145deg,rgba(var(--brand-rgb),.10),rgba(255,255,255,.02))}
.dash-cont-head{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
.dash-cont-head h2{font-size:1.4rem;margin:2px 0}
.dash-cont-head p{font-size:.86rem}
.dash-stages{display:grid;grid-template-columns:repeat(6,1fr);gap:8px;margin-top:18px}
.dash-stage{display:flex;flex-direction:column;gap:4px;padding:12px 12px 11px;border:1px solid var(--line);border-radius:11px;background:rgba(0,0,0,.25);
  color:var(--ink);text-decoration:none;min-width:0;border-top:2px solid var(--tint,var(--brand));transition:transform .18s,border-color .18s,background .18s}
.dash-stage:hover{transform:translateY(-2px);background:rgba(255,255,255,.05);border-color:var(--line-strong);border-top-color:var(--tint,var(--brand))}
.dash-stage .no{font:500 .62rem var(--font-mono);color:var(--muted);letter-spacing:.1em}
.dash-stage i{color:var(--tint,var(--brand));font-size:.9rem}
.dash-stage b{font-size:.88rem}
.dash-stage small{font-size:.7rem;color:var(--muted);line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.dash-sec-head{display:flex;align-items:baseline;justify-content:space-between;margin:0 0 12px}
.dash-sec-head h2{font-size:1.2rem}
.dash-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px}
.dash-card{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:14px;background:var(--surface);overflow:hidden;
  transition:transform .2s,border-color .18s,box-shadow .2s}
.dash-card:hover{transform:translateY(-2px);border-color:var(--line-strong);box-shadow:0 16px 36px -24px rgba(0,0,0,.85)}
.dash-card.active{border-color:rgba(var(--brand-rgb),.5)}
.dash-cover{position:relative;height:96px;display:flex;align-items:center;justify-content:center;cursor:pointer;
  background:radial-gradient(28rem 10rem at 80% -40%,rgba(var(--brand-rgb),.28),transparent 60%),var(--surface-2);border-bottom:1px solid var(--line)}
.dash-cover .proj-badge{position:absolute;top:10px;right:10px}
.dash-body{padding:14px 16px 8px;flex:1}
.dash-body h3{font-size:1.02rem;margin-bottom:3px}
.dash-body .note{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.dash-meta{display:flex;gap:12px;flex-wrap:wrap;margin-top:10px;font-size:.74rem;color:var(--muted)}
.dash-meta i{margin-right:3px}
.dash-bar{height:5px;background:var(--surface-3);border-radius:3px;overflow:hidden;margin-top:10px}
.dash-bar div{height:100%;background:var(--brand)}
.dash-actions{display:flex;gap:6px;align-items:center;padding:10px 12px 12px;border-top:1px solid var(--line)}
.dash-actions .btn.icon{padding:6px 9px}
.dash-actions .btn.ghost.on{border-color:var(--brand);color:var(--brand)}
.dash-actions .danger-text:hover{color:var(--danger);border-color:var(--danger)}
.dash-card.new{align-items:center;justify-content:center;gap:8px;padding:28px 20px;min-height:220px;border-style:dashed;background:transparent;cursor:pointer;
  color:var(--muted);font:inherit;text-align:center}
.dash-card.new i{font-size:1.4rem;color:var(--brand)}
.dash-card.new b{color:var(--ink);font-size:1rem}
.dash-card.new span{font-size:.8rem;max-width:26ch}
.dash-card.new:hover{border-color:rgba(var(--brand-rgb),.5);background:rgba(var(--brand-rgb),.05)}
.modal-overlay>.modal.dash-assets{display:flex;flex-direction:column;max-height:min(84vh,900px)}
.dash-assets-body{flex:1 1 auto;min-height:0;overflow:auto;margin-top:4px;padding-right:4px}
.dash-create.modal{max-width:560px}
@media (max-width:900px){.dash-stats{grid-template-columns:1fr 1fr}.dash-stages{grid-template-columns:repeat(3,1fr)}}
@media (max-width:560px){.dash-stats{grid-template-columns:1fr 1fr}.dash-stages{grid-template-columns:1fr 1fr}.dash-create-grid{grid-template-columns:1fr}
  .dash-head{flex-direction:column;align-items:flex-start}.dash-cont-head .btn{width:100%;justify-content:center}}

/* ---- account pages (/billing, /settings) ---- */
.acct{padding-bottom:80px}
.acct-head{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;padding:34px 0 18px}
.acct-head h1{font-size:clamp(1.7rem,3.5vw,2.4rem);letter-spacing:-.03em}
.acct-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.acct-grid .panel{margin:0}
.acct-grid h2{margin:2px 0 6px}
.acct-plan h2{font-size:1.5rem}
.acct-balance{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:16px}
.acct-balance>div{padding:12px 14px;border:1px solid var(--line);border-radius:11px;background:rgba(255,255,255,.025);display:flex;flex-direction:column;gap:2px}
.acct-balance .k{font-size:.64rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.acct-balance b{font-family:var(--font-disp);font-weight:500;font-size:1.7rem;line-height:1.1}
.acct-balance small{font-size:.72rem;color:var(--muted)}
.acct-meter{margin-top:14px}.acct .spread.sm{font-size:.8rem}
.acct-rows{display:grid;gap:0;margin-top:12px}
.acct-rows>*{padding:11px 0;border-top:1px solid var(--line);font-size:.9rem}
.acct-rows .link-row{color:var(--ink)}.acct-rows .link-row i{color:var(--muted)}.acct-rows .link-row:hover i{color:var(--brand)}
.acct-danger{border-color:rgba(var(--danger-rgb),.35)}
.acct-plans{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
.acct .pr-plan{position:relative;display:flex;flex-direction:column;padding:24px 22px;border:1px solid var(--line);border-radius:16px;background:var(--surface)}
.acct .pr-plan.hot{border-color:rgba(var(--brand-rgb),.6)}
.acct .pr-plan .pop{position:absolute;top:-11px;left:22px;font-size:.62rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--on-brand);background:linear-gradient(135deg,#D8C0FF,var(--brand));padding:4px 10px;border-radius:6px}
.acct .pr-plan h3{font-size:1.2rem}
.acct .pr-plan .for{font-size:.84rem;color:var(--muted);margin:4px 0 14px}
.acct .pr-plan .price{display:flex;align-items:baseline;gap:6px}
.acct .pr-plan .price b{font-family:var(--font-disp);font-weight:500;font-size:2.4rem;letter-spacing:-.04em;line-height:1}
.acct .pr-plan .price span{color:var(--muted);font-size:.86rem}
.acct .pr-plan .credits{margin:12px 0 4px;font-weight:700}
.acct .pr-plan .credits small{display:block;font-weight:500;color:var(--muted);font-size:.78rem;margin-top:2px}
.acct .pr-plan ul{list-style:none;margin:14px 0 18px;display:grid;gap:8px;font-size:.86rem;color:var(--paper-2)}
.acct .pr-plan li{display:flex;gap:9px;align-items:flex-start}.acct .pr-plan li i{color:var(--accent);font-size:.7rem;margin-top:5px}
.acct .pr-plan .btn{margin-top:auto;justify-content:center}
.acct .pr-plan .topup{margin-top:10px;text-align:center;font-size:.74rem;color:var(--muted)}
.acct .pr-note{margin-top:16px;font-size:.84rem;color:var(--muted)}
@media (max-width:900px){.acct-grid,.acct-plans{grid-template-columns:1fr}.acct-balance{grid-template-columns:1fr 1fr 1fr}}
@media (max-width:560px){.acct-balance{grid-template-columns:1fr}.acct-head{flex-direction:column;align-items:flex-start}}

/* ---- the Cut panel ---------------------------------------------------------
   The numbers are the point of it: "4.2s out, 5.2s left" is what somebody
   cutting a nine-second clip is actually asking, and before this panel it was
   nowhere on screen. */
.cut-fig{margin:10px 0;padding:10px 11px;border-radius:11px;
  background:var(--surface-2);border:1px solid var(--line)}
.cut-fig-row{display:flex;align-items:center;gap:8px;font-size:.95rem}
.cut-ab{display:inline-flex;align-items:center;justify-content:center;
  min-width:19px;height:19px;border-radius:6px;background:var(--ac);color:var(--on-brand);
  font-size:.66rem;font-weight:800}
.cut-sum{margin:7px 0 0;font-size:.82rem;color:var(--ink)}
.cut-why{margin:8px 0}
.cut-cancel{width:100%;margin-top:8px;justify-content:center}
/* The Fix panel's scope pair: two buttons, one of which is always on, so the
   answer is readable without reading the paragraph under it. */
.fx-scope{margin-bottom:4px}
.fx-scope .mini{flex:1;justify-content:center}
.fx-scope .mini.on{background:var(--ac);color:var(--on-brand);border-color:var(--ac)}
/* ---- the beat editor, inside Fix this clip --------------------------------
   One row per animation beat, because one beat is the unit of repair: the block
   you clicked on the track is the row you edit here. The active one is marked
   with the same accent the lane uses, so "that block" and "this box" are visibly
   the same thing. */
.fx-beat{border:1px solid var(--line);border-radius:6px;padding:6px;margin-bottom:6px;
  background:var(--surface)}
.fx-beat.on{border-color:var(--ac);box-shadow:inset 0 0 0 1px var(--ac)}
.fx-beat-head{align-items:center;margin-bottom:4px}
.fx-beat-head b{font-size:.72rem}
.fx-beat-sec{width:64px;padding:2px 6px;font-size:.72rem}
.fx-beat-cut{color:var(--warn);font-size:.7rem}
.fx-beat textarea{margin:0}

/* #4 comparison gallery + variations stepper */
.gal-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:12px}
.gal-cell{background:var(--surface);border:1px solid var(--line);border-radius:12px;overflow:hidden;
  display:flex;flex-direction:column;animation:scaleIn .3s var(--ease-spring) both}
.gal-media{position:relative;aspect-ratio:1;background:var(--ink-950);display:flex;align-items:center;justify-content:center}
.gal-media img,.gal-media video{width:100%;height:100%;object-fit:cover}
.gal-cell figcaption{padding:6px 8px;font-size:.7rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.gal-actions{display:flex;gap:6px;padding:0 8px 8px;align-items:center}
.gal-actions .mini:last-child{margin-left:auto}
.fp-run{display:flex;align-items:center;gap:10px;margin-top:14px}
.fp-run .run{flex:1;margin-top:0}
/* `.fp-vars` retired 2026-08-29 with the variations control it styled. */

/* #5 richer inspector cards */
.wa-grid.rich .cell{display:flex;flex-direction:column;cursor:pointer}
.cell-summary{padding:0 9px 6px;font-size:.72rem;color:var(--muted);line-height:1.45;
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.cell-actions{display:flex;gap:6px;padding:0 9px 9px;align-items:center}
.cell-actions .mini:last-child{margin-left:auto}

/* #6 assistant suggestion chips */
.asst-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.chip{border:1px solid var(--line);background:var(--surface);color:var(--ink);border-radius:999px;
  padding:5px 11px;font-size:.74rem;cursor:pointer;transition:border-color .15s,background .15s}
.chip:hover:not(:disabled){border-color:var(--ac-line);background:var(--ac-soft)}
.chip:disabled{opacity:.5;cursor:default}

/* #7 polish: loading skeletons + smoother cells */
@keyframes shimmer{100%{background-position:-200% 0}}
.cell{transition:border-color .15s,transform .15s}
.cell:hover{border-color:var(--border-strong,var(--line-2))}

/* Hide the app until petite-vue mounts (no {{ }} flash, modal overlays stay
   hidden by default). petite-vue strips [v-cloak] after a successful mount. */
[v-cloak]{display:none !important}
/* Feature-param rows: a real wrapper element per param (petite-vue's v-if/v-for
   cannot sit directly inside a <template>, whose content is a fragment). */
.fld-row{display:flex;flex-direction:column;gap:5px;margin-bottom:12px}
.fld-row .link-mini{align-self:flex-start}

/* petite-vue can't put v-if/v-for directly inside a <template> (its content is a
   fragment -> parentElement is null -> mount crash). These were <template>
   wrappers; a display:contents div is a real element (so directives work) but is
   invisible to layout, so grouping stays visually identical.

   INSIDE A TABLE IT MUST BE A <tbody>, not a div. <table> may only contain
   caption/colgroup/thead/tbody/tfoot/tr, and the HTML parser foster-parents
   anything else — the div is lifted OUT and inserted before the table, taking
   its rows' bindings with it. The admin Users list rendered as a single blank
   row for exactly this reason. .contents applies to both. */
.contents{display:contents}

/* ---- Folder picker (stage -> feature -> files), replaces the dropdown ---- */
.folderpick{position:relative;display:inline-block}
.fp-trigger{display:inline-flex;align-items:center;gap:8px;background:var(--surface);border:1px solid var(--line);
  border-radius:9px;padding:6px 12px;color:var(--ink);cursor:pointer;font-size:.82rem;min-width:190px}
.fp-trigger:hover{border-color:var(--ac-line)}
.fp-trigger span{flex:1;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fp-backdrop{position:fixed;inset:0;z-index:40}
/* The dock variant: the drill expands in place. `.rundock` is `overflow:auto`,
   so an absolutely-positioned `.fp-pop` inside it is clipped to whatever height
   is left below the trigger — which was one card. */
/* THE INPUT PICKER, DRAWN LIKE THE INSPECTOR'S. Raj, 2026-08-29: "The Input
   selector should look and feel like a smaller version of the insp-body,
   fp-levels." So: one card per row at every level, not a 2-up grid that puts
   two 150px cards in a 344px dock and truncates both names; the drawer's own
   card treatment, at the dock's scale.

   Rules below `.fp-inline` only, so the popover form used in bars is untouched. */
.fp-inline .fp-grid{grid-template-columns:1fr;gap:6px}
.fp-inline .fp-card{align-items:center;padding:8px 10px}
.fp-inline .fp-card .fp-tx b{white-space:normal;overflow:visible;text-overflow:clip;line-height:1.3}
.fp-inline .fp-hd{position:sticky;top:0;z-index:1;background:var(--surface-2,var(--surface));
  border-bottom:1px solid var(--line);margin-bottom:6px}
.fp-inline{margin-top:6px;border:1px solid var(--line);border-radius:10px;
  background:var(--surface-2);overflow:hidden}
.fp-inline .fp-hd{padding:8px 10px}
.fp-inline .fp-grid{max-height:44vh;overflow:auto}

/* the picker's breadcrumb, and the run cards' preview strip */
.fp-crumb{opacity:.45;margin:0 3px}
.fp-none{padding:6px 8px}
.fp-card.wide{align-items:flex-start;flex-wrap:wrap}
/* The label wins the width; the strip drops to its own row when the card is
   narrow, rather than truncating "Apply Anime Style 2 · …" down to nothing. */
.fp-card.wide .fp-tx{min-width:130px;flex:1 1 auto}
.fp-card.wide .fp-tx b{white-space:normal;overflow:visible;text-overflow:clip}
.fp-thumbs{display:flex;gap:3px;margin-left:auto;flex:0 0 auto}
.fp-thumbs img{width:34px;height:34px;object-fit:cover;border-radius:5px;
  border:1px solid var(--line);background:var(--ink-950)}
.fp-pop{position:absolute;top:calc(100% + 6px);left:0;z-index:41;width:340px;max-height:62vh;overflow:auto;
  background:var(--surface);border:1px solid var(--border-strong,var(--line-2));border-radius:14px;
  box-shadow:var(--shadow);padding:10px;animation:scaleIn .16s var(--ease-spring) both}
.fp-hd{display:flex;align-items:center;gap:8px;font-size:.7rem;font-weight:700;color:var(--muted);
  text-transform:uppercase;letter-spacing:.04em;padding:2px 4px 9px}
.fp-back{width:24px;height:24px;border-radius:6px;border:1px solid var(--line);background:var(--surface);color:var(--ink);cursor:pointer}
.fp-back:hover{border-color:var(--ac-line);color:var(--ac)}
.fp-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.fp-card{display:flex;align-items:center;gap:10px;text-align:left;background:var(--ink-850);border:1px solid var(--line);
  border-radius:11px;padding:10px;cursor:pointer;transition:border-color .14s,transform .14s}
.fp-card:hover{border-color:var(--ac-line);transform:translateY(-1px)}
.fp-card.on{border-color:var(--ac);background:var(--ac-soft)}
.fp-ic{font-size:1.3rem;flex-shrink:0;width:34px;height:34px;display:flex;align-items:center;justify-content:center;
  background:var(--surface);border-radius:9px}
.fp-tx{display:flex;flex-direction:column;min-width:0}
.fp-tx b{font-size:.82rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fp-tx small{font-size:.68rem;color:var(--muted)}

/* ---- New-script panel (Story) ---- */
.ns-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.ns-actions{display:flex;gap:10px;margin-top:12px;align-items:center}
.ns-actions .run{flex:1;margin-top:0}
.srcsec-hd.glow{border-color:var(--ac);box-shadow:0 0 0 2px var(--ac-soft),0 0 16px var(--ac-soft);color:var(--ac)}
/* The page/panel reading on a Source card. `.cell-summary` is shared with the Art
   inspector; this only gives it a little separation from the filename, since a
   .srccell is a tighter card than an inspector cell. */
.srccell .cell-summary{padding:0 9px 8px;border-top:1px solid var(--line);margin-top:2px;padding-top:6px}
.srcboard.dragging .srccell{cursor:grabbing}
.src-ghost{position:absolute;top:6px;left:34px;min-width:18px;height:18px;padding:0 5px;border-radius:6px;
  display:flex;align-items:center;justify-content:center;font-size:.68rem;font-weight:700;color:var(--muted);
  background:rgba(var(--paper-rgb),.16);border:1px dashed var(--line-2);opacity:.9;pointer-events:none}
.srccell.moved{outline:2px solid var(--ac);outline-offset:1px}
.srccell.moved .src-order{background:var(--ac);color:var(--on-brand)}
.danger-far{margin-right:16px;color:var(--danger);border-color:transparent}
.danger-far:hover{background:rgba(var(--danger-rgb),.12);border-color:var(--danger);color:var(--danger)}

/* ---- Stage guide strip: what to do here + one primary action + Next ---- */
.stage-guide{display:flex;align-items:center;gap:12px;padding:9px 14px;border-bottom:1px solid var(--line);
  background:linear-gradient(180deg,rgba(255,255,255,.015),transparent)}
.sg-blurb{color:var(--muted);font-size:.8rem;display:flex;align-items:center;gap:8px;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sg-blurb i{font-size:.95rem;flex-shrink:0}
.sg-primary{display:inline-flex;align-items:center;gap:7px;background:var(--ac);color:var(--on-brand);border:none;
  border-radius:9px;padding:7px 15px;font-weight:600;font-size:.82rem;cursor:pointer;white-space:nowrap;
  box-shadow:0 2px 12px var(--ac-soft);transition:transform .14s,filter .14s}
.sg-primary:hover{transform:translateY(-1px);filter:brightness(1.07)}
.sg-primary:active{transform:translateY(0)}
.sg-next{display:inline-flex;align-items:center;gap:7px;background:transparent;border:1px solid var(--line-2);
  color:var(--ink);border-radius:9px;padding:7px 13px;font-size:.8rem;font-weight:600;cursor:pointer;
  white-space:nowrap;transition:border-color .14s,color .14s}
.sg-next:hover{border-color:var(--ac);color:var(--ac)}

/* "Adjust" toggle that reveals a stage's advanced tools */
.tool-more{opacity:.62}
.tool-more:hover{opacity:1}
.tool-more .tool-ic{font-size:.9rem}

/* Drill-down: the "All files" back control (replaces the Editor/Inspector toggle) */
.viewtoggle .backbtn{color:var(--ink);font-weight:600}
.viewtoggle .backbtn:hover{color:var(--ac);background:var(--ac-soft)}

/* =========================================================== */
/*  FEATURE BAR — the stage's flow                              */
/*                                                              */
/*  Two groups that must never look alike, because they are not */
/*  alike: REQUIRED steps are a numbered path walked in order,   */
/*  OPTIONAL ones are side-trips the stage finishes without.     */
/*  Before this they were one row of identical pills separated   */
/*  only by `opacity:.62`, which reads as a rendering artefact    */
/*  rather than as "you do not have to run this".                */
/* =========================================================== */
/* Two columns, not one wrapping row: the chips wrap among THEMSELVES while Jobs
   stays pinned to the right edge. A single wrapping flex row put Jobs on its own
   line the moment the chips filled the first one. */
.featbar{display:flex;align-items:flex-start;gap:14px;padding:9px 14px;border-bottom:1px solid var(--line)}
.fb-main{display:flex;align-items:center;gap:14px;flex-wrap:wrap;row-gap:9px;flex:1;min-width:0}
.featbar > .fb-jobs{flex-shrink:0;margin-top:2px}

/* "Step 2 of 3: Inpaint & Outpaint" — says the thing out loud. */
.fb-step{display:inline-flex;align-items:center;gap:7px;font-size:.74rem;font-weight:700;
  letter-spacing:.02em;color:var(--muted);background:var(--surface);border:1px solid var(--line);
  border-radius:999px;padding:5px 12px;white-space:nowrap}
.fb-step i{color:var(--ac);font-size:.9em}
.fb-step.ok{color:var(--ok);border-color:rgba(var(--accent-rgb),.35);background:rgba(var(--accent-rgb),.08)}
.fb-step.ok i{color:var(--ok)}

/* ---- the required path ---- */
.fb-flow{display:flex;align-items:center;gap:0;flex-wrap:wrap;min-width:0}
/* the connector: a real line between consecutive steps, so the eye reads a
   sequence rather than a set. Drawn on the chip so wrapping cannot orphan it. */
.fb-flow .fb-chip.req + .fb-chip.req{margin-left:22px}
.fb-flow .fb-chip.req + .fb-chip.req::before{
  content:"";position:absolute;left:-22px;top:50%;width:22px;height:2px;
  background:linear-gradient(90deg,var(--line-2),var(--ac-line));transform:translateY(-50%)}
.fb-flow .fb-chip.req.is-done + .fb-chip.req::before{background:var(--ok);opacity:.5}

/* THE BOARD'S "WHAT AM I LOOKING AT" LABEL. It replaced the sub-tab BUTTONS in
   Source, Story and Video — the feature chips do that navigation now — but the
   count stayed, because the count is what a reorder board is about. Deliver still
   has real `.srctab` buttons (its Cut tab has no chip), so those rules stay too. */
.srcboard-what{display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:9px;
  background:var(--surface-2);border:1px solid var(--line);font-size:.82rem;font-weight:600}
.srcboard-what i{opacity:.7}
.srcboard-what b{color:var(--ac)}

/* THE CHIP'S ICON. Smaller and quieter than `.fb-no`, which is a different kind
   of mark: the number/tick is functional (where this step sits, whether it has
   run) and the icon identifies the step at a glance. Never let the icon outweigh
   the badge. */
.fb-chip .fb-ic{display:inline-grid;place-items:center;width:19px;height:19px;
  font-size:.82rem;opacity:.62;flex:none}
.fb-chip.is-active .fb-ic,.fb-chip.is-next .fb-ic{opacity:1;color:var(--ac)}
.fb-chip.is-done .fb-ic{color:var(--ok);opacity:.85}
.fb-chip.opt .fb-ic{width:auto;opacity:.75}

.fb-chip{position:relative;display:inline-flex;align-items:center;gap:9px;background:var(--surface-2);
  border:1px solid var(--line-2);color:var(--ink);border-radius:11px;padding:6px 15px 6px 6px;
  font-size:.82rem;font-weight:600;cursor:pointer;white-space:nowrap;
  transition:border-color .16s,transform .16s var(--ease-spring),box-shadow .16s,background .16s}
.fb-chip:hover{border-color:var(--ac-line);transform:translateY(-2px);box-shadow:0 6px 18px -6px rgba(0,0,0,.55)}
.fb-chip:active{transform:translateY(0)}
.fb-nm{line-height:1}

/* the step number — the order, stated. Becomes a tick when the step has run. */
.fb-no{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:8px;
  background:var(--surface-3);color:var(--muted);font-size:.78rem;font-weight:800;flex-shrink:0;
  transition:background .18s,color .18s,transform .18s var(--ease-spring)}

/* DONE — it produced files. Quiet: it is history, not a call to action. */
.fb-chip.is-done{border-color:rgba(var(--accent-rgb),.3);background:rgba(var(--accent-rgb),.05)}
.fb-chip.is-done .fb-no{background:var(--ok);color:var(--on-brand)}
.fb-chip.is-done .fb-nm{color:var(--muted)}

/* NEXT — the one thing to do, and THE ONLY CHIP THAT DRAWS ATTENTION. That is a
   real invariant, kept by studio.js chipState() and not by this file: `next` and
   `active` paint the same amber and `next` also pulses, so if both can be on at
   once you get two lit chips with the louder one being the chip the user did NOT
   click. chipState() returns `next` only while nothing has been picked by hand. */
.fb-chip.is-next{border-color:var(--ac);background:var(--ac-soft);
  box-shadow:0 0 0 1px var(--ac-line),0 6px 20px -8px rgba(var(--brand-rgb),.55)}
.fb-chip.is-next .fb-no{background:var(--ac);color:var(--on-brand)}
.fb-chip.is-next::after{content:"";position:absolute;inset:-1px;border-radius:11px;border:1px solid var(--ac);
  opacity:0;animation:chipPulse 2.6s ease-out infinite;pointer-events:none}
@keyframes chipPulse{0%{opacity:.6;transform:scale(1)}70%,100%{opacity:0;transform:scale(1.06)}}

/* TODO — a later required step. Present, legible, clearly not yet. */
.fb-chip.is-todo{opacity:.78}
.fb-chip.is-todo:hover{opacity:1}

/* ACTIVE — its panel is open right now. */
.fb-chip.is-active{border-color:var(--ac);background:var(--ac-soft);box-shadow:0 0 0 1px var(--ac-line)}
.fb-chip.is-active .fb-no,.fb-chip.is-active .fb-ic{background:var(--ac);color:var(--on-brand)}
.fb-chip.is-active::after{display:none}

/* ---- the optional side-trips ---- */
.fb-opts{display:flex;align-items:center;gap:7px;flex-wrap:wrap;min-width:0;
  padding-left:14px;border-left:1px dashed var(--line-2)}
.fb-optlb{font-size:.64rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase;
  color:var(--faint);cursor:default;user-select:none}
/* Dashed, unnumbered, flat — three independent signals that this is not the path. */
.fb-chip.opt{border-style:dashed;background:transparent;font-weight:500;padding:6px 13px 6px 6px}
.fb-chip.opt .fb-nm{color:var(--muted)}
.fb-chip.opt:hover .fb-nm{color:var(--ink)}
.fb-chip.opt.is-done{border-style:solid}
.fb-ic{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:8px;
  background:rgba(255,255,255,.05);color:var(--muted);font-size:.95rem;flex-shrink:0;transition:background .16s,color .16s}
.fb-chip.opt:hover .fb-ic{background:var(--ac-soft);color:var(--ac)}
.fb-tick{color:var(--ok);font-size:.76em;margin-left:1px}

.fb-jobs{display:inline-flex;align-items:center;gap:6px;background:transparent;border:1px solid var(--line-2);
  color:var(--muted);border-radius:999px;padding:6px 12px;font-size:.78rem;cursor:pointer;position:relative;
  white-space:nowrap;transition:border-color .14s,color .14s}
.fb-jobs:hover{border-color:var(--ac-line);color:var(--ink)}
.fb-jobs.on{color:var(--ac);border-color:var(--ac)}

/* ---- Source pages/panels tabs + move control ---- */
.srctabs{display:inline-flex;background:var(--surface);border:1px solid var(--line-2);border-radius:10px;padding:3px;gap:3px}
.srctab{display:inline-flex;align-items:center;gap:7px;background:transparent;border:none;color:var(--muted);
  padding:6px 15px;border-radius:8px;font-size:.84rem;font-weight:600;cursor:pointer;transition:background .14s,color .14s}
.srctab:hover{color:var(--ink)}
.srctab.on{background:var(--ac-soft);color:var(--ac)}
.srctab b{background:rgba(var(--paper-rgb),.2);border-radius:20px;padding:0 7px;font-size:.72rem;font-weight:700}
.srctab.on b{background:var(--ac);color:var(--on-brand)}
.src-move{position:absolute;bottom:6px;right:6px;z-index:2;background:rgba(var(--bg-rgb),.82);border:1px solid var(--line-2);
  color:var(--ink);border-radius:7px;padding:3px 8px;font-size:.68rem;font-weight:600;cursor:pointer;opacity:0;
  transition:opacity .14s,border-color .14s,color .14s}
.srccell:hover .src-move{opacity:1}
.src-move:hover{border-color:var(--ac);color:var(--ac)}

/* Source tabs glow as drop targets while dragging a card */
.srctab.drop{color:var(--ac);box-shadow:inset 0 0 0 1.5px var(--ac),0 0 12px var(--ac-soft)}

/* Resize handles for the menubar and the rundock */
.rz{position:absolute;z-index:30;background:transparent;transition:background .15s}
.rz:hover,.rz.dragging{background:var(--ac-soft)}
.rz-x{top:0;bottom:0;width:6px;cursor:col-resize}
.rz-y{left:0;right:0;height:6px;cursor:row-resize}
.rz-dock{left:-3px}
.rz-bar{bottom:-3px}

/* The containing block for `.rz-dock` above. IT ALSO OVERRODE THE MOBILE DOCK,
   silently: `@media(max-width:880px)` sets `position:fixed` back at the run-dock
   rules, this line has the same specificity and comes later, so from the day the
   resize handles landed the small-screen dock was `relative` again — a block
   laid out UNDER the work area, running off the bottom of the screen with its
   Run button unreachable. The mobile rules are therefore repeated BELOW this
   line rather than left where they read best. Anything else added here must not
   set `position`. */
.rundock{position:relative}

/* ---- the run dock on a small screen, and the bar above it ----------------
   Placed here, after the line above, for the reason it gives. */
@media(max-width:880px){
  /* ---- the menu bar wraps ------------------------------------------------
     Raj, 2026-09-03: "Flex wrap menubar on mobile."
     It was `nowrap` at a fixed 48px, and its three groups measure 605px — so on
     a 390px phone `Inspector`, `Assistant` and `Sign out` were laid out past the
     right edge with nothing to scroll them into view. Not cramped: unreachable.
     `height` becomes `min-height` because a fixed height is what left a second
     row nowhere to go. */
  .menubar{flex-wrap:wrap;height:auto;min-height:var(--bar-h,48px);
    padding:6px 10px;gap:8px;row-gap:6px;
    /* `.studio` is a fixed-height flex column and the work area under the bar
       is min-height 100vh-48px, so the column overflows and every item
       SHRINKS — the bar back to its 48px min-height, with its second row
       spilling over the work area. Measured 2026-09-17: height:auto computed
       to 48px, scrollHeight 66. A bar that wraps must not shrink. */
    flex:0 0 auto}
  /* The centre group stops centring and stops growing: `flex:1` on a wrapping
     row eats the space the next group needs, pushing it onto a row of its own
     even when both would have fitted. */
  .mb-center{flex:0 1 auto;justify-content:flex-start;flex-wrap:wrap}
  .mb-left,.mb-right{flex-wrap:wrap}
  .mb-right{margin-left:auto}
  /* The history label is a whole row of ellipsis on a phone. */
  .mb-step{max-width:110px}
  /* Dragging the bar's height is meaningless once it sizes itself to its rows,
     and the handle is a full-width strip sitting under the thumb. */
  .rz-bar{display:none}
  /* `--bar-real` is the bar's MEASURED height (studio.js `bindBarHeight`) — what
     it actually ends at once it wraps. `--bar-h` is the height the user dragged
     it to and is only right while it does NOT wrap, so a fixed dock pinned to it
     slid up behind a two-row bar. */
  .rundock{position:fixed;right:0;top:var(--bar-real,var(--bar-h,48px));bottom:60px;
    width:min(92vw,360px);z-index:41;border-left:1px solid var(--line);
    box-shadow:0 20px 60px -20px rgba(0,0,0,.9)}
  /* Collapsed is a ZERO-WIDTH GRID COLUMN on desktop, which is why it is styled
     rather than hidden. Taken out of the grid it is a full-height empty panel
     sitting over the work area instead, so on a small screen it has to go. */
  .rundock.collapsed{display:none}
  /* NO SCRIM, and it was tried. A `.rundock::before` cannot be one: the dock
     carries `animation:fadeSlideUp`, an animated transform makes it the
     containing block for its own fixed descendants, so the scrim covered the
     dock instead of the page — and `z-index:-1` then put it behind the dock's
     own background, where it was invisible. Measured, not guessed: it computed
     to exactly the dock's 390px width. A working one needs an element outside
     the dock, and the sheet is opaque over three quarters of the screen, so it
     is not worth the markup. Anything is better than the dead rule that was
     here, which is the same class of bug as the one this whole block fixes. */
  /* Dragging a fixed overlay wider is meaningless, and the handle sits under the
     thumb that is trying to scroll the panel. */
  .rz-dock{display:none}
  /* THE PRIMARY ACTION MUST NOT BE BELOW THE FOLD. A feature's description runs
     to a paragraph or more, so on first open the Run button sat 14px past the
     bottom of the sheet and behind the stage bar — reachable only by scrolling
     a wall of text you have already read. Pinned to the foot of the scroll box
     it is the one thing always on screen, which is what it is for. */
  .rundock .fp-run{position:sticky;bottom:0;z-index:1;background:var(--surface);
    padding:10px 0 4px;margin-top:8px;
    box-shadow:0 -12px 18px -14px rgba(0,0,0,.95)}
  .rundock .fp-run .run{margin-top:0}
}
@media(max-width:640px){
  /* THE STAGE BAR MUST NEVER TILE EXACTLY. Six stages at `min-width:130px`
     happen to fit a 390px phone THREE to a screen with no remainder — so the row
     looks complete, and a row that looks complete is a row nobody swipes. Sized
     as a fraction of the viewport instead, so a slice of the next stage is
     always showing at any width: 38% gives 2.6 tabs, and 2.6 is the whole point.
     `flex:0 0` because `flex:1` would stretch them back to fill the bar, which
     is the tiling this removes. */
  .stagebar .stage{flex:0 0 38%;min-width:0}

  /* THE HISTORY LABEL GOES, AND WITH IT A WHOLE ROW. The bar's left and centre
     groups measure 135px and 241px, which misses sharing a row on a 390px phone
     by fourteen pixels — and the fourteen are " · Plan Video", a note about the
     last action rather than a control. Dropped, the bar is two rows instead of
     three and the work area gets 30px back. The History BUTTON stays; only its
     trailing annotation goes. */
  .mb-step{display:none}
  /* A BOTTOM SHEET on a phone. 360px of side panel is most of a 390px screen
     with none of the affordances that make a sheet obvious, and it leaves a
     sliver of work area too narrow to read — so it becomes the shape a phone
     already knows, anchored to the bottom with the page still visible above it.
     `.rundock` already animates in with `fadeSlideUp`, which is the motion a
     sheet wants anyway. */
  .rundock{left:0;right:0;width:auto;max-width:none;top:22vh;
    border-left:none;border-top:1px solid var(--line);
    border-radius:16px 16px 0 0}
  /* The grab bar, so the top edge reads as a sheet rather than as a panel that
     happens to start there. */
  .rundock:not(.collapsed)::after{content:"";position:absolute;top:6px;left:50%;
    transform:translateX(-50%);width:36px;height:4px;border-radius:3px;
    background:var(--line)}
  .rd-panel{padding-top:18px}
}

/* Retracted panels stay in the grid (0-width column) so the workarea keeps its
   place — collapsing via display:none would shift the workarea into the rail column. */
.rundock.collapsed{padding:0;border:0;overflow:hidden}

/* menu-bar prep bar — cold-start progress (worker booting / downloading models / loading ComfyUI) */
.mb-prep{display:inline-flex;align-items:center;gap:7px;font-size:.72rem;font-weight:600;color:var(--muted);margin-right:10px;white-space:nowrap}
/* The prep chip narrates a cold-volume bootstrap now, and those sentences are far
   longer than "Provisioning GPU". .mb-prep is nowrap with no bound, so without
   this a 60-character message shoves the sign-out button off the menubar. */
.mb-prep-t{display:inline-block;max-width:34ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:middle}
.mb-prep-bar{display:inline-block;width:64px;height:4px;border-radius:3px;background:var(--surface-2);overflow:hidden;vertical-align:middle}
.mb-prep-bar>i{display:block;height:100%;background:var(--accent);border-radius:3px;transition:width .4s ease}

/* ============ Style Transfer panel ============ */
/* Style panel toolbar — the two verbs and their target, above the swatches. */
.st-bar{display:flex;align-items:center;gap:8px;margin:2px 0 12px;padding:7px 10px;
  background:var(--surface-2);border:1px solid var(--line);border-radius:10px}
.st-target{display:flex;align-items:center;gap:7px;font-size:.76rem;color:var(--muted);min-width:0;flex:1}
.st-target i{color:var(--faint)}
.st-target.ready{color:var(--ink)}
.st-target.ready i{color:var(--ac)}
.st-clear{background:transparent;border:1px solid var(--line-2);color:var(--muted);border-radius:999px;
  padding:3px 10px;font-size:.7rem;font-weight:600;cursor:pointer;white-space:nowrap;transition:color .14s,border-color .14s}
.st-clear:hover{color:var(--danger);border-color:var(--danger)}
.st-new summary{cursor:pointer;font-size:.8rem;font-weight:700;color:var(--muted);list-style:none;padding:2px 0}
.st-new summary::-webkit-details-marker{display:none}
.st-new summary:hover{color:var(--ink)}
.st-new[open] summary{color:var(--ink);margin-bottom:4px}
/* AUTO-FILL, not a hard two columns. The grid now mounts in two containers of
   very different widths — the feature panel and the editor's ~264px dock — and
   at a fixed two columns a card was ~127px wide, which is narrower than a name
   plus its motion badge plus the edit button: the badge (nowrap, margin-left:auto)
   pushed the pencil past the card's `overflow:hidden` and it was clipped away. */
.st-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:10px;margin-bottom:12px}
.st-card{border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--surface-2);display:flex;flex-direction:column}
.st-card.sel{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}
.st-thumb{position:relative;cursor:pointer;aspect-ratio:16/10;background:var(--ink-850)}
.st-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.st-noimg{width:100%;height:100%;display:grid;place-items:center;color:var(--muted);font-size:1.1rem}
.st-check{position:absolute;top:6px;right:6px;width:22px;height:22px;border-radius:50%;background:var(--accent);color:var(--paper);display:grid;place-items:center;font-size:.68rem}
.st-name{display:flex;align-items:center;justify-content:space-between;gap:6px;padding:6px 8px;font-size:.82rem;font-weight:600}
.st-name>span:first-child{min-width:0;overflow-wrap:anywhere}
/* the `motion` grade badge: does this look survive being animated */
.st-motion{position:absolute;left:5px;bottom:5px;font-size:.6rem;font-weight:800;letter-spacing:.04em;
  text-transform:uppercase;padding:1px 5px;border-radius:999px;border:1px solid currentColor;
  background:rgba(0,0,0,.62);cursor:help;white-space:nowrap}
.mo-holds{color:var(--ok)} .mo-risky{color:var(--warn)} .mo-boils{color:var(--danger)}
.st-del{background:none;border:none;color:var(--muted);cursor:pointer;padding:2px;font-size:.78rem}
.st-del:hover{color:var(--danger)}
.st-apply{margin:0 8px 8px}
.st-new{border-top:1px solid var(--line);margin-top:12px;padding-top:12px;display:flex;flex-direction:column;gap:8px}
.st-new input,.st-new textarea{width:100%}
.st-new textarea{min-height:56px;resize:vertical}

/* =========================================================== */
/*  DELIVER — render on the left, take it home underneath       */
/* =========================================================== */
.deliver-side{display:flex;flex-direction:column;gap:12px;min-height:0;overflow:auto}
.deliver-panel h3{display:flex;align-items:center;gap:8px;font-size:.98rem;margin-bottom:6px}
.deliver-panel h3 i{color:var(--ac);font-size:.9em}
.fld-note{display:block;font-weight:400;font-size:.68rem;color:var(--faint);letter-spacing:0}

/* RENDER & EXPORT'S OPTIONS, drawn in two places from one file
   (components/_render_options.html): the Render tab's panel, and the step's own
   run dock. These rules are scoped to neither, so both look the same. */
/* A tick's consequence, said under the tick. `.bad` is the case where the tick
   cannot do anything yet — amber rather than red, because it is a thing to go
   and do, not a failure. */
.ro-note{display:flex;gap:7px;margin-top:6px;line-height:1.45;color:var(--faint)}
.ro-note i{margin-top:2px}
.ro-note.bad{color:var(--warn)}
/* What a ticked option then asks. Indented and ruled, so a control that only
   exists because of the box above it reads as belonging to it. */
.ro-sub{margin:6px 0 2px 10px;padding-left:11px;border-left:2px solid var(--line-2);
  display:flex;flex-direction:column;gap:2px}
/* The dock's copy of the block. `.fp-slot`'s spacing does not apply here — this
   is the whole panel body for this one step, not a row in it. */
.fp-render{display:flex;flex-direction:column;gap:2px;margin:10px 0 4px}

/* Two scopes, each stating its own size, because "download" without a size is
   the difference between a 20MB file and a 40GB one. */
.deliver-panel.dl{padding-bottom:12px}
.dl-btn{display:flex;align-items:center;gap:11px;width:100%;text-align:left;margin-top:8px;
  background:var(--surface-2);border:1px solid var(--line-2);color:var(--ink);border-radius:11px;
  padding:10px 12px;cursor:pointer;
  transition:border-color .16s,transform .16s var(--ease-spring),box-shadow .16s,background .16s}
.dl-btn:hover:not(:disabled){border-color:var(--ac-line);background:var(--surface-3);
  transform:translateY(-2px);box-shadow:0 8px 22px -10px rgba(0,0,0,.7)}
.dl-btn:active:not(:disabled){transform:translateY(0)}
.dl-btn:disabled{opacity:.55;cursor:progress}
.dl-btn.primary{border-color:var(--ac-line);background:var(--ac-soft)}
.dl-ic{display:grid;place-items:center;width:34px;height:34px;border-radius:9px;flex-shrink:0;
  background:rgba(255,255,255,.06);color:var(--muted);font-size:.95rem}
.dl-btn.primary .dl-ic{background:var(--ac);color:var(--on-brand)}
.dl-btn:hover .dl-ic{color:var(--ac)}
.dl-btn.primary:hover .dl-ic{color:var(--paper)}
.dl-tx{display:flex;flex-direction:column;gap:2px;min-width:0}
.dl-tx b{font-size:.85rem;font-weight:700}
.dl-tx small{font-size:.7rem;color:var(--muted);line-height:1.35}
.dl-note{display:flex;gap:7px;margin-top:11px;line-height:1.45;color:var(--faint)}
.dl-note i{margin-top:2px}
.xs{font-size:.68rem}

/* A blank state that says what will appear and why it has not yet. */
.wa-blank{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;
  height:100%;min-height:220px;text-align:center;padding:28px;
  animation:fadeSlideUp .34s var(--ease-spring) both}
.wa-blank-ic{display:grid;place-items:center;width:62px;height:62px;border-radius:16px;margin-bottom:6px;
  background:var(--surface-2);border:1px solid var(--line);color:var(--faint);font-size:1.4rem}
.wa-blank-body{display:contents}   /* see inspector-view.html: a wrapper only petite-vue needs, invisible to the flex column */
.wa-blank p{margin:0;max-width:42ch}
.wa-blank p b{font-size:.95rem}

/* ---- Style family tabs: Animation vs Manga -------------------------------- */
.st-fam{display:flex;align-items:center;gap:6px;margin:2px 0 8px}
.st-fam button{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:7px;
  background:var(--surface-2);border:1px solid var(--line-2);color:var(--muted);
  border-radius:9px;padding:6px 10px;font-size:.78rem;cursor:pointer;
  transition:background .16s,color .16s,border-color .16s}
.st-fam button:hover{color:var(--ink)}
.st-fam button.on{background:var(--ac-soft,var(--surface-3));border-color:var(--ac-line);color:var(--ac)}
.st-count{font-size:.64rem;color:var(--faint);font-variant-numeric:tabular-nums;
  background:var(--bg);border-radius:999px;padding:1px 6px;margin-left:1px}
.st-fam button.on .st-count{color:var(--ac)}
.st-fam-note{margin:0 0 9px;font-size:.72rem;line-height:1.5;color:var(--muted)}

/* ---- Creative direction: the project's held look + tone --------------------
   Framed rather than flat, because it outranks everything under it: the swatches
   preview a style on one panel, this is the one applied to the whole film. */
.cd-box{border:1px solid var(--ac-line);border-radius:12px;background:var(--surface-2);
  padding:11px 13px;margin:0 0 12px}
.cd-head{display:flex;align-items:center;gap:9px}
.cd-head b{font-size:.86rem;white-space:nowrap}
.cd-toggle{margin-left:auto;flex-shrink:0}
.cd-note{color:var(--faint);font-size:.72rem;margin-top:2px}
.cd-sum{margin-top:8px;display:grid;gap:5px}
.cd-sum>div{display:flex;gap:9px;align-items:baseline}
.cd-k{font-size:.6rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;
  color:var(--faint);min-width:38px}
.cd-v{color:var(--ink);font-size:.86rem}
.cd-why{color:var(--muted);font-size:.76rem;font-style:italic;line-height:1.45;display:block}
.cd-edit{margin-top:9px;display:grid;gap:6px}
.cd-edit input[type=text],.cd-edit input:not([type]),.cd-edit textarea,.cd-edit select{width:100%}
.cd-edit textarea{resize:vertical;min-height:64px}
.cd-check{display:flex;align-items:center;gap:7px;cursor:pointer}
.cd-check input{width:auto}
.cd-actions{margin-top:3px}

/* The required/optional tag reused in panel headers. Same two signals the chip
   bar uses — dashed+faint for a side-trip, solid+accent for the core path — so a
   panel header and its chip never disagree about what kind of step this is. */
.rd-tag{font-size:.6rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  border-radius:999px;padding:2px 8px}
.rd-tag.opt{color:var(--faint);border:1px dashed var(--line-2)}
.rd-tag.req{color:var(--ac);border:1px solid var(--ac-line)}

/* =========================================================== */
/*  MOTION — a small, consistent vocabulary                     */
/*                                                              */
/*  Four ideas, used everywhere, so the app feels like one       */
/*  thing: chrome SETTLES in, panels SLIDE from the edge they    */
/*  belong to, lists STAGGER so the eye follows the order, and   */
/*  anything running BREATHES. Durations stay under 340ms —      */
/*  this is a tool people use all day, not a landing page.       */
/* =========================================================== */
@keyframes dockIn{from{opacity:0;transform:translateX(18px)}to{opacity:1;transform:translateX(0)}}
@keyframes popIn{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}
@keyframes breathe{0%,100%{opacity:1}50%{opacity:.55}}

/* the run dock: slides from the right edge it lives on */
.rd-panel{animation:dockIn .26s var(--ease-spring) both}
/* stage sub-tab content + work-area bodies settle in */
.wa-body > .anim,.wa-body > .deliver{
  animation:fadeSlideUp .28s var(--ease-spring) both}

/* chips stagger so the eye reads the flow left-to-right, in order.
   Six covers every stage; a longer bar simply lands together. */
.fb-flow .fb-chip,.fb-opts .fb-chip{animation:popIn .26s var(--ease-spring) both}
.fb-flow .fb-chip:nth-child(2){animation-delay:.05s}
.fb-flow .fb-chip:nth-child(3){animation-delay:.10s}
.fb-flow .fb-chip:nth-child(4){animation-delay:.15s}
.fb-opts .fb-chip:nth-child(2){animation-delay:.04s}
.fb-opts .fb-chip:nth-child(3){animation-delay:.08s}
.fb-opts .fb-chip:nth-child(4){animation-delay:.12s}
.fb-opts .fb-chip:nth-child(5){animation-delay:.16s}
.fb-opts .fb-chip:nth-child(6){animation-delay:.20s}

/* grid cells settle in rather than snapping */
.wa-grid .cell{animation:popIn .3s var(--ease-spring) both}

/* anything the machine is doing breathes, so "working" is never ambiguous */
.mb-prep,.mb-jobs{animation:breathe 2.2s ease-in-out infinite}

/* stage bar: the current stage lifts */
.stagebar .stage{transition:filter .24s,color .24s,box-shadow .24s,transform .24s var(--ease-spring)}
.stagebar .stage:hover{transform:translateY(-2px)}
.stagebar .stage.on{transform:translateY(-2px)}

/* focus that is visible without being loud — keyboard users get a real ring */
.fb-chip:focus-visible,.dl-btn:focus-visible,.stagebar .stage:focus-visible,
.mb-btn:focus-visible,.run:focus-visible,.mini:focus-visible{
  outline:2px solid var(--ac);outline-offset:2px}

/* =========================================================== */
/*  Respect the OS setting. Motion is decoration here — every    */
/*  state it expresses is also expressed by colour, a number or  */
/*  an icon, so turning it all off loses nothing.                */
/* =========================================================== */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;scroll-behavior:auto!important}
  .fb-chip:hover,.dl-btn:hover,.stagebar .stage:hover,.stagebar .stage.on{transform:none}
}

/* Search sits at a readable measure instead of spanning the whole canvas —
   a 1900px-wide input for a few words read as an unfinished layout. */
.inspview .search-box{max-width:460px}
.wa-blank-cta{margin-top:14px;width:auto;padding:10px 20px;
  display:inline-flex;align-items:center;gap:9px}

/* ---- THE INSPECTOR SHEET ---------------------------------------------------
   Over the work area, never instead of it. It is the LAST overlay in this app:
   the editor was the other one and became a work-area view on 2026-08-29, so
   opening a file from the drawer now swaps what is underneath rather than
   stacking a second veil on top. z-index 58 has nothing above it any more and is
   kept so it clears the menubar and the stage bar. */
.insp-veil{position:fixed;inset:0;z-index:58;display:flex;align-items:center;
  justify-content:center;padding:clamp(12px,3vh,40px);
  background:rgba(var(--bg-rgb),.55);backdrop-filter:blur(3px);animation:veil-in .12s ease-out}
.insp-drawer{width:min(1100px,94vw);height:min(88vh,900px);display:flex;flex-direction:column;
  overflow:hidden;border-radius:14px;background:var(--surface);border:1px solid var(--line);
  box-shadow:0 24px 70px rgba(0,0,0,.55);animation:sheet-in .14s cubic-bezier(.2,.8,.3,1)}
.insp-head{display:flex;align-items:center;gap:10px;padding:12px 16px;
  border-bottom:1px solid var(--line);font-size:.86rem}
.insp-bar{padding:10px 16px;border-bottom:1px solid var(--line)}
.insp-crumb{display:flex;align-items:center;gap:10px;font-size:.84rem}
/* With no trigger the levels are the content: let them breathe and scroll with
   the sheet rather than inside a 44vh box meant for a popover. */
.insp-body .fp-bare .fp-inline{margin-top:0;border:none;background:transparent}
.insp-body .fp-bare .fp-hd{padding:2px 2px 10px;font-size:.9rem}
/* The picker's own grid is sized for a 340px popover. In a 1100px sheet it has
   the room to lay the cards out properly, so let it. */
.insp-body .fp-bare .fp-grid{max-height:none;overflow:visible;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:10px}
.insp-body .fp-bare .fp-card{width:auto}
.insp-body{flex:1;min-height:0;overflow:auto;padding:14px 16px}
.insp-body .wa-grid{grid-template-columns:repeat(auto-fill,minmax(156px,1fr))}
@media (max-width: 720px){
  .insp-veil{padding:0}
  .insp-drawer{width:100vw;height:100vh;border-radius:0;border:0}
}

/* ---- the shared style picker ----------------------------------------------
   One grid, two homes: the Apply Anime Style feature panel and the editor's Try
   a Style tool. `.st-body` is the wrapper both mount, so the swatches are the
   same size and the same shape wherever you meet them. */
.st-body{display:flex;flex-direction:column;gap:8px}
.fp-style{display:flex;flex-direction:column;gap:6px;margin:10px 0 4px;
  padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}
/* A sentence, not a row. As a flex container the `<b>` holding the style's name
   became a flex item and wrapped on its own — "Anime / Cel" over two lines with
   the rest of the sentence beside it. */
.fp-style-cur{display:block;margin:0;line-height:1.45}
.fp-style-cur i{margin-right:6px;color:var(--muted)}
/* Edit sits beside Delete on the card's name row; on a built-in it is the only
   one there, because a catalog style cannot be deleted, only copied. */
.st-edit,.st-del{width:20px;height:20px;flex:0 0 auto;display:inline-flex;align-items:center;
  justify-content:center;border:none;background:transparent;color:var(--faint);
  cursor:pointer;border-radius:5px;font-size:.62rem}
.st-edit:hover{color:var(--ac);background:var(--ac-soft)}
.st-del:hover{color:var(--danger);background:rgba(var(--danger-rgb),.12)}
/* ONE FORM for new / edit / customise. It replaces the swatch grid's `<details>`
   creator, which could only ever make a style and never correct one. */
.st-form{display:flex;flex-direction:column;gap:6px;padding:10px;
  border:1px solid var(--ac-line);border-radius:10px;background:var(--surface)}
.st-form-hd{display:flex;align-items:center;font-size:.8rem}
.st-form-hd b{flex:1}
.st-add{align-self:flex-start}

/* ---- a file's own edit history --------------------------------------------
   Anchored to the button that opens it, in whichever editor toolbar that is.
   Drawn with `history-window.html`'s own classes (.hw-head/.hw-list/.hw-item)
   so the two lists are visibly the same idea at two scales — the only thing
   this rule adds is where it hangs and how wide it is. */
.eh-anchor{position:relative;display:inline-flex}
/* WHICH EDGE IT HANGS FROM depends on where its button is. The canvas bar's sits
   at the right, so the panel opens leftward; the script bar's is third from the
   left, and right-anchoring put a 320px panel off the left edge of a sheet that
   clips its overflow — half the list was simply not on screen. */
.eh-anchor.left .ed-hist{left:0;right:auto}
.ed-hist{position:absolute;z-index:24;top:calc(100% + 6px);right:0;
  width:min(320px,80vw);display:flex;flex-direction:column;max-height:340px;
  background:var(--surface);border:1px solid var(--line);border-radius:12px;
  box-shadow:0 18px 44px rgba(0,0,0,.5);overflow:hidden;
  animation:sheet-in .12s cubic-bezier(.2,.8,.3,1)}
.ed-hist .hw-list{flex:1;min-height:0;overflow:auto}
.eh-note{padding:8px 12px;border-top:1px solid var(--line);line-height:1.45;margin:0}
.canvas-bar .mini.on{border-color:var(--ac-line);color:var(--ink)}

/* THE FILE'S UNDO / REDO / HISTORY, DRAWN AS THE MENUBAR'S.
   Raj, 2026-08-30: "it's currently a mini ghost button, but I will prefer if it
   looks like menubar's history button (both do the same thing), just that ed-hist
   is file specific." So they ARE `.mb-btn` — the class, not a copy of it, which
   is the only way the two stay identical when one of them is restyled.
   All that is added here is the bar's own line, because a toolbar sitting on a
   panel needs an edge the menubar (which is the top of the window) does not. */
.canvas-bar .mb-btn,.srt-bar .mb-btn{border-color:var(--line);border-radius:9px}
.canvas-bar .mb-btn:hover:not(:disabled),.srt-bar .mb-btn:hover:not(:disabled){
  border-color:var(--brand)}
/* The "Writing…" / "Saved" word beside them. `flex:0 0 auto` because both bars
   are flex rows that will happily crush a trailing span to nothing — which read
   as "Savea" until this was added. */
.canvas-bar .mb-step,.srt-bar .mb-step{color:var(--muted);font-size:.78rem;
  display:inline-flex;align-items:center;gap:5px;flex:0 0 auto}

/* ============ THE EDITOR SHEET, AND ITS OWN DOCK ============
   Editing one file used to replace the whole work area, which meant leaving the
   folder you were working through. The editor is a sheet over the stage: the
   boards stay mounted underneath with their files and scroll position, and
   dismissing the sheet is the whole of "going back".

   It spent a few hours rendered inside `.wa-body` instead, so its tools could
   reach the app's run dock. That read worse — Raj, 2026-08-29: "I feel the editor
   pop-up looked better" — and the tools got a better answer out of the detour:
   the editor has its own dock now, beside the canvas, instead of borrowing the
   stage's. `.wa-views` and `.wa-body.editing` went with the detour. */
.editor-veil{position:fixed;inset:0;z-index:60;display:flex;align-items:center;
  justify-content:center;padding:clamp(12px,3vh,40px);
  background:rgba(var(--bg-rgb),.62);backdrop-filter:blur(3px);animation:veil-in .12s ease-out}
.editor-sheet{width:min(1500px,96vw);height:min(92vh,1000px);display:flex;flex-direction:column;
  overflow:hidden;border-radius:14px;background:var(--surface);
  border:1px solid var(--line);box-shadow:0 24px 70px rgba(0,0,0,.55);
  animation:sheet-in .14s cubic-bezier(.2,.8,.3,1)}
/* The editor bodies were written to fill `.wa-body`; inside the sheet they fill
   the sheet instead. Their trailing helper line was flush against `.wa-body`'s
   own padding and inside the sheet there is none, so it ran into the corner. */
.editor-sheet > .canvaswrap > p.muted,
.editor-sheet > .docwrap > p.muted{padding:0 14px 12px;margin:6px 0 0}
.editor-sheet > .canvaswrap,
.editor-sheet > .docwrap{flex:1;min-height:0;display:flex;flex-direction:column}

/* VIEW, not edit — the word beside the filename when a feature's output is
   read-only. Quiet: it is a statement of fact, not a warning. */
.canvas-trim{display:inline-flex;align-items:center;gap:6px;font-size:.72rem;
  color:var(--ac);white-space:nowrap}
.canvas-ro{display:inline-flex;align-items:center;gap:5px;padding:2px 8px;
  border:1px solid var(--line);border-radius:999px;
  font-size:.68rem;color:var(--muted);white-space:nowrap}
/* The clip editor reuses the image editor's frame so the transport lands in the
   same places, minus the rulers — a video has no pixel gutter to measure. */
.canvas-frame.vid{grid-template-columns:1fr;grid-template-rows:1fr;--gut:0px}
.canvas-frame.vid>.canvas-stage{grid-column:1;grid-row:1}
.canvas-stage.vid{padding:0}
.canvas-stage.vid .video-stage{width:100%;height:100%}
.canvas-stage.vid video{cursor:pointer}
/* Play sits bottom-centre, where a transport control belongs and where it cannot
   be confused with the four corner/edge buttons that navigate. */
.cf-play{bottom:var(--edge);left:50%;transform:translateX(-50%);
  width:46px;height:46px;font-size:1rem}

/* THE STAGE AND ITS DOCK, side by side. The row is what makes the aside possible
   at all: the canvas frame is a grid Fabric paints into and the dock must not be
   inside it. */
.canvas-work{flex:1;min-height:0;display:flex;gap:10px;min-width:0}
.canvas-work > .canvas-frame{flex:1;min-width:0}

/* THE EDITOR'S RUN DOCK. Narrower than the app's (344px) because it holds one
   tool's options rather than a whole feature's parameter form, and because the
   thing it sits beside is a picture being judged — the canvas gets the room. */
.ed-dock{flex:0 0 296px;max-width:44%;display:flex;flex-direction:column;min-height:0;
  border:1px solid var(--line);border-radius:12px;background:var(--surface-2,var(--surface));
  overflow:hidden;animation:dockIn .22s var(--ease-spring) both}
/* Fix this clip carries eight fields; the tools carry two or three. */
.ed-dock.wide{flex-basis:340px}
.ed-dock-head{display:flex;align-items:center;gap:8px;padding:9px 12px;
  border-bottom:1px solid var(--line);font-size:.82rem}
.ed-dock-head b{flex:1;display:flex;align-items:center;gap:7px;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-transform:capitalize}
.ed-dock-body{flex:1;min-height:0;overflow:auto;padding:12px}
/* The bodies were written for the app's dock, which is wider. Nothing in them
   should assume a width. */
.ed-dock-body .tp-sec{margin:0}
.ed-dock-body .run{width:100%;margin-top:10px}
.ed-dock-body .job{margin-bottom:8px}
@media (max-width:1100px){ .ed-dock{flex-basis:250px} .ed-dock.wide{flex-basis:270px} }
/* Under ~820px the sheet is nearly the viewport and a 250px aside leaves the
   picture unreadable; the dock stacks under the canvas instead. */
@media (max-width:820px){
  .canvas-work{flex-direction:column}
  .ed-dock{flex:0 0 auto;max-width:none;max-height:44%}
}
@keyframes veil-in  { from { opacity: 0 } to { opacity: 1 } }
@keyframes sheet-in { from { opacity: 0; transform: translateY(8px) scale(.985) } }
@media (max-width: 720px) {
  .editor-veil { padding: 0 }
  .editor-sheet { width: 100vw; height: 100vh; border-radius: 0; border: 0 }
}

/* ===================================================================
   SOUND — the cue sheet, and the reading of each cue
   -------------------------------------------------------------------
   The Sound stage's answer to `.videoboard`. One row per cue of the film,
   drawn as a LIST rather than a grid: a cue is a line of text with a
   number and a duration, and a grid of cards would give the text a
   column narrower than the sentence it holds.
   =================================================================== */
.soundboard{display:flex;flex-direction:column;gap:12px;min-height:0}

.sbd-cast{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.sbd-cast-lb{font-size:.7rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted)}
.sbd-voice{display:inline-flex;align-items:center;gap:7px;padding:4px 10px;font-size:.76rem;
  background:var(--surface-2);border:1px solid var(--line);border-radius:999px}
.sbd-voice i{font-size:.6rem;color:var(--faint)}
.sbd-vname{color:var(--ac);font-weight:600}
.sbd-voice{cursor:pointer;color:var(--ink);font-family:inherit;transition:border-color .15s,background .15s}
.sbd-voice:hover,.sbd-voice.on{border-color:var(--brand);background:rgba(var(--brand-rgb),.08)}
.sbd-voice.cloned .sbd-vname{color:var(--accent)}
.sbd-voicesheet{margin:10px 0 4px;padding:14px 16px;border:1px solid rgba(var(--brand-rgb),.35);border-radius:12px;background:var(--surface)}
.sbd-vs-grid{display:grid;grid-template-columns:1fr 1.4fr;gap:12px 18px;margin-top:8px}
.sbd-vs-grid label{display:block;font-size:.76rem;font-weight:700;color:var(--muted)}
.sbd-vs-grid label .ctl{margin-top:5px;width:100%}
.sbd-vs-grid label small{display:block;font-weight:500;margin-top:4px}
.sbd-vs-ref{grid-column:1/-1;display:flex;flex-direction:column;gap:4px}
.sbd-vs-up{align-self:flex-start;width:auto;cursor:pointer;margin-top:6px;display:inline-flex;font-size:.76rem;color:var(--muted)}
@media (max-width:760px){.sbd-vs-grid{grid-template-columns:1fr}}

.sbd-list{display:flex;flex-direction:column;gap:6px}
.sbd-cue{display:flex;align-items:flex-start;gap:10px;padding:9px 11px;
  background:var(--surface);border:1px solid var(--line);border-radius:10px;
  transition:border-color .15s,background .15s}
.sbd-cue:hover{border-color:var(--line-2,var(--line));background:var(--surface-2)}
/* NOT SPOKEN YET is a state worth seeing down a list of fifty: the row is
   dimmed rather than hidden, because "which lines are still silent" is the
   question this board exists to answer at a glance. */
.sbd-cue.quiet{opacity:.62}
.sbd-cue.playing{border-color:var(--ac);box-shadow:inset 0 0 0 1px var(--ac-line)}
.sbd-no{flex:0 0 auto;min-width:22px;text-align:right;font-size:.72rem;font-weight:700;
  color:var(--faint);padding-top:5px}
.sbd-play{flex:0 0 auto;width:30px;height:30px;border-radius:50%;border:1px solid var(--line);
  background:var(--surface-2);color:var(--ink);cursor:pointer;display:flex;
  align-items:center;justify-content:center;font-size:.7rem}
.sbd-play:hover:not(:disabled){border-color:var(--ac);color:var(--ac)}
.sbd-play:disabled{opacity:.35;cursor:default}
.sbd-body{flex:1;min-width:0}
.sbd-meta{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin-bottom:3px}
.sbd-who{font-size:.74rem;font-weight:700}
.sbd-vchip{font-size:.68rem;color:var(--ac);background:var(--ac-soft);
  border-radius:5px;padding:1px 6px}
.sbd-time,.sbd-budget{font-size:.68rem;color:var(--muted);font-variant-numeric:tabular-nums}
/* The reading rate, shown only when it is not 1.0 — see the template. `hard` is
   the shade for a rate that is audibly wrong rather than merely adjusted. */
.sbd-fit{font-size:.68rem;font-weight:700;color:var(--muted);border:1px solid var(--line);
  border-radius:5px;padding:0 5px}
.sbd-fit.hard{color:var(--warn);border-color:var(--warn)}
/* THE LINE IS THE WRONG LENGTH FOR ITS SHOT, and which way. This replaced a
   banner across the top of the board that said "2 lines cannot be read inside
   the shot they are spoken over" — a count you cannot act on, since it named
   neither line. The mark belongs on the line.

   TWO SHADES, because they are not the same trouble. `long` is warn: the words
   overrun the picture and the audience loses the end of the sentence. `short`
   is quiet: the line ends early and leaves silence, which is a flaw you may
   well choose to keep. Both are buttons — the tooltip carries the arithmetic
   and the click opens the reading, where a waveform knows its real length. */
.sbd-unfit{display:inline-flex;align-items:center;gap:4px;padding:0 6px;
  font-size:.68rem;font-weight:700;font-variant-numeric:tabular-nums;
  background:transparent;border:1px solid var(--warn);border-radius:5px;
  color:var(--warn);cursor:pointer;transition:background .15s,color .15s}
.sbd-unfit i{font-size:.6rem}
.sbd-unfit:hover{background:rgba(var(--warn-rgb,240,180,60),.14)}
.sbd-unfit.short{border-color:var(--line);color:var(--muted)}
.sbd-unfit.short:hover{background:var(--surface-2);color:var(--ink)}
.sbd-text{margin:0;font-size:.84rem;line-height:1.45;word-break:break-word}
/* ALWAYS VISIBLE. They were `opacity:0` until hover, which is how "where is the
   edit button?" became a question — a touch screen has no hover at all, and a row
   of invisible controls is a row of controls nobody finds. They dim instead, so
   the row still reads as text first. */
.sbd-acts{flex:0 0 auto;display:flex;gap:5px;opacity:.6;transition:opacity .15s}
.sbd-cue:hover .sbd-acts,.sbd-cue.playing .sbd-acts{opacity:1}

/* SKIPPED: greyed and labelled, exactly as a skipped frame is in the grid — one
   visual language for one idea. Not hidden, because the control that undoes it
   has to stay the easiest thing on the row to find. */
.sbd-cue.skipped .sbd-text,.sbd-cue.skipped .sbd-meta > *:not(.sbd-skipped){
  filter:grayscale(1);opacity:.5}
.sbd-cue.skipped .sbd-play{opacity:.4}
.sbd-skipped{font-size:.64rem;font-weight:800;text-transform:uppercase;letter-spacing:.05em;
  color:var(--warn);border:1px solid var(--warn);border-radius:5px;padding:0 5px}

/* THE FILM'S VOICE, in the bar. Beside the counts because it is a statement
   about the whole film, the way the cast strip is. */
.sbd-allvoice{display:inline-flex;align-items:center;gap:7px}
.sbd-allvoice .ctl{padding:4px 8px;font-size:.76rem;width:auto;min-width:170px}

/* THE PER-CUE VOICE. Quiet by default — most cues follow the film — and marked
   when it is an override, because "matches the default" and "was set to the
   default" behave differently under a mass change. */
.sbd-vpick{font-size:.7rem;padding:1px 5px;border-radius:6px;cursor:pointer;
  background:var(--surface-2);border:1px solid var(--line);color:var(--muted);max-width:190px}
.sbd-vpick:hover{color:var(--ink);border-color:var(--ac)}
.sbd-vpick.over{color:var(--ac);border-color:var(--ac-line);background:var(--ac-soft)}

/* THE SFX CUE. A line of its own under the words, not a chip beside them: it is
   a prompt, it gets long, and it is the second thing a card is for. */
.sbd-sfx{display:flex;align-items:center;gap:7px;margin-top:6px}
.sbd-sfx > i{font-size:.7rem;color:var(--faint);flex:0 0 auto}
.sbd-sfxin{flex:1;min-width:0;font-size:.74rem;padding:3px 8px;background:transparent;
  border:1px dashed var(--line);border-radius:7px;color:var(--muted)}
.sbd-sfxin:focus{background:var(--surface-2);border-style:solid;color:var(--ink)}
.sbd-sfxin::placeholder{color:var(--faint)}
.sbd-cue.skipped .sbd-sfx{opacity:.5}

/* THE TWO GENERATE BOARDS reuse `.wa-grid`/`.cell` so they read as the same app
   as the Video stage's Generate tab. Only what is audio-specific is added. */
.sbd-card.quiet{opacity:.62}
.sbd-card.skipped .cell-summary,.sbd-card.skipped figcaption{filter:grayscale(1);opacity:.55}
/* A reading has no picture, so its media area IS the play button — the card would
   otherwise be a caption with a dead grey rectangle above it. */
.sbd-cardmedia{position:relative;display:flex;align-items:center;justify-content:center;
  background:linear-gradient(160deg,var(--surface-2),var(--surface))}
.sbd-bigplay{width:52px;height:52px;border-radius:50%;border:1px solid var(--line);
  background:var(--surface);color:var(--ink);cursor:pointer;font-size:1rem;
  display:flex;align-items:center;justify-content:center;transition:transform .15s,border-color .15s}
.sbd-bigplay:hover:not(:disabled){border-color:var(--ac);color:var(--ac);transform:scale(1.06)}
.sbd-bigplay:disabled{opacity:.3;cursor:default}
.sbd-cardno{position:absolute;top:6px;left:8px;font-size:.7rem;font-weight:800;color:var(--faint)}
.sbd-card .sbd-skipped{position:absolute;top:6px;right:8px}
.sbd-card .cell-actions{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
/* THE LINE IS CLAMPED. A cue is a sentence and a card is not — unclamped, the
   text ran past the card and sat on top of the voice chip and the buttons under
   it. Three lines is enough to recognise a line you wrote; the whole of it is on
   the Plan Sound board, which is the one built to hold it. */
.sbd-card .cell-summary{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;
  overflow:hidden;margin-bottom:6px}

.sbd-text{cursor:text}
.sbd-text:hover{text-decoration:underline dotted var(--faint);text-underline-offset:3px}
.sbd-edit{width:100%;font-size:.84rem;line-height:1.45;resize:vertical}
.sbd-editrow{display:flex;align-items:center;gap:8px;margin-top:6px}

/* On a phone the row's actions cannot depend on hover, and the meta line needs
   its own row rather than competing with the text for width. */
@media(max-width:640px){
  .sbd-acts{opacity:1}
  .sbd-cue{padding:8px 9px;gap:8px}
  .sbd-no{display:none}
}

/* ===================================================================
   STORY — script files, and the subtitle editor
   -------------------------------------------------------------------
   A script is a FILE whose extension is the kind of film (.srt dialogue,
   .md narration — services/script_doc.py), so everything here is about
   making that legible: on the card, on the editor bar, and in the panel
   that creates one. The cue table is the only genuinely new surface.
   =================================================================== */

/* The Story stage's create bar, at the top of the Inspector — where the file it
   makes appears. */
.insp-make{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:10px;
  padding:9px 11px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}
.insp-make .mini{flex:0 0 auto}

/* Which kind of film a script is, on the card. Two hues so the pair is
   distinguishable at a glance in a folder holding both. */
.cell-tag{align-self:flex-start;margin:0 9px 4px;padding:2px 8px;border-radius:20px;
  font-size:.62rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  border:1px solid var(--line)}
.cell-tag.dialogue{color:var(--accent);background:rgba(var(--accent-rgb),.12);
  border-color:rgba(var(--accent-rgb),.35)}
.cell-tag.narration{color:var(--brand);background:rgba(var(--brand-rgb),.12);
  border-color:rgba(var(--brand-rgb),.35)}

/* The video-script mark. `on` is a STATE, not a hover — exactly one file in the
   project carries it, and it has to read as chosen from across the grid. */
.mini.on,.vs-mark.on{background:rgba(var(--brand-rgb),.16);color:var(--brand);
  border-color:rgba(var(--brand-rgb),.45)}
/* A script card carries four actions in a 150px column, so the row has to wrap
   rather than push Edit off the edge. Each button stays on one line: a control
   whose label breaks mid-word is unreadable at this size. */
.cell-actions{flex-wrap:wrap}
.cell-actions .mini{white-space:nowrap}

/* ---- "you have worked on this one" ----------------------------------------
   Top-left of the card, opposite the script style tag, and small: it is a fact
   about the file's past, not a state you need to act on. Filled for the file
   that IS the edit; hollow, in the muted colour, for the one it replaced. */
.cell-edit{position:absolute;top:7px;left:7px;z-index:3;
  width:20px;height:20px;display:inline-flex;align-items:center;justify-content:center;
  border-radius:6px;font-size:.6rem;cursor:default;
  background:var(--brand);color:var(--paper);border:1px solid var(--brand);
  box-shadow:0 2px 8px rgba(0,0,0,.4)}
.cell-edit.was{background:rgba(var(--bg-rgb),.78);color:var(--muted);border-color:var(--line);
  backdrop-filter:blur(4px)}
/* The Source board's cards already carry an order chip top-left and a drag
   handle top-right, so this one sits under the order chip rather than on it. */
.srccell{position:relative}
.srccell .cell-edit{top:34px}

/* OLDER THAN WHAT IT WAS MADE FROM. Sitting BESIDE the edit badge rather than
   replacing it: the two are different statements about one card — "I changed
   this" and "something I changed fed this" — and a file can carry both at once.

   QUIET, not amber. It was a filled amber chip with a warning triangle, which is
   the treatment this app uses for something that has gone wrong; nothing has.
   Raj, 2026-08-30: "too alarmist". It now wears `.cell-edit.was`'s exact
   clothes — the same dark plate, muted ink and hairline — because it is the same
   kind of remark about a file's past, and two marks that sit side by side on one
   card should not be shouting at different volumes. */
.cell-stale{position:absolute;top:7px;left:31px;z-index:3;
  width:20px;height:20px;display:inline-flex;align-items:center;justify-content:center;
  border-radius:6px;font-size:.6rem;cursor:default;
  background:rgba(var(--bg-rgb),.78);color:var(--muted);border:1px solid var(--line);
  backdrop-filter:blur(4px)}
.srccell .cell-stale{top:34px}
.cell-edit + .cell-stale{left:31px}
/* Nothing to the left of it when the file has no edit badge. */
.ipick-cell .cell-stale,.cell:not(:has(.cell-edit)) .cell-stale{left:7px}

/* THE LINE IS THE WRONG LENGTH FOR ITS SHOT — `.sbd-unfit`'s fact, on a card.
   Wears `.cell-stale`'s exact shape so the grids keep one badge language; the
   seconds are not on it because a card is small and the tooltip has them. */
.cell-fit{position:absolute;top:7px;left:7px;z-index:3;
  width:20px;height:20px;display:inline-flex;align-items:center;justify-content:center;
  border-radius:6px;font-size:.6rem;cursor:default;
  background:rgba(var(--bg-rgb),.78);color:var(--muted);border:1px solid var(--line);
  backdrop-filter:blur(4px)}
.cell-fit.long{color:var(--warn);border-color:var(--warn)}
/* A reading's card already carries its cue number top-left, so this sits beside
   it rather than on it — three digits of number still clear 30px. */
.sbd-card .cell-fit{top:5px;left:30px}

/* ---- the inspector's order chip -------------------------------------------
   Raj, 2026-08-31: he wanted the Source stage's card treatment on the other
   stages' folder view — the count in the bar, and the files numbered.

   Same badge as `.src-order`, ONE JOB LESS. On the Source board the number is a
   control: the order there is the film's order, so the chip is clickable and the
   card is draggable. Here it only says where the file sits in the folder it came
   back in, so it keeps the shape and the colour — a number in that corner has to
   mean the same thing on every grid in the app — and drops the pointer and the
   hover, which would promise a reorder that does not exist. */
.insp-order{position:absolute;top:6px;left:6px;z-index:2;min-width:24px;height:24px;padding:0 6px;border-radius:8px;
  background:var(--ac);color:var(--on-brand);font-size:.78rem;font-weight:800;
  display:flex;align-items:center;justify-content:center;cursor:default;
  box-shadow:0 2px 8px -2px rgba(0,0,0,.6)}
/* …and the marks about the file's past drop below it, exactly as they do on a
   Source card (`.srccell .cell-edit`), instead of sitting under the number. */
.wa-grid.rich .cell-edit,.wa-grid.rich .cell-stale{top:34px}
/* …and on any OTHER grid that numbers its cards. The clips grid is a plain
   `.wa-grid`, so its edit mark — `top:7px;left:7px;z-index:3` — sat exactly on
   top of the number at `top:6px;left:6px;z-index:2` and hid it on every clip that
   had ever been edited, which is every clip anybody has trimmed or re-rendered.
   The number is the whole of "show the order numbering in Generate Videos". */
.wa-grid .cell.ordered .cell-edit,.wa-grid .cell.ordered .cell-stale{top:34px}

/* ---- A FRAME THAT IS OUT OF THE FILM --------------------------------------
   `plan.is_animatable: false` — set by the planning pass or by hand in File
   Insights, and enforced on every step that turns art into shots
   (director_tools._not_animatable). Raj, 2026-09-02: "visually grey the frame
   out in wa-grid and update the order/number of files, while leaving the skipped
   frame empty."

   GREY THE PICTURE, NOT THE CARD. `filter:grayscale` plus a little opacity on
   the media alone: the name, the reading and the buttons stay fully legible,
   because a skipped frame is still a file you want to read and click Include on.
   Dimming the whole card would make the one control that undoes this the hardest
   thing on it to find.

   NOT `pointer-events:none` and not `disabled`. The card is fully live — that is
   the point of Raj's last clause, "if we include anyway, the frame can come
   back". This is a statement about the film, not about the file. */
.wa-grid .cell.skipped .cell-media{filter:grayscale(1);opacity:.42}
/* A SHOT THE FILM WILL NOT HAVE, on the storyboard — same grey and the same word
   the Art grid uses for a skipped frame, because it is the same idea one stage
   along. The card stays live: the thing that undoes it is on it. */
.vboard.skipped .shot-stack{filter:grayscale(1);opacity:.45}
.vboard.skipped .vb-meta,.vboard.skipped figcaption{opacity:.6}
.vb-skip{position:absolute;top:6px;right:6px;z-index:2;font-size:.62rem;font-weight:800;
  text-transform:uppercase;letter-spacing:.04em;padding:2px 7px;border-radius:6px;
  color:var(--warn);border:1px solid var(--warn);background:var(--surface)}
.vb-skip[title*="back"]{cursor:pointer}
.vb-skip[title*="back"]:hover{background:var(--warn);color:var(--on-brand)}
.wa-grid .cell.skipped figcaption{color:var(--faint,var(--muted))}
/* The badge keeps its SHAPE and loses its fill, so the run of numbers still
   lines up and the gap in it is a gap rather than a missing card. A dashed
   outline says "this slot exists and is empty", which is the whole message. */
.insp-order.blank{background:transparent;border:1px dashed var(--line-2,var(--line));
  box-shadow:none;color:transparent}
/* The word, once, on the card. Top-right so it never collides with the number
   badge (top-left) or the edit marks that stack under it. */
.cell-skip{position:absolute;top:6px;right:6px;z-index:2;display:inline-flex;align-items:center;gap:5px;
  padding:3px 8px;border-radius:999px;background:rgba(0,0,0,.62);
  border:1px solid var(--line-2,var(--line));color:var(--muted);
  font-size:.66rem;font-weight:700;letter-spacing:.02em}
.cell-skip i{font-size:.6rem}

/* ---- A SKIPPED FRAME, IN THE EDITOR ---------------------------------------
   Raj, 2026-09-03: "when you open a skipped frame in editor, edit or view, it
   should show up a black/white, with a skipped tag at the top right, just like
   it does in wa-grid."

   So it is deliberately the SAME two signals as the grid, with the same values:
   the picture goes grey and the word sits top-right. The editor and the grid are
   reporting one fact, and a second visual language for it would only be a second
   thing to learn — open a card that reads grey in the grid and it must not go
   full-colour the moment it fills the screen.

   The filter is on `.canvas-stage`, which is the element Fabric owns and the
   picture lives in; the tag is a sibling on `.canvas-frame`, because anything
   parked inside the stage fights Fabric for that subtree (the same reason the
   corner buttons are where they are). It also means the tag stays legible while
   the picture behind it is dimmed. */
.canvas-stage.skipped{filter:grayscale(1);opacity:.42}
/* Clear of `.cf-x`, which owns the corner: 32px of button plus a gap. */
.canvas-skip{position:absolute;z-index:7;top:calc(var(--gut) + var(--edge));
  right:calc(var(--edge) + 40px);display:inline-flex;align-items:center;gap:5px;
  padding:4px 10px;border-radius:999px;background:rgba(0,0,0,.62);
  border:1px solid var(--line-2,var(--line));color:var(--muted);
  font-size:.7rem;font-weight:700;letter-spacing:.02em;pointer-events:none}
.canvas-skip i{font-size:.62rem}
@media (max-width:900px){.canvas-skip{right:calc(var(--edge) + 34px)}}

/* ---- Generative Fill ------------------------------------------------------
   A plate with a fill in flight. It sits UNDER the picture rather than over it,
   because the picture is the thing being judged and covering it with a spinner
   is the one thing a repair UI must not do. The bar is the job's own progress,
   so a queued fill reads 0% and says why. */
.cell-busy{position:relative;display:flex;align-items:center;gap:7px;
  margin:0 9px 6px;padding:6px 9px 8px;border-radius:9px;overflow:hidden;
  background:var(--ac-soft);
  border:1px solid var(--ac-line);
  font-size:.7rem;color:var(--ink)}
/* TWO LINES, then ellipsis. A card is ~160px and the message is the point of the
   badge — "Repainting t…" says less than the icon does. The full text is on the
   element's `title` as well, because two lines is not always enough either. */
.cell-busy span{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;line-height:1.35}
.cell-busy .cb-bar{position:absolute;left:0;bottom:0;height:2px;
  background:var(--brand);transition:width .4s ease}
/* THE SAME THING IN AN EDITOR BAR. A chip rather than a block: the bar is a row
   of controls and this is a fact sitting among them, so it borrows the badge's
   colours and its progress bar and drops the width. */
.ed-landing{position:relative;display:inline-flex;align-items:center;gap:7px;
  padding:5px 10px 7px;border-radius:999px;overflow:hidden;font-size:.74rem;
  color:var(--ink);cursor:default;white-space:nowrap;
  background:var(--ac-soft);
  border:1px solid var(--ac-line)}
.ed-landing b{font-weight:700}
.ed-landing .cb-bar{position:absolute;left:0;bottom:0;height:2px;
  background:var(--brand);transition:width .4s ease}

/* The cell's own Fill button. Ghost like Insights and Discuss — it is an action
   on this file, not the card's primary one, which is still Edit. */
.gf-cell{white-space:nowrap}
.gf-cell:disabled{opacity:.45}

/* The prompt, in the dock. A textarea rather than an input because what belongs
   in it is a phrase with commas in it and people write more than one line. */
.gf-prompt{min-height:64px;resize:vertical;font:inherit;line-height:1.45;padding:8px 10px}
/* …and on the canvas bar, where it has to share a row with the brush and the
   button. Grows into whatever the bar has spare rather than being fixed, so a
   wide editor gives the words room. */
.gf-bar-in{flex:1 1 200px;min-width:140px;max-width:420px}
/* The cost-and-wait note. Bordered off from the help text above it: it is the
   part a user needs to have read BEFORE pressing the button, not after. */
.gf-note{margin-top:8px;padding-top:8px;border-top:1px solid var(--line);line-height:1.5}

/* A TEXT card has no picture, and a full square of empty placeholder made a
   folder of scripts read as a wall of blank tiles. The strip is enough to carry
   the glyph and keep the cards the same shape as each other. */
.cell.txt .cell-media{aspect-ratio:auto;height:52px}
.cell.txt .cell-ph{font-size:1.2rem}

/* New script: the kind-of-film picker. */
.ns-kinds{display:flex;flex-direction:column;gap:6px;margin-top:10px}
.ns-kind{display:flex;flex-direction:column;gap:2px;text-align:left;cursor:pointer;
  padding:8px 10px;border-radius:10px;border:1px solid var(--line);
  background:var(--surface-2);color:var(--ink)}
.ns-kind:hover{border-color:var(--brand-2)}
.ns-kind.on{border-color:var(--brand);background:rgba(var(--brand-rgb),.12)}
.ns-kind .nsk-h{display:flex;align-items:center;gap:7px;font-weight:700;font-size:.8rem}
.ns-kind small{color:var(--muted);font-size:.7rem;line-height:1.4}
/* What `auto` would pick, and why. Deliberately NOT card-shaped: with the same
   background, border and padding as the three .ns-kind buttons above it, it read
   as a fourth option you could choose. It is an annotation on the first one. */
.ns-why{display:flex;gap:8px;align-items:flex-start;margin:8px 0 0 10px;
  padding:2px 0 2px 10px;border-left:2px solid var(--line-strong);
  color:var(--muted);font-size:.72rem;line-height:1.5}
.ns-why b{color:var(--ink)}
.ns-why > i{margin-top:3px}

/* The Story editor's bar: the file's kind, its two tabs, and the disclosure that
   shows how it was written. */
.doc-kind{padding:2px 8px;border-radius:20px;font-size:.62rem;font-weight:800;
  letter-spacing:.06em;text-transform:uppercase;border:1px solid var(--line);flex:0 0 auto}
.doc-kind.dialogue{color:var(--accent);background:rgba(var(--accent-rgb),.12)}
.doc-kind.narration{color:var(--brand);background:rgba(var(--brand-rgb),.12)}
.doc-tabs{display:flex;gap:2px;background:var(--surface-2);border:1px solid var(--line);
  border-radius:9px;padding:2px;flex:0 0 auto}
.doc-tabs button{background:transparent;border:0;color:var(--muted);cursor:pointer;
  padding:4px 10px;border-radius:7px;font-size:.74rem;font-weight:700}
.doc-tabs button.on{background:var(--surface-3);color:var(--ink)}
.doc-info{display:flex;flex-direction:column;gap:8px;padding:11px 12px;margin:0 0 2px;
  border:1px solid var(--line);border-radius:10px;background:var(--surface)}
.doc-info .fld{margin-top:0}
.doc-prompt{display:flex;flex-direction:column;gap:8px}
.doc-prompt .ctl{font-size:.74rem;line-height:1.5}

/* ---- the SCRIPT PAGE ----------------------------------------------------
   Fields, not a text buffer: the timecodes are inputs and the index is derived
   from position, so inserting a line cannot desynchronise the file.

   `srt` as a bare class name is deliberately never a LAYOUT rule — it is a
   modifier elsewhere (it was `.doc-kind.srt`), and a layout rule under the same
   bare name once matched the badge too and stretched it across the editor bar as
   a flex child. Every element rule here is `.srt-<part>`. */
.srt-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;flex:0 0 auto}
.srt-sep{width:1px;height:18px;background:var(--line)}

/* THE SCRIPT PAGE. A shooting script's shape, not a table's: a narrow measure so
   a line is one glance wide, a monospace face because that is what a script is
   set in, and the speaker centred above its line. The shape is what tells a
   writer at a distance whether this is one voice or four — which is exactly the
   thing the two styles differ by. */
.srt-page{flex:1;min-height:0;overflow:auto;display:flex;flex-direction:column;gap:2px;
  padding:18px 0 40px;background:var(--surface);border:1px solid var(--line);border-radius:12px}
.srt-cue{display:flex;gap:12px;width:min(760px,100%);margin:0 auto;padding:10px 16px;
  border:1px solid transparent;border-radius:10px}
.srt-cue:hover{background:rgba(var(--brand-rgb),.05);border-color:var(--line)}
.srt-cue.bad{border-color:rgba(var(--danger-rgb),.55)}

/* The gutter: a line number, like an IDE's, and the moves that have to be
   reachable without opening anything. Right-aligned and tabular so the numbers
   form a column rather than a ragged edge. */
.srt-n{display:flex;flex-direction:column;align-items:flex-end;gap:2px;min-width:34px;
  color:var(--muted);font-size:.7rem;font-variant-numeric:tabular-nums;
  padding-top:2px;border-right:1px solid var(--line);padding-right:8px}
.srt-n b{color:var(--muted);font-size:.78rem;font-weight:600}
.srt-cue:hover .srt-n b{color:var(--ink)}
.srt-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}

/* The timecode row only surfaces on the cue you are working in. A page showing
   every in-point and out-point at once reads as a spreadsheet, and the reason to
   set a script like a script is that it should read as one. */
.srt-times{display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  opacity:0;transition:opacity .12s}
.srt-cue:hover .srt-times,.srt-cue:focus-within .srt-times,.srt-cue.bad .srt-times{opacity:1}
.srt-arrow{color:var(--muted);font-size:.8rem}
.opt-in.tc{width:118px;font-family:ui-monospace,'SFMono-Regular',monospace;font-size:.75rem;
  font-variant-numeric:tabular-nums}
.opt-in.who{width:140px}

/* SPEAKER above LINE, both set in the script face. The speaker is centred and
   upper-cased the way a character name is on a script page — by CSS, never in the
   stored words, because the file has to keep the description exactly as it was
   written for the Art and Sound stages to match it. */
.srt-who-in{width:100%;background:none;border:0;padding:2px 0;text-align:center;
  font-family:ui-monospace,'Courier New',monospace;font-size:.76rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--brand-2);outline:none}
.srt-who-in::placeholder{color:var(--muted);opacity:.5;text-transform:none;letter-spacing:0}
.srt-who-in:focus{background:rgba(var(--brand-rgb),.08);border-radius:6px}
/* resize:none, overflow:hidden and min-height:0 are load-bearing, not taste:
   `srtGrow` sizes the box from `scrollHeight`, which only equals the content's
   height when the box is neither already scrolling nor floored. The base
   `textarea` rule sets `min-height:78px` — right for a prompt box, and on a
   script page it made every one-line cue three lines tall, so four lines of
   dialogue filled a 900px screen and the page stopped reading as a script. */
.srt-tx{width:100%;resize:none;overflow:hidden;background:none;border:0;outline:none;
  padding:2px 0;color:var(--ink);line-height:1.65;min-height:0;
  font-family:ui-monospace,'Courier New',monospace;font-size:.86rem}
.srt-tx:focus{background:rgba(var(--brand-rgb),.08);border-radius:6px}
.srt-err{margin:0;color:var(--danger);font-size:.7rem}

/* A dialogue line is indented under its speaker, the way a script sets one. Only
   when there IS a speaker: a narration has nothing to indent under. */
.srt-page:not(.v-lines) .srt-cue .srt-who-in:not(:placeholder-shown) + .srt-tx{
  padding-left:12%;padding-right:12%}

/* THREE VIEWS OF ONE FILE. `Lines` hides every speaker, which is what a dialogue
   script reads like as a narration — a click instead of a job. `Speakers` hides
   the words, so a cast of descriptions can be read down and corrected as a list. */
.srt-page.v-lines .srt-who-in{display:none}
.srt-page.v-who .srt-tx{display:none}
.srt-page.v-who .srt-cue{padding-top:4px;padding-bottom:4px}
.srt-page.v-who .srt-who-in{text-align:left}
/* In `Speakers` the gutter loses its move arrows and the timecodes their space.
   Reordering is not what this view is for — correcting a description is — and
   with the arrows in it every row stood ~90px tall, which is not a list you can
   read a cast down. Without them a row is one line, which is. */
.srt-page.v-who .beat-mv{display:none}
.srt-page.v-who .srt-times{display:none}

.srt-views{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden}
.srt-views button{background:none;border:0;color:var(--muted);font-size:.72rem;
  padding:5px 10px;cursor:pointer}
.srt-views button.on{background:rgba(var(--brand-rgb),.18);color:var(--ink)}

/* WHO IS IN IT — the distinct speaker descriptions and how much each says. This
   is the list Plan Sound casts a voice from, so it is worth being able to read. */
.srt-cast{display:flex;flex-wrap:wrap;gap:6px;flex:0 0 auto}
.srt-who{font-size:.72rem;color:var(--muted);border:1px solid var(--line);
  border-radius:999px;padding:3px 10px}
.srt-who b{color:var(--ink);margin-left:4px}

.doc-legacy{font-size:.68rem;color:var(--muted);border:1px dashed var(--line);
  border-radius:999px;padding:2px 8px}

@media (max-width: 720px){
  .opt-in.tc{width:104px}
  .opt-in.who{width:100%}
  .srt-cue{padding:8px 10px}
  .srt-page:not(.v-lines) .srt-cue .srt-who-in:not(:placeholder-shown) + .srt-tx{
    padding-left:0;padding-right:0}
}

/* ---- THE STORY BOARD: what Plan Story read off each imported page ----
   Source's ordering board's cousin, and deliberately the same card, because both
   answer "what is on this page" — this one with the reading rather than the
   order. Wider cells than Source's: a scene line and a paragraph of story need
   room a filename does not. */
.storyboard-pages{display:flex;flex-direction:column;gap:10px;flex:1;min-height:0}
.stgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:12px;
  overflow:auto;align-content:start;padding-right:4px}
.stcell{position:relative;background:var(--surface);border:1px solid var(--line);
  border-radius:12px;overflow:hidden;margin:0;display:flex;flex-direction:column}
/* An open card takes the full row: the reading is paragraphs and a list of lines,
   and neither is editable in a 230px column. */
.stcell.open{grid-column:1/-1;border-color:var(--ac)}
.st-media{position:relative;aspect-ratio:1;display:flex;align-items:center;
  justify-content:center;background:var(--ink-950);cursor:pointer}
.stcell.open .st-media{aspect-ratio:auto;height:160px}
.st-media img{width:100%;height:100%;object-fit:cover}
.st-no{position:absolute;top:6px;left:6px;background:rgba(0,0,0,.66);color:var(--paper);
  font-size:.68rem;border-radius:6px;padding:1px 7px;font-variant-numeric:tabular-nums}
.st-seen{position:absolute;top:6px;right:6px;background:rgba(0,0,0,.66);
  color:var(--gold);font-size:.68rem;border-radius:6px;padding:2px 7px}
.stcell figcaption{padding:6px 8px 2px}
.st-scene{width:100%;background:none;border:0;outline:none;color:var(--ink);
  font-size:.76rem;font-weight:600;padding:2px 0}
.st-scene:focus{background:rgba(var(--brand-rgb),.08);border-radius:6px}
.st-insight{padding:0 9px 6px;margin:0;font-size:.72rem;color:var(--muted);line-height:1.45;
  display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden}
.st-say{padding:0 9px 8px;margin:0;font-size:.7rem;color:var(--muted)}
/* THE NARRATION on a closed card — what the audience actually hears, set apart
   from the editorial note it sits in place of. Quoted rather than muted grey:
   this is the film's dialogue track, and it reads as copy, not as metadata. */
.st-narr{padding:0 9px 6px;margin:0;font-size:.73rem;line-height:1.45;color:var(--ink);
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.st-narr i{color:var(--muted);margin-right:4px;font-size:.66rem}
/* Over budget: the page is narrated past what its artwork can hold, which
   stretches every shot made from it. Warning, not error — it still renders. */
.fld-note.over{color:var(--warn);font-weight:600}
.st-open{padding:4px 12px 14px;display:flex;flex-direction:column;gap:6px}
.st-line{display:flex;gap:8px;align-items:flex-start}
.st-line .opt-in.who{flex:0 0 200px}
.st-line textarea{flex:1;min-width:0;resize:vertical}
/* The panel number: narrow, because it holds at most two digits and a wider box
   would read as another text field. */
.opt-in.pnl{flex:0 0 52px;width:52px;text-align:center;font-variant-numeric:tabular-nums}

/* "Sure it is them" — the flag that lets a mouth move on a shot that is not a
   close-up. A filled bubble reads as committed and a hollow one as merely
   recorded, which is the same distinction the field itself makes. The checkbox
   is hidden rather than removed so it stays keyboard-reachable and labelled. */
.st-sure{flex:0 0 30px;display:flex;align-items:center;justify-content:center;
  height:32px;cursor:pointer;color:var(--muted);border:1px solid var(--line);
  border-radius:6px;transition:color .12s,border-color .12s}
.st-sure input{position:absolute;opacity:0;width:0;height:0}
.st-sure:hover{color:var(--ink)}
.st-sure.on{color:var(--brand-2);border-color:var(--brand-2)}
.st-sure input:focus-visible + i{outline:2px solid var(--brand-2);outline-offset:3px;border-radius:2px}

@media (max-width: 720px){
  .st-line{flex-wrap:wrap}
  .st-line .opt-in.who{flex:1 1 100%}
  .st-line .opt-in.pnl{flex:0 0 52px}
}

/* Where the words of a generated script will come from, in the New script panel. */
.ns-src{display:flex;gap:8px;align-items:flex-start;font-size:.72rem;color:var(--muted);
  margin:6px 0 0;line-height:1.5}
.ns-src i{margin-top:3px;color:var(--brand-2)}

/* An assistant turn in flight. Same bubble as a reply, dimmed, so the log keeps
   its shape while the job runs. */
.acm.pending{opacity:.65;font-style:italic}

/* ---- a shot as a STACK OF CARDS ----------------------------------------
   Plan Video groups the panels of one continuous moment into a single clip, and
   the storyboard used to draw only the first of them — so a shot standing in for
   three panels looked exactly like one standing in for one. Fanned cards say
   "these belong together" without needing a caption; the count says how many. */
.shot-stack{position:relative;flex:0 0 auto;width:96px;height:96px}
.shot-card{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  border-radius:8px;border:1px solid var(--line);background:var(--ink-950)}
.shot-stack.many .shot-card:nth-child(1){transform:translate(8px,-6px) rotate(3deg);opacity:.55}
.shot-stack.many .shot-card:nth-child(2){transform:translate(4px,-3px) rotate(1.5deg);opacity:.8}
.shot-stack.many .shot-card:last-of-type{transform:none;opacity:1}
.shot-count{position:absolute;right:-5px;bottom:-5px;min-width:20px;height:20px;padding:0 5px;
  display:flex;align-items:center;justify-content:center;border-radius:999px;
  background:var(--brand);color:var(--on-brand);font-size:.66rem;font-weight:800;
  border:2px solid var(--surface)}

/* ---- THE VIDEO BOARD ----------------------------------------------------
   A BOARD is everything one clip is generated from: a group of Fit to Frame
   plates and the analysis that becomes the H3 prompt. Laid out as a card grid
   rather than the old full-width rows, so the Video stage reads like Source and
   Story — a wall of things you can see at a glance — instead of a settings form.
   The stack of plates is the picture; the analysis sits under it, because the
   analysis is the half that decides what the clip does. */
.videoboard{flex:1;min-height:0;display:flex;flex-direction:column;gap:10px}
.vboards{flex:1;overflow:auto;display:grid;gap:12px;padding:2px 4px 10px 2px;
  grid-template-columns:repeat(auto-fill,minmax(250px,1fr));align-content:start}
.vboard{position:relative;display:flex;flex-direction:column;gap:8px;padding:12px;
  border:1px solid var(--line);border-radius:12px;background:var(--surface);
  cursor:pointer;transition:border-color .12s,transform .12s}
.vboard:hover{border-color:var(--brand-2);transform:translateY(-1px)}
.vboard.on{border-color:var(--brand);box-shadow:0 0 0 1px var(--brand) inset}
.vboard .shot-stack{width:100%;height:132px;align-self:center}
.vb-n{position:absolute;top:8px;left:8px;z-index:2;min-width:20px;height:20px;padding:0 5px;
  display:flex;align-items:center;justify-content:center;border-radius:999px;
  background:var(--brand);color:var(--on-brand);font-size:.66rem;font-weight:800}
.vb-cap{display:flex;flex-direction:column;gap:2px;min-width:0}
.vb-name{font-size:.78rem;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.vb-meta{font-size:.68rem;color:var(--muted);font-variant-numeric:tabular-nums}
/* Three lines of the prompt. Enough to tell two boards apart; the whole of it
   is one click away in the run dock, which is where it can be edited. */
.vb-analysis{margin:0;font-size:.72rem;line-height:1.45;color:var(--muted);
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;
  line-clamp:3;overflow:hidden}
/* One row, never two: the two move buttons are pinned right as a pair rather
   than each claiming the free space, which wrapped the second onto its own line
   on a narrow card. */
.vb-acts{display:flex;align-items:center;gap:6px;flex-wrap:nowrap;margin-top:auto}
.vb-acts .mini{flex:0 1 auto;min-width:0;overflow:hidden;white-space:nowrap}
.vb-moves{display:flex;gap:4px;margin-left:auto;flex:0 0 auto}

/* The frames popup: every plate in one board, full size. */
.modal.wide{max-width:min(1040px,92vw)}
.frames-grid{max-height:60vh;overflow:auto}

/* Deliver's Cut tab reuses the storyboard's track. */
.deliver-cut{flex:1;min-height:0;display:flex;flex-direction:column;gap:10px}

/* ============ THE INPUT PICKER ============
   A sheet over the WORK AREA, not over the screen. `.wa-body` is the scroll
   container and is `position:relative`-less by default, so the veil and sheet are
   pinned to `.workarea` instead — which is the box whose top edge is the stage
   guide and whose right edge is the dock. That is exactly the region this is
   allowed to cover: the dock stays readable beside it, so the feature being
   configured and its Run button are visible while you choose. */
.workarea{position:relative}
.ipick{position:absolute;inset:0;z-index:40;display:flex;align-items:stretch;
  justify-content:center;padding:14px}
.ipick-veil{position:absolute;inset:0;background:rgba(var(--bg-rgb),.62);backdrop-filter:blur(2px)}
.ipick-sheet{position:relative;z-index:1;display:flex;flex-direction:column;
  width:100%;max-width:980px;min-height:0;border-radius:14px;overflow:hidden;
  background:var(--surface);border:1px solid var(--line);
  box-shadow:0 24px 64px rgba(0,0,0,.55);
  animation:sheet-in .14s cubic-bezier(.2,.8,.3,1)}
.ipick-head{display:flex;align-items:center;gap:10px;padding:11px 13px;
  border-bottom:1px solid var(--line);background:var(--surface-2)}
.ipick-head b{display:inline-flex;align-items:center;gap:8px;font-size:.9rem}
.ipick-crumb{color:var(--muted);font-size:.78rem}
.ipick-body{flex:1;min-height:0;overflow:auto;padding:13px}
.ipick-hint{color:var(--muted);font-size:.82rem;margin:0 0 10px}
.ipick-back{background:transparent;border:none;color:var(--muted);cursor:pointer;
  font-size:.8rem;font-weight:600;padding:0 0 10px;display:inline-flex;align-items:center;gap:7px}
.ipick-back:hover{color:var(--ink)}
.ipick-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:11px}
.ipick-grid{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(148px,1fr))}
.ipick-grid.tiles{grid-template-columns:repeat(auto-fill,minmax(230px,1fr))}
.ipick-tile{display:flex;align-items:center;gap:10px;text-align:left;padding:10px 11px;
  border-radius:11px;background:var(--surface-2);border:1px solid var(--line);
  color:var(--ink);cursor:pointer}
.ipick-tile:hover{border-color:var(--brand)}
/* A CARD IS A TOGGLE, so it has to look like one before it is pressed. The tick
   is always in the DOM and only becomes visible when marked — laying it out on
   hover instead made the whole card shift under the cursor. */
.ipick-cell{position:relative;margin:0;border-radius:11px;overflow:hidden;cursor:pointer;
  background:var(--surface-2);border:1px solid var(--line);transition:border-color .12s}
.ipick-cell:hover{border-color:var(--brand)}
.ipick-cell.marked{border-color:var(--ac);box-shadow:inset 0 0 0 1px var(--ac-line)}
.ipick-cell.only{border-color:var(--ac)}
.ipick-media{aspect-ratio:1;display:flex;align-items:center;justify-content:center;
  background:var(--bg);overflow:hidden}
.ipick-media img,.ipick-media video{width:100%;height:100%;object-fit:cover}
.ipick-cell figcaption{padding:6px 8px;font-size:.72rem;color:var(--muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ipick-mark{position:absolute;top:7px;right:7px;z-index:3;width:20px;height:20px;
  display:inline-flex;align-items:center;justify-content:center;border-radius:6px;
  font-size:.6rem;opacity:0;background:var(--ac);color:var(--on-brand);
  border:1px solid var(--ac-line);transition:opacity .12s}
.ipick-cell.marked .ipick-mark{opacity:1}
.ipick-vers{position:absolute;bottom:30px;right:7px;z-index:3;width:22px;height:22px;
  display:inline-flex;align-items:center;justify-content:center;border-radius:6px;
  font-size:.62rem;cursor:pointer;opacity:0;transition:opacity .12s;
  background:rgba(var(--bg-rgb),.82);color:var(--ink);border:1px solid var(--line);
  backdrop-filter:blur(4px)}
.ipick-cell:hover .ipick-vers,.ipick-cell.marked .ipick-vers{opacity:1}
.ipick-vers:hover{border-color:var(--brand);color:var(--brand)}
.ipick-versions{display:flex;flex-direction:column;gap:2px;max-width:520px}
.ipick-foot{display:flex;align-items:center;gap:10px;padding:10px 13px;
  border-top:1px solid var(--line);background:var(--surface-2)}
/* `.run` is width:100% by design — it is the dock's full-width primary button —
   so both the summary and the button need explicit sizing here or the sentence is
   squeezed into two lines beside a button the width of the sheet. */
.ipick-foot .run{width:auto;flex:0 0 auto;padding:9px 20px}
.ipick-sum{flex:1;min-width:0;display:inline-flex;align-items:center;gap:8px;
  font-size:.82rem;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ipick-sum.none{color:var(--muted)}
.ipick-sum i{color:var(--ac)}
.ipick-sum.none i{color:var(--faint,var(--muted))}
@media (max-width:900px){.ipick{padding:0}.ipick-sheet{border-radius:0;border:none}}

/* The dock's trigger. Full width and it SAYS THE ANSWER rather than showing a
   folder name, because the answer is now one of four shapes. */
.ip-trigger{display:flex;align-items:center;gap:9px;width:100%;text-align:left;
  padding:9px 11px;border-radius:10px;cursor:pointer;font-size:.82rem;
  background:var(--surface-2);border:1px solid var(--line);color:var(--muted)}
.ip-trigger.set{color:var(--ink);border-color:var(--ac-line)}
.ip-trigger span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ip-trigger:hover{border-color:var(--brand)}
/* ONE ROW PER INPUT SLOT. A step that eats two kinds of thing draws two, and
   then which is which has to be readable in a 344px column — so the help line
   sits under its own trigger rather than in a description at the top that would
   have to name both. */
.fp-slot + .fp-slot{margin-top:12px}
.fp-slot-help{margin:6px 0 0;line-height:1.45}

/* ---- the Journey block in File Insights ---- */
.rd-journey{margin:12px 0;padding:10px;border-radius:11px;
  background:var(--surface-2);border:1px solid var(--line)}
.rd-jhd{display:flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;
  color:var(--muted);margin-bottom:8px}
.rd-jrow{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.rd-jdir{flex:0 0 46px;font-size:.68rem;color:var(--faint,var(--muted));text-transform:uppercase;
  letter-spacing:.04em}
.rd-jcard{flex:1;min-width:0;display:flex;align-items:center;gap:8px;padding:6px;
  border-radius:9px;background:var(--surface);border:1px solid var(--line);
  color:var(--ink);cursor:pointer;text-align:left}
.rd-jcard:hover{border-color:var(--brand)}
.rd-jcard.stale{border-color:var(--amber-600)}
.rd-jcard img,.rd-jcard video{width:34px;height:34px;object-fit:cover;border-radius:6px;flex:0 0 auto}
.rd-jtx{min-width:0;display:flex;flex-direction:column}
.rd-jtx b{font-size:.76rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rd-jtx small{font-size:.68rem;color:var(--muted)}
.rd-jflag{margin-left:auto;color:var(--warn);font-size:.7rem}
.rd-jstale{margin:6px 0;padding:7px 9px;border-radius:9px;font-size:.74rem;line-height:1.45;
  color:var(--warn);background:rgba(var(--brand-rgb),.12);border:1px solid rgba(var(--brand-rgb),.35)}
.rd-jend{display:flex;align-items:flex-start;gap:8px;margin-top:6px;padding:7px 9px;
  border-radius:9px;font-size:.74rem;line-height:1.45;color:var(--muted);
  background:var(--surface);border:1px dashed var(--line)}

/* ============ OUT OF DATE ============
   The count above the files it is about, and the sentence in a file's Journey
   block. Both used to be buttons that started a cascade; the cascade is gone
   (see .docs/threads/completed.md) and what is left says what is true and leaves
   the spending to a person.

   `.sg-prop` — the amber pill this count wore in the stage guide — is gone with
   it. It was the whole stage's total, sat in the bar above the feature chips,
   and it was loud about a number you could not act on from where it was shown.
   The count lives in the work area now, over the folder whose files are behind,
   and it is muted text rather than a badge. Raj, 2026-08-30. */
.wa-stale{display:flex;align-items:center;gap:7px;margin:0 0 10px;padding:0 2px;
  font-size:.76rem;line-height:1.5;color:var(--muted);cursor:default}
.wa-stale i{font-size:.7rem;opacity:.85}
/* THE STEP, NAMED, INSIDE THE SENTENCE. A word in the line rather than a button
   beside it: the sentence already reads "Re-run X on the same input", and X is
   the only part of it you can press, so making it look like anything other than
   the word it is would add a control to a note whose whole point is being quiet.
   Brand ink and an underline on hover — `.link-mini`'s idiom at the text's own
   size, since it sits mid-sentence and must keep the baseline. */
.wa-stale-go{background:none;border:0;padding:0;font:inherit;font-weight:600;
  color:var(--ac);cursor:pointer}
.wa-stale-go:hover{text-decoration:underline}
/* Inside a board that already has its own bar, it hangs under that bar rather
   than adding a second gap above the grid. */
.srcboard .wa-stale,.videoboard .wa-stale{margin:-2px 0 10px}

.rd-jtodo{display:flex;align-items:flex-start;gap:8px;margin:8px 0 0;padding:8px 10px;
  border-radius:9px;font-size:.74rem;line-height:1.5;color:var(--warn);
  background:rgba(var(--brand-rgb),.12);border:1px solid rgba(var(--brand-rgb),.34)}

/* ============ THE MIX BOARD ============
   Raj, 2026-09-04: "Mixdown is supposed to layer video, voiceover(can be
   multiple) and sfx(can be multiple) … so we can play a mixdown card and see how
   video/voiceover/sfx can play simultaneously".

   So the board is a PICTURE with LANES under it, and the lanes are the mix: one
   block per reading and per effect, at the offset it plays at, over a shared
   time axis. Everything here is proportional — a block's `left` and `width` are
   percentages of the film's length, set inline by `mixBlockStyle` — because the
   board has to be readable at the dock-open width and at full width without two
   sets of numbers.

   `.live` is the block under the playhead. It is a real state and not a hover
   affordance: while the film plays it is the only thing that says WHICH of four
   overlapping things you are currently hearing. */
.deliver-mix{flex:1;min-height:0;display:flex;flex-direction:column;gap:10px}
.mixboard{flex:1;min-height:0;overflow:auto;display:flex;flex-direction:column;gap:12px}
.mix-head{display:flex;align-items:center;gap:10px}
.mix-head h3{display:flex;align-items:center;gap:8px;font-size:.98rem}
.mix-head h3 i{color:var(--ac)}
.mix-sum{font-size:.78rem;color:var(--muted)}
.mix-notes{display:flex;flex-direction:column;gap:4px;background:var(--surface);
  border:1px solid var(--line);border-radius:10px;padding:9px 12px}
.mix-notes p{margin:0;display:flex;gap:7px;align-items:flex-start}
.mix-notes i{margin-top:2px;color:var(--ac)}

.mix-player{background:var(--surface);border:1px solid var(--line);border-radius:12px;
  padding:12px;display:flex;flex-direction:column;gap:10px}
/* 16:9 with the frame letterboxed inside it, so a portrait film and a landscape
   one both sit in a box that does not jump as the cut moves between them. */
/* CAPPED BY HEIGHT, not by aspect ratio. `aspect-ratio` with a full-bleed width
   wins over `max-height`, so at 1450px the screen came out 815px tall and pushed
   the lanes — the thing you came to look at — off the bottom of the board.
   `object-fit:contain` letterboxes whatever shape the film is inside it. */
.mix-screen{position:relative;width:100%;height:38vh;min-height:180px;max-height:400px;
  background:var(--ink-950);border-radius:10px;overflow:hidden;display:flex;
  align-items:center;justify-content:center}
.mix-screen video.mix-picture{width:100%;height:100%;object-fit:contain;display:block}
.mix-noshot{position:absolute;inset:0;display:flex;flex-direction:column;gap:8px;
  align-items:center;justify-content:center;color:var(--muted);font-size:.82rem;
  background:rgba(var(--bg-rgb),.9)}
.mix-noshot i{font-size:1.5rem;color:var(--ac)}

.mix-transport{display:flex;align-items:center;gap:10px}
.mix-play{width:38px;height:38px;border-radius:50%;border:none;cursor:pointer;
  background:var(--ac);color:var(--on-brand);font-size:.95rem;flex:none;
  display:inline-flex;align-items:center;justify-content:center;
  transition:transform .12s var(--ease-spring),filter .12s}
.mix-play:hover{transform:scale(1.06);filter:brightness(1.08)}
.mix-time{font-variant-numeric:tabular-nums;font-size:.82rem;font-weight:600;color:var(--ink)}
.mix-hint{display:inline-flex;align-items:center;gap:6px}

.mix-lanes{position:relative;display:flex;flex-direction:column;gap:6px;
  background:var(--ink-900);border:1px solid var(--line);border-radius:10px;
  padding:10px 10px 10px 0;cursor:pointer;overflow:hidden}
.mix-lane{display:flex;align-items:center;gap:8px;min-height:28px}
/* A FIXED GUTTER, so every lane's zero is the same x. Without it the tracks
   start wherever their label happens to end and nothing lines up vertically —
   which is the one thing this drawing exists to show. */
.mix-lane-name{flex:none;width:104px;padding-left:10px;font-size:.72rem;font-weight:700;
  letter-spacing:.03em;text-transform:uppercase;color:var(--muted);
  display:inline-flex;align-items:center;gap:6px}
.mix-lane-name i{font-size:.9em}
.mix-lane.pic .mix-lane-name i{color:var(--lane-pic)}
.mix-lane.voice .mix-lane-name i{color:var(--lane-voice)}
.mix-lane.sfx .mix-lane-name i{color:var(--lane-sfx)}
.mix-lane.music .mix-lane-name i{color:var(--lane-music)}
.mix-track{position:relative;flex:1;height:26px;background:rgba(var(--paper-rgb),.04);
  border-radius:6px;transition:height .12s}
.mix-lane{align-items:stretch}
.mix-lane .mix-lane-name{align-self:center}
.mix-block{position:absolute;border-radius:5px;overflow:hidden;
  padding:0 6px;font-size:.68rem;font-weight:600;line-height:20px;white-space:nowrap;
  text-overflow:ellipsis;border:1px solid transparent;
  transition:filter .12s,box-shadow .12s}
.mix-block.shot{background:rgba(var(--lane-pic-rgb),.22);border-color:rgba(var(--lane-pic-rgb),.45);color:var(--paper)}
.mix-block.vo  {background:rgba(var(--lane-voice-rgb),.24);border-color:rgba(var(--lane-voice-rgb),.5);color:var(--paper)}
.mix-block.fx  {background:rgba(var(--lane-sfx-rgb),.24);border-color:rgba(var(--lane-sfx-rgb),.5);color:var(--paper)}
.mix-block.mus {background:rgba(var(--lane-music-rgb),.22);border-color:rgba(var(--lane-music-rgb),.45);color:var(--paper)}
/* A reading whose cue the script has lost. It still plays and is still paid for,
   so it is drawn — dashed, because where it sits is a guess. */
.mix-block.orphan{border-style:dashed;opacity:.72}
.mix-block.live{filter:brightness(1.35);box-shadow:0 0 0 1px rgba(var(--paper-rgb),.5)}

/* THE TRACKS' OWN BOX. 104px of label + the 8px lane gap on the left, the
   strip's 10px padding on the right — the same geometry every `.mix-track` gets
   from the flex row, expressed once so the playhead and the click handler share
   one zero with the blocks. */
.mix-playfield{position:absolute;top:0;bottom:0;left:112px;right:10px;
  z-index:3;pointer-events:none}
.mix-playhead{position:absolute;top:0;bottom:0;width:2px;background:var(--ac);
  box-shadow:0 0 8px rgba(var(--accent-rgb),.8)}
.mix-unplaced{display:flex;gap:7px;align-items:flex-start}
.mix-unplaced i{margin-top:2px;color:var(--lane-sfx)}

.mix-make{background:var(--surface);border:1px solid var(--line);border-radius:12px;
  padding:14px;display:flex;flex-direction:column;gap:10px}
.mix-make h4{display:flex;align-items:center;gap:8px;font-size:.92rem;margin-bottom:4px}
.mix-make h4 i{color:var(--ac)}
.mix-make-head p{margin:0 0 8px}
.mix-made{border-top:1px solid var(--line);padding-top:10px;display:flex;
  flex-direction:column;gap:8px}
.mix-made p{display:flex;gap:7px;align-items:flex-start;margin:0}
.mix-made p.bad{color:var(--rust-400)}
.mix-made-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.mix-made-name{font-size:.78rem;font-weight:600}
.mix-made-row audio{height:32px;flex:1;min-width:220px}
.fp-tomix{align-self:flex-start}

/* The playhead's gutter offset only holds while the labels are shown. Below
   that the lanes stack their name above the track, so it goes back to zero. */
@media (max-width:820px){
  .mix-lane{flex-wrap:wrap}
  .mix-lane-name{width:auto}
  .mix-track{flex-basis:100%}
  .mix-playfield{left:0;right:10px}
}

/* ---- THE BOARD, in the board panel ----------------------------------------
   The grouping and the reference picks, read before anything generates. Both
   decisions are deterministic and both cost a scene of GPU to get wrong, so
   they are shown rather than inferred from the output afterwards. */
.rd-board{border-top:1px solid var(--line);margin-top:10px;padding-top:10px}
.rd-board .rd-scene{margin:4px 0 6px;font-size:.78rem;color:var(--ink)}
.rd-refs{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
.rd-ref{margin:0;width:78px}
.rd-ref img{width:100%;height:52px;object-fit:cover;border:1px solid var(--line);
  border-radius:4px;display:block;background:var(--surface-2)}
/* The tag, not a caption. `<Picture 3>` is what the prompt says, so the label is
   the literal string rather than a friendly "third panel" — matching what the
   model was told is the whole reason this strip is here. */
.rd-ref figcaption{font-size:.58rem;color:var(--muted);text-align:center;margin-top:2px;
  font-family:var(--mono,ui-monospace,monospace)}

/* ---- THE LEDGER COLUMN ----------------------------------------------------
   Grey holds, amber changes, dim for a state nobody wrote. The colour is on the
   CHANGE rather than on the value, because a sheet of twelve identical chips
   with one amber row tells you where the sword comes out without reading a word
   — and two amber rows where there should be one is a continuity break you can
   see before you pay to render it. */
/* SIX TRACKS WHEN THERE IS A LEDGER, five when there is not. The header and the
   rows are siblings in one grid-per-row layout, so if they disagree on the track
   count every column after the missing one shifts — which is why the modifier
   sits on `.cr-sheet` and not on the row. */
.cr-sheet.has-ledger .cr-row{
  grid-template-columns:26px 40px minmax(110px,1fr) minmax(190px,3fr)
                        minmax(90px,1.1fr) minmax(74px,.8fr)}
.cr-c-l{min-width:0;overflow:hidden;text-align:right}
.cr-ledger.lg-hold{background:var(--ink-800);color:var(--muted);border-color:var(--line)}
.cr-ledger.lg-first{background:var(--ink-800);color:var(--paper-2);border-color:var(--line-strong)}
.cr-ledger.lg-change{background:rgba(var(--brand-rgb),.16);color:var(--amber-300);
  border-color:rgba(var(--brand-rgb),.42)}
/* Dimmer than a repeated value, deliberately. An unwritten state is worse than
   one that has not moved: the prompt says nothing and the model invents. */
.cr-ledger.lg-none{background:none;border-color:transparent;color:var(--muted);opacity:.45}
