/* No framework. The product is a video — the interface around it stays out of
   the way and never competes with the thing playing.

   Two layouts, one stylesheet: sign-in and sign-up are a single 720px column,
   and the app itself is a wider two-column workbench — everything you type on
   the left, everything you get back on the right. `--page` is the switch, set
   once on <body> so the header, main and footer can never disagree about where
   the page edge is. */

:root {
  --bg: #0e0f13;
  --panel: #16181f;
  --line: #262a35;
  --ink: #e8eaf0;
  --dim: #9aa1b1;
  --accent: #7c3aed;        /* the same violet as the recorded cursor overlay */
  --accent-ink: #fff;
  --danger: #f87171;
  --ok: #10b981;            /* only ever a tint — "ready", nothing louder */
  --radius: 16px;
  --page: 720px;
  --stub: #0b0c10;          /* the mock browser's page area */
  --stub-line: #23262f;
  /* The glass: panels are translucent over the page's glow and blur what is
     behind them, so all three need theme-specific alphas — a white glass on a
     dark page is fog, not glass. */
  --panel-glass: rgba(22, 24, 31, 0.72);
  --glass-line: rgba(255, 255, 255, 0.08);
  --panel-shadow: 0 10px 32px rgba(0, 0, 0, 0.3);
}

body.wide { --page: 1120px; }

