/* OCHA QuickVid — LAYOUT ONLY.
   All tokens + components come from the OCHA App Kit (vendor/ocha-app-kit.css,
   synced from the design system). Do NOT add colors or component styling here —
   change the kit instead (see /CLAUDE.md). This file is only QuickVid-specific
   layout that no other app needs. Colours below are kit *tokens* (consumed, not defined).

   SPACING: use the kit's 4pt scale — var(--sp-4/8/12/16/24/32/48). Never type a
   raw rem for margin/padding/gap. The only exceptions in here are sub-4px optical
   nudges on labels and pills, and the timefield/durfield paddings, which are
   overlay geometry (room for the spinner and the "sec" unit), not rhythm. */

/* ---- Vertical rhythm: the main "do it" buttons + status were cramped in the flow ---- */
#run, #st-render {
  display: flex; width: 100%;
  margin: var(--sp-24) 0 var(--sp-8);
  padding-block: var(--sp-12);
  font-size: 1rem;
}
.status-slot { margin-top: var(--sp-16); }               /* the alert under the action button needs air */
.status-slot .cd-alert { margin-block: 0.15rem; }
/* result-card export buttons shouldn't hug the video/thumbnail above them */
#preview .cd-button--export, #st-card-out .cd-button--export { margin-top: var(--sp-8); }

/* ---- Mode chip (browser vs engine-connected) ---- */
.mode-chip { display: inline-flex; align-items: center; gap: var(--sp-8); font-size: 0.78rem; font-weight: 700; padding: var(--sp-4) var(--sp-12); border-radius: var(--radius-pill); }
.mode-chip--browser { background: var(--bg); color: var(--muted); border: 1px solid var(--line); }
.mode-chip--full { background: color-mix(in srgb, var(--ocha-cyan) 14%, var(--card)); color: var(--ocha-blue-dark); border: 1px solid var(--ocha-cyan); }
.mode-chip--full .fa-bolt { color: var(--ocha-cyan); }

/* ---- File drop zone (browser file picker) ---- */
.dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-8); padding: var(--sp-24) var(--sp-16); cursor: pointer; text-align: center;
  border: 2px dashed var(--input-border); border-radius: var(--radius); color: var(--muted);
  background: color-mix(in srgb, var(--ocha-cyan) 4%, var(--card));
}
.dropzone:hover, .dropzone.drag { border-color: var(--ocha-cyan); color: var(--ocha-blue-dark); }
.dropzone.has-file { border-style: solid; border-color: var(--ocha-cyan); color: var(--ink); }
.dropzone .fa-regular { font-size: 1.5rem; color: var(--ocha-cyan); }

/* ---- Titles & branding : lower-third rows ----
   One person per block: the three text fields stack a line each (they were
   cramped side-by-side), then a single Start | Duration | Alignment line.
   Each control carries its own caption, so there's no shared column header. */
/* Each lower third is an enclosed card, so it's obvious which fields belong
   together once there are several people. */
#lt-rows, #st-lt-rows { counter-reset: lt; display: grid; gap: var(--sp-12); }
.lt-row {
  display: flex; gap: var(--sp-8); align-items: center; flex-wrap: wrap;
  counter-increment: lt;
  border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--ocha-cyan) 3%, var(--card));
  padding: var(--sp-12) var(--sp-12);
}
/* auto-numbered header — a CSS counter, so removing a row renumbers by itself */
.lt-row::before {
  content: "Lower third " counter(lt);
  flex: 1 1 100%;
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.5px; color: var(--ocha-blue-dark); margin-bottom: 0.1rem;
}
.lt-name, .lt-org, .lt-org2 { flex: 1 1 100%; min-width: 0; }   /* one field per line */
/* flex-start so the CAPTIONS share a baseline (a select is ~2px shorter than the
   composite timefields, so bottom-aligning would stagger the labels instead) */
.lt-meta { flex: 1 1 100%; display: flex; gap: var(--sp-8); align-items: flex-start; flex-wrap: wrap; }
.lt-cell { display: block; }                        /* block, so the caption sits above its control */
.lt-cap {
  display: block; font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.5px; color: var(--muted); margin-bottom: 0.15rem;
}
.lt-cell--start { width: 5.75rem; }                 /* mm:ss + spinner */
.lt-cell--dur   { width: 6rem; }                    /* secs + unit + spinner */
.lt-cell--align { width: 6.5rem; }
.lt-cell > .timefield, .lt-cell > select { width: 100%; }
/* Remove: labelled (not a bare ×) and reads as destructive on hover */
.lt-remove { flex: none; width: auto; align-self: flex-end; }
.lt-meta .lt-remove { margin-inline-start: auto; }  /* trailing, away from the controls */
.lt-remove:hover, .lt-remove:focus-visible {
  color: var(--err); border-color: var(--err);
  background: color-mix(in srgb, var(--err) 8%, var(--card));
}
#lt-rows, #st-lt-rows { margin-top: var(--sp-4); }
/* sibling spacing is the container's `gap` — see #lt-rows above */

