/* Indy — PRISM ("first light"), app-wide dark rollout 2026-08-05.
 *
 * Thesis: cinema is captured light; Indy is the first light. The room is
 * black glass — every chrome surface achromatic (R=G=B) — and colour enters
 * only as information or as what light does through an edge: splits,
 * fringes, glows. Values translated from design/dark-tests/dark.css (base
 * `prism` block — the confirmed direction, 2026-08-04); the ten laws live
 * in that file's header and bind here too (laws 9 and 10 added, and laws 4
 * and 5 amended, 2026-08-25).
 *
 *  - chrome #61A0FF leads every interactive accent and is the only accent
 *    allowed to appear alone. "White and grey with a little blue in it"
 *    should describe any page.
 *  - vermillion #E63223 means LIVE: record, rendering, destructive. Only.
 *  - violet / cyan exist only inside dispersion devices, flanking blue.
 *  - light orange is the spectrum's warm tail and the attention state.
 *  - benchmark keeps its gold as a named information token (--bench) — a
 *    state colour Brett already reads, not decoration.
 *  - colour is never a material: no filled colour buttons, panels or bars.
 *
 * TYPE, 2026-09-14 (vault/24 §12): the interface is set in the Indy
 * wordmark's family. Barlow carries every word; Barlow Semi Condensed takes
 * labels, buttons and pills. Titles, labels, navigation and buttons are
 * capitals at 500-600 weight; reading text stays in sentence case. IBM Plex
 * Mono is for shot codes, ids and timecode only. Josefin Sans and IBM Plex
 * Sans have left the app.
 * BOXES, same day: every box carries one quiet achromatic gradient
 * (--box-*) and lines up with its neighbours on a 12-column grid (.g12).
 * Picture wells stay flat on the void.
 */

:root {
  /* Achromatic ramp — every value R=G=B. */
  --g0: #0E0E0E;   /* void — image-judging surround, lightbox, monitors */
  --g1: #161616;   /* ground — standard page */
  --g2: #1C1C1C;   /* surface — header, rails, wells */
  --g3: #222222;   /* card */
  --g4: #2A2A2A;   /* overlay — hover, popover, active well */
  --line:        #303030;
  --line-strong: #3D3D3D;

  --ink:       #DBDBDB;
  --ink-dim:   #9E9E9E;
  --ink-faint: #6F6F6F;
  --ink-ghost: #4B4B4B;
  --display-ink: #EFEFEF;
  --paper-ink:   #EFE6D6;   /* the logo mark's paper: front door type + wordmark (Brett, 2026-09-14) */
  --paper-dim:   #A39D93;   /* --ink-dim warmed to sit under --paper-ink */
  --paper-faint: #746E64;   /* --ink-faint warmed the same way */
  --brandtype: 'Barlow', 'Helvetica Neue', sans-serif;  /* Indy wordmark only */

  /* State semantics — fixed meanings, constant everywhere. */
  --ok:    #5FAE8C;   /* approved (cooled into the spectrum's family) */
  --warn:  #EFA063;   /* attention / feedback wanted — the light orange */
  --live:  #E63223;   /* vermillion — live, record, destructive. Nothing else. */
  --cut:   #6F6F6F;   /* cut / retired */
  --bench: #D6A84A;   /* benchmark — kept gold, information only */

  /* Prism light */
  --chrome:     #61A0FF;
  --chrome-dim: #41639C;
  --pr-violet: #A583FA;
  --pr-cyan:   #55CDE4;
  --pr-orange: #F2A46B;
  --pr-red:    #D9584A;
  --spectrum: linear-gradient(90deg, #A583FA, #61A0FF 38%, #55CDE4 72%, #F2A46B);
  --glow-chrome: 0 0 14px -2px color-mix(in srgb, var(--chrome) 65%, transparent);
  --glow-live:   0 0 16px -2px color-mix(in srgb, var(--live) 70%, transparent);
  --hairline: .6;

  /* Compat aliases — pre-rollout names re-pointed at the ramp so every
     existing rule keeps working. All alias chains resolve here at :root. */
  --surface-deep:  var(--g1);
  --surface-warm:  var(--ink);
  --surface-raise: var(--g2);
  --surface-card:  var(--g3);
  --rule:          var(--line);
  --bg:       var(--g1);
  --bg-raise: var(--g2);
  --bg-card:  var(--g3);
  --accent:        var(--chrome);   /* interactive accent = chrome blue */
  --accent-motion: var(--live);     /* literal again: live/destructive only */
  --accent-static: var(--chrome);   /* the old solar-highlight role → chrome */
  --solar:    var(--chrome);
  --midnight: var(--g2);
  --sand:     var(--ink);

  /* Faces — one family, the wordmark's (2026-09-14). */
  --display:  'Barlow', 'Helvetica Neue', Arial, sans-serif;
  --body:     'Barlow', 'Helvetica Neue', Arial, system-ui, sans-serif;
  --label:    'Barlow Semi Condensed', 'Barlow', 'Helvetica Neue', sans-serif;
  --mono:     'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  /* Back-compat alias for any rule that still asks for serif (screenplay) */
  --serif:    var(--body);

  /* Boxes (2026-09-14). Achromatic, so law 1 holds; a picture well never
     takes the gradient, a frame is judged on the flat void (--g0). */
  --box-bg:     linear-gradient(180deg, #242424 0%, #1D1D1D 100%);
  --box-line:   #2E2E2E;
  --box-shadow: inset 0 1px 0 rgba(255,255,255,.045), 0 1px 3px rgba(0,0,0,.3);
  --btn-bg:     linear-gradient(180deg, #232323 0%, #1B1B1B 100%);
  --btn-line:   #363636;
  --control-h:  36px;          /* one button height, everywhere */
}

* { box-sizing: border-box; }
html, body {
  margin: 0; padding: 0;
  background: var(--surface-deep);
  color: var(--surface-warm);
  font-family: var(--body);
  font-size: 16px;          /* brand base; √2 scale climbs from here */
  line-height: 1.55;
  font-weight: 400;
}

::selection { background: var(--chrome); color: #111; }

a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color .15s ease, color .15s ease;
}
a:hover { color: var(--chrome); border-bottom-color: var(--chrome-dim); }

/* Headings are WHITE, always — law 8. Barlow capitals at 600 with modest
   tracking is the product register; light, wide capitals are the Paper
   Dreams house voice and never appear here. One exception, the front door
   greeting: sentence case, Barlow 300, in the logo's paper (--paper-ink),
   no fringe. See .welcome-hail (Brett, 2026-09-14). */
h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: .06em;
  margin: 0 0 .4em;
  text-transform: uppercase;
  color: var(--display-ink);
}
h1 { font-size: 2.5rem; line-height: 1.05; }
h2 { font-size: 1.6rem; line-height: 1.12; letter-spacing: .08em; }
h3 { font-size: 1rem; line-height: 1.25; letter-spacing: .1em; }
/* h4 is the section-label role — the one small-caps label that may carry
   chrome. Semi Condensed with wider tracking: a label, not a headline. */
h4 {
  font-size: .72rem;
  font-family: var(--label);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .2em;
  color: var(--chrome);
}
/* Chromatic aberration — the display face read through glass. ~0.8px at
   hero size: lens character, not a broken screen. */
h1 {
  text-shadow: -.021em 0 0 color-mix(in srgb, var(--pr-violet) 30%, transparent),
                .021em 0 0 color-mix(in srgb, var(--pr-cyan) 26%, transparent);
}

/* Plex Mono is for codes, ids and timecode only. */
.mono, code, kbd {
  font-family: var(--mono);
  font-size: .78rem;
  letter-spacing: .04em;
}
label, .label, .meta {
  font-family: var(--body);
  font-size: .84rem;
  letter-spacing: 0;
}
/* A paragraph or disclosure marked .mono is prose (a hint, a status line),
   not a code, so it reads in Barlow. Inline .mono spans stay monospaced. */
p.mono, summary.mono, legend.mono, label.mono { font-family: var(--body); letter-spacing: .01em; }
/* The grey small-caps label. h4 is the one that may be blue (law 8). */
.lbl {
  font-family: var(--label); font-weight: 600;
  font-size: .72rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink-faint);
}
.dim { color: var(--ink-dim); }

/* The header carries the dispersion hairline along its bottom edge — the
   identity mark: every page opens on split light. Painted as a background
   layer (not a border, not a pseudo-element) so a void scrim can dim it
   via --hairline, and nothing is layered over anything. */
.site-header {
  display: flex; align-items: center; gap: 1.25rem;
  padding: calc(.35rem + 7.5px) 1.25rem calc(.35rem + 7.5px + 1px);  /* a slim bar: the page is the room, not the header */
  background-color: var(--g2);
  background-image:
    linear-gradient(rgba(14,14,14,calc(1 - var(--hairline))),
                    rgba(14,14,14,calc(1 - var(--hairline)))),
    var(--spectrum);
  background-size: 100% 1px, 100% 1px;
  background-position: bottom left, bottom left;
  background-repeat: no-repeat, no-repeat;
  position: sticky; top: 0; z-index: 5;
}
/* …and the light seeps out from under it. The header reads as a raised object
   lit from behind: the dispersion hairline is its hard bottom edge, and the
   same spectrum bleeds a few soft pixels onto the page below. Masked rather
   than blurred — a mask gives a dead-hard top (the line) with a smooth fade
   down, which is what light under a raised edge actually looks like. Opacity
   rides --hairline so dimming the line dims the glow with it. */
.site-header::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 100%;
  height: 4rem;
  pointer-events: none;
  background-image: var(--spectrum);
  opacity: calc(var(--hairline) * .45);
  -webkit-mask-image: linear-gradient(to bottom,
    rgba(0,0,0,.9) 0, rgba(0,0,0,.34) 26%, rgba(0,0,0,.10) 58%, rgba(0,0,0,0) 100%);
  mask-image: linear-gradient(to bottom,
    rgba(0,0,0,.9) 0, rgba(0,0,0,.34) 26%, rgba(0,0,0,.10) 58%, rgba(0,0,0,0) 100%);
}
/* Brand: the Indy lock-up — crease mark left of the wordmark (lock-up B,
   ~/Claude/projects/genesis/vault/21-indy-identity.md). The SVG paints its own
   ground in --g2, the header colour, so the fold lines sit on the bar with no
   box. Barlow 600 caps at .18em is the product register; the house (Paper
   Dreams) sets Barlow 300 at .44em, never here. */
.brand {
  display: inline-flex; align-items: center; gap: .5rem; flex: none;
  color: var(--paper-ink);
}
.brand-mark { width: 30px; height: 30px; display: block; }
.brand-word {
  font-family: var(--brandtype);
  font-weight: 600;
  font-size: .9rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  line-height: 1;
}
.brand:hover { color: var(--chrome); border-bottom-color: transparent; }
.project-nav {
  display: flex; align-items: center; gap: 1.05rem;
  /* Air between the lock-up and the rooms (Brett, 2026-09-27: 50px+). */
  margin-left: 3.5rem;
  /* Capitals run wider: below ~1300px the nav scrolls inside the header
     rather than widening every page. */
  min-width: 0; overflow-x: auto; scrollbar-width: none;
  padding-bottom: 6px; margin-bottom: -6px;
}
.project-nav::-webkit-scrollbar { display: none; }
/* Where the scrolled nav is cut off, the cut word fades so it reads as
   "more", not as a broken label. nav-place.js sets the classes (vault/47 A9). */
.project-nav.is-clip-end { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent); }
.project-nav.is-clip-start { -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 2.5rem), transparent); mask-image: linear-gradient(to left, #000 calc(100% - 2.5rem), transparent); }
.project-nav.is-clip-start.is-clip-end { -webkit-mask-image: linear-gradient(to right, transparent, #000 2.5rem, #000 calc(100% - 2.5rem), transparent); mask-image: linear-gradient(to right, transparent, #000 2.5rem, #000 calc(100% - 2.5rem), transparent); }
.project-nav a {
  font-family: var(--body);
  font-weight: 500;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-dim);
  white-space: nowrap;
}
.project-nav a:hover { color: var(--ink); border-bottom-color: var(--ink-faint); }
/* Active nav is one of blue's five sanctioned places — and it emits. */
.project-nav a.active {
  color: var(--chrome);
  border-bottom-color: var(--chrome);
  box-shadow: 0 2px 10px -4px var(--chrome);
}
/* Alpha tail. The settled rooms hold the pipeline; the rule cuts; the alpha
   rooms follow. The rule is the header's own dispersion hairline stood on
   end — same light, turned ninety degrees — so the device is the identity
   mark rather than a badge imported from somewhere else. Painted as its own
   vertical gradient, NOT a rotated sample of --spectrum: a horizontal
   gradient scaled into a 1px column resolves to one flat blue. */
.project-nav .nav-split {
  width: 1px; align-self: stretch;
  margin: -.15rem .25rem;
  background-image: linear-gradient(180deg,
    var(--pr-violet), var(--chrome) 38%, var(--pr-cyan) 72%, var(--pr-orange));
  opacity: .55;
}
/* Semi Condensed and ghosted, because it labels a group rather than naming
   a room — it must not read as another nav item you can click. */
.project-nav .nav-group-label {
  font-family: var(--label); font-weight: 600; font-size: .625rem;
  text-transform: uppercase; letter-spacing: .22em;
  color: var(--ink-ghost);
  align-self: center; margin-right: -.55rem;
  cursor: default; user-select: none;
}
.project-slug {
  margin-left: auto; font-family: var(--body); font-weight: 500;
  color: var(--ink-faint); font-size: .7rem;
  letter-spacing: .08em; text-transform: uppercase;
  white-space: nowrap; flex: none;
  max-width: 22ch; overflow: hidden; text-overflow: ellipsis;
}

/* The grouped title bar (web/title_bar.py, INDY_NEW_NAV=1). The film's
   name sits beside the lock-up behind a short rule; then five sections, the
   one you are in underlined in chrome (one of blue's sanctioned places). A
   section with rooms is a link with its list hanging under it on hover or
   focus, one row per room with a line saying what is in it. Not a scroller like
   .project-nav: five words always fit, and a clipped nav would clip its own
   menus. */
.title-bar-rule { width: 1px; height: 1.35rem; background: var(--line-strong); flex: none; }
.title-bar-name {
  /* The pipeline nav's own type (.project-slug), kept: Barlow 500 caps. */
  font-family: var(--body); font-weight: 500; font-size: .7rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--paper-ink);
  white-space: nowrap; flex: 0 1 auto; min-width: 4ch; max-width: 26ch;
  overflow: hidden; text-overflow: ellipsis;
}
/* The name is its own rename: press it and a one-field form hangs under it. */
.title-rename { position: relative; flex: 0 1 auto; min-width: 4ch; }
.title-rename > summary { list-style: none; cursor: text; display: block; padding: .2rem .4rem; margin: 0 -.4rem; border-radius: 6px; max-width: 26ch; }
.title-rename > summary::-webkit-details-marker { display: none; }
.title-rename > summary:hover, .title-rename[open] > summary { background: var(--g4); }
.title-rename > summary:focus-visible { outline: 2px solid var(--chrome); outline-offset: 1px; }
.title-rename > form {
  position: absolute; top: calc(100% + 8px); left: -.4rem; z-index: 8; display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  padding: .6rem; background: var(--g3); border: 1px solid var(--line-strong); border-radius: 10px; box-shadow: 0 24px 60px rgba(0,0,0,.55);
  width: max-content; max-width: calc(100vw - 2rem);
}
.title-rename input[type=text] { width: 20rem; max-width: calc(100vw - 9rem); }
.title-rename__note { flex-basis: 100%; font: 400 .72rem/1.3 var(--mono); color: var(--paper-faint); }
.title-nav { display: flex; align-items: center; gap: .25rem; margin-left: .5rem; flex: none; }
.title-nav__top {
  /* Set as the pipeline nav was (.project-nav a): Barlow 500 caps at .7rem,
     .08em, dim until pointed at; where you are is the chrome colour alone,
     no underline or glow (Didier, 2026-10-04). The menus changed, the type
     did not. */
  display: flex; align-items: center; gap: .3rem;
  height: 2rem; padding: 0 .55rem; border-radius: 4px; border: 0;
  font-family: var(--body); font-weight: 500; font-size: .7rem;
  text-transform: uppercase; letter-spacing: .08em; white-space: nowrap;
  color: var(--ink-dim); background: transparent; cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: color .15s, background .15s;
}
.title-menu > .title-nav__top { padding-right: .5rem; }
span.title-nav__top { cursor: default; }
.title-nav__top:hover { color: var(--ink); background: var(--g4); border-bottom-color: transparent; }
.title-nav__top.is-later { color: var(--ink-ghost); cursor: default; }
.title-nav__top.is-later:hover { color: var(--ink-ghost); background: transparent; }
.title-nav__top:focus-visible { outline: 2px solid var(--chrome); outline-offset: -2px; }
.title-nav__top.is-current { color: var(--chrome); }
.title-menu:hover > .title-nav__top, .title-menu:focus-within > .title-nav__top { color: var(--ink); background: var(--g4); }
.title-menu:hover > .title-nav__top.is-current, .title-menu:focus-within > .title-nav__top.is-current { color: var(--chrome); }
.title-nav__caret { width: .65rem; height: .65rem; stroke-width: 2.2; transform: rotate(90deg); transition: transform .15s; }
.title-menu:hover .title-nav__caret, .title-menu:focus-within .title-nav__caret { transform: rotate(-90deg); }
.title-menu { position: relative; }
.title-menu__list {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 7; width: 20rem;
  box-sizing: border-box; padding: .5rem; display: none; flex-direction: column; gap: 2px;
  background: var(--g3); border: 1px solid var(--line-strong); border-radius: 10px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .55);
}
.title-menu__item {
  display: flex; align-items: center; gap: .75rem; padding: .6rem .75rem;
  border-radius: 6px; border: 0; color: var(--ink); transition: background .12s;
}
a.title-menu__item:hover, a.title-menu__item:focus-visible { background: var(--g4); border: 0; }
.title-menu__item.is-here { background: color-mix(in srgb, var(--chrome) 10%, transparent); }
.title-menu__item.is-later { opacity: .45; cursor: default; }
.title-menu__dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--ink-faint); }
.title-menu__item.is-here .title-menu__dot { background: var(--chrome); }
.title-menu__item.is-later .title-menu__dot { background: var(--ink-ghost); }
.title-menu__text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.title-menu__label { font: 500 .95rem/1.2 var(--body); color: var(--paper-ink); }
.title-menu__desc { font: 400 .75rem/1.35 var(--body); color: var(--paper-dim); }
/* Open while the pointer or the keyboard is on the header or the list. The
   ::before bridges the gap under the header, so the pointer can cross it. */
.title-menu:hover > .title-menu__list, .title-menu:focus-within > .title-menu__list { display: flex; }
.title-menu__list::before { content: ""; position: absolute; left: 0; right: 0; top: -9px; height: 9px; }
/* The right end of the bar: the workspace and the account, as menus. Their
   lists hang from the right edge so they stay on the page. */
.title-nav--right { margin-left: auto; }
.title-nav--right .title-nav__top { max-width: 16rem; overflow: hidden; text-overflow: ellipsis; }
.title-menu--right > .title-menu__list { left: auto; right: 0; }
.title-menu__item--rule { border-top: 1px solid var(--line); border-radius: 0 0 6px 6px; margin-top: 4px; padding-top: .7rem; }
.title-menu__form { margin: 0; display: flex; }
button.title-menu__item { width: 100%; height: auto; background: none; border-left: 0; border-right: 0; border-bottom: 0; text-align: left; text-transform: none; letter-spacing: 0; font: inherit; cursor: pointer; box-shadow: none; }
button.title-menu__item:hover, button.title-menu__item:focus-visible { background: var(--g4); }
/* The help button's own auto margin would split the free space with these. */
.title-nav--right ~ .help-open { margin-left: .2rem; }
/* Narrow windows (Didier, 2026-10-04): the bar keeps every entry on the
   page. Below 1100px the section menu takes a row of its own under the
   lock-up, the film's name and the account menus; below 640px its entries
   wrap. Never a sideways-scrolling row: that would clip the dropdowns. The
   film's name keeps a readable width and ends in an ellipsis. */
@media (max-width: 1100px) {
  .site-header { flex-wrap: wrap; row-gap: .15rem; }
  .site-header > .title-nav:not(.title-nav--right) { order: 10; flex: 1 1 100%; margin-left: -.55rem; }
  .site-header > .title-bar-name, .site-header > .title-rename { min-width: 6ch; max-width: 40vw; }
  .site-header > .title-bar-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}
@media (max-width: 640px) {
  .site-header { gap: .5rem; padding-left: .9rem; padding-right: .9rem; }
  .site-header > .title-bar-rule { display: none; }
  .site-header > .title-nav:not(.title-nav--right) { flex-wrap: wrap; row-gap: .1rem; flex-basis: calc(100% - 7rem); }
  /* The job light and help sit at the end of the section row, beside it. */
  .site-header > .tally-anchor, .site-header > .help-open { order: 11; margin-left: 0; }
  .title-nav--right { gap: 0; }
  .title-nav--right .title-nav__top { max-width: 6.5rem; padding: 0 .35rem; }
  .site-header > .title-bar-name, .site-header > .title-rename { max-width: 28vw; }
  .site-header .brand-word { display: none; }
}
.title-menu__note {
  margin: .4rem .75rem .25rem; padding-top: .5rem; border-top: 1px solid var(--line);
  font: 400 .75rem/1.4 var(--body); color: var(--paper-faint);
}

main { max-width: 1280px; margin: 0 auto; padding: 2.5rem 2rem 6rem; }

/* One scrollbar rule for a pane that scrolls inside the page (Indy redesign,
   global rules): thin, and not there until the pane is pointed at or holds
   focus. Ink-coloured on a light surface (the script's white sheet). */
.soft-scroll { scrollbar-width: thin; scrollbar-color: transparent transparent; scrollbar-gutter: stable; }
.soft-scroll:hover, .soft-scroll:focus-within { scrollbar-color: rgba(239,230,214,.22) transparent; }
.soft-scroll::-webkit-scrollbar { width: 6px; height: 6px; }
.soft-scroll::-webkit-scrollbar-track { background: transparent; }
.soft-scroll::-webkit-scrollbar-thumb { background: transparent; border-radius: 3px; }
.soft-scroll:hover::-webkit-scrollbar-thumb, .soft-scroll:focus-within::-webkit-scrollbar-thumb { background: rgba(239,230,214,.22); }
.soft-scroll--paper:hover, .soft-scroll--paper:focus-within { scrollbar-color: rgba(29,27,24,.25) transparent; }
.soft-scroll--paper:hover::-webkit-scrollbar-thumb, .soft-scroll--paper:focus-within::-webkit-scrollbar-thumb { background: rgba(29,27,24,.25); }
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Canvas page goes full-bleed: a viewport-tall flex column (header + canvas),
   no max-width cap, no page padding, no footer. The node graph fills the rest.
   The whole app is dark now; what remains of the old page-dark split is
   law 1's second clause — image-judging pages drop the room to the void. */
body.page-canvas {
  height: 100vh; display: flex; flex-direction: column; overflow: hidden;
}
/* page-canvas = full-bleed judging tools (canvas, timeline); page-dark =
   scrolling pages that judge images (design rooms, board, gallery). Both
   sit on the void so no tinted surround biases the frames being graded. */
body.page-canvas, body.page-dark {
  background: var(--g0);
}
/* Below a short body the canvas is painted by <html>, which is g1: the
   void pages carry their ground down to the window's foot (vault/47 A11). */
html:has(body.page-canvas), html:has(body.page-dark) { background: var(--g0); }
body.page-canvas main { max-width: none; margin: 0; padding: 0; flex: 1; min-height: 0; }
body.page-canvas .site-footer { display: none; }
body.page-canvas #canvas-root { height: 100%; margin: 0; }

.flashes { list-style: none; padding: 0; margin: 1rem 2rem; }
.flash {
  padding: .6rem 1rem; border-left: 3px solid var(--chrome);
  background: var(--box-bg); font-family: var(--body); font-size: .92rem;
  letter-spacing: 0; margin-bottom: .3rem;
}
.flash-error { border-left-color: var(--warn); }
.flash-ok { border-left-color: var(--ok); }
/* keep-place.js put the page back far from the top, and the flash says why an
   action was refused: it rides under the header until clicked or ten seconds
   pass, then goes back to its place. Its own space at the top never moves. */
.flashes--docked { position: sticky; z-index: 5; cursor: pointer; }
.flashes--docked .flash { background-color: var(--g2); }

/* An action's return crossfades over the page you left. keep-place.js has
   already put the new page where the old one was, so the pixels that match
   stay still and only what the action changed fades. Every other navigation
   skips the transition and cuts, as before (keep-place.js, base.html). */
@view-transition { navigation: auto; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .2s; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

.site-footer {
  padding: 2rem; text-align: center; border-top: 1px solid var(--rule);
  font-family: var(--body); font-size: .8rem; letter-spacing: .01em;
  color: var(--ink-dim);
}

/* ---- Buttons / forms ----
   Colour is never a fill (law 1): every button is a ghost. Emphasis comes
   from the chrome border + glow, never from flooding a shape. Warn stays
   vermillion because destructive IS the live colour's meaning.

   The one height below is for CONTROLS — a word or an icon you press. A
   <button> that HOLDS something (a picture, a filename, a pill sitting among
   pills) is an element for the keyboard's sake, not a control, and it inherits
   this height by tag: an 88px take becomes a 36px slot its picture spills out
   of. Such a rule says `height: auto` where it is written. */
button, .btn {
  font-family: var(--label);
  font-size: .75rem; letter-spacing: .14em; text-transform: uppercase;
  font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  box-sizing: border-box;
  height: var(--control-h);
  padding: 0 1rem;
  line-height: 1;
  white-space: nowrap;
  background: var(--btn-bg);
  color: var(--ink);
  border: 1px solid var(--btn-line);
  cursor: pointer;
  transition: border-color .15s, color .15s, background .15s, box-shadow .15s;
}
button:disabled, .btn[disabled] { opacity: .45; cursor: not-allowed; }
button:hover, .btn:hover { border-color: var(--chrome); color: var(--chrome); }
.btn-primary {
  color: var(--chrome); border-color: var(--chrome);
  font-weight: 600;
}
.btn-primary:hover {
  background: var(--g4); box-shadow: var(--glow-chrome);
}
.btn-warn { border-color: var(--live); color: var(--live); }
.btn-warn:hover {
  background: var(--g4); box-shadow: var(--glow-live);
  color: orangered; border-color: orangered;
}

/* Sound room — FX section's per-scene palette cards. */
.sfx-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: .8rem;
}
/* Shot rows under a scene's palette: the cue list IS the shot list. The code
   carries the row's state in colour alone (ghost = this shot has no sound
   note, chrome = it has one) and links back to its board panel. */
.sfx-shots { margin-top: .6rem; padding-top: .5rem;
             border-top: 1px solid var(--rule);
             display: flex; flex-direction: column; gap: .3rem; }
.sfx-shot-row { display: flex; gap: .45rem; align-items: flex-start; }
.sfx-shot-code { flex: 0 0 auto; min-width: 3rem; padding-top: .5rem;
                 font-size: .72rem; text-decoration: none;
                 color: var(--ink-ghost); }
.sfx-shot-code:hover { color: var(--ink-dim); }
.sfx-shot-code.filled { color: var(--chrome); }
.sfx-shot-row textarea { flex: 1 1 auto; min-width: 0; resize: vertical;
                         font-size: .9rem; line-height: 1.45;
                         field-sizing: content; max-height: 10rem; }
.sfx-shot-row .btn-icon { flex: 0 0 auto; margin-top: .25rem; }

input[type=text], input[type=url], input[type=number], input[type=email],
input[type=password], input[type=search], textarea, select {
  background: var(--surface-raise); color: var(--surface-warm);
  border: 1px solid var(--rule); padding: .55rem .8rem;
  font-family: var(--body); font-size: 1rem; width: 100%;
}
textarea { font-family: var(--body); min-height: 4rem; line-height: 1.5; }
input:focus, textarea:focus, select:focus {
  outline: none; border-color: var(--chrome);
  box-shadow: 0 0 0 1px var(--chrome);
}

form .row { display: grid; grid-template-columns: 1fr; gap: .8rem; margin-bottom: 1rem; }
form .row.two { grid-template-columns: 1fr 1fr; }
form .row.three { grid-template-columns: 2fr 1fr 1fr; }
form label {
  color: var(--ink-dim);
  display: block; margin-bottom: .25rem;
  font-family: var(--label); font-size: .72rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .14em;
}

/* ---- Pills / chips ---- */
.pill {
  display: inline-block; padding: .15rem .6rem;
  border: 1px solid var(--rule); border-radius: 999px;
  color: var(--ink-dim); margin-right: .25rem; font-size: .68rem; font-weight: 600;
  font-family: var(--label); letter-spacing: .12em; text-transform: uppercase;
}
.pill-accent { color: var(--accent); border-color: var(--accent); }
.pill-warn { color: var(--warn); border-color: var(--warn); }
.pill-ok { color: var(--ok); border-color: var(--ok); }
/* The board's motion chip: a pulse while the take renders, then the count. */
.motion-pill { display: inline-flex; align-items: center; gap: .35rem; text-decoration: none; }
.motion-pill .pulse-dot { width: .4rem; height: .4rem; }

/* ---- Cards / grids ---- */
.card { background: var(--box-bg); border: 1px solid var(--box-line); box-shadow: var(--box-shadow); padding: 1.4rem; margin-bottom: 1rem; }
.box { background: var(--box-bg); border: 1px solid var(--box-line); box-shadow: var(--box-shadow); }
/* Twelve columns over the 1216px content width, 24 between: boxes start on
   a column line, and boxes in a row share their top and bottom edges. */
.g12 { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: 24px; }
.card h3 { margin-top: 0; }

.grid { display: grid; gap: 1rem; }
.grid-3 { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.grid-2 { grid-template-columns: 1fr 1fr; }

.kv { display: grid; grid-template-columns: 12rem 1fr; gap: .4rem 1.2rem; align-items: start; }
.kv .label { color: var(--ink-dim); align-self: start; }

.tabs { display: flex; gap: 0; border-bottom: 1px solid var(--rule); margin-bottom: 1.5rem; }
.tab {
  padding: .6rem 1rem; font-family: var(--label); font-size: .72rem;
  text-transform: uppercase; letter-spacing: .14em; font-weight: 600;
  border-bottom: 2px solid transparent; color: var(--ink-dim);
}
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.tab:hover { color: var(--surface-warm); border-bottom-color: transparent; }

table.list { width: 100%; border-collapse: collapse; font-size: .92rem; }
table.list th, table.list td { padding: .65rem .8rem; border-bottom: 1px solid var(--rule); text-align: left; }
table.list th {
  font-family: var(--label); font-size: .68rem;
  text-transform: uppercase; letter-spacing: .14em;
  color: var(--ink-dim); font-weight: 600;
}
table.list tr:hover td { background: var(--surface-raise); }

/* ---- Entity rooms (Production Design hub) ---- */
.entity-rooms { display: flex; flex-direction: column; gap: 2rem; }
.entity-section { display: flex; flex-direction: column; gap: .85rem; }
.entity-section { margin-top: 2.4rem; }
.entity-section-head { margin: 0; font-size: 1.15rem; }
.entity-section-count { font-size: .8rem; margin-left: .35rem; }
.entity-section-sub { margin: -.25rem 0 .35rem; font-size: .82rem; }

/* ---- Character design: hero + sheet, big ---- */
.design-two-up {
  /* minmax(0,1fr): a grid item's default min-width:auto lets a wide sheet
     image (16:9, 1376px natural) blow the column out and force the whole
     page to scroll horizontally. */
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1.4rem;
}
@media (max-width: 720px) { .design-two-up { grid-template-columns: minmax(0, 1fr); } }
.design-slot {
  display: flex; flex-direction: column; gap: .55rem;
  border: 1px solid var(--rule); background: var(--bg-card);
  padding: .9rem; border-radius: 4px;
  min-width: 0;
}
.design-slot-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.design-slot-label { font-family: var(--display); font-weight: 600; font-size: .82rem; letter-spacing: .1em; text-transform: uppercase; }
.design-hero-img {
  /* Size to the image's own aspect (width:auto, centred) — width:100% +
     object-fit:contain letterboxed narrow images in black side bars. */
  max-width: 100%; max-height: 460px; width: auto; margin: 0 auto;
  object-fit: contain;
  background: var(--g0); border-radius: 3px; display: block;
}
.design-slot-empty {
  min-height: 180px; display: flex; align-items: center; justify-content: center;
  text-align: center; padding: 1.2rem; border: 1px dashed var(--rule); border-radius: 3px;
  font-size: .85rem;
}
.design-slot-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.design-clear { margin-top: .2rem; }

/* Visual hero/sheet picker — click a thumbnail to designate. */
.design-candidates {
  display: flex; gap: .4rem; overflow-x: auto; padding: .25rem .1rem .4rem;
  scrollbar-width: thin;
}
.design-candidates form { margin: 0; flex: 0 0 auto; }
.design-candidate {
  padding: 0; border: 2px solid var(--rule); background: var(--g0); cursor: pointer;
  border-radius: 3px; width: 64px; height: 64px; overflow: hidden; display: block;
  transition: border-color .12s ease, transform .12s ease;
}
.design-candidate img { width: 100%; height: 100%; object-fit: cover; display: block; }
.design-candidate:hover { border-color: var(--accent); transform: translateY(-2px); }
.design-candidate.state-benchmark { border-color: var(--bench); }
.design-candidate.is-current { border-color: var(--ok); box-shadow: 0 0 0 2px var(--ok) inset; }
.design-warn { margin: .2rem 0 0; font-size: .72rem; color: var(--warn); }
/* Geometry variant switcher — an entity with several bodies (Drone passive vs
   attack, Scout Ship open vs closed) picks one for the viewer. Keyline-only
   current state, matching the approve/reject buttons. */
.geo-variants { display: flex; gap: .3rem; flex-wrap: wrap; padding: .3rem 0 .35rem; }
.geo-variant {
  font-family: var(--label); font-weight: 600; font-size: .68rem; letter-spacing: .12em;
  text-transform: uppercase; height: 28px;
  padding: 0 .6rem; border: 1px solid var(--rule); border-radius: 0;
  background: var(--g0); color: var(--dim); cursor: pointer;
  transition: border-color .12s ease, color .12s ease;
}
.geo-variant:hover { border-color: var(--accent); color: var(--fg); }
.geo-variant.is-current { border-color: var(--accent); color: var(--accent); }
/* Pull the character design visual above the text brief on entity pages. */
.entity-design-stack { display: flex; flex-direction: column; }
.entity-design-stack > #designs { order: -1; }
.design-warn-row { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin: .2rem 0 0; }
.design-warn-row .design-warn { margin: 0; }
.design-lock-pill {
  display: inline-block; padding: .1rem .5rem; border-radius: 999px;
  border: 1px solid var(--ok); color: var(--ok); font-family: var(--label); font-weight: 600;
  font-size: .68rem; letter-spacing: .12em; text-transform: uppercase;
}
.mj-upload { display: flex; flex-direction: column; gap: .35rem; margin-top: .4rem; }

/* ---- Entity gallery: scene appearances, small thumbs ---- */
.entity-gallery-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: .5rem;
}
.entity-gallery-tile {
  display: block; aspect-ratio: 16/9; overflow: hidden; border: 1px solid var(--rule);
  border-radius: 3px; background: var(--g0);
}
.entity-gallery-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.entity-gallery-tile.state-benchmark { border-color: var(--bench); }
.entity-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 1rem; }
.entity-grid-hero { grid-template-columns: 1fr; }
.entity-card { display: flex; flex-direction: column; background: var(--bg-card); border: 1px solid var(--rule); color: inherit; text-decoration: none; transition: border-color .12s ease; }
.entity-card:hover { border-color: var(--accent); border-bottom-color: var(--accent); }
.entity-card-hero { flex-direction: row; }
.entity-card-hero .entity-card-thumb { flex: 0 0 38%; aspect-ratio: 16/9; }
.entity-card-hero .entity-card-body { flex: 1; padding: 1.4rem 1.6rem; }
.entity-card-hero .entity-card-name { font-size: 1.4rem; margin-bottom: .35rem; }
.entity-card-thumb { aspect-ratio: 1/1; background: var(--surface-raise); display: flex; align-items: center; justify-content: center; overflow: hidden; }
.entity-card-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.entity-card-empty .entity-card-thumb { background: linear-gradient(135deg, var(--surface-raise) 0%, var(--rule) 100%); }
.entity-card-empty-icon { font-family: var(--mono); font-size: 2.2rem; color: var(--ink-dim); }
.entity-card-body { padding: .75rem 1rem .85rem; display: flex; flex-direction: column; gap: .35rem; flex: 1; }
.entity-card-name { font-size: 1rem; margin: 0; line-height: 1.25; }
.entity-card-desc { margin: 0; color: var(--ink-dim); font-size: .78rem; line-height: 1.4; }
.entity-card-pips { display: flex; gap: .35rem; margin-top: auto; flex-wrap: wrap; padding-top: .35rem; }
.entity-pip { font-family: var(--label); font-size: .62rem; letter-spacing: .04em; padding: .15rem .45rem; border: 1px solid var(--rule); color: var(--ink-dim); }
.entity-pip.on { color: var(--accent); border-color: var(--accent); }
.entity-pip.off { opacity: .55; }
.entity-pip-bench { color: var(--bench); border-color: var(--bench); }