@media (prefers-color-scheme: light) {
  :root {
    --bg: #f7f7f9; --panel: #fff; --line: #e3e5ea;
    --ink: #14161c; --dim: #5c6473;
    --stub: #eef0f4; --stub-line: #dcdfe6;
    --panel-glass: rgba(255, 255, 255, 0.7);
    --glass-line: rgba(20, 22, 28, 0.08);
    --panel-shadow: 0 10px 32px rgba(20, 22, 28, 0.07);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.55 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* Four soft glows over the flat base, one per corner — the brand violet where
   the eye starts, a cooler blue answering it across the top, then red and green
   weighting the bottom edge. Fixed pixel sizes rather than percentages so a
   wide page stretches the calm part of the canvas, never the glow; color-mix
   over `transparent` keeps the same rules honest in both themes.

   The three non-brand hues are literals, not `--danger`/`--ok`. Those tokens
   mean "this failed" and "this is ready"; spending them on wallpaper would make
   every future change to a status colour repaint the page behind it.

   They live on a `position: fixed` layer rather than on <body> so scrolling
   leaves them where they are. `background-attachment: fixed` says the same
   thing in one line, but iOS Safari has never honoured it on the body — the
   glows would scroll away there and nowhere else. z-index: -1 puts the layer
   behind every content box while still painting over body's flat --bg;
   pointer-events: none keeps it out of hit-testing. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(1100px 560px at 16% -6%,
      color-mix(in srgb, var(--accent) 20%, transparent), transparent 68%),
    radial-gradient(900px 520px at 94% 8%,
      color-mix(in srgb, #3b82f6 12%, transparent), transparent 62%),
    radial-gradient(900px 520px at 92% 104%,
      color-mix(in srgb, #ef4444 12%, transparent), transparent 62%),
    radial-gradient(950px 540px at 6% 96%,
      color-mix(in srgb, #22c55e 12%, transparent), transparent 64%);
  background-repeat: no-repeat;
}

main { max-width: var(--page); margin: 0 auto; padding: 0 20px 36px; }

/* Header ------------------------------------------------------------------ */
.bar {
  display: flex; align-items: center; justify-content: space-between;
  max-width: var(--page); margin: 0 auto; padding: 22px 20px;
}
.wordmark {
  font-weight: 650; letter-spacing: -0.02em; text-decoration: none; color: var(--ink);
  display: inline-flex; align-items: center; gap: 9px;
}
.wordmark .logo { display: block; }
.bar nav { display: flex; align-items: center; gap: 18px; }
.bar a { color: var(--dim); text-decoration: none; font-size: 14px; }
.bar a:hover { color: var(--accent); }
.inline { display: inline; }
.linkish {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--dim); font: inherit; font-size: 14px;
}
.linkish:hover { color: var(--accent); }
/* Nav items are icon + label, so they lay out as a row rather than relying on
   the SVG's inline baseline — which sits a glyph low against its text and is
   the reason icon-in-a-link usually looks slightly dropped. */
.bar nav a, .bar nav .linkish { display: inline-flex; align-items: center; gap: 6px; }
/* Sized in em so it tracks the 14px label instead of being pinned to a pixel
   size that stops matching the day the label changes.

   Nudged down, because `align-items: center` centres the glyph on the text's
   *line box* — and the bottom of a line box is empty descender space, so a
   mathematically centred icon reads high beside a word like "Sign in". Measured
   rather than eyeballed: at 0 the icon sits 0.47px above the cap band and
   1.72px above the x-height band; 0.08em puts it between the two (+0.64 / -0.60),
   which is the balance point. In em so the correction scales with the label. */
.nav-icon { width: 1.15em; height: 1.15em; flex: none; position: relative; top: 0.08em; }

/* Hero -------------------------------------------------------------------- */
.hero { padding: 16px 0 28px; }
.hero h1 {
  margin: 0 0 12px; font-size: clamp(28px, 5vw, 40px);
  line-height: 1.12; letter-spacing: -0.03em; font-weight: 660;
  /* Ink running into the brand violet. `color` stays as the fallback: a
     browser that ignores background-clip paints normal ink instead of an
     invisible headline. */
  color: var(--ink);
  background: linear-gradient(92deg, var(--ink) 30%, var(--accent) 92%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
/* Full width, same edges as the cards below — capping it narrower left the
   hero ragged against a column of things that all agree where the page ends. */
.lede { margin: 0; color: var(--dim); font-size: 17px; }

/* Links in body copy. Without this they render in the browser's default blue,
   which is the one colour on the page that belongs to no palette. Scoped to
   <main> so the header's own muted link styling is left alone. */
main a { color: var(--accent); text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent); }
main a:hover { color: var(--ink); }

/* Workbench --------------------------------------------------------------- */
/* Form left, video right. The form column is deliberately narrow: every field in
   it is short, and a 700px-wide text input just makes the eye travel. The width
   it gives up goes to the video, which is the thing being sold.

   `align-items: start` matters — without it the stage card stretches to the
   form's height and the placeholder floats in a sea of panel. */
.workbench {
  display: grid;
  grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
  align-items: start;
  gap: 20px;
}
.form-column { min-width: 0; }

/* The form's header row: its title, and the one link that abandons the current
   video. The link lives up here, away from the submit buttons, precisely so it
   cannot be mistaken for one of them. */
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.card-head h2 { margin: 0 0 14px; }
.brief-summary {
  margin: 0 0 12px; font-size: 13px; color: var(--dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The conversation so far: the brief, then every correction, oldest first. All
   of it still applies — that is why it stays on screen instead of collapsing. */
.said { margin: 0 0 18px; padding-left: 18px; font-size: 13px; color: var(--dim); }
.said li { margin-bottom: 5px; }
.said li:last-child { color: var(--ink); }
/* One column before the video gets too small to be worth showing large. Below
   this the form comes first, which is also the order you use them in.
   `minmax(0, 1fr)` for the same reason the two-column rule above uses it: a bare
   `1fr` track floors at its content's min-content width, so one wide child —
   a long address in the renders list — silently widens the whole page. */
@media (max-width: 900px) { .workbench { grid-template-columns: minmax(0, 1fr); } }

/* Panels ------------------------------------------------------------------ */
.panel {
  background: var(--panel-glass); border: 1px solid var(--glass-line);
  border-radius: var(--radius); padding: 24px; margin-bottom: 20px;
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  backdrop-filter: blur(16px) saturate(140%);
  box-shadow: var(--panel-shadow);
}
.panel h2 { margin: 0 0 14px; font-size: 19px; letter-spacing: -0.01em; }

/* Form -------------------------------------------------------------------- */
.field { display: block; margin-bottom: 20px; }
.label { display: block; font-size: 14px; font-weight: 550; margin-bottom: 7px; }
.hint { display: block; margin-top: 7px; font-size: 13px; color: var(--dim); }

/* A label with something beside it. The 7px moves from the label to the row, so
   the row is exactly as tall as the label text and a bare label and a row leave
   the field below them in the same place — and so the panel below can be placed
   to meet the row's bottom edge exactly (see .help-pop). */
.label-row { display: flex; align-items: baseline; gap: 7px; margin-bottom: 7px; }
.label-row .label { margin-bottom: 0; }

/* The ? that opens the description field's instructions -------------------- */
/* Sized in em so it tracks the 14px label rather than being pinned to a pixel
   size that stops matching the day the label changes — the same reasoning as
   .nav-icon. A circle rather than a bare glyph because at this size the
   question mark alone doesn't read as something you can press. */
.help-icon {
  flex: none; width: 1.35em; height: 1.35em; padding: 0;
  border: 1px solid var(--line); border-radius: 50%;
  background: none; color: var(--dim);
  font: inherit; font-size: 12px; line-height: 1; font-weight: 600;
  cursor: pointer; align-self: center;
}
.help-icon:hover, .help-icon[aria-expanded="true"] {
  color: var(--accent); border-color: var(--accent);
}
.help-icon:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The panel it opens. Absolute, so opening it never moves the field underneath
   the cursor — the reason a pushed-down layout is worse than an overlay here is
   that the textarea you are about to type in would walk away as you read.

   z-index because it overlaps the textarea below it, which is a later sibling
   and would otherwise paint on top. Its own background rather than the panel
   glass: this sits *over* a panel, and a translucent layer on a translucent
   layer is a smear in both themes. */
.has-help { position: relative; }
.help-pop {
  /* 22px is the label row's own height, so the panel *meets* it rather than
     floating below: the pointer travels from the ? into the panel without
     crossing a gap, which is what makes hover-to-open usable at all. */
  position: absolute; z-index: 5; top: 22px; left: 0; right: 0;
  max-height: min(60vh, 420px); overflow-y: auto;
  padding: 14px 16px; border-radius: 12px;
  /* Its own solid background, not the panel glass: this sits *over* a panel,
     and a translucent layer on a translucent layer is a smear in both themes. */
  background: var(--panel); border: 1px solid var(--line);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.28);
  font-size: 13px; color: var(--dim);
}
.help-pop .hint { margin-top: 0; }
.help-pop .help-title { margin: 12px 0 6px; color: var(--ink); font-weight: 550; }
.help-pop ul { margin: 0; padding-left: 18px; }
.help-pop li { margin-bottom: 6px; }
.help-pop li strong, .help-pop .hint strong { color: var(--ink); font-weight: 600; }
.help-pop > .hint:last-child { margin-top: 12px; }
/* Two ways in, one panel — hover for a pointer, the ? for a tap, and a phone is
   never shown less than a laptop. The hover rules override the `hidden`
   attribute (higher specificity than the UA's `[hidden] { display: none }`)
   rather than replacing it, so app.js keeps owning the open/closed state and
   the button's aria-expanded stays true to what a click did.

   Scoped to the row and the panel, never the whole field: `.has-help:hover`
   would pop the panel open over the textarea the moment the pointer crossed it
   on the way to typing. */
.label-row:hover + .help-pop[hidden],
.help-pop[hidden]:hover { display: block; }

input[type=text], input[type=url], input[type=email], input[type=password],
textarea, select {
  width: 100%; padding: 11px 13px;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: 9px;
  font: inherit; font-size: 15px;
  /* One height for every control, stated rather than left to each one's own
     text metrics. The arithmetic is the inputs': one line of 1.55 leading, plus
     11px padding and 1px border, twice over. */
  min-height: calc(1.55em + 24px);
}
/* A <select> is the one control the platform insists on painting itself, and it
   paints it to its own taste: WebKit sizes a native menulist from the control and
   ignores min-height and padding outright (in Safari the field came out 23px in a
   column of 47px inputs), and it rounds the corners to the platform's radius
   rather than ours, which put a visibly tighter box beside every input on the
   form. `height` fixes the first; nothing an author can write fixes the second
   while the control is native.
   So it isn't: appearance:none hands the box back to us. That was rejected once,
   for the height problem, on the grounds that it means drawing the arrow
   ourselves — which turned out to be sixty bytes of SVG, against a field that
   doesn't match its neighbours on every visit. The popup itself is still the
   platform's; only the closed control is ours.
   The arrow's colour is baked into the data URI because a background image is
   its own document and cannot see currentColor. One mid grey rather than a pair
   of theme-specific copies: it sits between --dim's two values and reads on both
   backgrounds at 11px. */
select {
  height: calc(1.55em + 24px);
  appearance: none; -webkit-appearance: none;
  padding-right: 34px;
  background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'><path d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23868d9c' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 11px 7px;
}
textarea { resize: vertical; }
/* Placeholders read as suggestions, not as entered values: at full --dim they
   sat one shade from real input and a filled-looking form that isn't filled is
   a form people re-read. Opacity rather than a fixed colour so one rule holds
   in both themes (and Firefox, which dims placeholders again by default). */
input::placeholder, textarea::placeholder { color: var(--dim); opacity: 0.55; }
input:focus, textarea:focus, select:focus {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent;
}

/* auto-fit rather than a viewport media query: this grid now lives inside a
   400px column, so what matters is the space the *container* has, not the
   window. It drops to one column when two fields would be cramped. */
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); gap: 14px; }

/* The voice control: one choice, two buttons, field-sized. The inputs are real
   radios (keyboard and form semantics for free), drawn as a segmented row. */
.segmented { display: flex; }
.segmented label { flex: 1; cursor: pointer; }
.segmented input {
  position: absolute; width: 1px; height: 1px;
  opacity: 0; pointer-events: none;
}
.segmented span {
  display: block; text-align: center; padding: 11px 13px;
  background: var(--bg); border: 1px solid var(--line);
  color: var(--dim); font-size: 15px;
}
.segmented label:first-child span { border-radius: 9px 0 0 9px; }
.segmented label + label span { border-left: 0; border-radius: 0 9px 9px 0; }
.segmented input:checked + span {
  color: var(--ink); border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--bg));
}
/* Restore the left edge the unchecked neighbour gave up, or the highlight
   stops dead on a missing border. */
.segmented label + label input:checked + span { border-left: 1px solid var(--accent); }
.segmented input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }

details.field > summary {
  cursor: pointer; font-size: 14px; font-weight: 550;
  padding: 10px 0; color: var(--dim);
}
details.field[open] > summary { color: var(--ink); margin-bottom: 10px; }
.warn {
  font-size: 13px; color: var(--dim); border-left: 2px solid var(--accent);
  padding-left: 12px; margin: 14px 0 0;
}

/* Buttons ----------------------------------------------------------------- */
.actions { display: flex; gap: 12px; flex-wrap: wrap; }
/* The demo form's pair split its row edge to edge, so the buttons end where
   the fields above them do — at their natural widths the row stopped short of
   the card's right margin and read as misaligned. Scoped to this form: the
   auth pages put a single button in .actions, and a lone full-width primary
   is a different (unasked-for) look. */
#render-form .actions button { flex: 1; }
button.primary, button.secondary {
  padding: 12px 20px; border-radius: 12px; font: inherit; font-weight: 550;
  cursor: pointer; border: 1px solid transparent;
}
button.primary { background: var(--accent); color: var(--accent-ink); }
button.primary:hover { filter: brightness(1.1); }
button.secondary { background: none; color: var(--ink); border-color: var(--line); }
button.secondary:hover:not(:disabled) { border-color: var(--accent); }
button:disabled { opacity: 0.45; cursor: not-allowed; }

/* Script editor ----------------------------------------------------------- */
/* A native <dialog>, so focus trapping, Escape and the backdrop are the
   browser's rather than three hundred lines of ours. Dressed as a panel because
   that is what every other box on the page is — `.panel`'s own rules can't be
   borrowed wholesale here (a dialog needs its own centring and max sizes, and
   the glass would be a smear over a dimmed page), so the shared parts are
   restated and the differences are the point. */
.sheet {
  width: min(680px, calc(100vw - 32px));
  max-height: min(80vh, 780px);
  padding: 24px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); color: var(--ink);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);
  overflow: hidden;
}
/* The layout goes on [open], NOT on .sheet — a closed <dialog> is hidden by a
   *user-agent* rule (`dialog:not([open]) { display: none }`), and any author
   `display` wins over the UA origin no matter how specific the UA selector is.
   A bare `.sheet { display: flex }` therefore un-hides the closed dialog: it
   renders as a panel dumped at the bottom of the page, in every engine.
   The dialog is the scroll container so the action row can be pinned below a
   long list of steps instead of walking off the bottom of the screen. */
.sheet[open] { display: flex; flex-direction: column; }
.sheet::backdrop { background: rgba(6, 7, 10, 0.62); }
.sheet-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.sheet-head h2 { margin: 0 0 6px; font-size: 19px; letter-spacing: -0.01em; }
.sheet > .hint { flex: none; }
/* The dialog convention every desktop has agreed on: the thing that commits sits
   at the far right where the eye finishes, and the way out sits at the far left,
   away from it. Right-aligning the whole row put Cancel next to the button that
   spends a render, which is exactly the pair you don't want adjacent.
   `margin-right: auto` on the way out rather than space-between, because there
   may be two commit buttons and those two belong together. */
.sheet .actions { flex: none; margin-top: 16px; justify-content: flex-end; }
.sheet .actions > [data-close-script] { margin-right: auto; }
/* Only the step list scrolls — the heading above and the buttons below stay
   put, which is the difference between a long script being browsable and being
   a page you have to scroll past to find the button. */
/* `1 1 auto`, and the `auto` is the whole rule. `flex: 1` means `flex: 1 1 0%`,
   and a percentage basis resolved against a <dialog>'s indefinite (fit-content)
   height is zero in Safari — so the step list contributed nothing to the height
   the dialog sized itself to, and then had no free space to grow back into. It
   rendered as a 0px box: every textarea present in the DOM, none of them visible,
   the buttons sitting directly under the hint. Chromium and WebKit-via-Playwright
   both size it from content and show nothing wrong, which is why this survived
   being checked in two engines.
   `auto` makes the content count toward the dialog's height; min-height: 0 is
   what still lets it shrink and scroll once the dialog hits its max-height.

   Padded by exactly the focus ring — 2px of outline plus its 1px offset. A
   scroll container clips at its padding box (and `overflow-y: auto` makes the
   other axis auto too, so it clips both ways), which cut the ring off the top
   and the right of the first box, the one <dialog> hands focus to on open.
   scroll-padding keeps that room when Tab scrolls a later row into view. */
#script-lines {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; margin: 14px 0 0;
  padding: 3px; scroll-padding: 3px;
}
.script-line { display: flex; gap: 10px; align-items: flex-start; margin-bottom: 10px; }
/* A fixed slot, so the boxes line up down the list rather than each row
   arranging itself around the width of its own number — the same reasoning as
   the render row's fixed action slots. */