/* ---- Start-time selector (mm:ss with ▲▼ nudge) ---- */
.timefield { position: relative; display: block; }
.timefield__input { text-align: center; padding: 0.55rem 1.15rem 0.55rem 0.35rem; }
.durfield__input  { text-align: center; padding: 0.55rem 2.6rem 0.55rem 0.35rem; }  /* room for "sec" + spinner */
.durfield__unit   { position: absolute; right: 1.35rem; top: 0; bottom: 0; display: flex; align-items: center; font-size: 0.78rem; color: var(--muted); pointer-events: none; }
.timefield__spin {
  position: absolute; top: 1px; right: 1px; bottom: 1px; width: 1.1rem;
  display: flex; flex-direction: column;
}
.timefield__spin button {
  flex: 1; padding: 0; border: none; background: transparent; cursor: pointer;
  color: var(--muted); font-size: 0.5rem; line-height: 1; display: grid; place-items: center;
}
.timefield__spin button:hover { color: var(--ocha-blue-dark); }

/* ---- Example thumbnail: what a lower third looks like (portrait phone preview) ---- */
.ex-thumb { margin: 1.5rem 0 0; }                  /* breathing room below title; flush-left (drop default <figure> indent) */
.ex-thumb img {
  display: block; height: auto; max-height: 280px; width: auto; max-width: 100%;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.ex-thumb figcaption { font-size: 0.78rem; color: var(--muted); margin-top: var(--sp-8); }
/* One preview PER ELEMENT, each at the frame that element appears on. auto-fit so
   two lower thirds sit side by side and five text blocks wrap, with no breakpoint. */
.bp-multi {
  display: grid; gap: var(--sp-12); margin-top: var(--sp-8);
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.bp-multi[hidden] { display: none; }
.bp-item { margin: 0; min-width: 0; }
.bp-item img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.bp-item__cap {
  margin-top: var(--sp-4); font-size: 0.75rem; color: var(--muted);
  overflow-wrap: anywhere;
}

/* live-preview strip under an example thumbnail (browser/brandpreview.js) */
.bp-bar { display: flex; align-items: center; gap: var(--sp-8); flex-wrap: wrap; margin-top: var(--sp-8); }
.bp-note:empty { display: none; }

/* subsection divider — Subtitles / Ending stacked in the one Edit branding card
   (the Titles tab splits these into separate cards, so it doesn't need this) */
.st-subsection { border-top: 1px solid var(--line); margin-top: var(--sp-32); padding-top: var(--sp-24); }

/* ---- Statement clip wizard (Edit tab) — layout only, kit tokens throughout ---- */
.st-source-row { display: flex; gap: var(--sp-8); flex-wrap: wrap; }
.st-source-row .cd-form__input { flex: 1 1 16rem; min-width: 0; }
.st-chip-row { display: flex; gap: var(--sp-8); flex-wrap: wrap; align-items: center; }
.st-btn-row { display: flex; gap: var(--sp-8); flex-wrap: wrap; }
/* "Try another moment" row under the sync preview */
.st-sync-toolbar { display: flex; align-items: center; gap: var(--sp-8); flex-wrap: wrap; }
.st-sync-toolbar .app-hint { margin: 0; }
/* +4f = the usual broadcast fix: draw the eye to it while it is NOT the selected chip.
   Soft cyan ring on the outline (unselected) state; none once picked (it fills cyan). */
.st-sync-usual.cd-button--outline { box-shadow: 0 0 0 2px color-mix(in srgb, var(--ocha-cyan) 45%, transparent); }
/* the little "usual fix" pill — white so it reads on both the white chip and the cyan selected chip */
.st-sync-tag {
  display: inline-block; margin-inline-start: 0.35rem; padding: 0 var(--sp-4); border-radius: var(--radius-pill);
  font-size: 0.6rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.4px;
  background: #fff; color: var(--ocha-blue-dark); border: 1px solid var(--ocha-cyan); vertical-align: middle;
}
.st-optional { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 0.05rem var(--sp-8); margin-inline-start: 0.4rem; vertical-align: middle; }
.field-row { display: flex; gap: var(--sp-12); flex-wrap: wrap; align-items: end; }
.field-row label { display: flex; flex-direction: column; gap: var(--sp-4); font-size: 0.8rem; font-weight: 700; color: var(--muted); }
.st-time { width: 6rem; }
/* "Find the words" window: compact mm:ss steppers (reuses the .timefield component) */
.st-timefield { display: block; width: 6.25rem; }
/* step 1: job-folder picker (layout only) */
/* A GRID with gap, not four rows each carrying its own margin-top: the name field,
   the picker, the path hint and "Open a saved project" were a loose stack with
   uneven spacing between them. */
.st-folder { display: grid; gap: var(--sp-8); margin-top: var(--sp-12); }
.st-folder-row { display: flex; gap: var(--sp-8); align-items: center; flex-wrap: wrap; }
.st-folder-row .cd-button { flex: none; }
.st-folder-row .app-hint { margin: 0; flex: 1 1 18rem; }

/* "required" tag next to the label — quiet until it matters */
.req {
  font-style: normal; font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.5px; color: var(--muted); margin-inline-start: 0.35rem;
}

/* Missing-field state: the job folder is required, so pressing the CTA without one
   turns the whole block red rather than only printing a message somewhere else on
   the page — the error and the thing to fix have to be visible together. */
.field-err {
  margin: var(--sp-8) 0 0; display: flex; align-items: flex-start; gap: var(--sp-8);
  font-size: 0.85rem; font-weight: 600; color: var(--err);
}
.st-folder.is-missing .cd-form__input {
  border-color: var(--err);
  background: color-mix(in srgb, var(--err) 5%, var(--card));
}
.st-folder.is-missing .cd-form__input:focus { outline-color: var(--err); }
.st-folder.is-missing .cd-button--outline {
  color: var(--err); border-color: var(--err);
}
.st-folder.is-missing .req { color: var(--err); }
.st-proj-name { flex: 1 1 16rem; min-width: 0; }
/* "Open a saved project" — a secondary entry point under the new-project fields */
/* margin-top drops to sp-8 — the parent grid's gap now supplies the rest, and the
   two used to add up to an uneven gutter above the divider. */
.st-open-row { display: flex; gap: var(--sp-8); align-items: center; flex-wrap: wrap; margin-top: var(--sp-8); padding-top: var(--sp-16); border-top: 1px solid var(--line); }
.st-open-row .cd-button { flex: none; }
.st-open-row .app-hint { margin: 0; flex: 1 1 16rem; }
/* Use-AI modal paste box */
.st-ai-paste { width: 100%; font-family: var(--mono-font, ui-monospace, monospace); font-size: 0.8rem; resize: vertical; }
/* AI modal: the two modes. Grid gap, never stacked margins. */
#st-ai-match, .st-ai-brief { display: grid; gap: var(--sp-8); }
.st-ai-brief { margin-top: var(--sp-16); }
#st-ai-match[hidden], #st-ai-choose[hidden] { display: none; }   /* grid would beat [hidden] */
/* Locked-Edit card: installer / starter blocks */
.st-setup-block { margin-top: var(--sp-16); padding-top: var(--sp-4); }
.st-setup-block + .st-setup-block { border-top: 1px solid var(--line); margin-top: var(--sp-24); padding-top: var(--sp-16); }
.st-setup-block .app-hint { margin-top: var(--sp-8); }
/* step 4: stacked from/to range rows */
#st-ranges { display: flex; flex-direction: column; gap: var(--sp-8); }
.st-range .st-range-del { align-self: end; }
/* step 7: keep the title fields a normal size — cap the width, and stop the kit's
   `.field-row .cd-form__input{flex:1 1 18rem}` from growing inputs VERTICALLY (18rem
   ≈ 288px tall!) once they're flex items inside our column labels. */
#st-card-brand .field-row label { flex: 1 1 11rem; }
.field-row label .cd-form__input { flex: 0 0 auto; }
/* ---- Engine setup steps (E0 card, browser mode) ---- */
.st-setup { margin: 0; padding-inline-start: 1.4rem; display: flex; flex-direction: column; gap: var(--sp-16); }
.st-setup > li { padding-inline-start: 0.3rem; }
.st-setup > li::marker { font-weight: 800; color: var(--ocha-blue-dark); }
.st-setup .st-btn-row { margin-top: var(--sp-8); }
.st-setup__note { margin-top: var(--sp-8); font-size: 0.85rem; color: var(--muted); line-height: 1.5; }
.st-setup__note .fa-regular { color: var(--ocha-cyan); margin-inline-end: 0.4rem; }

/* copy-paste command box (Mac install one-liner) */
.cmd-box { display: flex; align-items: center; gap: var(--sp-8); margin-top: var(--sp-8);
  background: var(--ink); border-radius: var(--radius-sm); padding: var(--sp-8) var(--sp-8) var(--sp-8) var(--sp-12); }
.cmd-box code { flex: 1 1 auto; min-width: 0; overflow-x: auto; white-space: nowrap;
  font-family: var(--mono, ui-monospace, "SF Mono", Menlo, monospace); font-size: 0.82rem;
  color: #fff; -webkit-overflow-scrolling: touch; }
.cmd-box__copy { flex: 0 0 auto; }
/* numbered install steps */
.setup-steps { margin: var(--sp-8) 0 0; padding-inline-start: 1.4rem; display: flex; flex-direction: column; gap: var(--sp-8);
  font-size: 0.9rem; color: var(--ink); line-height: 1.5; }
.setup-steps li { padding-inline-start: 0.2rem; }
.setup-steps li::marker { color: var(--ocha-cyan); font-weight: 700; }
kbd { font-family: var(--mono, ui-monospace, "SF Mono", Menlo, monospace); font-size: 0.8em;
  background: var(--bg); border: 1px solid var(--line); border-bottom-width: 2px;
  border-radius: var(--radius-sm); padding: 0.05rem var(--sp-4); }

/* autosave indicator + resume banner */
.st-autosave { font-size: 0.75rem; color: var(--muted); display: flex; align-items: center; gap: var(--sp-4); margin: 0 0 var(--sp-8); }
.st-autosave .fa-regular { color: var(--ocha-cyan); }
#st-resume .st-btn-row { margin-top: var(--sp-8); }
.st-total { font-size: 0.9rem; }
.st-seg-list { display: flex; flex-direction: column; gap: var(--sp-4); max-height: 24rem; overflow-y: auto; }
.st-seg { display: flex; gap: var(--sp-8); align-items: center; padding: var(--sp-4) var(--sp-8); border: 1px solid var(--line); border-radius: var(--radius-sm); }
.st-seg.is-sel { border-color: var(--ocha-cyan); background: color-mix(in srgb, var(--ocha-cyan) 5%, var(--card)); }
.st-seg__dur { flex: none; font-size: 0.72rem; color: var(--muted); width: 2.9rem; text-align: right; }
.st-seg__text { flex: 1 1 auto; min-width: 0; border: none; background: transparent; font: inherit; color: var(--ink); }
.st-seg__text:focus { outline: 1px solid var(--ocha-cyan); border-radius: 3px; }
.st-seg__shot { display: flex; gap: 2px; flex: none; }
.st-shot { width: 1.7rem; height: 1.7rem; border: 1px solid var(--input-border); background: var(--card); border-radius: 4px; cursor: pointer; font-size: 0.72rem; font-weight: 700; color: var(--muted); }
.st-shot.is-on { background: var(--ocha-cyan); border-color: var(--ocha-cyan); color: #fff; }
/* Grid with a FIXED column, not flex sized by the picture: a 16:9 Event still is
   3x the width of a 9:16 reel still, and the zoom slider (flex:1) grew with it. */
.st-frames { display: grid; grid-template-columns: repeat(auto-fit, 240px); gap: var(--sp-16); align-items: start; justify-content: start; }
.st-frames figure { margin: 0; min-width: 0; }
.st-frame-box {
  position: relative; height: 240px; display: grid; place-items: center; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--bg);
}
.st-frames img { display: block; max-height: 100%; max-width: 100%; width: auto; height: auto; }
.st-frames figcaption { font-size: 0.75rem; color: var(--muted); margin-top: var(--sp-4); }
/* each frame is its own editor: drag the picture, zoom below it */
.st-frame-drag { cursor: grab; touch-action: none; user-select: none; -webkit-user-drag: none; }
.st-frame-drag.is-dragging { cursor: grabbing; outline: 2px solid var(--ocha-cyan); outline-offset: 1px; }
.st-zoom-row { display: flex; align-items: center; gap: var(--sp-8); margin-top: var(--sp-4); }
.st-zoom-row .fa-regular { font-size: 0.7rem; color: var(--muted); }
.st-zoom-row input[type="range"] { flex: 1 1 auto; min-width: 0; accent-color: var(--ocha-cyan); }
.st-zoom-val { font-size: 0.72rem; color: var(--muted); font-variant-numeric: tabular-nums; min-width: 2.5em; text-align: right; }
/* Drag affordance: shown for a few seconds, then it gets out of the way. The old
   hint under the slider appeared and vanished with the crop lock state, which is
   what made the row jump — deleted, not restyled. */
.st-drag-tip {
  position: absolute; top: var(--sp-8); left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: var(--sp-4); white-space: nowrap;
  padding: var(--sp-4) var(--sp-8); border-radius: 999px; pointer-events: none;
  font-size: 0.7rem; color: #fff; background: rgba(0, 0, 0, 0.62);
  opacity: 0; transition: opacity 0.4s ease;
}
.st-drag-tip.is-on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .st-drag-tip { transition: none; } }
.st-check { display: flex; gap: var(--sp-8); align-items: center; font-size: 0.9rem; }
.st-thumbs { display: flex; gap: var(--sp-8); flex-wrap: wrap; }
.st-thumb { height: 150px; width: auto; border: 2px solid var(--line); border-radius: var(--radius-sm); cursor: pointer; }
.st-thumb.is-on { border-color: var(--ocha-cyan); }
.end-opt--soon { opacity: 0.45; pointer-events: none; }
.end-opt__icon .fa-regular { font-size: 1.6rem; color: var(--ocha-cyan); }

/* ---- Ending picker : selectable option cards with example thumbnails ---- */
.end-options { display: flex; flex-wrap: wrap; gap: var(--sp-12); }
.end-opt {
  position: relative; flex: 1 1 12rem; display: flex; flex-direction: column; gap: var(--sp-4);
  padding: var(--sp-8); cursor: pointer;
  background: var(--card); border: 1px solid var(--input-border); border-radius: var(--radius-sm);
}
.end-opt input { position: absolute; opacity: 0; width: 0; height: 0; }
.end-opt img { display: block; width: 100%; height: auto; border-radius: 4px; }  /* height:auto — the width/height ATTRS reserve space; without this they'd stretch */
.end-opt__label { font-weight: 700; font-size: 0.9rem; }
.end-opt__hint  { font-size: 0.78rem; color: var(--muted); line-height: 1.35; }
.end-opt--none  { justify-content: flex-start; }
/* "No ending" has no footage — show the Film humanitarian icon in a matching 16:9 slot */
.end-opt__icon {
  aspect-ratio: 16 / 9; display: grid; place-items: center; border-radius: 4px;
  background: color-mix(in srgb, var(--ocha-cyan) 10%, var(--card));
}
.end-opt__icon img { width: 34%; height: auto; }
/* Icon-only card rows (AI modal): the 16:9 slot exists to stand in for a 360x203
   still. Where there is no artwork to preview it is just a big empty box. */
.end-options--compact .end-opt__icon { aspect-ratio: auto; padding: var(--sp-12) 0; }
/* Format picker: 4-up when there is room, 2x2 when there isn't — and NEVER 3, which
   strands the fourth card on its own line (the original flex-wrap bug, and what
   `auto-fit` also produces at some widths: measured 3 columns at 1280px). Explicit
   counts are the only way to guarantee "4 or 2+2". */
.fmt-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-12); }
@media (min-width: 700px) { .fmt-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.fmt-shape { display: block; color: var(--ocha-cyan); line-height: 1; margin-bottom: var(--sp-4); }
.fmt-shape .fa-regular { font-size: 1.5rem; }
/* No 4:5 glyph exists in the library — squash the 9:16 one rather than draw a new
   icon (icons are never invented; see CLAUDE.md). */
.fmt-shape--45 .fa-regular { display: inline-block; transform: scaleX(1.28); transform-origin: left center; }
/* selected + keyboard-focus states use the kit's cyan token */
.end-opt:has(input:checked) { border-color: var(--ocha-cyan); box-shadow: inset 0 0 0 1px var(--ocha-cyan); }
.end-opt:focus-within { outline: 2px solid var(--ocha-cyan); outline-offset: 2px; }

/* ---- ending tail control (Edit step 7) ---- */
.st-tail-input { width: 5rem; flex: 0 0 auto; }
#st-tail-row { align-items: center; gap: var(--sp-8); }

/* ---- Location strips (pin locator) — ONE component, both tabs (location.js) ----
   Same enclosed-card treatment as the lower thirds, so a video with two locations
   reads as two cards rather than one long row of fields. */
#t-loc-rows, #st-loc-rows { counter-reset: loc; display: grid; gap: var(--sp-12); margin-top: var(--sp-4); }
.loc-row {
  display: flex; gap: var(--sp-8); align-items: center; flex-wrap: wrap;
  counter-increment: loc;
  border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--ocha-cyan) 3%, var(--card));
  padding: var(--sp-12) var(--sp-12);
}
/* auto-numbered header — a CSS counter, so removing a card renumbers by itself */
.loc-row::before {
  content: "Location " counter(loc);
  flex: 1 1 100%;
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.5px; color: var(--ocha-blue-dark); margin-bottom: 0.1rem;
}
/* spacing: #t-loc-rows `gap` */
#t-loc-rows:not(:empty) + #t-loc-add, #st-loc-rows:not(:empty) + #st-loc-add { margin-top: var(--sp-8); }