/* ---- Ref gallery ---- */
.refs-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 1.2rem; }
/* Overview "Latest renders": minimal image-only .ref-cards — click → lightbox, hover → delete. No card chrome. */
.refs-grid--overview .ref-card { position: relative; background: none; border: 0; box-shadow: none; }
/* Kill the review-state chrome (proposed left-stripe, benchmark gold border/star) — overview tiles are image-only. */
.refs-grid--overview .ref-card.state-proposed, .refs-grid--overview .ref-card.state-benchmark { border: 0; box-shadow: none; }
.refs-grid--overview .ref-card.state-benchmark::before { display: none; }
/* Research triage: a ref card + an inline role selector to re-bucket it. */
.research-tile { display: flex; flex-direction: column; gap: .4rem; }
.research-tile .ref-card { flex: 1; }
.role-picker { width: 100%; font-family: var(--mono); font-size: .68rem; padding: 4px 6px; background: var(--surface-raise); color: var(--ink); border: 1px solid var(--rule); border-radius: 3px; cursor: pointer; transition: border-color .15s; }
.role-picker:hover { border-color: var(--chrome); }
.role-picker--ok { border-color: var(--ok); }
.role-picker--err { border-color: var(--warn); }
.refs-grid--overview .ref-card .thumb { border-radius: 4px; cursor: zoom-in; }
.refs-grid--overview .ref-del { position: absolute; top: 6px; right: 6px; opacity: 0; transition: opacity .12s, background .12s; font-family: var(--label); font-size: .58rem; letter-spacing: .04em; text-transform: uppercase; line-height: 1; padding: 5px 8px; border-radius: 3px; border: 0; cursor: pointer; background: rgba(0,0,0,.72); color: var(--ink); }
.refs-grid--overview .ref-card:hover .ref-del, .refs-grid--overview .ref-del:focus-visible { opacity: 1; }
.refs-grid--overview .ref-del:hover { background: var(--warn); color: var(--surface-deep); }
.ref-card { background: var(--bg-card); border: 1px solid var(--rule); display: flex; flex-direction: column; }
.ref-card .thumb { width: 100%; aspect-ratio: 16/10; background: var(--g0); object-fit: cover; display: block; }
.ref-card.prov-storyboard_gen .thumb { aspect-ratio: 16/9; object-fit: contain; }
.ref-card .thumb-placeholder { width: 100%; aspect-ratio: 16/10; background: var(--surface-raise); display: flex; align-items: center; justify-content: center; color: var(--ink-dim); font-family: var(--label); font-size: .75rem; }
/* PDF drawing thumbnail: page-1 raster on a doc-icon backdrop, so a failed
   raster (img self-removes via onerror) degrades to the icon. */
.ref-card .thumb-link--doc { position: relative; display: block; background: var(--surface-raise); }
.ref-card .thumb-link--doc::before { content: "doc"; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-family: var(--label); font-size: .8rem; letter-spacing: .3em; text-transform: uppercase; color: var(--ink-faint); }
.ref-card .thumb-link--doc .thumb { position: relative; }
.thumb-doc-badge { position: absolute; bottom: 6px; right: 6px; background: rgba(0,0,0,.72); color: var(--ink); font-size: .58rem; padding: 1px 6px; border-radius: 3px; text-transform: uppercase; letter-spacing: .06em; }
.ref-card .thumb-placeholder--doc { flex-direction: column; gap: .35rem; text-decoration: none; cursor: pointer; transition: background .12s, color .12s; }
.ref-card .thumb-placeholder--doc:hover { background: var(--surface-card); color: var(--accent); }
.thumb-placeholder-icon { font-size: 1.8rem; line-height: 1; }
.thumb-placeholder-kind { text-transform: uppercase; letter-spacing: .08em; }
.ref-card .thumb-fallback {
  width: 100%; aspect-ratio: 16/10;
  background: linear-gradient(135deg, var(--surface-raise) 0%, var(--rule) 100%);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .25rem;
  color: var(--ink-dim); font-family: var(--body);
  border-bottom: 1px solid var(--rule);
  transition: background .15s, color .15s;
}
.ref-card .thumb-fallback:hover { background: linear-gradient(135deg, var(--g4) 0%, var(--g2) 100%); color: var(--accent); border-bottom-color: var(--accent); }
.ref-card .thumb-fallback-icon { font-size: 1.6rem; color: var(--accent); line-height: 1; }
.ref-card .thumb-fallback-host { font-size: .72rem; letter-spacing: .06em; }
.ref-card .thumb-fallback-hint { font-size: .65rem; text-transform: uppercase; letter-spacing: .14em; opacity: .65; }
.ref-card .body { padding: .8rem 1rem; flex-grow: 1; display: flex; flex-direction: column; gap: .45rem; }
/* Titles can never escape their card again (machine-filename strings used
   to overlap neighbours). Clamped so one verbose title can't stretch a row. */
.ref-card .body { min-width: 0; }
.ref-card .title {
  font-size: .98rem; line-height: 1.3;
  overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.ref-card .meta { color: var(--ink-dim); display: flex; flex-wrap: wrap; gap: .35rem; }
.ref-card .actions { display: flex; flex-wrap: wrap; gap: .3rem; row-gap: .35rem; padding: .5rem 1rem; border-top: 1px solid var(--rule); align-items: center; }
.ref-card .actions form { display: inline; }
.ref-card .actions button, .ref-card .actions .btn { height: 30px; padding: 0 .55rem; font-size: .7rem; white-space: nowrap; }
.ref-card .actions .promote-panel { margin-left: auto; }  /* push the promote affordance to the right edge */

/* Inline promote-to-benchmark dialog: clicking the ★ benchmark summary
   expands an inline form for source_artist + film. Stays inside the card,
   so the grid row just grows taller. */
.promote-panel { display: inline-block; }
/* When the panel is open it needs to span the row so the form below can lay
   out at full card width instead of being squeezed alongside the summary. */
.promote-panel[open] { flex-basis: 100%; display: block; margin-left: auto; }
.promote-panel > summary {
  list-style: none;
  cursor: pointer;
  display: inline-block;
  padding: .3rem .55rem;
  font-size: .7rem;
  white-space: nowrap;
  border: 1px solid var(--bench);
  color: var(--bench);
  background: transparent;
  font-family: inherit;
  user-select: none;
}
.promote-panel > summary::-webkit-details-marker { display: none; }
.promote-panel > summary:hover { background: var(--bench); color: var(--bg); }
.promote-panel[open] > summary { background: var(--bench); color: var(--bg); }
.promote-form {
  margin-top: .55rem;
  padding: .65rem .75rem;
  background: rgba(214, 168, 74, 0.07);
  border: 1px solid rgba(214, 168, 74, 0.4);
  display: flex;
  flex-direction: column;
  gap: .4rem;
}
.promote-field { display: flex; flex-direction: column; gap: .2rem; }
.promote-field > span { font-size: .65rem; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-dim); }
.promote-field input[type=text] {
  background: var(--bg-card);
  color: var(--ink);
  border: 1px solid var(--rule);
  padding: .35rem .5rem;
  font-size: .8rem;
  font-family: inherit;
}
.promote-field input[type=text]:focus { outline: none; border-color: var(--bench); }
.promote-actions { display: flex; justify-content: flex-end; margin-top: .15rem; }
.promote-actions button { padding: .35rem .8rem; font-size: .75rem; }
/* Proposed is the DEFAULT state of fresh material — flagging the default
   in vermillion turned review grids into walls of red. Solar left edge =
   static "pending" highlight (the brand's static-highlight token), quiet
   at scale. Vermillion stays reserved for live/running. */
.ref-card.state-proposed { border-color: var(--rule); border-left: 3px solid var(--warn); }
.ref-card.state-rejected, .ref-card.state-archived { opacity: .5; }
/* Benchmark: the calibration wall. Warm-tone border + corner star so it reads
   as 'pinned exemplar' rather than 'one more approved ref'. */
.ref-card.state-benchmark { border-color: var(--bench); box-shadow: 0 0 0 1px rgba(214, 168, 74, 0.3) inset; }
.ref-card.state-benchmark::before {
  content: "★";
  position: absolute; top: .35rem; right: .55rem;
  color: var(--bench); font-size: 1.1rem; line-height: 1;
  text-shadow: 0 0 4px rgba(0,0,0,.7);
  pointer-events: none;
  z-index: 2;
}
.ref-card { position: relative; }
.btn-benchmark { border-color: var(--bench); color: var(--bench); }
.btn-benchmark:hover { background: var(--bench); color: var(--bg); border-color: var(--bench); }
.filter-chip-benchmark { border-color: var(--bench) !important; color: var(--bench); }
.filter-chip-benchmark.active { background: var(--bench) !important; color: var(--bg) !important; border-color: var(--bench) !important; }

/* Editable metadata form on ref_detail — compact inline layout. */
.meta-form { display: flex; flex-wrap: wrap; gap: .55rem; align-items: flex-end; margin-top: .4rem; }
.meta-form .meta-field { display: flex; flex-direction: column; gap: .2rem; flex: 1 1 12rem; min-width: 10rem; }
.meta-form .meta-label { font-size: .7rem; color: var(--ink-dim); letter-spacing: .06em; text-transform: uppercase; }
.meta-form input[type=text] { background: var(--bg-card); color: var(--ink); border: 1px solid var(--rule); padding: .35rem .55rem; font-size: .85rem; font-family: inherit; }
.meta-form input[type=text]:focus { outline: none; border-color: var(--accent); }

/* ---- Scene/script-style elements ---- */
.slug-line { font-family: var(--mono); text-transform: uppercase; color: var(--accent); letter-spacing: .08em; }

/* ---- Script page: Hollywood-format viewer ---- */
.script-toolbar { display: flex; align-items: flex-end; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; margin-bottom: 1.6rem; }
.script-toolbar-meta { flex: 1 1 auto; }
.script-title { margin: 0 0 .15em; }
.script-byline { margin: 0; font-family: var(--body); font-size: .78rem; letter-spacing: .06em; }
.script-toolbar-actions { display: flex; gap: .5rem; }

.screenplay-page {
  background: #EFEFEF; /* neutral paper — the document is content, not chrome */
  padding: 2rem 0;
  border: 1px solid var(--rule);
  max-height: 84vh;
  overflow-y: auto;
  scroll-behavior: smooth;
}
.screenplay {
  background: #ffffff;
  color: #111;
  font-family: 'Courier Prime', 'Courier New', Courier, monospace;
  font-size: 12pt;
  line-height: 1.25;
  width: 8.5in;
  max-width: 100%;
  margin: 0 auto;
  padding: 1in 1in 1in 1.5in;
  box-shadow: 0 2px 18px rgba(0,0,0,.35);
}
.screenplay .sp-blank { height: 1em; }
.screenplay .sp-slug { font-family: inherit; font-size: 12pt; font-weight: 700; text-transform: uppercase; letter-spacing: 0; margin: 1.5em 0 .5em; color: #111; }
.screenplay .sp-action { margin: 0 0 .25em; white-space: pre-wrap; }
.screenplay .sp-cue { margin: 1em 0 0 3.5in; text-transform: uppercase; }
.screenplay .sp-paren { margin: 0 0 0 2.5in; font-style: italic; }
.screenplay .sp-dialogue { margin: 0 1.5in 0 1.5in; }
.screenplay .sp-trans { text-align: right; text-transform: uppercase; margin: 1em 0; }
/* Act and part markers: production structure, so they sit centred and apart
   from the scene rather than reading as a line someone says. */
.screenplay .sp-section { text-align: center; text-transform: uppercase; margin: 2em 0 1.5em; letter-spacing: .12em; }
.screenplay .sp-link { color: #111; border-bottom: 1px dotted #41639C; text-decoration: none; transition: background .15s, color .15s; padding: 0 .05em; }
.screenplay .sp-link:hover { background: #E4E4E4; color: #41639C; }
.screenplay-hint { margin-top: .8rem; font-family: var(--body); font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; }

/* ---- Inline generation cards ---- */
/* One card per fired gen, appended where the user clicked: the image on
   success, the provider's whole error on failure. The pending card is the
   htmx request indicator — visible only mid-flight. */
.iter-results { display: flex; flex-direction: column; gap: .6rem; }
/* Only take up space once something is actually showing (a finished card,
   or the pending indicator mid-flight). */
.iter-results:has(.gen-card-ok, .gen-card-fail, .gen-card-prompt, .htmx-request) { margin-bottom: 1rem; }
.gen-card {
  display: flex; gap: .9rem; align-items: flex-start;
  border: 1px solid var(--rule); background: var(--g3);
  padding: .7rem .9rem;
}
.gen-card-ok { border-left: 3px solid var(--accent); }
.gen-card-fail { border-left: 3px solid #a05252; }
.gen-card-thumb { flex: 0 0 auto; display: block; width: 9rem; }
.gen-card-thumb img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block; }
.gen-card-body { display: flex; flex-direction: column; gap: .3rem; min-width: 0; flex: 1; }
.gen-card-status { font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-dim); }
.gen-card-fail .gen-card-status { color: #c97b7b; }
.gen-card-title { color: var(--ink); }
.gen-card-meta, .gen-card-note { font-size: .72rem; letter-spacing: .04em; }
.gen-card-error {
  margin: 0; padding: .5rem .6rem;
  background: var(--g0); border: 1px solid var(--rule);
  font-size: .78rem; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word;
  max-height: 12rem; overflow-y: auto;
  color: var(--ink-dim);
}
.gen-card-actions { display: flex; gap: .5rem; margin-top: .2rem; }
/* A prompt for a tool with no API here (Midjourney): the words, a copy
   button, and the way back in for the picture. */
.gen-card-prompt { border-left: 3px solid var(--accent); }
/* Arrived at by #draft-<id> (?draft=): clear the sticky site header. */
.gen-card-prompt { scroll-margin-top: 6rem; }
.gen-card-words {
  margin: 0; padding: .5rem .6rem;
  background: var(--g0); border: 1px solid var(--rule);
  font-size: .8rem; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word;
  max-height: 12rem; overflow-y: auto;
  color: var(--ink);
}
.gen-card-pics { display: flex; flex-wrap: wrap; gap: .4rem; }
.gen-card-pics a { display: block; width: 4.5rem; }
.gen-card-pics img { width: 100%; aspect-ratio: 1; object-fit: cover; display: block; }
.gen-card-prompt .gen-card-actions { flex-wrap: wrap; align-items: center; }
.gen-card-prompt .gen-card-actions form { display: contents; }
.gen-card-pending { display: none; border-left: 3px solid var(--rule); }
.gen-card-pending.htmx-request { display: flex; }
.gen-card-pending .gen-card-status { animation: gen-pulse 1.2s ease-in-out infinite; }
/* Long-job status card (board draft): always visible while polling. */
.gen-card-running { border-left: 3px solid var(--rule); }
.gen-card-running .gen-card-status { animation: gen-pulse 1.2s ease-in-out infinite; }
@keyframes gen-pulse {
  0%, 100% { opacity: .45; }
  50% { opacity: 1; }
}

/* The read view when the .fountain on disk is ahead of the last ingest —
   same honesty pattern as the board's stale flags. */
.script-stale-note {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  margin-bottom: 1rem; padding: .6rem .9rem;
  border: 1px solid var(--rule); border-left: 3px solid var(--accent);
  font-family: var(--body); font-size: .78rem; letter-spacing: .04em;
  color: var(--ink-dim);
}
.script-stale-note form { margin: 0; }

/* ---- Script page: edit mode ---- */
/* The editor IS the read view's page: same .screenplay sheet, same sp-*
   classes, but every line is a contenteditable div. Formatting stays a pure
   function of the text, so the sp-* rules above apply unchanged. */
.screenplay-edit #fountain-editor {
  outline: none;
  white-space: pre-wrap;
  caret-color: #111;
  min-height: 9in;
}
/* Title-page lines are hidden in the read view (rendered from the script
   row); in the editor they are text like any other, shown quiet. */
.screenplay-edit .sp-titlepage { color: #888; }
/* sp-blank carries height:1em for the read view's empty divs; editor blanks
   hold a <br> so they size naturally and stay clickable. */
.screenplay-edit .sp-blank { height: auto; min-height: 1em; }
/* No-JS fallback: a bare Fountain textarea on the same white sheet. */
.script-edit-fallback textarea {
  width: 100%; border: none; outline: none; resize: vertical;
  background: #fff; color: #111;
  font-family: 'Courier Prime', 'Courier New', Courier, monospace;
  font-size: 12pt; line-height: 1.25;
}

/* ---- Sidebar layout ---- */
.layout-sidebar { display: grid; grid-template-columns: 280px 1fr; gap: 2.5rem; }
.sidebar { position: sticky; top: 5rem; align-self: start; }
.sidebar h4 { margin-bottom: .8rem; }
.sidebar a { display: block; padding: .35rem 0; color: var(--ink-dim); border-bottom: 1px solid transparent; font-family: var(--body); font-size: .8rem; }
.sidebar a:hover, .sidebar a.active { color: var(--accent); }

/* ---- Gallery page chrome (production design + similar) ---- */
.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; margin-bottom: 1.4rem; }
.page-head h1 { margin-bottom: .2em; }

.add-drawer { position: relative; }
.add-drawer summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: .4rem; }
.add-drawer summary::-webkit-details-marker { display: none; }
.add-drawer[open] summary { background: transparent; color: var(--accent); border-color: var(--accent); }
.add-drawer-body { position: absolute; right: 0; top: calc(100% + .5rem); width: min(640px, calc(100vw - 4rem)); background: var(--bg-raise); border: 1px solid var(--rule); padding: 1.4rem; z-index: 10; box-shadow: 0 8px 28px rgba(0,0,0,.45); }
.add-drawer-body .card { background: transparent; border: 0; padding: 0; margin: 0; }
.add-drawer-body h4 { margin-top: 0; }

.filterbar { display: flex; flex-direction: column; gap: .6rem; padding: 1rem 1.1rem; border: 1px solid var(--rule); background: var(--bg-card); margin-bottom: 1.6rem; }
.filterbar-group { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .55rem; }
.filterbar-label { font-family: var(--label); font-weight: 600; font-size: .68rem; text-transform: uppercase; letter-spacing: .16em; color: var(--ink-dim); margin-right: .4rem; }
.filter-chip {
  display: inline-block; padding: .25rem .7rem; font-family: var(--label); font-weight: 600; font-size: .72rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-dim);
  border: 1px solid var(--rule); border-radius: 999px; transition: color .15s, border-color .15s, background .15s;
}
.filter-chip:hover { color: var(--ink); border-color: var(--ink-dim); }
.filter-chip.active { color: var(--bg); background: var(--accent); border-color: var(--accent); }

.gallery-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 1rem; }
.gallery-empty { padding: 2rem 1rem; border: 1px dashed var(--rule); text-align: center; }

/* ---- "How this was made" (ref_detail, gen refs) ----
   A read-only lineage summary: the anchors that were sent with the prompt,
   as a row of small plates with their role beneath. Square crops on purpose —
   these are identifiers, not pictures to judge; judging happens on the plate
   above or on the canvas. */
.anchor-strip { display: flex; flex-wrap: wrap; gap: .7rem; margin-top: .7rem; }
.anchor-chip { display: flex; flex-direction: column; gap: .25rem; width: 7.5rem; }
.anchor-chip img.thumb, .anchor-chip .thumb-placeholder { width: 100%; aspect-ratio: 1; object-fit: cover; border: 1px solid var(--rule); display: block; }
.anchor-chip a:hover img.thumb { border-color: var(--accent); }
.anchor-chip .mono { font-size: .65rem; letter-spacing: .04em; }
/* The prompt as sent runs to several thousand characters — it is evidence,
   not prose, so it gets a scroll box rather than a wall down the page. */
.prompt-frozen { max-height: 22rem; overflow: auto; background: var(--bg-card); border: 1px solid var(--rule); padding: .8rem 1rem; margin-top: .6rem; font-family: var(--mono); font-size: .75rem; line-height: 1.6; white-space: pre-wrap; color: var(--ink-dim); }

/* ---- Ref attach / detach (ref_detail) ---- */
.attach-block { background: var(--bg-card); border: 1px solid var(--rule); padding: 1.2rem 1.4rem; margin-bottom: 1rem; }
.attach-block.attach-primary { border-left: 3px solid var(--accent); }
.attach-block.attach-secondary { border-left: 3px solid var(--rule); }
.attach-block summary { cursor: pointer; font-family: var(--label); font-size: .85rem; font-weight: 600; text-transform: uppercase; letter-spacing: .1em; color: var(--ink-dim); list-style: none; }
.attach-block summary::-webkit-details-marker { display: none; }
.attach-block summary::before { content: "▸ "; display: inline-block; transition: transform .15s; }
.attach-block[open] summary::before { transform: rotate(90deg); }
.attach-block summary:hover { color: var(--ink); }
.attach-header h4 { margin-bottom: .8rem; }
.attach-hint { font-family: var(--serif); font-size: .85rem; text-transform: none; letter-spacing: 0; color: var(--ink-dim); margin-left: .4rem; }
.attach-list { list-style: none; padding: 0; margin: .8rem 0 1rem; }
.attach-list li { display: flex; align-items: center; gap: .8rem; padding: .35rem 0; border-bottom: 1px dotted var(--rule); }
.attach-list li:last-child { border-bottom: 0; }
.attach-list li a { flex: 1 1 auto; }
.attach-list li form { display: inline; margin: 0; }
.detach-btn { padding: .15rem .55rem; font-size: .65rem; }
.attach-empty { font-style: italic; margin: .6rem 0 1rem; }
.attach-form { display: flex; gap: .6rem; align-items: stretch; margin-top: .6rem; }
.attach-form select { flex: 1 1 auto; }
.attach-form button { flex: 0 0 auto; white-space: nowrap; }

/* ---- Prompt draft page ---- */
.page-head-actions { display: flex; gap: .5rem; align-items: stretch; }
.gallery-head-actions { display: flex; gap: .5rem; align-items: stretch; flex-wrap: wrap; }
/* Direct-child only: the Compose-prompt mini-form sits inline with the
   action buttons. Nested forms (like Add Reference inside an .add-drawer)
   must NOT inherit this — they're full multi-row forms and got crushed
   into a single horizontal line. */
.gallery-head-actions > form { display: flex; gap: .35rem; align-items: stretch; }
.gallery-head-actions > form select { font-family: var(--label); font-size: .75rem; padding: .35rem .5rem; width: auto; }

.prompt-scope a { margin-right: .8rem; }

.iteration-chain { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .5rem; padding: .8rem 1rem; background: var(--bg-card); border: 1px solid var(--rule); margin-bottom: 1.4rem; }
.chain-arrow { font-family: var(--mono); }

.prompt-editor { padding: 1.4rem; }
.mono-prompt { font-family: var(--mono); font-size: .92rem; line-height: 1.5; min-height: 18rem; white-space: pre-wrap; }
.prompt-actions { display: flex; gap: .5rem; margin-top: .8rem; }
.prompt-actions button.btn-primary { font-size: .85rem; padding: .65rem 1.4rem; }

.prompts-list td { vertical-align: top; }
.prompts-list .pill { margin-right: .25rem; }

/* ---- Storyboard ---- */
.storyboard-grid { display: grid; gap: 1.4rem; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
/* NB: every selector here is qualified with .panel-actions on purpose —
   `.storyboard-panel .panel-actions form { display: inline }` below has the
   same specificity and comes later, so an unqualified rule loses and the
   editor lays out inline: label, textarea and save button side by side,
   with the button pushed off the right of the screen.
   Shot description — opened from the action row, never a band: the strip is one
   continuous flow and a full-width block would chop it into scenes.
   FIXED, not absolute. Its button lives inside .panel-actions, which needs
   `overflow-x` for the scroll fallback — and any overflow other than visible
   makes that row a clipping context, so an absolutely-positioned editor was
   getting cut off by the 53px strip it hung from. Fixed positioning leaves
   the clip entirely. Centred rather than tethered because these run to
   1400+ characters and want real room to read and edit. */
.panel-actions .desc-pop[open] > form { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 200; width: min(52rem, 92vw); background: var(--bg-card); border: 1px solid var(--rule); padding: .9rem; box-shadow: 0 18px 50px rgba(0,0,0,.7); display: flex; flex-direction: column; gap: .6rem; }
.panel-actions .desc-pop[open] > form textarea { width: 100%; resize: vertical; font: inherit; line-height: 1.55; min-height: 40vh; }
.panel-actions .desc-pop[open] > form > button { align-self: flex-start; }
.desc-pop-head { display: flex; align-items: center; justify-content: space-between; gap: .6rem; }
.desc-pop-head b { color: var(--fg); }
/* The trigger stays visible and above the editor, so clicking it again is the
   way out — a <details> cannot be closed from a button without script. */
.panel-actions .desc-pop[open] > summary { position: relative; z-index: 201; border-color: var(--accent); }

.storyboard-panel { background: var(--bg-card); border: 1px solid var(--rule); display: flex; flex-direction: column; }
.storyboard-panel.state-proposed { border-color: var(--rule); border-left: 3px solid var(--warn); }
.storyboard-panel.state-rejected, .storyboard-panel.state-archived { opacity: .55; }
/* Fixed height so every frame in a row starts at the same y — pill count
   varies per shot; the slug truncates instead of wrapping to a 2nd line. */
.storyboard-panel .panel-head { height: 2.7rem; padding: 0 1rem; border-bottom: 1px solid var(--rule); display: flex; align-items: center; gap: .55rem; cursor: grab; user-select: none; }
.storyboard-panel .panel-head:active { cursor: grabbing; }
.storyboard-panel .panel-head .pill { flex: 0 0 auto; white-space: nowrap; }
.storyboard-panel .drag-grip { color: var(--ink-dim); font-family: var(--mono); font-size: 1.1rem; line-height: 1; }
.storyboard-panel .panel-head:hover .drag-grip { color: var(--accent); }
.storyboard-panel .panel-slug { display: flex; align-items: center; gap: .55rem; font-family: var(--mono); font-size: .82rem; line-height: 1.35; flex: 1 1 auto; min-width: 0; }
.storyboard-panel .panel-slug > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.storyboard-panel .panel-slug:hover { color: var(--accent); border-bottom-color: transparent; }
.sortable-ghost { opacity: .35; }
.sortable-chosen { box-shadow: 0 6px 22px rgba(0,0,0,.55); transform: scale(1.01); }
.storyboard-panel .panel-canvas { aspect-ratio: 16/9; background: var(--surface-raise); display: flex; align-items: center; justify-content: center; overflow: hidden; }
.storyboard-panel .panel-canvas img { width: 100%; height: 100%; object-fit: cover; display: block; }
.storyboard-panel .panel-empty { color: var(--ink-dim); font-family: var(--body); font-size: .8rem; letter-spacing: .06em; }
.storyboard-panel .panel-running { color: var(--live); font-family: var(--label); font-size: .85rem; letter-spacing: .08em; text-transform: uppercase; display: flex; align-items: center; gap: .55rem; }
.pulse-dot { width: .55rem; height: .55rem; border-radius: 50%; background: var(--accent); display: inline-block; animation: pulse 1.2s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(1.4); } }
.storyboard-panel .panel-meta { padding: .55rem 1rem; display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; border-top: 1px solid var(--rule); }
/* The shot description — the one block of prose in the panel. It carried no
   padding at all, so it ran flush into both borders while the head, the meta
   pills and the action row were all inset 1rem: the text read as if it had
   fallen out of the card. Same 1rem gutter as every other row here, and a
   bottom gap so it doesn't sit on the action row's keyline. */
.storyboard-panel .panel-desc { font-size: .78rem; line-height: 1.45; margin: 0; padding: .55rem 1rem .65rem; }
.panel-anchors-label { margin-right: .35rem; }

/* Anchor thumbnail strip — the actual reference images Gemini will see when
   the panel is generated. Surfaces the calibration so you can verify before
   spending the gen cost. */