.script-num {
  flex: none; width: 22px; padding-top: 12px;
  font-size: 12px; color: var(--dim); text-align: right;
}
.script-line textarea { min-height: 0; }

/* The form card's header actions: edit the script, or abandon it. */
.card-actions { display: flex; align-items: center; gap: 8px; flex: none; }

/* Buttons: tier note ------------------------------------------------------ */
.tier-note { margin: 14px 0 0; }
.tier-note strong { color: var(--ink); font-weight: 600; }
.tier-note + .tier-note { margin-top: 6px; }

/* Result ------------------------------------------------------------------ */
video {
  width: 100%; border-radius: 10px; background: #000;
  border: 1px solid var(--line); display: block; margin: 6px 0 12px;
}

/* Empty stage -------------------------------------------------------------- */
/* Before the first render there is nothing to show, and an empty card reads as
   a broken page. So the stage draws what it is about to do instead: a browser
   window at the address being typed, with a play button over it. It is a
   picture, not a control — the render starts from the form, and a play button
   that does nothing when clicked is worse than no play button, so it is
   presented as decoration and the caption below carries the meaning. */
.stub {
  border: 1px solid var(--stub-line); border-radius: 10px;
  overflow: hidden; background: var(--stub);
}
.stub-chrome {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 12px; border-bottom: 1px solid var(--stub-line);
}
.dot { width: 9px; height: 9px; border-radius: 50%; background: var(--stub-line); flex: none; }
.dot:first-child { margin-left: 2px; }
.stub-addr {
  flex: 1; margin-left: 8px; padding: 4px 10px;
  background: var(--bg); border-radius: 99px;
  font-size: 12px; color: var(--dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.stub-screen {
  aspect-ratio: 16 / 9; display: grid; place-items: center;
  background: radial-gradient(120% 90% at 50% 0%, rgba(124, 58, 237, 0.16), transparent 70%);
}
.stub-play {
  width: 76px; height: 76px; border-radius: 50%;
  background: var(--accent); display: grid; place-items: center;
  animation: pulse 2.6s ease-out infinite;
}
/* A CSS triangle, nudged right so it reads as centred: a triangle's optical
   centre sits left of its bounding box. */
.stub-play::after {
  content: ""; margin-left: 6px;
  border-left: 24px solid var(--accent-ink);
  border-top: 15px solid transparent;
  border-bottom: 15px solid transparent;
}
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(124, 58, 237, 0.45); }
  70%  { box-shadow: 0 0 0 24px rgba(124, 58, 237, 0); }
  100% { box-shadow: 0 0 0 0 rgba(124, 58, 237, 0); }
}
@media (prefers-reduced-motion: reduce) { .stub-play { animation: none; } }