/* Text-on-screen cards — the same card model as the location strip (add, fill,
   remove, auto-numbered), so the two read as one pattern. The meta row reuses
   .loc-meta / .loc-cell wholesale; only the card shell and the line inputs are
   text-specific. */
#t-tx-rows, #st-tx-rows { counter-reset: tx; display: grid; gap: var(--sp-12); margin-top: var(--sp-4); }
.tx-row {
  display: flex; gap: var(--sp-8); align-items: center; flex-wrap: wrap;
  counter-increment: tx;
  border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--ocha-cyan) 3%, var(--card));
  padding: var(--sp-12) var(--sp-12);
}
.tx-row::before {
  content: "Text " counter(tx);
  flex: 1 1 100%;
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.5px; color: var(--ocha-blue-dark); margin-bottom: 0.1rem;
}
/* spacing: #t-tx-rows `gap` */
#t-tx-rows:not(:empty) + #t-tx-add, #st-tx-rows:not(:empty) + #st-tx-add { margin-top: var(--sp-8); }
/* the three lines stack: they ARE stacked on screen, so the field order shows it */
.tx-fields { flex: 1 1 100%; display: flex; flex-direction: column; gap: var(--sp-4); }
.tx-fields .cd-form__input { font-weight: 400; width: 100%; }
.tx-remove { flex: none; width: auto; align-self: flex-end; margin-inline-start: auto; }
.tx-remove:hover, .tx-remove:focus-visible {
  color: var(--err); border-color: var(--err);
  background: color-mix(in srgb, var(--err) 8%, var(--card));
}