.panel-anchor-strip { display: flex; gap: .35rem; padding: .35rem 1rem .55rem; flex-wrap: wrap; }
.anchor-thumb { position: relative; display: block; width: 3.2rem; flex: 0 0 auto; }
.anchor-thumb img { width: 3.2rem; height: 3.2rem; object-fit: cover; border: 1px solid var(--rule); background: var(--surface-raise); display: block; }
.anchor-thumb:hover img { border-color: var(--accent); }
.anchor-thumb .anchor-badge { position: absolute; top: -.25rem; right: -.25rem; background: var(--bench); color: var(--bg); font-size: .65rem; padding: 0 .25rem; line-height: 1.1; border-radius: 2px; pointer-events: none; }
.anchor-thumb .anchor-caption { display: block; font-family: var(--body); font-size: .58rem; color: var(--ink-dim); margin-top: .15rem; text-align: center; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.anchor-thumb:hover .anchor-caption { color: var(--accent); }
.panel-anchor-empty { padding: .35rem 1rem .55rem; color: var(--ink-dim); font-family: var(--body); font-size: .65rem; letter-spacing: .04em; font-style: italic; }

/* ONE row — the controls never wrap onto a second line, so every panel's
   footer is the same height and the trash always sits in the same place at
   the far right. Overflow scrolls inside the row rather than reflowing. */
/* ONE row, never wrapped: every panel's footer is the same height and the
   trash is always the last control, in the same place on every panel. The row
   has to FIT, not scroll — the scrollbar is hidden, so anything past the right
   edge is silently unreachable, and the thing past the edge was the trash.
   Measured budget at the 4-up width (390px panel, 388px inner, 1rem gutters):
   356px for 14 visible controls + 13 gaps. The sizing below spends 350.8px of
   it. Add a 15th control and this stops fitting — take one out, don't let it
   scroll. Same 1rem gutter as .panel-head / .panel-meta / .panel-desc so the
   ✓ lines up with the scene chip above it. */
/* margin-top:auto pins the row to the bottom of the panel. Grid stretches
   every panel to the tallest in its row, so a short description used to leave
   dead space BELOW the buttons and the rows sat at a different height in each
   panel; now the slack goes above the row and the footers line up across the
   strip. */
.storyboard-panel .panel-actions { display: flex; flex-wrap: nowrap; align-items: center; gap: 2px; margin-top: auto; padding: .55rem 1rem .8rem; border-top: 1px solid var(--rule); overflow-x: auto; scrollbar-width: none; }
.storyboard-panel .panel-actions::-webkit-scrollbar { display: none; }
/* Every control may give up a few pixels rather than one falling off the
   end. The row's scrollbar is hidden, so a row one button over budget did not
   scroll visibly: it clipped the trash at the right edge (15 controls in a
   388px panel at 1400 wide, 20px over). Shrinking spreads the deficit evenly,
   so every button stays the same width as its neighbours. */
.storyboard-panel .panel-actions > * { flex: 0 1 auto; min-width: 0; }
.storyboard-panel .panel-actions .btn-icon,
.storyboard-panel .panel-actions > details > summary { max-width: 100%; }
/* Keyline like its peers — no fill anywhere. Green is the state language:
   hover previews it, click/approved commit it. */
.btn-approve { font-weight: 700; }
.btn-approve:hover {
  background: transparent; color: var(--ok);
  border-color: var(--ok);
}
/* Green from the moment of the click, green for as long as the take is
   approved — the button no longer vanishes on approval, it becomes the
   state indicator. */
.btn-approve:active,
.btn-approve.is-approved,
.btn-approve.is-approved:hover {
  background: transparent; color: var(--ok);
  border-color: var(--ok);
  box-shadow: 0 0 9px rgba(95, 174, 140, .55);
}
.btn-approve.is-approved { cursor: default; }
/* Same story in red for reject — strokes only, no fill. */
.btn-reject:hover {
  background: transparent; color: var(--live);
  border-color: var(--live);
}
.btn-reject:active,
.btn-reject.is-rejected,
.btn-reject.is-rejected:hover {
  background: transparent; color: var(--live);
  border-color: var(--live);
  box-shadow: var(--glow-live);
}
.btn-reject.is-rejected { cursor: default; }
/* The verdict strip ✓ ✕ ? (partials/verdict.html): the two buttons above plus
   ?, which is blue and stays lit while its inline thread is open. One row,
   36px targets. On a light surface, the script's white sheet
   (.verdict--light) the keylines and the three state colours are darkened to
   hold contrast on a light ground. */
.verdict { display: inline-flex; align-items: center; gap: 4px; flex: none; vertical-align: middle; }
.verdict form { display: inline-flex; margin: 0; }
.verdict .btn-icon { width: 36px; height: 36px; min-width: 36px; padding: 0; border-radius: 8px; }
.btn-ask:hover { background: transparent; color: var(--chrome); border-color: var(--chrome); }
.btn-ask:active, .btn-ask.is-on, .btn-ask.is-on:hover {
  color: var(--chrome); border-color: var(--chrome);
  background: color-mix(in srgb, var(--chrome) 14%, transparent);
}
.verdict .btn-icon:disabled { opacity: .4; cursor: default; }
.verdict--light { --ok: #2F7D5B; --live: #B3261A; --chrome: #285AB4; }
.verdict--light .btn-icon { color: #1d1b18; border-color: rgba(29,27,24,.22); background: transparent; box-shadow: none; }
.verdict--light .btn-approve:hover, .verdict--light .btn-approve:active { box-shadow: none; }
.verdict--light .btn-reject:hover, .verdict--light .btn-reject:active { box-shadow: none; }
.verdict--light .btn-ask.is-on { background: rgba(40,90,180,.1); }
/* The hover rule: on a secondary row the strip waits for the pointer or the
   keyboard, and stays while its thread is open. The row is whatever carries
   .verdict-row. Never hidden where there is no hover to reveal it. */
@media (hover: hover) {
  .verdict--quiet { opacity: 0; transition: opacity .12s; }
  .verdict-row:hover .verdict--quiet, .verdict-row:focus-within .verdict--quiet,
  .verdict--quiet.is-asking, .verdict-row.is-open .verdict--quiet { opacity: 1; }
}
.storyboard-panel .panel-actions form { display: inline; }
.storyboard-panel .panel-actions button { padding: .35rem .7rem; font-size: .72rem; }
.storyboard-panel .panel-actions .btn { padding: .35rem .7rem; font-size: .72rem; }
/* Music pipeline stepper — direction → brief → compose → placed. */
.music-stepper { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; font-size: .74rem; margin: .2rem 0 1.4rem; padding: .5rem .8rem; border: 1px solid var(--rule); background: var(--bg-raise); }
.music-stepper .step { display: inline-flex; align-items: center; gap: .3rem; color: var(--ink-faint); text-transform: uppercase; letter-spacing: .06em; }
.music-stepper .step.done { color: var(--ok); }
.music-stepper .step.active { color: var(--ink); font-weight: 700; }
.music-stepper.is-failed .step.active { color: var(--warn); }
.music-stepper .step-arrow { color: var(--ink-faint); }
.music-stepper .step-detail { margin-left: auto; text-transform: none; letter-spacing: 0; }

/* Shot-workbench mirror of the board panel's control row — same affordances
   under each of the shot's current takes. */
.shot-take-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; margin-top: .45rem; }
.shot-take-actions form { display: inline; }
/* Thin cluster separators — state · feedback · generate · edit, the one
   semantic order every take row (board / styleframe / motion) follows. */
.shot-take-actions .sep { width: 1px; height: 18px; background: var(--rule); margin: 0 .15rem; flex: 0 0 auto; }
.shot-take-actions .sep:first-child { display: none; }
/* Long warn pills (draft-stale) must not push their sibling button to a
   second row — the row stays nowrap and the pill shrinks with ellipsis,
   the title attr carries the full text. */
.shot-take-actions.stale-row { flex-wrap: nowrap; }
.shot-take-actions .pill { flex: 0 1 auto; min-width: 0; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.shot-take-actions button { padding: .35rem .7rem; font-size: .72rem; }
.shot-take-actions .btn { padding: .35rem .7rem; font-size: .72rem; }
.shot-take-actions .btn-icon { padding: .35rem .5rem; }

/* ---- Project index ---- */
.project-card { padding: 1.6rem 1.8rem; border: 1px solid var(--rule); background: var(--bg-card); margin-bottom: 1rem; }
.project-card h2 { margin-bottom: .25rem; }
.project-card .path { font-family: var(--mono); font-size: .75rem; color: var(--ink-dim); word-break: break-all; }

/* ---- Entity-room iteration loop ---- */
.iter-section { margin-top: 2.6rem; padding: 1.4rem 1.6rem 1.8rem; background: var(--bg-raise); border: 1px solid var(--rule); }
.iter-head { display: flex; align-items: baseline; gap: 1.2rem; margin-bottom: 1rem; }
.iter-head h3 { margin: 0; }
.iter-head p { margin: 0; font-size: .85rem; }
.iter-form { display: flex; flex-direction: column; gap: 1rem; }
.iter-controls { display: flex; align-items: flex-start; gap: 2rem; flex-wrap: wrap; }
.iter-provider { border: 1px solid var(--rule); padding: .55rem 1rem .65rem; background: var(--bg-card); display: flex; flex-direction: column; gap: .35rem; min-width: 22rem; }
.iter-provider legend { padding: 0 .35rem; font-size: .7rem; letter-spacing: .12em; text-transform: uppercase; }
.iter-provider label { font-family: var(--serif); font-size: .9rem; letter-spacing: 0; cursor: pointer; display: flex; align-items: center; gap: .55rem; }
.iter-action { display: flex; flex-direction: column; gap: .4rem; }
.iter-action .btn-primary { padding: .65rem 1.4rem; font-size: .9rem; }
.iter-cine-hint { margin: 0; font-size: .68rem; letter-spacing: .04em; color: var(--ok); cursor: help; }
.iter-hint { margin: .2rem 0 0; font-size: .68rem; letter-spacing: .04em; }
.iter-anchor-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(5.6rem, 1fr)); gap: .6rem; }
.iter-anchor-tile { display: flex; flex-direction: column; gap: .25rem; padding: .3rem; background: var(--bg-card); border: 1px solid var(--rule); cursor: pointer; position: relative; transition: border-color .12s ease; }
.iter-anchor-tile:hover { border-color: var(--accent); }
.iter-anchor-tile.state-benchmark { border-color: var(--bench); }
.iter-anchor-tile input[type=checkbox] { position: absolute; top: .35rem; left: .35rem; margin: 0; z-index: 2; cursor: pointer; }
.iter-anchor-tile:has(input:checked) { border-color: var(--accent); background: var(--g4); box-shadow: inset 0 0 0 1px var(--accent); }
.iter-anchor-tile img { width: 100%; aspect-ratio: 1/1; object-fit: cover; background: var(--surface-raise); display: block; }
.iter-anchor-tile .iter-anchor-placeholder { display: flex; align-items: center; justify-content: center; aspect-ratio: 1/1; background: var(--surface-raise); font-family: var(--label); font-size: .6rem; color: var(--ink-dim); }
.iter-anchor-tile .iter-anchor-name { font-family: var(--label); font-size: .58rem; line-height: 1.2; color: var(--ink-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.iter-anchor-tile:has(input:checked) .iter-anchor-name { color: var(--accent); }

/* ---- Brief Q&A wizard ---- */
.brief-form-actions { display: flex; gap: .55rem; align-items: center; margin-top: .35rem; }
.brief-wizard { max-width: 56rem; display: flex; flex-direction: column; gap: 1rem; }
.brief-wizard-q label { display: flex; flex-direction: column; gap: .25rem; margin-bottom: .55rem; }
.brief-wizard-heading { font-family: var(--display); font-weight: 600; font-size: .9rem; letter-spacing: .1em; text-transform: uppercase; color: var(--display-ink); }
.brief-wizard-prompt { font-family: var(--body); font-weight: 400; font-size: .9rem; line-height: 1.4; letter-spacing: normal; text-transform: none; color: var(--ink-dim); }
.brief-wizard-q textarea { font-family: var(--serif); font-size: 1rem; line-height: 1.5; }
.brief-wizard-actions { display: flex; gap: .55rem; align-items: center; margin-top: .5rem; }

/* ---- Reference lightbox ---- */
/* Click any .ref-card .thumb to open the full image with inline triage
   buttons. Keyboard: ← / → / esc / A / R / B. */
.ref-card img.thumb,
[data-lightbox-card] img.thumb { cursor: zoom-in; }
.ref-card-leaving { opacity: 0; transition: opacity .18s ease; pointer-events: none; }
body.ref-lightbox-open { overflow: hidden; }
.ref-lightbox {
  position: fixed; inset: 0; z-index: 100;
  background: var(--g0);
  display: flex; align-items: center; justify-content: center;
  padding: 3vh 5vw;
}
.ref-lightbox[hidden] { display: none; }
/* Picture wins: the image takes the whole stage; meta + actions live in a
   single horizontal strip UNDER it — never a side column stealing width. */
.ref-lightbox-frame {
  display: flex; flex-direction: column;
  gap: .9rem;
  max-width: 100%; max-height: 100%;
  width: 100%; height: 100%;
  min-height: 0;
}
.ref-lightbox-left {
  display: flex; align-items: center; justify-content: center;
  flex: 1 1 auto;
  min-height: 0;
  /* Clips the zoomed image to the picture stage so a magnified plate can
     never spill over the meta strip below it. */
  overflow: hidden;
}
.ref-lightbox-img,
.ref-lightbox-video {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border: 1px solid var(--rule);
  background: var(--surface-raise);
}
/* Zoom/pan. No transition on transform — the wheel drives it frame by frame
   and an easing curve would lag the cursor. `.zoomed` is set by lightbox.js
   whenever scale > 1, and is the only thing that turns on grab affordance. */
.ref-lightbox-img { will-change: transform; cursor: zoom-in; }
.ref-lightbox-img.zoomed { cursor: grab; }
.ref-lightbox-img.panning { cursor: grabbing; }
/* Full screen: the padding that frames the lightbox as an overlay is wrong
   once it IS the screen — give the picture the whole panel. */
.ref-lightbox:fullscreen { padding: 0; background: var(--g0); }
.ref-lightbox:fullscreen .ref-lightbox-frame { gap: .5rem; padding: .5rem; }
/* Viewer controls — under the picture, in the meta strip. */
.ref-lightbox-view {
  display: flex; align-items: center; gap: .5rem;
}
.ref-lightbox-view .ref-lightbox-zoom {
  font-size: .72rem; letter-spacing: .04em;
  min-width: 3.4em; text-align: right;
}
/* Law 10 — the rail's rows are AUTHORED, in priority order, one kind of thing
   each. It used to be `flex-direction: row; flex-wrap: wrap` with a title, a
   state word, a filename, seven icon buttons, a hint string and a strip of
   pictures all entering one box: the rows formed by viewport width, so the
   panel stacked differently at every size and the takes strip — the row that
   matters most — was whichever one happened to fall off the bottom of a 26vh
   cap. The cap is gone too; the rail is content-height, and what it costs the
   picture is now a fixed, known number instead of a function of the window. */
.ref-lightbox-right {
  flex: 0 0 auto;
  display: flex; flex-direction: column; flex-wrap: nowrap;
  align-items: stretch;
  gap: .5rem;
  background: var(--g2);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: .6rem 1rem .7rem;
}
/* Row 1 · IDENTITY. Law 5 amended — where text and pictures compete for a
   width, the text truncates and the pictures do not. So this row, and only
   this row, is allowed to shrink. */
.ref-lightbox-identity {
  display: flex; gap: .8rem; align-items: baseline; flex-wrap: nowrap;
  min-width: 0;
  font-family: var(--mono); font-size: .8rem;
}
.ref-lightbox-identity .ref-lightbox-title {
  font-family: var(--serif); font-weight: 400; font-size: 1.1rem;
  color: var(--ink);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Row 2 · CONTROLS. Icons never shrink and never wrap (law 5, inside a row):
   a control you cannot hit is not a control. The row grows sideways as the
   tool gains verbs, and nothing above or below it moves. */
.ref-lightbox-controls {
  display: flex; align-items: center; flex-wrap: nowrap;
  gap: .6rem 1.3rem;
  overflow-x: auto;
}
.ref-lightbox-controls > * { flex: 0 0 auto; }
.ref-lightbox-controls .ref-lightbox-view { margin-left: auto; }
/* Unique filename chip — the image's identity; click copies it. */
.ref-lightbox-identity .ref-lightbox-file {
  font-family: var(--mono); font-size: .75rem;
  background: none; border: 1px dashed var(--line-strong);
  border-radius: 3px; padding: .1rem .4rem;
  color: var(--ink-dim); cursor: copy;
  /* A chip, not a control: its text's height, not the button height. */
  height: auto;
}
.ref-lightbox-identity .ref-lightbox-file:hover {
  color: var(--ink); border-color: var(--ink-faint);
}
.ref-lightbox-actions {
  display: flex; flex-wrap: wrap; gap: .55rem; align-items: center;
}
.ref-lightbox-actions form { display: inline; }
.ref-lightbox-actions details.promote-panel { display: inline-block; }
.ref-lightbox-workbench { display: contents; }
/* Takes strip — every take behind the panel, newest first; the one on
   screen is keylined in the single highlight, the others by state. Archived
   takes ride along dimmed (they are the audition history — ✓ resurrects);
   only rejected takes stay out. Sits under the picture with the actions,
   never over it. */
.ref-lightbox-takes {
  display: flex; align-items: center; gap: .4rem;
  overflow-x: auto; padding: .1rem 0 .15rem;
}
.ref-lightbox-takes[hidden] { display: none; }
.ref-lightbox-take {
  flex: 0 0 auto; padding: 0; margin: 0; background: none;
  border: 2px solid var(--line); border-radius: 3px;
  cursor: pointer; line-height: 0;
  /* The take is as tall as its picture. Left at the button height it was a
     36px slot round an 88px plate, and the strip's overflow cropped every
     take to a band through its middle. */
  height: auto;
}
/* 88px, not 64: a thumbnail you cannot judge from is not a thumbnail, it is
   a bullet point (law 5, amended). `flex: 0 0 auto` above is what stops the
   row from paying for the text in the row above it. */
.ref-lightbox-take img { height: 88px; width: auto; display: block; border-radius: 1px; }
.ref-lightbox-take.state-approved { border-color: var(--ok); }
.ref-lightbox-take.state-benchmark { border-color: var(--bench); }
.ref-lightbox-take.is-current { border-color: var(--chrome); }
.ref-lightbox-take:hover { border-color: var(--line-strong); }
.ref-lightbox-take.is-current:hover { border-color: var(--chrome); }
/* Archived = history, not candidate: dimmed at rest, full when you look at
   it (hover) or step onto it — a plate you cannot judge is no plate. */
.ref-lightbox-take.state-archived img { opacity: .45; }
.ref-lightbox-take.state-archived:hover img,
.ref-lightbox-take.state-archived.is-current img { opacity: 1; }
.btn-roll { position: relative; }
.btn-roll .roll-n {
  position: absolute; right: -.15rem; top: -.2rem;
  font-size: .55rem; line-height: 1; font-weight: 600; letter-spacing: 0;
}
.btn-roll.is-rolling { opacity: .45; }
.ref-lightbox-workbench .workbench {
  display: block !important;
  border-top: 1px solid var(--rule);
  padding-top: .85rem;
}
.workbench-form { display: flex; flex-direction: column; gap: .55rem; }
.workbench-meta { font-size: .72rem; letter-spacing: .04em; }
.workbench-anchors {
  display: flex; gap: .35rem; flex-wrap: wrap;
}
.workbench-anchor {
  position: relative; display: inline-block; cursor: pointer;
  border: 1px solid var(--rule);
}
.workbench-anchor input { position: absolute; top: .15rem; left: .15rem; z-index: 2; }
.workbench-anchor img {
  display: block; width: 48px; height: 48px; object-fit: cover;
  opacity: .55; transition: opacity .15s;
}
.workbench-anchor input:checked + img,
.workbench-anchor:has(input:checked) img { opacity: 1; }
.workbench-label { font-size: .68rem; text-transform: uppercase; letter-spacing: .08em; }
.workbench-prompt {
  font-family: var(--mono); font-size: .78rem; line-height: 1.45;
  background: var(--bg-card); color: var(--ink);
  border: 1px solid var(--rule); padding: .55rem .65rem;
  resize: vertical; min-height: 6rem;
}
.workbench-provider {
  font-family: var(--label); font-size: .78rem;
  background: var(--bg-card); color: var(--ink);
  border: 1px solid var(--rule); padding: .35rem .5rem;
}
.workbench-actions { display: flex; gap: .6rem; align-items: center; margin-top: .25rem; }
.workbench-cost { font-size: .68rem; letter-spacing: .04em; }
/* The lightbox is a void room, but its action panel clones the card's
   buttons/pills (lightbox.js populateActions) — so the panel has to re-tone
   what it inherits, or cloned controls read as empty boxes.

   This block used to hard-code the sand family at a dozen alphas, because the
   tokens it reached for (var(--surface-deep)) were LIGHT-theme values then and
   made every icon invisible. Those tokens are the dark ramp now, so the
   workaround outlived its reason: the ink ladder says the same thing, and says
   it in a value the rest of the app moves with. */
.ref-lightbox-right { color: var(--ink); }
.ref-lightbox-identity .ref-lightbox-title { color: var(--display-ink); }
.ref-lightbox-right a { color: var(--ink); }
.ref-lightbox-right a:hover { color: var(--chrome); }
/* No keylines in the strip — the icons themselves are the controls, always
   at full visibility; hover just warms them to chrome. */
.ref-lightbox-actions button,
.ref-lightbox-actions .btn {
  color: var(--ink);
  border: 0;
  background: transparent;
}
.ref-lightbox-actions button:hover,
.ref-lightbox-actions .btn:hover {
  color: var(--chrome);
  background: transparent;
}
.ref-lightbox-actions .btn-primary {
  background: var(--chrome); color: var(--surface-warm);
  border-color: var(--chrome);
}
.ref-lightbox-actions .btn-warn { color: color-mix(in srgb, var(--live) 70%, white); border-color: color-mix(in srgb, var(--live) 70%, white); }
.ref-lightbox-actions .btn-warn:hover { background: var(--g4); color: var(--live); border-color: var(--live); box-shadow: var(--glow-live); }
.ref-lightbox-right .pill { color: var(--ink-dim); border-color: var(--line-strong); }
.ref-lightbox-right .dim { color: var(--ink-faint); }
.ref-lightbox-right .promote-form {
  background: color-mix(in srgb, var(--bench) 12%, transparent);
  border-color: color-mix(in srgb, var(--bench) 50%, transparent);
}
.ref-lightbox-right .promote-field > span { color: var(--ink-dim); }
.ref-lightbox-right .workbench { color: var(--ink); }
.ref-lightbox-right .workbench-label,
.ref-lightbox-right .workbench-meta,
.ref-lightbox-right .workbench-cost { color: var(--ink-dim); }

.ref-lightbox-close,
.ref-lightbox-nav {
  position: absolute;
  background: transparent;
  color: var(--ink-dim);
  border: 1px solid var(--rule);
  font-size: 1.6rem;
  width: 2.6rem; height: 2.6rem;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  /* The mark is centred by the box. A letter on a text baseline (the old
     "×") sat low and to one side. */
  display: grid; place-items: center;
}
.ref-lightbox-close { top: 1.2rem; right: 1.2rem; }
.ref-lightbox-close[hidden], .ref-lightbox-nav[hidden] { display: none; }
.ref-lightbox-close .icon, .feedback-modal-close .icon { width: 1.15rem; height: 1.15rem; }
.ref-lightbox-nav { top: 50%; transform: translateY(-50%); }
.ref-lightbox-nav.prev { left: 1.2rem; }
.ref-lightbox-nav.next { right: 1.2rem; }
.ref-lightbox-close:hover,
.ref-lightbox-nav:hover { color: var(--accent); border-color: var(--accent); }
@media (max-width: 900px) {
  .ref-lightbox-frame { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) auto; }
  .ref-lightbox-right { max-height: 40vh; }
  .ref-lightbox-img { max-height: 50vh; }
}
@media (max-width: 720px) {
  .ref-lightbox { padding: 1vh 2vw; }
  .ref-lightbox-img { max-height: 40vh; }
  .ref-lightbox-nav { display: none; }
}

/* ---- Storyboard panel feedback (modal + inline list) -----------------
 * The modal pins a canvas over the panel image so the user can mark up
 * areas of concern in red/yellow strokes — those are the only two colours
 * Gemini will be told to interpret as mark-up. Vanilla canvas, no library.
 * Brand note: the modal uses vermillion + solar as the brush colours, which
 * matches the brand's two-of-the-three primitives chord — both are legit
 * here because they're carrying information (revise this region) not
 * decoration.
 */
body.feedback-modal-open { overflow: hidden; }
.feedback-modal {
  position: fixed; inset: 0; z-index: 110;
  background: var(--g0);
  display: flex; align-items: center; justify-content: center;
  padding: 3vh 4vw;
}
.feedback-modal[hidden] { display: none; }
/* `button` sets display, which beats the UA [hidden] rule: say it here or
   "save and remake" shows on boards and styleframes too. */
.feedback-modal [data-feedback-take-only][hidden] { display: none; }
.feedback-modal-frame {
  background: var(--surface-raise);
  border: 1px solid var(--rule);
  width: min(1100px, 100%); max-height: 94vh;
  display: flex; flex-direction: column;
}
.feedback-modal-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: .9rem 1.2rem; border-bottom: 1px solid var(--rule);
}
.feedback-modal-title {
  font-family: var(--display); font-weight: 600; font-size: 1rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--display-ink);
}
.feedback-modal-close {
  background: transparent; color: var(--ink-dim);
  border: 1px solid var(--rule); width: 2.2rem; height: 2.2rem;
  padding: 0; font-size: 1.4rem; line-height: 1; cursor: pointer;
  display: grid; place-items: center;
}
.feedback-modal-close:hover { color: var(--accent); border-color: var(--accent); }
.feedback-modal-body {
  display: grid; grid-template-columns: minmax(0, 1fr) 22rem;
  gap: 1.2rem; padding: 1rem 1.2rem 1.2rem;
  overflow-y: auto;
}
@media (max-width: 880px) {
  .feedback-modal-body { grid-template-columns: 1fr; }
}
.feedback-canvas-wrap {
  position: relative; background: var(--surface-raise);
  display: flex; align-items: center; justify-content: center;
  min-height: 18rem; aspect-ratio: 16/9;
  border: 1px solid var(--rule);
}
.feedback-bg, .feedback-canvas {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain;
}
.feedback-bg { z-index: 1; }
.feedback-canvas {
  z-index: 2; cursor: crosshair;
  touch-action: none;   /* let touch paint instead of pan */
}
/* A flopped shot is judged flopped — plate and paint layer mirror together.
   feedback.js inverts pointer x so the stored buffer stays native-space. */
.feedback-bg.flopped, .feedback-canvas.flopped { transform: scaleX(-1); }
.feedback-tools {
  display: flex; flex-direction: column; gap: .8rem;
}
.feedback-tool-row {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
}
.feedback-tool-label {
  flex: 0 0 3.5rem;
  font-family: var(--label); font-weight: 600; font-size: .68rem;
  text-transform: uppercase; letter-spacing: .14em;
}
.feedback-color-btn {
  width: 2rem; height: 2rem; padding: 0;
  border: 2px solid var(--rule); border-radius: 999px;
  cursor: pointer; transition: transform .12s ease, border-color .12s ease;
}
.feedback-color-btn:hover { transform: scale(1.08); }
.feedback-color-btn.active {
  border-color: var(--surface-warm);
  box-shadow: 0 0 0 2px var(--surface-deep), 0 0 0 3px var(--surface-warm);
}
.feedback-brush-btn {
  min-width: 2.4rem; padding: .35rem .6rem;
  font-family: var(--body); font-size: 1.05rem;
  background: transparent; color: var(--ink-dim);
  border: 1px solid var(--rule);
}
.feedback-brush-btn.active { color: var(--chrome); border-color: var(--chrome); }
.feedback-note-label {
  display: flex; flex-direction: column; gap: .25rem;
  margin: .25rem 0 0;
}
.feedback-note-label textarea { min-height: 6rem; }
.feedback-submit-row {
  display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
  margin-top: .55rem;
}
.feedback-submit-hint { font-size: .65rem; letter-spacing: .04em; }

/* The expandable prior-feedback list lives at the bottom of each panel. */
.panel-feedback-list {
  border-top: 1px solid var(--rule);
  padding: .55rem 1rem .8rem;
}
.panel-feedback-list summary {
  cursor: pointer; list-style: none;
  font-family: var(--label); font-weight: 600; font-size: .7rem;
  text-transform: uppercase; letter-spacing: .14em;
  color: var(--ink-dim); padding: .25rem 0;
}
.panel-feedback-list summary::-webkit-details-marker { display: none; }
.panel-feedback-list summary::before { content: "▸ "; display: inline-block; transition: transform .15s; }
.panel-feedback-list[open] summary::before { transform: rotate(90deg); }
.panel-feedback-list summary:hover { color: var(--accent); }
.panel-feedback-list ul {
  list-style: none; padding: .55rem 0 0; margin: 0;
  display: flex; flex-direction: column; gap: .65rem;
}
.panel-feedback-item {
  display: grid; grid-template-columns: 4.5rem 1fr; gap: .65rem;
  align-items: start;
  padding: .55rem .6rem;
  background: var(--surface-deep); border: 1px solid var(--rule);
}
.panel-feedback-meta {
  grid-column: 1 / -1;
  font-size: .65rem; letter-spacing: .04em;
}
.panel-feedback-thumb {
  display: block; width: 4.5rem; height: 4.5rem;
  background: var(--surface-raise); border: 1px solid var(--rule);
  overflow: hidden;
}
.panel-feedback-thumb img {
  width: 100%; height: 100%; object-fit: contain; display: block;
}
.panel-feedback-note {
  margin: 0; font-size: .85rem; line-height: 1.4;
  white-space: pre-wrap;
}
.panel-feedback-item:not(:has(.panel-feedback-thumb)) .panel-feedback-note {
  grid-column: 1 / -1;
}

/* ===================================================================== *
 *  Image-first surfaces (2026-06 redesign)
 *
 *  The tool's output is pictures; the surfaces lead with pictures.
 *  Editorial register borrowed from the production bible: one hero plate
 *  dominates, supporting tiles follow, admin collapses. Same brand
 *  tokens — vermillion stays live-only.
 * ===================================================================== */

@keyframes plate-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* ---- Poster wall (home) ---- */
/* On the box rule's twelve columns, two posters a row, so the wall's edges are
   the doors' edges above it. */
.poster-wall {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 24px;
  margin-top: 1.8rem;
}
.poster-card {
  grid-column: span 6;
  display: flex; flex-direction: column;
  border: 1px solid var(--box-line);
  border-bottom-color: var(--box-line); /* suppress link underline rule */
  background: var(--g3);
  padding-bottom: .9rem;
  animation: plate-rise .45s ease backwards;
}
.poster-card .poster-cap { padding: .75rem .9rem 0; }
.poster-card:nth-child(2) { animation-delay: .07s; }
.poster-card:nth-child(3) { animation-delay: .14s; }
.poster-card:nth-child(4) { animation-delay: .21s; }
.poster-card:hover { border-color: var(--accent); }
.poster-plate {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
}
.poster-plate img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .6s cubic-bezier(.2, .6, .2, 1);
}
.poster-card:hover .poster-plate img { transform: scale(1.03); }
.poster-empty {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(150deg, var(--g0) 0%, var(--g3) 70%);
  color: var(--paper-faint);
  font-family: var(--body); font-size: .75rem;
  text-transform: none;
}
/* Law 6: the title lives UNDER the poster, never burned into it. */
.poster-cap { display: block; padding: .75rem .1rem 0; }
/* The front door's type carries down the wall (Brett, 2026-09-14): paper ink,
   Regular like the door names, titles as they were written. */
.poster-cap h2 {
  margin: 0 0 .15rem;
  color: var(--paper-ink);
  font-size: 1.8rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
}
.poster-stats {
  color: var(--paper-faint);
  font-size: .7rem; letter-spacing: .08em;
}
/* The card is an <article> holding the link and, for an owner, a trash can
   beside it (a button inside a link is invalid and would navigate). The link
   carries the card's whole face, so hover still lights plate and title. */
.poster-card { position: relative; }
.poster-link { display: flex; flex-direction: column; color: inherit; border-bottom: 0; }
.poster-link:hover { color: inherit; border-bottom: 0; }
.poster-card.can-delete .poster-cap { padding-right: 3.6rem; }
/* The trash can: in the caption, under the picture (law 6), quiet until
   pointed at, then the destructive colour. 44px so a finger on the iPad
   finds it. */
.poster-trash {
  position: absolute; right: .5rem; bottom: .5rem;
  min-width: 44px; min-height: 44px;
  border-color: transparent; color: var(--ink-faint);
}
.poster-trash .icon { width: 1.15rem; height: 1.15rem; }
.poster-trash:hover {
  color: var(--live); border-color: var(--live); box-shadow: var(--glow-live);
}
/* Keyboard focus is the house blue ring, not the destructive colour: closing
   the approval hands focus back to the can, and a cancelled delete must not
   leave the can looking armed. */
.poster-trash:focus-visible { outline: none; border-color: var(--accent); color: var(--ink); }
/* On the front door's cards the can sits on the picture's top-right
   corner, shown on hover or focus (always on a touch screen), so it never
   covers the card's own words (Didier, 2026-10-04: it sat over "today"). */
.front-card .poster-trash {
  top: .5rem; right: .5rem; bottom: auto; border-radius: 10px;
  background: rgba(11,13,20,.72); color: var(--paper-ink); opacity: 0; transition: opacity .15s;
}
.front-card:hover .poster-trash, .front-card .poster-trash:focus-visible { opacity: 1; }
@media (hover: none) { .front-card .poster-trash { opacity: 1; } }

/* ---- The front door as one start box (web/front_door.py,
   INDY_NEW_FRONT_DOOR=1). The title bar is the same bar as on every
   other screen (Didier, 2026-10-04); the picture runs full-bleed under it.
   Two layouts on one wrapper: .front--first centres the box over the whole
   picture; .front--wall is the poster grid on the plain ground, with no
   picture, so the Paper Dreams image never clashes with the films' own
   posters. The box itself is the one glass surface in the app: it sits ON a
   picture, so it is the picture, blurred, that makes its ground. ---- */
body.page-front main { max-width: none; margin: 0; padding: 0; }
.front { position: relative; min-height: calc(100vh - 3.4rem); color: var(--paper-ink); }
.front-picture {
  position: absolute; inset: 0; z-index: 0;
  background: var(--g0) url('door-bg-idea.webp') 50% 70% / cover no-repeat;
}
.front--first .front-picture::after {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(ellipse 60% 55% at 50% 52%, rgba(14,14,14,.55), rgba(14,14,14,0) 70%),
    linear-gradient(180deg, rgba(14,14,14,.6) 0%, rgba(14,14,14,0) 22%, rgba(14,14,14,0) 70%, rgba(14,14,14,.75) 100%);
}
.front-stage { position: relative; z-index: 1; }
.front--first .front-stage {
  min-height: calc(100vh - 3.4rem); box-sizing: border-box; padding: 3rem 1.5rem 4.5rem;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.front-centre { width: 100%; display: flex; justify-content: center; }
.front-drop-hint { position: absolute; left: 0; right: 0; bottom: 1.75rem; margin: 0; text-align: center; font-size: .82rem; color: var(--paper-dim); }
.front--wall .front-stage { max-width: 90rem; margin: 0 auto; padding: 3.5rem 4rem 6rem; box-sizing: border-box; }
.front-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 2rem; flex-wrap: wrap; margin-bottom: 2.25rem; }
.front-head__title { display: flex; flex-direction: column; gap: .5rem; }
.front-eyebrow, .start-box__eyebrow {
  font-family: var(--label); font-weight: 600; font-size: .8rem;
  letter-spacing: .2em; text-transform: uppercase; color: var(--paper-dim);
}
.front-head h1 { margin: 0; font: 400 2.75rem/1.1 var(--body); letter-spacing: -.01em; text-transform: none; color: var(--paper-ink); }
.front-head__actions { display: flex; align-items: center; gap: 1.25rem; }
.front-head__hint { font-size: .82rem; color: var(--paper-dim); }
/* The screen's one filled button: paper, not colour (law: colour is never a
   material). Shared with the box's own Start. */
.front-new, .start-box__go {
  display: inline-flex; align-items: center; gap: .6rem; height: 3rem; padding: 0 1.35rem 0 1.1rem;
  border: 0; border-radius: 999px; background: var(--paper-ink); color: var(--g0);
  font-family: var(--label); font-weight: 600; font-size: .875rem;
  letter-spacing: .12em; text-transform: uppercase; cursor: pointer; box-shadow: none;
}
.front-new:hover, .start-box__go:hover { background: #fff; color: var(--g0); border: 0; }
.front-new:focus-visible, .start-box__go:focus-visible { outline: 2px solid var(--chrome); outline-offset: 3px; }
.front-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 1.75rem; row-gap: 2rem; }
.front-card { position: relative; animation: plate-rise .45s ease backwards; }
.front-card__link { display: flex; flex-direction: column; gap: .75rem; color: inherit; border-bottom: 0; }
.front-card__link:hover { color: inherit; border-bottom: 0; }
.front-card__plate {
  display: block; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 14px;
  background: var(--g2); border: 1px solid var(--line); transition: border-color .2s;
}
.front-card__plate img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .5s ease; }
.front-card__link:hover .front-card__plate { border-color: var(--paper-dim); }
.front-card__link:hover .front-card__plate img { transform: scale(1.04); }
.front-card__link:focus-visible { outline: none; }
.front-card__link:focus-visible .front-card__plate { border-color: var(--chrome); box-shadow: 0 0 0 3px color-mix(in srgb, var(--chrome) 25%, transparent); }
.front-card__empty {
  height: 100%; display: flex; align-items: center; justify-content: center;
  font-family: var(--label); font-size: .8rem; letter-spacing: .16em; text-transform: uppercase; color: var(--paper-faint);
}
.front-card__line { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; }
.front-card__title { font: 500 1.125rem/1.25 var(--body); color: var(--paper-ink); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.front-card__when { font-size: .82rem; color: var(--paper-faint); white-space: nowrap; }
.front-card__stats { font-size: .75rem; color: var(--paper-dim); margin-top: -.25rem; }
.front-card__new {
  aspect-ratio: 16 / 9; align-self: start; width: 100%; height: auto;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .6rem;
  border: 1px dashed var(--line-strong); border-radius: 14px; background: transparent;
  font-family: var(--label); font-weight: 600; font-size: .875rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--paper-dim); cursor: pointer; box-shadow: none;
  transition: border-color .2s, color .2s;
}
.front-card__new .icon { width: 1.6rem; height: 1.6rem; }
.front-card__new:hover, .front-card__new:focus-visible { border-color: var(--paper-dim); color: var(--paper-ink); background: transparent; outline: none; }
.front-trash { margin: 2.5rem 0 0; font-size: .8rem; color: var(--paper-dim); }
.front--first .front-trash { position: absolute; left: 0; right: 0; bottom: 3.5rem; margin: 0; text-align: center; }