.stub-caption { margin: 14px 0 0; color: var(--dim); font-size: 14px; }
.stub-caption strong { color: var(--ink); font-weight: 600; }
.status {
  display: flex; align-items: center; gap: 10px;
  color: var(--dim); margin: 6px 0 14px; min-width: 0;
}
.status .status-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.spinner {
  width: 14px; height: 14px; border-radius: 50%; flex: none;
  border: 2px solid var(--line); border-top-color: var(--accent);
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* Respect a reduced-motion preference: the spinner is decorative, and the text
   beside it already says what is happening. */
@media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }

.bar-track { height: 5px; background: var(--line); border-radius: 99px; overflow: hidden; }
.bar-fill { height: 100%; background: var(--accent); transition: width 0.4s ease; }

.error { color: var(--danger); font-size: 14px; }
.notes { margin-top: 10px; font-size: 13px; color: var(--dim); }
.notes ul, .notes ol { margin: 8px 0 0; padding-left: 18px; }
.notes summary { cursor: pointer; }

.renders { list-style: none; margin: 0; padding: 0; }
.renders li {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 0; border-bottom: 1px solid var(--line);
}
.renders li:last-child { border-bottom: 0; }
.renders a { text-decoration: none; }
/* :not(.tag), so these two rules can never reach a pill again — the download
   pill was silently inheriting its colour from here instead of from .tag,
   which is why no amount of styling the delete button made them match. */