.loc-fields { flex: 1 1 100%; display: flex; gap: var(--sp-8); flex-wrap: wrap; }
/* id-scoped twin overrides #st-card-brand's `flex: 1 1 11rem` on labels in the Edit tab */
.loc-fields label, #st-card-brand .loc-fields label {
  display: flex; flex-direction: column; gap: var(--sp-4);
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--muted); flex: 1 1 14rem;
}
.loc-fields label em { font-weight: 400; text-transform: none; letter-spacing: 0; font-style: normal; opacity: 0.75; }
.loc-fields .cd-form__input { font-weight: 400; }

/* flex-start so the CAPTIONS share a baseline (matches .lt-meta) */
.loc-meta { flex: 1 1 100%; display: flex; gap: var(--sp-8); align-items: flex-start; flex-wrap: wrap; }
.loc-cell { display: block; }                       /* block, so the caption sits above its control */
.loc-cell--start  { width: 5.75rem; }               /* mm:ss + spinner (matches .lt-cell--start) */
.loc-cell--dur    { width: 6rem; }                  /* secs + unit + spinner */
.loc-cell--colour { width: auto; }
.loc-cell--grad   { width: 10.5rem; }               /* "80% — default" without truncating */
.loc-cell--grad select { width: 100%; }
.loc-cell > .timefield { width: 100%; }
.loc-cell .cd-form__input { width: 100%; font-weight: 400; }
.loc-colour { display: inline-flex; gap: var(--sp-4); }
/* the icon toggle stays a single [box] label row in BOTH tabs — the id-scoped twin
   beats #st-card-brand's flex:1 1 11rem, which would otherwise stack it */