/* The start box. */
.start-box {
  position: relative; width: 100%; max-width: 41.25rem; box-sizing: border-box;
  padding: 2.25rem 2.25rem 1.9rem; display: flex; flex-direction: column; gap: 1.4rem;
  border-radius: 22px; border: 1px solid rgba(239,230,214,.14);
  background: rgba(14,14,14,.62); box-shadow: 0 30px 80px rgba(0,0,0,.45);
  -webkit-backdrop-filter: blur(22px) saturate(1.1); backdrop-filter: blur(22px) saturate(1.1);
  text-align: left; color: var(--paper-ink);
}
.start-box__head { display: flex; flex-direction: column; gap: .5rem; }
.start-box__ask { font: 400 2.125rem/1.15 var(--body); letter-spacing: -.005em; color: var(--paper-ink); text-transform: none; }
.start-box__field {
  display: flex; flex-direction: column; gap: .6rem; padding: 1.1rem 1.1rem .9rem;
  border-radius: 14px; background: rgba(239,230,214,.06); border: 1px solid rgba(239,230,214,.18);
  transition: border-color .15s, box-shadow .15s;
}
.start-box__field:focus-within { border-color: color-mix(in srgb, var(--chrome) 75%, transparent); box-shadow: 0 0 0 4px color-mix(in srgb, var(--chrome) 16%, transparent); }
.start-box textarea {
  width: 100%; box-sizing: border-box; padding: 0; border: 0; background: transparent; resize: none; outline: none;
  box-shadow: none; color: var(--paper-ink); font: 400 1.19rem/1.5 var(--body);
}
.start-box textarea::placeholder { color: var(--paper-faint); }
.start-box__foot { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.start-box__hint { font-size: .82rem; color: var(--paper-dim); }
.start-box__go { height: 2.75rem; flex: none; padding: 0 1.1rem 0 1.25rem; }
.start-box__or { display: flex; align-items: center; gap: .9rem; font-size: .82rem; color: var(--paper-dim); }
.start-box__or::before, .start-box__or::after { content: ""; flex: 1; height: 1px; background: rgba(239,230,214,.14); }
.start-box__ways { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .75rem; }
.start-box__way {
  min-height: 4rem; box-sizing: border-box; padding: .75rem 1rem; display: flex; align-items: center; gap: .9rem;
  border-radius: 12px; background: rgba(239,230,214,.03); border: 1px solid rgba(239,230,214,.18);
  color: var(--paper-ink); transition: background .15s, border-color .15s;
}
.start-box__way:hover, .start-box__way:focus-visible { background: rgba(239,230,214,.08); border-color: rgba(239,230,214,.32); color: var(--paper-ink); outline: none; }
.start-box__way .icon { width: 1.4rem; height: 1.4rem; }
.start-box__way > span { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.start-box__way-name { font-family: var(--label); font-weight: 600; font-size: .94rem; letter-spacing: .1em; text-transform: uppercase; }
.start-box__way-note { font-size: .82rem; color: var(--paper-dim); }
.start-box__close {
  position: absolute; top: 1.25rem; right: 1.25rem; width: 2.75rem; height: 2.75rem; padding: 0;
  display: flex; align-items: center; justify-content: center; border-radius: 50%;
  background: transparent; border: 1px solid rgba(239,230,214,.18); color: var(--paper-ink); cursor: pointer; box-shadow: none;
}
.start-box__close:hover { background: rgba(239,230,214,.08); border-color: rgba(239,230,214,.32); }
.start-dialog { padding: 0; border: 0; background: transparent; width: min(41.25rem, calc(100vw - 2rem)); max-width: none; overflow: visible; }
.start-dialog::backdrop { background: rgba(14,14,14,.62); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); }
.start-dialog .start-box { background: rgba(22,22,22,.92); }

/* A file dragged over the page, or a script being read: the stage steps
   aside and the drop frame / reading page (the old door's own) take it. */
.front-work { position: relative; z-index: 1; max-width: 70rem; margin: 0 auto; padding: 7rem 2rem 4rem; display: none; }
body.front-door-dropping .front-work, body.front-door-reading .front-work { display: block; }
body.front-door-dropping .front-stage, body.front-door-reading .front-stage { display: none; }
body.front-door-reading .front-picture { opacity: .18; }
.front-work .door-drop { margin: 0; background: rgba(14,14,14,.7); }
@media (max-width: 900px) {
  .front--wall .front-stage { padding: 2rem 1.25rem 4rem; }
  .front-grid { grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
  .start-box { padding: 1.5rem 1.25rem; }
  .start-box__ask { font-size: 1.6rem; }
  .start-box__ways { grid-template-columns: 1fr; }
  .start-box__hint { display: none; }
}

/* ---- Approval dialog (delete project) ---- */
.confirm-dialog {
  padding: 0; border: 1px solid var(--live);
  background: var(--g3); color: var(--ink);
  width: min(30rem, calc(100vw - 2rem));
}
/* A confirm that destroys nothing (the Master tick) does not wear vermillion. */
.confirm-dialog.is-plain { border-color: var(--line-strong); }
.confirm-dialog::backdrop { background: rgba(0, 0, 0, .62); }
.confirm-dialog-frame {
  display: flex; flex-direction: column; gap: .8rem;
  padding: 1rem 1.2rem 1.2rem;
}
.confirm-dialog-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}
.confirm-dialog-head h3 { margin: 0; color: var(--display-ink); font-size: 1.2rem; }
.confirm-dialog-head .btn-icon { border-color: transparent; color: var(--ink-faint); }
.confirm-dialog p { margin: 0; color: var(--ink-dim); font-size: .9rem; line-height: 1.5; }
.confirm-dialog label { color: var(--ink-dim); font-size: .8rem; }
.confirm-dialog-actions { display: flex; justify-content: flex-end; gap: .5rem; margin-top: .2rem; }
.confirm-dialog button[disabled] { opacity: .4; cursor: not-allowed; box-shadow: none; }

/* The create-project tile: same plate geometry as its neighbours, but it is
   a form, not a link, so it keeps a quieter dashed border and no image zoom. */
.poster-new { border-style: dashed; }
.poster-new .poster-cap { display: flex; flex-direction: column; gap: .55rem; align-items: flex-start; }
.poster-new input[type=text] { width: 100%; font-size: 1.05rem; }

/* ---- Overview: pulse chips ---- */
/* The logline now sits in the head, directly under the title, so the pulse
   chips align to the TOP of a tall masthead block rather than floating at its
   vertical centre. */
.overview-head { align-items: flex-start; }
.overview-head-text { flex: 1 1 26rem; min-width: 0; }
.overview-byline { margin: .55rem 0 0; font-size: .78rem; }
/* The logline is prose, not metadata — body face, measured to ~62ch so it
   reads as a paragraph instead of running the full 1280px column. */
.overview-logline {
  margin: .5rem 0 0;
  max-width: 62ch;
  font-size: .92rem;
  line-height: 1.6;
  color: var(--ink-dim);
}
.pulse-chips { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.pulse-chip {
  font-family: var(--label); font-size: .72rem; letter-spacing: .05em;
  padding: .35rem .85rem; border: 1px solid var(--rule); border-radius: 999px;
  color: var(--ink-dim);
}
.pulse-chip.pulse-live {
  border-color: var(--live); color: var(--live);
}
/* Waiting-on-a-decision counts are ATTENTION, not live (law 2). */
.pulse-chip.pulse-attn {
  border-color: color-mix(in srgb, var(--warn) 55%, var(--g1)); color: var(--warn);
}
.pulse-chip.pulse-attn:hover { background: var(--g4); }
.pulse-chip.pulse-live:hover { background: var(--g4); color: var(--live); box-shadow: var(--glow-live); }
.pulse-chip.pulse-running {
  border-color: var(--live); color: var(--live);
  animation: pulse 1.6s ease-in-out infinite;
}
.pulse-chip.pulse-clear { border-color: var(--ok); color: var(--ok); }
/* A chip that opens the cost panel is a <button>, and the global button
   box rule reaches it by tag: restate the chip's own box. */
button.pulse-chip {
  height: auto; padding: .35rem .85rem; border-radius: 999px;
  font-size: .72rem; font-weight: inherit; letter-spacing: .05em;
  text-transform: none; line-height: inherit; background: none;
}
.pulse-chip .n { margin-left: .35em; }

/* ---- Overview: latest work — hero + supporting tiles ---- */
.latest-work {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 11rem;
  gap: .7rem;
  margin: 0 0 2.6rem;
}
.lw-tile {
  position: relative; overflow: hidden;
  border: 1px solid var(--rule);
  background: var(--surface-raise);
  animation: plate-rise .45s ease backwards;
}
.lw-tile:nth-child(2) { animation-delay: .05s; }
.lw-tile:nth-child(3) { animation-delay: .1s; }
.lw-tile:nth-child(4) { animation-delay: .15s; }
.lw-tile:nth-child(5) { animation-delay: .2s; }
.lw-tile:nth-child(6) { animation-delay: .25s; }
.lw-tile:nth-child(7) { animation-delay: .3s; }
.lw-tile img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .5s cubic-bezier(.2, .6, .2, 1);
}
.lw-tile:hover { border-color: var(--accent); }
.lw-tile:hover img { transform: scale(1.04); }
.lw-hero { grid-column: span 2; grid-row: span 2; }
/* Law 6: the hero caption is a row under the frame, not a chip over it. */
.lw-tile { display: flex; flex-direction: column; }
.lw-tile img { flex: 1; min-height: 0; }
.lw-cap {
  font-size: .65rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint);
  border-top: 1px solid var(--line);
  padding: .35rem .6rem;
}
@media (max-width: 900px) {
  .latest-work { grid-template-columns: repeat(2, 1fr); }
}

/* Brand overview — the cut is a SEQUENCE, so it gets an even auto-fit strip
   rather than .latest-work's showcase grid (1 hero + supporting tiles). A
   hero would scramble reading order, and on a cut the order IS the content. */
.brand-beats {
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  grid-auto-rows: 9rem;
}
@media (max-width: 900px) {
  .brand-beats { grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
}

/* ---- Overview: pipeline stage rail ---- */
.rail-label { margin-bottom: .8rem; }
.stage-rail {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
  gap: .7rem;
  margin-bottom: 2.6rem;
}
.stage-card {
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  border: 1px solid var(--rule);
  background: var(--bg-card);
  padding: 1.3rem .8rem 1rem;
  transition: border-color .15s ease;
}
.stage-card:hover { border-color: var(--accent); }

/* Completion ring — solar sweep on a quiet track, stage icon centred.
   --pct (0-100) drives the dash; pathLength=100 on the circles makes the
   math trivial. Solar (static highlight) is the right token: completion
   is state, not motion. */
.stage-ring {
  position: relative;
  display: inline-block;
  width: 4.4rem; height: 4.4rem;
  margin-bottom: .65rem;
}
.stage-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.stage-ring circle { fill: none; stroke-width: 2.4; }
.ring-track { stroke: var(--rule); }
.ring-fill {
  /* Dusk prism sweep — the arc reveals more of the spectrum as the stage
     completes (violet → chrome → cyan → orange). Def lives in project.html;
     chrome fallback for any surface without it. */
  stroke: var(--chrome);
  stroke: url(#dusk-ring);
  stroke-linecap: round;
  stroke-dasharray: var(--pct) 100;
  animation: ring-sweep .9s cubic-bezier(.3, .7, .2, 1) backwards;
}
@keyframes ring-sweep { from { stroke-dasharray: 0 100; } }
.stage-icon {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
}
.stage-icon svg {
  width: 1.7rem; height: 1.7rem;
  transform: none;
  fill: none; stroke: var(--surface-warm); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}
.stage-card:hover .stage-icon svg { stroke: var(--accent); }

.stage-name {
  font-family: var(--label); font-size: .68rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .14em;
  color: var(--ink-dim);
}
.stage-card:hover .stage-name { color: var(--accent); }
.stage-fig {
  font-family: var(--display); font-weight: 500;
  font-size: 1.8rem; line-height: 1.1;
  color: var(--surface-warm);
  padding-top: .1rem;
}
.stage-fig small {
  font-family: var(--body); font-size: .68rem; font-weight: 400;
  color: var(--ink-dim); letter-spacing: .05em;
}
.stage-sub { font-family: var(--body);
  font-size: .66rem; color: var(--ink-dim); letter-spacing: .04em;
  padding-top: .25rem;
  margin-top: auto;
}
.stage-sub .live { font-style: normal; color: var(--live); }

/* ---- Overview: admin drawer ---- */
.admin-drawer {
  border-top: 1px solid var(--rule);
  padding-top: 1rem;
  margin-top: 1rem;
}
.admin-drawer > summary {
  cursor: pointer; list-style: none;
  font-family: var(--label); font-size: .72rem;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint);
  padding: .35rem 0;
  user-select: none;
}
.admin-drawer > summary::-webkit-details-marker { display: none; }
.admin-drawer > summary::before { content: "▸ "; display: inline-block; transition: transform .15s; }
.admin-drawer[open] > summary::before { transform: rotate(90deg); }
.admin-drawer > summary:hover { color: var(--ink-dim); }
.admin-drawer-body { padding-top: 1.2rem; }

/* ---- Scenes: cards — the description beside the latest generation ---- */
.scene-circle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.5rem; height: 2.5rem; border-radius: 50%;
  font-family: var(--display); font-weight: 500; font-size: 1.05rem;
  line-height: 1;
  background: transparent;
  color: var(--surface-warm);
  /* Ring weight matched to the overview completion rings (stroke 2.4 in a
     36-unit viewBox at 4.4rem ≈ 3px rendered). */
  border: 3px solid;
}
.circle-solar { border-color: var(--chrome); }
.circle-ink   { border-color: var(--pr-orange); }   /* light orange — soft vermillion family */
.scene-cards {
  display: grid; gap: 1.4rem;
  grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
  margin-top: 1.8rem;
}
.scene-card {
  background: var(--bg-card); border: 1px solid var(--rule);
  display: flex; flex-direction: column;
}
.scene-card-canvas {
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 16/9; background: var(--surface-raise);
  overflow: hidden; border-bottom: 1px solid var(--rule);
}
.scene-card-canvas:hover { border-bottom-color: var(--rule); }
.scene-card-canvas img { width: 100%; height: 100%; object-fit: cover; display: block; }
.scene-card-empty { font-size: .75rem; letter-spacing: .06em; }
.scene-card-body { padding: .85rem 1rem 1rem; display: flex; flex-direction: column; gap: .55rem; flex: 1; }
.scene-card-head { display: flex; align-items: center; gap: .6rem; min-width: 0; }
.scene-card-grip { cursor: grab; font-size: 1.05rem; flex: 0 0 auto; user-select: none; }
.scene-card-grip:active { cursor: grabbing; }
.scene-card-grip:hover { color: var(--accent); }
.scene-card-head .scene-circle { width: 2rem; height: 2rem; font-size: .9rem; border-width: 2.5px; flex: 0 0 auto; }
.scene-card-heading {
  font-size: .85rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--surface-warm);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.scene-card:hover .scene-card-heading { color: var(--accent); border-bottom-color: transparent; }
.scene-card-time { flex: 0 0 auto; font-size: .7rem; margin-left: auto; }
.scene-card-desc { font-family: var(--body);
  margin: 0; font-size: .86rem; line-height: 1.5; font-style: italic;
  color: var(--ink-dim);
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
  overflow: hidden;
}
.scene-card-meta { font-family: var(--body); font-size: .72rem; letter-spacing: .04em; margin-top: auto; }

/* ---- Folder rail — entities as folders (Research files into them,
        Design browses them) ---- */
.rail-sticky {
  position: sticky; top: 3.55rem; z-index: 6;
  background: var(--surface-deep);
  padding: .7rem 0 .3rem;
  border-bottom: 1px solid var(--rule);
  margin-bottom: .4rem;
}
.folder-rail {
  display: flex; gap: 1.6rem; align-items: flex-start;
  overflow-x: auto;
  padding-bottom: .35rem;
}
.folder-group { display: flex; flex-direction: column; gap: .35rem; flex: 0 0 auto; }
.folder-group-label {
  font-family: var(--label); font-size: .6rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .14em;
  color: var(--ink-faint);
}
.folder-tiles { display: flex; gap: .55rem; }
.folder {
  display: flex; flex-direction: column; align-items: center; gap: .25rem;
  width: 4.1rem; flex: 0 0 auto;
  border-bottom: none;
}
.folder-rail--lg .folder { width: 5.4rem; }
.folder-thumb {
  position: relative;
  width: 4.1rem; height: 4.1rem;
  border: 1px solid var(--rule);
  background: var(--surface-raise);
  overflow: hidden;
  transition: border-color .15s ease, box-shadow .15s ease, transform .12s ease;
}
.folder-rail--lg .folder-thumb { width: 5.4rem; height: 5.4rem; }
.folder-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.folder-thumb-empty {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
  font-family: var(--body); font-size: 1.3rem; color: var(--ink-faint);
}
.folder-count {
  position: absolute; right: .15rem; bottom: .15rem;
  font-family: var(--mono); font-size: .58rem; line-height: 1;
  padding: .15rem .3rem;
  background: rgba(11, 17, 48, .8); color: var(--surface-deep);
  border-radius: 2px;
}
.folder-name {
  font-family: var(--display); font-size: .6rem; letter-spacing: .03em;
  color: var(--ink-dim); text-align: center; line-height: 1.2;
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.folder:hover .folder-thumb { border-color: var(--accent); }
.folder:hover .folder-name { color: var(--accent); }

/* Poster-scale folders — Design's default view: the folders ARE the page.
   Same component family as the rail, scaled to plates. */
.folder-section { margin-bottom: 2.2rem; }
.folder-wall {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: .9rem;
}
.folder-wall--hero { grid-template-columns: 1fr; }
/* stretch, not the rail's centring: a centred caption row grows to its text
   and a long name runs into the next poster */
.folder--poster { width: auto; gap: 0; align-items: stretch; min-width: 0; }
.folder--poster .folder-thumb {
  width: 100%; height: auto; aspect-ratio: 1 / 1;
}
.folder-wall--hero .folder--poster .folder-thumb { aspect-ratio: 21 / 7; }
/* Law 6: name, benchmark count and corpus count live in a row UNDER the
   folder plate — nothing sits on the picture. */
.folder-postrow {
  display: flex; align-items: baseline; gap: .55rem;
  padding: .55rem .1rem 0;
  min-width: 0;
}
.folder-poster-name {
  font-family: var(--display); font-weight: 600;
  font-size: 1rem; line-height: 1.2; letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--display-ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0; flex: 1;
}
.folder-wall--hero .folder-poster-name { font-size: 1.6rem; letter-spacing: .08em; }
.folder--poster:hover .folder-poster-name { color: var(--chrome); }
.folder--poster .folder-count {
  position: static;
  font-size: .68rem; padding: .25rem .45rem;
  background: var(--g3); color: var(--ink-dim);
  border: 1px solid var(--line);
}
.folder-bench {
  font-family: var(--label); font-size: .62rem; line-height: 1;
  padding: .25rem .4rem; border-radius: 2px;
  color: var(--bench); border: 1px solid color-mix(in srgb, var(--bench) 45%, var(--g1));
}
.folder--poster.folder--empty .folder-thumb {
  background: linear-gradient(150deg, var(--surface-raise) 0%, var(--rule) 100%);
}

/* Drag states — vermillion is correct here: a live interaction. */
body.sorting-active .folder-rail--droppable .folder-thumb {
  border-style: dashed; border-color: var(--ink-dim);
}
.folder.drop-target .folder-thumb {
  border: 2px solid var(--accent);
  box-shadow: 0 0 0 3px rgba(230, 50, 35, .18);
  transform: scale(1.06);
}
.folder.drop-target .folder-name { color: var(--accent); }
/* A run of the to-sort pile: the subject an import filed these under. The
   heading is a child of the one grid, so it takes the whole row. */
.refs-grid > .sort-run { grid-column: 1 / -1; margin: .6rem 0 -.4rem; }
.refs-grid > .sort-run:first-child { margin-top: 0; }
.refs-grid--sortable .ref-card[draggable="true"] { cursor: grab; }
.refs-grid--sortable .ref-card.dragging { opacity: .45; cursor: grabbing; }
.sort-status {
  min-height: 1.1em; margin: .25rem 0 0;
  font-size: .68rem; letter-spacing: .08em;
  color: var(--ok);
}
.sort-status.sort-status-error { color: var(--warn); }

/* ---- Research: condensed brief strip ---- */
/* ---- Research: the scout hero — one box, describe the look, dispatch ---- */
.scout-hero { margin: .2rem 0 1.2rem; }
.scout-form {
  display: flex; gap: .5rem; align-items: stretch;
  max-width: 46rem;
}
.scout-form input[type="text"] {
  flex: 1 1 auto; min-width: 0;
  font-size: .92rem; padding: .55rem .8rem;
}
.scout-form .btn-icon { flex: 0 0 auto; }
.scout-under {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem 1.2rem;
  margin-top: .45rem; max-width: 46rem;
}
.scout-scope summary {
  cursor: pointer; font-family: var(--label); font-weight: 600; font-size: .7rem; color: var(--ink-dim);
  letter-spacing: .14em; text-transform: uppercase;
}
.scout-scope summary:hover { color: var(--ink); }
.scout-scope .row { margin-top: .6rem; }
.scout-sources { font-size: .68rem; color: var(--ink-faint); margin: 0; }
.scout-preflight { margin: .5rem 0 0; }
.brief-chip-link {
  display: inline-flex; align-items: center; gap: .45rem;
  min-width: 0; text-decoration: none; color: inherit;
}
.brief-chip-note {
  font-size: .64rem; color: var(--ink-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 16rem;
}
.brief-chip-note:empty { display: none; }
.brief-failed .brief-chip-note { color: var(--warn); }
.brief-chip-retry { display: inline-flex; }
.brief-chip-retry .btn-icon { padding: .2rem .4rem; }

.brief-strip {
  display: flex; flex-wrap: wrap; gap: .45rem;
  margin: .4rem 0 1.4rem;
}
.brief-chip {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .32rem .7rem;
  border: 1px solid var(--rule);
  background: var(--bg-card);
  font-size: .76rem;
  max-width: 24rem;
}
.brief-chip:hover { border-color: var(--accent); border-bottom-color: var(--accent); }
.brief-dot {
  width: .5rem; height: .5rem; border-radius: 50%; flex: 0 0 auto;
  background: var(--ink-faint);
}
.brief-returned .brief-dot { background: var(--ok); }
.brief-failed .brief-dot { background: var(--warn); }
.brief-queued .brief-dot { background: var(--ink-faint); }
.brief-running .brief-dot { background: var(--live); animation: pulse 1.4s ease-in-out infinite; }
/* Music room: a cue that has audio on the timeline's music lane. */
.cue-dot {
  width: .5rem; height: .5rem; border-radius: 50%; flex: 0 0 auto;
  display: inline-block; background: var(--ok);
}
/* A note left on the music lane, shown against the cue it was made over.
   Ruled off to the left rather than boxed: it is an annotation ON the
   cue's direction, not a second kind of direction. */
.cue-note {
  margin-top: .4rem; padding-left: .55rem;
  border-left: 2px solid var(--accent);
  color: var(--ink); font-size: .82rem; line-height: 1.45;
}
.cue-note .mono {
  display: block; font-size: .66rem; color: var(--ink-faint);
  letter-spacing: .04em;
}
.score-notes { margin: .5rem 0 1.4rem; }
/* A recompose that has something to answer. */
.btn-icon.has-note { border-color: var(--accent); }
.btn-icon.has-note .icon { color: var(--accent); }
.brief-chip-id { font-size: .64rem; color: var(--ink-faint); }
.brief-chip-task { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.brief-chip-count {
  font-size: .64rem; color: var(--ink-dim);
  border: 1px solid var(--rule); border-radius: 999px; padding: .05rem .4rem;
}

/* ---- Storyboard: human board upload ---- */
.board-upload-form { display: inline; }
/* Wrapper only — the <summary> inside is the button (.btn.btn-icon), so the
   details element carries no border, padding or box of its own. */
.board-upload { display: inline-block; white-space: nowrap; cursor: pointer; }
.board-hand-pill { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, var(--g1)); }

/* ── Architecture mode: Overview shot table + section count chips ──────────── */
.data-table { border-collapse: collapse; font-size: .92rem; }
.data-table th {
  text-align: left; font-family: var(--label); font-size: .68rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-dim);
  font-weight: 600; padding: .35rem .7rem; border-bottom: 1px solid var(--rule);
}
.data-table td { padding: .45rem .7rem; border-bottom: 1px solid var(--rule); vertical-align: middle; }
.data-table tbody tr:hover { background: var(--bg-raise); }
.grid-count { color: var(--ink-faint); font-family: var(--mono); font-size: .82rem; font-weight: 400; }
.filter-row .pill { cursor: pointer; }

/* --- Scene detail: the spread ---------------------------------------------
   Script page (source) beside its shot list (interpretation). The shots are
   a filmstrip: each card pairs the board frame with its text. */
.scene-cast { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 1.1rem; }
.scene-cast-chip {
  display: inline-flex; align-items: baseline; gap: .5rem;
  font-family: var(--label); font-size: .74rem; letter-spacing: .04em;
  border: 1px solid var(--rule); padding: .3rem .75rem;
  color: var(--surface-warm);
}
.scene-cast-chip:hover { border-color: var(--accent); border-bottom-color: var(--accent); color: var(--accent); }
.scene-cast-kind { font-size: .62rem; text-transform: uppercase; letter-spacing: .12em; color: var(--ink-faint); }
.scene-cast-src { font-size: .62rem; color: var(--ink-faint); }

.scene-title { margin: .6rem 0 0; }
.scene-spread {
  display: grid; grid-template-columns: minmax(0, 37.5rem) minmax(0, 1fr);
  gap: 3.5rem; margin-top: 2.8rem;
}

/* The scene's script on paper (Brett, 2026-09-14): a white sheet in
   screenplay format beside the shots, so it reads as THE SCRIPT at a glance.
   The column holds its place while the shots scroll, and the sheet scrolls
   by itself. It borrows the script page's .screenplay sheet and scopes the
   layout to a 37.5rem page: a 6in text column at 5rem to the inch, with the
   standard positions from the left margin (character 2.2in, parenthetical
   1.6in, dialogue 1.0in and 3.5in wide) and the scene number in both
   margins of the slug line. */
.scene-script { position: sticky; top: 6rem; align-self: start; }
.scene-paper {
  max-height: calc(100vh - 10rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: #fff;
  border-radius: 2px;
  box-shadow: 0 2px 18px rgba(0,0,0,.35);
}
.screenplay.scene-screenplay {
  width: auto;
  max-width: none;
  margin: 0;
  box-shadow: none;
  padding: 3.5rem 3rem 5rem 4.5rem;
  font-size: 13.5px;
  line-height: 17px;
}
.scene-screenplay .sp-slug {
  display: flex;
  margin: 0 -2.5rem 17px -3.25rem;
  font-size: inherit;
  line-height: inherit;
}
.scene-screenplay .sp-num { flex: 0 0 auto; width: 3.25rem; }
.scene-screenplay .sp-num:last-child { width: 2.5rem; text-align: right; }
.scene-screenplay .scene-slug-text { flex: 1; min-width: 0; }
.scene-screenplay .sp-blank { height: 17px; }
.scene-screenplay .sp-action { margin: 0; }
.scene-screenplay .sp-cue { margin: 0 0 0 11rem; }
.scene-screenplay .sp-paren { margin: 0 0 0 8rem; max-width: 12.5rem; font-style: normal; }
.scene-screenplay .sp-dialogue { margin: 0 0 0 5rem; max-width: 17.5rem; }
.scene-screenplay .sp-trans { margin: 0; }

@media (max-width: 1080px) {
  .scene-spread { grid-template-columns: 1fr; gap: 2.5rem; }
  .scene-script { position: static; }
  .scene-paper { max-height: 75vh; }
}
@media (max-width: 640px) {
  .screenplay.scene-screenplay { padding: 2rem 1.25rem 3rem 2.5rem; }
  .scene-screenplay .sp-slug { margin-left: -1.75rem; margin-right: -1rem; }
  .scene-screenplay .sp-num { width: 1.75rem; }
  .scene-screenplay .sp-num:last-child { width: 1rem; }
  .scene-screenplay .sp-cue { margin-left: 35%; }
  .scene-screenplay .sp-paren { margin-left: 25%; }
  .scene-screenplay .sp-dialogue { margin-left: 15%; }
}

.scene-shots-head { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.1rem; min-height: 2.2rem; }
.scene-shots-meta { font-size: .74rem; letter-spacing: .04em; }
.scene-shots-head form, .scene-plan-ok { margin-left: auto; }
.scene-plan-ok { font-size: .74rem; color: var(--ok); letter-spacing: .04em; }

.shot-cards { display: flex; flex-direction: column; gap: 1rem; }
.shot-card {
  display: flex; gap: 1.1rem;
  background: var(--bg-card); border: 1px solid var(--rule);
  transition: border-color .15s ease;
}
.shot-card:hover { border-color: var(--ink-dim); }
/* The thumb sits at the top of the card at the frame's own aspect: a long
   description grows the card, never the picture (vault/47 A5, NOMAN 7.70).
   The min-height is only the "no frame" placeholder's. */
.shot-card-thumb {
  position: relative; width: 11.5rem; flex: 0 0 auto; align-self: flex-start;
  min-height: 6.5rem;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-raise); border-right: 1px solid var(--rule);
  overflow: hidden;
}
.shot-card-thumb:has(img) { min-height: 0; }
.shot-card-thumb img { display: block; width: 100%; height: auto; }
.shot-card-thumb span { font-size: .68rem; letter-spacing: .06em; }
.shot-card-body { flex: 1; min-width: 0; padding: .8rem 1rem .85rem 0; display: flex; flex-direction: column; }
.shot-card-head { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; }
.shot-card-code { font-size: .95rem; font-weight: 600; letter-spacing: .04em; }
/* Stable shot identity (shot.id) beside the renumberable code — every key
   review surface carries it so "7.60" and "s66" can be cross-checked. */
.shot-idtag { font-family: var(--mono); font-size: .68em; font-weight: 400; color: var(--ink-faint); letter-spacing: .06em; text-transform: none; }
.shot-card-code:hover { color: var(--accent); border-bottom-color: transparent; }
.shot-card-meta { font-size: .74rem; letter-spacing: .04em; }
.shot-card-actions {
  margin-left: auto; display: flex; gap: .3rem;
  opacity: .3; transition: opacity .15s ease;
}
.shot-card:hover .shot-card-actions,
.shot-card:focus-within .shot-card-actions { opacity: 1; }
.shot-card-desc { margin: .4rem 0 0; font-size: 1rem; line-height: 1.6; }
.shot-card--cut { opacity: .45; }
.shot-card--cut .shot-card-desc { text-decoration: line-through; }
.shot-desc-form { display: flex; flex-direction: column; gap: .55rem; margin-top: .5rem; }
.shot-desc-form[hidden] { display: none; }
.shot-desc-form textarea { font-family: var(--body); font-size: .98rem; line-height: 1.5; min-height: 4.5rem; }
.shot-desc-form-row { display: flex; gap: .5rem; align-items: center; }
/* The whole shot edits in place (2026-09-16): size, camera move and seconds
   beside the description, labelled the way the scene notes are. */
.shot-desc-form-fields { display: flex; flex-wrap: wrap; gap: .6rem 1.1rem; align-items: flex-end; }
.shot-desc-form-fields label {
  display: flex; flex-direction: column; gap: .3rem; margin: 0;
  font-family: var(--label); font-weight: 600; font-size: .62rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint);
}
.shot-desc-form-fields select, .shot-desc-form-fields input {
  width: auto; min-width: 8rem; margin: 0;
  font-family: var(--body); font-size: .9rem; letter-spacing: 0; text-transform: none;
}
.shot-desc-form-fields input[name="duration_est"] { width: 5.5rem; min-width: 0; }

/* A shot and its passage light each other up (2026-09-16): hover a shot and
   the lines it covers wash blue on the paper; hover a line and its shot
   answers. The wash is the one accent, as a tint, so the paper stays paper. */
.scene-screenplay [data-tok] { border-radius: 2px; transition: background-color .12s ease, box-shadow .12s ease; }
.scene-screenplay [data-tok].is-hot { background-color: rgba(97, 160, 255, .22); box-shadow: 0 0 0 .35em rgba(97, 160, 255, .22); }
.shot-card.is-hot { border-color: var(--accent); }

.scene-shots-empty { padding: 2.2rem 1.5rem; border: 1px dashed var(--rule); }
.scene-shots-empty p { margin: 0 0 1rem; }
.scene-shots-foot { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1.4rem; align-items: flex-start; }

/* ---- The scene page, tidied (2026-09-14) ----
   Nothing on this page floats. The scene's notes are labelled lines that open
   in place; the add-shot, compose and add-reference panels open below their
   buttons and push the page down, one at a time. A shot carries its own
   takes, and References holds what was brought in. */
.scene-notes { margin: 0 0 1.8rem; border-top: 1px solid var(--box-line); }
.scene-note { border-bottom: 1px solid var(--box-line); }
.scene-note-row {
  display: grid; grid-template-columns: 5.5rem minmax(0, 1fr) auto;
  column-gap: .9rem; align-items: baseline;
  padding: .6rem 0; list-style: none; cursor: pointer;
}
.scene-note-row::-webkit-details-marker { display: none; }
.scene-note--static .scene-note-row { cursor: default; }
.scene-note-k {
  font-family: var(--label); font-weight: 600; font-size: .62rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint);
}
.scene-note-v {
  min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  font-size: .9rem; color: var(--ink-dim);
}
.scene-note-v.is-empty { color: var(--ink-ghost); }
.scene-note-v a { color: var(--ink-dim); }
.scene-note-v a:hover { color: var(--chrome); }
.scene-note-go { font-size: .78rem; color: var(--ink-faint); white-space: nowrap; }
.scene-note-go .icon { width: .8rem; height: .8rem; }
.scene-note-row:hover .scene-note-go, .scene-note[open] .scene-note-go { color: var(--chrome); }
.scene-note[open] .scene-note-v { visibility: hidden; }
.scene-note-form { display: flex; flex-direction: column; gap: .55rem; padding: 0 0 1rem 6.4rem; }
.scene-note-form textarea { font-family: var(--body); font-size: .95rem; line-height: 1.5; min-height: 5rem; }
.scene-note-actions { display: flex; gap: .5rem; }

.scene-panel {
  margin: .9rem 0 0; padding: 1.2rem 1.4rem;
  background: var(--box-bg); border: 1px solid var(--box-line); box-shadow: var(--box-shadow);
}
.scene-panel[hidden] { display: none; }
.scene-panel--inline { display: flex; align-items: center; flex-wrap: wrap; gap: .7rem; }
.scene-panel--inline label { margin: 0; }
.scene-panel--inline select { width: auto; }
.scene-panel-actions { display: flex; gap: .5rem; margin-top: .9rem; }

/* The calm reference card: the picture, then its title and, while it is
   undecided, ✓ ✕. Every other action stays in the markup for the lightbox. */
.ref-card.ref-card--quiet { display: grid; grid-template-columns: minmax(0, 1fr) auto; }
.ref-card--quiet > .thumb-link,
.ref-card--quiet > .thumb-fallback,
.ref-card--quiet > .thumb-placeholder { grid-column: 1 / -1; }
.ref-card.ref-card--quiet .body { padding: .6rem .8rem; gap: 0; justify-content: center; }
.ref-card.ref-card--quiet .title { -webkit-line-clamp: 1; font-size: .92rem; }
.ref-card.ref-card--quiet .body > .meta { display: none; }
.ref-card.ref-card--quiet .actions { border-top: 0; padding: .45rem .6rem .45rem 0; flex-wrap: nowrap; }
.ref-card.ref-card--quiet .actions > * { display: none; }
.ref-card.ref-card--quiet .actions > form:has(.btn-approve),
.ref-card.ref-card--quiet .actions > form:has(.btn-reject) { display: inline; }
.ref-card.ref-card--quiet.state-proposed { border-left: 1px solid var(--rule); }
/* Benchmark says so with its gold edge; the star glyph sat on the picture. */
.ref-card.ref-card--quiet.state-benchmark::before { display: none; }

.scene-refs { margin-top: 3.2rem; }
.scene-refs .gallery-head h3 { margin: 0; }
.scene-refs .gallery-head { margin-bottom: 0; }
.scene-refs .refs-grid, .scene-refs .gallery-empty { margin-top: 1.2rem; }
.scene-fold { margin-top: 2rem; }
.scene-fold > summary {
  display: flex; align-items: baseline; gap: .6rem;
  padding: .7rem 0; border-top: 1px solid var(--box-line);
  list-style: none; cursor: pointer;
}
.scene-fold > summary::-webkit-details-marker { display: none; }
.scene-fold > summary:hover .lbl { color: var(--ink-dim); }
.scene-fold-n { font-family: var(--mono); font-size: .78rem; color: var(--ink-ghost); }

@media (max-width: 760px) {
  .scene-note-form { padding-left: 0; }
}

/* Entities-in-scene: one row per entity, quiet source suffix */
.scene-entities { list-style: none; padding: 0; }
.scene-entities li { margin-bottom: .45rem; }
.scene-entity-src { font-size: .68rem; margin-left: .35rem; }

/* Empty-state hygiene: the to-sort zone only earns its height when cards wait. */
.gallery-empty { padding: .8rem 1rem; font-size: .85rem; }

/* ---- Icon buttons ----
   One icon vocabulary (partials/icons.html), same hand-drawn stroke language
   as the overview stage rail. Icon-only buttons carry title + aria-label —
   the lightbox reads labels from there, never from textContent. */
.icon {
  width: 1.05rem; height: 1.05rem;
  fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
  display: block; flex: 0 0 auto;
}
.btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  gap: .3rem;
  height: 32px; min-width: 32px;
  padding: 0 .45rem;
  line-height: 0;
}
.btn-icon .count { font-family: var(--mono); font-size: .68rem; line-height: 1; }
/* Showcase toggle — lit chrome when the ref is on the overview strip. */
.btn-icon.showcase-on { color: var(--chrome); border-color: var(--chrome); }
/* Fourteen controls have to sit on ONE line inside a ~390px panel, with the
   trash reachable at the far right — so the board's icon buttons run tighter
   than everywhere else in the app. Fixed width rather than icon + padding:
   the width is the thing under budget (23.2px × 14 + 2px × 13 = 350.8), and a
   fixed box keeps every button identical no matter what it contains. */
.storyboard-panel .panel-actions .btn-icon {
  position: relative;
  width: 1.45rem; height: auto; min-width: 0; padding: .35rem 0;
}
.storyboard-panel .panel-actions .icon { width: .95rem; height: .95rem; }
/* The feedback count used to sit inline and widen just that one button, which
   is exactly the button that has a count — so the row's worst case was also
   its most common one. As a corner badge it costs no width at all. */
/* Corner, but INSIDE the button: .panel-actions carries overflow-x for the
   narrow-panel fallback, and a row with overflow on one axis clips the other
   too — a badge hung outside the button's top edge is clipped away. */
.storyboard-panel .panel-actions .btn-icon .count {
  position: absolute; top: 1px; right: 1px;
  background: var(--accent); color: var(--bg);
  font-size: .55rem; line-height: 1.2; padding: 0 .12rem;
  border-radius: 2px; pointer-events: none;
}
/* Page-level CTAs keep a short label beside the icon. */
.btn-labeled { display: inline-flex; align-items: center; gap: .45rem; }
.btn-labeled .icon { width: .9rem; height: .9rem; }
/* The hand-board disclosure sits in the actions row as a peer of the other
   icon buttons — solid border like the rest, no dashed outlier. */
.board-upload > summary.btn-icon { border-style: solid; }

/* ---- Board: one continuous strip ---- */
.scene-chip { flex: 0 0 auto; }
.board-empty {
  margin-top: 2.5rem; padding: 3rem 1.5rem;
  border: 1px dashed var(--rule);
  text-align: center;
}
.board-empty p { max-width: 42rem; margin: 0 auto; }
.board-empty form { display: inline-block; margin-top: 1.2rem; }

/* Board styleframes toggle — .board-strip shows board sketches by default;
   .sf-on swaps each panel to its styleframe where one exists (panels without
   one keep the board frame, dimmed via .sf-fallback so the gap reads). */
.board-strip .view-sf { display: none; }
.board-strip.sf-on .view-sf { display: block; }
.board-strip.sf-on .view-board { display: none; }
.board-strip.sf-on .view-sf.sf-fallback img { opacity: .35; }
#sf-toggle.active { border-color: var(--chrome); color: var(--chrome); }
/* Icon-only header toggles (2026-08-20): active state must be visible on
   the keyline, since there is no label left to read. */
.page-head-actions .btn.active { border-color: var(--chrome); color: var(--chrome); }
.sf-pill { border-color: var(--chrome); color: var(--chrome); }

/* Per-panel styleframe flip (SF pill click) — XOR against the global toggle:
   flipped panel shows the OTHER view from whatever the strip is showing. */