.renders a:not(.tag) { color: var(--ink); }
.renders a:not(.tag):hover { color: var(--accent); }
/* Only the title takes the slack. `flex: 1` used to be on every anchor in the
   row, which stretched the download pill into a long empty box.

   `overflow-wrap` because a title is often a bare URL, which contains nothing a
   browser will break a line at. `min-width: 0` lets the flex *item* shrink but
   does nothing about the text inside it, so without this a long address ran out
   of its slot and took the whole page sideways with it. `anywhere` rather than
   `break-word`: only `anywhere` also shrinks the item's min-content width,
   which is the half that stops the row from forcing the page wide. */
.render-title { flex: 1; min-width: 0; font-size: 18px; overflow-wrap: anywhere; }
.render-title time { display: block; font-size: 12px; color: var(--dim); }
/* Worn by <span>, <a> and <button>, which is why every text metric is stated
   here rather than inherited: the three elements have different browser
   defaults (buttons don't inherit font-family, and their line-height is
   `normal` where the others inherit 1.55), and any metric left unstated makes
   one pill a different size from its neighbours. It has happened twice. */
.tag {
  font-family: inherit; font-size: 11px; line-height: 1.55; font-weight: 400;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--dim); border: 1px solid var(--line);
  padding: 6px 11px; border-radius: 99px; white-space: nowrap;
  /* Stated here, not left to .renders' anchor rules: a .tag can be worn by an
     anchor anywhere (the form's New video button), and the UA underline is
     part of what makes a link not look like a button. */
  text-decoration: none;
}
/* Actionable pills (links and buttons) in ink; passive labels stay dim. Both get
   the hover, too: written for `a.tag` alone, the button pills were the only
   controls on the page with no hover state at all — Edit script sat next to New
   video, identical until the pointer reached it. The destructive pair overrides
   this further down (same specificity, later wins) with the warning colour. */