.loc-meta .loc-icon-check, #st-card-brand .loc-meta .loc-icon-check {
  display: flex; flex-direction: row; align-items: center; gap: var(--sp-8);
  flex: 0 0 auto; align-self: flex-end; padding-bottom: var(--sp-4);
}
/* Remove: labelled (not a bare ×) and reads as destructive on hover — as .lt-remove */
.loc-remove { flex: none; width: auto; align-self: flex-end; margin-inline-start: auto; }
.loc-remove:hover, .loc-remove:focus-visible {
  color: var(--err); border-color: var(--err);
  background: color-mix(in srgb, var(--err) 8%, var(--card));
}

/* ---------- Beta banner (full-width strip below the header) ----------
   Slim "still in beta, tell us" strip. Colours derive from the app-kit --warn
   token via color-mix so it stays on-brand without hardcoding hex (no kit
   component covers a slim full-width strip). Remove when QuickVid exits beta. */
.beta-banner {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 7px 16px; font-size: 13px; font-weight: 500; line-height: 1.35; text-align: center;
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 9%, var(--card));
  border-bottom: 1px solid color-mix(in srgb, var(--warn) 22%, var(--card));
}
.beta-banner .fa-circle-info { flex-shrink: 0; }
.beta-banner a { color: var(--warn); font-weight: 600; text-decoration: underline; }
.beta-banner a:hover { text-decoration: none; }