.board-strip .storyboard-panel.sf-flip .view-sf { display: block; }
.board-strip .storyboard-panel.sf-flip .view-board { display: none; }
.board-strip.sf-on .storyboard-panel.sf-flip .view-sf { display: none; }
.board-strip.sf-on .storyboard-panel.sf-flip .view-board { display: block; }
/* The image is the button: clicking a panel image opens the lightbox, so
   the cursor says zoom and a small magnifier hint fades in on hover
   (pointer-events none — it's a cue, not a control; the whole image is). */
.panel-canvas [data-lightbox-card] { position: relative; width: 100%; height: 100%; }
.panel-canvas [data-lightbox-card] img.thumb { cursor: zoom-in; }
.panel-canvas [data-lightbox-card]::after {
  content: '';
  position: absolute; right: .5rem; bottom: .5rem;
  width: 1.15rem; height: 1.15rem;
  pointer-events: none;
  opacity: 0;
  transition: opacity .15s ease;
  background-color: rgba(14, 14, 14, .62);
  border-radius: 3px;
  padding: .2rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23DBDBDB' stroke-width='1.5' stroke-linecap='round'%3E%3Ccircle cx='10.5' cy='10.5' r='6'/%3E%3Cpath d='M15 15l6 6 M8 10.5h5 M10.5 8v5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 1.05rem;
}
.panel-canvas [data-lightbox-card]:hover::after { opacity: 1; }

/* Inline describe-form — a shot with no description shows this instead of
   the description line, so a freshly added shot can be written up without
   leaving the strip. */
.panel-desc-form { display: flex; gap: .35rem; align-items: flex-start;
                   margin: .35rem 0 0; }
.panel-desc-form textarea { flex: 1 1 auto; font-size: .78rem;
                            min-height: 2.6rem; resize: vertical; }

/* Panel text layer — description + dialogue collapsed to one summary line so
   the strip stays a judging surface. Everything achromatic. The panel's text
   gutter is 1rem (matches .panel-desc), carried by the container so the
   summary AND the editors inside share it. Flex on a <summary> suppresses the
   native disclosure triangle, so the chevron is drawn explicitly — right when
   closed, down when open. */
.panel-text { padding: 0 1rem .45rem; }
.panel-text > summary { display: flex; align-items: flex-start; gap: .45rem;
                        cursor: pointer; font-size: .76rem; color: var(--fg);
                        padding: .4rem 0 .35rem; list-style: none; }
.panel-text > summary::-webkit-details-marker { display: none; }
/* Disclosure — the house chevron icon, not a CSS border artefact. Points
   right closed, down open; ghost at rest, ink on hover. */
.panel-text > summary .pt-chevron { flex: 0 0 auto; color: var(--ink-ghost);
                                    margin-top: .18rem; }
.panel-text > summary:hover .pt-chevron { color: var(--ink-dim); }
.panel-text > summary .pt-chevron .icon { width: .68rem; height: .68rem;
                                          transition: transform .14s ease; }
.panel-text[open] > summary .pt-chevron .icon { transform: rotate(90deg); }
.panel-text > summary .pt-sum-col { flex: 1 1 auto; min-width: 0;
                                    display: flex; flex-direction: column;
                                    gap: .15rem; }
/* The collapsed face says what each line is (Brett, 2026-09-14: "not super
   clear what everything is"). A label column in the house label face, then
   the value: ACTION, the speaker's name, SOUND; SCENE MUSIC and SCENE SOUND on
   a scene's first panel. Values are grey reading text, never blue (blue is
   never body copy). One line each, except speech, which wraps in full at
   reading size because the lines are part of the sequence being judged. */
.panel-text > summary .pt-row { display: grid; grid-template-columns: 4.75rem minmax(0, 1fr);
                                column-gap: .6rem; align-items: baseline; min-width: 0; }
.panel-text > summary .pt-k { font-family: var(--label); font-weight: 600; font-size: .6rem;
                              letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint);
                              white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.panel-text > summary .pt-v { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
                              font-size: .8rem; line-height: 1.45; color: var(--ink-dim); }
.panel-text > summary .pt-v.dim { color: var(--ink-ghost); }
.panel-text > summary .pt-dlg-line .pt-k { color: var(--ink-dim); }
.panel-text > summary .pt-dlg-line .pt-v { white-space: normal; font-size: .9rem; font-style: italic; color: var(--ink); }
.panel-text > summary .pt-row.is-empty .pt-k { color: var(--ink-ghost); }
.panel-text[open] > summary .pt-sum-col { display: none; }
/* Inside the details the desc already sits in the container's gutter — drop
   its own 1rem so it doesn't double-indent. */
.panel-text .panel-desc { padding: .1rem 0 .4rem; }

/* Scene audio drawer: music + sound notes on the scene's first panel. The
   collapsed face is the same labelled rows (SCENE MUSIC, SCENE SOUND), set
   off from the shot's own lines by a hairline because they belong to the
   whole scene. Open: icon-keyed rows with LARGE textareas, since direction
   is read at arm's length. */
.panel-audio { border-top: 1px solid var(--box-line); padding-top: .4rem; }

/* Revised, hold to compare: good news, so it is quiet (Brett, 2026-09-14). A
   small label with a green tick, no pill, no border; it lights while held. */
.storyboard-panel .panel-meta.rev-row { border-top: 0; padding: 0 1rem .45rem; }
.storyboard-panel .panel-meta .rev-compare {
  display: inline-flex; align-items: center; gap: .3rem;
  height: auto; min-height: 0; padding: .1rem 0;
  background: none; border: 0; box-shadow: none;
  font-family: var(--label); font-weight: 600; font-size: .6rem;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-faint); cursor: pointer; user-select: none;
}
.storyboard-panel .panel-meta .rev-compare .icon { width: .7rem; height: .7rem; color: var(--ok); }
.storyboard-panel .panel-meta .rev-compare:hover,
.storyboard-panel .panel-meta .rev-compare.comparing { color: var(--ink); border: 0; background: none; }
.audio-form { display: flex; gap: .45rem; align-items: flex-start;
              margin: .4rem 0 0; }
.audio-form .audio-key { flex: 0 0 auto; color: var(--ink-ghost);
                         padding-top: .45rem; }
.audio-form .audio-key.filled { color: var(--chrome); }
.audio-form .audio-key .icon { width: 1.05rem; height: 1.05rem; }
.audio-form textarea { flex: 1 1 auto; min-width: 0; resize: vertical;
                       font-size: .95rem; line-height: 1.5;
                       min-height: 3.6rem; field-sizing: content;
                       max-height: 16rem; }
.audio-form .btn-icon { flex: 0 0 auto; margin-top: .3rem; }

/* Dialogue lines editor — speaker pulldown, spoken line (italic = speech),
   remove per line; +, pull-from-script and save on the controls row. */
.panel-dialogue-form { display: flex; flex-direction: column; gap: .45rem;
                       margin: .5rem 0 .15rem; }
.dlg-line { display: flex; gap: .45rem; align-items: flex-start; }
.dlg-line select { flex: 0 0 auto; max-width: 8rem; font-size: .78rem; }
/* field-sizing: the box grows to fit the line — a spoken line is read in
   full while editing, never scrolled inside a two-row slot. */
.dlg-line textarea { flex: 1 1 auto; font-size: .95rem; font-style: italic;
                     line-height: 1.5; padding: .35rem .5rem;
                     min-height: 2.6rem; field-sizing: content;
                     max-height: 14rem; resize: vertical; min-width: 0; }
.dlg-line textarea::placeholder { font-style: italic; }
.dlg-controls { display: flex; gap: .3rem; align-items: center; }
.dlg-controls select { flex: 1 1 auto; font-size: .72rem; min-width: 0; }

/* Request-a-new-storyboard popover in the board header — same centered-card
   treatment as the description editor (.desc-pop). */
.new-shot-pop { display: inline-block; }
.new-shot-pop[open] > summary { position: relative; z-index: 201;
                                border-color: var(--accent); }
.new-shot-pop[open] > form { position: fixed; top: 50%; left: 50%;
                             transform: translate(-50%, -50%); z-index: 200;
                             width: min(38rem, 92vw);
                             background: var(--bg-card);
                             border: 1px solid var(--rule); padding: .9rem;
                             box-shadow: 0 18px 50px rgba(0,0,0,.7);
                             display: flex; flex-direction: column;
                             gap: .6rem; }
.new-shot-pop[open] > form textarea { width: 100%; resize: vertical;
                                      font: inherit; line-height: 1.55; }
.new-shot-pop[open] > form > button { align-self: flex-start; }

/* Feedback controls pair up per panel: 💬/brush aimed at the board frame
   (.fb-board) vs at the styleframe (.fb-sf). The pair whose image is on
   the canvas is live — same global-toggle XOR per-panel-flip logic as the
   picture itself, but with inline display values so the actions row keeps
   its flow. Panels without a styleframe (.has-sf absent) keep the board
   pair even in styleframes view: the dimmed board IS the visible image. */
.board-strip .fb-sf { display: none; }
.board-strip.sf-on .storyboard-panel.has-sf:not(.sf-flip) .fb-board,
.board-strip:not(.sf-on) .storyboard-panel.has-sf.sf-flip .fb-board { display: none; }
.board-strip.sf-on .storyboard-panel.has-sf:not(.sf-flip) button.fb-sf,
.board-strip:not(.sf-on) .storyboard-panel.has-sf.sf-flip button.fb-sf { display: inline-flex; }
.board-strip.sf-on .storyboard-panel.has-sf:not(.sf-flip) form.fb-sf,
.board-strip:not(.sf-on) .storyboard-panel.has-sf.sf-flip form.fb-sf { display: inline; }

.sf-flip-btn { background: none; font: inherit; height: auto; }
.storyboard-panel.sf-flip .sf-flip-btn { background: var(--g4); color: var(--chrome); border-color: var(--chrome); }

/* Continuity flop — a horizontal mirror for screen direction. The flag rides
   the SHOT, so board sketch and styleframe mirror together and can never
   disagree with the timeline or the rendered cut. Display-only here; the mp4
   renderer applies the same mirror via ffmpeg hflip. The button's own icon
   mirrors on hover, which is the whole preview the control needs. */
.storyboard-panel.flopped .panel-canvas img { transform: scaleX(-1); }
.flop-btn .icon { transition: transform .16s ease; }
.flop-btn:hover .icon { transform: scaleX(-1); }
.storyboard-panel.flopped .flop-btn,
.shot-media.flopped .flop-btn { border-color: var(--chrome); color: var(--chrome); }

/* Workbench (shot page): same flag, same mirror, applied to whichever take
   is on show — board frame, styleframe or motion take. */
.shot-media.flopped img,
.shot-media.flopped video { transform: scaleX(-1); }
/* The mirror takes the <video>'s native controls with it, so the time
   reads backwards. Mirror the controls panel back: the picture is flopped,
   the controls are not (vault/47 A8). WebKit/Blink only; checked in
   Chromium, not yet in Safari. */
.shot-media.flopped video::-webkit-media-controls-panel { transform: scaleX(-1); }
.row-head { display: flex; align-items: center; gap: .6rem; }

/* ---- Tooltips ----------------------------------------------------------
   The native `title` bubble waits ~1–1.5s and the delay cannot be changed, so
   tooltip.js takes the title off the element and re-shows it here on our own
   timing (140ms cold, instant while warm). Chrome-on-glass, like every other
   floating surface: g3 body, hairline, mono at label size. */
.g-tip {
  position: fixed; z-index: 900; pointer-events: none;
  max-width: 300px; padding: 5px 9px;
  background: var(--g3); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: 5px;
  box-shadow: 0 10px 26px -12px #000;
  font-family: var(--body); font-size: .8rem; line-height: 1.42;
  letter-spacing: .02em; text-transform: none;
  opacity: 0; transition: opacity .09s ease;
}
.g-tip.on { opacity: 1; }

/* State decisions sitting inline with the state pill on ref_detail (2026-08-10)
   — the pill answers "what state is this?" and the buttons answer "change it",
   so they read as one control rather than a label at the top and an action
   bar below the fold. Icon-only, keyline, matching the board's ✓/✕. */
.state-actions {
  display: inline-flex;
  gap: 4px;
  vertical-align: middle;
  margin-left: 6px;
}
.state-actions form { display: inline-flex; }
.state-actions .btn-icon {
  padding: 2px 6px;
  line-height: 1;
}
/* The id/state/provenance line was a <p>; it is a div now (forms can't live in
   a paragraph), so it needs the paragraph's own bottom spacing back. */
.ref-idline { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 1em 0; }

/* Scene text editor (scene page). The paper hides while the drawer is open
   so the same text is never on screen twice. */
.scene-script-head { display: flex; align-items: center; gap: .6rem; margin-bottom: .5rem; min-height: 2.2rem; }
.scene-script-head > details { margin-left: auto; }
/* Centred, not inline: these run to thousands of characters (scene 7 is
   3273), so an in-column textarea is unusable. Same treatment as the board's
   description editor, deliberately — one way to edit story text, wherever
   you are. */
.scene-body-edit[open] > .scene-body-form { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 200; width: min(52rem, 92vw); margin: 0; padding: .9rem; box-shadow: 0 18px 50px rgba(0,0,0,.7); }
.scene-body-edit[open] > .scene-body-form textarea { resize: vertical; font: inherit; line-height: 1.55; min-height: 42vh; }
.scene-body-actions { display: flex; gap: .5rem; margin-top: .6rem; align-items: center; }
/* The trigger stays above the editor so clicking it again closes it. */
.scene-body-edit[open] > summary { position: relative; z-index: 201; border-color: var(--accent); }
.scene-script-head:has(details[open]) ~ .scene-paper { display: none; }

/* ---- The welcome, and the three doors ---------------------------------- */
/* The front door. One line of welcome, one invitation, three ways in. The
   doors are the only thing competing for attention here on purpose: the
   project wall moves below them, because "start something" is what an empty
   Indy is for and "carry on" is a second thought. */

/* Option A1 (Brett, 2026-09-14). Each door leads with its crease mark on a
   void plate, then its name and one line. The doors act in place (front-door.js):
   the idea door opens into a panel, a dragged script turns the page into the
   drop target, and a chosen script is read here. Those states are body
   classes, and each one REPLACES what it stands in for: nothing is ever laid
   over the posters (law 6). One blue per state: the drop frame, the caret, the
   dot on the scene being read. */

.welcome { text-align: center; padding: 1.5rem 0 2.2rem; }
.welcome-mark { display: block; width: 120px; height: 120px; margin: 0 auto .9rem; }
.welcome-hail {
  font-size: 3.25rem;
  font-weight: 300;            /* Light, not the house 600 (Brett, 2026-09-14) */
  margin: 0;
  color: var(--paper-ink);     /* the logo's paper, not #fff: sketch A (Brett, 2026-09-14) */
  text-shadow: none;           /* no prism fringe on the greeting (Brett, 2026-09-14) */
  letter-spacing: .01em;
  /* The house sets h1 in capitals. This one is a greeting written as a
     greeting, so it keeps sentence case. */
  text-transform: none;
}
.welcome-sub {
  margin: .5rem 0 0;
  font-size: 1.2rem;
  color: var(--paper-dim);
}

/* Box rule (vault/24 §12): the doors sit on the page's twelve columns, four
   each, full content width, so their outer edges are the poster wall's edges
   below. No lift on hover: a raised door leaves its neighbours' edges. The
   hover is the poster wall's (Brett, 2026-09-14): the picture zooms a little
   inside its box, the box stays put, and the words never change colour. */
.doors {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 24px;
  margin: 2.75rem 0 0;
  text-align: left;
}
.door {
  grid-column: span 4;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--box-line);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: border-color .12s ease;
}
.door:hover {
  border-color: var(--accent);
  color: inherit;
}
.door-plate {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 148px;
  background: var(--g0);
  border-bottom: 1px solid var(--box-line);
}
/* The plates are pictures, not black, and carry no mark (Brett, 2026-09-14):
   each tells its door's story in the Paper Dreams manner, paper lit with the
   soft blue and warm yellow and a touch of rainbow. Never the unicorn: the
   lit figure stays the house's. One per door, made for this page
   (vault/research/front-door-plates-2026-09-14), subject centred so any crop
   (the tall plate of the open idea panel, the small stacked doors) keeps it. */
.door-plate {
  position: relative;
  overflow: hidden;
  background-color: var(--g0);
}
/* The picture sits on its own layer so it can zoom without the plate's rule
   line zooming with it. Same scale and easing as .poster-plate img. */
.door-plate::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--plate-img, none);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  transition: transform .6s cubic-bezier(.2, .6, .2, 1);
}
.door:hover .door-plate::before { transform: scale(1.03); }
.door[data-door="idea"], .door-open { --plate-img: url("door-bg-idea.webp"); }
.door[data-door="script"] { --plate-img: url("door-bg-script.webp"); }
.door[data-door="world"] { --plate-img: url("door-bg-world.webp"); }
.door-text { display: flex; flex-direction: column; gap: .4rem; padding: 1.25rem 1.5rem 1.5rem; }
.door-name {
  font-family: var(--display);
  font-weight: 400;
  font-size: 1.56rem;
  line-height: 1.15;
  color: var(--paper-ink);
}
.door-note { font-size: .94rem; line-height: 1.5; color: var(--paper-dim); }

/* The idea door, opened in place: the panel takes eight columns, and the other
   two doors stack in the last four so a change of mind needs no back button.
   Two equal rows, so the stacked doors are one height and together share the
   panel's top and bottom edges. */
.doors.is-idea-open { grid-template-rows: repeat(2, minmax(0, 1fr)); }
.doors.is-idea-open .door[data-door="idea"] { display: none; }
.doors.is-idea-open .door-open { grid-column: 1 / span 8; grid-row: 1 / span 2; }
.doors.is-idea-open .door:not([data-door="idea"]) { grid-column: 9 / span 4; flex-direction: row; }
.doors.is-idea-open .door:not([data-door="idea"]) .door-plate {
  width: 7.75rem; height: auto; flex: 0 0 auto;
  border-bottom: 0; border-right: 1px solid var(--box-line);
}
.doors.is-idea-open .door:not([data-door="idea"]) .door-text { justify-content: center; padding: 1rem 1.1rem; }
.doors.is-idea-open .door:not([data-door="idea"]) .door-name { font-size: 1.25rem; }
.doors.is-idea-open .door:not([data-door="idea"]) .door-note { font-size: .84rem; }

.door-open {
  display: flex;
  min-height: 18rem;
  margin: 0;
  border: 1px solid var(--line-strong);   /* the open state keeps its stronger line */
  overflow: hidden;
}
.door-open[hidden] { display: none; }
.door-open .door-plate {
  width: 15.6rem; height: auto; flex: 0 0 auto;
  border-bottom: 0; border-right: 1px solid var(--box-line);
}
.door-open-body { flex: 1; min-width: 0; display: flex; flex-direction: column; padding: 1.5rem 1.75rem 1.5rem 2.25rem; }
.door-open-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.door-close { border-color: transparent; color: var(--ink-faint); }
.door-open .door-open-lede {
  margin-top: .4rem;
  font-family: var(--body);
  font-size: .94rem;
  font-weight: 400;            /* the label default is 600; the page's type is lighter */
  letter-spacing: 0;
  text-transform: none;        /* a sentence, not a form label */
  color: var(--paper-dim);
}
/* An ordinary field at reading size (Brett, 2026-09-14: the 2.6rem line was
   too big). */
.door-open input.door-open-input {
  margin-top: 1.5rem;
  width: 100%;
  height: 44px;
  padding: 0 .85rem;
  background: var(--g0);
  border: 1px solid var(--line-strong);
  font-family: var(--body);
  font-weight: 400;
  font-size: 1.125rem;
  line-height: 1;
  color: var(--paper-ink);
  caret-color: var(--chrome);
}
.door-open input.door-open-input::placeholder { color: var(--ink-ghost); font-weight: 400; }
.door-open input.door-open-input:focus { outline: none; border-color: var(--ink-faint); box-shadow: none; }
.door-open-foot { margin-top: auto; padding-top: 1.5rem; display: flex; align-items: center; gap: .9rem; }
.door-open-small { flex: 1; min-width: 0; font-size: .875rem; color: var(--paper-faint); }
.door-key {
  font-family: var(--mono);
  font-size: .75rem;
  color: var(--paper-faint);
  border: 1px solid var(--line-strong);
  padding: .1rem .5rem;
}
.door-go { width: 2.75rem; height: 2.75rem; display: inline-flex; align-items: center; justify-content: center; }

/* A script dragged over the page: the frame takes the doors' place and the
   wall steps aside. */
.door-drop {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.1rem;
  min-height: 30rem;
  margin: 2.75rem 0 0;          /* the doors' full width: it takes their place */
  background: var(--g0);
  border: 1.5px dashed var(--chrome);
  box-shadow: 0 0 22px -6px color-mix(in srgb, var(--chrome) 45%, transparent);
}
.door-drop img { display: block; width: 120px; height: 120px; }
.door-drop-title { font-family: var(--display); font-weight: 300; font-size: 2.6rem; color: var(--paper-ink); }
.door-drop-note { font-size: .94rem; color: var(--paper-dim); }
body.front-door-dropping .door-drop { display: flex; }
body.front-door-dropping .doors,
body.front-door-dropping .welcome-back,
body.front-door-dropping .poster-wall,
body.front-door-dropping .front-door-trash { display: none; }

/* A chosen script, read here: its first page as paper (the script page's own
   .screenplay sheet, scaled), the scene headings as they are found. */