a.tag, button.tag { color: var(--ink); }
a.tag:hover, button.tag:hover { color: var(--accent); border-color: var(--accent); }
/* Kind and status are information, not controls, and they dress accordingly:
   stacked into one slot and set smaller, so the row reads as title — caption —
   actions instead of as a bar of four look-alike buttons. */
.tags { display: flex; flex-direction: column; gap: 4px; flex: none; }
/* Tinted fills rather than the actions' outlined look — the second thing,
   after size, separating labels from buttons. The tint is the information:
   violet is the paid tier, green is ready, red is failed or unpaid; everything
   in between (queued, rendering, a preview) stays neutral. The border goes
   transparent, not away, so the padding boxes stay identical to the actions'. */
.tags .tag {
  font-size: 10px; line-height: 1.5; padding: 2px 8px; min-width: 84px;
  text-align: center; border-color: transparent;
  background: color-mix(in srgb, var(--dim) 12%, transparent);
}
.tag-kind.is-final {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: color-mix(in srgb, var(--accent) 55%, var(--ink));
}
.tag-status.is-done {
  background: color-mix(in srgb, var(--ok) 16%, transparent);
  color: color-mix(in srgb, var(--ok) 45%, var(--ink));
}
.tag-status.is-failed, .tag-status.is-awaiting_payment {
  background: color-mix(in srgb, var(--danger) 14%, transparent);
  color: color-mix(in srgb, var(--danger) 55%, var(--ink));
}
/* Cancelled stays on the neutral fill every in-between status uses. It is not a
   failure and not a success — nothing went wrong and nothing was made — and
   tinting it red would put a warning colour on the one outcome the customer
   chose deliberately. */

