/* =====================================================================================================
   PRODUCER SKIN — OpenArt's composition, Senova's palette, in WHICHEVER THEME THE CUSTOMER CHOSE.
   =====================================================================================================

   R1 SHIPPED A DARK ISLAND, AND THAT WAS THE MISTAKE THIS FILE EXISTS TO UNDO.

   R1 copied OpenArt's PALETTE — a forced near-black canvas — when the thing worth copying was their
   COMPOSITION: panel proportions, card anatomy, density, one-accent discipline, imagery-forward stage.
   Rob runs the studio in LIGHT, so the Producer opened as a black rectangle inside an ivory app: "it's
   like you built a new site inside the producer." Every rule below now resolves through the studio's own
   `data-theme` tokens, and the sheet designs BOTH themes rather than overriding one of them.

   THE SCOPE CLASS IS STILL A SCOPE, NOT A MODE. `body.producer-skin` keeps this composition off the
   other tools; it no longer carries a colour opinion of its own. Nothing here restyles the studio's
   sidebar or topbar any more — the shell belongs to the shell, which is most of what "part of Senova"
   means in practice.

   WHAT WE STILL DO NOT COPY: their magenta (ours is Senova ember — the discipline of one accent, not the
   hue), and every dollar figure they print. Credits only, everywhere, forever.

   MOTION IS THE OTHER HALF OF THE FEEL. Their vocabulary is three tweens — a ~200ms opacity cross-fade,
   a rolling number, and streaming growth on the working card. EVERYTHING ELSE IS A CUT: tab switches,
   stage swaps, menu opens, modal opens. Adding easing where they have none is what made V1 feel like a
   settings panel. The instant response IS the feel, so restraint here is a feature, not laziness.

   ONE COLOUR RULE RUNS THROUGH THE WHOLE SHEET: chrome is tokenised, and anything that sits ON A
   PHOTOGRAPH stays literal. A caption burned over a still, a scrim under a card label, the black behind
   a letterboxed player — those read identically in either theme because the image, not the page, is what
   they sit on. Theming them would be theming the film.
   ===================================================================================================== */

/* =====================================================================================================
   §62 · THE PRODUCER IS DARK BY DEFAULT, AND NEUTRAL — and this REVERSES the rule at the top of the file.
   =====================================================================================================

   ⚠️ READ THIS BEFORE "FIXING" IT BACK. The header above records R1's dark Producer as "the mistake this
   file exists to undo" — Rob, in a light studio, read a black rectangle as "you built a new site inside
   the producer". That was true then and the reasoning was sound. It has been OVERTAKEN: Rob put our
   specimen next to OpenArt's editor and his verdict was that ours "looks nothing like OpenArt", with the
   theme named first. OpenArt is a near-black studio; a cutting room is dark because the picture is the
   only thing that should be bright. So the Producer now defaults to DARK, deliberately, on Rob's
   instruction after seeing both side by side.

   The light palette is NOT deleted — it moves to `[data-theme="light"]`, so a customer who runs the studio
   in light still gets a coherent Producer rather than an unstyled one.

   AND THE NEUTRALS ARE NEUTRAL NOW. The old dark was a WARM palette — #121110 / #1A1917 / #2A2825 are
   brown-tinted charcoals, and against OpenArt's #0b0b0d they read as sepia. Warmth was carrying an
   accent's job. Every ground token below is a true neutral with a faint cool cast, so the ONE accent is
   the only colour in the room.
   ===================================================================================================== */
body.producer-skin {
  /* 🔵 §70 — THE RAMP IS shadcn/ui's OWN DARK SCALE, in its own units.
     Rob: the previous look reads as "AI slop", and he wants a real foundation — 21st.dev, which is a
     shadcn REGISTRY (its components are shadcn/ui conventions, React + Tailwind, distributed in shadcn
     registry format). So the source of truth underneath 21st.dev is shadcn itself, and these are its
     published `.dark` values copied verbatim rather than eyeballed:
         --background oklch(.145 0 0) · --card oklch(.205 0 0) · --secondary/muted/accent oklch(.269 0 0)
         --foreground oklch(.985 0 0) · --muted-foreground oklch(.708 0 0)
         --border oklch(1 0 0 / 10%) · --input oklch(1 0 0 / 15%) · --ring oklch(.556 0 0)
     Chroma is ZERO on every ground and text step. That is the Geist/Linear restraint: the interface is
     greyscale, and colour is an event. */
  --pd-canvas: oklch(0.145 0 0);            /* --background: the room itself */
  --pd-panel: oklch(0.178 0 0);             /* columns — one step, between background and card */
  --pd-card: oklch(0.205 0 0);              /* --card: cards, menus, the composer */
  --pd-card-2: oklch(0.269 0 0);            /* --secondary / --muted / --accent: hover, raised */
  --pd-sunk: oklch(0.165 0 0);              /* inputs and wells — a step DOWN from a card */
  /* HAIRLINES, NOT OUTLINES. OpenArt separates with SPACE and a ~8% line; it almost never draws a border.
     Both of these are deliberately low-contrast so a card reads as a surface, not as a boxed-in region. */
  --pd-line: oklch(1 0 0 / 10%);            /* --border */
  --pd-line-2: oklch(1 0 0 / 15%);          /* --input: the stronger line, for a field or a selected row */
  --pd-ring: oklch(0.556 0 0);              /* --ring: focus is NEUTRAL, not branded */
  --pd-skel: #141417;
  --pd-skel-2: #1C1C21;
  --pd-ghost: #2A2A31;
  --pd-scrollthumb: #26262C;
  --pd-scrollthumb-h: #35353D;

  /* text ramp — hierarchy by luminance, on a neutral grey axis */
  --pd-ink: oklch(0.985 0 0);               /* --foreground */
  --pd-ink-2: oklch(0.83 0 0);
  --pd-ink-3: oklch(0.708 0 0);             /* --muted-foreground */
  --pd-ink-4: oklch(0.58 0 0);

  /* 🔵 §65 — THE ONE ACCENT IS THE SENOVA LOGO (blue→indigo→violet, sampled from the mark; see
     senova-ui.css). It means EXACTLY two things and nothing else: the single primary action in a context,
     and the active view pill. Everything else is ghost. (OpenArt reserves GREEN for the upgrade/credits
     chip alone — `--pd-green` is kept for that job and is not a second accent.) */
  /* 🔴 §64 — DERIVED, NOT DECLARED. §62 hard-coded magenta here while the shell's --sn-accent stayed
     orange, so the running app had TWO accents: the Producer's own components (the active view pill, the
     primary button) went magenta while the sidebar rail, the Generate dot and the Admin chip stayed
     orange. Rob spotted it immediately. The Producer no longer has an opinion about WHICH colour the
     accent is — only about how it uses it (one primary action, one active view pill). Change
     `--sn-accent` in senova-ui.css and everything follows in one edit.
     `color-mix` gives us the dim/line tints without restating the hue. */
  /* 🔴🔴 §72 — THE PRIMARY IS NEUTRAL. THIS RETIRES BRAND COLOUR FROM THE UI ENTIRELY.
     Rob, closing the "AI slop" thread: *"the primary buttons become NEUTRAL/near-white — shadcn's real
     `--primary` is oklch(0.922 0 0) … retire the brand color from UI fills entirely; the logo stays the
     ONLY brand-coloured element in the whole studio."*

     Those are shadcn's actual dark values, not an approximation of them:
       --primary            oklch(0.922 0 0)   near-WHITE, chroma 0
       --primary-foreground oklch(0.205 0 0)   near-black ink ON it
       --accent             oklch(0.269 0 0)   a GREY, used for hover — never a colour
     and light inverts primary to near-black, which is the Vercel move exactly.

     ⚠️ THE TRAP THIS RE-OPENS, AND IT HAS ALREADY BITTEN ONCE HERE: changing an accent changes what can
     SIT on it. Six rules hard-coded `color: #fff` on an accent fill — white on white is an invisible
     button. Every one is now `var(--pd-on-accent)` and there is a contrast test that fails if a seventh
     ever appears.

     ⚠️ AND `--pd-accent` IS NO LONGER A COLOUR, so anything that used it to MEAN "brand" now means
     "primary". That is intended: `--sn-brand-grad` and `--sn-accent` still exist, still hold the real
     mark, and belong to the logo and to nothing else. Do not reach for them from a control.

     The tints stay TRANSLUCENT rather than becoming shadcn's opaque greys, because they are layered over
     cards, sunken wells and the canvas alike here; an opaque fill would band against two of the three. */
  /* §74 — DERIVED FROM THE BRAND AGAIN. §72 pinned these to shadcn's neutral primary; Rob has taken
     the studio back to the logo colours with a glossy finish, so they go back to deriving from
     `--sn-accent` and the Producer once more has no opinion about WHICH colour the accent is. */
  --pd-accent: var(--sn-accent);
  --pd-accent-hi: var(--sn-accent-ink);
  --pd-accent-dim: color-mix(in srgb, var(--sn-accent) 14%, transparent);
  --pd-accent-line: color-mix(in srgb, var(--sn-accent) 38%, transparent);
  /* focus and selection carry the brand again — Rob's "tasteful brand presence … active states, focus" */
  --pd-ring: var(--sn-accent);
  /* A FLAT FILL, NOT A GRADIENT. A 3-stop gradient on a small pill is a texture the eye reads as
     decoration; OpenArt's accent is one flat colour. Kept as a token so nothing downstream has to change. */
  /* 🔴 §70 — THE GRADIENT IS GONE FROM EVERY SURFACE, AND THAT REVERSES §65/§68 DELIBERATELY.
     Rob: a purple gradient on every button IS the "AI slop" tell. Vercel, Linear and OpenArt are all
     essentially monochrome; the brand appears as ONE solid, sparingly. So this token — which every accent
     surface in the Producer already derives from — is now simply the solid. One line retires the gradient
     everywhere it was applied, with no hunting, which is the payoff for having centralised it in §65.
     ⚠️ `--sn-brand-grad` still EXISTS and is still the mark's real ramp. It belongs to the logo and to
     nothing else. Do not put it back on a control. */
  --pd-stage-bg: oklch(0.105 0 0);   /* §83: one step below the canvas — the film sits on black */
  --pd-grad: var(--pd-accent);
  --pd-on-accent: #FFFFFF;   /* §74: white on the gloss — 4.60:1 under the glyph band, verified */

  /* money + state — readable on near-black. Green is the credits/upgrade colour, NOT a second accent. */
  --pd-green: #3FBF6A;  --pd-green-ink: #7DE8A4;  --pd-green-dim: rgba(63, 191, 106, .14);  --pd-green-line: rgba(63, 191, 106, .34);
  --pd-violet: #8B7FD4; --pd-violet-ink: #B6AEE8; --pd-violet-dim: rgba(139, 127, 212, .14); --pd-violet-line: rgba(139, 127, 212, .30);
  --pd-amber: #D8A24A;
  --pd-red: #E2564A;    --pd-red-ink: #F08A80;
  --pd-warm-ink: var(--sn-accent-ink);   /* §74: a tinted link is brand presence at zero cost */

  --pd-float: rgba(20, 20, 23, .95);
  --pd-stop-bg: #ECECEC;
  --pd-stop-ink: #111111;
  --pd-toast-bg: #F0F0F2;
  --pd-toast-ink: #131316;

  --pd-elev-1: 0 2px 8px rgba(0, 0, 0, .55);
  --pd-elev-2: 0 8px 28px rgba(0, 0, 0, .55);
  --pd-elev-3: 0 16px 44px rgba(0, 0, 0, .66);

  /* radii (adopted exactly from the spec's §2 table) */
  --pd-r-card: 10px;
  --pd-r-player: 12px;
  --pd-r-modal: 16px;
  --pd-r-hero: 28px;

  /* ---- §61 · THE CONTROL SCALE — TWO HEIGHTS, AND EVERY CONTROL IS ONE OF THEM ------------------
     Measured in a real browser against the shipped sheet: a tab was 26px, a utility button 28px, a phase
     pill 30px and a .pd-btn 32px. Four heights for four families read as one row. Nothing was wrong with
     any single number; what was wrong is that there was no number.
     SM is the chrome tier (tabs, utility, phase pills — things that navigate).
     MD is the action tier (anything that commits, and the fields it sits beside). */
  /* §70: shadcn's own button heights — `h-8` (sm) and `h-9` (default). Ours were 28/32, a hair tighter
     than the system Rob is asking us to actually use. */
  --pd-h-sm: 32px;
  --pd-h-md: 36px;
  --pd-radius: 0.625rem;                    /* --radius */
  --pd-r-ctl: calc(var(--pd-radius) - 2px); /* `rounded-md` — what every control uses */
  /* `shadow-xs`. Rob asked for stacked shadows, not heavy drops; shadcn's control shadow is one hairline. */
  --pd-shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.28);

  /* the only tween that is not a cut */
  --pd-fade: 200ms;

  color: var(--pd-ink);
}

/* 🔴 §110 — THE PRODUCER HOME'S PANEL IS WHITE IN LIGHT, NOT THE PAGE'S OFF-WHITE.
   Measured on staging: `.pd-root` paints `--pd-canvas`, and in light that token is #FAF9F5 — the SAME
   value as the page background — over a `#stage` the system deliberately sets to #FFFFFF. Two things
   went wrong at once and together they made the bug look random:
     · the panel was the same colour as the page, so it read as no panel at all; and
     · `.pd-root` is only as tall as the viewport while the home scrolls well past it, so BELOW its box
       the white stage showed through — which is why the lower rows (Social clips) were pure white and
       everything above them was beige. Not a gradient, not a token drifting: one box ending.
   Scoped to `.is-lib` (the home) because that is the surface Rob reported; the workspace keeps its own
   canvas, which is a room and is supposed to recede. Light only — dark is untouched by design. */
[data-theme="light"] body.producer-skin .pd-root.is-lib { background: var(--sn-surface); }

/* ---- LIGHT — kept whole, now an OPTION rather than the default (see §62 above) --------------------- */
[data-theme="light"] body.producer-skin {
  --pd-canvas: #FAF9F5;
  --pd-panel: #F5F4EE;
  --pd-card: var(--sn-surface);
  --pd-card-2: #F0EEE6;
  --pd-sunk: #F5F4EE;
  --pd-line: rgba(0, 0, 0, .09);
  --pd-line-2: rgba(0, 0, 0, .14);
  --pd-skel: #EFEDE4;
  --pd-skel-2: #F8F7F2;
  --pd-ghost: #D8D5CA;
  --pd-scrollthumb: #DAD7CC;
  --pd-scrollthumb-h: #C4C0B4;

  --pd-ink: var(--sn-ink);
  --pd-ink-2: var(--sn-ink-2);
  --pd-ink-3: #75736D;
  --pd-ink-4: #8A8984;

  /* §72 — LIGHT INVERTS THE PRIMARY to near-BLACK on near-white, which is shadcn's own light `--primary`
     and the reason a Vercel page reads as monochrome in either theme. */
  --pd-accent: var(--sn-accent);
  --pd-accent-hi: var(--sn-accent-ink);
  --pd-accent-dim: color-mix(in srgb, var(--sn-accent) 10%, transparent);
  --pd-accent-line: color-mix(in srgb, var(--sn-accent) 34%, transparent);
  --pd-ring: var(--sn-accent);
  /* 🔴 §70 — THE GRADIENT IS GONE FROM EVERY SURFACE, AND THAT REVERSES §65/§68 DELIBERATELY.
     Rob: a purple gradient on every button IS the "AI slop" tell. Vercel, Linear and OpenArt are all
     essentially monochrome; the brand appears as ONE solid, sparingly. So this token — which every accent
     surface in the Producer already derives from — is now simply the solid. One line retires the gradient
     everywhere it was applied, with no hunting, which is the payoff for having centralised it in §65.
     ⚠️ `--sn-brand-grad` still EXISTS and is still the mark's real ramp. It belongs to the logo and to
     nothing else. Do not put it back on a control. */
  /* 🔴 §130 — LIGHT IS LIGHT, and this REVERSES §83's light value on Rob's live verdict. §83 kept the
     whole centre track near-black in light ("a film is never judged against paper") — and Rob, switching
     the studio to light, read the result as a defect: the middle of the app stays a black rectangle, and
     the stage's own chrome (the premiere header, the transport) sits its now-dark ink on it illegibly.
     The place a film must stay on black is the FRAME, and `.tl-screen`/the letterbox already paint their
     own `--sn-bg-deep` behind every pixel of video — that survives. The TRACK is chrome, and chrome
     follows the theme: one quiet step below the canvas, the same relationship dark keeps. */
  --pd-stage-bg: #F1EFE9;
  --pd-grad: var(--pd-accent);
  --pd-on-accent: #FFFFFF;   /* §74: the gloss is dark in BOTH themes, so the ink is white in both */

  --pd-green: #2E9E56;  --pd-green-ink: #256F3F;  --pd-green-dim: rgba(46, 158, 86, .10);  --pd-green-line: rgba(46, 158, 86, .32);
  --pd-violet: #6E62B8; --pd-violet-ink: #554AA0; --pd-violet-dim: rgba(110, 98, 184, .10); --pd-violet-line: rgba(110, 98, 184, .30);
  --pd-amber: #B9862F;
  --pd-red: #B3402E;    --pd-red-ink: #9A3626;
  --pd-warm-ink: var(--sn-accent-ink);   /* §74: a tinted link is brand presence at zero cost */

  --pd-float: rgba(255, 255, 255, .95);
  --pd-stop-bg: var(--sn-ink);
  --pd-stop-ink: var(--sn-bg);
  --pd-toast-bg: #232220;
  --pd-toast-ink: #F5F4EF;

  --pd-elev-1: var(--sn-elev-1);
  --pd-elev-2: var(--sn-elev-2);
  --pd-elev-3: var(--sn-elev-3);
}

/* The studio's ambient orbs sit behind a full-height opaque work frame, so they cost a paint and show
   through nowhere. This is the one shell element the Producer still touches, and only to stop drawing it. */
body.producer-skin #studio-ambient { display: none; }

/* ONE SCROLL CONTEXT PER PANEL, PERIOD (spec §4.3 — the shipped triple-nested-scrollbar bug).
   V1 stacked page scroll + a `max-height:78vh` scenes box + the thread scroller, which made scenes 3-5
   literally unreachable by page scroll and bottomed the page into a black void. The workspace is a fixed
   viewport-height frame; the chat scrolls, the stage scrolls, and the document does not scroll at all. */
/* …but the frame is the SHELL'S frame. Above 861px studio-system.css floats `.app` as `position:fixed;
   inset:10px; height:auto` — a rounded card with the ambient ground showing around it — and forcing
   `height:100vh` onto a box whose top is already 10px down pushed its bottom 20px past the viewport, so
   the Producer's own last row sat under the edge of the screen while every other tool fit. The clamp is
   only needed where there is no fixed frame to inherit. */
@media (max-width: 860px) { body.producer-skin .app { height: 100vh; min-height: 100vh; } }
body.producer-skin .main { min-height: 0; overflow: hidden; }
body.producer-skin .stage { min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
/* THE SHELL BELONGS TO THE SHELL. R1 repainted the sidebar and the topbar too, which is precisely how a
   tool stops looking like a tab of an app and starts looking like a different website inside one. */

/* THE SEAM (R3 directive #3). The Producer used to paint its own canvas — ivory `--sn-bg` in light — and
   sit it inside the studio's content panel, which studio-system.css paints WHITE in light and a shade
   BELOW `--sn-bg` in dark. Two surfaces, one rectangle: the Producer's top bar and stage toolbar read as
   a slightly different colour from the section they live in, which is exactly what Rob's screenshot shows
   and exactly what makes the tool look bolted on.
   The fix is not a matching hex — a matching hex goes stale the moment the shell re-tones. The workspace
   and the stage are TRANSPARENT, so the surface under the Producer's chrome IS `#stage.stage`, the same
   surface the Generate and Video tabs sit on, in whichever theme, at any width, forever. `--pd-black` is
   gone with it; there is no third colour left to drift. Panels (chat / assets / scene cards) keep their
   one deliberate step off that ground via `--pd-panel`, which is composition, not chrome. */
/* 🔴 §62 — AND NOW THE PRODUCER PAINTS ITS OWN GROUND, which is the other half of "default to dark".
   The paragraph above is why this was `transparent`: inheriting `#stage` guaranteed no seam in either
   theme. That guarantee is exactly what a dark-by-default Producer has to give up — a cutting room that
   inherits an ivory panel is not dark, it is a light room with dark cards in it. So the canvas is
   declared here, once, and the seam it used to avoid is now a deliberate boundary: the Producer is a
   dark room you walk into. (Rob, after seeing ours beside OpenArt — see §62 at the top of this file.) */
body.producer-skin .pd-root { flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--pd-canvas); }
body.producer-skin .pd-root.is-home { overflow: auto; }        /* the home page is a page — it scrolls */
body.producer-skin .pd-root.is-ws { overflow: hidden; }        /* the workspace is a frame — it does not */

/* Scrollbars stay out of the way; they are chrome and the chrome is silent. */
body.producer-skin .pd-scroll { overflow: auto; scrollbar-width: thin; scrollbar-color: var(--pd-ghost) transparent; }
body.producer-skin .pd-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
body.producer-skin .pd-scroll::-webkit-scrollbar-track { background: transparent; }
body.producer-skin .pd-scroll::-webkit-scrollbar-thumb { background: var(--pd-scrollthumb); border-radius: 99px; border: 3px solid transparent; background-clip: content-box; }
body.producer-skin .pd-scroll::-webkit-scrollbar-thumb:hover { background: var(--pd-scrollthumb-h); background-clip: content-box; }

/* =====================================================================================================
   1. THE WORKSPACE SHELL — two panels (spec §3.2, target f240 populated / f200 empty)
   ===================================================================================================== */

body.producer-skin .pd-ws { flex: 1; min-height: 0; display: flex; flex-direction: column; background: transparent; }

/* Header: logo dot + film title at ~13px. NOT a hero. V1 stacked a 230px hero on top of an H2 that
   repeated the same word, which is why the film sat below the fold on a 1080px screen (spec §4.1). */
body.producer-skin .pd-head { display: flex; align-items: center; gap: 10px; padding: 0 14px; height: 46px; flex: none; border-bottom: 1px solid var(--pd-line); }
/* THE POSTER CHIP. R1's ember square read as a placeholder icon nobody finished — a coloured rectangle
   beside a title promises a thumbnail. This either IS the film's first delivered still or it is an
   obviously-empty frame with a film glyph in it, which promises nothing. */