/* ---- caption editor (shared component, both tabs) — layout only ---- */
.cap-review { margin-top: 12px; }
.cap-review .app-hint { display: block; margin-top: 6px; }
.cap-rows { display: grid; gap: 6px; margin-top: 10px; max-height: 340px; overflow-y: auto; padding-right: 4px; }
.cap-row { display: grid; grid-template-columns: 46px 1fr; gap: 8px; align-items: center; }
.cap-time { font-size: 12px; font-variant-numeric: tabular-nums; color: var(--ink-tertiary, #6e6e6e); text-align: right; }

/* ---- footage Look picker (shared component, both tabs) — layout only ---- */
.look-review { margin-top: 12px; }
/* Option-card grid — the house pattern for "here are N choices, each needing a
   line of explanation". Used by Extra settings (both tabs) and the Help modal's
   Windows downloads. A GRID, not stacked rows: hints crammed inline beside a
   control wrap into a tangle. auto-fit gives side-by-side when there's room and
   one per row when there isn't, so there's no breakpoint to maintain. */
.opt-grid {
  display: grid; gap: var(--sp-8); margin-top: var(--sp-12);
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}
/* NO `display` here on purpose — gate classes like .look-review ride on this
   element, and any display value would beat the `hidden` attribute. */
.opt-card {
  border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  background: var(--card); padding: var(--sp-12);
}
.opt-card .st-check { font-weight: 600; }
.opt-card__title { font-weight: 600; font-size: 0.9rem; margin: 0; }
.opt-card__hint { margin: var(--sp-4) 0 0; font-size: 0.8rem; line-height: 1.45; color: var(--muted); }
.opt-card .st-check + .opt-card__hint { margin-left: var(--sp-24); }    /* clear the checkbox */
.opt-card .cd-button { margin-top: var(--sp-8); }
.extra-review .look-review { margin-top: 0; }
/* 200px, not 120: a 9:16 still at 120px told you nothing. The stills are cached
   server-side and browser-cached, so larger costs no extra render. */
.look-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--sp-12); margin: var(--sp-12) 0; }
.look-card {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 8px 6px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--card); cursor: pointer; font: inherit; text-align: center;
}
.look-card img { width: 100%; border-radius: 4px; display: block; }
.look-card img[hidden] { display: none; }
.look-card strong { font-size: 0.9rem; }
.look-card span { font-size: 11px; color: var(--ink-tertiary, #6e6e6e); }
.look-card.is-active { border-color: var(--ocha-cyan); box-shadow: 0 0 0 1px var(--ocha-cyan); }
/* Magnifier: a SIBLING of the card, not a child — the card is a <button> and nesting
   buttons is invalid markup and breaks tab order. */
/* Waiting line under the progress bar on the long steps. Quiet by design — it must
   never compete with the step's own status text. */
.app-waiting { margin: var(--sp-8) 0 0; font-style: italic; }
.app-waiting[hidden] { display: none; }

.look-card-wrap { position: relative; display: grid; }
.look-card-wrap .look-card { width: 100%; height: 100%; }
.look-zoom {
  position: absolute; top: var(--sp-12); right: var(--sp-12);
  display: grid; place-items: center; width: 1.9rem; height: 1.9rem;
  border: 0; border-radius: 999px; cursor: pointer;
  color: #fff; background: rgba(0, 0, 0, 0.55); font: inherit; line-height: 1;
}
.look-zoom:hover { background: rgba(0, 0, 0, 0.78); }
.look-zoom:focus-visible { outline: 2px solid var(--ocha-cyan); outline-offset: 2px; }
.look-zoom[hidden] { display: none; }
.look-zoom .fa-regular { font-size: 0.8rem; }
.look-zoom-modal__panel { max-width: min(92vw, 1100px); display: grid; gap: var(--sp-8); }
.look-zoom-modal__panel img { display: block; width: 100%; height: auto; border-radius: var(--radius-sm); }
.look-zoom-modal__cap { margin: 0; font-weight: 700; }
.look-review .st-check { margin-top: 4px; }

/* ---- Toolbox tab (utilities) — layout only ---- */
.tool-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; }
.tool-tile {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 18px 12px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); cursor: pointer; font: inherit; text-align: center;
}
.tool-tile i { font-size: 26px; color: var(--ocha-cyan); }
.tool-tile strong { font-size: 14px; }
.tool-tile span { font-size: 12px; color: var(--ink-tertiary, #6e6e6e); }
.tool-tile:hover { border-color: var(--ocha-cyan); }
.tb-levels { display: grid; gap: 8px; margin: 12px 0; }
.tb-level {
  display: flex; flex-direction: column; gap: 3px; text-align: left;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--card); cursor: pointer; font: inherit;
}
.tb-level.is-active { border-color: var(--ocha-cyan); box-shadow: 0 0 0 1px var(--ocha-cyan); }
.tb-level strong { font-size: 14px; }
.tb-level span { font-size: 12px; color: var(--ink-tertiary, #6e6e6e); }
.tb-reco {
  font-style: normal; font-size: 10px; font-weight: 700; letter-spacing: .4px;
  color: var(--ocha-cyan); text-transform: uppercase; margin-left: 6px;
}
#tb-preview video { width: 100%; margin: 10px 0; }

/* Help & reinstall — the install one-liner as a copyable command block */
#help-cmd {
  display: block; width: 100%; box-sizing: border-box;
  font-family: var(--mono-font); font-size: 0.8rem; line-height: 1.5;
  background: #161616; color: #f2f2f2; border-radius: var(--radius-sm);
  padding: var(--sp-12) var(--sp-12); user-select: all;
  /* WRAPS, never scrolls sideways: `white-space: pre` cut the tail off the URL
     (703px of text in a 659px box) and there is no reason to hide half a command
     the user is meant to read before pasting. break-all because it is one long
     unbroken token. */
  white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all;
}
/* Help modal — a labelled step, so the OS switch and each block read as sections
   rather than a stack of loose rows. */
.help-step { margin-top: var(--sp-16); }
.help-step__label {
  display: block; font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.5px; color: var(--muted); margin-bottom: var(--sp-8);
}
.help-copy-row { display: flex; gap: var(--sp-8); align-items: center; flex-wrap: wrap; margin-top: var(--sp-8); }

/* ---- Caption editor: show the caption in the SHAPE the video will use ----
   The field was one line while the render wraps to two, so you could not see
   whether a caption would split or merge until after rendering. The preview
   below each field wraps on the engine's own two-line budget
   (/api/statement/caption-shape), so what you group is what you get. */
.cap-edit { flex: 1 1 auto; min-width: 0; display: grid; gap: var(--sp-4); }
.cap-input { resize: vertical; min-height: 3.2rem; line-height: 1.35; }
.cap-preview {
  display: grid; gap: 2px; justify-items: center;
  padding: var(--sp-8); border-radius: var(--radius-sm);
  background: #111; color: #fff;
  font-family: "Raleway", system-ui, sans-serif; font-weight: 500;
  font-size: 0.82rem; line-height: 1.25; text-align: center;
}
.cap-preview span { display: block; min-height: 1em; }
.cap-preview--box span:not(:empty) {
  background: rgba(0, 0, 0, 0.72); padding: 1px 6px; border-radius: 3px;
}
.cap-note { margin: 0; font-size: 0.72rem; color: var(--muted); min-height: 1em; }
.cap-note.is-over { color: var(--err); font-weight: 600; }
.cap-note.is-soft { color: var(--warn, var(--muted)); }

/* ---- Basic colour adjustments (shared look.js) ----
   Four corrective sliders under the Look row. A grid so the label, both end
   words, the track and the reset line up in columns across all four rows. */
.adjust-grid { display: grid; gap: var(--sp-8); margin-top: var(--sp-12); }
.adjust-row {
  display: grid; align-items: center; gap: var(--sp-8);
  grid-template-columns: 5.5rem auto minmax(0, 1fr) auto 1.75rem;
}
.adjust-row__label { font-size: 0.8rem; font-weight: 600; }
.adjust-row__end { font-size: 0.7rem; color: var(--muted); }
/* min-width:0 is load-bearing: a grid item's default min-width is `auto`, i.e. its
   CONTENT size, so a range input's intrinsic width pushed the row 2px past the
   container at 375px. `1fr` alone does not stop that — measured. */
.adjust-row__slider { width: 100%; min-width: 0; accent-color: var(--ocha-cyan); }
.adjust-row__reset {
  width: 1.75rem; height: 1.5rem; padding: 0; cursor: pointer;
  font-size: 0.7rem; color: var(--muted);
  background: var(--card); border: 1px solid var(--input-border);
  border-radius: var(--radius-sm); visibility: hidden;
}
.adjust-row.is-set .adjust-row__reset { visibility: visible; }   /* only when it does something */
.adjust-row.is-set .adjust-row__label { color: var(--ocha-blue-dark); }
@media (max-width: 480px) {
  .adjust-row { grid-template-columns: 1fr auto; grid-template-areas: "label reset" "slider slider"; }
  .adjust-row__label { grid-area: label; }
  .adjust-row__reset { grid-area: reset; }
  .adjust-row__slider { grid-area: slider; }
  .adjust-row__end { display: none; }
}