/* A button that reads as a link: the cancel offered inside the progress panel,
   which belongs in the sentence about how long this takes rather than as a
   second button competing with the form's. Its own name rather than reusing the
   nav's .linkish — that one is a nav item and gets the accent on hover, this one
   stops something and gets the warning colour. Sharing the class made the header
   change when this was added, which is how the pair got separated. */
.linkish-stop {
  background: none; border: 0; padding: 0; font: inherit; color: var(--dim);
  text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.linkish-stop:hover { color: var(--danger); }
.linkish-stop:disabled { cursor: default; text-decoration: none; }
/* A fixed slot, so the columns line up down the list rather than each row
   arranging itself around the width of its own words. The download slot keeps
   its width even when the row has nothing to download. */
.tag-download { min-width: 96px; text-align: center; flex: none; }
/* The button's own contributions only — everything typographic lives in .tag.
   The same fixed slot as the download pill: the label changes in place ("sure?"
   when a delete is armed, "stopping" once a cancel is sent), and without a
   stated width the whole row re-flowed around the swap.

   On `button.tag` rather than on one named button. Scoped to .tag-delete, this
   was invisible inheritance: the next button added to a row came out wearing
   the UA's own chrome — a filled grey pill — at its own width, which pushed the
   status pills out of line with the row above. Anything wearing .tag is one of
   these pills, so the reset belongs to the class they share. */
button.tag { appearance: none; background: none; cursor: pointer; margin: 0;
             min-width: 96px; text-align: center; flex: none; }
/* Both row actions are destructive-ish — one stops a render, the other throws
   one away — so both warn on hover. Named rather than applied to every
   button.tag, so a future row action that isn't destructive doesn't inherit it. */
button.tag-delete:hover, button.tag-delete[data-armed="1"],
button.tag-cancel:hover {
  color: var(--danger); border-color: var(--danger);
}

/* Footer ------------------------------------------------------------------ */
footer {
  max-width: var(--page); margin: 0 auto; padding: 18px 20px 34px;
  display: flex; justify-content: space-between;
  color: var(--dim); font-size: 13px; border-top: 1px solid var(--line);
  /* Two sentences that together are wider than a small phone. Wrapping puts the
     second on its own line instead of pushing the page out. */
  flex-wrap: wrap; gap: 4px 18px;
}

/* Quieter than the copyright it sits beside — it is diagnostic, not something
   the reader is meant to take in. Tabular figures so it doesn't jiggle the line
   when a digit changes. */
.version {
  font-size: 12px; opacity: 0.7; margin-left: 4px;
  font-variant-numeric: tabular-nums;
}

/* Phone ------------------------------------------------------------------- */
/* One breakpoint, below the narrowest layout the workbench already has (it goes
   single-column at 900px). Everything here is the same page folded, not a
   second design — nothing is hidden and nothing is resized away. */
@media (max-width: 600px) {
  /* The frame costs more than it earns at this width: 20px of page margin plus
     24px of panel padding spends 88px of a 375px screen on nothing. The header
     and footer are centred on the same edge and have to move with it. */
  main { padding: 0 14px 28px; }
  .bar, footer { padding-left: 14px; padding-right: 14px; }
  .panel { padding: 16px; }

  /* The render row is a title and three fixed-width slots — the pills, the
     download, the action — and the slots are fixed precisely so the columns
     line up down the list. Their total (306px with the gaps) is wider than a
     phone, so the title was being squeezed to nothing and the row still
     overflowed, which is what scrolled the page sideways.

     So the row folds rather than shrinks: the title takes a line of its own and
     the same three slots sit in a strip beneath it, at the same widths. The
     columns still line up down the list; the row is simply two lines tall. */
  .renders li { flex-wrap: wrap; }
  .render-title { flex: 1 0 100%; font-size: 16px; }
  /* Actions to the right edge of the strip, pills to the left — the same
     left-to-right order as the wide row, so nothing moves except the fold. */
  .tag-download { margin-left: auto; }
}