body.producer-skin .pd-poster { width: 24px; height: 24px; border-radius: 6px; overflow: hidden; flex: none; background: var(--pd-card-2); border: 1px solid var(--pd-line); }
body.producer-skin .pd-poster img { width: 100%; height: 100%; object-fit: cover; display: block; }
body.producer-skin .pd-poster.is-ph { display: grid; place-items: center; color: var(--pd-ink-4); }
body.producer-skin .pd-poster.is-ph .sn-ic { width: 13px; height: 13px; }
body.producer-skin .pd-title { font: 600 .8125rem/1 var(--sn-sans); color: var(--pd-ink); background: none; border: 1px solid transparent; border-radius: 6px; padding: 5px 7px; cursor: text; max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.producer-skin .pd-title:hover { border-color: var(--pd-line-2); }
body.producer-skin .pd-title:focus { outline: none; border-color: var(--pd-accent-line); background: var(--pd-card); }

/* THREE COLUMNS, AND EVERY BOUNDARY IS A HANDLE (R3 directive #1): CHAT LEFT | STAGE | ASSETS RIGHT.
   R2 put the chat on the right on Rob's instruction; he reversed it after living with it, and left is
   also where real OpenArt keeps it. The world rail crosses to the far right and becomes the ASSETS
   column (§5).
   The two boundaries are 6px GRID TRACKS, not borders — a border cannot be grabbed, and a divider drawn
   as an absolutely-positioned overlay drifts the moment a column is resized. Their widths live in
   `--pd-w-chat` / `--pd-w-assets`, which is the single thing the drag writes; everything else is layout
   doing its job. The default widths are the spec's ~30% / ~20%.
   Every content track is minmax(0,…) because a grid item defaults to `min-width:auto`, which is what let
   the board's min-content push a track wider than the panel and put a horizontal scrollbar on a surface
   with exactly one scroll direction. That bug shipped once; the tracks are written this way so it cannot. */
/* 🔵🔵 §80 — THE PROPORTIONS, MEASURED OFF openart-editor-3column.jpg RATHER THAN ESTIMATED.
   Within OpenArt's app frame the three tracks come out at:
       chat 32%   ·   stage 56%   ·   inspector 12%
   Ours were 30 / 43.5 / 20, and the 20 is the number that was wrong: the inspector was taking EIGHT
   POINTS off the picture. On a 1600px screen that is ~130px of film given to a panel that holds four
   tabs and, most of the time, an empty state. The film is the hero of this screen and it was the
   smallest it has ever been.

   ⚠️ CLAMPED, NOT BARE PERCENTAGES. At 1200px a 12% inspector is 144px, which cannot hold "Voiceover"
   under an icon; at 2560px a 32% chat is 819px of prose measure, which is unreadable for a different
   reason. The clamps hold both columns in the band where their CONTENT works and let the stage take
   everything else — which is the same thing OpenArt's fixed-width panels achieve on one screen size. */
body.producer-skin .pd-ws { --pd-w-chat: clamp(320px, 32%, 520px); --pd-w-assets: clamp(190px, 12%, 260px); }
/* 🔵🔵 §82 — TWO TRACKS AT THIS LEVEL NOW: the chat, and everything to its right.
   In the reference the chat runs the FULL HEIGHT of the app and the timeline spans only the picture and
   the inspector. Ours was a footer of `.pd-ws`, so it ran the whole width and cut the conversation off at
   the same line as the film. Nesting the stage, the inspector and the timeline into one region is what
   lets the chat keep going past the timeline's top edge.

   ⚠️ THE RESIZE MATH IS UNAFFECTED, which is why this is safe. `wireTlDividers` measures
   `body.clientWidth` as the total and each column's own rect — and the body's width is still
   chat + stage + inspector + the two 6px handles no matter which element nests them. Both dividers keep
   working with no JS change; the `--pd-w-*` vars still live on `.pd-ws`, above both grids. */
body.producer-skin .pd-body {
  flex: 1; min-height: 0; display: grid;
  grid-template-columns: minmax(0, var(--pd-w-chat)) 6px minmax(0, 1fr);
}
body.producer-skin .pd-right {
  min-width: 0; min-height: 0; display: grid;
  grid-template-rows: minmax(0, 1fr) auto;      /* the picture takes the room, the timeline takes its own */
}
body.producer-skin .pd-right-top {
  min-width: 0; min-height: 0; display: grid;
  grid-template-columns: minmax(0, 1fr) 6px minmax(0, var(--pd-w-assets));
}

/* ---- the three columns: ONE SURFACE, NOT THREE PANELS (Rob, on the R3 screenshot) ----------------
   All three tracks are TRANSPARENT, so what you see under the Producer's chrome is the studio's own
   content panel — white in light, the studio's dark surface in dark — continuous from the chat, across
   the film, to the assets. Same reasoning that fixed the canvas seam: an explicit background here is a
   second surface that has to be kept in step with the shell by hand, and it drifted the moment the two
   were compared side by side. Transparent cannot drift.
   THE LUMINANCE STEP WAS DOING TWO JOBS AND ONLY ONE OF THEM WAS WANTED. It tinted the side columns AND
   it drew the seam between them. The seam does not need it: `.pd-div::before` has always painted its own
   1px hairline, which is what the pointer actually aims at, so the boundaries stay exactly as legible
   and as grabbable with the tint gone. */
body.producer-skin .pd-chat { min-width: 0; min-height: 0; display: flex; flex-direction: column; background: transparent; position: relative; }
body.producer-skin .pd-stage { min-width: 0; min-height: 0; display: flex; flex-direction: column; background: transparent; position: relative; }
/* EVERY PICTURE THE PRODUCER MAKES OPENS FULL SIZE, AND SAYS SO BEFORE IT IS CLICKED.
   `is-zoom` is written by tlMarkZoomable on every paint — only onto media that is not already a control
   and not a `<video controls>` — so this rule can never put a zoom cursor on a take tile whose click
   switches the cut. The lift is the affordance: a picture that answers the pointer is a picture you know
   is a door. Honoured only where the pointer is fine enough to hover. */
body.producer-skin .is-zoom { cursor: zoom-in; }
@media (hover: hover) and (pointer: fine) {
  body.producer-skin .is-zoom { transition: opacity var(--pd-fade) var(--sn-ease); }
  body.producer-skin .is-zoom:hover { opacity: .86; }
}

/* ---- the world column — the cast of known things you direct against, now on the FAR RIGHT (§5) ---- */
body.producer-skin .pd-railcol { min-width: 0; min-height: 0; background: transparent; padding: 12px 11px 18px; }
body.producer-skin .pd-railcol:empty { padding: 0; }

/* ASSETS BELONG TO THE SCENE (R3 directive #5a). Off the Scene tab the column and its handle collapse to
   nothing — not `display:none` on the aside alone, which would leave a 6px handle dividing a column from
   itself — and a toggle in the stage bar brings it back for a peek.

   IT USED TO BE A TAB PINNED TO THE MIDDLE OF THE VIEWPORT'S RIGHT EDGE (`position: fixed; top: 50%`),
   floating over the film. It is a `.pd-ubtn` in the stage bar's utility row now, so it inherits that
   row's size, hover and disabled treatment and only needs its OWN state — the accent it wears while the
   column is open. Nothing here positions it any more; the bar does. */
/* §82: the inspector's absence is now a fact about the RIGHT region, not about the body. */
body.producer-skin .pd-ws.is-noassets .pd-right-top { grid-template-columns: minmax(0, 1fr); }
body.producer-skin .pd-ws.is-noassets .pd-railcol,
body.producer-skin .pd-ws.is-noassets .pd-div[data-div="assets"] { display: none; }
body.producer-skin .pd-assets-tab.is-open { color: var(--pd-ink); border-color: var(--pd-accent-line); background: var(--pd-card); }
/* CURRENT SHOT, NOW IN THE SHOT TAB'S OWN STAGE. The rail's 2-up tile grid is a rail shape; on a 640px
   stage it would draw two enormous squares, so the same markup lays out as a horizontal takes STRIP —
   which is also what rob-05 shows. Same buttons, same wiring, same free take switch. */
body.producer-skin .pd-takes { border-top: 1px solid var(--pd-line); padding-top: 11px; }
body.producer-skin .pd-takes .pd-rail-grid { display: flex; flex-wrap: wrap; gap: 7px; }
body.producer-skin .pd-takes .pd-rail-grid > * { width: 118px; aspect-ratio: 16 / 9; flex: none; }
body.producer-skin .pd-takes .pd-rail-acts { justify-content: flex-start; }
body.producer-skin .pd-takes .pd-rail-acts .pd-btn { flex: none; }

/* ---- BRAND, THE ANCHOR LAYER WITH A DIFFERENT SCOPE (§6.5) --------------------------------------- */
body.producer-skin .pd-brandrow.is-on { border-left: 2px solid var(--pd-accent); padding-left: 8px; margin-left: -10px; }
body.producer-skin .pd-brand-face { display: flex; align-items: center; gap: 9px; margin-top: 7px; }
body.producer-skin .pd-brand-logo { width: 34px; height: 34px; flex: none; border-radius: 7px; overflow: hidden; border: 1px solid var(--pd-line); background: var(--pd-card); display: grid; place-items: center; }
body.producer-skin .pd-brand-logo img { width: 100%; height: 100%; object-fit: contain; display: block; }
body.producer-skin .pd-brand-logo.is-ph { color: var(--pd-ghost); }
body.producer-skin .pd-brand-tx { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
body.producer-skin .pd-brand-dots { display: flex; gap: 3px; }
body.producer-skin .pd-brand-dots i { width: 13px; height: 13px; border-radius: 4px; display: block; border: 1px solid var(--pd-line); }
body.producer-skin .pd-brand-font { font: .6875rem/1 var(--sn-sans); color: var(--pd-ink-4); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.producer-skin .pd-rail-empty a { color: var(--pd-warm-ink); text-decoration: underline; }

/* ---- ANCHORS IN PLAY (§6.2) — what this shot is built from, on the shot ------------------------- */
body.producer-skin .pd-anchors { display: flex; flex-direction: column; gap: 6px; }
body.producer-skin .pd-anchor-chips { display: flex; gap: 6px; flex-wrap: wrap; }
body.producer-skin .pd-anchor { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px 0 3px; border: 1px solid var(--pd-line-2); border-radius: 99px; background: var(--pd-card); color: var(--pd-ink-2); font: .6875rem/1 var(--sn-sans); }
body.producer-skin .pd-anchor img, body.producer-skin .pd-anchor .pd-anchor-ph { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; display: block; flex: none; background: var(--pd-skel); }
body.producer-skin .pd-anchor b { font-weight: 600; color: var(--pd-ink); }
body.producer-skin .pd-anchor em { font-style: normal; color: var(--pd-ink-4); }

/* ---- THE PHASE GATES (§2.2) ------------------------------------------------------------------------
   The plain link that opens the plan. It is a sentence with an affordance, not a button — the script is a
   moment you step into, and a full-weight CTA in the middle of the Director's prose would read as the
   thing that spends. Nothing on this line spends. */
body.producer-skin .pd-lnk { display: inline-flex; align-items: center; gap: 4px; padding: 0; border: 0; background: none; color: var(--pd-warm-ink); font: 600 .75rem/1.5 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-lnk:hover { color: var(--pd-accent-hi); text-decoration: underline; }
body.producer-skin .pd-lnk .sn-ic { width: 12px; height: 12px; }
/* THE SCRIPT GATE — the approval moment, at the foot of the plan document. One ember button carrying the
   server's own figure, and one free way out of it. */
/* §62: was an accent border PLUS a 3px accent glow — the bright ring Rob saw around every card.
   OpenArt separates with SPACE and a ~8% hairline and draws no ring at all. The accent stays in this
   card, but only on the ONE button inside it that spends. */
body.producer-skin .pd-scriptgate { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 18px; padding: 14px 16px; border-radius: var(--pd-r-modal); border: 1px solid var(--pd-line); background: var(--pd-card); }
body.producer-skin .pd-sg-tx { flex: 1; min-width: 200px; display: flex; flex-direction: column; gap: 3px; }
body.producer-skin .pd-sg-tx b { font: 600 .8125rem/1.3 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-sg-tx span { font: .6875rem/1.45 var(--sn-sans); color: var(--pd-ink-4); }
/* THE RE-GATE — amber, because it is a stop, not a failure and not a sale. */
body.producer-skin .pd-regate { border-color: var(--pd-amber); }
body.producer-skin .pd-regate .pd-card-h .sn-ic { color: var(--pd-amber); }

/* ---- THE PHASE RAIL (P6, spec §2.2) -----------------------------------------------------------------
   Three steps at the top of the stage, and the film's own figure on the last one — the number you are
   walking towards is visible from the first step rather than sprung at the end. A step ahead of you is a
   genuinely disabled button, not a greyed span: it cannot be clicked, and it says why on hover. */
body.producer-skin .pd-phases { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
body.producer-skin .pd-phase { display: inline-flex; align-items: center; gap: 7px; height: var(--pd-h-sm); padding: 0 11px; border-radius: 99px; border: 1px solid var(--pd-line); background: var(--pd-card); color: var(--pd-ink-4); font: 600 .75rem/1 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-phase + .pd-phase { margin-left: 8px; position: relative; }
body.producer-skin .pd-phase + .pd-phase::before { content: ""; position: absolute; left: -13px; top: 50%; width: 8px; height: 1px; background: var(--pd-line-2); }
body.producer-skin .pd-phase .pd-phase-n { display: inline-flex; align-items: center; justify-content: center; width: 17px; height: 17px; border-radius: 50%; background: var(--pd-skel); color: var(--pd-ink-3); font: 700 .625rem/1 var(--sn-sans); flex: none; }
body.producer-skin .pd-phase .pd-phase-n .sn-ic { width: 10px; height: 10px; }
body.producer-skin .pd-phase.is-done { color: var(--pd-ink-2); border-color: var(--pd-line-2); }
body.producer-skin .pd-phase.is-done .pd-phase-n { background: var(--pd-accent-dim); color: var(--pd-warm-ink); }
/* §62: the current step is stated with ink + a raised surface, not a glowing outline. The one accent
   FILL in a view belongs to the primary action and the active view pill, and this is neither. */
body.producer-skin .pd-phase.is-on { color: var(--pd-ink); border-color: var(--pd-line-2); background: var(--pd-card-2); }
body.producer-skin .pd-phase.is-on .pd-phase-n { background: var(--pd-accent); color: var(--pd-on-accent); }
/* §60 — a step you cannot take is a <span> now, not a disabled <button>, so the rule that dims it keys on
   the ARIA state rather than the form control attribute. Both selectors are kept: `[disabled]` costs
   nothing and means a <button> that regains the shape is still dimmed rather than silently lit. */
body.producer-skin .pd-phase[disabled],
body.producer-skin .pd-phase[aria-disabled="true"] { cursor: default; opacity: .55; }
body.producer-skin .pd-phase .pd-price { margin-left: 2px; }
/* THE VOIDED WALK — amber, same voice as the re-gate, because it is the same rule: an approval is scoped
   to a figure and does not survive it moving. Nothing here is a failure and nothing here has been paid. */
body.producer-skin .pd-gatevoid { display: flex; gap: 9px; padding: 11px 12px; margin-bottom: 14px; border: 1px solid var(--pd-amber); border-radius: var(--pd-r-card); background: var(--pd-card); }
body.producer-skin .pd-gatevoid > .sn-ic-slot .sn-ic, body.producer-skin .pd-gatevoid .sn-ic { color: var(--pd-amber); width: 15px; height: 15px; flex: none; }
body.producer-skin .pd-gv-tx { display: flex; flex-direction: column; gap: 3px; }
body.producer-skin .pd-gv-tx b { font: 600 .8125rem/1.3 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-gv-tx span { font: .75rem/1.5 var(--sn-sans); color: var(--pd-ink-3); }

/* ---- THE ANCHORS GATE (P6, spec §2.2 step 2) --------------------------------------------------------
   What the film is locked to, once, before it is shot — and, in the same card, what is NOT locked. Every
   control on it is free; there is deliberately no priced button on this step. */
body.producer-skin .pd-anchgate { max-width: 760px; }
body.producer-skin .pd-anch-h h3 { font: 650 1.0625rem/1.3 var(--sn-sans); color: var(--pd-ink); margin: 0; }
body.producer-skin .pd-anch-h p { font: .75rem/1.5 var(--sn-sans); color: var(--pd-ink-4); margin: 3px 0 0; }
body.producer-skin .pd-anchgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; margin-top: 14px; }
body.producer-skin .pd-anchtile { display: flex; align-items: center; gap: 9px; padding: 8px; border: 1px solid var(--pd-line); border-radius: var(--pd-r-card); background: var(--pd-card); }
body.producer-skin .pd-anchtile img, body.producer-skin .pd-anchtile .pd-anchtile-ph { width: 40px; height: 40px; border-radius: 8px; object-fit: cover; display: flex; align-items: center; justify-content: center; flex: none; background: var(--pd-skel); }
body.producer-skin .pd-anchtile-ph .sn-ic { width: 15px; height: 15px; color: var(--pd-ink-4); }
body.producer-skin .pd-anchtile-tx { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
body.producer-skin .pd-anchtile-tx .k { font: 700 .5625rem/1 var(--sn-sans); letter-spacing: .08em; text-transform: uppercase; color: var(--pd-ink-4); }
body.producer-skin .pd-anchtile-tx b { font: 600 .75rem/1.3 var(--sn-sans); color: var(--pd-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.producer-skin .pd-anchtile-tx em { font: normal .6875rem/1.3 var(--sn-sans); color: var(--pd-ink-4); }
body.producer-skin .pd-anch-open { margin: 14px 0 0; padding: 10px 12px 10px 26px; border: 1px dashed var(--pd-line-2); border-radius: var(--pd-r-card); }
body.producer-skin .pd-anch-open li { font: .75rem/1.6 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-anch-foot { font: .75rem/1.6 var(--sn-sans); color: var(--pd-ink-4); margin: 14px 0 0; }

/* ---- THE DRAG HANDLES -----------------------------------------------------------------------------
   6px of hit area with a 1px hairline painted down the middle, so the seam still reads as a hairline and
   the target is still fat enough to hit. `touch-action:none` is what makes pointer-capture drag work on
   a trackpad/touch surface at all — without it the browser claims the gesture as a scroll. */
body.producer-skin .pd-div { position: relative; cursor: col-resize; background: none; border: 0; padding: 0; touch-action: none; z-index: 5; }
body.producer-skin .pd-div::before { content: ""; position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; transform: translateX(-.5px); background: var(--pd-line); }
body.producer-skin .pd-div:hover::before, body.producer-skin .pd-div.is-dragging::before { background: var(--pd-accent); width: 2px; }
body.producer-skin .pd-div:focus-visible::before { background: var(--pd-accent); width: 2px; }
/* while a drag is live the whole page stops selecting text and keeps the resize cursor, or the cursor
   flickers to a text caret every time the pointer crosses a label mid-drag */
body.producer-skin.is-coldrag, body.producer-skin.is-coldrag * { cursor: col-resize !important; user-select: none !important; }

/* ---- THE GENERATION EDITOR CROSS-FADE (spec §3.5, target t2161) --------------------------------
   Clicking Recreate/Modify replaces the CHAT COLUMN's content in place over ~200ms of opacity — same
   column width, no slide, no overlay on the stage. t2161 is a genuine mid-fade frame with both contents
   visible semi-transparent, which is how we know it is a cross-fade and not a swap. */
body.producer-skin .pd-chat > .pd-chat-pane,
body.producer-skin .pd-chat > .pd-gen-pane { position: absolute; inset: 0; display: flex; flex-direction: column; transition: opacity var(--pd-fade) linear; }
body.producer-skin .pd-chat > .pd-gen-pane { opacity: 0; pointer-events: none; z-index: 2; }
body.producer-skin .pd-chat.is-gen > .pd-chat-pane { opacity: 0; pointer-events: none; }
body.producer-skin .pd-chat.is-gen > .pd-gen-pane { opacity: 1; pointer-events: auto; }

/* ---- the stage's floating tab bar (spec §3.2) ---------------------------------------------------- */
/* The bar WRAPS rather than overflowing. At a narrow stage the utility row is the widest thing on the
   panel; letting it push the panel's scrollWidth out is how a horizontal scrollbar appears on a surface
   that is supposed to have exactly one scroll direction. */
body.producer-skin .pd-stage-bar { flex: none; display: flex; align-items: center; gap: 8px; padding: 8px 12px; min-height: 44px; flex-wrap: wrap; }
body.producer-skin .pd-util { flex-wrap: wrap; }
body.producer-skin .pd-tabs { display: inline-flex; gap: 3px; padding: 3px; border-radius: 99px; background: var(--pd-card); border: 1px solid var(--pd-line); }
body.producer-skin .pd-tab { display: inline-flex; align-items: center; gap: 6px; height: var(--pd-h-sm); padding: 0 12px; border: 0; border-radius: 99px; background: none; color: var(--pd-ink-3); font: 600 .75rem/1 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-tab .sn-ic { width: 13px; height: 13px; }
body.producer-skin .pd-tab:hover { color: var(--pd-ink-2); }
/* Active = a SOLID accent pill with white icon+label. No transition: the tab jump and the full stage
   re-render land in the same frame (t2112 → t2117), and that instantaneity is the product's feel. */
/* §68: the active view pill is an ACCENT SURFACE, so it carries the mark's gradient like the primary
   buttons do. The flat mid stop stays for ink, hairlines and tints. */
/* §74: .pd-tab.is-active is glossed with the primaries above. */
body.producer-skin .pd-tab.is-active .sn-ic { color: #fff; }

body.producer-skin .pd-bar-grow { flex: 1; }
body.producer-skin .pd-util { display: inline-flex; align-items: center; gap: 4px; }
body.producer-skin .pd-ubtn { display: inline-flex; align-items: center; gap: 6px; height: var(--pd-h-sm); padding: 0 10px; border: 1px solid transparent; border-radius: 7px; background: none; color: var(--pd-ink-3); font: 500 .75rem/1 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-ubtn .sn-ic { width: 14px; height: 14px; }
body.producer-skin .pd-ubtn:hover:not(:disabled) { background: var(--pd-card); color: var(--pd-ink); }
body.producer-skin .pd-ubtn:disabled { opacity: .34; cursor: default; }
body.producer-skin .pd-ubtn.is-icon { padding: 0; width: var(--pd-h-sm); justify-content: center; }

/* The credits chip: gem + balance, green, with a fused Upgrade pill. Credits only — never a currency. */
body.producer-skin .pd-cr { display: inline-flex; align-items: center; height: 28px; border-radius: 99px; border: 1px solid var(--pd-line-2); overflow: hidden; background: var(--pd-card); }
body.producer-skin .pd-cr-n { display: inline-flex; align-items: center; gap: 6px; padding: 0 10px; font: 600 .75rem/1 var(--sn-sans); color: var(--pd-green); font-variant-numeric: tabular-nums; text-decoration: none; }
body.producer-skin .pd-cr-n .sn-ic { width: 12px; height: 12px; }
body.producer-skin .pd-cr-up { padding: 0 10px; height: 100%; display: inline-flex; align-items: center; border: 0; border-left: 1px solid var(--pd-line-2); background: none; color: var(--pd-ink-2); font: 600 .6875rem/1 var(--sn-sans); cursor: pointer; text-decoration: none; }
body.producer-skin .pd-cr-up:hover { background: var(--pd-card-2); color: var(--pd-ink); }

/* THE ROLLING NUMBER (spec §3.9, observed t2367–t2369 and the refund tick at t2372).
   The balance is ALIVE: a charge counts it down in visible steps over ~2s, and a refund ticks it back UP.
   Ours rolls far less often than theirs, because chatting here is free and theirs is not. */
body.producer-skin .pd-cr-n.is-rolling { color: var(--pd-accent); }
body.producer-skin .pd-cr-n.is-refund { color: var(--pd-green); }

/* ---- the stage's scroll body --------------------------------------------------------------------- */
body.producer-skin .pd-stage-body { flex: 1; min-height: 0; overflow: auto; padding: 0 14px 18px; }

/* ---- THE COMMIT CARD — the render, as the LAST MESSAGE IN THE THREAD (R2 directive #3) ------------
   R1 stood this across the bottom of the stage forever, which made the most expensive button in the
   product ambient furniture. It is a card in the conversation now: it appears while there is a film left
   to shoot, states the facts the server priced, and carries its figure on its own button. */
/* §62: same — hairline, no ring. This is the card that SPENDS, and it says so with its button, not
   with a coloured outline around the whole thing. */
body.producer-skin .pd-commit { border-radius: var(--pd-r-modal); border: 1px solid var(--pd-line); background: var(--pd-card); padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }
body.producer-skin .pd-commit-h { display: flex; align-items: center; gap: 7px; font: 600 .8125rem/1 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-commit-h .sn-ic { width: 14px; height: 14px; color: var(--pd-accent); }
body.producer-skin .pd-commit-f { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
body.producer-skin .pd-commit-n { margin: 0; font: .6875rem/1.5 var(--sn-sans); color: var(--pd-ink-4); }
body.producer-skin .pd-commit .q-cap { display: flex; align-items: flex-start; gap: 7px; font: .6875rem/1.45 var(--sn-sans); color: var(--pd-warm-ink); }
body.producer-skin .pd-commit .q-cap .sn-ic { width: 13px; height: 13px; flex: none; margin-top: 2px; }
body.producer-skin .pd-qb-facts { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; font: .75rem/1 var(--sn-sans); color: var(--pd-ink-4); }
body.producer-skin .pd-qb-facts b { font: 600 .8125rem/1 var(--sn-sans); color: var(--pd-ink-2); }
body.producer-skin .pd-qb-chip { display: inline-flex; align-items: center; height: 24px; padding: 0 10px; border-radius: 99px; border: 1px solid var(--pd-line-2); font: 600 .6875rem/1 var(--sn-sans); color: var(--pd-ink-3); font-variant-numeric: tabular-nums; }
body.producer-skin .pd-qb-chip.is-cr { color: var(--pd-green); border-color: var(--pd-green-line); }
body.producer-skin .pd-head .tl-progress { display: flex; align-items: center; gap: 8px; font: .6875rem/1 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-head .tl-prog-track { display: flex; gap: 2px; }
body.producer-skin .pd-head .tl-prog-track i { width: 12px; height: 3px; border-radius: 99px; background: var(--pd-line-2); display: block; }
body.producer-skin .pd-head .tl-prog-track i.is-done { background: var(--pd-green); }
body.producer-skin .pd-head .tl-prog-track i.is-live { background: var(--pd-accent); }
body.producer-skin .pd-head .tl-prog-say { color: var(--pd-amber); }

/* ---- EMPTY STAGE (spec §3.2, target f200) --------------------------------------------------------
   Art-directed, not a text block. Centred projector, a headline whose emphasis word is serif-italic
   (the one Ink & Ivory trait worth keeping), and a ghosted, disabled timeline strip along the bottom. */
body.producer-skin .pd-empty { flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative; text-align: center; padding: 24px; }
body.producer-skin .pd-empty-art { color: var(--pd-ghost); margin-bottom: 20px; }
body.producer-skin .pd-empty-art svg { width: 74px; height: 74px; display: block; }
body.producer-skin .pd-empty h3 { font: 700 1.5rem/1.2 var(--sn-sans); color: var(--pd-ink-2); margin: 0 0 8px; }
/* §63: "Your film will LAND HERE." was serif-italic — the single most editorial thing on the Producer.
   The emphasis stays (it is doing real work) but it is now WEIGHT and INK, not a change of typeface. */
body.producer-skin .pd-empty h3 em { font-family: inherit; font-style: normal; font-weight: 700; color: var(--pd-ink); }
body.producer-skin .pd-empty p { font: .8125rem/1.6 var(--sn-sans); color: var(--pd-ink-4); margin: 0; max-width: 300px; }
body.producer-skin .pd-empty-free { margin-top: 14px; font: 600 .75rem/1 var(--sn-sans); color: var(--pd-green); }
/* the ghosted timeline that says "a real editor lands here" without pretending to be one yet */
body.producer-skin .pd-empty-strip { position: absolute; left: 0; right: 0; bottom: 0; height: 132px; opacity: .34; pointer-events: none; display: flex; flex-direction: column; gap: 6px; padding: 0 26px 22px; justify-content: flex-end; }
body.producer-skin .pd-empty-strip i { display: block; border-radius: 4px; background: var(--pd-skel); }
body.producer-skin .pd-empty-strip i.r1 { height: 34px; }
body.producer-skin .pd-empty-strip i.r2 { height: 10px; width: 72%; }
body.producer-skin .pd-empty-strip i.r3 { height: 10px; width: 46%; }

/* =====================================================================================================
   2. THE CHAT — a conversation, not an audit log (spec §3.3, targets f200–f215, f293, 04-step2)
   ===================================================================================================== */

/* NO `scroll-behavior: smooth` HERE, AND ITS REMOVAL IS A BUG FIX RATHER THAN A TASTE CHANGE.
   Measured on the deployed panel, on a real board: `body.scrollTop = n` did nothing, and
   `scrollTo({top: n, behavior: "smooth"})` did nothing either — only `behavior: "instant"` moved it.
   So this one declaration had been silently disabling BOTH the autoscroll to the newest message and the
   jump button, which is the whole of "the reply lands off screen and the thread never comes back to it".
   It also disagreed with the skin's own motion doctrine (§ the header): everything here is a cut. */
/* §67 — A CALMER RHYTHM. Every block in the thread was separated by the same 14px whether it was a
   paragraph or a card, so nothing grouped and everything read as a list. 10px is the base beat; the two
   heavyweight blocks (a media card, the commit) get a wider gap so they read as their own moment. That is
   the OpenArt density: prose close together, objects given room. */
/* §71 — MESSAGE-ROW RHYTHM. Elements sets its container at p-6; on a 24%-wide plan column that reads as
   16px. The gap is the row gap between turns, not between every block — see the grouping rules below. */
/* 🔵🔵 §73 — THE RHYTHM IS THE FIX. Ported from AI SDK Elements, the shadcn-registry chat block:
     ConversationContent   flex flex-col gap-8 p-4     → 32px BETWEEN turns
     Message               flex w-full max-w-[95%] flex-col gap-2  →  8px INSIDE a turn
   A 4:1 ratio. §71 set BOTH to 14px, and that single number is why Rob read the column as a text dump:
   a diff card and a change of speaker were the same distance apart, so the eye had nothing to group on.
   Padding matches too — Elements pads the scroller, not the individual message. */
body.producer-skin .pd-thread { flex: 1; min-height: 0; overflow: auto; padding: 16px; display: flex; flex-direction: column; gap: 32px; }
body.producer-skin .pd-msg { display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 95%; }

/* THREE LANES, and the third is the one that was missing. `assistant` is the Director speaking and wears
   the avatar. `system` is the BOARD's own state — what is queued, what a run costs, the cut that came
   back — and giving it the Director's avatar would claim the app said something it did not. It aligns to
   the same text column so the conversation still reads as one thread, and stays unmarked. */
body.producer-skin .pd-msg[data-from="assistant"],
body.producer-skin .pd-msg[data-from="system"] { padding-left: 34px; position: relative; }
body.producer-skin .pd-msg[data-from="assistant"]::before {
  content: ""; position: absolute; left: 0; top: 1px;
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--pd-card-2); border: 1px solid var(--pd-line);
}
/* The system lane gets a hairline instead of a mark: present, quiet, and unmistakably not a speaker. */
body.producer-skin .pd-msg[data-from="system"]::before {
  content: ""; position: absolute; left: 11px; top: 4px; bottom: 4px;
  width: 1px; background: var(--pd-line);
}
body.producer-skin .pd-msg[data-from="user"] { align-self: flex-end; align-items: flex-end; max-width: 80%; }
/* THE ROLE MARKER, ONCE PER RUN. A 24px avatar on every assistant line would be the chrome §67 just took
   out; a marker on the FIRST line of a run is how a polished chat shows who is speaking without repeating
   itself. `+` means "an assistant line directly after another" — those get no marker and tighten up,
   because they are one voice continuing, not a new turn. */
/* §71 hung the marker off every `.pd-say` and then had to SUPPRESS it on continuation lines with an
   adjacent-sibling rule. That was a workaround for having no row: now that a turn is one element, the
   marker belongs to the turn and the suppression hack is gone rather than merely quieter.
   ⚠️ AND THE AVATAR STAYS GREY. The obvious move is to stamp /images/logo-mark.svg in it — it is
   the logo, so it would not break "logo only" on a technicality. But it would put the one coloured thing
   in the studio on EVERY assistant turn, in the busiest column, one turn after Rob signed off on fully
   monochrome. shadcn's own <Avatar> fallback is a grey; the mark stays in the rail where it is said once. */
body.producer-skin .pd-msg > * { margin: 0; }

/* USER MESSAGES: right-aligned, terse, a subtle pill. V1 made these heavy orange lozenges, which put the
   loudest colour on the screen on the least important content. */
/* 🔵 §71 — THE MESSAGE ROW, FROM AI SDK ELEMENTS' OWN ANATOMY.
   Their <Message from="…"> is the canonical shadcn-registry AI-chat primitive, and its rule is precise:
     USER      → secondary background, aligned, a bubble
     ASSISTANT → FULL WIDTH across the container, minimalist flat design — no bubble at all
   That asymmetry IS the modern chat pattern (ChatGPT, Claude, OpenArt all do it): the reply is a document
   you read, the prompt is a receipt of what you asked.

   ⚠️ THIS CORRECTS §68, which stripped the user fill entirely on the reasoning that "OpenArt's left column
   has no chat bubbles". Half right: it has no ASSISTANT bubbles. The user's own line still needs a surface
   or the two voices are one undifferentiated column. `bg-secondary` is oklch(.269 0 0) — our --pd-card-2 —
   a grey, never the brand. */
/* MessageContent: `w-fit max-w-full`, secondary background, rounded. The ROW does the aligning now, so
   this is just the bubble — and `w-fit` is why a three-word reply is a three-word bubble. */
body.producer-skin .pd-m-user {
  width: fit-content; max-width: 100%;
  background: var(--pd-card-2); border: 0;
  border-radius: var(--pd-radius); padding: 10px 14px;
  font: .875rem/1.6 var(--sn-sans); color: var(--pd-ink);
}
body.producer-skin .pd-m-user .pd-m-atts { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 7px; }
body.producer-skin .pd-m-user .pd-m-atts img { width: 46px; height: 46px; object-fit: cover; border-radius: 6px; display: block; }

/* THE COLLAPSED BRIEF (rob-03). A real brief is 250 words; R1 printed all of them as the first message
   and the conversation started below the fold of its own opening turn. Four lines, a fade, an Expand. */
body.producer-skin .pd-brief { max-width: 92%; }
body.producer-skin .pd-brief .pd-brief-b { position: relative; max-height: 5.4em; overflow: hidden; }
body.producer-skin .pd-brief .pd-brief-b::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2.2em; background: linear-gradient(transparent, var(--pd-panel)); pointer-events: none; }
body.producer-skin .pd-brief.is-open .pd-brief-b { max-height: none; }
body.producer-skin .pd-brief.is-open .pd-brief-b::after { display: none; }
/* §68: a quiet text affordance, not a bordered footer on a card that no longer exists. */
body.producer-skin .pd-brief-x { display: flex; align-items: center; gap: 5px; margin-top: 2px; padding: 0; border: 0; background: none; color: var(--pd-ink-4); font: 500 .625rem/1.8 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-brief-x:hover { color: var(--pd-ink); }
body.producer-skin .pd-brief-x .sn-ic { width: 11px; height: 11px; }

/* WHAT DID NOT COST YOU ANYTHING, SAID OUT LOUD (rob-03's amber tiles). Our refunds were correct and
   invisible; these are the same fact, on screen, in the thread where the failure happened. */
body.producer-skin .pd-refunds { display: flex; flex-direction: column; gap: 7px; }
/* The icon SLOT is the grid item — `.sn-ic` is the svg inside it, and spanning the wrong element put the
   message text in the 16px icon column, one word per line. */
body.producer-skin .pd-refund { display: grid; grid-template-columns: 16px minmax(0, 1fr); grid-template-rows: auto auto; column-gap: 8px; row-gap: 2px; padding: 9px 11px; border-radius: 10px; border: 1px solid var(--pd-amber); background: var(--sn-warn-soft); }
body.producer-skin .pd-refund > .sn-ic-slot { grid-column: 1; grid-row: 1 / span 2; display: block; }
body.producer-skin .pd-refund > b, body.producer-skin .pd-refund > span { grid-column: 2; }
body.producer-skin .pd-refund .sn-ic { width: 14px; height: 14px; margin-top: 2px; color: var(--pd-amber); }
body.producer-skin .pd-refund b { font: 700 .6875rem/1.4 var(--sn-sans); letter-spacing: .03em; text-transform: uppercase; color: var(--pd-amber); }
body.producer-skin .pd-refund span { font: .75rem/1.5 var(--sn-sans); color: var(--pd-ink-2); }

/* THE JUMP BUTTON. It floats over the thread's bottom edge and only exists while you are not there. */
body.producer-skin .pd-thread-wrap { flex: 1; min-height: 0; position: relative; display: flex; flex-direction: column; }
body.producer-skin .pd-jump { position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%) scale(.85); z-index: 8; width: 30px; height: 30px; display: grid; place-items: center; border: 1px solid var(--pd-line-2); border-radius: 50%; background: var(--pd-card); color: var(--pd-ink-2); cursor: pointer; box-shadow: var(--pd-elev-2); opacity: 0; pointer-events: none; transition: opacity var(--pd-fade) linear, transform var(--pd-fade) var(--sn-ease); }
body.producer-skin .pd-jump.is-on { opacity: 1; pointer-events: auto; transform: translateX(-50%) scale(1); }
body.producer-skin .pd-jump .sn-ic { width: 15px; height: 15px; }

/* THE DIRECTOR SPEAKS UNBUBBLED. Flat light-grey prose on the panel, generous line height, bold spec
   tokens. This single change is most of the difference between "a chat" and "a log of form submissions":
   V1's assistant replies were dense cards with mono uppercase labels and legalese footers (spec §4.2). */
/* §71 — THE ASSISTANT IS FULL WIDTH AND FLAT, which is the other half of the Elements rule. text-sm on
   shadcn's scale (14px) with generous leading: a plan is something you READ, not a panel you scan, and the
   calm comes from the absence of chrome around the words rather than from pushing blocks apart. */
/* Full width and FLAT — the other half of the Elements rule: the assistant is a document you read, not a
   bubble. The indent moved to the row, so the paragraph itself is now plain. */
/* 🔴 §89 — 1.7 WAS A DOCUMENT'S LEADING APPLIED TO A CHAT MESSAGE, and Rob read the result as broken
   formatting rather than as a paragraph: 14px text on a 23.8px rhythm, so a four-line reply stood 95px
   tall with a visible band of nothing between every wrapped line.
   The note above says "a plan is something you READ, not a panel you scan" and that is still true of the
   SCRIPT tab, which stays more generous at 1.55 (`.tl-script-dir`, studio-live.css — the `.pd-doc-b`
   this once pointed at was dead CSS and is deleted). It is not true of a chat message: it is read once,
   in place, against a 32px turn gap — the rhythm that groups it is the turn spacing, and loose leading
   inside the paragraph fights that instead of helping it. 1.45 is the normal reading value for 14px UI
   prose and is what every chat surface worth copying uses. */
body.producer-skin .pd-say { width: 100%; font: .875rem/1.45 var(--sn-sans); color: var(--pd-ink-2); white-space: pre-wrap; }
/* 🔴 §89 — AND A PARAGRAPH BREAK IS NOT A BLANK LINE.
   `white-space: pre-wrap` rendered every "\n\n" the model wrote as a whole EMPTY LINE at the full leading:
   a 23.8px void between paragraphs, on top of already-loose lines. A run of "\n\n\n\n" — which a model
   does emit — opened three of them. tlSayHTML now splits paragraphs into real <p>s (and collapses the
   ragged runs), so the gap is a typographic one we choose rather than however many newlines arrived. */
body.producer-skin .pd-say > p, body.producer-skin .pd-m-user > p { margin: 0; }
body.producer-skin .pd-say > p + p, body.producer-skin .pd-m-user > p + p { margin-top: .5em; }
body.producer-skin .pd-say b, body.producer-skin .pd-say strong { color: var(--pd-ink); font-weight: 600; }
body.producer-skin .pd-say .pd-cr-tok { color: var(--pd-green); font-weight: 600; font-variant-numeric: tabular-nums; }
body.producer-skin .pd-say.is-thinking { color: var(--pd-ink-4); display: flex; align-items: center; gap: 2px; }
/* THE USAGE LIMIT — CALM, NOT ALARMED. Running out of credits is not an error and must not be dressed as
   one: no red, no icon shouting, no border that pulls the eye off the conversation. It is a bordered
   panel only so the reset line and the way out read as belonging to the sentence above them rather than
   floating in the thread. The rule is the same one the refund tile follows — state the fact, once. */
body.producer-skin .pd-gate { margin: 2px 0; padding: 12px 14px; border: 1px solid var(--pd-line-2); border-radius: var(--pd-r-card); background: var(--pd-card); }
body.producer-skin .pd-gate-say { white-space: pre-wrap; }
body.producer-skin .pd-gate-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--pd-line-2); }
/* The words the sentence ends on ARE the link — same weight as the prose around them, so it reads as a
   sentence rather than as a button that wandered into one. Underlined because it is the only thing in
   the card that colour alone would have to carry, and colour alone is not an affordance. */
body.producer-skin .pd-gate-lnk { color: var(--pd-warm-ink); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
body.producer-skin .pd-gate-lnk:hover { color: var(--pd-accent-hi); }
/* WHY THAT ENTER DID NOTHING — under the box, where the customer is looking, not in a corner toast.
   Pressing enter mid-turn used to clear the line and drop the send in silence. */
body.producer-skin .pd-busy-say { max-height: 0; overflow: hidden; opacity: 0; padding: 0 14px; font: .8125rem/1.5 var(--sn-sans); color: var(--pd-ink-3); transition: max-height var(--pd-fade) var(--sn-ease), opacity var(--pd-fade) linear, padding var(--pd-fade) var(--sn-ease); }
body.producer-skin .pd-busy-say.is-on { max-height: 3.4em; opacity: 1; padding: 6px 14px 2px; }
/* THE WAIT, MADE VISIBLE. Model routing is 2.0-4.7s on the live endpoint and the row said one motionless
   grey sentence for all of it, which is how a working system reads as a stopped one. Three dots and a
   counter — the counter ticks through a text node, never a repaint (see tlThinkFor). */
body.producer-skin .pd-typing { display: inline-flex; align-items: center; gap: 3px; margin-right: 7px; }
body.producer-skin .pd-typing i { width: 4px; height: 4px; border-radius: 50%; background: currentColor; opacity: .3; animation: pdTyping 1.15s ease-in-out infinite; }
body.producer-skin .pd-typing i:nth-child(2) { animation-delay: .16s; }
body.producer-skin .pd-typing i:nth-child(3) { animation-delay: .32s; }
body.producer-skin .pd-think-t { margin-left: 7px; font-variant-numeric: tabular-nums; opacity: .75; }
body.producer-skin .pd-think-t:empty { margin-left: 0; }
@keyframes pdTyping { 0%, 62%, 100% { opacity: .25; transform: translateY(0); } 31% { opacity: .95; transform: translateY(-2px); } }
@media (prefers-reduced-motion: reduce) { body.producer-skin .pd-typing i { animation: none; opacity: .5; } }
body.producer-skin .pd-say.is-micro { font-size: .75rem; color: var(--pd-ink-4); }

/* @SHOT MENTION CHIPS (spec §3.3.7, target 05-step3) — 24px thumb + label, inline in the prose. */
body.producer-skin .pd-mention { display: inline-flex; align-items: center; gap: 5px; vertical-align: -5px; height: 22px; padding: 0 8px 0 3px; border-radius: 99px; background: var(--pd-card-2); border: 1px solid var(--pd-line-2); color: var(--pd-ink); font: 600 .75rem/1 var(--sn-sans); }
body.producer-skin .pd-mention img { width: 17px; height: 17px; border-radius: 4px; object-fit: cover; display: block; }

/* ---- ELICITATION CARD (spec §3.3.5, target 04-step2-directors-seat) -------------------------------
   One question at a time. Numbered keycap chips (the number keys work), full-width 44px option rows, a
   Custom… input and Skip. This replaces V1's loose chip row, which read as a filter bar. */
body.producer-skin .pd-ask-host:empty { display: none; }
body.producer-skin .pd-ask-host { max-width: 540px; margin: 16px auto 0; text-align: left; }
body.producer-skin .pd-ask { background: var(--pd-card); border: 1px solid var(--pd-line-2); border-radius: var(--pd-r-modal); overflow: hidden; }
body.producer-skin .pd-ask-head { display: flex; align-items: center; gap: 10px; padding: 13px 14px 11px; }
body.producer-skin .pd-ask-head b { flex: 1; font: 600 .8125rem/1.4 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-ask-pager { font: .6875rem/1 var(--sn-sans); color: var(--pd-ink-4); font-variant-numeric: tabular-nums; }
body.producer-skin .pd-ask-x { width: 22px; height: 22px; display: grid; place-items: center; border: 0; border-radius: 6px; background: none; color: var(--pd-ink-3); cursor: pointer; }
body.producer-skin .pd-ask-x:hover { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-ask-opts { display: flex; flex-direction: column; gap: 6px; padding: 0 10px 10px; }
body.producer-skin .pd-ask-opt { display: flex; align-items: center; gap: 11px; width: 100%; min-height: 44px; padding: 6px 12px; border: 1px solid var(--pd-line); border-radius: 10px; background: var(--pd-sunk); color: var(--pd-ink-2); font: .8125rem/1.4 var(--sn-sans); text-align: left; cursor: pointer; }
body.producer-skin .pd-ask-opt:hover { background: var(--pd-card-2); border-color: var(--pd-line-2); color: var(--pd-ink); }
body.producer-skin .pd-ask-key { flex: none; width: 20px; height: 20px; display: grid; place-items: center; border-radius: 5px; background: var(--pd-card-2); border: 1px solid var(--pd-line-2); color: var(--pd-ink-3); font: 600 .6875rem/1 var(--sn-sans); }
body.producer-skin .pd-ask-opt:hover .pd-ask-key { color: var(--pd-ink); }
body.producer-skin .pd-ask-opt .pd-ask-hint { color: var(--pd-ink-4); font-size: .75rem; }
body.producer-skin .pd-ask-foot { display: flex; align-items: center; gap: 8px; padding: 0 10px 10px; }
body.producer-skin .pd-ask-custom { flex: 1; min-width: 0; height: 36px; padding: 0 11px; border: 1px solid var(--pd-line); border-radius: 9px; background: var(--pd-sunk); color: var(--pd-ink); font: .8125rem var(--sn-sans); outline: none; }
body.producer-skin .pd-ask-custom::placeholder { color: var(--pd-ink-4); }
body.producer-skin .pd-ask-custom:focus { border-color: var(--pd-accent-line); }
body.producer-skin .pd-ask-skip { flex: none; height: 36px; padding: 0 12px; border: 0; border-radius: 9px; background: none; color: var(--pd-ink-3); font: 500 .8125rem var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-ask-skip:hover { color: var(--pd-ink); }

/* THE PITCH CARD (spec §2.3) — the elicitation card's shape, given a second line per option.
   A direction is not a filter value: it needs its name, what it MEANS as a film, and which one we would
   pick. So the row grows a description line and the keycap pins to the top of it rather than floating in
   the middle of two lines of prose. Every colour is a token — this is chrome, and chrome takes the
   studio's own surfaces in both themes (R3 directive #3). */
body.producer-skin .pd-ask-opt.is-tall { align-items: flex-start; padding: 11px 12px; }
body.producer-skin .pd-ask-opt.is-tall > span:last-child { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
body.producer-skin .pd-ask-opt.is-tall .pd-ask-key { margin-top: 1px; }
body.producer-skin .pd-pitch-n { display: flex; align-items: center; gap: 7px; color: var(--pd-ink); font-weight: 600; }
/* "my pick" is the one accent on this card, because the recommendation IS the act — everything else is
   the director talking. Credit-green stays reserved for money-positive; ember is for the choice. */
body.producer-skin .pd-pitch-rec { flex: none; padding: 1px 6px; border-radius: 999px; background: var(--pd-accent-dim); border: 1px solid var(--pd-accent-line); color: var(--pd-accent); font: 600 .625rem/1.6 var(--sn-sans); font-style: normal; letter-spacing: .02em; }
body.producer-skin .pd-pitch-l { color: var(--pd-ink-3); font-size: .75rem; line-height: 1.55; }
body.producer-skin .pd-pitch-why { margin: 0; padding: 0 14px 11px; font: .75rem/1.6 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-pitch-why b { color: var(--pd-ink-2); font-weight: 600; }
body.producer-skin .pd-pitch .pd-ask-custom { font-size: .8125rem; }

/* THE ANSWERED CARD COLLAPSES TO A RECEIPT (observed t1770 → t2370). The decision stays visible in the
   transcript without the card's bulk — a compact highlighted Q/A pair, right-aligned like the answer it
   records. This is what keeps a long thread readable instead of a stack of spent forms. */
body.producer-skin .pd-receipt { align-self: flex-end; max-width: 88%; border-radius: 8px; background: var(--pd-accent-dim); border: 1px solid var(--pd-accent-line); padding: 7px 10px; font: .75rem/1.5 var(--sn-sans); color: var(--pd-ink-2); }
body.producer-skin .pd-receipt b { color: var(--pd-ink); font-weight: 600; }

/* ---- THE WORKING CHECKLIST CARD (spec §3.3.6, targets f210/f215, animation t1706–t1714) -----------
   A glowing outlined card with a LIVE timer, checklist rows, and thumbnails that pop in as they finish.
   This is the in-thread progress V1 never had: its progress lived only on a board header, so the chat
   went silent for the entire render — the single loudest "is it broken?" moment in the product. */
/* §62: hairline, one flat surface, no ring and no gradient — a working card is still just a card. */
body.producer-skin .pd-work { border-radius: 14px; border: 1px solid var(--pd-line); background: var(--pd-card); padding: 12px; display: flex; flex-direction: column; gap: 9px; }
body.producer-skin .pd-work-t { display: flex; align-items: center; gap: 7px; font: 600 .75rem/1 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-work-t .sn-ic { width: 13px; height: 13px; color: var(--pd-accent); animation: pd-flicker 2.4s steps(1, end) infinite; }
@keyframes pd-flicker { 0%, 62% { color: var(--pd-accent); } 63%, 100% { color: var(--pd-ink-4); } }
body.producer-skin .pd-work-row { display: flex; align-items: flex-start; gap: 9px; font: .75rem/1.5 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-work-row.is-done { color: var(--pd-ink-3); }
body.producer-skin .pd-work-row.is-live { color: var(--pd-amber); }
body.producer-skin .pd-work-row.is-fail { color: var(--pd-red); }
body.producer-skin .pd-work-ic { flex: none; width: 14px; height: 14px; margin-top: 1px; display: grid; place-items: center; border-radius: 50%; }
body.producer-skin .pd-work-row.is-done .pd-work-ic { color: var(--pd-ink-4); }
body.producer-skin .pd-work-row.is-done .pd-work-ic .sn-ic { width: 12px; height: 12px; }
/* the active row's label rotates THROUGH an ellipsis rather than cutting between labels (t1706–t1714) */
body.producer-skin .pd-work-dots i { display: inline-block; width: 3px; height: 3px; margin: 0 1px 2px; border-radius: 50%; background: currentColor; animation: pd-dots 1.4s ease-in-out infinite; }
body.producer-skin .pd-work-dots i:nth-child(2) { animation-delay: .18s; }
body.producer-skin .pd-work-dots i:nth-child(3) { animation-delay: .36s; }
@keyframes pd-dots { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }
/* thumbnails pop in as each still lands */
body.producer-skin .pd-work-thumbs { display: flex; gap: 5px; flex-wrap: wrap; padding-left: 23px; }
body.producer-skin .pd-work-thumbs img { width: 56px; height: 34px; object-fit: cover; border-radius: 5px; display: block; animation: pd-pop var(--pd-fade) ease-out; }
@keyframes pd-pop { from { opacity: 0; } to { opacity: 1; } }
/* grey skeleton blocks for sections that have not started yet (t2370) */
body.producer-skin .pd-work-skel { height: 34px; border-radius: 5px; background: linear-gradient(100deg, var(--pd-skel) 32%, var(--pd-skel-2) 50%, var(--pd-skel) 68%); background-size: 200% 100%; animation: sl-shimmer 1.8s linear infinite; margin-left: 23px; }
body.producer-skin .pd-work-audio { display: inline-flex; align-items: center; gap: 7px; margin-left: 23px; padding: 6px 10px; border-radius: 7px; background: var(--pd-violet-dim); border: 1px solid var(--pd-violet-line); color: var(--pd-violet-ink); font: .6875rem var(--sn-sans); }

/* ---- DIFF / REGION / GATE CARDS — V1's content, in the elicitation card's body style ------------- */
body.producer-skin .pd-card { background: var(--pd-card); border: 1px solid var(--pd-line-2); border-radius: var(--pd-r-modal); padding: 13px 14px; display: flex; flex-direction: column; gap: 10px; }
body.producer-skin .pd-card-h { display: flex; align-items: center; gap: 8px; font: 600 .8125rem/1.3 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-card-h .sn-ic { width: 14px; height: 14px; color: var(--pd-accent); }
body.producer-skin .pd-card-sub { font: .75rem/1.55 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-card-acts { display: flex; gap: 7px; flex-wrap: wrap; }
body.producer-skin .pd-money { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding-top: 9px; border-top: 1px solid var(--pd-line); }
body.producer-skin .pd-money span { font: .75rem var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-money b { font: 700 .9375rem var(--sn-sans); color: var(--pd-ink); font-variant-numeric: tabular-nums; }

/* the old→new diff lines keep V1's information design; only the chrome changes */
body.producer-skin .pd-diff-l { font: .75rem/1.55 var(--sn-sans); }
body.producer-skin .pd-diff-l .k { display: block; font: 600 .625rem/1 var(--sn-sans); letter-spacing: .06em; text-transform: uppercase; color: var(--pd-ink-4); margin-bottom: 3px; }
body.producer-skin .pd-diff-l del { display: block; color: var(--pd-ink-4); text-decoration-color: var(--pd-ink-4); }
body.producer-skin .pd-diff-l ins { display: block; color: var(--pd-ink-2); text-decoration: none; }

body.producer-skin .pd-card-sub.is-good { color: var(--pd-green-ink); }
body.producer-skin .pd-rg-ask { margin: 0; font: 500 .8125rem/1.5 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-rg-steps { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 7px; }
body.producer-skin .pd-rg-steps li { font: .75rem/1.45 var(--sn-sans); }
body.producer-skin .pd-rg-steps b { display: block; color: var(--pd-ink-2); font-weight: 600; }
body.producer-skin .pd-rg-steps span { color: var(--pd-ink-4); }
body.producer-skin .pd-diff-l > div { padding: 7px 0; border-bottom: 1px solid var(--pd-line); }
body.producer-skin .pd-diff-l > div:last-child { border-bottom: 0; }
body.producer-skin .pd-diff-l .is-bad ins { color: var(--pd-red-ink); }

/* HOW A SCENE ARRIVES (Film OS Phase 3.2) — a chip on the scene card and on the timeline tile, and the
   note under the picker. It is STORED INTENT: the stitch is a straight cut and this does not change that,
   which is why the chip is quiet chrome rather than a status colour — a badge that looked like "rendered"
   would be the interface claiming an effect the engine does not produce. */
body.producer-skin .ts-trans,
body.producer-skin .tl-film-tr { position: absolute; left: 6px; bottom: 6px; z-index: 2; padding: 2px 6px; border-radius: 999px; border: 1px solid var(--pd-line-2); background: color-mix(in srgb, var(--pd-card) 82%, transparent); color: var(--pd-ink-4); font: 600 .5625rem/1.3 var(--sn-sans); letter-spacing: .04em; text-transform: uppercase; white-space: nowrap; pointer-events: none; }
body.producer-skin .sn-menu-note { padding: 8px 12px 10px; border-top: 1px solid var(--pd-line); color: var(--pd-ink-4); font: .6875rem/1.45 var(--sn-sans); max-width: 260px; }

/* AN INGREDIENT TURN IS THE SAME CARD, DIFFERENTLY HEADED (Film OS Phase 3.3) — what the picture DOES,
   with the picture. The money footer below it is the diff card's own, unchanged: a price rendered by two
   pieces of markup is two surfaces to keep in step with the confirm sheet. */
body.producer-skin .pd-diff-pics { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
body.producer-skin .pd-diff-pics img { width: 52px; height: 52px; object-fit: cover; border-radius: 7px; border: 1px solid var(--pd-line-2); background: var(--pd-card-2); }

/* ---- BUTTONS: exactly one of them is a money button ---------------------------------------------- */
/* §62 — GHOST BY DEFAULT. Rob: "OpenArt buttons are small, evenly sized, mostly ghost/text, with
   exactly ONE filled accent button per context. Ours are chunky filled orange everywhere." A filled
   surface on every button is what made the old sheet shout: when everything is emphasised, the eye has
   nowhere to land. The default is now transparent with a hairline; `.is-accent` is the one exception. */
/* 21st.dev is a shadcn REGISTRY, so shadcn/ui is the source of truth underneath it. This is its button
   cva base, ported rather than approximated:
     inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-all
     outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50
     disabled:opacity-50 [&_svg]:size-4
   gap-2 = 8px, text-sm = 14px, font-medium = 500, rounded-md = --pd-r-ctl, the focus ring is 3px of
   NEUTRAL --ring at 50%, a disabled control is opacity .5 rather than a different colour, and icons are
   16px. The default here is shadcn's `outline` variant, because the Producer's default button is a
   bordered ghost. */
body.producer-skin .pd-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: var(--pd-h-md); padding: 0 16px;
  border: 1px solid var(--pd-line); border-radius: var(--pd-r-ctl);
  background: transparent; color: var(--pd-ink);
  font: 500 .875rem/1 var(--sn-sans);
  box-shadow: var(--pd-shadow-xs);
  cursor: pointer; outline: none;
  transition: background-color 150ms var(--sn-ease), color 150ms var(--sn-ease), border-color 150ms var(--sn-ease);
}
body.producer-skin .pd-btn:focus-visible { border-color: var(--pd-ring); box-shadow: 0 0 0 3px color-mix(in srgb, var(--pd-ring) 50%, transparent); }
body.producer-skin .pd-btn:disabled { opacity: .5; pointer-events: none; }
body.producer-skin .pd-btn .sn-ic { width: 16px; height: 16px; }
body.producer-skin .pd-btn:hover:not(:disabled) { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-btn:hover:not(:disabled) { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-btn:active:not(:disabled) { transform: scale(.96); }
body.producer-skin .pd-btn:disabled { opacity: .4; cursor: default; }
body.producer-skin .pd-btn .sn-ic { width: 14px; height: 14px; }
body.producer-skin .pd-btn.is-ghost { background: none; border-color: transparent; color: var(--pd-ink-3); }
body.producer-skin .pd-btn.is-ghost:hover:not(:disabled) { background: var(--pd-card); color: var(--pd-ink); }
/* THE MONEY BUTTON. Ember gradient, and the price is printed ON it — every paid click in the Producer
   carries its own figure, in credits, before it is clicked (spec §5.3). */
/* THE one filled control in a context. §65: it wears the MARK'S OWN GRADIENT — this is the hero primary,
   the one place the brand gradient is earned. Small accents (the active view pill, chips, focus rings)
   keep the flat `--pd-accent` mid stop, because a 3-stop gradient on a 28px pill reads as decoration. */
/* THE one primary in a context -- shadcn `bg-primary ... hover:bg-primary/90`. SOLID brand, no gradient. */
/* 🔵 §74 — THE GLOSS, from the one recipe in senova-ui.css. Every value is a token so there is exactly
   one place to tune the finish; see that file for the five layers and the contrast working. */
body.producer-skin .pd-btn.is-accent,
body.producer-skin .pd-act.is-primary,
body.producer-skin .pd-tab.is-active,
body.producer-skin .pd-send.is-ready {
  position: relative; isolation: isolate; overflow: hidden;
  background-color: var(--sn-gloss-base);
  background-image: var(--sn-gloss-spec), var(--sn-gloss-grad);
  color: var(--pd-on-accent); border-color: transparent; font-weight: 500;
  box-shadow: var(--sn-gloss-rim), var(--sn-gloss-depth), var(--sn-gloss-cast);
  transition: box-shadow 180ms var(--sn-ease), filter 180ms var(--sn-ease);
}
body.producer-skin .pd-btn.is-accent:hover:not(:disabled),
body.producer-skin .pd-act.is-primary:hover:not(:disabled),
body.producer-skin .pd-tab.is-active:hover,
body.producer-skin .pd-send.is-ready:hover:not(:disabled) {
  filter: brightness(1.06) saturate(1.03);
  box-shadow: var(--sn-gloss-rim), var(--sn-gloss-depth), var(--sn-gloss-cast-hi);
}
body.producer-skin .pd-btn.is-accent:active:not(:disabled),
body.producer-skin .pd-act.is-primary:active:not(:disabled),
body.producer-skin .pd-send.is-ready:active:not(:disabled) {
  filter: brightness(.97); box-shadow: inset 0 2px 4px rgba(0,0,0,.32), var(--sn-gloss-cast);
}
body.producer-skin .pd-btn.is-accent::after,
body.producer-skin .pd-act.is-primary::after,
body.producer-skin .pd-tab.is-active::after,
body.producer-skin .pd-send.is-ready::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(100deg, transparent 20%, rgba(255,255,255,.20) 42%, rgba(255,255,255,.38) 50%, rgba(255,255,255,.20) 58%, transparent 80%);
  transform: translateX(-130%);
}
body.producer-skin .pd-btn.is-accent:hover:not(:disabled)::after,
body.producer-skin .pd-act.is-primary:hover:not(:disabled)::after,
body.producer-skin .pd-tab.is-active:hover::after,
body.producer-skin .pd-send.is-ready:hover:not(:disabled)::after { animation: sn-sheen 1100ms var(--sn-ease) 1; }
@media (prefers-reduced-motion: reduce) {
  body.producer-skin .pd-btn.is-accent::after,
  body.producer-skin .pd-act.is-primary::after,
  body.producer-skin .pd-tab.is-active::after,
  body.producer-skin .pd-send.is-ready::after { display: none; }
}
/* §63: a translucent WHITE wash rather than a black one — on a magenta/orange fill a black chip reads
   as a hole punched in the button; white at 18% reads as part of it. */
body.producer-skin .pd-price { display: inline-flex; align-items: center; gap: 3px; padding: 2px 7px; border-radius: 99px; background: rgba(255, 255, 255, .18); font-variant-numeric: tabular-nums; font-size: .6875rem; }
body.producer-skin .pd-btn.is-free .pd-price { background: var(--pd-green-dim); color: var(--pd-green-ink); }

/* ---- THE COMPOSER, INSIDE THE CHAT PANEL, NEVER OVER THE STAGE (spec §3.3.1 / §4.5) --------------
   V1 docked it over the whole surface, where it covered the quote card's own total — the number the
   dock existed to help you decide about. It lives in the chat column now and cannot cover the film. */
/* THE COMPOSER SITS FLUSH IN THE COLUMN, IT IS NOT A CARD FLOATING IN IT (Rob, on the R3 screenshot).
   It was a white card with a 1px border and a 12px radius sitting on a tinted column — which read as a
   box because it WAS one. On a white column that same card is a rounded outline drawn around nothing:
   the fill already matches its surroundings, so all the border does is announce a container. So the
   footer keeps only the one line that carries information — a hairline saying where the thread stops and
   the place you type begins — and the send button and icon controls carry the affordance, as they do in
   every other composer in the studio.
   THE HOME HERO KEEPS ITS CARD. `.pd-hero-comp` sits on a photographic hero band, not on the panel; a
   borderless input there would have nothing behind it to sit flush WITH. Splitting the shared rule is
   the point of this change, not an oversight. */
/* THE !important IS LOAD-BEARING AND SCOPED, AND HERE IS WHY IT HAS TO BE.
   `studio-system.css` carries `.composer.composer, #sl-composer.composer, .gen-chat #sl-composer {
   border: 1px solid var(--sn-hairline-strong) !important; box-shadow: var(--sn-elev-2) !important }` —
   Rob's own rule, added to beat the id-based orange focus rings in studio-ambient/elevate/live. It is
   right, and it is global, so no amount of plain specificity here can flatten the Producer's composer;
   the first attempt at this change looked correct in the CSS and left the box on screen. The override is
   therefore matched in kind — !important AND the same `#sl-composer` id, because an id outranks any
   number of classes and a class-only override loses however long you make it — and confined to
   `.pd-composer`, the chat column's footer and nothing else.
   Generate, the chat surfaces and the hero keep their hairline card exactly as they are.
   The focus shadow goes with it: a borderless footer that grows an elevation on focus is the same
   floating box again, arriving a keystroke later. */
/* 🔵 §71 — THE COMPOSER IS A shadcn INPUT, not a bare textarea under a rule.
   shadcn's field anatomy: `rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs
   transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50`.
   The composer is a field CONTAINING controls, so the border and the focus ring belong to the container and
   the textarea inside it stays chromeless — otherwise you get a box inside a box, which is what the old
   `.cmp-bar` fill was doing. */
/* 🔵 §73 — THE PLAN AND THE COMPOSER WERE ONE UNDIFFERENTIATED STACK. Rob asked for "clear separation
   between the running plan and the composer": the thread scrolls, the composer does not, and nothing on
   screen said so. A hairline above it is what every chat surface uses to make the composer a FLOOR rather
   than the last paragraph — and it is the canvas colour behind it that stops the thread appearing to run
   underneath. */
body.producer-skin .pd-composer {
  flex: none; padding: 12px 16px 16px;
  border-top: 1px solid var(--pd-line); background: var(--pd-canvas);
}
body.producer-skin .pd-composer #sl-composer.composer {
  background: var(--pd-sunk) !important;
  border: 1px solid var(--pd-line-2) !important;
  border-radius: var(--pd-r-ctl) !important;
  box-shadow: var(--pd-shadow-xs) !important;
  padding: 10px 12px !important;
  transition: border-color 150ms var(--sn-ease), box-shadow 150ms var(--sn-ease);
}
body.producer-skin .pd-composer #sl-composer.composer:focus-within {
  border-color: var(--pd-ring) !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--pd-ring) 50%, transparent) !important;
}
body.producer-skin .pd-hero-comp .composer { background: var(--pd-card); border: 1px solid var(--pd-line-2); border-radius: var(--pd-r-player); box-shadow: none; padding: 10px 12px; }
body.producer-skin .pd-composer .composer textarea,
body.producer-skin .pd-hero-comp .composer textarea { color: var(--pd-ink); font: .875rem/1.6 var(--sn-sans); min-height: 40px; }
body.producer-skin .pd-composer .composer textarea::placeholder,
body.producer-skin .pd-hero-comp .composer textarea::placeholder { color: var(--pd-ink-4); font-style: normal; }
body.producer-skin .pd-composer .composer .bar,
body.producer-skin .pd-hero-comp .composer .bar { border-top-color: var(--pd-line); }
/* AND THE ONE THAT WAS ACTUALLY DRAWING THE BOX. The rounded outline around the composer was not the
   composer — it was `.cmp-bar`, the shared control tray, filled #F5F4EE with a 12px radius by
   studio-live.css. That is the exact off-white Rob pointed at, and it is a shape the studio's own chat
   surfaces do not draw. On a white column the tray needs no fill of its own: the controls inside it are
   already outlined. Measured before changing it, rather than guessed at from the flattened `.composer`
   rule above, which was transparent and square and still left a visible box on screen. */
body.producer-skin .pd-composer .composer .bar.cmp-bar { background: none; border-radius: 0; }
/* 🔴 §63 — THE PRIMARY BUTTON WAS WRAPPING ONTO TWO LINES, and it is the most important control on the
   screen. In Rob's capture of the real Producer, "Plan the film  50 cr" breaks after "Plan the" and the
   button grows to two rows while the model picker beside it keeps its full width. Flex children default to
   `min-width:auto`, so the picker (a long string — "1m · Seedance 1.0 Pro Fast") refuses to shrink and the
   button is the one that gives. The fix is to let the PICKER be the flexible one and pin the button:
   the thing that spends never re-flows to make room for a label. */
body.producer-skin .pd-comp-row { display: flex; align-items: center; gap: 6px; margin-top: 6px; }
body.producer-skin .pd-comp-row .cmp-adv { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.producer-skin .pd-comp-row > .pd-btn { flex: none; white-space: nowrap; }
body.producer-skin .pd-comp-row .grow { flex: 1; }
body.producer-skin .pd-icb { width: 28px; height: 28px; display: grid; place-items: center; border: 1px solid var(--pd-line-2); border-radius: 50%; background: none; color: var(--pd-ink-3); cursor: pointer; }
body.producer-skin .pd-icb:hover { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-icb .sn-ic { width: 14px; height: 14px; }
/* SEND: idle accent ↑ circle → white ■ stop while the Director works (t2112 vs t1712). Stop is a real
   control, not decoration — it is wired to abort the turn. */
body.producer-skin .pd-send { width: 30px; height: 30px; display: grid; place-items: center; border: 0; border-radius: 50%; background: var(--pd-card-2); color: var(--pd-ink-4); cursor: pointer; transition: transform 120ms var(--sn-ease); }
/* §74: .pd-send.is-ready is glossed with the primaries above. */
body.producer-skin .pd-send.is-stop { background: var(--pd-stop-bg); color: var(--pd-card); }
body.producer-skin .pd-send:active { transform: scale(.94); }
body.producer-skin .pd-send .sn-ic { width: 15px; height: 15px; }
/* WHAT AN ANSWER COSTS, ON THE CONTROL (chat pricing, Phase 39). A quiet chip, not a warning: it sits at
   the same weight as the model picker beside it because it is the same kind of fact — a standing property
   of the next message, not an alert about this one. The figure is bolded and the rest is muted, so the
   thing a returning customer re-reads at a glance is the number. */
body.producer-skin .pd-cost { display: inline-flex; align-items: center; gap: 4px; height: 26px; padding: 0 8px; border-radius: 7px; color: var(--pd-ink-4); font: 500 .7rem/1 var(--sn-sans); white-space: nowrap; cursor: default; }
body.producer-skin .pd-cost b { color: var(--pd-ink-2); font-weight: 600; }
/* On a narrow panel the model picker and the send button are the controls that must survive; the price
   keeps its number and drops its explanation rather than wrapping the row onto two lines. */
@media (max-width: 560px) { body.producer-skin .pd-cost { font-size: 0; padding: 0 4px; } body.producer-skin .pd-cost b { font-size: .6875rem; } }
body.producer-skin .pd-mode { display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 9px; border: 0; border-radius: 7px; background: none; color: var(--pd-ink-3); font: 500 .75rem/1 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-mode:hover { background: var(--pd-card-2); color: var(--pd-ink); }

/* ---- SHARED STUDIO WIDGETS, RE-TONED FOR THE DARK PANEL ------------------------------------------
   The composer reuses the studio's own attach strip, advanced panel and segmented control so the two
   surfaces keep ONE set of behaviours. Only their colour and, for the ⋯ button, their forced 38px square
   geometry are overridden — the wiring underneath is untouched. */
body.producer-skin .composer .cmp-adv.pd-mode { width: auto; height: 26px; padding: 0 9px; display: inline-flex; align-self: auto; border: 0; border-radius: 7px; background: none; color: var(--pd-ink-3); }
body.producer-skin .composer .cmp-adv.pd-mode:hover, body.producer-skin .composer.adv-open .cmp-adv.pd-mode { background: var(--pd-card-2); color: var(--pd-ink); border-color: transparent; }
body.producer-skin .composer .cmp-adv.pd-mode .sn-ic-slot { width: 12px; height: 12px; }
body.producer-skin .composer.is-slim .cmp-adv-panel { background: var(--pd-sunk); border: 1px solid var(--pd-line); }
body.producer-skin .composer .seg-lbl, body.producer-skin .composer .cmp-grp-lbl { color: var(--pd-ink-4); }
body.producer-skin .composer .sn-seg { background: var(--pd-sunk); border-color: var(--pd-line-2); }
body.producer-skin .composer .sn-seg button { color: var(--pd-ink-3); }
body.producer-skin .composer .sn-seg button.is-active { background: var(--pd-accent); color: var(--pd-on-accent); }
body.producer-skin .composer .model-pick { background: var(--pd-sunk); border-color: var(--pd-line-2); color: var(--pd-ink-2); }
body.producer-skin .composer .sn-btn-secondary { background: var(--pd-sunk); border-color: var(--pd-line-2); color: var(--pd-ink-2); }
body.producer-skin .composer .cmp-attach { background: none; border-color: var(--pd-line-2); color: var(--pd-ink-3); width: 28px; height: 28px; }
body.producer-skin .composer .cmp-attach:hover { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .cmp-note { color: var(--pd-ink-3); }
body.producer-skin .cmp-note.err { color: var(--pd-red-ink); }
/* the pre-plan credits estimate rail, under the hero composer */
body.producer-skin .tl-est { margin: 12px auto 0; max-width: 790px; display: flex; align-items: center; justify-content: center; gap: 9px; font: .75rem/1.5 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .tl-est b, body.producer-skin .tl-est strong { color: var(--pd-green); }

/* the @-mention picker (typing `@` in the composer) */
body.producer-skin .pd-atlist { position: absolute; z-index: 40; min-width: 230px; max-height: 260px; overflow: auto; background: var(--pd-card); border: 1px solid var(--pd-line-2); border-radius: 10px; box-shadow: var(--pd-elev-3); padding: 5px; }
body.producer-skin .pd-atlist-g { font: 600 .625rem/1 var(--sn-sans); letter-spacing: .06em; text-transform: uppercase; color: var(--pd-ink-4); padding: 8px 8px 5px; }
body.producer-skin .pd-atlist button { display: flex; align-items: center; gap: 8px; width: 100%; padding: 5px 7px; border: 0; border-radius: 7px; background: none; color: var(--pd-ink-2); font: .75rem var(--sn-sans); cursor: pointer; text-align: left; }
body.producer-skin .pd-atlist button:hover, body.producer-skin .pd-atlist button.is-on { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-atlist img { width: 34px; height: 20px; object-fit: cover; border-radius: 4px; flex: none; }

/* =====================================================================================================
   3. SCENE TAB — the storyboard (spec §3.4, target f265, hover t2117, add-shot morph t2124)
   ===================================================================================================== */

body.producer-skin .pd-board { display: flex; flex-direction: column; gap: 9px; padding-top: 4px; }
body.producer-skin .pd-board-top { display: flex; align-items: center; gap: 9px; padding: 2px 2px 4px; }
body.producer-skin .pd-board-top .grow { flex: 1; }
body.producer-skin .pd-toggle { display: inline-flex; align-items: center; gap: 8px; font: 500 .75rem/1 var(--sn-sans); color: var(--pd-ink-3); background: none; border: 0; cursor: pointer; }
body.producer-skin .pd-toggle i { width: 30px; height: 17px; border-radius: 99px; background: var(--pd-line-2); position: relative; display: block; }
body.producer-skin .pd-toggle i::after { content: ""; position: absolute; top: 2px; left: 2px; width: 13px; height: 13px; border-radius: 50%; background: var(--pd-ink-4); }
body.producer-skin .pd-toggle[aria-pressed="true"] i { background: var(--pd-accent); }
body.producer-skin .pd-toggle[aria-pressed="true"] i::after { left: 15px; background: #fff; }

/* a scene ROW: drag handle, caret, SCENE N, and the beat's own one-line logline */
body.producer-skin .pd-scene { border: 1px solid var(--pd-line); border-radius: var(--pd-r-card); background: var(--pd-panel); overflow: hidden; }
body.producer-skin .pd-scene-h { display: flex; align-items: center; gap: 9px; padding: 9px 11px; }
body.producer-skin .pd-grip { color: var(--pd-ink-4); cursor: grab; display: grid; place-items: center; width: 14px; }
body.producer-skin .pd-grip .sn-ic { width: 13px; height: 13px; }
body.producer-skin .pd-caret { width: 18px; height: 18px; display: grid; place-items: center; border: 0; border-radius: 4px; background: none; color: var(--pd-ink-3); cursor: pointer; }
body.producer-skin .pd-scene-n { font: 700 .6875rem/1 var(--sn-sans); letter-spacing: .07em; color: var(--pd-ink); }
body.producer-skin .pd-scene-log { flex: 1; min-width: 0; font: .75rem/1.4 var(--sn-sans); color: var(--pd-ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.producer-skin .pd-scene-cr { font: 600 .6875rem/1 var(--sn-sans); color: var(--pd-ink-4); font-variant-numeric: tabular-nums; }
body.producer-skin .pd-caret .sn-ic { width: 12px; height: 12px; }
body.producer-skin .pd-caret:hover { background: var(--pd-card-2); color: var(--pd-ink); }
/* THE ROW IS NOT MOSTLY EMPTY. OpenArt fills the space beside a shot with an "Add Shot" ghost; our
   engine appends shots to the END of the board (addShot takes MAX(position)+1 and no index), so a
   per-row add tile would say "here" and mean "last". The space carries the scene's actual beat
   instead — the direction and its spoken line, which is what V1 printed under the card anyway. */
body.producer-skin .pd-scene-beat { flex: 1; min-width: 180px; list-style: none; padding: 2px 0 0; }
body.producer-skin .pd-scene-beat p { margin: 0; font: .75rem/1.6 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-scene-beat p.vo { margin-top: 7px; display: flex; gap: 6px; align-items: flex-start; color: var(--pd-violet-ink); font-style: italic; }
body.producer-skin .pd-scene-beat p.vo .sn-ic { width: 12px; height: 12px; flex: none; margin-top: 3px; }
body.producer-skin .pd-scene.is-collapsed .pd-shots { display: none; }
body.producer-skin .pd-shots { display: flex; gap: 9px; flex-wrap: wrap; padding: 0 11px 11px; }

/* SHOT CARD. 16:9 thumb, label overlay bottom-left. The imagery is the card. */
body.producer-skin .pd-shot { position: relative; width: 185px; flex: none; }
body.producer-skin .pd-shot-fr { position: relative; width: 185px; height: 100px; border-radius: 8px; overflow: hidden; background: var(--pd-card); border: 1px solid var(--pd-line); }
body.producer-skin .pd-shot-fr img, body.producer-skin .pd-shot-fr video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
body.producer-skin .pd-shot-lb { position: absolute; left: 7px; bottom: 6px; z-index: 2; display: inline-flex; align-items: center; gap: 4px; font: 700 .625rem/1 var(--sn-sans); letter-spacing: .05em; color: #fff; text-shadow: 0 1px 4px rgba(0, 0, 0, .9); }
body.producer-skin .pd-shot-dur { position: absolute; right: 7px; bottom: 6px; z-index: 2; font: 600 .625rem/1 var(--sn-sans); color: rgba(255, 255, 255, .8); text-shadow: 0 1px 4px rgba(0, 0, 0, .9); font-variant-numeric: tabular-nums; }

/* HOVER: a floating pill at the card's TOP EDGE and a grip outside the thumbnail. NO SCRIM — the
   observed truth at t2117 is that the imagery never darkens on hover, and darkening it was one of the
   things that made V1 feel like a file manager rather than a cutting room. */
body.producer-skin .pd-shot-pill { position: absolute; top: 6px; right: 6px; z-index: 3; display: none; align-items: center; gap: 1px; padding: 2px; border-radius: 7px; background: var(--pd-float); border: 1px solid var(--pd-line-2); box-shadow: var(--pd-elev-1); }
body.producer-skin .pd-shot:hover .pd-shot-pill, body.producer-skin .pd-shot:focus-within .pd-shot-pill { display: flex; }
body.producer-skin .pd-shot-pill button { display: inline-flex; align-items: center; gap: 4px; height: 22px; padding: 0 7px; border: 0; border-radius: 5px; background: none; color: var(--pd-ink-2); font: 600 .6875rem/1 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-shot-pill button:hover { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-shot-pill .sn-ic { width: 12px; height: 12px; }
body.producer-skin .pd-shot-grip { position: absolute; top: 4px; left: -12px; z-index: 3; display: none; color: var(--pd-ink-4); cursor: grab; }
body.producer-skin .pd-shot:hover .pd-shot-grip { display: block; }

/* the ⋮ menu opens FULLY FORMED — no transition, matching t2112 */
body.producer-skin .pd-menu { position: absolute; z-index: 60; min-width: 176px; padding: 5px; border-radius: 10px; background: var(--pd-card); border: 1px solid var(--pd-line-2); box-shadow: var(--pd-elev-3); }
body.producer-skin .pd-menu button { display: flex; align-items: center; gap: 9px; width: 100%; height: 30px; padding: 0 9px; border: 0; border-radius: 6px; background: none; color: var(--pd-ink-2); font: .75rem var(--sn-sans); cursor: pointer; text-align: left; }
body.producer-skin .pd-menu button:hover { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-menu button.is-danger:hover { color: var(--pd-red-ink); }
body.producer-skin .pd-menu .sn-ic { width: 13px; height: 13px; }
body.producer-skin .pd-menu hr { border: 0; border-top: 1px solid var(--pd-line); margin: 4px 2px; }

/* CARD STATES. The badge is the ONLY status voice on a card, which is how the "STILLing still…"
   overprint (spec §4.4) becomes structurally impossible rather than merely fixed. */
body.producer-skin .pd-shot-badge { position: absolute; top: 6px; left: 6px; z-index: 2; height: 18px; padding: 0 7px; display: inline-flex; align-items: center; gap: 4px; border-radius: 99px; background: rgba(10, 10, 10, .78); backdrop-filter: blur(5px); color: var(--pd-ink-2); font: 600 .5625rem/1 var(--sn-sans); letter-spacing: .05em; text-transform: uppercase; }
body.producer-skin .pd-shot-badge.is-filmed { color: var(--pd-green-ink); }
body.producer-skin .pd-shot-badge.is-working { color: var(--pd-amber); }
body.producer-skin .pd-shot-badge.is-failed { color: var(--pd-red-ink); }
body.producer-skin .pd-shot.is-working .pd-shot-fr { border-color: var(--pd-accent-line); }
body.producer-skin .pd-shot.is-working .pd-shot-fr img { opacity: .72; }
body.producer-skin .pd-shot-skel { position: absolute; inset: 0; background: linear-gradient(100deg, var(--pd-skel) 32%, var(--pd-skel-2) 50%, var(--pd-skel) 68%); background-size: 200% 100%; animation: sl-shimmer 1.8s linear infinite; }
body.producer-skin .pd-shot-spin { position: absolute; inset: 0; display: grid; place-items: center; z-index: 2; }
body.producer-skin .pd-shot-spin i { width: 20px; height: 20px; border-radius: 50%; border: 2px solid rgba(255, 255, 255, .18); border-top-color: var(--pd-accent); animation: sn-spin .9s linear infinite; display: block; }
/* A PLANNED shot shows a quiet placeholder GLYPH — never a caption laid over a delivered picture. */
body.producer-skin .pd-shot-ph { position: absolute; inset: 0; display: grid; place-items: center; color: var(--pd-ghost); }
body.producer-skin .pd-shot-ph .sn-ic { width: 22px; height: 22px; }

/* THE STORYBOARD TAB — the film as pictures, and nothing else on it.
   A wider frame than the Scene wall's 185px card on purpose: this room's whole job is that you can SEE
   the shot, and the card wall is already the place where every control lives. The grid is `auto-fill` at
   a min of 220px, so a four-scene film on a wide stage fills the row and a nine-scene film wraps instead
   of scrolling sideways.
   ⚠️ NO `content-visibility` and NO `loading="lazy"` on these frames, for the reason recorded at
   .tl-script-still in studio-live.css: the studio scrolls inside #stage, the document never scrolls, and
   content-visibility on an item feeding its own scroll container froze the page in production. */
body.producer-skin .pd-sbboard { padding-top: 4px; }
body.producer-skin .pd-sb-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; margin: 0; padding: 0; list-style: none; }
body.producer-skin .pd-sb-cell { min-width: 0; }
body.producer-skin .pd-sb-fr { position: relative; display: block; width: 100%; aspect-ratio: 16/9; padding: 0; border-radius: var(--pd-r-card); overflow: hidden; background: var(--pd-card); border: 1px solid var(--pd-line); cursor: pointer; }
body.producer-skin .pd-sb-fr img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
body.producer-skin .pd-sb-fr:hover { border-color: var(--pd-accent); }
body.producer-skin .pd-sb-fr:focus-visible { outline: none; border-color: var(--pd-accent); box-shadow: 0 0 0 3px var(--pd-accent-dim); }
body.producer-skin .pd-sb-n { position: absolute; left: 8px; top: 7px; z-index: 2; height: 19px; padding: 0 8px; display: inline-flex; align-items: center; border-radius: 99px; background: rgba(10, 10, 10, .78); backdrop-filter: blur(5px); color: #fff; font: 700 .625rem/1 var(--sn-sans); letter-spacing: .06em; font-variant-numeric: tabular-nums; }
body.producer-skin .pd-sb-d { position: absolute; right: 8px; bottom: 7px; z-index: 2; font: 600 .625rem/1 var(--sn-sans); color: rgba(255, 255, 255, .82); text-shadow: 0 1px 4px rgba(0, 0, 0, .9); font-variant-numeric: tabular-nums; }
/* A frame with no picture yet has no white text to hang over a dark still, so the duration reads off the
   card surface instead of off an image that is not there. */
body.producer-skin .pd-sb-cell:not(.has-still) .pd-sb-d { color: var(--pd-ink-4); text-shadow: none; }
body.producer-skin .pd-sb-err { position: absolute; inset: 0; display: grid; place-items: center; align-content: center; gap: 5px; padding: 8px; text-align: center; color: var(--pd-red-ink); font: .625rem/1.4 var(--sn-sans); }
body.producer-skin .pd-sb-err .sn-ic { width: 15px; height: 15px; }
body.producer-skin .pd-sb-log { margin: 7px 1px 0; font: .75rem/1.45 var(--sn-sans); color: var(--pd-ink-3);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
body.producer-skin .pd-sb-foot { margin: 18px 0 0; padding-top: 13px; border-top: 1px solid var(--pd-line); font: .75rem/1.5 var(--sn-sans); color: var(--pd-ink-4); }

/* ADHERENCE LEDGER BADGE (spec §5.1) — the ✓ that says the render was CHECKED against your own words. */
body.producer-skin .pd-adh { position: absolute; right: 6px; top: 6px; z-index: 3; display: inline-flex; align-items: center; gap: 4px; height: 18px; padding: 0 7px; border-radius: 99px; background: rgba(10, 10, 10, .78); backdrop-filter: blur(5px); font: 600 .5625rem/1 var(--sn-sans); letter-spacing: .04em; }
body.producer-skin .pd-adh.is-ok { color: var(--pd-green-ink); }
body.producer-skin .pd-adh.is-miss { color: var(--pd-amber); }
body.producer-skin .pd-shot:hover .pd-adh { display: none; }
body.producer-skin .pd-adh-note { margin-top: 5px; font: .6875rem/1.45 var(--sn-sans); color: var(--pd-amber); }

/* HOW WELL A SCENE HELD (Film OS Phase 7.3) — a dot and four words UNDER the frame, never over it. The
   frame's overlays are all status ("Filmed", "Drawing", "Failed") and this is not a status: it is an
   observation about a picture that is already there. A pill on the still would read as work in progress
   and — worse — as a correction being made. The retry loop is off; nothing here is being fixed, so
   nothing here gets a badge's urgency. `is-drift` is the rail's own warm ink rather than red for the
   same reason: drift is a note, not a failure.
   ⚠️ IT IS A SIBLING OF `.ts-frame`, NOT A CHILD OF `.ts-body`. `.pd-shot .ts-body` is `display: none`
   in this skin (see below), so the first placement rendered the right words at zero by zero pixels on
   every card on the board. `test/film-consistency-surface.test.mjs` pins the position. */
body.producer-skin .ts-cons { display: flex; align-items: center; gap: 6px; margin: 6px 0 0; padding: 0 2px; font: 600 .625rem/1.3 var(--sn-sans); letter-spacing: .03em; color: var(--pd-ink-4); cursor: help; }
body.producer-skin .ts-cons i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: none; opacity: .9; }
body.producer-skin .ts-cons.is-held { color: var(--pd-ink-3); }
body.producer-skin .ts-cons.is-drift { color: var(--pd-warm-ink); }
body.producer-skin .ts-cons.is-open { color: var(--pd-ghost); }

/* THE STORY BIBLE ON SCREEN (Film OS Phase 2.3) — "Holding to": the rules this film is keeping, in the
   customer's own words. Deliberately the QUIETEST block in the rail: it is a record of what was said, not
   a status, and anything that read as a verdict here would be the adherence ledger that was cut. So no
   colour, no tick, no state classes — a hairline rule per rule and the same ink as the rail's own text. */
body.producer-skin .pd-bible { margin-top: 8px; }
body.producer-skin .pd-bible-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; }
body.producer-skin .pd-bible-list li { padding: 6px 0 6px 9px; border-left: 2px solid var(--pd-line-2); font: .6875rem/1.45 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-bible-list li + li { border-top: 1px solid var(--pd-line); }
body.producer-skin .pd-bible .pd-rail-empty { margin-top: 8px; }

/* PER-SHOT REFERENCE PIN (spec §5.2) — a pin dot on the card, set by dropping a reference onto it. */
body.producer-skin .pd-pin { position: absolute; left: 6px; bottom: 22px; z-index: 3; width: 16px; height: 16px; border-radius: 50%; background: var(--pd-accent); color: var(--pd-on-accent); display: grid; place-items: center; box-shadow: var(--pd-elev-1); }
body.producer-skin .pd-pin .sn-ic { width: 9px; height: 9px; color: #fff; }
body.producer-skin .pd-shot.is-droptarget .pd-shot-fr { border-color: var(--pd-accent); box-shadow: 0 0 0 3px var(--pd-accent-dim); }

/* THE "ADD SHOT" GHOST TILE, and its inline morph into a two-option chooser (t2124/t2188). The TILE
   re-renders — this is deliberately not a popover, because a popover would cover the board. */
body.producer-skin .pd-add { width: 185px; height: 100px; flex: none; border: 1px dashed var(--pd-line-2); border-radius: 8px; background: var(--pd-sunk); color: var(--pd-ink-4); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; font: 600 .6875rem/1 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-add:hover { border-color: var(--pd-line-2); color: var(--pd-ink-3); background: var(--pd-card); }
body.producer-skin .pd-add .sn-ic { width: 16px; height: 16px; }
body.producer-skin .pd-add-open { width: 185px; height: 100px; flex: none; border: 1px solid var(--pd-line-2); border-radius: 8px; background: var(--pd-card); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px; padding: 8px; }
body.producer-skin .pd-add-open .k { font: 700 .5625rem/1 var(--sn-sans); letter-spacing: .07em; text-transform: uppercase; color: var(--pd-ink-4); }
/* §61: was 26px — a third height inside a row of .pd-btns. The dense variant is the CHROME tier. */
body.producer-skin .pd-add-open .pd-btn { height: var(--pd-h-sm); font-size: .6875rem; }
body.producer-skin .pd-newscene { display: flex; align-items: center; gap: 10px; width: 100%; padding: 13px; border: 1px dashed var(--pd-line-2); border-radius: var(--pd-r-card); background: var(--pd-sunk); color: var(--pd-ink-3); cursor: pointer; text-align: left; }
body.producer-skin .pd-newscene:hover { background: var(--pd-card); color: var(--pd-ink); }
body.producer-skin .pd-newscene b { display: block; font: 600 .8125rem/1.3 var(--sn-sans); }
body.producer-skin .pd-newscene span { display: block; font: .6875rem/1.4 var(--sn-sans); color: var(--pd-ink-4); }

/* MULTI-SELECT → one priced, opt-in batch click */
body.producer-skin .pd-check { position: absolute; left: 6px; top: 6px; z-index: 4; width: 17px; height: 17px; border-radius: 5px; border: 1px solid rgba(255, 255, 255, .45); background: rgba(10, 10, 10, .6); display: none; place-items: center; cursor: pointer; }
body.producer-skin .pd-shot:hover .pd-check, body.producer-skin .pd-shot.is-picked .pd-check, body.producer-skin .pd-board.is-picking .pd-check { display: grid; }
body.producer-skin .pd-shot.is-picked .pd-check { background: var(--pd-accent); border-color: var(--pd-accent); color: var(--pd-on-accent); }
body.producer-skin .pd-shot.is-picked .pd-shot-fr { border-color: var(--pd-accent); }
body.producer-skin .pd-batch { position: sticky; bottom: 10px; z-index: 20; align-self: center; display: inline-flex; align-items: center; gap: 11px; padding: 7px 8px 7px 14px; border-radius: 99px; background: var(--pd-float); border: 1px solid var(--pd-line-2); box-shadow: var(--pd-elev-3); font: 600 .75rem/1 var(--sn-sans); color: var(--pd-ink-2); }

/* THE SHOT CARD IS A NEW COMPONENT WEARING THE OLD ELEMENT'S CLASSES. `.tl-scene` and `.ts-*` stay on
   the markup because every wired control in wireTlScenes finds its shot through them — the card was
   rebuilt, not re-plumbed. These resets drop the warm Ink & Ivory card the old selectors would otherwise
   paint underneath, and hide the parts that moved (the body text, the editor) until they are needed. */
body.producer-skin .pd-shot.tl-scene { background: none; border: 0; padding: 0; margin: 0; box-shadow: none; border-radius: 0; animation: none; list-style: none; overflow: visible; }
body.producer-skin .pd-shot .ts-body { display: none; }
body.producer-skin .pd-shot.is-editing .ts-body { display: block; width: 185px; margin-top: 7px; }
body.producer-skin .pd-shot.is-editing .ts-title, body.producer-skin .pd-shot.is-editing .ts-line { display: none; }
body.producer-skin .pd-shot .ts-editor { display: none; }
body.producer-skin .pd-shot.is-editing .ts-editor { display: flex; flex-direction: column; gap: 6px; }
body.producer-skin .pd-shot .ts-edit, body.producer-skin .pd-shot .ts-chat-in { background: var(--pd-sunk); border-color: var(--pd-line-2); color: var(--pd-ink); }
body.producer-skin .pd-shot .ts-frame { border: 0; }
body.producer-skin .pd-shot .ts-dur { position: absolute; right: 7px; bottom: 6px; z-index: 2; font: 600 .625rem/1 var(--sn-sans); color: rgba(255, 255, 255, .82); text-shadow: 0 1px 4px rgba(0, 0, 0, .9); font-variant-numeric: tabular-nums; background: none; padding: 0; border: 0; }
/* the failure overlay keeps its own voice, and its retry button keeps the class the suite pins */
body.producer-skin .pd-shot .ts-err { position: absolute; inset: 0; z-index: 4; display: none; flex-direction: column; align-items: center; justify-content: center; gap: 5px; padding: 9px; text-align: center; background: rgba(10, 10, 10, .9); color: var(--pd-red-ink); font: .625rem/1.4 var(--sn-sans); }
body.producer-skin .pd-shot.is-failed .ts-err { display: flex; }
body.producer-skin .pd-shot .ts-err .sn-btn { height: 24px; padding: 0 9px; font-size: .625rem; }
body.producer-skin .pd-shot .ts-err-s { color: var(--pd-ink-4); }
body.producer-skin .pd-shot .ts-ord { display: flex; gap: 4px; margin-top: 6px; }
body.producer-skin .pd-shot:not(.is-editing) .ts-ord, body.producer-skin .pd-shot:not(.is-editing) .ts-cands,
body.producer-skin .pd-shot:not(.is-editing) .ts-takes, body.producer-skin .pd-shot:not(.is-editing) .ts-audio { display: none; }
body.producer-skin .pd-shots { display: flex; gap: 9px; flex-wrap: wrap; padding: 0 11px 11px; margin: 0; list-style: none; }
body.producer-skin .pd-shots.is-flat { padding: 4px 0 0; }
body.producer-skin .pd-addrow { list-style: none; margin: 0; padding: 0; }
body.producer-skin .pd-addrow .tl-add-scene { list-style: none; }
body.producer-skin .pd-addrow .tl-add-scene > button { display: flex; align-items: center; gap: 10px; width: 100%; padding: 13px; border: 1px dashed var(--pd-line-2); border-radius: var(--pd-r-card); background: var(--pd-sunk); color: var(--pd-ink-3); cursor: pointer; text-align: left; }
body.producer-skin .pd-addrow .tl-add-scene > button:hover { background: var(--pd-card); color: var(--pd-ink); }
body.producer-skin .pd-addrow .tl-add-tx b { display: block; font: 600 .8125rem/1.3 var(--sn-sans); }
body.producer-skin .pd-addrow .tl-add-tx span { display: block; font: .6875rem/1.4 var(--sn-sans); color: var(--pd-ink-4); }
body.producer-skin .pd-addrow .tl-add-ic { width: 26px; height: 26px; display: grid; place-items: center; border-radius: 7px; background: var(--pd-card-2); }
/* the shot tab's action bar borrows .tl-scene for its wiring; it must not borrow the card's chrome */
body.producer-skin .pd-shotbar.tl-scene { background: none; border: 0; padding: 0; box-shadow: none; animation: none; }
body.producer-skin .pd-shotbar .ts-director { display: flex; justify-content: center; gap: 7px; padding-bottom: 8px; }
body.producer-skin .pd-shotbar .ts-ctl { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px; border: 1px solid var(--pd-line-2); border-radius: 99px; background: var(--pd-card); color: var(--pd-ink-2); font: 500 .75rem var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-shotbar .ts-ctl .sn-ic { width: 13px; height: 13px; }
body.producer-skin .pd-rail-sel { width: 100%; margin-top: 6px; background: var(--pd-sunk); border: 1px solid var(--pd-line-2); color: var(--pd-ink-2); border-radius: 7px; height: 30px; font: .75rem var(--sn-sans); padding: 0 8px; }

/* ---- THE WORLD RAIL — its own column now, not a track inside the board's scroll (§3.6) ------------ */
/* The min-width:0 guards stay: a grid item's default `min-width:auto` lets its min-content push a track
   wider than the panel, which is what put a horizontal scrollbar on the board at narrow widths. */
body.producer-skin .pd-board, body.producer-skin .pd-scene, body.producer-skin .pd-scene-h, body.producer-skin .pd-shots { min-width: 0; }
/* 🔵 §73 CHUNK 3 — THE INSPECTOR CONTENT, ON shadcn's SIDEBAR ANATOMY.
   Fetched rather than eyeballed. SidebarGroup is `flex w-full min-w-0 flex-col p-2`; SidebarGroupLabel is
   `h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 [&>svg]:size-4`.

   🔑 TWO THINGS IN THE OLD RULES WERE FROM A DIFFERENT DESIGN LANGUAGE ENTIRELY:
   1. THE SECTION ICON WAS PAINTED WITH THE ACCENT. That was invisible-ish when the accent was indigo. §72
      made the accent NEAR-WHITE, so every section heading in this panel silently became the brightest
      pixel on it — brighter than its own heading text. A token change reaches further than the thing it
      was changed for; this is the second time that has bitten in two phases.
   2. `text-transform: uppercase` + `letter-spacing: .08em` on the group key. That is the dashboard idiom
      shadcn deliberately does NOT use — SidebarGroupLabel is plain `text-xs font-medium`, no caps, no
      tracking. Small-caps-with-tracking is exactly the "AI slop" texture Rob was reacting to.

   Spacing moves onto one scale: 16px between groups, 8px (gap-2) inside a grid, `--pd-r-ctl` on every
   tile so nothing in the column carries a bespoke 5px or 6px radius any more. */
body.producer-skin .pd-rail { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
body.producer-skin .pd-rail-h { display: flex; align-items: center; gap: 8px; font: 500 .875rem/1 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-rail-h .sn-ic { width: 16px; height: 16px; color: var(--pd-ink-3); }
body.producer-skin .pd-rail-k { font: 500 .75rem/1 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-rail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
body.producer-skin .pd-rail-grid img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--pd-r-ctl); display: block; cursor: grab; }
body.producer-skin .pd-rail-add { aspect-ratio: 1; border: 1px dashed var(--pd-line-2); border-radius: var(--pd-r-ctl); background: none; color: var(--pd-ink-4); display: grid; place-items: center; cursor: pointer; text-decoration: none; }
body.producer-skin .pd-rail-add:hover { color: var(--pd-ink-2); background: var(--pd-card); }
body.producer-skin .pd-rail-empty { font: .6875rem/1.5 var(--sn-sans); color: var(--pd-ink-4); margin: 6px 0 0; }

/* CHARACTERS & WORLD (rob-04). Three groups of square tiles, each with its own add slot, so the film's
   cast and set are a THING YOU CAN SEE rather than a sentence in a settings panel. A tile is the picture
   plus a name strip; the featured one carries the accent border and a ✓, exactly like the take that is in
   the cut. Dragging one onto a shot card pins it as that shot's framing. */
body.producer-skin .pd-rail-g { margin-top: 10px; }
body.producer-skin .pd-rail-g .pd-rail-grid { margin-top: 6px; }
body.producer-skin .pd-tile { position: relative; display: block; width: 100%; aspect-ratio: 1; padding: 0; border: 1px solid var(--pd-line); border-radius: var(--pd-r-ctl); overflow: hidden; background: var(--pd-card); cursor: pointer; }
body.producer-skin .pd-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
body.producer-skin .pd-tile .pd-tile-ph { position: absolute; inset: 0; display: grid; place-items: center; color: var(--pd-ghost); }
body.producer-skin .pd-tile .pd-tile-ph .sn-ic { width: 20px; height: 20px; }
body.producer-skin .pd-tile .lb { position: absolute; left: 0; right: 0; bottom: 0; padding: 8px 5px 3px; background: linear-gradient(transparent, rgba(0, 0, 0, .78)); color: #fff; font: 600 .5625rem/1.3 var(--sn-sans); text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ⚠️ WAS a 2px halo in `--pd-accent-dim`. That token is a WHITE wash since §72, so the selected tile grew
   a glow. shadcn selects with a ring, one value, no bloom. */
body.producer-skin .pd-tile.is-on { border-color: var(--pd-ring); box-shadow: 0 0 0 2px color-mix(in srgb, var(--pd-ring) 50%, transparent); }
body.producer-skin .pd-tile-on { position: absolute; top: 4px; right: 4px; width: 15px; height: 15px; display: grid; place-items: center; border-radius: 50%; background: var(--pd-accent); color: var(--pd-on-accent); }
body.producer-skin .pd-tile-on .sn-ic { width: 9px; height: 9px; }
body.producer-skin .pd-tile[draggable="true"] { cursor: grab; }
body.producer-skin .pd-rail-h + .pd-rail-g { margin-top: 8px; }

/* =====================================================================================================
   4. SHOT TAB (spec §3.5) + TIMELINE TAB (spec §3.6)
   ===================================================================================================== */

body.producer-skin .pd-shotview { display: flex; flex-direction: column; gap: 11px; padding-top: 4px; }
body.producer-skin .pd-shotview-h { display: flex; align-items: center; gap: 9px; }
body.producer-skin .pd-shotview-h b { font: 600 .8125rem/1 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-shotview-h .grow { flex: 1; }

/* the filmstrip navigator, with "+" insertion points between shots */
body.producer-skin .pd-strip { display: flex; align-items: center; gap: 0; overflow-x: auto; padding-bottom: 5px; }
body.producer-skin .pd-strip-cell { position: relative; flex: none; width: 92px; height: 52px; border-radius: 6px; overflow: hidden; border: 2px solid transparent; background: var(--pd-card); cursor: pointer; padding: 0; }
body.producer-skin .pd-strip-cell img, body.producer-skin .pd-strip-cell video { width: 100%; height: 100%; object-fit: cover; display: block; }
body.producer-skin .pd-strip-cell.is-on { border-color: var(--pd-accent); }
body.producer-skin .pd-strip-cell .n { position: absolute; left: 4px; bottom: 3px; font: 700 .5625rem/1 var(--sn-sans); color: #fff; text-shadow: 0 1px 3px #000; }
body.producer-skin .pd-strip-ins { flex: none; width: 18px; height: 18px; margin: 0 3px; display: grid; place-items: center; border: 0; border-radius: 50%; background: none; color: var(--pd-ink-4); cursor: pointer; }
body.producer-skin .pd-strip-ins:hover { background: var(--pd-accent); color: var(--pd-on-accent); }
body.producer-skin .pd-strip-ins .sn-ic { width: 11px; height: 11px; }
body.producer-skin .pd-strip-new { flex: none; width: 78px; height: 52px; margin-left: 3px; border: 1px dashed var(--pd-line-2); border-radius: 6px; background: none; color: var(--pd-ink-4); font: 600 .625rem var(--sn-sans); cursor: pointer; }

body.producer-skin .pd-player { border-radius: var(--pd-r-player); overflow: hidden; background: #000; border: 1px solid var(--pd-line); }
body.producer-skin .pd-player video, body.producer-skin .pd-player img { width: 100%; display: block; aspect-ratio: 16/9; object-fit: contain; background: #000; }

/* THE ACTION BAR — icon above label (rob-05). Every verb Rob counted is here; the two whose results
   cannot yet get back into the cut are visibly disabled and say why on hover, rather than being quietly
   left off a bar he has already read. */
body.producer-skin .pd-acts { display: flex; justify-content: center; align-items: flex-start; gap: 22px; padding: 12px 0 4px; flex-wrap: wrap; }
body.producer-skin .pd-act { display: flex; flex-direction: column; align-items: center; gap: 6px; border: 0; background: none; color: var(--pd-ink-3); font: 500 .6875rem/1 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-act:hover:not(:disabled) { color: var(--pd-ink); }
body.producer-skin .pd-act:disabled { opacity: .38; cursor: default; }
body.producer-skin .pd-act .sn-ic { width: 17px; height: 17px; }
/* 🔴 §73 — AN INVISIBLE ICON, and the contrast test did not catch it because it looks at `color`
   in the SAME block as the fill. This one is a DESCENDANT: the button is background:var(--pd-accent) and
   the icon inside it was color:var(--pd-accent) too. Correct against indigo, near-white-on-near-white now.
   The test has been widened to scan descendant rules for exactly this shape. */
body.producer-skin .pd-act.is-primary .sn-ic { color: var(--pd-on-accent); }
body.producer-skin .pd-act.is-hidden { display: none; }
/* Recreate is a SPLIT control: the primary path on the left, our other two re-generation paths behind ▾ */
/* §61 — THE LONE PRIMARY IS A BUTTON NOW, NOT A LEFTOVER TOOLBAR ICON.
   `.pd-act` is an icon-ABOVE-label toolbar button, and that was the right shape when this bar carried
   seven of them in a row. §60 collapsed it to ONE visible verb plus a ▾, and the shape did not follow:
   a single centred icon-with-a-caption, floating in a `justify-content:center; gap:22px` row, reads as
   a leftover rather than as the thing the screen wants you to press. The primary now sits on the
   action tier like every other committing control — horizontal, accent-filled, --pd-h-md tall, the
   same 8px radius as .pd-btn — so the split control reads as one button with a menu attached.
   The hidden siblings are untouched: they are still `.pd-act`s, still cloned into the ▾ carrying their
   own label, icon and disabled state, and they never render in this bar. */
body.producer-skin .pd-acts:has(.pd-act.is-primary) { gap: 10px; }
body.producer-skin .pd-act.is-primary {
  flex-direction: row; align-items: center; gap: 7px;
  height: var(--pd-h-md); padding: 0 14px;
  border-radius: var(--pd-r-ctl) 0 0 var(--pd-r-ctl);
  color: var(--pd-on-accent);
  font: 500 .8125rem/1 var(--sn-sans);
}
body.producer-skin .pd-act.is-primary:hover:not(:disabled) { color: var(--pd-on-accent); background: color-mix(in srgb, var(--pd-accent) 90%, transparent); }
body.producer-skin .pd-act.is-primary .sn-ic { width: 15px; height: 15px; color: var(--pd-on-accent); }
body.producer-skin .pd-act-split { display: flex; align-items: stretch; gap: 0; }
/* …and the ▾ becomes the second half of that button rather than a floating chevron beside it. */
body.producer-skin .pd-act-split:has(.pd-act.is-primary) .pd-act-more {
  align-self: stretch; width: 30px; height: var(--pd-h-md);
  border-radius: 0 var(--pd-r-ctl) var(--pd-r-ctl) 0; border-left: 1px solid rgba(255, 255, 255, .18);
  background: var(--pd-accent); color: var(--pd-on-accent);
}
/* 🔴 §74 — THE SPLIT BUTTON'S TWO HALVES MUST SHARE ONE RAMP. Glossing `.pd-act.is-primary` alone
   would have shipped a gradient left half butted against a flat solid chevron — and even glossing the
   chevron separately restarts the gradient, so violet would meet blue at the seam. The finish belongs to
   the CONTAINER: it paints one continuous face across both halves, the children go transparent, and a
   white hairline stands in for the divider the seam used to be. */
body.producer-skin .pd-act-split:has(.pd-act.is-primary) {
  position: relative; isolation: isolate; overflow: hidden;
  border-radius: var(--pd-r-ctl);
  background-color: var(--sn-gloss-base);
  background-image: var(--sn-gloss-spec), var(--sn-gloss-grad);
  box-shadow: var(--sn-gloss-rim), var(--sn-gloss-depth), var(--sn-gloss-cast);
  transition: box-shadow 180ms var(--sn-ease), filter 180ms var(--sn-ease);
}
body.producer-skin .pd-act-split:has(.pd-act.is-primary):hover { filter: brightness(1.06) saturate(1.03); box-shadow: var(--sn-gloss-rim), var(--sn-gloss-depth), var(--sn-gloss-cast-hi); }
body.producer-skin .pd-act-split:has(.pd-act.is-primary) .pd-act.is-primary,
body.producer-skin .pd-act-split:has(.pd-act.is-primary) .pd-act-more {
  background: none; background-image: none; box-shadow: none; color: var(--pd-on-accent); filter: none;
}
body.producer-skin .pd-act-split:has(.pd-act.is-primary) .pd-act.is-primary::after { display: none; }
body.producer-skin .pd-act-split:has(.pd-act.is-primary) .pd-act-more { border-left: 1px solid rgba(255,255,255,.22); border-radius: 0; }
body.producer-skin .pd-act-split:has(.pd-act.is-primary) .pd-act-more:hover { background: rgba(255,255,255,.12); color: var(--pd-on-accent); }
body.producer-skin .pd-act-more { align-self: center; width: 20px; height: 20px; display: grid; place-items: center; border: 0; border-radius: 5px; background: none; color: var(--pd-ink-4); cursor: pointer; }
body.producer-skin .pd-act-more:hover { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-act-more .sn-ic { width: 12px; height: 12px; }
/* Current Shot's own two buttons, under the takes strip */
body.producer-skin .pd-rail-acts { display: flex; gap: 6px; margin-top: 8px; }
body.producer-skin .pd-rail-acts .pd-btn { flex: 1; min-width: 0; }
body.producer-skin .pd-rail-acts .pd-btn.is-icon { flex: none; width: 32px; padding: 0; }
body.producer-skin .pd-take .lb { text-align: center; }

/* ---- the generation editor's own column contents (cross-fades in over the chat) ------------------ */
body.producer-skin .pd-gen-h { flex: none; display: flex; align-items: center; gap: 5px; padding: 9px 12px; border-bottom: 1px solid var(--pd-line); }
body.producer-skin .pd-gen-tab { height: 26px; padding: 0 11px; border: 0; border-radius: 7px; background: none; color: var(--pd-ink-3); font: 600 .75rem/1 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-gen-tab.is-on { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-gen-body { flex: 1; min-height: 0; overflow: auto; padding: 12px; display: flex; flex-direction: column; gap: 13px; }
body.producer-skin .pd-gen-k { font: 600 .5625rem/1 var(--sn-sans); letter-spacing: .08em; text-transform: uppercase; color: var(--pd-ink-4); }
body.producer-skin .pd-modes { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
body.producer-skin .pd-mode-tile { display: flex; flex-direction: column; gap: 7px; padding: 10px; border: 1px solid var(--pd-line); border-radius: 9px; background: var(--pd-sunk); color: var(--pd-ink-3); font: 600 .6875rem/1.3 var(--sn-sans); cursor: pointer; text-align: left; }
body.producer-skin .pd-mode-tile .sn-ic { width: 15px; height: 15px; }
body.producer-skin .pd-mode-tile.is-on { border-color: var(--pd-accent-line); color: var(--pd-ink); }
body.producer-skin .pd-mode-tile.is-on .sn-ic { color: var(--pd-ink); }
body.producer-skin .pd-gen-ta { width: 100%; min-height: 168px; padding: 10px; border: 1px solid var(--pd-line); border-radius: 9px; background: var(--pd-sunk); color: var(--pd-ink-2); font: .75rem/1.6 var(--sn-sans); outline: none; resize: vertical; }
body.producer-skin .pd-gen-ta:focus { border-color: var(--pd-accent-line); }
body.producer-skin .pd-gen-foot { flex: none; display: flex; align-items: center; gap: 9px; padding: 10px 12px; border-top: 1px solid var(--pd-line); }
body.producer-skin .pd-step { display: inline-flex; align-items: center; gap: 8px; height: 30px; padding: 0 4px; border: 1px solid var(--pd-line-2); border-radius: 8px; color: var(--pd-ink-2); font: 600 .75rem var(--sn-sans); font-variant-numeric: tabular-nums; }
body.producer-skin .pd-step button { width: 22px; height: 22px; border: 0; border-radius: 5px; background: none; color: var(--pd-ink-3); cursor: pointer; font-size: .875rem; }
body.producer-skin .pd-step button:hover { background: var(--pd-card-2); color: var(--pd-ink); }
/* §61: was 36px — the full-width commit in the generate sheet. It is an ACTION, so it is the action tier. */
body.producer-skin .pd-gen-foot .pd-btn.is-accent { flex: 1; height: var(--pd-h-md); }
/* reference tiles with a hover remove badge (t2166–t2168) */
body.producer-skin .pd-refs { display: flex; gap: 6px; flex-wrap: wrap; }
body.producer-skin .pd-ref { position: relative; width: 52px; height: 52px; border-radius: 7px; overflow: hidden; border: 1px solid var(--pd-line-2); }
body.producer-skin .pd-ref img { width: 100%; height: 100%; object-fit: cover; display: block; }
body.producer-skin .pd-ref .lb { position: absolute; left: 0; right: 0; bottom: 0; padding: 1px 3px; background: rgba(0, 0, 0, .68); color: #fff; font: 600 .5rem/1.4 var(--sn-sans); text-align: center; }
body.producer-skin .pd-ref-x { position: absolute; top: 2px; right: 2px; width: 15px; height: 15px; display: none; place-items: center; border: 0; border-radius: 50%; background: rgba(10, 10, 10, .88); color: #fff; cursor: pointer; }
body.producer-skin .pd-ref:hover .pd-ref-x { display: grid; }
body.producer-skin .pd-ref-x .sn-ic { width: 9px; height: 9px; }
body.producer-skin .pd-ref-add { width: 52px; height: 52px; border: 1px dashed var(--pd-line-2); border-radius: 7px; background: none; color: var(--pd-ink-4); display: grid; place-items: center; cursor: pointer; }

/* ---- timeline ------------------------------------------------------------------------------------ */
body.producer-skin .pd-tl { display: flex; flex-direction: column; gap: 10px; padding-top: 4px; }
body.producer-skin .pd-tl-tools { display: flex; align-items: center; gap: 4px; padding: 6px 0; border-bottom: 1px solid var(--pd-line); }
/* THE SCROLL CONTAINER IS THE BROWSER'S, AND THAT IS THE WHOLE FIX (R3 directive #4). The old strip
   painted an edge that looked like a scrollbar and could not be dragged. `overflow-x: auto` gives a real
   one -- draggable, wheelable, keyboard-reachable -- and it is the same scrollbar every other panel here
   uses. Vertical is hidden: a timeline never scrolls down. */
body.producer-skin .pd-tlwrap { position: relative; overflow-x: auto; overflow-y: hidden; padding-bottom: 4px; }
body.producer-skin .pd-tlinner { position: relative; display: flex; flex-direction: column; gap: 8px; min-width: 100%; }
body.producer-skin .pd-track { display: flex; align-items: center; gap: 8px; }
body.producer-skin .pd-track-ic { width: 18px; flex: none; color: var(--pd-ink-4); display: grid; place-items: center; }
body.producer-skin .pd-track-lane { flex: 1; min-width: 0; display: flex; gap: 2px; height: 44px; border-radius: 6px; overflow: hidden; background: var(--pd-sunk); }
/* THE PLAYHEAD. A 2px accent line the full height of the tracks with a fat grab handle on top -- the line
   is the precision, the handle is the target. It sits ABOVE the clips and ignores the pointer everywhere
   except on the handle, so pressing a clip still presses the clip. */
body.producer-skin .pd-playhead { position: absolute; top: 0; bottom: 0; left: 0; width: 2px; background: var(--pd-accent); z-index: 6; pointer-events: none; will-change: transform; }
body.producer-skin .pd-ph-grab { position: absolute; top: -3px; left: 50%; transform: translateX(-50%); width: 13px; height: 13px; border-radius: 50%; background: var(--pd-accent); box-shadow: 0 0 0 3px var(--pd-accent-dim); cursor: grab; pointer-events: auto; }
body.producer-skin.is-scrubbing, body.producer-skin.is-scrubbing * { user-select: none !important; }
body.producer-skin.is-scrubbing .pd-ph-grab { cursor: grabbing; }
body.producer-skin .pd-tl-time { font: 600 .6875rem/1 var(--sn-sans); color: var(--pd-ink-3); font-variant-numeric: tabular-nums; margin-left: 8px; }
body.producer-skin .pd-zoom { display: inline-flex; align-items: center; gap: 6px; color: var(--pd-ink-4); }
body.producer-skin .pd-zoom .sn-ic { width: 13px; height: 13px; }
body.producer-skin .pd-zoom input { width: 96px; accent-color: var(--pd-accent); }
body.producer-skin .pd-music-lb { align-self: center; padding-left: 8px; font: 600 .625rem/1 var(--sn-sans); color: var(--pd-violet-ink); }
body.producer-skin .pd-track-lane img, body.producer-skin .pd-track-lane video { height: 100%; width: 62px; object-fit: cover; flex: none; display: block; }
body.producer-skin .pd-track-lane .cell { position: relative; flex: none; cursor: pointer; border: 2px solid transparent; border-radius: 5px; overflow: hidden; }
body.producer-skin .pd-track-lane .cell.is-on { border-color: var(--pd-accent); }
body.producer-skin .pd-track.is-music .pd-track-lane { background: var(--pd-violet-dim); border: 1px solid var(--pd-violet-line); height: 26px; }
body.producer-skin .pd-track.is-ghost .pd-track-lane { height: 22px; border: 1px dashed var(--pd-line-2); background: none; align-items: center; padding: 0 7px; color: var(--pd-ink-4); font: .625rem var(--sn-sans); }
/* The ruler is now a POSITIONED strip -- every tick sits at its own timecode in pixels, because a flexed
   row of equal cells cannot describe a film whose scenes are different lengths. It is also the seek
   surface, so it carries the cursor that says so. */
body.producer-skin .pd-ruler { position: relative; height: 16px; color: var(--pd-ink-4); font: .5625rem/16px var(--sn-sans); font-variant-numeric: tabular-nums; cursor: pointer; border-bottom: 1px solid var(--pd-line); }
body.producer-skin .pd-ruler:focus-visible { outline: 2px solid var(--pd-accent); outline-offset: 2px; border-radius: 3px; }
body.producer-skin .pd-tick { position: absolute; top: 0; padding-left: 4px; border-left: 1px solid var(--pd-line); height: 100%; white-space: nowrap; }
/* the video track's cells are the same ones the filmstrip always drew — on a ruler, and sized by time */
body.producer-skin .pd-track-lane.tl-film-track { display: flex; gap: 2px; padding: 0; background: var(--pd-sunk); }
body.producer-skin .pd-track-lane .tl-film-cell { position: relative; flex: none; min-width: 12px; height: 100%; padding: 0; border: 0; border-radius: 4px; overflow: hidden; background: var(--pd-skel); cursor: pointer; }
body.producer-skin .pd-track-lane .tl-film-cell img, body.producer-skin .pd-track-lane .tl-film-cell video { width: 100%; height: 100%; object-fit: cover; display: block; }
body.producer-skin .pd-track-lane .tl-film-cell:hover { outline: 2px solid var(--pd-accent); outline-offset: -2px; }
body.producer-skin .pd-track-lane .tl-film-no { position: absolute; left: 4px; top: 3px; z-index: 2; font: 700 .5625rem/1 var(--sn-sans); color: #fff; text-shadow: 0 1px 3px #000; }
body.producer-skin .pd-track-lane .tl-film-dur { position: absolute; right: 4px; bottom: 3px; z-index: 2; font: 600 .5625rem/1 var(--sn-sans); color: rgba(255, 255, 255, .82); text-shadow: 0 1px 3px #000; }
body.producer-skin .pd-track-lane .tl-film-ph { position: absolute; inset: 0; display: grid; place-items: center; color: var(--pd-ghost); }
body.producer-skin .pd-tl-nocut { position: absolute; left: 0; right: 0; bottom: 0; padding: 8px 12px; background: linear-gradient(transparent, rgba(0, 0, 0, .82)); color: var(--pd-ink-3); font: .6875rem var(--sn-sans); text-align: center; }
body.producer-skin .pd-tl .pd-player { position: relative; }
body.producer-skin .pd-tl .pd-player img { aspect-ratio: 16/9; object-fit: cover; }

/* =====================================================================================================
   5. THE PRODUCER HOME — a cinematic landing, not a template grid (spec §3.1, target 10-director-home)
   ===================================================================================================== */

body.producer-skin .pd-home-in { max-width: 1180px; margin: 0 auto; padding: 18px 24px 64px; width: 100%; }

/* Hero band: rounded panel, black, ONE ember bloom rising from the bottom edge. */
body.producer-skin .pd-hero { position: relative; border-radius: var(--pd-r-hero); overflow: hidden; background: var(--sn-bg-deep); padding: 52px 24px 34px; text-align: center; }
/* §72 — THE GLOW IS GONE. A 62%-opacity accent bloom behind the headline was the single largest
   coloured area in the studio, and at near-white it would be a blown-out smear rather than a tint. A
   Vercel page has no bloom: the headline sits on the canvas and the type does the work. */
body.producer-skin .pd-hero > * { position: relative; z-index: 1; }
/* the luminance ramp across the headline: first word white, last word dim */
body.producer-skin .pd-hero h1 { font: 700 3.5rem/1.08 var(--sn-sans); letter-spacing: -.025em; margin: 0 0 10px; color: #fff; }
body.producer-skin .pd-hero h1 .w2 { color: #cfcfcf; }
body.producer-skin .pd-hero h1 .w3 { color: #8f8f8f; }
/* THE HERO BAND IS A MEDIA SURFACE IN BOTH THEMES — a poster, not a panel, exactly like the studio's own
   `--sn-bg-deep` cinematic blocks. So it re-declares the ink ramp against ITS ground rather than the
   page's; without this the light theme would paint #75736D subtext onto near-black. */
body.producer-skin .pd-hero {
  --pd-ink: #FFFFFF; --pd-ink-2: #D6D3CC; --pd-ink-3: #A7A39B; --pd-ink-4: #7C7972;
  --pd-card: #131211; --pd-card-2: #1D1B19; --pd-sunk: #0F0E0D; --pd-line: #2A2825; --pd-line-2: #383530;
  color: #FFFFFF;
}
body.producer-skin .pd-hero .sub { font: .875rem/1.5 var(--sn-sans); color: var(--pd-ink-3); margin: 0 0 30px; }
body.producer-skin .pd-hero-comp { max-width: 790px; margin: 0 auto; }
body.producer-skin .pd-hero-comp .composer { background: var(--pd-sunk); border: 1px solid var(--pd-line-2); border-radius: 24px; padding: 16px 18px; min-height: 150px; display: flex; flex-direction: column; text-align: left; }
body.producer-skin .pd-hero-comp .composer textarea { min-height: 62px; font-size: .9375rem; }
/* suggestion chips sit ON the bloom */
body.producer-skin .pd-chips { display: flex; justify-content: center; gap: 9px; flex-wrap: wrap; margin-top: 20px; }
body.producer-skin .pd-chip { display: inline-flex; align-items: center; gap: 7px; height: 34px; padding: 0 15px; border-radius: 99px; border: 1px solid rgba(255, 255, 255, .17); background: rgba(255, 255, 255, .09); backdrop-filter: blur(9px); color: #fff; font: 500 .8125rem/1 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-chip:hover { background: rgba(255, 255, 255, .16); }
body.producer-skin .pd-chip .sn-ic { width: 11px; height: 11px; opacity: .7; }
body.producer-skin .pd-chip.is-round { width: 34px; padding: 0; justify-content: center; }

body.producer-skin .pd-sec { margin-top: 40px; }
body.producer-skin .pd-sec-h { display: flex; align-items: baseline; gap: 12px; margin-bottom: 14px; }
body.producer-skin .pd-sec-h h2 { font: 700 1.375rem/1 var(--sn-sans); color: var(--pd-ink); margin: 0; }
body.producer-skin .pd-sec-h .grow { flex: 1; }
body.producer-skin .pd-sec-h a, body.producer-skin .pd-sec-h button { font: 600 .8125rem var(--sn-sans); color: var(--pd-ink-3); background: none; border: 0; cursor: pointer; text-decoration: none; }
body.producer-skin .pd-sec-h a:hover, body.producer-skin .pd-sec-h button:hover { color: var(--pd-ink); }

/* Quick Starts: ONE large featured card + a use-case grid. Card art is stills from OUR films. */
body.producer-skin .pd-qs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
body.producer-skin .pd-qs-card { position: relative; border-radius: var(--pd-r-card); overflow: hidden; background: var(--pd-card); border: 1px solid var(--pd-line); cursor: pointer; padding: 0; text-align: left; aspect-ratio: 225/205; }
/* The featured card is 2x2 so the grid closes cleanly at eight quick starts (4 cells + 7 singles fills
   three rows), and so the one card that is meant to be the invitation actually looks like one. */
body.producer-skin .pd-qs-card.is-feat { grid-column: span 2; grid-row: span 2; aspect-ratio: auto; }
body.producer-skin .pd-qs-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
body.producer-skin .pd-qs-card .scrim { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 34%, rgba(0, 0, 0, .84)); }
body.producer-skin .pd-qs-card .lb { position: absolute; left: 12px; right: 12px; bottom: 11px; display: flex; align-items: center; gap: 7px; color: #fff; font: 700 .8125rem/1.2 var(--sn-sans); }
body.producer-skin .pd-qs-card .lb .sn-ic { width: 14px; height: 14px; }
body.producer-skin .pd-qs-card:hover img { transform: scale(1.02); }
body.producer-skin .pd-qs-card img { transition: transform var(--pd-fade) var(--sn-ease); }
body.producer-skin .pd-qs-card .feat-badge { position: absolute; left: 14px; top: 14px; height: 20px; padding: 0 9px; display: inline-flex; align-items: center; border-radius: 99px; background: #cfe94f; color: #14180a; font: 700 .625rem/1 var(--sn-sans); }
body.producer-skin .pd-qs-card.is-feat .lb { font-size: 1.375rem; left: 20px; bottom: 18px; flex-direction: column; align-items: flex-start; gap: 3px; }
body.producer-skin .pd-qs-card.is-feat .lb small { font: 400 .8125rem var(--sn-sans); color: rgba(255, 255, 255, .72); }
body.producer-skin .pd-qs-cta { position: absolute; right: 16px; bottom: 18px; height: 32px; padding: 0 15px; display: inline-flex; align-items: center; gap: 6px; border-radius: 99px; background: #fff; color: #141413; font: 700 .75rem/1 var(--sn-sans); }
body.producer-skin .pd-qs-ph { position: absolute; inset: 0; background: linear-gradient(150deg, #1b1512, var(--pd-card)); }

/* project cards */
/* minmax(0, 1fr), NOT 1fr (§58). A bare 1fr track floors at the item's MAX-CONTENT width, so a film
   whose title is a whole sentence widened its own column and the row came out ragged — and the title's
   own text-overflow:ellipsis never fired, because a box the grid sized to fit the text can never overflow
   it. Seen on the deployed library: four columns at three different widths, one title running past its
   poster. min-width:0 on the card is the same rule one level down, for the same reason. */
body.producer-skin .pd-films { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
/* 🔴 §58 — `display: block` IS LOAD-BEARING. This card is a POSTER over a title, and it never said so:
   it carried `tl-recent-card` from its previous life as a horizontal row, studio-live.css styles that
   class `display:flex; align-items:center`, and an undeclared display loses to a declared one. The card
   laid out as a row, the poster became a flex item with an aspect-ratio and no basis, and it collapsed to
   3.5×2px beside its own title — which is a card showing only words. The class is gone from the markup;
   this states the layout outright so nothing can win it by default again. */
body.producer-skin .pd-film { display: block; width: 100%; min-width: 0; text-align: left; background: none; border: 0; padding: 0; cursor: pointer;
  transition: transform var(--sn-t-hover), opacity var(--sn-t-hover); }
body.producer-skin .pd-film:hover { transform: translateY(-2px); }
body.producer-skin .pd-film:hover .po { border-color: var(--pd-accent, var(--sn-accent)); }
body.producer-skin .pd-film:focus-visible { outline: none; }
body.producer-skin .pd-film:focus-visible .po { border-color: var(--sn-accent); box-shadow: 0 0 0 3px var(--sn-accent-soft); }
/* …and `display: block` here for the same reason, one level down: the state line below is also a <span>,
   so the rule that styles it used to match this box too and hand the poster `display:flex` and a 11px
   font. The picture box is a picture box; it is not a line of type. */
body.producer-skin .pd-film .po { display: block; position: relative; aspect-ratio: 16/9; border-radius: var(--pd-r-card); overflow: hidden; background: var(--pd-card); border: 1px solid var(--pd-line); transition: border-color var(--sn-t-hover); }
body.producer-skin .pd-film .po img, body.producer-skin .pd-film .po video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
body.producer-skin .pd-film b { display: block; margin-top: 8px; font: 600 .8125rem/1.35 var(--sn-sans); color: var(--pd-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.producer-skin .pd-film span:not(.po) { display: flex; gap: 8px; margin-top: 3px; font: .6875rem var(--sn-sans); color: var(--pd-ink-4); }
body.producer-skin .pd-film span .cr { color: var(--pd-green); font-variant-numeric: tabular-nums; }

/* ---- FORMAT WIZARDS — they STRUCTURE the brief, they never plan and never charge (spec §3.1.5) --- */
body.producer-skin .pd-wiz-back { position: fixed; inset: 0; z-index: 1200; background: var(--sn-scrim); display: grid; place-items: center; padding: 24px; }
body.producer-skin .pd-wiz { width: min(620px, 94vw); max-height: 88vh; overflow: auto; border-radius: var(--pd-r-modal); background: var(--pd-card); border: 1px solid var(--pd-line-2); box-shadow: var(--pd-elev-3); }
body.producer-skin .pd-wiz-h { display: flex; align-items: center; gap: 10px; padding: 16px 18px 12px; }
body.producer-skin .pd-wiz-h b { flex: 1; font: 700 1.0625rem/1 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-wiz-b { padding: 0 18px 18px; display: flex; flex-direction: column; gap: 15px; }
body.producer-skin .pd-fld { display: flex; flex-direction: column; gap: 7px; }
body.producer-skin .pd-fld > label { font: 600 .75rem/1 var(--sn-sans); color: var(--pd-ink-2); }
body.producer-skin .pd-fld textarea, body.producer-skin .pd-fld input[type="text"] { width: 100%; padding: 10px 11px; border: 1px solid var(--pd-line-2); border-radius: 9px; background: var(--pd-sunk); color: var(--pd-ink); font: .8125rem/1.6 var(--sn-sans); outline: none; resize: vertical; }
body.producer-skin .pd-fld textarea { min-height: 92px; }
body.producer-skin .pd-fld textarea:focus, body.producer-skin .pd-fld input:focus { border-color: var(--pd-accent-line); }
body.producer-skin .pd-pills { display: flex; gap: 6px; flex-wrap: wrap; }
body.producer-skin .pd-pill { height: 30px; padding: 0 13px; border: 1px solid var(--pd-line-2); border-radius: 99px; background: var(--pd-sunk); color: var(--pd-ink-3); font: 500 .75rem/1 var(--sn-sans); cursor: pointer; }
body.producer-skin .pd-pill:hover { color: var(--pd-ink); }
body.producer-skin .pd-pill.is-on { background: var(--pd-accent); border-color: var(--pd-accent); color: var(--pd-on-accent); }
body.producer-skin .pd-wiz-f { display: flex; align-items: center; gap: 9px; padding: 13px 18px; border-top: 1px solid var(--pd-line); }
body.producer-skin .pd-wiz-f .grow { flex: 1; }
body.producer-skin .pd-wiz-f .note { font: .6875rem/1.4 var(--sn-sans); color: var(--pd-green); }

/* =====================================================================================================
   6. SCRIPT-REVIEW TAKEOVER — DELETED (§104). The whole `.pd-doc*` family was DEAD CSS.
   =====================================================================================================
   `.pd-doc`, `.pd-doc-toc`, `.pd-doc-b` and `.pd-doc-cta` were defined here and emitted by NOTHING —
   grepping every file under public/js finds zero uses. The script-review takeover this described was
   re-implemented as `.tl-script*` in studio-live.css and these rules were left behind.

   ⚠️ AND ONE OF THEM WAS BEING ASSERTED. §89's chat-leading test pinned `.pd-doc-b p` at 1.7 to prove
   "the script tab is long-form reading and KEEPS its generous leading" — an assertion about a class no
   page renders, so it protected nothing while reading as coverage. The rule it was really about is
   `.tl-script-dir` (1.55 against the chat's 1.45), and the test now points there.

   ⚠️ THE MIRROR-IMAGE DEFECT IS STILL OPEN AND IS **NOT** FIXED HERE: `.pd-doclink` is EMITTED by the
   client and defined in no stylesheet. Giving it a treatment is a design decision, not a cleanup, so it
   is flagged rather than invented.
   ===================================================================================================== */

/* CONSTRAINT CHIPS — the adherence contract, made visible and editable (spec §5.1) */
body.producer-skin .pd-cons { display: flex; gap: 6px; flex-wrap: wrap; margin: 4px 0 16px; }
body.producer-skin .pd-con { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px; border-radius: 99px; font: 600 .6875rem/1 var(--sn-sans); border: 1px solid; }
body.producer-skin .pd-con.is-must { color: var(--pd-green-ink); border-color: var(--pd-green-line); background: var(--pd-green-dim); }
body.producer-skin .pd-con.is-never { color: var(--pd-warm-ink); border-color: var(--pd-accent-line); background: var(--pd-accent-dim); }
body.producer-skin .pd-con button { border: 0; background: none; color: inherit; opacity: .6; cursor: pointer; display: grid; place-items: center; }
body.producer-skin .pd-con button:hover { opacity: 1; }
body.producer-skin .pd-con .sn-ic { width: 10px; height: 10px; }
body.producer-skin .pd-con-add { height: 26px; padding: 0 10px; border-radius: 99px; border: 1px dashed var(--pd-line-2); background: none; color: var(--pd-ink-4); font: 600 .6875rem/1 var(--sn-sans); cursor: pointer; }

/* =====================================================================================================
   7. TOASTS + RESPONSIVE
   ===================================================================================================== */

/* THE EXPORT SHEET — one row per thing the customer already owns (rob-02's "Download assets"). */
body.producer-skin .pd-exp-row { display: flex; align-items: center; gap: 11px; padding: 9px; border: 1px solid var(--pd-line); border-radius: 10px; background: var(--pd-card); color: var(--pd-ink-2); text-decoration: none; margin-bottom: 7px; }
body.producer-skin .pd-exp-row:hover { border-color: var(--pd-line-2); background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-exp-row.is-main { border-color: var(--pd-accent-line); }
body.producer-skin .pd-exp-row .po { width: 62px; height: 36px; flex: none; border-radius: 6px; overflow: hidden; background: var(--pd-skel); }
body.producer-skin .pd-exp-row .po img { width: 100%; height: 100%; object-fit: cover; display: block; }
body.producer-skin .pd-exp-row .tx { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
body.producer-skin .pd-exp-row .tx b { font: 600 .8125rem/1.3 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-exp-row .tx span { font: .6875rem/1.4 var(--sn-sans); color: var(--pd-ink-4); }
body.producer-skin .pd-exp-row .sn-ic { width: 15px; height: 15px; flex: none; }
body.producer-skin .pd-exp-k { font: 600 .5625rem/1 var(--sn-sans); letter-spacing: .08em; text-transform: uppercase; color: var(--pd-ink-4); margin: 14px 0 7px; }

/* The ONLY toast style: a top-centre cream pill with a status dot, ~3–4s (t2152–t2156). */
body.producer-skin .pd-toast { position: fixed; top: 16px; left: 50%; transform: translateX(-50%); z-index: 1400; display: inline-flex; align-items: center; gap: 9px; height: 34px; padding: 0 15px; border-radius: 99px; background: var(--pd-toast-bg); color: var(--pd-toast-ink); font: 600 .75rem/1 var(--sn-sans); box-shadow: var(--pd-elev-2); }
body.producer-skin .pd-toast i { width: 7px; height: 7px; border-radius: 50%; background: var(--pd-accent); flex: none; }

/* THE COMPOSER IS NEVER MORE THAN ONE TAP AWAY (§3.1). Below 1180px the assets column collapses first —
   it is reference, and the board is the work. Below 960px the chat becomes a slide-over FROM THE LEFT
   (it is the left column now), with a persistent edge tab, because the one thing that must never be the
   first casualty of a narrow window is the box you direct with. Both breakpoints also drop the dividers:
   there is nothing left to divide, and a 6px track with a hairline in it would be a seam that resizes
   nothing. */
@media (max-width: 1180px) {
  body.producer-skin .pd-right-top { grid-template-columns: minmax(0, 1fr); }
  body.producer-skin .pd-railcol, body.producer-skin .pd-div[data-div="assets"] { display: none; }
  /* …and so does the control that opens it. Below this width the column is gone, so the toggle could only
     ever flip a flag nothing reads — the dead-control class the toolbar audit exists to delete. As a
     floating edge tab it stayed on screen here and did exactly that. */
  body.producer-skin .pd-assets-tab { display: none; }
  body.producer-skin .pd-qs, body.producer-skin .pd-films { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* 🔴 §70 — ON A PHONE THE CHAT WAS HIDDEN, AND THAT IS "THE CHAT IS NOT WORKING".
   Below 960px the plan column left the grid entirely and became a fixed slide-over behind a "Direct" edge
   tab. So a narrow viewport showed a stage, a strip of chrome, and NO CONVERSATION — on a product whose
   whole interaction is the conversation. Rob's mobile screenshot is that, and it reads as broken because
   functionally it is: the primary surface was one tap away with nothing saying so.

   IT STACKS NOW. One column, in the order the work happens: the picture you are judging, then the chat you
   are judging it with. Both are real page content rather than an overlay, so the composer is always
   reachable and nothing floats over the film. The inspector and the timeline stay collapsed (they are
   reference and a filmstrip; neither survives a 390px column), and the view switcher scrolls rather than
   wrapping into three ragged rows. */
@media (max-width: 960px) {
  body.producer-skin .pd-root.is-ws { overflow: auto; }          /* the workspace becomes a PAGE at this width */
  body.producer-skin .pd-body { grid-template-columns: minmax(0, 1fr); grid-auto-rows: min-content; }
  /* §82: on one column the right region is just its children in order — a 1fr row track here would
     stretch the stage to the viewport and push the timeline off the bottom. */
  body.producer-skin .pd-right { display: contents; }
  body.producer-skin .pd-div { display: none; }
  /* THE STAGE FIRST — it is what a phone is best at showing — then the conversation under it. `order` and
     not source order, because the DOM order is what the desktop grid and every handler already expect. */
  body.producer-skin .pd-stage { order: 1; min-height: 46vh; }
  body.producer-skin .pd-chat { order: 2; min-height: 52vh; border-top: 1px solid var(--pd-line); }
  /* The chat is IN the flow now, so every rule that made it an overlay has to go — a leftover
     `position:fixed` here would put the conversation back on top of the film. */
  body.producer-skin .pd-chat { position: static; width: auto; max-width: none; transform: none; box-shadow: none; background: transparent; }
  /* 🔴 THE MISSED HALF OF §70's FIX. `.pd-chat` losing its `position` also un-does its job as the
     containing block for `.pd-chat-pane` / `.pd-gen-pane`, which are UNCONDITIONALLY `position:absolute;
     inset:0` (desktop crossfade, ~L517). With no positioned ancestor nearby, the browser walks up to the
     nearest one it CAN find — nothing in this shell qualifies close by, so the pane lands near the
     document root: full-viewport-sized, painted over the sidebar and the editor's own topbar. The first
     bubble in the thread (the original prompt — i.e. the film's title, verbatim) is what a screenshot at
     this width actually shows sitting at the top-left, on top of everything. Static positioning here, and
     since the two panes can no longer crossfade by stacking, `.is-gen` switches which one is IN the flow
     instead of which one is opaque. */
  body.producer-skin .pd-chat > .pd-chat-pane,
  body.producer-skin .pd-chat > .pd-gen-pane { position: static; inset: auto; transition: none; }
  body.producer-skin .pd-chat.is-gen > .pd-chat-pane { display: none; }
  body.producer-skin .pd-chat:not(.is-gen) > .pd-gen-pane { display: none; }
  body.producer-skin .pd-chat-tab { display: none; }             /* nothing to slide open any more */
  body.producer-skin .pd-stage-body { padding: 14px; }
  /* THE TOP BAR WRAPS TO TWO ROWS rather than crushing three groups onto one. The switcher gets its own
     line and scrolls sideways; the title truncates instead of pushing the actions off-screen. */
  body.producer-skin .pd-topbar { grid-template-columns: 1fr auto; grid-template-areas: "l r" "c c"; row-gap: 8px; padding: 8px 12px; }
  body.producer-skin .pd-tb-l { grid-area: l; }
  body.producer-skin .pd-tb-r { grid-area: r; }
  body.producer-skin .pd-tb-c { grid-area: c; justify-content: flex-start; overflow-x: auto; scrollbar-width: none; }
  body.producer-skin .pd-tb-c::-webkit-scrollbar { display: none; }
  body.producer-skin .pd-tabs { flex: none; }
  body.producer-skin .pd-title { max-width: 44vw; }
  body.producer-skin .pd-qs, body.producer-skin .pd-films { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.producer-skin .pd-qs-card.is-feat { grid-column: span 2; }
  body.producer-skin .pd-hero h1 { font-size: 2.25rem; }
}
/* the edge tab that reopens the slide-over; hidden above 960px, where the chat is simply a column */
body.producer-skin .pd-chat-tab { position: fixed; left: 0; top: 50%; transform: translateY(-50%); z-index: 880; display: none; align-items: center; gap: 6px; height: 40px; padding: 0 13px 0 11px; border: 1px solid var(--pd-line-2); border-left: 0; border-radius: 0 10px 10px 0; background: var(--pd-card); color: var(--pd-ink-2); font: 600 .75rem/1 var(--sn-sans); cursor: pointer; box-shadow: var(--pd-elev-2); }
body.producer-skin .pd-chat-tab .sn-ic { width: 14px; height: 14px; color: var(--pd-accent); }

/* Reduced motion: the three tweens are already small; the streaming ones stop entirely. */
@media (prefers-reduced-motion: reduce) {
  body.producer-skin .pd-work-t .sn-ic,
  body.producer-skin .pd-work-dots i,
  body.producer-skin .pd-work-skel,
  body.producer-skin .pd-shot-skel { animation: none; }
  body.producer-skin .pd-chat > .pd-chat-pane,
  body.producer-skin .pd-chat > .pd-gen-pane { transition: none; }
}

/* ---- THE SCENE YOUR LAST DIRECTION CHANGED (A1) --------------------------------------------------
   One ember pulse when the turn lands, then a quiet ring that stays until the next direction. It marks
   WHERE the words landed and nothing else: it never states a price, never claims a scene is current,
   and nothing behind it can spend. The same class on all four surfaces, so the film answers whichever
   tab you happen to be in.

   OUTLINE, not a border or a pseudo-element: outlines are drawn outside the box and take no space, so a
   highlight can never reflow a storyboard grid, a timeline tile whose width IS its duration, or a card
   the drag-to-reorder logic measures. */
body.producer-skin .is-directed {
  outline: 2px solid var(--pd-accent);
  outline-offset: 1px;
  animation: pd-directed-land 1200ms ease-out 1;
}
@keyframes pd-directed-land {
  0%   { box-shadow: 0 0 0 0 var(--pd-accent-line); }
  55%  { box-shadow: 0 0 0 9px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) {
  body.producer-skin .is-directed { animation: none; }
}

/* ---- FILMED, BUT BEFORE YOUR LAST CHANGE (B2) ----------------------------------------------------
   The clip stays visible and stays playable — the customer paid for it and it is the most recent
   footage of this beat that exists. What changes is the CLAIM: a green "Filmed" chip over a clip that
   predates the direction above it is the interface telling a lie the engine never told.

   Amber, not red: nothing failed and nothing was lost. The words moved, which is a thing the customer
   did on purpose. The re-render is the Recreate button that was already on the card, behind the server
   quote and confirm sheet it always had — this styling opens no door of its own. */
body.producer-skin .pd-shot-badge.is-stale { color: var(--pd-amber); }
body.producer-skin .ts-stale {
  margin: 6px 0 0; padding: 7px 9px; border-radius: 8px;
  border: 1px solid rgba(200, 150, 60, .38);
  background: rgba(200, 150, 60, .10);
  color: var(--pd-ink-2); font: 500 .75rem/1.45 var(--sn-sans);
}
/* On the dense surfaces there is no room for a sentence, so the tile carries a quiet amber edge and its
   title attribute says the rest. Outline again — a timeline tile's WIDTH IS ITS DURATION. */
body.producer-skin .tl-film-cell.is-stale,
body.producer-skin .pd-strip-cell.is-stale { outline: 1px solid rgba(200, 150, 60, .38); outline-offset: -1px; }
body.producer-skin .pd-sb-cell.is-stale .pd-sb-fr { outline: 1px solid rgba(200, 150, 60, .38); outline-offset: -1px; }

/* ---- THE CHAT CONTAINS THE FILM (A2) -------------------------------------------------------------
   A mention chip and a diff-card scene label are now BUTTONS that open the shot, so they need to look
   like something you can press. Both reuse the delegated [data-shotpick] binding the board already has
   — this styling opens no door of its own and nothing behind it can spend. */
body.producer-skin button.pd-mention { cursor: pointer; font-family: var(--sn-sans); }
body.producer-skin button.pd-mention:hover { border-color: var(--pd-accent-line); background: var(--pd-accent-dim); }
body.producer-skin .pd-diff-k {
  display: inline-flex; align-items: center; gap: 6px; padding: 2px 7px 2px 3px;
  border: 1px solid transparent; border-radius: 99px; background: none; cursor: pointer;
  color: inherit; font: inherit; text-align: left;
}
body.producer-skin .pd-diff-k img { width: 22px; height: 22px; border-radius: 4px; object-fit: cover; display: block; }
body.producer-skin .pd-diff-k:hover { border-color: var(--pd-accent-line); background: var(--pd-accent-dim); }

/* ---- THE AUTEUR LANE: THE THREAD AS THE NARRATIVE SPINE -------------------------------------------
   Rob's reference drops a row into the CHAT for each stage of the work and puts the script stage on a
   card there rather than in a side panel. Ours did all of that work and narrated none of it, so the
   product looked like it had done less than it had.

   ⚠️ MEASURED, NOT ASSUMED. §30's law on this surface: "it is in the markup" is not "it is on screen" —
   the last card built here rendered at 0×0 because an unrelated `.pd-shot .ts-body { display: none }`
   was matching it. Nothing below inherits from a `.ts-*` or a `.pd-shot` scope, and every one of these
   classes is asserted to have a real rect on the deployed page before this is called done. */

/* The model's opening reaction to the brief. It sits above the pitch and again above the plan, and it is
   the ONE sentence on this surface written by a model about the brief itself — so it reads as speech,
   not as a system notice. No border, no icon: a box around it would make it look like a status. */
body.producer-skin .pd-riff {
  margin: 0 0 10px; font: 500 .9375rem/1.65 var(--sn-sans); color: var(--pd-ink);
  white-space: pre-wrap;
}
body.producer-skin .pd-say.pd-riff { margin: 0 0 2px; }

/* THE STAGE ROWS. A quiet stack of disclosures — the chevron rotates, the body is one paragraph of
   plain English derived from the board. Deliberately understated: this is the spine of the story, and a
   spine that shouts competes with the film. */
/* §67 — THE STEP LIST IS ONE QUIET LINE UNTIL ASKED. Rob: "OpenArt's left is calmer / more purely
   conversational … collapse the section list by default so the prose leads." The rows are unchanged and
   still honest; they simply stopped being five rows of chrome in front of the first readable sentence. */
body.producer-skin .pd-arcs-wrap > summary {
  display: flex; align-items: center; gap: 7px; cursor: pointer; list-style: none;
  padding: 6px 8px; border-radius: 8px; color: var(--pd-ink-4);
  font: 500 .6875rem/1 var(--sn-sans);
}
body.producer-skin .pd-arcs-wrap > summary::-webkit-details-marker { display: none; }
body.producer-skin .pd-arcs-wrap > summary:hover { background: var(--pd-card-2); color: var(--pd-ink-2); }
body.producer-skin .pd-arcs-wrap > summary .sn-ic { width: 12px; height: 12px; transition: transform var(--pd-fade) var(--sn-ease); }
body.producer-skin .pd-arcs-wrap[open] > summary .sn-ic { transform: rotate(90deg); }
body.producer-skin .pd-arcs-n {
  margin-left: auto; padding: 1px 7px; border-radius: 99px;
  background: var(--pd-card-2); color: var(--pd-ink-4); font: 500 .5625rem/1.6 var(--sn-sans);
}
body.producer-skin .pd-arcs { display: flex; flex-direction: column; gap: 1px; margin: 2px 0 4px; }
body.producer-skin .pd-arc { border-radius: 8px; }
body.producer-skin .pd-arc > summary {
  display: flex; align-items: center; gap: 7px; padding: 6px 8px; border-radius: 8px;
  cursor: pointer; list-style: none;
  font: 600 .8125rem/1.4 var(--sn-sans); color: var(--pd-ink-3);
}
body.producer-skin .pd-arc > summary::-webkit-details-marker { display: none; }
body.producer-skin .pd-arc > summary:hover { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-arc > summary .sn-ic-slot {
  display: inline-flex; color: var(--pd-ink-4);
  transition: transform var(--pd-fade) ease;
}
body.producer-skin .pd-arc[open] > summary { color: var(--pd-ink); }
body.producer-skin .pd-arc[open] > summary .sn-ic-slot { transform: rotate(90deg); }
body.producer-skin .pd-arc > p {
  margin: 0 0 6px; padding: 0 8px 0 26px;
  font: .8125rem/1.65 var(--sn-sans); color: var(--pd-ink-3);
}

/* THE SCRIPT-CONCEPT CARD. The teaser in the thread; the whole script is still one click away and is
   still the honest pitch. Its Continue advances the WALK — it can no more spend than the phase rail can,
   and the render lives on the commit card below it with its own confirm sheet. */
body.producer-skin .pd-concept {
  margin: 8px 0 2px; padding: 12px 13px 11px; border-radius: var(--pd-r-card);
  border: 1px solid var(--pd-line-2); background: var(--pd-card); box-shadow: var(--pd-elev-1);
}
body.producer-skin .pd-concept-h { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
body.producer-skin .pd-concept-h .sn-ic-slot { color: var(--pd-ink-3); display: inline-flex; }
body.producer-skin .pd-concept-h b { font: 600 .9375rem/1.35 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-concept-m {
  margin-left: auto; font: 500 .75rem/1.4 var(--sn-sans); color: var(--pd-ink-4);
  font-variant-numeric: tabular-nums;
}
body.producer-skin .pd-concept-b {
  /* §89 — the plan's prose sits IN the thread, so it reads on the chat's rhythm, not the document's. */
  margin: 8px 0 0; font: .8125rem/1.5 var(--sn-sans); color: var(--pd-ink-2);
}
body.producer-skin .pd-concept-f { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 11px; }
body.producer-skin .pd-concept-f .pd-btn { flex: 0 1 auto; }
body.producer-skin .pd-concept-n { margin: 9px 0 0; font: .6875rem/1.5 var(--sn-sans); color: var(--pd-ink-4); }

/* THE REFERENCES CARD. Same shape as the reference flow's "Coco & The Sets", opposite promise: it counts
   what the film is genuinely locked to and names what it is NOT, and its only control is View. There is
   no priced button on it — the draw offer lives on the anchors step and stays there. */
body.producer-skin .pd-refs {
  margin: 8px 0 2px; padding: 11px 12px; border-radius: var(--pd-r-card);
  border: 1px solid var(--pd-line); background: var(--pd-sunk);
}
body.producer-skin .pd-refs-h { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
body.producer-skin .pd-refs-h .sn-ic-slot { color: var(--pd-ink-3); display: inline-flex; }
body.producer-skin .pd-refs-h b { font: 600 .875rem/1.35 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-refs-m {
  margin-left: auto; font: 500 .75rem/1.4 var(--sn-sans); color: var(--pd-ink-4);
  font-variant-numeric: tabular-nums;
}
body.producer-skin .pd-refs-strip { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 9px; }
body.producer-skin .pd-refs-strip img {
  width: 46px; height: 46px; border-radius: 7px; object-fit: cover; display: block;
  border: 1px solid var(--pd-line-2); background: var(--pd-skel);
}
body.producer-skin .pd-refs-open {
  margin: 9px 0 0; padding-left: 17px; font: .75rem/1.6 var(--sn-sans); color: var(--pd-ink-3);
}
body.producer-skin .pd-refs-f { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
body.producer-skin .pd-refs-n { flex: 1 1 200px; font: .6875rem/1.5 var(--sn-sans); color: var(--pd-ink-4); }

/* THE FINISHED CUT, IN THE CONVERSATION THAT MADE IT. The premiere is untouched and is still where you
   scrub; this is the deliverable where the talking happened. Muted on arrival — a paid deliverable that
   starts making noise is a bug this codebase has already shipped once. */
body.producer-skin .pd-cut {
  margin: 10px 0 2px; padding: 11px 12px 12px; border-radius: var(--pd-r-card);
  border: 1px solid var(--pd-line-2); background: var(--pd-card); box-shadow: var(--pd-elev-1);
}
body.producer-skin .pd-cut-h { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 9px; }
body.producer-skin .pd-cut-h .sn-ic-slot { color: var(--pd-ink-3); display: inline-flex; }
body.producer-skin .pd-cut-h b { font: 600 .9375rem/1.35 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-cut-m {
  margin-left: auto; font: 500 .75rem/1.4 var(--sn-sans); color: var(--pd-ink-4);
  font-variant-numeric: tabular-nums;
}
body.producer-skin .pd-cut-v {
  display: block; width: 100%; max-height: 46vh; border-radius: var(--pd-r-player);
  background: #14130F; object-fit: contain;
}
body.producer-skin .pd-cut-f { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
body.producer-skin .pd-cut-f .pd-btn .sn-ic-slot { display: inline-flex; margin-right: 5px; }

/* =====================================================================================================
   §55. THE PRODUCER LIBRARY — the DEFAULT surface, and the back arrow out of the create flow
   =====================================================================================================
   Opening Producer opens a library, not a chat box. This block styles that landing and the one control
   that returns to it. It borrows .pd-sec / .pd-sec-h / .pd-qs / .pd-films wholesale rather than restating
   them: the shelves ARE the quick-start grid geometry and Your films IS the project-card grid, so a shelf
   and a real film line up on the same column edges and the page reads as one library instead of two. */

body.producer-skin .pd-lib-top { padding: 26px 0 6px; text-align: center; }
body.producer-skin .pd-lib-top h1 { font: 700 2.5rem/1.1 var(--sn-sans); letter-spacing: -.02em; margin: 0 0 8px; color: var(--pd-ink); }
body.producer-skin .pd-lib-top .sub { font: .875rem/1.55 var(--sn-sans); color: var(--pd-ink-3); margin: 0 auto 22px; max-width: 560px; }
/* The primary CTA is the loudest thing on the page — it is the one job this screen has. */
body.producer-skin .pd-lib-cta { height: 46px; padding: 0 26px; border-radius: 99px; font-size: .9375rem; font-weight: 700; }
body.producer-skin .pd-lib-cta .sn-ic-slot { display: inline-flex; margin-right: 8px; }
body.producer-skin .pd-lib-cta .sn-ic { width: 15px; height: 15px; }
/* §87 — the old §58 `.pd-lib-starts` block lived here and is deleted, not moved: it declared the same
   selector a second time, and a property set in only ONE of two blocks (`width: fit-content`) survives the
   cascade invisibly. That split rule is the bug this sheet has now hit four times. The one live definition
   is beside the hero it belongs to, under "THE HERO IS THE COMPOSER". */

/* SHELVES. The "Coming soon" badge sits beside the heading rather than on the tiles, so the placeholder
   art can stay quiet — one honest label per shelf, not four repeated over empty frames. */
body.producer-skin .pd-shelf-soon {
  display: inline-flex; align-items: center; height: 20px; padding: 0 9px; border-radius: 99px;
  border: 1px solid var(--pd-line-2); background: var(--pd-sunk); color: var(--pd-ink-4);
  font: 600 .625rem/1 var(--sn-sans); letter-spacing: .04em; text-transform: uppercase;
}
/* Greyed, dashed, unclickable — a frame where a film will go. Deliberately NOT a card with a shadow:
   it must not read as a thing that failed to load, and it must not read as a thing you can press. */
body.producer-skin .pd-ph-card {
  position: relative; aspect-ratio: 225/205; border-radius: var(--pd-r-card);
  border: 1px dashed var(--pd-line-2); background: var(--pd-sunk);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  color: var(--pd-ghost); opacity: .85; pointer-events: none; user-select: none;
}
body.producer-skin .pd-ph-card .sn-ic { width: 20px; height: 20px; }
body.producer-skin .pd-ph-card .pd-ph-lb { font: 600 .6875rem/1 var(--sn-sans); color: var(--pd-ink-3); }

/* 🔴 THE HONESTY LINE IS NOT DECORATION, SO IT DOES NOT GET THE DECORATION INK.
   .pd-rail-empty is the skin's quietest tone — measured at 3.4:1 on the deployed page, which is fine for
   an aside and not fine for the one sentence that tells a customer these tiles are not real films. A
   claim nobody can read is a claim we have not made, so the shelf's note steps up one rung (5.3:1) while
   every other .pd-rail-empty in the product keeps the tone it was designed with. */
body.producer-skin .pd-shelf .pd-rail-empty { color: var(--pd-ink-3); }
body.producer-skin .pd-shelf-soon { color: var(--pd-ink-3); }

/* Your films, when there are none yet. */
body.producer-skin .pd-lib-empty {
  display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center;
  padding: 40px 24px; border: 1px dashed var(--pd-line-2); border-radius: var(--pd-r-card);
  background: var(--pd-sunk);
}
body.producer-skin .pd-lib-empty .sn-ic { width: 26px; height: 26px; color: var(--pd-ghost); }
body.producer-skin .pd-lib-empty b { font: 700 .9375rem/1.3 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-lib-empty > span { font: .8125rem/1.55 var(--sn-sans); color: var(--pd-ink-3); max-width: 420px; }
body.producer-skin .pd-lib-empty .sn-btn { margin-top: 8px; }

/* THE WAY BACK. One affordance, two hosts: a bar above the create hero, and the first cell of the
   workspace header. It is sized to the 46px header row so adding it does not reflow the workspace. */
body.producer-skin .pd-topbar { display: flex; align-items: center; gap: 8px; padding: 4px 0 12px; }
body.producer-skin .pd-back-lb { font: 600 .8125rem/1 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-back {
  flex: none; display: grid; place-items: center; width: 30px; height: 30px; padding: 0;
  border: 1px solid var(--pd-line); border-radius: 8px; background: var(--pd-card);
  color: var(--pd-ink-2); cursor: pointer;
}
body.producer-skin .pd-back:hover { color: var(--pd-ink); border-color: var(--pd-line-2); }
body.producer-skin .pd-back .sn-ic { width: 15px; height: 15px; }
body.producer-skin .pd-head .pd-back { width: 28px; height: 28px; }

@media (max-width: 900px) {
  body.producer-skin .pd-lib-top h1 { font-size: 1.875rem; }
  body.producer-skin .pd-ph-row { grid-template-columns: repeat(2, 1fr); }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════
   §66 · THE DIRECTOR EDITOR — FOUR REGIONS, MEASURED OFF OPENART'S OWN EDITOR
   ═══════════════════════════════════════════════════════════════════════════════════════════════════
   Proportions read from the reference capture (1568px wide): the left plan column runs to ~24%, the
   inspector is ~9% and deliberately narrow (it is a panel, not a third of the app), and the timeline
   region is ~100px — a strip, not a drawer. The stage takes everything left over, which is the point:
   the picture is the biggest thing on screen.

   ⚠️ `.pd-body` and `.pd-railcol` keep their names because tlApplyCols() queries them by class. The
   drag handles still write --pd-w-chat / --pd-w-assets, so a customer's saved widths survive this. */
body.producer-skin .pd-ws { --pd-w-chat: 24%; --pd-w-assets: 264px; }

/* ---- TOP BAR: title left · switcher CENTRED · actions right ---------------------------------------
   Three tracks with the centre one auto-sized, so the switcher sits at the true centre of the window
   and does NOT drift as the title grows — which is what a flex row with a spacer would have done. */
/* §80 — THE TOP BAR IS A RULE, NOT A HEADER. In the reference it is ~3.8% of the app height — about
   34px on a 900px viewport — and it carries the title, three view tabs and seven controls without ever
   feeling like a section of its own. Ours was 46px, which on a laptop is a sixth of the picture's
   height spent on chrome. */
body.producer-skin .pd-topbar {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: 10px; padding: 5px 12px; min-height: 38px;
  border-bottom: 1px solid var(--pd-line); background: var(--pd-panel);
}
body.producer-skin .pd-tb-l { display: flex; align-items: center; gap: 8px; min-width: 0; }
body.producer-skin .pd-tb-c { display: flex; justify-content: center; }
body.producer-skin .pd-tb-r { display: flex; align-items: center; justify-content: flex-end; gap: 8px; min-width: 0; }
body.producer-skin .pd-tb-l .count { font: 500 .6875rem/1 var(--sn-sans); color: var(--pd-ink-4); white-space: nowrap; }

/* ---- CENTRE: the picture, then the transport UNDER it ---------------------------------------------
   OpenArt never covers the frame with controls. The stage is a column; the transport is its last row. */
/* 🔵 §83 — THE PICTURE SITS ON BLACK, which is the most visible thing left between us and the
   reference. OpenArt's centre track is markedly darker than its chrome — near-black behind the frame —
   and that is not decoration: a film judged against a light-grey panel is judged against the panel.
   Letterboxing an unknown aspect over anything but black also shows the seam.
   One step below the canvas rather than pure #000, so the frame's own black still reads as black
   against it and the region does not become a hole in the page. */
body.producer-skin .pd-stage { display: flex; flex-direction: column; min-height: 0; background: var(--pd-stage-bg); }
/* §80 — 20/22 was a comfortable page margin around a small picture. OpenArt runs its frame nearly to
   the edges of the centre track because the frame IS the content; the breathing room belongs OUTSIDE
   the stage, between the columns. */
body.producer-skin .pd-stage-body { flex: 1; min-height: 0; padding: 12px 14px; }
/* The transport is a caption under the picture, not a toolbar: in the reference it is a timecode, a
   play button and a timecode, centred, about 20px tall. */
/* §83 — CENTRED, as the reference draws it: elapsed · play · total sit under the middle of the frame,
   and the shot count is pushed to the edge rather than trailing the group. A transport that starts at
   the left margin reads as a toolbar; centred under the picture it reads as part of the picture. */
body.producer-skin .pd-transport {
  position: relative; flex: none; display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 5px 14px; border-top: 1px solid var(--pd-line);
}
body.producer-skin .pd-transport .pd-bar-grow { display: none; }
body.producer-skin .pd-tp-meta { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); }
body.producer-skin .pd-tp-t { font: 500 .6875rem/1 var(--sn-mono, var(--sn-sans)); color: var(--pd-ink-4); font-variant-numeric: tabular-nums; }
body.producer-skin .pd-tp-play {
  width: 22px; height: 22px; display: grid; place-items: center; border: 0; border-radius: 50%;
  background: var(--pd-card-2); color: var(--pd-ink); cursor: pointer;
}
body.producer-skin .pd-tp-play:disabled { opacity: .38; cursor: default; }
body.producer-skin .pd-tp-play .sn-ic { width: 12px; height: 12px; }
body.producer-skin .pd-tp-meta { font: 500 .6875rem/1 var(--sn-sans); color: var(--pd-ink-4); }

/* ---- RIGHT: the tabbed inspector -------------------------------------------------------------------
   Icon-over-label tabs, four across, exactly as OpenArt draws them. The active one is marked with the
   accent as INK plus a 2px underline — not a filled pill, because the filled accent in this view already
   belongs to the view switcher and the primary action. */
body.producer-skin .pd-railcol { display: flex; flex-direction: column; min-height: 0; padding: 0; background: transparent; }
/* 🔵 §71 — THE INSPECTOR IS A shadcn <Tabs>, ported from its own class strings:
     TabsList     inline-flex items-center justify-center rounded-lg p-[3px] h-9 bg-muted text-muted-foreground
     TabsTrigger  h-[calc(100%-1px)] flex-1 gap-1.5 rounded-md border border-transparent px-2 py-1
                  text-sm font-medium  +  data-[state=active]:bg-background data-[state=active]:text-foreground
   🔑 NOTE WHAT THE ACTIVE STATE IS: `bg-background` — the CANVAS colour, raised out of a muted track. A
   segmented control, NOT a coloured pill. That is deliberate restraint on shadcn's part and it is the
   reason this panel does not compete with the one brand-solid thing in the editor.

   ⚠️ THE VIEW SWITCHER KEEPS THE BRAND, and the difference is the point: Rob's rule is one accent on "the
   active state and the single primary". The switcher is the editor's primary navigation and earns it; the
   inspector is a panel control inside one column and does not. Two tab components, two treatments, one
   accent in the room.

   The triggers stay icon-over-label because "Voiceover" cannot sit beside three siblings in a 264px column
   at text-sm — so the label drops to text-[10px] and the anatomy above is otherwise kept. */
body.producer-skin .pd-insp-tabs {
  flex: none; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 2px; margin: 8px 8px 4px; padding: 3px;
  border-radius: var(--pd-radius); background: var(--pd-card-2);
}
body.producer-skin .pd-insp-tab {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  padding: 7px 2px 6px; border: 1px solid transparent; border-radius: var(--pd-r-ctl);
  background: none; cursor: pointer; outline: none;
  color: var(--pd-ink-3); font: 500 .625rem/1 var(--sn-sans);
  transition: background-color 150ms var(--sn-ease), color 150ms var(--sn-ease);
}
body.producer-skin .pd-insp-tab .sn-ic { width: 15px; height: 15px; }
body.producer-skin .pd-insp-tab:hover { color: var(--pd-ink-2); }
body.producer-skin .pd-insp-tab:focus-visible { border-color: var(--pd-ring); box-shadow: 0 0 0 3px color-mix(in srgb, var(--pd-ring) 50%, transparent); }
/* data-[state=active]:bg-background data-[state=active]:text-foreground — raised, neutral, shadow-xs. */
body.producer-skin .pd-insp-tab.is-on { background: var(--pd-canvas); color: var(--pd-ink); box-shadow: var(--pd-shadow-xs); }
/* §80 — a 190px column cannot afford 10px of gutter on each side AND a two-column tile grid. */
body.producer-skin .pd-insp-body { flex: 1; min-height: 0; padding: 8px 8px 16px; }
body.producer-skin .pd-insp-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
  height: 100%; min-height: 160px; padding: 0 16px; text-align: center;
}
body.producer-skin .pd-insp-empty b { font: 500 .875rem/1.3 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-insp-empty span { font: 400 .8125rem/1.55 var(--sn-sans); color: var(--pd-ink-3); max-width: 30ch; }

/* ---- BOTTOM: the filmstrip, with its OWN toolbar --------------------------------------------------
   The four "Add …" verbs live HERE, beside the strip they act on. That is the distribution argument in
   one region: they were never stage-bar controls, they were timeline controls sitting in the wrong room. */
body.producer-skin .pd-timeline {
  flex: none; border-top: 1px solid var(--pd-line); background: var(--pd-panel);
}
body.producer-skin .pd-tl-bar { display: flex; align-items: center; gap: 4px; padding: 6px 12px; }
body.producer-skin .pd-tl-act {
  display: inline-flex; align-items: center; gap: 6px; height: var(--pd-h-sm); padding: 0 9px;
  border: 0; border-radius: 7px; background: none; color: var(--pd-ink-3);
  font: 500 .6875rem/1 var(--sn-sans); cursor: pointer;
}
body.producer-skin .pd-tl-act .sn-ic { width: 13px; height: 13px; }
body.producer-skin .pd-tl-act:hover:not(:disabled) { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-tl-act:disabled { opacity: .34; cursor: default; }
body.producer-skin .pd-tl-zoom { display: inline-flex; align-items: flex-end; gap: 3px; height: 12px; opacity: .5; }
body.producer-skin .pd-tl-zoom i { width: 3px; background: var(--pd-ink-4); border-radius: 1px; }
body.producer-skin .pd-tl-zoom i:nth-child(1) { height: 5px; }
body.producer-skin .pd-tl-zoom i:nth-child(2) { height: 8px; }
body.producer-skin .pd-tl-zoom i:nth-child(3) { height: 12px; }
body.producer-skin .pd-tl-times { display: flex; align-items: center; gap: 5px; padding: 0 12px; }
body.producer-skin .pd-tl-tick {
  flex: none; width: 74px; padding-left: 2px;
  font: .5625rem/16px var(--sn-sans); color: var(--pd-ink-4);
}
body.producer-skin .pd-tl-strip { position: relative; display: flex; align-items: center; gap: 5px; padding: 0 12px 10px; overflow-x: auto; overflow-y: hidden; }
/* The playhead. Positioned inside the strip's scrolling content so it rides the tiles rather than the
   viewport, transform-only so the raf loop never touches layout, and untouchable — the scrub bar above
   the picture is still the only place you can grab the film. */
body.producer-skin .pd-tl-head {
  position: absolute; top: 0; bottom: 10px; left: 0; width: 2px; z-index: 3;
  margin-left: -1px; border-radius: 1px; background: var(--pd-accent);
  pointer-events: none; opacity: 0; transition: opacity .18s ease; will-change: transform;
}
body.producer-skin .pd-tl-head.is-on { opacity: 1; }
body.producer-skin .pd-tl-head::before {
  content: ""; position: absolute; top: -1px; left: 50%; width: 7px; height: 7px;
  transform: translateX(-50%); border-radius: 99px; background: var(--pd-accent);
}
body.producer-skin .pd-tlc.is-now { border-color: var(--pd-accent); box-shadow: 0 0 0 1px var(--pd-accent); }
@media (prefers-reduced-motion: reduce) { body.producer-skin .pd-tl-head { transition: none; } }
body.producer-skin .pd-tlc {
  position: relative; flex: none; width: 74px; aspect-ratio: 16 / 9; padding: 0;
  border: 1px solid var(--pd-line); border-radius: 5px; overflow: hidden;
  background: var(--pd-sunk); cursor: pointer;
}
body.producer-skin .pd-tlc img { width: 100%; height: 100%; object-fit: cover; display: block; }
body.producer-skin .pd-tlc.is-on { border-color: var(--pd-accent); }
body.producer-skin .pd-tlc .n {
  position: absolute; left: 3px; bottom: 2px; padding: 0 4px; border-radius: 99px;
  background: rgba(0, 0, 0, .62); color: #fff; font: 600 .5625rem/1.5 var(--sn-sans);
}
body.producer-skin .pd-tlc-ph { display: grid; place-items: center; width: 100%; height: 100%; color: var(--pd-ghost); }
body.producer-skin .pd-tlc-ph .sn-ic { width: 15px; height: 15px; }
body.producer-skin .pd-tl-none { font: 400 .6875rem/1 var(--sn-sans); color: var(--pd-ink-4); padding: 12px 0; }

/* Narrow: the inspector is the first thing to go (it is reference), then the timeline (it is a film with
   no shots yet on a phone). Same order the columns already collapse in. */
@media (max-width: 1180px) {
  body.producer-skin .pd-timeline { display: none; }
}

/* §70 — PHONE. Below 640px the utility row is the thing that overflows: six chrome buttons beside a title
   on a 390px screen. The ones that survive are the ones you cannot do without on a phone (undo, and the
   view you are in); the rest are desktop conveniences and are hidden rather than shrunk into unreadable
   nubs. Type and padding step down one notch, not several — this is still the same product. */
@media (max-width: 640px) {
  body.producer-skin .pd-topbar { padding: 7px 10px; min-height: 42px; }
  body.producer-skin .pd-title { max-width: 38vw; font-size: .75rem; }
  body.producer-skin .pd-tb-l .count { display: none; }
  body.producer-skin .pd-util #pd-history,
  body.producer-skin .pd-util #tl-reset { display: none; }
  body.producer-skin .pd-tab { padding: 0 11px; font-size: .6875rem; }
  body.producer-skin .pd-stage { min-height: 40vh; }
  body.producer-skin .pd-chat { min-height: 56vh; }
  body.producer-skin .pd-stage-body { padding: 12px 10px; }
  body.producer-skin .pd-thread { padding: 12px 12px 8px; gap: 24px; }
  body.producer-skin .pd-msg[data-from="user"] { max-width: 92%; }
  body.producer-skin .pd-msg[data-from="assistant"],
  body.producer-skin .pd-msg[data-from="system"] { padding-left: 30px; }
  body.producer-skin .pd-qs, body.producer-skin .pd-films { grid-template-columns: minmax(0, 1fr); }
  body.producer-skin .pd-qs-card.is-feat { grid-column: span 1; }
  body.producer-skin .pd-hero h1 { font-size: 1.75rem; }
  /* A composer whose primary wraps is the §63 bug at a smaller width — pin it, let the picker shrink. */
  body.producer-skin .pd-comp-row { flex-wrap: nowrap; }
  body.producer-skin .pd-comp-row .cmp-adv { min-width: 0; flex: 1 1 auto; }
}

/* 🔵 §73 — THE COMPOSER, AS shadcn's InputGroup. Its published nesting is:
     <InputGroup>
       <InputGroupTextarea  "field-sizing-content max-h-48 min-h-16">
       <InputGroupAddon     "justify-between gap-1">      ← a FOOTER, not a trailing button
         <PromptInputTools  "flex items-center gap-1">    ← left
         <PromptInputSubmit>                              ← right
   The shipped markup puts attach · textarea · ⋯ · Generate on ONE row, which is why the prompt gets a
   narrow slot with four controls crowding it. Ported with flex-wrap rather than by rewriting the markup:
   `#sl-composer` is the SHARED studio composer and every other view renders the same node — restyling
   inside `body.producer-skin` changes the Producer and touches nothing else.
   min-h-16 = 64px is Elements' own minimum; a one-line box is what makes a composer feel like an
   afterthought. */
body.producer-skin .pd-composer .cmp-prompt-row { flex-wrap: wrap; align-items: center; gap: 8px; }
body.producer-skin .pd-composer .cmp-prompt-row > textarea#cm-ta {
  flex: 1 0 100%; order: 1; min-height: 64px; max-height: 192px;
  padding: 0; border: 0; background: none; resize: none;
}
body.producer-skin .pd-composer .cmp-prompt-row > .side-attach,
body.producer-skin .pd-composer .cmp-prompt-row > .cmp-adv { order: 2; }
/* `margin-left: auto` on the submit is what makes the footer `justify-between` without a wrapper div. */
body.producer-skin .pd-composer .cmp-prompt-row > .cmp-gen { order: 3; margin-left: auto; }
/* shadcn Button, default size: h-9 rounded-md px-4 text-sm font-medium — and the fill is the neutral
   primary from §72, so the send button is near-white with dark ink like every other primary. */
body.producer-skin .pd-composer .cmp-gen.sn-btn {
  height: var(--pd-h-md); padding: 0 16px; border-radius: var(--pd-r-ctl);
  font: 500 .875rem/1 var(--sn-sans); box-shadow: var(--pd-shadow-xs);
}

/* §75 — THE MUSIC + VOICEOVER INSPECTOR PANELS. Same tokens, same scale, same restraint as chunk 3:
   16px between groups, text-sm bodies, --pd-r-ctl on every field, and the one brand-coloured thing in
   here is the focus ring. Nothing in this panel spends, so nothing in it is styled like a primary. */
body.producer-skin .pd-insp-pane { padding: 4px 2px 8px; }
body.producer-skin .pd-insp-state { display: flex; flex-direction: column; gap: 3px; }
body.producer-skin .pd-insp-state b { font: 500 .875rem/1.3 var(--sn-sans); color: var(--pd-ink); }
body.producer-skin .pd-insp-state span { font: 400 .8125rem/1.5 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-insp-note { margin: 0; font: 400 .8125rem/1.5 var(--sn-sans); color: var(--pd-ink-3);
  padding: 9px 11px; border-radius: var(--pd-r-ctl); background: var(--pd-sunk); border: 1px solid var(--pd-line); }
/* shadcn Textarea: rounded-md border-input bg-transparent px-3 py-2 text-sm shadow-xs, ring-[3px] on focus. */
body.producer-skin .pd-insp-ta {
  width: 100%; resize: vertical; padding: 8px 11px;
  border-radius: var(--pd-r-ctl); border: 1px solid var(--pd-line-2);
  background: var(--pd-sunk); color: var(--pd-ink);
  font: .875rem/1.55 var(--sn-sans); box-shadow: var(--pd-shadow-xs);
  transition: border-color 150ms var(--sn-ease), box-shadow 150ms var(--sn-ease);
}
body.producer-skin .pd-insp-ta::placeholder { color: var(--pd-ink-4); }
body.producer-skin .pd-insp-ta:focus { outline: none; border-color: var(--pd-ring); box-shadow: 0 0 0 3px color-mix(in srgb, var(--pd-ring) 50%, transparent); }
body.producer-skin .pd-insp-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* 💰 THE FREENESS LINE IS PART OF THE CONTROL, not decoration. Every other surface in the Producer states
   what a click costs at the moment it could cost something; these cost nothing and say so. */
body.producer-skin .pd-insp-free { font: 400 .75rem/1.45 var(--sn-sans); color: var(--pd-ink-4); flex: 1; min-width: 12ch; }
body.producer-skin .pd-vo-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
body.producer-skin .pd-vo-h { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; min-width: 0; }
body.producer-skin .pd-vo-h .pd-rail-k { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.producer-skin .pd-vo-n {
  flex: none; width: 18px; height: 18px; border-radius: 50%;
  display: grid; place-items: center; background: var(--pd-card-2);
  color: var(--pd-ink-2); font: 500 .625rem/1 var(--sn-sans); font-variant-numeric: tabular-nums;
}


/* §78 — THE LIBRARY IS A COLUMN OF COLLAPSED ACCORDIONS. Rob: all sections closed by default, and
   VERTICAL — no horizontal carousels. A row you have to scroll sideways hides its own contents; a grid
   that wraps shows you how much is there. `<details>` handles the disclosure natively. */
body.producer-skin .pd-acc { margin-bottom: 10px; border-bottom: 1px solid var(--pd-line); padding-bottom: 10px; }
body.producer-skin .pd-acc > summary {
  display: flex; align-items: center; gap: 10px; cursor: pointer; list-style: none;
  padding: 10px 2px; border-radius: var(--pd-r-ctl); user-select: none;
}
body.producer-skin .pd-acc > summary::-webkit-details-marker { display: none; }
body.producer-skin .pd-acc > summary:hover { background: var(--pd-card); }
body.producer-skin .pd-acc > summary:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--pd-ring) 50%, transparent); }
body.producer-skin .pd-acc > summary h2 { font: 500 1rem/1.2 var(--sn-sans); color: var(--pd-ink); margin: 0; }
/* the chevron IS the affordance — it turns, so open/closed is legible without reading the label */
body.producer-skin .pd-acc > summary .sn-ic { width: 16px; height: 16px; color: var(--pd-ink-3); transition: transform 160ms var(--sn-ease); }
body.producer-skin .pd-acc[open] > summary .sn-ic { transform: rotate(90deg); }
body.producer-skin .pd-shelf-n {
  flex: none; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px;
  display: grid; place-items: center; background: var(--pd-card-2);
  color: var(--pd-ink-3); font: 500 .6875rem/1 var(--sn-sans); font-variant-numeric: tabular-nums;
}
body.producer-skin .pd-acc-note { margin: 2px 2px 12px; font: 400 .8125rem/1.5 var(--sn-sans); color: var(--pd-ink-3); max-width: 68ch; }
/* VERTICAL: a wrapping grid, never a sideways scroller. */
body.producer-skin .pd-acc-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 12px; padding-bottom: 6px;
}
body.producer-skin .pd-acc .pd-ph-card {
  aspect-ratio: 16 / 10; display: grid; place-items: center;
  border-radius: var(--pd-r-ctl); background: var(--pd-sunk);
  border: 1px dashed var(--pd-line-2); color: var(--pd-ghost);
}
@media (max-width: 640px) {
  body.producer-skin .pd-acc-grid { grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 10px; }
}

/* =============================================================================
   §81 — THE MESSAGE ACTION ROW, ported from 21st.dev "AI Message" (id 23819).
   =============================================================================
   The component's own reasoning, kept because each line is a defect it avoids:

   · ALWAYS MOUNTED, ONLY FADED. Mounting the row on hover changes the row's height, so every message
     below it jumps as the pointer travels down a thread. It is present and invisible instead.
   · HOVER **AND** :focus-within. A hover-only control row is unreachable by keyboard.
   · SCOPED TO ITS OWN CLASS, not a generic descendant/group selector, so a hover anywhere else on the
     page cannot reveal every row at once.
   · IT SLIDES OUT OF THE MESSAGE'S OWN EDGE — negative for the assistant, positive for the user — so it
     reads as belonging to that message rather than fading in from nowhere.
   · AND IT IS OFF ENTIRELY UNDER prefers-reduced-motion. */
body.producer-skin .pd-msg-acts {
  display: flex; align-items: center; gap: 2px; margin-top: 2px;
  --pd-msg-slide: -6px;
}
body.producer-skin .pd-msg-acts[data-side="user"] { justify-content: flex-end; --pd-msg-slide: 6px; }
body.producer-skin .pd-msg-act {
  display: inline-grid; place-items: center; width: 26px; height: 26px;
  border: 0; border-radius: var(--pd-r-ctl); background: none; color: var(--pd-ink-4); cursor: pointer;
  opacity: 0; transform: translateX(var(--pd-msg-slide)) scale(.9);
  transition: opacity 200ms cubic-bezier(.23, 1, .32, 1),
              transform 200ms cubic-bezier(.23, 1, .32, 1),
              background-color 150ms ease, color 150ms ease;
}
body.producer-skin .ai-msg-root:hover .pd-msg-act,
body.producer-skin .ai-msg-root:focus-within .pd-msg-act { opacity: 1; transform: translateX(0) scale(1); }
body.producer-skin .pd-msg-act:hover { background: var(--pd-card-2); color: var(--pd-ink-2); }
body.producer-skin .pd-msg-act:focus-visible { opacity: 1; transform: none; outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--pd-ring) 50%, transparent); }
body.producer-skin .pd-msg-act .sn-ic { width: 13px; height: 13px; }
/* The confirmation is IN PLACE — a toast for something the customer just did on purpose is noise. */
body.producer-skin .pd-msg-act.is-done { color: var(--pd-green); animation: pd-msg-pop 250ms cubic-bezier(.23, 1, .32, 1); }
@keyframes pd-msg-pop { 0% { transform: scale(1); } 45% { transform: scale(1.25); } 100% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) {
  body.producer-skin .pd-msg-act { transition-duration: 0ms; transform: none; }
  body.producer-skin .ai-msg-root:hover .pd-msg-act,
  body.producer-skin .ai-msg-root:focus-within .pd-msg-act { transform: none; }
  body.producer-skin .pd-msg-act.is-done { animation: none; }
}

/* §81 — the composer is a FIELD, so the whole box takes a text cursor and a click anywhere in it puts
   the caret in the prompt (Input Bar, id 12400). The auto-grow is in JS because it needs scrollHeight. */
body.producer-skin .pd-composer .composer { cursor: text; }
body.producer-skin .pd-composer .composer button,
body.producer-skin .pd-composer .composer textarea { cursor: auto; }
body.producer-skin .pd-composer .composer button { cursor: pointer; }
body.producer-skin .pd-composer #tl-direct { resize: none; }

/* §84 — THE SIZE CONTROL. Five ratios is more than Length carries, so the segment wraps rather than
   forcing the advanced panel wider than the column it lives in. */
body.producer-skin #tl-aspect { flex-wrap: wrap; }
body.producer-skin #tl-aspect button { font-variant-numeric: tabular-nums; padding: 0 8px; }

/* =============================================================================
   §85 — DELETE: A HOVER TRASH ON ONE FILM, AND A SELECTION MODE FOR MANY.
   =============================================================================
   Both controls appear ONLY on the caller's own films. The template categories carry neither, because
   they are not the customer's rows to delete — that is enforced in the markup, and this stylesheet
   simply has nothing to say about a control that is never drawn there. */
body.producer-skin .pd-film { position: relative; }
/* The single-film trash: quiet until wanted, and reachable by keyboard — a hover-only destructive
   control is one a keyboard user cannot see and can still trigger. */
body.producer-skin .pd-film-del {
  position: absolute; top: 6px; right: 6px; z-index: 2;
  display: grid; place-items: center; width: 24px; height: 24px;
  border-radius: var(--pd-r-ctl); background: rgba(8, 10, 13, .72); color: var(--pd-ink-2);
  opacity: 0; transition: opacity 150ms var(--sn-ease), background-color 150ms var(--sn-ease);
}
body.producer-skin .pd-film:hover .pd-film-del,
body.producer-skin .pd-film:focus-within .pd-film-del { opacity: 1; }
body.producer-skin .pd-film-del:hover { background: var(--pd-red); color: #fff; }
body.producer-skin .pd-film-del:focus-visible { opacity: 1; outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--pd-ring) 50%, transparent); }
body.producer-skin .pd-film-del .sn-ic { width: 12px; height: 12px; }

/* SELECT MODE. The checkbox is always visible once the mode is on — a tick you have to hover to find is
   not a selection UI. ⚠️ Its APPEARANCE is defined once, in the §86 block at the end of this file; only
   the things that place it live here, so there is one rule to change and not two that disagree. */
body.producer-skin .pd-film-sel { position: absolute; top: 6px; left: 6px; z-index: 2; display: grid; place-items: center; }
body.producer-skin .pd-film-sel .sn-ic { width: 12px; height: 12px; }


/* §86 — THE SELECTION BAR DOCKS. A row in the flow pushed the grid down the moment it appeared and read
   as an error banner; docked, nothing reflows, it cannot scroll out of reach, and it plainly belongs to
   the selection rather than to the section. It also rises rather than snapping in — a destructive
   control appearing instantly under the pointer is how a mis-click happens. */
body.producer-skin .pd-selbar {
  position: sticky; bottom: 16px; z-index: 30;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 18px auto 0; padding: 10px 12px; width: fit-content; min-width: min(560px, 100%);
  border-radius: 14px; border: 1px solid var(--pd-line-2);
  background: color-mix(in srgb, var(--pd-panel) 92%, transparent);
  backdrop-filter: blur(12px) saturate(1.1);
  box-shadow: 0 1px 2px rgba(0,0,0,.3), 0 16px 40px -12px rgba(0,0,0,.65);
  animation: pd-selbar-in 220ms cubic-bezier(.23, 1, .32, 1);
}
@keyframes pd-selbar-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
body.producer-skin .pd-selbar-n { font: 400 .8125rem/1 var(--sn-sans); color: var(--pd-ink-3); }
body.producer-skin .pd-selbar-n b { font-weight: 600; color: var(--pd-ink); font-variant-numeric: tabular-nums; }
body.producer-skin .pd-selbar-div { width: 1px; height: 18px; background: var(--pd-line-2); }
body.producer-skin .pd-selbar-lnk {
  border: 0; background: none; cursor: pointer; padding: 4px 2px;
  font: 500 .8125rem/1 var(--sn-sans); color: var(--pd-ink-2); border-radius: 6px;
}
body.producer-skin .pd-selbar-lnk:hover { color: var(--pd-ink); text-decoration: underline; text-underline-offset: 3px; }
body.producer-skin .pd-selbar-btn {
  display: inline-flex; align-items: center; gap: 6px; height: var(--pd-h-sm); padding: 0 12px;
  border-radius: var(--pd-r-ctl); border: 1px solid var(--pd-line-2); background: var(--pd-card);
  color: var(--pd-ink-2); font: 500 .8125rem/1 var(--sn-sans); cursor: pointer;
  transition: background-color 150ms var(--sn-ease), color 150ms var(--sn-ease), border-color 150ms var(--sn-ease);
}
body.producer-skin .pd-selbar-btn:hover:not(:disabled) { background: var(--pd-card-2); color: var(--pd-ink); }
body.producer-skin .pd-selbar-btn .sn-ic { width: 13px; height: 13px; }
body.producer-skin .pd-selbar-btn.is-danger { background: var(--pd-red); border-color: transparent; color: #fff; }
body.producer-skin .pd-selbar-btn.is-danger:hover:not(:disabled) { filter: brightness(1.08); }
body.producer-skin .pd-selbar-btn:disabled { opacity: .42; cursor: default; }
body.producer-skin .pd-selbar-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--pd-ring) 50%, transparent); }
/* The way IN is a quiet control in the header — entering a selection is not the loud act, deleting is. */
body.producer-skin .pd-sel-enter { display: inline-flex; align-items: center; gap: 6px; }
@media (prefers-reduced-motion: reduce) { body.producer-skin .pd-selbar { animation: none; } }

/* §86 — THE CHECKBOX READS AS ONE. A rounded tinted square with a tick is what every polished grid
   uses; the §85 version was a dark chip that only differed from the poster behind it when ticked. */
body.producer-skin .pd-film-sel {
  width: 22px; height: 22px; border-radius: 7px;
  border: 1.5px solid rgba(255,255,255,.55); background: rgba(8,10,13,.55);
  backdrop-filter: blur(6px); color: transparent;
  transition: background-color 140ms var(--sn-ease), border-color 140ms var(--sn-ease), transform 140ms var(--sn-ease);
}
body.producer-skin .pd-film:hover .pd-film-sel { border-color: rgba(255,255,255,.85); }
body.producer-skin .pd-film-sel.is-on {
  background: var(--pd-accent); border-color: var(--pd-accent); color: var(--pd-on-accent);
  transform: scale(1.06);
}
/* …and the CARD says so too, so the target is the whole tile rather than a 22px square. */
body.producer-skin .pd-film.is-picked { border-color: var(--pd-accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--pd-accent) 45%, transparent); }
body.producer-skin .pd-film.is-picked .po::after {
  content: ""; position: absolute; inset: 0; background: color-mix(in srgb, var(--pd-accent) 16%, transparent);
}
body.producer-skin .pd-film.is-selmode { cursor: pointer; }

/* =============================================================================
   §86 — THE HOME, COMPOSED. Rob: "the home page layout is crap … reads cheap."
   =============================================================================
   The diagnosis was not taste, it was a regression: §78 moved the templates into accordions and wrote a
   PLAINER card on the way — an image in a box with a bold title and a paragraph under it — while
   `.pd-qs-card` already carried a full poster treatment (cover image, bottom scrim, label laid ON the
   picture, a featured variant spanning two columns). The plain markup inherited the positioning and none
   of the styling, so the grid rendered stark text over half-painted art. The card is now drawn the way
   this sheet always drew it; what follows is the composition around it. */

/* §87 — THE HERO IS THE COMPOSER. Centred, because the reference centres its own and because a single
   composed column reads as one object rather than as a header with a form bolted under it. The measure
   is held at ~62ch so the subtitle cannot wrap into the awkward two-and-a-bit lines it was doing at the
   full 1180px page width. */
body.producer-skin .pd-lib-top { padding: 44px 0 26px; text-align: center; }
body.producer-skin .pd-lib-top h1 { font: 600 2.125rem/1.15 var(--sn-sans); letter-spacing: -.022em; margin: 0 0 8px; color: var(--pd-ink); }
body.producer-skin .pd-lib-top .sub { font: .9375rem/1.55 var(--sn-sans); color: var(--pd-ink-3); margin: 0 auto 22px; max-width: 44ch; }
/* The composer keeps its own internal alignment — a centred TEXTAREA is unreadable — so the panel is
   centred and its contents are not. */
body.producer-skin .pd-lib-comp { max-width: 720px; margin: 0 auto; text-align: left; }
/* …and the template link sits under it as a quiet second thought, which is what it is. */
body.producer-skin .pd-lib-starts {
  display: inline-flex; align-items: center; gap: 4px; margin: 14px auto 0; padding: 6px 10px;
  border: 0; background: none; cursor: pointer; border-radius: var(--pd-r-ctl);
  font: 500 .8125rem/1 var(--sn-sans); color: var(--pd-ink-3);
}
body.producer-skin .pd-lib-starts:hover { color: var(--pd-ink); background: var(--pd-card); }
body.producer-skin .pd-lib-starts .sn-ic { width: 13px; height: 13px; }

/* THE SECTION HEADER, from the reference: a filled icon badge, the title, and the subtitle UNDER it.
   The chevron moves to the far end where it reads as the disclosure control rather than as a bullet. */
body.producer-skin .pd-acc > summary.pd-sec-h { gap: 12px; padding: 12px 4px; }
body.producer-skin .pd-sec-badge {
  flex: none; display: grid; place-items: center; width: 32px; height: 32px;
  border-radius: 9px; background: var(--pd-card-2); color: var(--pd-ink-2);
}
body.producer-skin .pd-sec-badge .sn-ic { width: 16px; height: 16px; }
body.producer-skin .pd-sec-tx { display: flex; flex-direction: column; gap: 2px; min-width: 0; text-align: left; }
body.producer-skin .pd-sec-tx h2 { font: 500 1rem/1.2 var(--sn-sans); color: var(--pd-ink); margin: 0; }
body.producer-skin .pd-sec-tx small { font: 400 .8125rem/1.4 var(--sn-sans); color: var(--pd-ink-4); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.producer-skin .pd-sec-chev { color: var(--pd-ink-4); transition: transform 160ms var(--sn-ease); }
body.producer-skin .pd-sec-chev .sn-ic { width: 16px; height: 16px; }
body.producer-skin .pd-acc[open] > summary .pd-sec-chev { transform: rotate(90deg); }

/* THE MOSAIC. `.pd-qs` is the existing quick-starts grid and `.is-feat` its existing two-up hero tile;
   inside an accordion it only needs its own row rhythm. A uniform grid of six equal tiles is the thing
   that reads as a scaffold — the reference leads each row with one large tile. */
body.producer-skin .pd-acc-qs { padding: 2px 2px 10px; }
/* 💰 The freeness line survived the header rewrite as its own quiet row. A card that starts a film is
   exactly the control a customer expects to cost something, so it says, once, that it does not. */
body.producer-skin .pd-acc-free { margin: 0 2px 14px; font: 400 .75rem/1.5 var(--sn-sans); color: var(--pd-ink-4); }
body.producer-skin .pd-acc .pd-qs-card { aspect-ratio: 16 / 11; }
body.producer-skin .pd-acc .pd-qs-card.is-feat { aspect-ratio: auto; }

/* =====================================================================================================
   §130 · THE RESPONSIVE LAST WORD — structural media rules re-asserted BELOW every block that can
   out-cascade them.
   =====================================================================================================
   🔴 THE BUG THIS SECTION EXISTS TO PIN DOWN. §66 re-declared `.pd-railcol { display: flex }` (and the
   `.pd-topbar` grid) roughly four hundred lines AFTER the responsive section. Same specificity, later
   in source — so below 1180px the "hidden" inspector column came back, and in the ≤960px single-column
   stack it landed ABOVE the stage (it carries no `order`; the stage is order 1), which put 2½ viewports
   of rail between the top bar and the film and pushed the chat to 3½. Rob's "too much scrolling" was
   this rail. A structural media rule that is not the LAST word on its property is a suggestion, not a
   rule — so the last word lives here, at the end of the sheet, where an appended section cannot
   silently outrank it. There is a test that fails if anything unconditional about these selectors is
   ever declared after this line.
   ===================================================================================================== */
@media (max-width: 1180px) {
  body.producer-skin .pd-railcol,
  body.producer-skin .pd-div[data-div="assets"] { display: none; }
}
@media (max-width: 960px) {
  /* the two calm rows the ≤960 section intended — §66's later grid was overriding the column template
     while the areas survived, which is half a layout */
  body.producer-skin .pd-topbar { grid-template-columns: 1fr auto; grid-template-areas: "l r" "c c"; row-gap: 6px; padding: 7px 12px; }
}

/* ---- §130 · BUTTONS THAT FIT THEIR ROW --------------------------------------------------------------
   Rob: "buttons aren't properly fixed and fitted". Measured at 1536×826 (a common laptop, signed in,
   film delivered): the top bar's `1fr auto 1fr` grid hands the right track 431px while its buttons want
   ~500px, so `.pd-util { flex-wrap: wrap }` folded Preview and Export onto a ragged second line — three
   control groups at three baselines and a 79px bar. A toolbar is a ROW. If it cannot fit as one row it
   takes a DESIGNED second row (the same two-row template the phone uses), never a wrapped one. */
body.producer-skin .pd-tb-l, body.producer-skin .pd-tb-r { min-width: 0; }
body.producer-skin .pd-topbar .pd-util { flex-wrap: nowrap; }
/* Mid widths: the two spoken-label utilities drop to their icons. Each keeps its title and — for the
   one that spends — its confirm sheet, which states the figure before anything runs; the sheet, not the
   label, is what guards the money. ~170px of label demand is the difference between one clean row and
   the fold at every width from here up. */
@media (max-width: 1760px) {
  body.producer-skin .pd-util #tl-rescore,
  body.producer-skin .pd-util #pd-assets-tab,
  body.producer-skin .pd-util #pd-preview { font-size: 0; gap: 0; width: var(--pd-h-sm); padding: 0; justify-content: center; }
}
/* Between the desktop row and the phone stack the bar takes its two designed rows: title and actions
   on the first, the view switcher centred on its own. `safe center` so an overflowing switcher scrolls
   from its start instead of clipping it. Measured on the delivered-film cluster: the single row keeps
   ~30px of slack at 1521px and loses it by ~1440 — the breakpoint sits above the cliff, not on it. */
@media (max-width: 1520px) {
  body.producer-skin .pd-topbar { grid-template-columns: 1fr auto; grid-template-areas: "l r" "c c"; row-gap: 6px; padding: 7px 12px; }
  body.producer-skin .pd-tb-l { grid-area: l; }
  body.producer-skin .pd-tb-r { grid-area: r; }
  body.producer-skin .pd-tb-c { grid-area: c; justify-content: safe center; min-width: 0; overflow-x: auto; scrollbar-width: none; }
  body.producer-skin .pd-tb-c::-webkit-scrollbar { display: none; }
  body.producer-skin .pd-tabs { flex: none; }
}

/* ---- §130 · THE PREMIERE BELONGS TO THE SCENE VIEW ------------------------------------------------
   Rob, clarifying: the crowding is INSIDE the workspace. Measured on the delivered demo film at
   1536×826: every stage view opened with the SAME ~390px premiere block (player, scrubber, share row)
   above its own content — the Shot editor's first pixel sat at 552px inside a 576px-tall scroller, so
   the work was below the fold on every tab, and the film existed three times on one screen (the chat's
   film card, the premiere, the filmstrip). One film, one home: the SCENE view keeps the screening at
   full size; the WORK views (Storyboard, Shot, Timeline) fold the premiere to its caption row — the
   "this film is delivered" orientation survives, and ~340px go back to the surface the tab is actually
   for. `.tl-partial` (a shipped-with-a-problem note) stays visible everywhere: honesty is not bulk. */
body.producer-skin .pd-stage-body:has(> .pd-shotview) .tl-premiere > :not(.sec-head, .tl-partial),
body.producer-skin .pd-stage-body:has(> .pd-sbboard) .tl-premiere > :not(.sec-head, .tl-partial),
body.producer-skin .pd-stage-body:has(> .pd-tl) .tl-premiere > :not(.sec-head, .tl-partial) { display: none; }
body.producer-skin .pd-stage-body:has(> .pd-shotview) .tl-premiere,
body.producer-skin .pd-stage-body:has(> .pd-sbboard) .tl-premiere,
body.producer-skin .pd-stage-body:has(> .pd-tl) .tl-premiere { margin: 0 0 12px; padding: 0 0 10px; }

/* ---- §130 · THE GRIP DRAGS, AND THE OTHER ROWS MOVE OVER --------------------------------------------
   Both grips have carried title="Drag to reorder" since they first rendered, with no listener behind
   them — the dead-control class, experienced as "stuffs overlapping and not moving over with drag".
   Now the lifted row follows the pointer and every row it crosses TRANSLATES out of the way, live, in
   transforms; the drop replays the move through the same free server swaps the ↑/↓ buttons use. */
body.producer-skin .pd-grip, body.producer-skin .pd-shot-grip { cursor: grab; touch-action: none; }
body.pd-reordering, body.pd-reordering * { cursor: grabbing !important; }
body.pd-reordering { user-select: none; -webkit-user-select: none; }
body.producer-skin .pd-scene.is-lifting,
body.producer-skin .tl-scene.is-lifting { position: relative; z-index: 40; box-shadow: var(--pd-elev-2); }
/* the lifted row rides OVER its neighbours, so it needs its own opaque ground */
body.producer-skin .pd-scene.is-lifting { background: var(--pd-stage-bg); border-radius: var(--pd-r-ctl); }
/* neighbours ease aside; the lifted row itself tracks the pointer raw — an eased drag feels like lag */
body.pd-reordering .pd-scene:not(.is-lifting),
body.pd-reordering .tl-scene:not(.is-lifting) { transition: transform 170ms var(--sn-ease); }
@media (prefers-reduced-motion: reduce) {
  body.pd-reordering .pd-scene:not(.is-lifting),
  body.pd-reordering .tl-scene:not(.is-lifting) { transition: none; }
}

/* ---- §130 · CRAFT — the details that separate a tool from a template ---------------------------------
   Rob: "it still has an ai feel to it". Each rule below fixes something SEEN in the live screenshots,
   not a vibe: a label wrapping inside its own button, a caption floating with no anchor, grips shouting
   on every row, numerals that jitter as they count. Restraint is the theme — nothing here adds colour,
   boxes or weight; it removes noise and lines things up. */

/* A control's label never wraps inside its own control — "Change the sound" was rendering as two
   stacked lines INSIDE its button at 1920px, which is the single most template-grade pixel we shipped. */
body.producer-skin .pd-ubtn, body.producer-skin .pd-tab, body.producer-skin .pd-btn { white-space: nowrap; }

/* The folded premiere reads as an EYEBROW — a quiet overline anchored by a hairline, not a floating
   sentence. Scoped to the fold (the Scene view's full screening keeps its section voice). */
body.producer-skin .pd-stage-body:has(> .pd-shotview) .tl-premiere,
body.producer-skin .pd-stage-body:has(> .pd-sbboard) .tl-premiere,
body.producer-skin .pd-stage-body:has(> .pd-tl) .tl-premiere { border-bottom: 1px solid var(--pd-line); }
body.producer-skin .pd-stage-body:has(> .pd-shotview) .tl-premiere .sec-head h2,
body.producer-skin .pd-stage-body:has(> .pd-sbboard) .tl-premiere .sec-head h2,
body.producer-skin .pd-stage-body:has(> .pd-tl) .tl-premiere .sec-head h2 {
  font: 600 .6875rem/1 var(--sn-sans); letter-spacing: .08em; text-transform: uppercase; color: var(--pd-ink-3);
}

/* Grips whisper until the row is under the pointer — always audible to touch and to the keyboard. */
@media (hover: hover) and (pointer: fine) {
  body.producer-skin .pd-scene-h .pd-grip { opacity: 0; transition: opacity 120ms var(--sn-ease); }
  body.producer-skin .pd-scene:hover .pd-grip,
  body.producer-skin .pd-scene-h .pd-grip:focus-visible,
  body.pd-reordering .pd-scene-h .pd-grip { opacity: 1; }
}

/* Every numeral in the chrome is tabular, so nothing shivers as it counts. */
body.producer-skin .tl-progress, body.producer-skin .pd-tb-l .count,
body.producer-skin .pd-tl-bar, body.producer-skin .pd-shotview-h b { font-variant-numeric: tabular-nums; }

/* The shot editor's header speaks in the same voice as the scene rows — one label system, one room. */
body.producer-skin .pd-shotview-h b { font: 600 .6875rem/1 var(--sn-sans); letter-spacing: .07em; text-transform: uppercase; color: var(--pd-ink); }

/* The transport is a boundary, so it stands on its own ground — nothing half-scrolled ever shows
   through the row that says where the film is. */
body.producer-skin .pd-transport { background: var(--pd-stage-bg); }

/* ---- §131 · THE PANEL'S CORNER SHOWS ITS CURVE --------------------------------------------------------
   Rob, with a zoom of the top-left corner: "the corner with the curve doesn't even show". Measured on
   the shipped PNG (pixel grid at the corner): one flat colour. Two causes stacked: the Producer's own
   top bar paints `--pd-panel`, which in light is the SAME #F5F4EE as the shell chrome around the stage
   panel — and the panel's defining 1px inset ring (`box-shadow: inset` on `#stage`) paints UNDER
   opaque children, so the Producer's full-bleed chrome buried it. A curve with no line and no contrast
   is a square, whatever the clip says.
   The Producer now takes the panel's shape as its own: the root clips to the shell's radius (its
   existing overflow does the rounding), and the ring is re-drawn ABOVE the content, so every corner of
   the workspace draws the same 1px curve every other tool's panel shows. ≥861px only — below that the
   shell reverts to page scroll and there is no rounded panel to honour. */
@media (min-width: 861px) {
  body.producer-skin .pd-root { position: relative; border-radius: 18px; }
  body.producer-skin .pd-root::after {
    content: ""; position: absolute; inset: 0; border-radius: 18px; pointer-events: none; z-index: 60;
    /* --pd-line, not the shell's softened hairline: over the near-black canvas the softened mix lands
       2–3 RGB points from its ground (measured) — a curve nobody can see. The Producer's own line
       token is 10% white in dark and 9% black in light, which is exactly one legible hairline. */
    box-shadow: inset 0 0 0 1px var(--pd-line);
  }
}