.door-reading {
  display: none;
  grid-template-columns: 36.75rem minmax(0, 1fr);
  gap: 4rem;
  max-width: 70rem;
  margin: 0 auto;
  text-align: left;
  align-items: start;
}
body.front-door-reading .door-reading { display: grid; }
body.front-door-reading .welcome-mark,
body.front-door-reading .welcome-hail,
body.front-door-reading .welcome-sub,
body.front-door-reading .doors,
body.front-door-reading .door-drop,
body.front-door-reading .welcome-back,
body.front-door-reading .poster-wall,
body.front-door-reading .front-door-trash { display: none; }
.door-paper {
  width: 36.75rem;
  height: 47.5rem;
  overflow: hidden;
  background: #fff;
  border-radius: 2px;
  box-shadow: 0 2px 18px rgba(0,0,0,.35);
}
.door-paper .screenplay {
  max-width: none;
  margin: 0;
  box-shadow: none;
  transform: scale(.72);      /* 8.5in sheet → 36.75rem */
  transform-origin: top left;
}
.door-reading-title { margin: .5rem 0 0; font-size: 3.5rem; font-weight: 300; letter-spacing: .01em; text-transform: none; color: var(--paper-ink); }
.door-reading-label {
  display: block;
  margin-top: 1.9rem;
  font-family: var(--label);
  font-weight: 600;
  font-size: .72rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--paper-dim);
}
.door-reading-label[hidden] { display: none; }
.door-reading-scenes { list-style: none; margin: .9rem 0 0; padding: 0; }
.door-reading-scenes li {
  display: flex;
  align-items: center;
  gap: .75rem;
  height: 2.125rem;
  font-family: var(--mono);
  font-size: .875rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink);
}
.door-reading-scenes li::before {
  content: "";
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  margin: 0 2.5px;
  border-radius: 50%;
}
.door-reading-scenes li.is-current { color: #fff; }
.door-reading-scenes li.is-current::before {
  background: var(--chrome);
  box-shadow: 0 0 10px color-mix(in srgb, var(--chrome) 80%, transparent);
}
.door-reading-scenes .n { flex: 0 0 auto; width: 1.4rem; text-align: right; font-size: .78rem; color: var(--ink-faint); }
.door-reading-scenes .h { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.door-reading-bar { margin-top: 1.6rem; height: 1px; background: var(--line); }
.door-reading-bar span { display: block; width: 0; height: 1px; background: var(--ink); transition: width .2s ease; }
.door-reading-error { margin: 1.4rem 0 0; color: var(--warn); }
.door-reading-error[hidden], .door-retry[hidden] { display: none; }
.door-retry { margin-top: 1rem; }
/* Refused before any page was read (too big, not text): the side stands alone. */
.door-reading.is-refused { grid-template-columns: minmax(0, 1fr); max-width: 36rem; }
.door-reading.is-refused .door-paper { display: none; }
/* A folder being copied (front-door.js copyFolder): no paper, no scene list. */
.door-reading.is-folder { grid-template-columns: minmax(0, 1fr); max-width: 36rem; }
.door-reading.is-document { grid-template-columns: minmax(0, 1fr); max-width: 36rem; }
.door-reading.is-document .door-paper { display: none; }
.door-reading.is-folder .door-paper,
.door-reading.is-folder .door-reading-scenes { display: none; }

.welcome-back {
  margin: 3rem 0 0;
  font-size: .95rem;
  font-weight: normal;
  color: var(--paper-dim);
  text-transform: none;
  letter-spacing: normal;
}

/* The door pages themselves: one question each, centred, nothing else. */
.start-card { max-width: 34rem; margin: 3.2rem auto; }
.start-card.start-wide { max-width: 48rem; }
.front-door-trash.dim { color: var(--paper-dim); }
.start-back {
  display: inline-block;
  margin-bottom: 1.2rem;
  font-size: .85rem;
  color: var(--ink-dim, #9a9a9a);
  text-decoration: none;
}
.start-back:hover { color: var(--accent); }
.start-lede { color: var(--ink-dim, #9a9a9a); margin: .4rem 0 1.4rem; }
.start-small { font-size: .82rem; color: var(--ink-dim, #9a9a9a); margin-top: .7rem; }
/* ---- I have a world (vault/24 §7, artboard 6) ----
   The pick is one box with the world plate at its side; what the survey found
   is three boxes in a row, then the name and the build. The page changes
   state while a folder is dragged or copied; nothing is laid over it. */
.world-page { margin: 2.5rem 0 4rem; }
.world-page h1 { margin: 1.1rem 0 0; }
.world-lede { margin: .9rem 0 0; font-size: 1.06rem; max-width: 47.5rem; }
.world-pick {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  margin-top: 2rem;
  overflow: hidden;
}
.world-pick-plate {
  min-height: 9.5rem;
  background: var(--g0) url("door-bg-world.webp") center / cover no-repeat;
  border-right: 1px solid var(--box-line);
}
.world-pick-body { display: flex; flex-direction: column; gap: .9rem; min-width: 0; padding: 1.4rem 1.6rem; }
.world-pick-row { display: flex; align-items: center; flex-wrap: wrap; gap: .9rem; }
.world-pick .pill { align-self: flex-start; margin: 0; }
.world-path { display: flex; align-items: center; flex-wrap: wrap; gap: .75rem; margin: 0; }
.world-path .pill { align-self: center; }
.world-path input[type="text"] {
  flex: 1 1 18rem; min-width: 0; box-sizing: border-box;
  height: var(--control-h); padding: 0 .8rem;
  font-family: var(--mono); font-size: .8rem;
}
.world-pick-note { margin: 0; font-size: .82rem; color: var(--ink-faint); }
.world-progress[hidden] { display: none; }
.world-progress .door-reading-bar { margin-top: .8rem; }
body.world-copying .world-pick-row,
body.world-copying .world-path,
body.world-copying .world-pick-note,
body.world-copying .world-pick-body > .pill,
body.world-copying .world-result { display: none; }
.world-drop { margin-top: 2rem; }
body.world-dropping .door-drop { display: flex; }
body.world-dropping .world-pick,
body.world-dropping .world-result { display: none; }

.world-found-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 1rem; margin-top: 3.25rem; }
.world-found-head h2 { margin: 0; }
.world-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; align-items: start; margin-top: 1.5rem; }
.world-card { display: flex; flex-direction: column; gap: 1rem; min-width: 0; padding: 1.25rem 1.4rem 1.4rem; }
.world-card-head { display: flex; align-items: baseline; gap: .75rem; }
.world-count { font-family: var(--mono); font-size: .78rem; color: var(--ink-faint); }
.world-cand {
  display: grid; grid-template-columns: 1.1rem minmax(0, 1fr); gap: .75rem;
  align-items: start; padding: .75rem 0;
  border-top: 1px solid var(--box-line); cursor: pointer;
}
.world-cand:first-child { border-top: 0; padding-top: 0; }
.world-cand input { margin: .3rem 0 0; accent-color: var(--ink); }
/* These are <label>s, which the house sets as small capitals; a file name is
   read as written. */
.world-cand, .world-check, .world-name {
  font-family: var(--body); font-size: 1rem; font-weight: 400;
  letter-spacing: normal; text-transform: none; color: var(--ink);
}
.world-cand-main { display: flex; align-items: flex-start; gap: .9rem; min-width: 0; }
.world-cand-text { min-width: 0; }
.world-cand-name { display: block; color: var(--ink-dim); overflow-wrap: anywhere; }
.world-cand:has(input:checked) .world-cand-name { color: #fff; }
.world-cand-detail { display: block; margin-top: .15rem; font-family: var(--mono); font-size: .74rem; color: var(--ink-faint); }
/* The picked screenplay shows as a sheet of paper. */
.world-paper {
  display: none; flex: 0 0 auto; flex-direction: column; gap: 4px;
  width: 44px; height: 58px; padding: 10px 7px; box-sizing: border-box;
  background: #F4F1EA;
}
.world-cand:has(input:checked) .world-paper { display: flex; }
.world-paper i { display: block; height: 2px; background: #B9B3A6; }
.world-paper i:nth-child(1) { width: 60%; }
.world-paper i:nth-child(4) { width: 40%; }
.world-paper i:nth-child(6) { width: 70%; }
.world-thumbs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.world-thumb {
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 1 / 1; max-width: 100%; overflow: hidden;
  background: var(--g2); border: 1px solid var(--box-line);
  font-family: var(--mono); font-size: .78rem; color: var(--ink-faint);
}
.world-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.world-check { display: flex; align-items: center; gap: .6rem; cursor: pointer; }
.world-groups { display: flex; flex-direction: column; gap: .45rem; }
.world-group { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.world-card-note { margin: 0; font-size: .8rem; color: var(--ink-faint); }
.world-models { margin-top: 24px; }
.world-left { margin: 1.25rem 0 0; font-family: var(--mono); font-size: .78rem; color: var(--ink-faint); }
.world-empty { margin-top: 2rem; }
.world-build { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 1.25rem; margin-top: 2.75rem; }
.world-name { display: flex; flex-direction: column; gap: .5rem; width: 26rem; max-width: 100%; }
.world-name input { box-sizing: border-box; height: 44px; padding: 0 .9rem; font-size: 1.06rem; }
.world-build .btn { height: 44px; }
.world-build > .dim { padding-bottom: .7rem; }
@media (max-width: 760px) {
  .world-pick { grid-template-columns: minmax(0, 1fr); }
  .world-pick-plate { min-height: 6rem; border-right: 0; border-bottom: 1px solid var(--box-line); }
  .world-cards { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 780px) {
  /* One column. Every door, the panel and each poster span it, or a span-4
     door would invent three implicit columns and push the page sideways. */
  .doors, .doors.is-idea-open { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
  .door, .poster-card { grid-column: 1 / -1; }
  .poster-wall { grid-template-columns: minmax(0, 1fr); }
  .doors.is-idea-open .door-open { grid-column: 1 / -1; grid-row: auto; order: -1; }  /* where the idea door was */
  .doors.is-idea-open .door:not([data-door="idea"]) { grid-column: 1 / -1; }
  .door-open { flex-direction: column; }
  .door-open .door-plate { width: auto; height: 6rem; border-right: 0; border-bottom: 1px solid var(--box-line); }
  .welcome-mark { width: 88px; height: 88px; }
  .welcome-hail { font-size: 2.2rem; }
  .door-reading { grid-template-columns: 1fr; }
  .door-paper { display: none; }
}

/* ---- "nothing of yours in here yet" ------------------------------------ */
/* Shown on the overview of a project with no user-supplied reference, and
   only until the first one lands. Sits under the pipeline rail so the rooms
   still read first: this is an invitation, not an alert. */
.ref-invite {
  margin: 1.6rem 0 2.6rem;   /* the rail labels' own distance (vault/47 A11) */
  padding: 1rem 1.15rem;
  background: var(--g2);
  border: 1px solid var(--g4);
  border-left: 2px solid var(--accent);
  border-radius: 4px;
  display: flex;
  flex-direction: column;
  gap: .8rem;
  max-width: 62rem;
}
.ref-invite b { color: #fff; font-weight: normal; }
.ref-invite-ways {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .35rem 1rem;
  align-items: baseline;
  font-size: .9rem;
}
.ref-invite-ways a {
  display: inline-flex; align-items: center; gap: .4rem;
  text-decoration: none; color: var(--accent); white-space: nowrap;
}
.ref-invite-ways a:hover { text-decoration: underline; }
.ref-invite-ways svg { width: 15px; height: 15px; }
@media (max-width: 700px) {
  .ref-invite-ways { grid-template-columns: 1fr; gap: .2rem; }
}

/* ---- Rooms, round 2 (2026-09-14, vault/24 §12) ----
   The character room and the brief are declared rows on the 12-column grid.
   A tile is a flat picture well with its caption box underneath (law 6:
   nothing on the picture); captions are one fixed height so a row of tiles
   ends level. */
.span-3 { grid-column: span 3; } .span-4 { grid-column: span 4; }
.span-5 { grid-column: span 5; } .span-7 { grid-column: span 7; }
.span-8 { grid-column: span 8; } .span-12 { grid-column: span 12; }

.room-back { margin: 0 0 .5rem; }
.room-back a {
  font-family: var(--body); font-weight: 500; font-size: .75rem;
  letter-spacing: .12em; text-transform: uppercase;
}
.room-head { display: flex; flex-direction: row; align-items: center; flex-wrap: wrap; gap: .4rem 1rem; }
.room-head h1 { margin: 0; font-size: 1.7rem; line-height: 1.1; }
.room-head > .lbl { order: -1; }
.room-head .room-head-row { flex: 1 1 24rem; margin-top: 0; }
.room-head-row { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem .75rem; min-height: var(--control-h); margin-top: .4rem; }
.room-actions { margin-left: auto; display: flex; gap: .5rem; align-items: center; }
.room-actions > form { margin: 0; display: inline-flex; }
.room .pill, .room-head .pill { display: inline-flex; align-items: center; gap: .35rem; height: 24px; padding: 0 .6rem; margin: 0; }
.pill .icon { width: .7rem; height: .7rem; }
.state-pill.state-approved { color: var(--ok); border-color: #3F6E59; }
.state-pill.state-benchmark { color: var(--bench); border-color: #6B5A33; }
.state-pill.state-rejected { color: var(--ink-faint); }
.btn-sm { height: 32px; padding: 0 .8rem; }
.btn .icon { width: .85rem; height: .85rem; }

.room-desc { margin-top: 1.5rem; padding: 1.1rem 1.5rem 1rem; }
.room-kv { display: grid; grid-template-columns: 200px minmax(0, 1fr); column-gap: 24px; row-gap: .5rem; align-items: baseline; margin: 0; }
.room-kv dt { font-family: var(--label); font-weight: 600; font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-faint); }
.room-kv dd {
  margin: 0; font-size: .95rem; line-height: 1.5; color: var(--ink);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.room-fold { margin-top: .85rem; border-top: 1px solid #2C2C2C; padding-top: .6rem; }
.room-fold > summary, .more-fold > summary, .sec-fold > summary, .room-fold-row > summary {
  list-style: none; cursor: pointer;
}
.room-fold > summary::-webkit-details-marker, .more-fold > summary::-webkit-details-marker,
.sec-fold > summary::-webkit-details-marker, .room-fold-row > summary::-webkit-details-marker { display: none; }
.room-fold > summary { display: flex; align-items: center; gap: .6rem; min-height: 32px; }
.room-fold > summary .room-fold-edit { margin-left: auto; }
.room-fold-body { margin-top: 1.1rem; align-items: start; row-gap: 1rem; }
.room-fold-body .brief-guide-body {
  margin: .55rem 0 0; padding: .8rem 1rem; background: var(--g0); border: 1px solid var(--box-line);
  font-family: var(--body); font-size: .85rem; line-height: 1.5; white-space: pre-wrap; color: var(--ink-dim);
}
.chev { display: inline-flex; color: var(--ink-faint); }
.chev .icon { width: .8rem; height: .8rem; transition: transform .15s ease; }
details[open] > summary .chev .icon { transform: rotate(90deg); }

.room-sec { margin-top: 3rem; }
.sech { display: flex; align-items: center; gap: .85rem; min-height: 44px; border-bottom: 1px solid #2A2A2A; margin: 0 0 1.25rem; }
.sech h3 { margin: 0; }
.sech .cnt { font-family: var(--label); font-weight: 600; font-size: .75rem; letter-spacing: .12em; color: var(--ink-faint); }
.sech .meta { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.sech-right { margin-left: auto; display: flex; gap: .5rem; align-items: center; flex: 0 0 auto; }

.tile { display: flex; flex-direction: column; min-width: 0; }
.tile-grid { row-gap: 24px; }
.well {
  background: var(--g0); border: 1px solid var(--box-line); border-bottom: 0;
  overflow: hidden; display: flex; align-items: center; justify-content: center;
}
a.well, a.well:hover { border-bottom: 0; }
.well > a { display: block; width: 100%; height: 100%; border-bottom: 0; }
.well img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* Concept art fills its well (Brett, 2026-09-14); the whole picture is one
   click away. Design wells and references stay contained. */
.well-16x9 { aspect-ratio: 16 / 9; }
.well-model { height: 390px; display: block; }
.well-empty { margin: 0; padding: 1.5rem; max-width: 26rem; text-align: center; color: var(--ink-faint); font-size: .9rem; }
.cap { display: flex; align-items: center; gap: .6rem; height: 52px; padding: 0 .75rem 0 1rem; min-width: 0; }
.cap-name {
  font-family: var(--display); font-weight: 600; font-size: .8rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--display-ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.cap .pill { flex: 0 0 auto; }
.cap .mono { font-size: .72rem; color: var(--ink-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.cap-hint { white-space: nowrap; }
.cap-actions { margin-left: auto; display: flex; gap: .4rem; align-items: center; flex: 0 0 auto; }
.cap-actions > form { margin: 0; display: inline-flex; }
.cap-ghost { margin-left: auto; display: flex; gap: .25rem; flex: 0 0 auto; opacity: .45; transition: opacity .15s ease; }
.cap-ghost form { margin: 0; display: inline-flex; }
.tile:hover .cap-ghost, .cap-ghost:focus-within { opacity: 1; }
.cap-ghost .btn-icon { height: 30px; min-width: 30px; padding: 0 .35rem; }
.cap .geo-variants { padding: 0; flex-wrap: nowrap; }
#sheet-status:not(:empty) { margin-top: 1rem; }

.more-fold > summary { display: flex; align-items: center; gap: .6rem; margin-top: 1rem; min-height: 32px; }
.more-fold > .tile-grid { margin-top: 1rem; }
.sec-fold > summary.sech { cursor: pointer; }
.sec-tail { margin: 1rem 0 0; }

.gen-field { display: flex; flex-direction: column; gap: .5rem; min-width: 0; }
.gen-empty { line-height: var(--control-h); }
.seg { display: inline-flex; border: 1px solid var(--btn-line); height: var(--control-h); max-width: 100%; overflow-x: auto; scrollbar-width: none; }
.seg label { position: relative; display: inline-flex; align-items: stretch; margin: 0; cursor: pointer; }
.seg input { position: absolute; opacity: 0; pointer-events: none; }
.seg label > span {
  display: inline-flex; align-items: center; padding: 0 .85rem;
  font-family: var(--label); font-weight: 600; font-size: .72rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-dim); border-right: 1px solid var(--btn-line); white-space: nowrap;
}
.seg label:last-child > span { border-right: 0; }
.seg input:checked + span { color: var(--display-ink); background: #2E2E2E; }
.seg input:focus-visible + span { box-shadow: inset 0 0 0 1px var(--chrome); }
/* The one picture-model picker (partials/model_picker.html). It stands in a
   narrow door as well as in the wide generate box, so the row scrolls rather
   than squeezing a name out of shape, and at tablet width it grows to the
   44pt a finger needs. Scoped to this picker: every other segmented row in
   the app is Brett's to size. */
.gen-field--pick { flex: 0 0 auto; min-width: 0; }
/* Now that it WRAPS rather than clipping, it has to be allowed to shrink:
   held at its own width it drew six names 88px past the right edge of the
   generate box at 1440 and 233px past it at 1024. Measured, both. The rule
   above it, which pins the first field so the tool choice never clips, was
   written when clipping was what happened instead. */
/* Hug the names. In a door the picker is a flex ITEM of a column, which
   stretches it, and a segmented control stretched past its last label draws
   an empty box on the end of itself. */
.gen-field--pick .model-pick { align-self: flex-start; }
/* Wrap rather than scroll. The picker stands in a 380px door as well as in
   the wide box, and six models in one scrolling row clip the last name with
   nothing on screen to say it scrolled. Nothing here sits beside a picture,
   so wrapping steals no height from one.

   It wraps as SEPARATE names, not as one bordered strip. A strip that wraps
   draws the leftover space at the end of every short line as an empty
   bordered cell, and the divider on that line's last name has nothing after
   it to divide: at 1440 in the "write a few lines" door, six models came out
   as three ragged lines with a blank stub after GPT IMAGE and another after
   RUNWAY SEEDREAM 5. Names that carry their own box have no line to be
   ragged, at any width either surface can be. */
.model-pick {
  flex-wrap: wrap; overflow-x: visible; height: auto; max-width: 100%;
  border: 0; gap: 6px;
}
.model-pick label > span {
  flex-direction: column; align-items: flex-start; justify-content: center;
  gap: .1rem; min-height: var(--control-h); padding: .3rem .7rem;
  border: 1px solid var(--btn-line);
}
/* The strip's own no-divider-on-the-last-one rule would leave one name with
   three sides. */
.model-pick label:last-child > span { border-right: 1px solid var(--btn-line); }
.model-pick input:checked + span { border-color: var(--display-ink); }
/* What it costs, on the name rather than only in a tooltip: a finger has no
   hover, and this picker is read on an iPad. */
.pick-price {
  font-family: var(--body); font-style: normal; font-weight: 400;
  font-size: .72rem; letter-spacing: 0; text-transform: none;
  color: var(--ink-faint);
}
.model-pick input:checked + span .pick-price { color: var(--ink-dim); }
@media (max-width: 1024px) { .model-pick label > span { min-height: 44px; } }


/* ==========================================================================
   The character room: ONE SURFACE (vault/32, 2026-09-20)
   A rail, a stage, a tray. Everything below is scoped to this room -- in
   particular the 44px touch floor at iPad width, which is NOT the app-wide
   --control-h token (that is a separate decision and Brett's to make).
   ========================================================================== */

/* ---- What the boards are waiting on: ONE sentence under the header, drawn
   only while a character or a vehicle is actually holding the boards up, and
   gone the moment it is not. It replaced the five-box rail. ---- */
.room-needs {
  margin: 1rem 0 0; padding: .6rem .9rem; max-width: 72ch;
  border-left: 2px solid var(--warn); background: var(--g2);
  color: var(--ink-dim); font-size: .92rem; line-height: 1.45;
}
.room-needs b { color: var(--ink); font-weight: 500; }
.btn-lock.is-on { color: var(--ok); border-color: #3C6455; }

/* A set-once choice from a short closed list is a dropdown, one control wide
   (cast tier here; the same rule as the bench's levers). */
.dd-select {
  width: auto; height: var(--control-h); padding: 0 1.9rem 0 .75rem;
  font-family: var(--label); font-weight: 600; font-size: .72rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink);
  background-color: var(--g3); border: 1px solid var(--line-strong); border-radius: 4px;
  appearance: none; -webkit-appearance: none; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-faint) 50%),
                    linear-gradient(135deg, var(--ink-faint) 50%, transparent 50%);
  background-position: calc(100% - 14px) 55%, calc(100% - 9px) 55%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
}
.dd-select:hover { border-color: var(--ink-dim); }
.room-tier { margin: 0; display: inline-flex; }

/* ---- The rail: where am I. Type only, no thumbnails, and it stays put ---- */
.rail-wrap {
  position: sticky; top: 73px; z-index: 4;
  margin: 1.5rem 0 0; background: var(--g0); padding-bottom: 1px;
}
.rail {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  list-style: none; margin: 0; padding: 0;
  border: 1px solid var(--box-line); background: var(--g2);
}
/* A place or a prop never gates the boards, so it stops after the look and
   its three steps take the whole width. */
.rail.rail-short { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.rail li { border-right: 1px solid var(--box-line); min-width: 0; position: relative; }
.rail li:last-child { border-right: 0; }
.rail li > a, .rail li > .rail-flat {
  display: block; padding: .6rem .9rem; min-height: 46px; color: inherit;
  border-bottom: 0; text-decoration: none;
}
.rail li > a:hover { background: var(--g3); border-bottom: 0; }
.rail li > a:focus-visible { outline: 1px solid var(--chrome); outline-offset: -2px; }
.rail li b {
  display: block; font-family: var(--display); font-weight: 600; font-size: .76rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-ghost);
}
.rail li span {
  display: block; font-size: .78rem; margin-top: .1rem; color: var(--ink-ghost);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail li.done b { color: var(--ink); }
.rail li.done span { color: var(--ink-faint); }
.rail li.now { background: var(--g4); }
.rail li.now b { color: var(--display-ink); }
.rail li.now span { color: var(--ink-dim); }
.rail li.now b, .rail li.now span { padding-left: .7rem; }
.rail .tick { color: var(--ok); margin-left: .4rem; display: inline-flex; }
.rail .tick .icon { width: .7rem; height: .7rem; }
/* The ONE you-are-here dot. Besides the small section labels and the active
   nav item, the only blue on this page (feedback_white_headings_blue_spare). */
.rail .dot {
  position: absolute; left: .6rem; top: 1rem;
  width: 6px; height: 6px; border-radius: 50%; background: var(--chrome);
}
.rail-said {
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
  margin: .7rem 0 1.5rem; color: var(--ink-faint); font-size: .88rem;
}
.rail-said form { margin: 0; display: inline-flex; }
.rail-roll { margin-left: auto; color: var(--ink-faint); font-size: .82rem; }

/* ---- The stage, and the tools beside it ---- */
/* THREE PANES, the timeline's (2026-09-21): the bin, the viewer, the
   inspector. Pictures on the left, the one being judged in the middle,
   everything you can do to it on the right. The frame fills what is left of
   the window under the header (--panes-top is measured by the page), and each
   pane scrolls on its own, so judging a picture never scrolls the page. It
   bleeds past the 1280px column the rest of the page keeps, because a viewer
   wants the width. */
.work {
  --bin-w: 264px; --insp-w: 348px;
  display: grid; grid-template-columns: var(--bin-w) minmax(0, 1fr) var(--insp-w);
  gap: 0; align-items: stretch;
  width: calc(100vw - 4rem); margin: 1rem 0 1.5rem calc(50% - 50vw + 2rem);
  height: calc(100vh - var(--panes-top, 230px) - 1rem); min-height: 34rem;
  border: 1px solid var(--box-line); background: var(--g1);
}
.pane { min-width: 0; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.pane + .pane { border-left: 1px solid var(--box-line); }
.pane-h {
  position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: .5rem;
  height: 36px; margin: 0; padding: 0 .5rem 0 .8rem;
  background: var(--g2); border-bottom: 1px solid var(--box-line);
}
.pane-h .grow { flex: 1 1 auto; }
.pane-h .btn-icon { height: 26px; min-width: 26px; border-color: transparent; color: var(--ink-faint); }
.pane-body { padding: .8rem; }

/* The viewer: the picture takes what the pane has, its name and its verdicts
   stay pinned under it. */
.stage { display: flex; flex-direction: column; min-width: 0; scroll-margin-top: 9rem; padding: .8rem; }
.stage.pane { overflow: hidden; }
.stage .well-focus {
  flex: 1 1 auto; height: auto; min-height: 0; max-height: none; background: var(--g0);
  display: grid; place-items: center; overflow: hidden; padding: 8px;
}
.stage .well-focus a { display: grid; place-items: center; width: 100%; height: 100%; min-height: 0; border-bottom: 0; }
.stage .ident, .stage .vstrip, .stage .vnext, .stage .vhint, .stage .iter-results { flex: 0 0 auto; }
.stage .well-focus img { max-height: 100%; width: auto; max-width: 100%; object-fit: contain; }
/* Identity first, then controls (law 10), and never over the picture. */
.ident { display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap; margin: .7rem 0 0; }
.ident b {
  font-family: var(--display); font-weight: 600; font-size: .8rem;
  letter-spacing: .11em; text-transform: uppercase; color: var(--display-ink);
}
.ident span { color: var(--ink-faint); font-size: .85rem; }
.ident.sm b { font-size: .7rem; text-transform: none; letter-spacing: .04em; }
/* Keep and not this first, both always drawn and both always showing their
   own state; the one step press in the middle; the bin last. */
/* ---- The verdict strip (partials/verdict_strip.html): the app's one way to
   judge a picture. The check and the cross ARE the board's and the bench's
   (.btn-approve / .btn-reject); only the Master tick and the more menu are
   defined here. ---- */
.vstrip { display: flex; align-items: center; gap: .4rem; margin-top: .75rem; }
.vstrip form { margin: 0; display: inline-flex; }
.vstrip-sep { width: 1px; height: 18px; background: var(--line-strong); margin: 0 .3rem; }
.vstrip-grow { flex: 1 1 auto; }
.vstrip button:disabled, .vstrip .btn-icon:disabled { opacity: .35; cursor: not-allowed; box-shadow: none; }
/* A taken-back verdict is one press away, so a pressed check or cross stays a
   live button here (the board's locked cursor is for its one-way approve). */
.vstrip .btn-approve.is-approved, .vstrip .btn-reject.is-rejected { cursor: pointer; }
/* The Master tick: a labelled checkbox. Gold, because gold already means the
   picture others are measured against (--bench). */
.master-tick {
  display: inline-flex; align-items: center; gap: .5rem;
  height: 32px; padding: 0 .7rem 0 .5rem;
}
.master-tick:hover { color: var(--bench); border-color: var(--bench); background: transparent; }
.master-box {
  display: grid; place-items: center; width: 15px; height: 15px;
  border: 1px solid var(--ink-faint); border-radius: 3px; background: var(--g0);
}
.master-box .icon { width: 11px; height: 11px; }
.master-tick.is-on { color: var(--bench); border-color: #6E5A2C; background: #1F1A0E; }
.master-tick.is-on .master-box { background: var(--bench); border-color: var(--bench); color: #111; }
/* Rare and destructive things, destructive last. */
.more-menu { position: relative; }
.more-menu > summary { list-style: none; cursor: pointer; border: 1px solid transparent; color: var(--ink-faint); }
.more-menu > summary::-webkit-details-marker { display: none; }
.more-menu[open] > summary { color: var(--ink); border-color: var(--line-strong); }
.more-menu-list {
  position: absolute; right: 0; bottom: calc(100% + 6px); z-index: 6; min-width: 200px;
  padding: 4px; background: var(--g3); border: 1px solid var(--line-strong);
  border-radius: 6px; box-shadow: 0 12px 32px rgba(0, 0, 0, .55);
}
.more-menu-list form { display: block; margin: 0; }
.more-menu-list a, .more-menu-list button, .more-menu-off {
  display: block; width: 100%; height: auto; padding: .45rem .6rem; border: 0; border-radius: 3px;
  background: none; text-align: left; text-transform: none; letter-spacing: 0;
  font: 400 .88rem/1.3 var(--body); color: var(--ink); box-shadow: none;
}
.more-menu-list a:hover, .more-menu-list button:hover { background: var(--g4); border: 0; }
.more-menu-list .more-menu-danger { color: #E0766B; border-top: 1px solid var(--line); border-radius: 0 0 3px 3px; margin-top: 4px; padding-top: .55rem; }
.more-menu-off { color: var(--ink-ghost); border-top: 1px solid var(--line); margin-top: 4px; }
.vnext { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin: .75rem 0 0; }
.vnext .vhint { margin: 0; }
.btn-wide { width: 100%; }
.vhint { margin: .6rem 0 0; color: var(--ink-faint); font-size: .85rem; }

/* The inspector: flat sections one under another, as the timeline's. */
.tools { display: flex; flex-direction: column; gap: 0; min-width: 0; }
.tools.pane > .tool {
  background: none; border: 0; border-bottom: 1px solid var(--box-line);
  padding: .9rem .9rem 1rem;
}
/* What is MADE from the master look: a slot each for the turnaround and the
   3D model. A state word, the thing itself, and one press. */
.made-slot { display: flex; flex-direction: column; gap: .5rem; }
.made-slot + .made-slot { margin-top: .4rem; padding-top: .8rem; border-top: 1px solid var(--box-line); }
.made-h { display: flex; align-items: baseline; gap: .6rem; margin: 0; }
.made-h b {
  font-family: var(--label); font-weight: 600; font-size: .74rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink);
}
.made-state { font-size: .8rem; color: var(--ink-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.made-state.is-ok { color: var(--ok); }
.made-state.is-stale { color: var(--warn); }
.made-pic { display: block; border: 1px solid var(--box-line); background: var(--g0); }
.made-pic:hover { border-color: var(--chrome); border-bottom: 1px solid var(--chrome); }
.made-pic img { display: block; width: 100%; height: 150px; object-fit: contain; }
.made-slot form { margin: 0; }
.made-slot .iter-results:empty { display: none; }
.insp-brief-say {
  margin: 0; color: var(--ink-dim); font-size: .88rem; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}
.insp-brief .btn { align-self: flex-start; }
.insp-from:empty { display: none; }
.insp-from { margin-top: .2rem; }
.insp-from .gen-note { margin: 0; }
.tool {
  display: flex; flex-direction: column; gap: .6rem; padding: .85rem .9rem;
  background: var(--box-bg); border: 1px solid var(--box-line); margin: 0;
}
.tool .hint { margin: 0; color: var(--ink-faint); font-size: .8rem; }
.tool .gwell { height: 198px; background: var(--g0); border: 1px solid var(--box-line); }
.tool .chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip-sm {
  height: auto; min-height: 28px; padding: 0 .55rem; font-size: .68rem;
  color: var(--ink-dim);
}
.chip-sm.is-current { color: var(--display-ink); background: var(--g4); }

/* The folded picker: the name you are drawing with, and a press to change
   it. Same radios underneath, so what is chosen and what a confirm says
   cannot drift between the two shapes. */
.pick-fold > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center;
  gap: .5rem; min-height: 34px;
}
.pick-fold > summary::-webkit-details-marker { display: none; }
/* The chosen name and its chevron are ONE control: the chevron drawn loose
   beside the box read as a stray glyph. It rides on ::after because the
   picker script rewrites the name's text on every choice. */
.pick-now {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--display); font-weight: 600; font-size: .72rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink);
  border: 1px solid var(--btn-line); background: var(--btn-bg); padding: .3rem .55rem;
}
.pick-now::after { content: '\25BE'; color: var(--ink-faint); font-size: .7rem; }
.pick-fold[open] .pick-now { border-color: var(--display-ink); }
.chev2 { display: none; }
.gen-field--fold { gap: .4rem; }
/* Open, it is a menu: one box, rows the full width of it, a hairline between
   them, and the chosen row filled. Each name carrying its own box (the
   wrapping strip's rule, above) drew ragged boxes of different widths, the
   chosen one narrower and outlined on its own. */
.gen-field--fold .model-pick {
  flex-direction: column; flex-wrap: nowrap; gap: 0; height: auto;
  margin-top: .5rem; overflow: visible; min-width: 190px;
  border: 1px solid var(--line-strong); background: var(--g3);
}
.gen-field--fold .model-pick label { display: flex; width: 100%; }
.gen-field--fold .model-pick label > span {
  flex: 1; border: 0; border-bottom: 1px solid var(--btn-line);
  padding: .5rem .75rem;
}
.gen-field--fold .model-pick label:last-child > span { border: 0; }
.gen-field--fold .model-pick label:hover > span { background: var(--g4); color: var(--ink); }
.gen-field--fold .model-pick input:checked + span {
  background: #2E2E2E; color: var(--display-ink);
  box-shadow: inset 2px 0 0 var(--display-ink);
}
/* The board toolbar's fold (what draws the bulk run) opens OVER the strip,
   the way the bench's upload row does: opened inside the toolbar it grew the
   row, and the row stretches every control in it to the new height (the
   running-time pill drew as a tall capsule). */
#bulk-pick { position: relative; align-self: center; }
#bulk-pick .pick-fold[open] .model-pick {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 5; margin-top: 0;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .45);
}

/* How we got here, in WORDS. Thumbnails here would put the same pictures on
   the page a second time, which is the thing this page exists to fix. */
.trail { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; margin: 0; padding: 0; }
.tstep {
  display: inline-flex; align-items: center; min-height: 30px; padding: 0 .6rem;
  border: 1px solid var(--btn-line); background: var(--btn-bg); color: var(--ink-dim);
  font-family: var(--display); font-weight: 600; font-size: .68rem;
  letter-spacing: .08em; text-transform: uppercase; white-space: nowrap;
}
a.tstep { text-decoration: none; }
a.tstep:hover { color: var(--display-ink); border-bottom: 1px solid var(--btn-line); }
.tstep.is-current { color: var(--display-ink); background: var(--g4); }
.tstep.is-new { border-style: dashed; background: none; color: var(--ink-faint); }
.tsay { font-size: .78rem; color: var(--ink-faint); font-style: italic; }
.tsay::before { content: "\201C"; }
.tsay::after { content: "\201D"; }

/* ---- The tray: every picture of this character, exactly once ---- */
.tray { --tray-th: 134px; border-top: 1px solid var(--box-line); padding-top: 1rem; margin: 0 0 1.5rem; }
/* The tray as the bin: two columns of thumbnails, the state of each said on
   its corner (the board's marks: gold star for the master, green check for
   approved), and the one on the stage scrolled into view. */
.tray.pane-bin { --tray-th: 112px; border-top: 0; padding-top: 0; margin: 0; }
.tray.pane-bin .tlab { margin: 0; flex-wrap: nowrap; }
.tray.pane-bin .grid { grid-template-columns: repeat(2, var(--tray-th)); gap: 10px; }
.tray.pane-bin .tgroup { margin-bottom: 1.1rem; }
.tray.pane-bin .th figcaption { font-size: .72rem; }
.tray.pane-bin .th.is-current figcaption::after { content: none; }
.th-pic { position: relative; }
/* A generation in the bin is a TILE: the shape of the picture it will become,
   in the cell where it will land. Running pulses; landed shows the picture
   with "new" on it; a failure takes the bin's full width so it can be read. */
.tray .iter-results { width: var(--tray-th); min-width: 0; }
.tray .iter-results:empty, .tray .iter-results:not(:has(.gen-card:not(.htmx-indicator), .htmx-request)) { display: none; }
.tray .iter-results:has(.htmx-request) { display: block; }
.tray .iter-results:has(.gen-card-fail) { grid-column: 1 / -1; width: auto; }
.tgroup-landing:not(:has(.gen-card:not(.htmx-indicator), .htmx-request)) { display: none; }
.gen-tile {
  position: relative; display: grid; place-items: center; align-content: center; gap: .4rem;
  width: var(--tray-th, 112px); height: var(--tray-th, 112px); margin: 0; padding: 0;
  border: 1px dashed var(--line-strong); background: var(--g2); color: var(--ink-dim);
  text-decoration: none; overflow: hidden;
}
.gen-tile.htmx-indicator { display: none; }
.gen-tile.htmx-indicator.htmx-request, .htmx-request .gen-tile.htmx-indicator { display: grid; }
.gen-tile-say {
  font-family: var(--label); font-weight: 600; font-size: .62rem; letter-spacing: .1em;
  text-transform: uppercase; text-align: center;
}
a.gen-tile { border-style: solid; border-color: var(--chrome); border-bottom: 1px solid var(--chrome); }
a.gen-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
a.gen-tile .gen-tile-say.is-new {
  position: absolute; left: 4px; top: 4px; padding: 3px 5px; border-radius: 3px;
  background: var(--chrome); color: #0E0E0E;
}
/* The viewer, while the next picture is made from the one it is showing. */
.stage .well-focus { position: relative; }
.stage-making {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; flex: 0 0 auto;
  margin: .6rem 0 0; padding: .55rem .8rem; background: var(--g2);
  border-left: 2px solid var(--chrome); font-size: .88rem; color: var(--ink-dim);
}
.stage-making b { color: var(--display-ink); font-weight: 500; }
.stage-making[hidden] { display: none; }
/* "Hold on to" (draw the next one from this too) is a second-order control:
   in the bin it shows on the thumbnail you are pointing at, and stays shown
   on the ones you have ticked. */
@media (min-width: 1101px) and (hover: hover) {
  .tray.pane-bin .th-acts { opacity: 0; transition: opacity .12s; }
  .tray.pane-bin .th:hover .th-acts, .tray.pane-bin .th:focus-within .th-acts,
  .tray.pane-bin .th.is-held .th-acts { opacity: 1; }
}
.th-badge {
  position: absolute; top: 4px; display: grid; place-items: center;
  width: 18px; height: 18px; border-radius: 3px; background: rgba(14, 14, 14, .82);
}
.th-badge .icon { width: 11px; height: 11px; }
.th-badge.is-master { left: 4px; color: var(--bench); }
.th-badge.is-approved { right: 4px; color: var(--ok); }
.tlab { display: flex; align-items: center; gap: .6rem; margin: 0 0 1rem; flex-wrap: wrap; }
.tlab .grow { flex: 1 1 auto; }
.tgroup { margin: 0 0 1.4rem; scroll-margin-top: 9rem; }
/* The empty landing pad: present for the card to arrive in, but it
   is not a row until a row is there. */
.tgroup-landing { margin: 0; }
.tgroup-landing .grid:empty { display: none; }
.glab { display: flex; align-items: center; gap: .6rem; margin: 0 0 .6rem; flex-wrap: wrap; }
.glab-note { font-size: .8rem; color: var(--ink-ghost); }
.tray .grid { display: grid; grid-template-columns: repeat(auto-fill, var(--tray-th)); gap: 14px; }
.th { margin: 0; min-width: 0; }
.th-pic {
  display: block; width: var(--tray-th); height: var(--tray-th);
  background: var(--g0); border: 1px solid var(--box-line); overflow: hidden;
}
.th-pic:hover { border-color: var(--chrome); border-bottom: 1px solid var(--chrome); }
.th-pic img { width: 100%; height: 100%; object-fit: cover; display: block; opacity: .8; }
.th-pic:hover img, .th.is-current .th-pic img { opacity: 1; }
/* The one permitted repeat on the page: the stage's picture, marked in the
   tray the way a viewer marks the strip's current frame. Outside the
   picture, never over it. */
.th.is-current .th-pic { outline: 2px solid var(--ink); outline-offset: 3px; }
.th.is-current figcaption::after {
  content: " \00b7 on the stage"; color: var(--ink-faint); font-weight: 400;
  letter-spacing: 0; text-transform: none;
}
.th figcaption {
  margin-top: .35rem; font-size: .78rem; color: var(--ink-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: var(--tray-th);
}
.th-chosen figcaption {
  color: var(--display-ink); font-family: var(--display); font-weight: 600;
  font-size: .72rem; letter-spacing: .09em; text-transform: uppercase;
}
.th-name { margin: 0; }
.th-name input {
  width: var(--tray-th); height: 26px; padding: 0 .35rem; font-size: .78rem;
  background: var(--g0); border: 1px solid var(--box-line); color: var(--ink);
}
.th-acts { display: flex; align-items: center; gap: .35rem; margin-top: .15rem; min-height: 24px; }
.th-hold {
  display: inline-flex; align-items: center; gap: .3rem; cursor: pointer; margin: 0;
  font-size: .68rem; color: var(--ink-ghost); opacity: .5; transition: opacity .12s ease;
}
.th:hover .th-hold, .th-hold:focus-within, .th.is-held .th-hold { opacity: 1; }
.th.is-held .th-hold { color: var(--ok); }
.th-making {
  /* Not a grid cell of its own: the swap target is always in the markup so
     htmx has somewhere to put the card, and an empty one would draw a hole
     in the tray. When a card lands, the card itself becomes the cell. */
  display: contents;
}
.th-making .gen-card {
  width: var(--tray-th); min-height: var(--tray-th); margin: 0;
  border: 1px dashed var(--btn-line); place-items: center; text-align: center;
}
/* The waiting card is `display: none` until a request is in flight
   (.gen-card-pending, above). Setting `display` on every card here outranked
   that, so an invisible card held the first cell of "drawn here" and every
   take sat one column in. Only a card that is really there is a cell. */
.th-making .gen-card:not(.gen-card-pending),
.th-making .gen-card-pending.htmx-request { display: grid; }
.th-making .gen-card-body { display: flex; flex-direction: column; gap: .3rem; padding: .4rem; }
.th-making .gen-card-status { font-size: .66rem; }
.tray-fold > summary { min-height: 44px; display: flex; align-items: center; gap: .6rem; cursor: pointer; }
.tray-fold > summary::-webkit-details-marker { display: none; }
.tray-fold > .grid { margin-top: .6rem; }
.tray .more { margin: .6rem 0 0; min-height: 44px; display: flex; align-items: center; gap: .5rem; }
/* The chevron sits BESIDE the words: .icon is a block, so an inline link
   holding one puts its own text on the next line. */
.tray .more a, .tray-fold > summary {
  display: inline-flex; align-items: center; gap: .5rem;
}
.tray .more .icon, .tray-fold > summary .icon { width: .8rem; height: .8rem; }

/* Voice and In scenes: real jobs, but not this one. They sit after the
   picture work and fold to one line. */
.strip {
  display: flex; align-items: center; gap: .7rem; min-height: 52px;
  padding: 0 1rem; border: 1px solid var(--box-line); background: var(--g2);
  list-style: none; cursor: pointer;
}
.strip::-webkit-details-marker { display: none; }
.strip b {
  font-family: var(--display); font-weight: 600; font-size: .8rem;
  letter-spacing: .11em; text-transform: uppercase; color: var(--ink);
}
.strip .grow { flex: 1 1 auto; }
.voice-sec { margin-top: 2.5rem; }
.voice-sec .voice-body { padding-top: 1.25rem; }
details[open] > .strip .chev .icon { transform: rotate(90deg); }

/* ---- iPad and below: this room's own 44px floor ----
   Scoped to the room. The app-wide --control-h stays 36px: raising it is a
   separate decision and Brett's to make (vault/32 frame 6 note). */
@media (max-width: 1024px) {
  .work { grid-template-columns: minmax(0, 1fr) 312px; gap: 18px; }
  .room-kv { grid-template-columns: 180px minmax(0, 1fr); }
  .rail li > a, .rail li > .rail-flat { min-height: 56px; }
  .room-head .btn, .room-head button, .room-head summary.btn,
  .rail-said .btn, .rail-said button, .rail-said form button,
  .vstrip .btn, .vstrip button, .vstrip .btn-icon,
  .tool .btn, .tool button, .tool .pick-fold > summary,
  .tray .btn, .tray button, .tray summary, .tray .more a,
  .strip .btn, .voice-body .btn, .voice-body button,
  .start-door .btn, .start-door button {
    height: auto; min-height: 44px;
  }
  .tool .chip-sm, .tool .geo-variant { min-height: 44px; }
  /* The voice panel's own row of fields, and the two quiet text links that
     are still presses: back to design, and roll out. */
  .voice-body input, .voice-body select, .voice-body textarea,
  .voice-body .btn-primary, .voice-body .btn-icon {
    height: auto; min-height: 44px; min-width: 44px;
  }
  /* A field-row shares its width out; the narrowest share was 28px, which
     is a target no finger can hit. Wrap the row instead. */
  .voice-body .field-row { flex-wrap: wrap; }
  .voice-body .field-row > input, .voice-body .field-row > select { flex: 1 1 8rem; }
  .room-back a, .rail-said .rail-roll {
    display: inline-flex; align-items: center; min-height: 44px;
  }
  /* An icon-only press is square, so the 44px floor is a WIDTH here too. */
  .vstrip .btn-icon, .voice-body .btn-icon, .tray .btn-icon { min-width: 44px; }
  .th-acts, .th-hold { min-height: 44px; }
  .th-name input { height: 44px; }
  .th-hold input { width: 20px; height: 20px; }
  .room-head .room-tier button { min-height: 44px; }
}
@media (max-width: 900px) {
  .work { grid-template-columns: minmax(0, 1fr); }
  .stage .well-focus { min-height: 0; aspect-ratio: 4 / 5; max-height: none; }
  .rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rail.rail-short { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rail li { border-bottom: 1px solid var(--box-line); }
  .rail-wrap { position: static; }
}

/* Nobody has drawn them yet: three ways in (vault/31 frame A). Same shape
   as the front door, so there is nothing new to learn. */
.room-start-sub { color: var(--ink-dim); margin: 0 0 1.25rem; max-width: 60ch; }
.start-doors { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; margin-bottom: 24px; }
.start-doors:has(.start-door:only-child) { grid-template-columns: minmax(0, 24rem); }
.start-door {
  background: var(--g2); border: 1px solid var(--box-line); padding: 1.1rem;
  display: flex; flex-direction: column; gap: .7rem; min-height: 15rem; min-width: 0;
}
.start-door h3 { margin: 0; font-weight: 400; font-size: 1.15rem; color: var(--display-ink); }
.start-door p { margin: 0; color: var(--ink-dim); font-size: .9rem; }
.start-grow { flex: 1 1 auto; }
.start-form { margin: 0; display: flex; flex-direction: column; gap: .7rem; flex: 1 1 auto; }
.start-form .btn { align-self: flex-start; }
.start-drop { flex: 1 1 auto; aspect-ratio: auto; }

@media (max-width: 900px) { .start-doors { grid-template-columns: 1fr; } }

/* The one "what should change?" box, beside the stage. */
.gen-note-box {
  width: 100%; min-height: 3.4rem; resize: vertical;
  font-family: var(--body); font-size: .9rem; line-height: 1.45;
}
.gen-note { margin: -8px 0 24px; }
.gen-note summary { cursor: pointer; }
.gen-note pre { white-space: pre-wrap; font-family: var(--body); font-size: .85rem; color: var(--ink-dim); margin: .6rem 0 0; }

.drop-zone {
  aspect-ratio: 16 / 9; margin: 0; border: 1px dashed #3A3A3A; background: #121212;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .5rem;
  color: var(--ink-faint); cursor: pointer; padding: 1rem; text-align: center;
  font-family: var(--body); font-weight: 400; font-size: .85rem; letter-spacing: 0; text-transform: none;
  transition: border-color .15s ease, color .15s ease;
}
.drop-zone .icon { width: 1.25rem; height: 1.25rem; }
.drop-zone:hover, .drop-zone.is-over { border-color: var(--chrome); color: var(--ink); }

/* The board door (partials/board_door.html): a drop face that takes a film,
   a folder or a document, not one picture, so it is a band rather than a
   16:9 frame. */
.board-door { display: flex; flex-direction: column; gap: .5rem; max-width: 44rem; }
.board-door-face { aspect-ratio: auto; min-height: 8.5rem; position: relative; }
.board-door-face input[type=file], .board-door-folder input[type=file] { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.board-door-words { color: var(--ink); font-size: .95rem; }
.board-door-note { max-width: 34rem; font-size: .8rem; }
.board-door-opts { display: flex; flex-wrap: wrap; gap: .4rem 1.2rem; align-items: center; font-size: .75rem; }
.board-door-opts label { font-family: var(--mono); text-transform: none; letter-spacing: 0; font-weight: 400; font-size: .75rem; }
.board-door-opts select { margin-left: .4rem; }
.board-door-folder { position: relative; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
.board-door.is-busy .drop-zone, .model-drop.is-busy .drop-zone { border-color: var(--accent); color: var(--ink); cursor: progress; }
.board-door-or { margin: 1.6rem 0 .6rem; }
.board-door-pop[open] > form { width: min(44rem, 92vw); }
.pill-tween { font-size: .65rem; cursor: pointer; border-color: var(--accent); color: var(--ink); background: transparent; }
.pill-tween:hover { background: var(--accent); color: var(--bg, #111); }
.pill-join { font-size: .65rem; cursor: pointer; }
.join-pop { display: inline-block; position: relative; }
.join-pop > summary { list-style: none; }
.join-pop > summary::-webkit-details-marker { display: none; }
.join-pop-body { position: absolute; top: calc(100% + .3rem); left: 0; z-index: 30; min-width: 16rem;
  display: flex; flex-direction: column; gap: .5rem; padding: .6rem .7rem;
  background: var(--g2, #1b1b1b); border: 1px solid var(--line-strong, #3a3a3a); }
.join-pop-body form { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin: 0; }
.join-pop-body label { font-family: var(--mono); text-transform: none; letter-spacing: 0; font-size: .72rem; }
/* A shot's action frames under its board: a strip, not a second grid. */
.panel-action { display: flex; flex-direction: column; gap: .3rem; padding: .45rem .6rem 0; }
.panel-action .pt-k { font-family: var(--mono); font-size: .65rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-faint); }
.panel-action-strip { display: flex; gap: .3rem; overflow-x: auto; scrollbar-width: thin; padding-bottom: .3rem; }
.panel-action-frame { flex: 0 0 auto; width: 5.5rem; }
.panel-action-frame img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block; border: 1px solid var(--line, #2a2a2a); }
.panel-action-frame a:hover img { border-color: var(--chrome); }

.voice-grid { align-items: start; row-gap: 24px; }
.voice-col { display: flex; flex-direction: column; gap: .6rem; min-width: 0; }
.voice-col > .box + .lbl { margin-top: 1rem; }
.voice-box { margin: 0; padding: 1rem 1.25rem; display: flex; flex-direction: column; gap: .75rem; }
.voice-cast { flex-direction: row; align-items: center; gap: 1rem; min-height: 88px; }
.voice-icon {
  width: 44px; height: 44px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--btn-line); background: var(--btn-bg); color: var(--ink);
}
.voice-who { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.voice-name { font-weight: 500; font-size: 1.1rem; color: var(--display-ink); }
.voice-cast > form { margin: 0 0 0 auto; }
.field-row { display: flex; gap: .5rem; align-items: stretch; }
.field-row > input, .field-row > textarea, .field-row > select { flex: 1 1 auto; min-width: 0; width: auto; }
.field-row > select { flex: 0 1 auto; min-width: 7.5rem; }
/* The voice panel's filter row carries three inputs and a button. Without
   a wrap the button wins the argument and the accent box is squeezed to a
   couple of characters, which is not a box you can type "east london"
   into. */
.voice-box .field-row { flex-wrap: wrap; }
.voice-box .field-row > input[type="text"], .voice-box .field-row > input[type="search"] { min-width: 9rem; }
.field-row > button, .field-row > .btn { flex: 0 0 auto; height: auto; min-height: var(--control-h); }
.aud-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
.aud { padding: 1rem 1.1rem; display: flex; flex-direction: column; gap: .75rem; min-height: 132px; justify-content: space-between; }
.aud-head { display: flex; align-items: baseline; gap: .6rem; min-width: 0; }
.aud-name { font-family: var(--display); font-weight: 600; font-size: .9rem; letter-spacing: .08em; text-transform: uppercase; color: var(--display-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.aud-labels { font-size: .82rem; color: var(--ink-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The voice's own prose, which is where the casting information actually
   lives: the accent facet says "british" for a voice whose description says
   "East London geezer". Three lines is enough to judge on and keeps the
   cards on one grid. */
.aud-desc { font-size: .82rem; line-height: 1.45; color: var(--ink-dim); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.aud-mine { font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-dim); border: 1px solid var(--rule); border-radius: 2px; padding: .1rem .35rem; flex: 0 0 auto; margin-left: auto; }
.voice-searched { margin: -.35rem 0 .1rem; line-height: 1.45; }
.aud-row { display: flex; gap: .5rem; align-items: center; }
.aud-row form { margin: 0 0 0 auto; }
.aud-empty { grid-column: 1 / -1; margin: 0; }
.aud .btn-icon.is-playing { color: var(--chrome); border-color: var(--chrome); }

.room-fold-row { margin-top: 3rem; }
.room-fold-row > summary { display: flex; align-items: center; gap: .85rem; min-height: 60px; padding: 0 1.25rem; }
.room-fold-row > summary h3 { margin: 0; }
.room-fold-row .scene-pills { display: flex; gap: .35rem; margin-left: .5rem; overflow: hidden; min-width: 0; }
.room-fold-row .fold-body { padding: .25rem 1.25rem 1.25rem; display: flex; flex-direction: column; gap: 1rem; }
.scene-links { list-style: none; padding: 0; margin: 0; columns: 2; column-gap: 24px; }
.scene-links li { break-inside: avoid; padding: .2rem 0; }
.scene-links .mono { color: var(--ink-faint); margin-right: .5rem; }

/* Brief, typeset: the document boxed over 8 columns, the rail over 4, top
   edges shared. */
.brief-head { display: flex; align-items: flex-end; gap: 1.5rem; }
.brief-head h1 { margin: 0; }
.brief-head .meta { margin: .6rem 0 0; color: var(--ink-dim); }
.brief-head .room-actions > form { margin: 0; }
.brief-grid { margin-top: 2.25rem; align-items: start; }
.brief-doc { grid-column: span 8; display: flex; flex-direction: column; min-width: 0; }
.brief-rail { grid-column: span 4; display: flex; flex-direction: column; gap: 24px; min-width: 0; }
.brief-sec { display: flex; flex-direction: column; gap: .9rem; padding: 1.75rem 2rem; border-top: 1px solid #2C2C2C; }
.brief-doc > .brief-sec:first-child { border-top: 0; }
.brief-sec p { margin: 0; font-size: 1.05rem; line-height: 1.6; }
.brief-sec b { font-weight: 600; color: var(--display-ink); }
.brief-sec ul, .brief-sec ol { margin: 0; padding-left: 1.2rem; display: flex; flex-direction: column; gap: .4rem; font-size: 1.05rem; line-height: 1.55; }
.brief-pre p { color: var(--ink-dim); font-size: 1rem; }
.brief-logline p:first-of-type { font-weight: 500; font-size: 1.6rem; line-height: 1.32; color: var(--display-ink); }
.brief-logline p + p { color: var(--ink-dim); }
.brief-who { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 1.25rem; align-items: start; }
.brief-who-pic { position: relative; width: 72px; height: 72px; background: var(--g0); overflow: hidden; display: flex; align-items: center; justify-content: center; border-bottom: 0; color: var(--ink-ghost); }
a.brief-who-pic:hover { border-bottom: 0; }
.brief-who-pic img { width: 100%; height: 100%; object-fit: cover; display: block; }
.brief-who-text { display: flex; flex-direction: column; gap: .25rem; min-width: 0; }
.brief-who-name { font-family: var(--display); font-weight: 600; font-size: .875rem; letter-spacing: .1em; text-transform: uppercase; color: var(--display-ink); }
.brief-who p, .brief-beat p { font-size: 1rem; }
.brief-beat { display: grid; grid-template-columns: 32px minmax(0, 1fr); gap: .75rem; align-items: baseline; }
.brief-beat .mono { color: var(--ink-faint); }
.brief-fold { border-top: 1px solid #2C2C2C; }
.brief-fold > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: .75rem; min-height: 60px; padding: 0 2rem; }
.brief-fold > summary::-webkit-details-marker { display: none; }
.brief-fold > summary h3 { margin: 0; font-size: .875rem; }
.brief-fold > .brief-sec { border-top: 0; padding-top: .25rem; }
.brief-beats-more > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: .6rem; padding-left: 44px; min-height: 28px; }
.brief-beats-more > summary::-webkit-details-marker { display: none; }
.brief-beats-more[open] > summary { margin-bottom: .9rem; }
.brief-beats-more > div { display: flex; flex-direction: column; gap: .9rem; }
.rail-box { display: flex; flex-direction: column; gap: .85rem; padding: 1.4rem 1.5rem 1.5rem; }
.rail-box h3 { margin: 0; }
.rail-box p { margin: 0; font-size: .95rem; line-height: 1.55; }
.rail-box .btn, .rail-box form { align-self: flex-start; margin: 0; }
.brief-tick { display: flex; align-items: center; gap: .6rem; font-size: .92rem; }
.brief-tick-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ink); flex: 0 0 auto; }
.brief-tick.is-open { color: var(--ink-dim); }
.brief-tick.is-open .brief-tick-dot { background: transparent; border: 1px solid var(--ink-faint); }
.rail-upload { display: flex; flex-direction: column; gap: .5rem; }
.rail-upload input[type=file] { font-size: .85rem; color: var(--ink-dim); }

@media (max-width: 900px) {
  .brief-doc, .brief-rail { grid-column: 1 / -1; }
  .brief-rail { margin-top: 24px; }
  .g12 > [class*="span-"] { grid-column: 1 / -1; }
  /* A COLUMN flex container that is also allowed to WRAP sizes its line to
     the widest item's own width, and stops stretching items to the box. The
     row of held pictures is 834px of thumbs, so under 900px the generate box
     grew to 887 and put a horizontal scrollbar under an iPad. Wrapping is a
     row-direction affordance; in a column it is the defect. Carries the id
     because that is what the rule it undoes carries. Measured 2026-09-20; it
     predates the model picker standing beside it. */
  .room-kv { grid-template-columns: minmax(0, 1fr); }
  .aud-grid { grid-template-columns: minmax(0, 1fr); }
  .room-head-row { flex-wrap: wrap; }
  .room-actions { margin-left: 0; flex-wrap: wrap; }
  .scene-links { columns: 1; }
  .field-row { flex-wrap: wrap; }
  .model-tile .cap .mono, .model-tile .cap-hint { display: none; }
  .cap .geo-variants { min-width: 0; overflow-x: auto; scrollbar-width: none; }
}

/* ---- BOX RULE, applied in one place (2026-09-14) ----
   Every card-like surface takes the same quiet gradient. Declared last so it
   wins over each component's old flat background; state variants (two
   classes, :hover) keep their own border and shadow. A new box is a selector
   added here, not a gradient re-typed. Picture wells are never in this list. */
.card, .box, .design-slot, .entity-card, .ref-card, .storyboard-panel,
.project-card, .iter-section, .iter-provider, .attach-block, .filterbar,
.gen-card, .poster-card, .stage-card, .scene-card, .shot-card,
.confirm-dialog, .add-drawer-body, .door, .door-open {
  background: var(--box-bg);
  box-shadow: var(--box-shadow);
}
.add-drawer-body { box-shadow: var(--box-shadow), 0 8px 28px rgba(0,0,0,.45); }

/* The React canvas reads its own face variables; point them at the family. */
html body.page-canvas {
  --font-body: var(--body);
  --font-label: var(--label);
  --gn-font-label: var(--label);
}

/* ---- The design room's world of the film and cast; a character room that
   designs from words (2026-09-14) ---- */
.world-grid { row-gap: 24px; }
.world-poster { grid-column: span 4; }
.world-poster .folder-thumb { aspect-ratio: 4 / 3; }
.world-principles { grid-column: span 8; padding: 1.1rem 1.5rem 1rem; display: flex; flex-direction: column; gap: .8rem; align-items: flex-start; }
.world-principles .room-kv { width: 100%; }
.world-principles--start form { margin: 0; }
.world-lede, .wiz-lede { margin: 0; max-width: 44rem; font-size: 1rem; line-height: 1.5; color: var(--ink); }
.wiz-lede { margin: .8rem 0 1.5rem; }
.clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.cast-tier { margin-top: 1.25rem; }
.cast-tier-label { display: flex; gap: .6rem; align-items: baseline; margin: 0 0 .7rem; }
.cast-wall { min-height: 5rem; padding: 4px; margin: -4px; border: 1px dashed transparent; transition: border-color .15s ease, background-color .15s ease; }
.cast-wall--supporting { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.cast-wall--extra { grid-template-columns: repeat(auto-fill, minmax(116px, 1fr)); }
.cast-wall--supporting .folder-poster-name, .cast-wall--extra .folder-poster-name { font-size: .8rem; }
.cast-wall.is-over { border-color: var(--accent); background: rgba(97, 160, 255, .05); }
.cast-wall .folder--poster { cursor: grab; }
.cast-wall .folder--poster.is-dragging { opacity: .35; }
.cast-lines { font-family: var(--label); font-weight: 600; font-size: .66rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint); white-space: nowrap; }
.cast-empty { grid-column: 1 / -1; margin: 0; padding: 1.2rem 0; display: none; }
.cast-wall:not(:has(.folder--poster)) .cast-empty { display: block; }
.cast-status { margin: 1rem 0 0; }
.cast-status:empty { display: none; }
.seg-btns { margin: 0; }
.room-tier { flex: 0 0 auto; }
.room-tier + .meta { white-space: nowrap; }
.seg-btns button {
  display: inline-flex; align-items: center; height: 100%; min-height: 0; margin: 0; padding: 0 .85rem;
  background: transparent; border: 0; border-right: 1px solid var(--btn-line); border-radius: 0; box-shadow: none;
  font-family: var(--label); font-weight: 600; font-size: .72rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-dim); white-space: nowrap; cursor: pointer;
}
.seg-btns button:last-child { border-right: 0; }
.seg-btns button:hover { color: var(--display-ink); }
.seg-btns button[aria-pressed="true"] { color: var(--display-ink); background: #2E2E2E; }
.seg-btns button:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--chrome); }
.gen-from { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .6rem; margin: 0 0 .75rem; }
.gen-src { font-family: var(--label); font-weight: 600; font-size: .7rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-dim); border: 1px solid var(--btn-line); padding: .3rem .55rem; }
.gen-note--from { margin: 0 0 1rem; }
.gen-quote { margin: .6rem 0 0; max-width: 48rem; font-size: .9rem; line-height: 1.55; color: var(--ink-dim); white-space: pre-line; }
.gen-quote .lbl { margin-right: .4rem; }
/* The look of the film, in the world room (indy/look.py): a strip of its
   pictures, each with its take-out control beneath (nothing over a picture),
   then the words. */
.look-scope { margin-top: 1rem; padding: 1rem 1.25rem; display: grid; gap: .9rem; }
.look-scope-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem .75rem; }
.look-scope-head .look-add { margin-left: auto; }
.look-pics { display: flex; gap: 12px; overflow-x: auto; scrollbar-width: thin; }
.look-pic { flex: 0 0 auto; display: grid; justify-items: end; gap: 4px; }
.look-pic .well { display: block; width: 148px; height: 148px; overflow: hidden; background: var(--g0); }
.look-pic img { width: 100%; height: 100%; object-fit: cover; display: block; }
.look-pic form { margin: 0; }
.look-words { display: grid; gap: .45rem; max-width: 48rem; }
@media (max-width: 640px) { .look-pic .well { width: 112px; height: 112px; } }
.wiz-notes { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: .9rem 1.25rem; margin: 0 0 1.5rem; }
.wiz-notes p { margin: 0; }
.wiz-group { margin: 2.2rem 0 .75rem; }
.wiz-from { align-self: flex-start; margin-top: .2rem; }
.world-sec > .sech, .cast-sec > .sech { flex-wrap: wrap; row-gap: .5rem; padding-bottom: .5rem; }
.world-sec > .sech h3, .cast-sec > .sech h3 { flex: 0 0 auto; }
@media (max-width: 720px) {
  .world-poster, .world-principles { grid-column: 1 / -1; }
  .world-sec > .sech .meta, .cast-sec > .sech .meta { flex-basis: 100%; white-space: normal; }
  .world-sec > .sech .sech-right { margin-left: 0; flex-wrap: wrap; flex-shrink: 1; min-width: 0; }
  .room-head-row { flex-wrap: wrap; }
  .wiz-notes { flex-direction: column; align-items: flex-start; }
}

/* ── The shot bench (2026-09-18; tidied the same day) ─────────────────────
   One component, three sizes: .bench--full (the shot's page), .bench--drawer
   (over the board and the scene page), .bench--dock (beside the cut).
   partials/shot_bench.html declares the rows; this only lays them out, and
   design/bench-tidy-spec.md says why each control sits where it does.

   The bench measures ITSELF (a container), not the window: a dock or a
   narrow drawer is one column whatever the screen, and in one column the
   make row comes straight after the ladder, where NEXT points.

   Law 8: white and grey. At rest the bench holds no blue at all: the rung
   you are on, a lit pin or flop, and the labelled first-take button are
   WHITE, because the dock sits beside the timeline's live timecode and two
   blue things in one glance is one too many. The one colour a control
   wears is attention orange (.is-wanted), the ⚠ on the rung's own colour,
   on the control that clears it.
   Law 10: the picture's size is declared (width 100%, its own ratio); the
   rows beneath it are fixed-height, so a control row can never take a
   pixel from it. Inside a row, text truncates; icons and pictures do not
   shrink. */
.bench { container: bench / inline-size; display: flex; flex-direction: column; gap: .75rem; min-width: 0; }
.bench-crumbs { font-size: .85rem; margin-bottom: .75rem; }
.bench .sep { width: 1px; height: 18px; background: var(--rule); margin: 0 .3rem; flex: 0 0 auto; }
.bench-controls form, .bench-make form, .bench-notes form { display: inline-flex; margin: 0; }
.bench-controls .btn-icon, .bench-make .btn-icon, .bench-head .btn-icon, .bench-notes .btn-icon { flex: 0 0 auto; }

/* HEAD: identity (steps round the code) · meta · doors · window. One row;
   narrow, the meta drops to a line of its own rather than squeeze. */
.bench-head { display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto;
  grid-template-areas: "id meta doors window"; align-items: center; column-gap: .75rem; min-width: 0; }
.bench--full .bench-head { grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "id meta doors"; }
.bench-id { grid-area: id; display: flex; align-items: center; gap: .6rem; min-width: 0; }
.bench-steps { display: flex; align-items: center; gap: .3rem; }
.bench-code { margin: 0; font-size: 1.6rem; line-height: 1.1; white-space: nowrap; }
.bench-meta { grid-area: meta; font-size: .72rem; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bench-doors { grid-area: doors; display: flex; align-items: center; gap: 2px; }
.bench-window { grid-area: window; display: flex; align-items: center; gap: 2px; padding-left: .75rem; border-left: 1px solid var(--rule); }
.bench-step[aria-disabled="true"] { opacity: .3; pointer-events: none; }
/* Opened from the grouped shot canvas on some rungs only (?only=anim): the
   way back first, and the head names what the bench is showing. */
.bench-backrow { margin: 0 0 .6rem; }
.bench-only { font-family: var(--mono); font-size: .8rem; letter-spacing: .06em; text-transform: uppercase;
  padding: .2rem .5rem; border-radius: 4px; background: #D9773F; color: #171717; white-space: nowrap; }
.bench-controls .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
.bench-controls .btn[aria-disabled="true"]:hover { color: var(--ink); border-color: var(--btn-line); }
.icon.icon-flip { transform: scaleX(-1); }
/* The plan's own select says where it stands while the plan is open. */
.bench:has(.bench-plan[open]) .bench-plan-state { visibility: hidden; }

.bench-plan > summary { list-style: none; display: flex; align-items: flex-start; gap: .5rem; cursor: pointer; }
.bench-plan > summary::-webkit-details-marker { display: none; }
.bench-desc { flex: 0 1 70ch; min-width: 0; color: var(--ink); }
.bench-plan > summary .btn { flex: 0 0 auto; }
/* Open, the description is in the field below: say it once (law 9). */
.bench-plan[open] > summary .bench-desc { visibility: hidden; height: 0; overflow: hidden; }

.bench-fields { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: .55rem .75rem; margin: .5rem 0 0; }
.bench-fields label, .bench-make-wide { display: flex; flex-direction: column; gap: .2rem; min-width: 0;
  font-family: var(--mono); font-size: .66rem; font-weight: 400; color: var(--ink-dim); text-transform: lowercase; letter-spacing: 0; }
.bench-fields .wide { grid-column: 1 / -1; }
.bench-fields input, .bench-fields select, .bench-fields textarea, .bench-make-wide select { width: 100%; min-width: 0; }
.bench-fields input[data-dim] { opacity: .5; }
.bench-fields.is-saving { opacity: .7; }
.bench-plan .bench-fields { max-width: 44rem; }

/* LADDER */
.bench-ladder { display: flex; gap: .5rem; border-bottom: 1px solid var(--rule); overflow-x: auto; scrollbar-width: none; }
.bench-ladder::-webkit-scrollbar { display: none; }
.bench-rung { flex: 1 1 0; min-width: 76px; max-width: 176px; display: flex; flex-direction: column; gap: .3rem;
  padding: .3rem .3rem .45rem; color: var(--ink-dim); text-decoration: none;
  border-bottom: 2px solid transparent; margin-bottom: -1px; opacity: .66; }
.bench-rung:hover { opacity: 1; text-decoration: none; color: var(--ink); }
.bench-rung.is-here { opacity: 1; color: var(--display-ink); border-bottom-color: var(--display-ink); }
.bench-rung-pic { display: block; aspect-ratio: 16 / 9; background: var(--bg-card); border: 1px solid var(--rule); overflow: hidden; }
.bench-rung-pic img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bench-rung.is-empty .bench-rung-pic { border-style: dashed; background: transparent; }
.bench-rung-line { display: flex; align-items: center; gap: .35rem; min-width: 0;
  font-family: var(--label); font-size: .7rem; letter-spacing: .06em; text-transform: uppercase; white-space: nowrap; }
.bench-rung-name { overflow: hidden; text-overflow: ellipsis; }
.bench-dot { flex: 0 0 auto; width: .62rem; height: .62rem; border-radius: 50%; border: 1.5px solid var(--ink-faint);
  display: inline-flex; align-items: center; justify-content: center; }
.bench-dot .icon { width: .85rem; height: .85rem; flex: 0 0 auto; }
.bench-rung.is-undecided .bench-dot { background: var(--ink-dim); border-color: var(--ink-dim); }
.bench-rung.is-approved .bench-dot { border: 0; color: var(--ok); }
.bench-rung.is-stale .bench-dot, .bench-rung.is-failed .bench-dot { border: 0; color: var(--warn); }
.bench-rung.is-running .bench-dot { border-color: var(--ink); border-top-color: transparent; animation: bench-spin 1s linear infinite; }
@keyframes bench-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .bench-rung.is-running .bench-dot { animation: none; border-top-color: var(--ink); border-style: dotted; } }
.bench-next { margin-left: auto; flex: 0 0 auto; font-family: var(--mono); font-size: .58rem; letter-spacing: 0;
  color: var(--display-ink); border: 1px solid var(--line-strong); padding: 0 .3rem; }

/* BODY: wide, the picture's column beside the rung's own (make, levers). */
.bench-body { display: grid; grid-template-columns: minmax(0, 1fr) 20rem; gap: 1.5rem; align-items: start; }
.bench-main { display: flex; flex-direction: column; min-width: 0; }
.bench-side { display: flex; flex-direction: column; gap: .75rem; min-width: 0; position: sticky; top: 5rem; }
.bench--dock .bench-side, .bench--drawer .bench-side, .bench--window .bench-side { position: static; }
/* In the window on the canvas the next take is the canvas's own sheet, beside
   this fragment (NextTake.jsx): the fragment is one column, the picture as
   wide as the column, and what is left of its side (the run's own status)
   sits under the picture. */
.bench--window[data-next="sheet"] .bench-body { grid-template-columns: minmax(0, 1fr); gap: .75rem; }
.bench--window[data-next="sheet"] .bench-side:empty { display: none; }
.bench-stage { min-width: 0; }
.bench-viewer { display: grid; grid-template-columns: minmax(0, 1fr); gap: .5rem; }
.bench-viewer.is-ab { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.bench-pic { display: block; width: 100%; height: auto; border: 1px solid var(--rule); background: #000; }
.bench-viewer .thumb-placeholder.bench-pic { aspect-ratio: 16 / 9; background: transparent; border-style: dashed; }
.bench-pic-btn { all: unset; display: block; width: 100%; cursor: pointer; }
.bench-pic-btn:focus-visible { outline: 2px solid var(--chrome); outline-offset: 2px; }
.bench-view.is-source { opacity: .4; }
.bench-take-id { margin: .4rem 0 0; font-size: .72rem; min-height: 1.1rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* CONTROLS: the take in view. A lit state is white, like "you are here":
   the pin when the cut plays this take (filled when the cut is HELD on
   it), the flop when the shot is mirrored. */
.bench-controls { display: flex; align-items: center; gap: 2px; flex-wrap: nowrap; margin: .35rem 0 0; }
.bench .btn-icon.active, .bench .shot-media.flopped .flop-btn { color: var(--display-ink); border-color: var(--display-ink); }
.bench .btn-icon.active[data-pinned="1"] { background: var(--g4); }

/* MAKE: the rung's own moves. Primary first, then the fixes; a select
   takes what width is left and a wide field (starts on) its own line. */
.bench-make { display: flex; align-items: center; flex-wrap: wrap; gap: 2px; row-gap: .45rem; min-width: 0; }
.bench-make > form.bench-make-form { display: contents; }
.bench-make-fix { order: 1; }
.bench-make-engine { order: 2; flex: 1 1 7rem; min-width: 0; max-width: 12rem; margin-left: .4rem; }
.bench-make-wide { order: 3; flex: 1 0 100%; }
/* The upload's file picker drops under the row, pinned to the ROW's left
   edge and never wider than it, so it cannot run off a narrow column. */
.bench-make { position: relative; }
.bench-make .board-upload { display: inline-flex; }
.bench-make .board-upload > form { position: absolute; top: calc(100% + 4px); left: 0; max-width: 100%; box-sizing: border-box; z-index: 5;
  display: flex; gap: .35rem; align-items: center; padding: .5rem; background: var(--g3); border: 1px solid var(--line-strong); }
.bench-make .board-upload input[type="file"] { min-width: 0; flex: 1 1 auto; font-size: .78rem; }
.bench-make .board-upload[data-disabled] > summary { opacity: .45; pointer-events: none; }
.bench .btn-primary { color: var(--display-ink); border-color: var(--display-ink); }
.bench .btn-primary:hover { color: var(--chrome); border-color: var(--chrome); }
.bench .is-wanted, .bench .btn-primary.is-wanted { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 70%, transparent); }
.bench .is-wanted:hover { color: var(--warn); border-color: var(--warn); }
.bench-say { margin: 0; font-size: .82rem; }
.board-lines-banner { grid-column: 1 / -1; flex-basis: 100%; display: flex; flex-wrap: wrap; gap: .45rem 1rem; align-items: center; padding: .5rem .75rem; border: 1px dashed var(--warn); border-radius: 8px; font-size: .82rem; }
.board-lines-banner form { display: inline-flex; gap: .4rem; align-items: center; margin: 0; }
.bench-plate { display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
.bench-plate img { height: 26px; width: auto; border-radius: 3px; display: block; }
.bench-plate.is-warn { color: var(--warn); }
.bench-plate form { display: inline; margin: 0; }
.bench-hint { margin: 0; font-size: .78rem; }

/* PICTURES: the rung's takes. */
.bench-takes { display: flex; gap: .5rem; overflow-x: auto; padding: .6rem 0 .35rem; margin-top: .6rem; border-top: 1px solid var(--rule); scrollbar-width: thin; }
.bench-take { flex: 0 0 132px; display: flex; flex-direction: column; gap: .2rem; text-decoration: none; color: var(--ink-dim); opacity: .66; }
.bench--dock .bench-take { flex-basis: 104px; }
.bench-take:hover { opacity: 1; text-decoration: none; }
.bench-take img { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border: 1px solid var(--rule); }
.bench-take.is-here { opacity: 1; color: var(--display-ink); }
.bench-take.is-here img { border-color: var(--display-ink); }
.bench-take.is-vs { opacity: 1; }
.bench-take.is-vs img { border-color: var(--ink-dim); border-style: dashed; }
.bench-take.is-aside img { filter: grayscale(1) brightness(.6); }
/* The approved take, said on the picture: the strip runs newest first, so
   the approved one can sit anywhere in it. */
.bench-take-pic { position: relative; display: block; }
.bench-take.is-approved { opacity: 1; }
.bench-take.is-approved img { border: 2px solid var(--ok); }
.bench-take-flag { position: absolute; left: 0; bottom: 0; padding: .05rem .3rem; font-size: .56rem; letter-spacing: .08em; text-transform: uppercase; color: var(--bg, #111); background: var(--ok); }
.bench-take-line { display: flex; align-items: center; gap: .3rem; font-size: .64rem; white-space: nowrap; }
.bench-mark { display: inline-flex; align-items: center; gap: .1rem; }
.bench-mark .icon { width: .78rem; height: .78rem; }
.bench-mark.ok { color: var(--ok); }

/* NOTES: each says what it became; the newest carries the revise. */
.bench-notes { list-style: none; margin: .8rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .6rem; max-width: 70ch; }
.bench-notes li { display: flex; align-items: flex-start; gap: .5rem; border-left: 2px solid var(--rule); padding-left: .75rem; }
.bench-note-text { flex: 1 1 auto; min-width: 0; }
.bench-notes p { margin: 0 0 .15rem; }
.bench-notes .mono { font-size: .66rem; }

.bench-flash { margin: 0; padding: .4rem .6rem; border: 1px solid var(--warn); color: var(--warn); font-size: .8rem; }
.bench-flash.is-ok { border-color: var(--line-strong); color: var(--ink); }
[data-bench-host].is-loading .bench-stage { opacity: .6; transition: opacity .15s .1s; }

/* One column: the dock, a narrow drawer, a narrow window. The columns
   dissolve (display: contents) so their rows can be declared in one order:
   the make row at the ladder's foot, then the picture and everything under
   it, then the levers. The picture keeps its declared size. */
@container bench (max-width: 760px) {
  .bench-head { grid-template-areas: "id . doors window" "meta meta meta meta"; row-gap: .25rem; }
  .bench--full .bench-head { grid-template-areas: "id . doors" "meta meta meta"; }
  .bench-code { font-size: 1.25rem; }
  /* A long description would push the ladder and the make row off the
     first screen; three lines here, the whole of it in the field when the
     plan is open. */
  .bench-desc { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
  .bench-body { display: flex; flex-direction: column; align-items: stretch; gap: .75rem; }
  .bench-main, .bench-side { display: contents; }
  .bench-make { order: 1; }
  .bench-say { order: 2; }
  .bench-side > .iter-results { order: 3; }
  .bench-stage { order: 4; }
  .bench-keys { order: 5; margin-top: 0; }
  .bench-takes { order: 5; margin-top: 0; }
  .bench-passes { order: 5; margin-top: 0; }
  .bench-notes { order: 6; margin-top: 0; }
  .bench-from { order: 1; }
  .bench-anim { order: 1; }
  .bench-levers { order: 7; margin-top: 0; }
  .bench-hint { order: 8; }
  /* The ladder must FIT, not scroll: a hidden scrollbar makes the last rung
     silently unreachable. Five rungs share ~370px, so the name takes its
     own line above the mark and the `next` tag, and the type tightens. */
  .bench-ladder { gap: .3rem; overflow-x: visible; }
  .bench-rung { min-width: 0; padding: .25rem .1rem .4rem; }
  .bench-rung-line { flex-wrap: wrap; row-gap: .15rem; font-size: .58rem; letter-spacing: .02em; }
  .bench-rung-name { order: -1; flex: 1 0 100%; }
  .bench-next { margin-left: 0; }
}

/* Over the board and the scene page. Under the lightbox (100) and the note
   painter (110), which open FROM it. */
.bench-drawer { position: fixed; inset: 0; z-index: 90; background: rgba(0, 0, 0, .55); display: flex; justify-content: flex-end; }
.bench-drawer-panel { width: min(1100px, 94vw); height: 100%; overflow-y: auto; outline: none;
  background: var(--bg); border-left: 1px solid var(--rule); padding: 1.25rem 1.5rem 3rem; }
body.bench-drawer-open { overflow: hidden; }

/* The master chain, inside the motion rung (moved from shot_detail.html's
   inline block with the markup). */
.bench-chain { border-top: 1px solid var(--rule); padding-top: .25rem; }
.chain-strip { display: flex; gap: 12px; overflow-x: auto; padding: 10px 2px 6px; }
.chain-state { flex: 0 0 auto; width: 300px; display: flex; flex-direction: column; gap: 7px;
  background: var(--bg-card); border: 1px solid var(--rule); border-radius: 6px; padding: 10px; }
.chain-state .csn { font-family: var(--mono); font-size: .68rem; color: var(--ink-faint); }
.cs-rung { display: flex; flex-direction: column; gap: 3px; }
.cs-rung .lbl { font-family: var(--mono); font-size: .62rem; color: var(--ink-faint); display: flex; align-items: center; gap: 6px; }
.cs-rung .lbl form { display: inline; margin-left: auto; }
.cs-thumb { width: 100%; border-radius: 3px; display: block; border: 1px solid var(--rule); cursor: pointer; }
.cs-thumb.ok { border-color: var(--ok); }
.cs-miss { font-family: var(--mono); font-size: .62rem; color: var(--ink-faint); }

/* With the panes filling the window, a link to a picture ("?focus=7#focus")
   must not nudge the page down to the viewer: a scroll margin taller than the
   window sends the jump back to the top, where the whole frame is in view. */
@media (min-width: 1101px) {
  .stage.pane, .tray.pane-bin .tgroup { scroll-margin-top: 100vh; }
}

/* The three panes at iPad width and below: stacked, viewer first, then the
   inspector, then the bin, and the page scrolls as it used to. */
@media (max-width: 1100px) {
  .work {
    grid-template-columns: minmax(0, 1fr); height: auto; min-height: 0;
    width: auto; margin: 1rem 0 1.5rem; border: 0;
  }
  .work > .pane { overflow: visible; border-left: 0; }
  .work > .pane-view { order: 1; padding: 0; }
  .work > .pane-insp { order: 2; }
  .work > .pane-bin { order: 3; }
  .stage .well-focus { min-height: 22rem; max-height: 64vh; }
  .tray.pane-bin { --tray-th: 128px; }
  .tray.pane-bin .grid { grid-template-columns: repeat(auto-fill, var(--tray-th)); }
  .pane-h { position: static; }
}

/* === Character room, tidy pass (Brett, 2026-09-21) =======================
   Five moves, all of them the same move: stop making the page compete with
   the picture it exists to judge.
     1 the bin stops being a rail and becomes a strip UNDER the hero
     2 the groups stop being five stacked headings and become a filter
     3 the strip is ONE declared row, so it can never go ragged (law 10)
     4 state comes off the pictures and sits under them (law 6)
     5 the header's sentences become marks, the tooltip keeps the words
   ------------------------------------------------------------------- */

/* 1 · Two panes, not three. */
.work.work-2up { grid-template-columns: minmax(0, 1fr) var(--insp-w); }

/* The stage is a column: the picture takes the leftover height, the strip
   under it keeps its own. */
.work-2up > .pane-view {
  display: flex; flex-direction: column; gap: .7rem;
  padding: 1rem 1.25rem 1.1rem;
}
.work-2up > .pane-view .well-focus { flex: 1 1 auto; min-height: 0; }

.tray { display: flex; flex-direction: column; gap: .55rem; flex: 0 0 auto; min-width: 0; }

/* 2 · The filter. It carries every label and count, which is why the group
   headings below drop theirs: law 9, said once. */
.tray-filter {
  display: flex; align-items: center; gap: .35rem;
  flex-wrap: nowrap; overflow: hidden;
}
.tray-filter .grow { flex: 1 1 auto; }
.tfilt {
  height: 30px; padding: 0 .7rem; flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: .45rem; white-space: nowrap;
  background: transparent; border: 1px solid var(--line); color: var(--ink-dim);
  font-family: var(--body); font-weight: 300; font-size: .8rem; letter-spacing: .01em;
  cursor: pointer;
}
.tfilt:hover { color: var(--ink); border-color: var(--line-strong); }
/* The chip that is on is raised and white, NOT blue: the one primary press
   in this view is already blue, and two blue things in a glance means one of
   them is wrong. */
.tfilt.is-on {
  background: var(--g4); border-color: var(--line-strong); color: var(--display-ink);
}
.tfilt .cnt { font-size: .7rem; color: var(--ink-ghost); }
.tfilt.is-on .cnt { color: var(--ink-dim); }
.tray-say { flex: 0 1 auto; font-size: .75rem; color: var(--ink-ghost); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* 3 · ONE row. It scrolls sideways and never wraps, so no caption can take
   a pixel from a picture. */
.tray-strip {
  /* The tile was sized for a 264px rail. The strip has the whole room now,
     so it grows with it: a picture is never made small to make room for
     anything (law 5), and a 104px thumbnail in 1300px of strip read as a
     broken grid rather than as a shelf. */
  --tray-th: clamp(104px, 8.5vw, 152px);
  display: flex; align-items: flex-start; gap: 14px;
  overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain;
  padding: .7rem .8rem .5rem; scrollbar-width: thin;
  /* A shelf, so a group holding one picture reads as a shelf with room on
     it and not as a row that failed to fill. */
  background: var(--g0); border: 1px solid var(--line);
}
.tray-strip .tgroup { margin: 0; flex: 0 0 auto; display: flex; align-items: flex-start; gap: 14px; }
.tray-strip .tgroup[hidden] { display: none; }
.tray-strip .grid { display: flex; gap: 14px; grid-template-columns: none; }
.tray-strip .th { width: var(--tray-th); flex: 0 0 auto; }
/* A DECLARED two lines: enough for "turnaround, #560" and "the look, on
   the boards" without truncating them to nothing, and a fixed height, so a
   caption still cannot take a pixel from a picture. */
.tray-strip .th figcaption {
  height: 2.1rem; margin-top: .3rem;
  font-size: .72rem; line-height: 1.16; white-space: normal;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.tray-strip .more-fold, .tray-strip .more { flex: 0 0 auto; align-self: center; margin: 0; }
.tray-strip .iter-results { width: var(--tray-th); }

/* The chip says the group, so the heading goes entirely. A group that
   carries a note ("dragged in. Type a name under any of them") has it read
   out into the line beside the filter when that group is picked, rather
   than left floating between two rows of pictures. */
.tray-strip .glab { display: none; }

/* 4 · State under the picture, never on it. */
.th-state { display: block; height: 2px; margin-top: 4px; background: var(--line); }
.th-state.is-master { background: var(--bench); }
.th-state.is-approved { background: var(--ok); }
.th-state i {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}

/* 5 · An icon-only action. The words live in the tooltip. */
.btn.btn-mark {
  width: 44px; min-width: 44px; height: 38px; padding: 0; gap: 0;
  display: inline-grid; place-items: center;
}
.btn.btn-mark .icon { margin: 0; }
.room-actions { gap: .4rem; }

/* Who they are, set at a measure instead of against a 200px label column. */
.room-desc { padding: 1.7rem 1.9rem 1.5rem; }
.room-desc .room-kv { grid-template-columns: minmax(0, 1fr); row-gap: .3rem; }
.room-desc .room-kv dt { margin-top: 1.4rem; }
.room-desc .room-kv dt:first-of-type { margin-top: 0; }
.room-desc .room-kv dd {
  margin: 0; max-width: 64ch;
  font-size: .94rem; font-weight: 300; line-height: 1.62; color: var(--ink);
}
/* The sentence is the one that carries the character, so it is set as type
   and not as a table cell. */
.room-desc .room-kv dd:nth-of-type(2) {
  max-width: 42ch;
  font-size: 1.32rem; font-weight: 200; line-height: 1.42; letter-spacing: .005em;
  color: var(--paper-ink);
}

/* How they sound. */
.voice-sec .voice-body { padding-top: 1.4rem; }

/* The room's own breakpoints, restated for the two-pane layout. They have
   to sit after the pass above: `.work.work-2up` outranks `.work`, so the
   stacking rules further up the file would never have bitten. */
@media (max-width: 1024px) {
  .work.work-2up { grid-template-columns: minmax(0, 1fr) 312px; gap: 18px; }
  .tray-strip { --tray-th: 112px; }
  .tfilt { height: 44px; }
  .btn.btn-mark { height: 44px; }
}
@media (max-width: 1100px) {
  .tray-filter { flex-wrap: wrap; overflow: visible; }
  .tray-say { display: none; }
}
@media (max-width: 900px) {
  .work.work-2up {
    grid-template-columns: minmax(0, 1fr); height: auto; min-height: 0;
    width: auto; margin: 1rem 0 1.5rem; border: 0;
  }
  .work-2up > .pane-view { order: 1; padding: 0; }
  .work-2up > .pane-insp { order: 2; }
  .work-2up > .pane-view .well-focus { min-height: 22rem; max-height: 64vh; }
  .room-desc .room-kv dd:nth-of-type(2) { font-size: 1.16rem; }
}

/* --- The picture gets the leftover height, not what is left after it -----
   Law 5: a picture is never made small to make room for text. The strip,
   the identity row and the controls are all DECLARED heights, so the well
   can take everything else and the image fits inside it. ---------------- */
.work-2up > .pane-view .well-focus {
  display: grid; place-items: center; overflow: hidden; min-height: 0;
}
.work-2up > .pane-view .well-focus > a { display: grid; place-items: center; max-width: 100%; max-height: 100%; }
.work-2up > .pane-view .well-focus img.thumb {
  width: auto; height: auto; max-width: 100%; max-height: 100%; object-fit: contain;
}

/* The identity row carries the lock date on its far side. */
.ident .ident-end { margin-left: auto; color: var(--ink-faint); }
.work-2up > .pane-view .ident { display: flex; align-items: baseline; gap: .5rem; margin: 0; }

/* "Hold on to" is a control, not a fact, so it stops charging every picture
   in the strip 24px of height for the whole time it is not being used. It
   comes up under the tile on hover or keyboard focus. */
.tray-strip .th { position: relative; }
.tray-strip .th-acts {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 3;
  margin-top: 2px; padding: .25rem .3rem;
  background: var(--g3); border: 1px solid var(--line);
  opacity: 0; pointer-events: none; transition: opacity .12s ease;
}
.tray-strip .th:hover .th-acts,
.tray-strip .th:focus-within .th-acts,
.tray-strip .th.is-held .th-acts { opacity: 1; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) { .tray-strip .th-acts { transition: none; } }

/* The percentage trap: `max-height: 100%` on the image resolved to `none`,
   because its anchor's height was auto-with-a-max and so not definite, and
   a 1024px picture spilled out of a 282px well. Give the anchor a real row
   to fill and let object-fit letterbox inside it. */
.work-2up > .pane-view .well-focus {
  display: grid; grid-template-rows: minmax(0, 1fr); grid-template-columns: minmax(0, 1fr);
  overflow: hidden; min-height: 0;
}
.work-2up > .pane-view .well-focus > a {
  display: block; width: 100%; height: 100%; min-width: 0; min-height: 0;
}
.work-2up > .pane-view .well-focus img.thumb {
  width: 100%; height: 100%; max-width: none; max-height: none; object-fit: contain;
}

/* --- How they sound -----------------------------------------------------
   The cast voice is the settled fact, so it runs the width. The tools and
   the auditions split underneath, and the empty auditions side says what
   will appear there rather than leaving seven columns blank. ----------- */
.voice-cast-wide {
  display: flex; align-items: center; gap: 1.1rem;
  padding: .9rem 1.2rem; margin-bottom: 1.4rem;
}
.voice-cast-wide .voice-who { display: flex; flex-direction: column; gap: .15rem; }
.voice-cast-wide .voice-name { font-size: 1.02rem; color: var(--display-ink); }
.voice-cast-wide .grow { flex: 1 1 auto; }
.voice-state {
  font-family: var(--label); font-weight: 600; font-size: .68rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ok);
}
.voice-grid .aud-empty {
  display: grid; place-items: center; min-height: 11rem; margin: 0;
  border: 1px dashed var(--line); color: var(--ink-ghost); text-align: center;
  padding: 1rem 1.4rem;
}
.voice-body > .tlab { margin-bottom: .8rem; }

/* The cast tier travels with the controls on the right. */
.room-actions .room-tier { display: flex; align-items: center; }
.room-actions .room-tier .dd-select { height: 38px; }

/* === Round two, 2026-09-21 ==============================================
   Brett: "the lockup at the top of the page bothers me, no visual
   hierarchy, this occurs on many surfaces. The width of all elements needs
   to match, no reason to have so much black around the char design."
   ------------------------------------------------------------------- */

/* ONE MEASURE. The panes broke out of the page to the full window
   (`width: calc(100vw - 4rem)`) while every box under them sat inside
   main's 1280px. Nothing lined up with anything. The room is the page's
   width now, like the brief and the voice below it. */
.work.work-2up {
  width: auto; margin: 1rem 0 1.5rem;
  height: auto; min-height: 0;
}

/* The picture gets a real height instead of whatever the viewport had left
   after the chrome, which is what put a small square in a wide black field. */
.work-2up > .pane-view .well-focus { height: clamp(360px, 54vh, 620px); }

/* A masthead is TWO things: the name, and one line saying what it is and
   what the script does with it. Four stacked lines of near-equal weight is
   what "no hierarchy" looks like. */
.room-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap; margin-top: .5rem;
}
.room-id { display: flex; flex-direction: column; gap: .3rem; min-width: 0; }
.room-id > h1 { margin: 0; font-size: 2.5rem; line-height: 1; letter-spacing: .01em; }
.room-byline { margin: 0; font-size: .9rem; color: var(--ink-dim); text-transform: none; }
.room-byline-dot { color: var(--ink-ghost); }
.room-head-row { margin: 0; }

/* Going back is a control, so it sits with the controls. The chevron points
   the way it takes you. */
.btn.room-back-btn {
  height: 38px; padding: 0 .75rem; margin-right: .3rem;
  font-family: var(--label); font-size: .68rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint);
  border-color: var(--line);
}
.btn.room-back-btn:hover { color: var(--ink); border-color: var(--line-strong); }
.btn.room-back-btn .icon { transform: rotate(180deg); }

/* Hear the voice you cast. */
.voice-hear { width: 44px; height: 44px; flex-shrink: 0; }
.voice-hear.is-playing { border-color: var(--ok); color: var(--ok); }
.voice-hear.is-mute { color: var(--ink-ghost); }

/* The two verdicts are a named group so the viewer can clone them; they keep
   the row they always had. */
.vstrip-verdicts { display: contents; }

/* === THE PAGE IS THE PROCESS (Brett, 2026-09-21) ========================
   "The page should be clean and walk you through the process. The rail had
   promise but did not deliver." Five stations in the order the work happens,
   numbered down one spine. Each says its own state; one that cannot start
   yet says what it is waiting for, where the work would go, instead of a box
   going dim somewhere else on the page. ----------------------------- */
/* ONE LEFT EDGE. The spine was a 56px column, and a column takes width
   whether or not anything is in it: every station began 56px right of the
   masthead, so the page had two left edges (Brett, 2026-09-21: "off grid
   and crowded"). The rhythm it gave comes from a full-width rule above each
   heading instead, which costs nothing. */
.stn { display: block; border-top: 1px solid var(--line); padding-top: 1.1rem; }
.stn + .stn { margin-top: 2.4rem; }
.stn:first-of-type { margin-top: 2.2rem; }

.stn-head {
  display: flex; align-items: baseline; gap: .8rem; flex-wrap: wrap;
  margin: 0 0 1.1rem;
}
/* The number IS the heading: same face, size, weight and tracking, one step
   down in ink. Tabular figures so 01 and 10 hold the same width. */
.stn-head .stn-num {
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}
.stn-head h2, .stn-head .stn-num {
  margin: 0; font-family: var(--display); font-size: 1.32rem; font-weight: 500;
  letter-spacing: .06em; text-transform: uppercase; line-height: 1.15;
}
.stn-head h2 { color: var(--display-ink); }
.stn.is-blocked .stn-head h2 { color: var(--ink-faint); }
.stn.is-blocked .stn-head .stn-num { color: var(--ink-ghost); }
.stn-sub { font-size: .85rem; color: var(--ink-faint); text-transform: none; }
.stn-head .grow { flex: 1 1 auto; }
.stn-state {
  font-family: var(--label); font-size: .7rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-ghost);
}
.stn.is-done .stn-state { color: var(--ok); }
.stn.is-warn .stn-state { color: var(--warn); }
.stn.is-here .stn-state { color: var(--ink-dim); }

.stn-work { display: flex; gap: 1.25rem; align-items: stretch; }
/* The grid's one row and column are pinned to the well's own box: with no
   template the implicit row is `auto` and grows to the picture's natural
   height, so the 100% below meant 100% of that, not of the 300px well and
   a portrait sheet showed only its backdrop (vault/47 A7). */
.stn-well {
  flex: 1 1 auto; min-width: 0; height: 300px;
  display: grid; place-items: center; overflow: hidden;
  grid-template: minmax(0, 1fr) / minmax(0, 1fr);
  background: var(--g0); border: 1px solid var(--line);
}
.stn-well.is-empty { border-style: dashed; border-color: var(--line); background: transparent; }
.stn-well > a { display: block; width: 100%; height: 100%; }
.stn-well img { display: block; width: 100%; height: 100%; object-fit: contain; }
.stn-well .gwell { width: 100%; height: 100%; margin: 0; }
.stn-wait {
  margin: 0; max-width: 34ch; padding: 1rem; text-align: center;
  font-size: .9rem; line-height: 1.6; color: var(--ink-ghost);
}
.stn-side {
  width: 340px; flex-shrink: 0; box-sizing: border-box;
  background: var(--g2); border: 1px solid var(--box-line);
  padding: 1.1rem 1.15rem; display: flex; flex-direction: column; gap: .8rem;
}
.stn-side .grow { flex: 1 1 auto; }
.stn-said { margin: 0; font-size: .86rem; line-height: 1.58; color: var(--ink-dim); }
.stn-presses { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.stn-presses form { flex: 1 1 auto; }
.stn-presses form .btn { width: 100%; }

/* The station owns the spacing now, so its contents drop their own. */
.stn > .room-desc { margin-top: 0; }
.stn > .work.work-2up { margin: 0; }
.stn > .room-sec { margin-top: 0; }
.stn > .room-sec > .voice-body { padding-top: 0; }

@media (max-width: 900px) {
  .stn-work { flex-direction: column; }
  .stn-side { width: auto; }
  .stn-well { height: 240px; }
}

/* Why a press did nothing, said on the page rather than in a tooltip. */
.voice-said { font-size: .82rem; color: var(--warn); }

/* ---- The tally: what is generating, and what it is costing -------------
   A tally light in the header. It exists only while something is out
   there: no dot at all when nothing is generating, so the slot appears
   with its feed rather than sitting there as a badge you stop seeing.
   Vermillion is the one colour reserved for live / record, which is what
   a firing render is; it is used here and nowhere else on the bar.
   The rails inside the panel: vermillion = out there now, chrome =
   landed, go and look (deliberately NOT the approved green, because a
   landed generation is undecided and this widget must not read as a
   verdict), warn = it wants you. */
.tally {
  display: flex; align-items: center; justify-content: center; gap: .42rem;
  /* The button rule reaches by tag: undo its box, keep its face. */
  height: auto; background: none; border: 0; padding: .5rem .45rem;
  font-size: .8rem; letter-spacing: .06em; font-weight: 400;
  color: var(--ink-dim); cursor: pointer; border-radius: 3px;
  /* Idle and rolling are the same width, so the help icon beside it does
     not slide sideways every time a job starts or finishes. Two digits of
     count fit; a third would be ninety-nine concurrent jobs. */
  min-width: 34px;
}
/* Idle: a hollow achromatic ring at the help icon's weight. The panel
   holds the spend figures and what landed, which are worth reading when
   nothing is running - so the way in never leaves the bar. Vermillion
   still only ever appears while something is actually out there. */
.tally.is-idle { color: var(--ink-faint); }
.tally.is-idle:hover { color: var(--ink); }
.tally.is-idle .tally-dot {
  background: none; box-shadow: none; animation: none;
  border: 1px solid currentColor; width: 9px; height: 9px;
}
.tally.is-idle .tally-dot::after { display: none; }
.tally:hover { background: var(--g4); color: var(--ink); border-color: transparent; }
.tally-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; position: relative;
  background: var(--live); box-shadow: var(--glow-live);
  animation: tally-breathe 2.4s ease-in-out infinite;
}
.tally-dot::after {
  content: ''; position: absolute; inset: -4px; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--live) 55%, transparent);
  animation: tally-ring 2.4s ease-in-out infinite;
}
@keyframes tally-breathe { 0%, 100% { opacity: .62 } 50% { opacity: 1 } }
@keyframes tally-ring {
  0%, 100% { transform: scale(.82); opacity: 0 }
  45% { opacity: .5 }
  70% { transform: scale(1.25); opacity: 0 }
}
/* The last one lands: red turns chrome for a few seconds, then the whole
   cluster goes. You get told it finished without having to be watching. */
.tally.is-settling { color: var(--chrome); }
.tally.is-settling .tally-dot {
  background: var(--chrome); box-shadow: var(--glow-chrome); animation: none;
}
.tally.is-settling .tally-dot::after { display: none; }
.tally-count { font-variant-numeric: tabular-nums; }
@media (prefers-reduced-motion: reduce) {
  .tally-dot, .tally-dot::after { animation: none; }
}
/* Touch: the disc stays 7px and the button grows around it to a 44pt
   target. The dot is the signal, not the hit area. */
@media (pointer: coarse) {
  .tally { min-height: 44px; min-width: 44px; justify-content: center; }
}

.tally-anchor { position: relative; }
.tally-panel {
  position: absolute; top: calc(100% + 9px); right: 0; width: 384px;
  max-width: calc(100vw - 32px);
  background: var(--g2); border: 1px solid var(--line-strong); border-radius: 4px;
  box-shadow: 0 18px 40px -12px rgba(0, 0, 0, .72), 0 2px 8px rgba(0, 0, 0, .4);
  z-index: 40; overflow: hidden; transform-origin: top right;
}
/* The entrance runs when the panel OPENS, never on a refresh. The panel
   replaces itself every few seconds while it is on screen, and an
   animation on the element itself restarts with each of those: the panel
   fades from nothing several times a minute while you are reading it.
   tally.js adds this class to the first render only. */
.tally-panel.is-opening {
  animation: tally-panel-in .16s cubic-bezier(.2, .7, .3, 1);
}
@keyframes tally-panel-in {
  from { opacity: 0; transform: translateY(-6px) scale(.985) }
  to { opacity: 1; transform: none }
}
.tally-panel::before {
  content: ''; position: absolute; top: -1px; left: 0; right: 0; height: 1px;
  opacity: .55; background: var(--spectrum);
}
/* The list scrolls; the money bar and the foot stay put, so the two
   numbers that matter are never the thing you scrolled past. */
.tally-scroll {
  max-height: min(58vh, 404px); overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent;
}
.tally-scroll::-webkit-scrollbar { width: 7px; }
.tally-scroll::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 4px; }

.tally-grp {
  position: sticky; top: 0; z-index: 1; background: var(--g2);
  font-family: var(--label); text-transform: uppercase; letter-spacing: .14em;
  font-size: .68rem; color: var(--ink-faint);
  padding: .72rem .95rem .38rem;
  display: flex; justify-content: space-between; align-items: baseline;
}
.tally-grp .n { color: var(--ink-ghost); font-variant-numeric: tabular-nums; }
.tally-grp + .tally-row { border-top: 1px solid var(--line); }

.tally-row {
  display: grid; grid-template-columns: 3px 1fr auto; gap: 0 .7rem;
  align-items: start; padding: .62rem .95rem .62rem .82rem;
  border-bottom: 1px solid var(--line); position: relative;
}
.tally-row:last-child { border-bottom: 0; }
.tally-row:hover { background: var(--g3); }
.tally-row .rail {
  grid-row: 1 / -1; width: 3px; align-self: stretch; border-radius: 2px;
  background: var(--ink-ghost);
}
.tally-row.is-rolling .rail { background: var(--live); }
.tally-row.is-landed .rail { background: var(--chrome); }
.tally-row.is-failed .rail { background: var(--warn); }

.tally-what { color: var(--ink); font-size: .93rem; line-height: 1.25; }
.tally-what a { color: inherit; }
.tally-what a:hover { color: var(--chrome); }
.tally-what .code {
  font-family: var(--mono); font-size: .82em; color: var(--chrome); margin-right: .35em;
}
.tally-sub {
  grid-column: 2; color: var(--ink-faint); font-size: .78rem;
  font-family: var(--label); letter-spacing: .03em; margin-top: .16rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tally-sub .sep { color: var(--ink-ghost); margin: 0 .3rem; }
.tally-right {
  grid-column: 3; grid-row: 1; align-self: start; text-align: right;
  display: flex; flex-direction: column; align-items: flex-end; gap: .16rem;
  padding-top: .05rem;
}
.tally-time {
  font-family: var(--mono); font-size: .78rem; color: var(--ink-dim);
  font-variant-numeric: tabular-nums;
}
/* Money is read down a column, so it is tabular; but it is not a code, so
   it stays in the body face rather than the mono one. */
.tally-cost {
  font-size: .82rem; color: var(--ink-faint);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.tally-row.is-landed .tally-cost { color: var(--ink-dim); }
.tally-cost .est { color: var(--ink-ghost); }
.tally-new {
  font-family: var(--label); text-transform: uppercase; letter-spacing: .12em;
  font-size: .62rem; color: var(--chrome);
  border: 1px solid color-mix(in srgb, var(--chrome) 45%, transparent);
  border-radius: 2px; padding: .02rem .28rem; margin-left: .4rem; vertical-align: .08em;
}
.tally-err {
  grid-column: 2; color: var(--warn); font-size: .78rem;
  font-family: var(--label); letter-spacing: .02em; margin-top: .2rem;
  overflow-wrap: anywhere;
}
.tally-acts { grid-column: 2; margin-top: .45rem; display: flex; gap: .4rem; }
.tally-acts form { display: inline; }
.tally-mini {
  height: auto; font-family: var(--label); text-transform: uppercase;
  letter-spacing: .1em; font-size: .66rem; font-weight: 400;
  background: none; border: 1px solid var(--line-strong); color: var(--ink-dim);
  border-radius: 2px; padding: .2rem .45rem;
}
.tally-mini:hover { border-color: var(--chrome); color: var(--chrome); }
.tally-row .stop { position: absolute; right: .95rem; bottom: .55rem; opacity: 0; transition: opacity .12s; }
.tally-row.is-rolling:hover .stop, .tally-row .stop:focus-visible { opacity: 1; }

.tally-money { display: flex; border-top: 1px solid var(--line-strong); background: var(--g3); }
.tally-money div { flex: 1; padding: .62rem .7rem .68rem; border-right: 1px solid var(--line); }
.tally-money div:last-child { border-right: 0; }
.tally-money .k {
  display: block; font-family: var(--label); text-transform: uppercase;
  letter-spacing: .1em; font-size: .62rem; color: var(--ink-ghost); margin-bottom: .16rem;
}
.tally-money .v { font-size: .98rem; color: var(--ink); font-variant-numeric: tabular-nums; }
.tally-money .v.flight { color: var(--ink-dim); }
.tally-money .v small { font-size: .66em; color: var(--ink-ghost); margin-left: .15em; }
.tally-foot {
  padding: .55rem .95rem .62rem; display: flex; justify-content: space-between;
  align-items: center; border-top: 1px solid var(--line);
  font-family: var(--label); font-size: .74rem; letter-spacing: .05em;
}
.tally-foot .cap { color: var(--ink-ghost); }
.tally-empty { padding: 1.1rem .95rem 1.2rem; color: var(--ink-faint); font-size: .86rem; }

/* ---- 3D models brought in: where each one has got to (model_progress.html) ---- */
.model-progress-head { margin: 0 0 .5rem; display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.model-progress-clear { display: inline; margin-left: auto; }
.model-progress-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; }
.model-progress-row { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; font-size: .9rem; }
.model-progress-row.is-failed .dim, .model-progress-row.is-stopped .dim { color: var(--warn, #c9a227); }
.model-drop { margin-top: .6rem; }
.stn-side .model-progress { margin-top: .6rem; }
.model-drop .drop-zone { aspect-ratio: auto; min-height: 0; height: auto; padding: .7rem 1rem; flex-direction: row; gap: .6rem; justify-content: flex-start; text-align: left; }

/* Rename in place (partials/project_rename.html). The pencil sits beside
   the heading that names the project; the form opens as a layer under it
   rather than into the row, because rooms like develop measure their panes
   from a fixed title-row height. It anchors to the heading's row (the
   nearest positioned ancestor: .title-row on the overviews, .dev-head in
   develop), not the pencil, so it opens at the row's left edge whatever the
   title's length and never runs off the right. */
.title-row { position: relative; display: flex; align-items: center; gap: .5rem; }
.rename-inline { align-self: center; }
.rename-inline > summary { list-style: none; cursor: pointer; }
.rename-inline > summary::-webkit-details-marker { display: none; }
.rename-inline > form {
  position: absolute; top: calc(100% + .4rem); left: 0; z-index: 20;
  display: flex; gap: .5rem; align-items: center;
  padding: .6rem; background: var(--surface-raise); border: 1px solid var(--rule);
  border-radius: 6px; box-shadow: 0 8px 24px rgba(0,0,0,.35);
}
.rename-inline input[type=text] { width: 22rem; max-width: calc(100vw - 10rem); }

/* THE NEXT TAKE, FROM A GUIDE (vault/35; redrawn vault/50). The switch sits
   above the make row; choosing the guide swaps the start-picture make row
   for the rows of what the guided take is made from. */
.bench-from { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin: 0 0 .6rem;
  font-family: var(--mono); font-size: .66rem; color: var(--ink-dim); text-transform: lowercase; }
.bench-from label { display: inline-flex; align-items: center; gap: .3rem; cursor: pointer; }
.bench-from label:has(input:checked) { color: var(--display-ink); }
.bench-from label:has(input:disabled) { opacity: .45; cursor: default; }
.bench-anim { display: none; }
.bench-from:has(input[value="animatic"]:checked) ~ .bench-anim { display: flex; flex-direction: column; gap: .6rem; }
.bench-from:has(input[value="animatic"]:checked) ~ .bench-make { display: none; }
/* the start row already says which picture it is */
.bench-from:has(input[value="animatic"]:checked) ~ .bench-plate { display: none; }
/* One row per thing the take is made from, in the order it is sent. The
   row's keyline wears the colour that input wears on the canvas (theme.js:
   guide, start, keys, cast, place), so the bench and the motion prompt read
   as the same list. An empty row is one line. */
.bench-in { display: flex; flex-direction: column; }
.bench-in-row { display: grid; grid-template-columns: 3.4rem minmax(0, 1fr); gap: .5rem; align-items: start;
  padding: .45rem 0; border-top: 1px solid var(--rule); }
.bench-in-row:last-child { border-bottom: 1px solid var(--rule); }
.bench-in-k { font-family: var(--mono); font-size: .66rem; text-transform: lowercase; color: var(--display-ink);
  border-left: 3px solid var(--rc, var(--rule)); padding-left: .45rem; line-height: 1.5; }
.bench-in-row.is-empty .bench-in-k { color: var(--ink-faint); border-left-color: color-mix(in srgb, var(--rc, #666) 45%, transparent); }
.bench-in-v { display: flex; flex-wrap: wrap; gap: .4rem; align-items: flex-start; min-width: 0; }
.bench-in-pic { margin: 0; display: flex; flex-direction: column; gap: .2rem; width: 4.6rem; min-width: 0; }
.bench-in-pic img { display: block; width: 100%; aspect-ratio: 16 / 10; object-fit: cover; object-position: 50% 20%;
  border: 1px solid var(--rule); background: #000; }
.bench-in-pic figcaption { display: flex; align-items: center; gap: .25rem; min-width: 0; font-size: .58rem; color: var(--ink-dim); }
.bench-in-pic figcaption span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bench-in-pic figcaption input { margin: 0; flex: 0 0 auto; }
.bench-in-pic:has(input:not(:checked)) img { opacity: .3; }
.bench-in-none { font-size: .76rem; color: var(--ink-faint); line-height: 1.5; }
.bench-in-pick { flex: 1 1 6rem; min-width: 0; max-width: 100%; font-size: .72rem; align-self: flex-start; }
.bench-render { gap: .5rem; }
.bench-price { font-size: .72rem; opacity: .7; padding-left: .5rem; border-left: 1px solid currentColor; }

/* THE KEYS BAND (vault/50): the boardmatic's drawings where the cut has them
   now, and the finished picture standing at each. Two tracks on one scale,
   the shot's own length: a thing at 3.1 s sits 3.1 s along. The tracks are
   inset by half a key's width so a key at 0 s is not cut off. Law 8: white
   and grey; the one colour is the canvas's own key and start colours on the
   keyline of a picture that is SENT, the same colours those inputs wear in
   the next take's rows. Nothing is drawn over a picture. */
.bench-label { font-family: var(--mono); font-size: .66rem; text-transform: lowercase; color: var(--display-ink); }
.bench-keys { margin-top: .7rem; padding: .55rem 0 .5rem; border-top: 1px solid var(--rule); min-width: 0; }
.bench-keys-head, .bench-passes-head, .bench-keys-loose { margin: 0 0 .35rem; display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; }
.bench-keys-say { font-size: .78rem; color: var(--ink-dim); }
.bench-keys-say .mono { font-size: .68rem; color: var(--ink); }
.bench-keys-count { margin-left: auto; font-size: .66rem; color: var(--ink-dim); white-space: nowrap; }
/* 7.6rem a second: two keys half a second apart (animatic.CLOSE_S) are
   3.8rem apart, which is a key's own width, so keys that stand never touch.
   A six second shot fits the full bench; a longer one scrolls sideways. */
.bench-keys-scroll { overflow-x: auto; scrollbar-width: thin; padding-bottom: .2rem; }
.bench-keys-track { position: relative; margin: 0 2.2rem; min-width: calc(var(--span, 6) * 7.6rem); }
.bench-keys-drawn { height: 4.4rem; }
.bench-keys-made { height: 7.6rem; border-top: 1px dashed var(--rule); }
.bench-kd { position: absolute; top: .3rem; margin: 0; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: .15rem; cursor: zoom-in; }
.bench-kd.is-low { top: 2.2rem; }
.bench-kd img { display: block; width: 2.6rem; aspect-ratio: 16 / 9; object-fit: cover; background: #fff; border: 1px solid var(--rule); }
.bench-kd figcaption { font-size: .56rem; color: var(--ink-dim); line-height: 1; }
.bench-kk { position: absolute; top: .45rem; transform: translateX(-50%); width: 3.8rem; display: flex; flex-direction: column; align-items: center; gap: .2rem; }
.bench-kk img { display: block; width: 3.6rem; aspect-ratio: 16 / 9; object-fit: cover; border: 1px solid var(--rule); background: #000; }
.bench-kk.is-sent img { border-color: #E0B84A; box-shadow: 0 0 0 1px #E0B84A; }
.bench-kk.is-start img { border-color: #E8875A; box-shadow: 0 0 0 1px #E8875A; }
.bench-kk-cap { font-size: .56rem; color: var(--ink-dim); white-space: nowrap; line-height: 1.2; max-width: 3.7rem; overflow: hidden; text-overflow: ellipsis; }
.bench-kk-cap.is-on { color: var(--display-ink); }
.bench-kk-verdict { display: inline-flex; gap: 1px; }
.bench-kk-verdict form, .bench-kk form { display: inline-flex; margin: 0; }
/* 20 by 24 px each: three abreast still fit between keys half a second
   apart (they were 18 by 18, too small to hit). */
.bench-kk .btn-icon { width: 1.25rem; height: 1.5rem; min-height: 0; padding: 0; }
.bench-kk .btn-icon .icon { width: .8rem; height: .8rem; }
.bench-kk.is-none { top: .7rem; }
.bench-kk .bench-kk-add { width: 2.8rem; height: 1.75rem; border-style: dashed; }
/* close behind the key before: small, on the line under the keys that stand */
.bench-kk.is-close { top: 5.2rem; width: 2.2rem; opacity: .75; }
.bench-kk.is-close img { width: 2rem; }
.bench-kk.is-close .bench-kk-add { width: 1.6rem; height: 1.1rem; }
.bench-kk-box { display: grid; place-items: center; width: 3.6rem; aspect-ratio: 16 / 9; border: 1px solid var(--rule);
  font-family: var(--mono); font-size: .54rem; line-height: 1.25; color: var(--ink-faint); text-align: center; }
.bench-keys-loose { margin: .4rem 0 0; align-items: center; }
.bench-keys-loose img { display: block; height: 1.9rem; width: auto; border: 1px solid var(--rule); }

/* The inputs shelf: guides and passes, apart from the takes. */
.bench-passes { margin-top: .5rem; }
.bench-takes-head { margin-top: .6rem; padding-top: .5rem; border-top: 1px solid var(--rule); }
.bench-takes-head + .bench-takes { border-top: 0; margin-top: 0; padding-top: 0; }
.bench-passes .bench-takes { border-top: 0; margin-top: 0; padding-top: 0; }
.bench-pass { flex: 0 0 132px; display: flex; flex-direction: column; gap: .2rem; }
.bench-pass .bench-take { flex: 0 0 auto; }
.bench-pass .bench-take-line { color: var(--ink-dim); min-height: 1.4rem; }
.bench-pass.is-guide .bench-take { opacity: 1; }
.bench-pass.is-guide .bench-take img { border-color: #8FBEDD; }
.bench-pass-say { color: #8FBEDD; }
.bench-pass-use { width: 1.5rem; height: 1.3rem; min-height: 0; padding: 0; margin-left: auto; }
.bench-pass-use .icon { width: .8rem; height: .8rem; }

/* The timing a clip was made on (vault/52, INDY_BOARD_RETIME). A drawing
   dragged on the timeline wears the timeline's moved mark, a second ring
   (timeline.html .clip-key.drag.moved), and says where the board had it. A
   take, a guide or a pass made on the earlier timing says so in grey, under
   its number; a guide or pass has a clock to bring it to this timing. */
.bench-keys-drawn.has-moved { height: 5.2rem; }
.bench-kd.is-moved img { box-shadow: 0 0 0 1px var(--g1), 0 0 0 2px var(--ink-faint); }
.bench-kd-moved { font-size: .56rem; color: var(--ink-dim); line-height: 1; white-space: nowrap; }
.bench-take-time { display: flex; align-items: center; gap: .3rem; min-height: 1.3rem; font-size: .62rem; color: var(--ink-faint); white-space: nowrap; }
.bench-pass-time { width: 1.5rem; height: 1.3rem; min-height: 0; padding: 0; margin-left: auto; }
.bench-pass-time .icon { width: .8rem; height: .8rem; }
.bench-in-time { flex-basis: 100%; }

/* The take's own clock: where it is parked, and a mark for every note that
   has a second. The marks sit ABOVE the bar, never on the picture. */
.bench-scrub { position: relative; height: 1.25rem; margin-top: .3rem; }
.bench-scrub-bar { position: absolute; left: 0; right: 0; bottom: .2rem; height: 3px; border-radius: 2px; background: var(--rule); overflow: hidden; cursor: pointer; }
.bench-scrub-now { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: var(--display-ink); }
.bench-scrub-mark { all: unset; position: absolute; bottom: .35rem; transform: translateX(-50%); cursor: pointer; color: var(--ink); line-height: 0; }
.bench-scrub-mark:hover { color: var(--display-ink); }
.bench-scrub-mark .icon { width: .85rem; height: .85rem; }
.bench-note-at { font-size: .62rem; margin-left: .3rem; color: var(--ink-dim); text-transform: none; letter-spacing: 0; }
.bench-controls .js-open-feedback:has(.bench-note-at) { width: auto; padding: 0 .45rem; }
.bench-tc { all: unset; flex: 0 0 auto; cursor: pointer; font-size: .66rem; color: var(--display-ink); border: 1px solid var(--rule);
  border-radius: 999px; padding: .15rem .5rem; white-space: nowrap; }
.bench-tc:hover { border-color: var(--ink-dim); }
.bench-anim-dirs summary { cursor: pointer; font-family: var(--mono); font-size: .66rem; color: var(--ink-dim); }
.bench-anim-dirs pre { white-space: pre-wrap; font-family: var(--mono); font-size: .62rem; line-height: 1.5;
  color: var(--ink); max-height: 18rem; overflow: auto; margin: .4rem 0 0; }
.board-lines-beyond { grid-column: 1 / -1; flex-basis: 100%; padding: .35rem .75rem; border: 1px solid var(--line); border-radius: 8px; font-size: .8rem; }
.board-lines-beyond[open] { display: flex; flex-wrap: wrap; gap: .45rem 1rem; align-items: center; }
.board-lines-beyond summary { cursor: pointer; flex-basis: 100%; }
.board-lines-beyond form { display: inline-flex; gap: .4rem; align-items: center; margin: 0; }

/* ---- Out-of-date notes on a board panel ----
   A pill that holds a sentence, not a label: sentence case, wraps, and the
   words can be selected and copied. The fix button sits beside it, in the
   same plain voice. */
.pill-sentence {
  text-transform: none; letter-spacing: .01em; font-weight: 500;
  font-size: .75rem; line-height: 1.4;
  white-space: normal; border-radius: .7rem;
  user-select: text; -webkit-user-select: text; cursor: text;
}
.stale-fix { display: inline-flex; margin: 0; }
.stale-fix-btn {
  height: 26px; padding: 0 .7rem;
  text-transform: none; letter-spacing: .01em; font-weight: 500;
  font-size: .75rem; border-radius: 999px;
}
