/* =========================================================================
   Video Studio — styles
   ========================================================================= */
:root {
  --st-ok: #16a34a;
  --st-run: #f59e0b;
  --st-pend: #94a3b8;
  --st-err: #dc2626;
  --st-accent: #6366f1;
}

.studio-shell {
  display: flex;
  flex-direction: column;
  gap: 18px;
  /* Hard rule: the studio never exceeds the viewport width. `min-width: 0`
     stops a grid/flex child's automatic minimum from blowing the shell out,
     and the inherited `overflow-wrap: anywhere` both breaks long unbreakable
     strings (project names, filenames, URLs) and — unlike `break-word` —
     counts toward intrinsic sizing, so it also prevents the blowout rather
     than only hiding it. */
  min-width: 0;
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* ---- Flowchart ---- */
.st-flowchart {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  background: var(--card, #fff);
  border: 1px solid var(--border, #e2e8f0);
  border-radius: 14px;
  padding: 16px 18px;
}
/* Workflow flowchart only (has a progress bar on top + a row of steps). Used
   by the project editor; the project-list ".st-flowchart.st-toolbar" must stay
   a horizontal row, so we scope the column layout to the workflow variant. */
.st-flowchart.st-flow {
  flex-direction: column;
  align-items: stretch;
}
.st-steprow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  flex-wrap: wrap;
  width: 100%;
}
/* % progress bar sits ABOVE the flowchart box (outside), exactly like Photo Studio. */
.st-fc-progress.st-fc-outside {
  width: 100%;
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 12px;
  padding: 2px 6px 12px;
}
.st-fc-pct {
  font-size: 12px;
  font-weight: 700;
  color: #334155;
  white-space: nowrap;
}
.st-fc-track {
  width: 100%;
  height: 6px;
  background: #f1f5f9;
  border-radius: 4px;
  overflow: hidden;
  margin: 0;
}
.st-fc-fill {
  height: 100%;
  background: #f97316;              /* video studio orange */
  border-radius: 4px;
  transition: width .3s ease;
  width: 0;
}
/* Workflow steps — shaped EXACTLY like Photo Studio's numbered pills but with
   an ORANGE active ("running") step. Completed steps stay green like Photo. */
.st-step {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 13px; border-radius: 20px; cursor: pointer;
  font-size: 13px; font-weight: 600; color: var(--text-3, #64748b);
  border: none; background: transparent; transition: all .2s;
  flex-wrap: nowrap; white-space: nowrap; min-width: 0;
}
.st-step:hover { background: var(--bg-2, #f1f5f9); }
.st-step.st-disabled { cursor: not-allowed; opacity: .55; }
.st-flowchart.st-flow .st-step.st-run { background: #f97316; color: #fff; }
.st-flowchart.st-flow .st-step.st-ok { background: rgba(34, 197, 94, .12); color: #22c55e; }
.st-step .st-step-num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--bg-2, #f1f5f9);
  font-size: 11px; font-weight: 800; color: var(--text-2, #334155);
  flex: none;
}
.st-flowchart.st-flow .st-step.st-run .st-step-num { background: rgba(255, 255, 255, .28); color: #fff; }
.st-flowchart.st-flow .st-step.st-ok .st-step-num { background: #22c55e; color: #fff; }
.st-flowchart.st-flow .st-step.st-run:hover { background: #ea580c; }
.st-step .st-step-label {
  display: inline-flex; align-items: center; gap: 5px;
}
.st-step .st-step-label img { width: 18px; height: 18px; object-fit: contain; flex: none; }
.st-arrow { color: var(--border-2, #cbd5e1); font-size: 15px; flex: none; }


@keyframes st-pulse { 0%,100%{opacity:1} 50%{opacity:.55} }
/* Work-step content: use the full workspace width (consistent across steps). */
.st-step-wide { width: 100%; }
/* Pinned live preview at the top of a step: sticks to the top of the scroll
   container so the video stays visible while the controls below scroll. */
.st-video-top {
  position: sticky; top: 0; z-index: 6;
  margin: 0 0 14px;
}

/* ---- Mobile preview handling (Video Studio) ----
   The old "shrink-on-scroll" dock proved unreliable across real devices, so on
   phones the preview now scrolls away normally while you edit (no giant pinned
   wall, no shrink/flash). A slim fixed "▲ Preview" pill appears whenever the
   active step's preview has scrolled out of view and jumps you back up to it. */
.st-dockcap,
.st-vidtop.st-min .st-dockcap { display: none !important; }

@media (max-width: 560px) {
  /* Do not pin the preview — let it scroll away with the step content. */
  .st-vidtop { position: static !important; }
}


/* ---- Metadata fetching spinner ---- */
.st-spinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 2px solid rgba(99,102,241,.25);
  border-top-color: var(--st-accent, #6366f1);
  border-radius: 50%;
  animation: st-spin .7s linear infinite;
}
@keyframes st-spin { to { transform: rotate(360deg); } }

/* ---- Steps panel ---- */
.st-panel {
  display: none;
  background: var(--card, #fff);
  border: 1px solid var(--border, #e2e8f0);
  border-radius: 14px;
  padding: 20px;
}
.st-panel.st-active { display: block; }
.st-panel h3.st-title {
  margin: 0 0 14px; font-size: 18px; font-weight: 800;
  color: var(--text, #0f172a);
}
.st-panel .st-desc { color: var(--text-3, #64748b); font-size: 13px; margin: -6px 0 14px; }

/* ---- Controls ---- */
.st-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.st-row > * { min-width: 0; }
.st-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr)); gap: 14px; }
.st-grid > * { min-width: 0; }
.st-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; max-width: 100%; box-sizing: border-box; }
.st-field label { font-size: 12px; font-weight: 600; color: var(--text-2, #334155); }
.st-field input[type=text], .st-field input[type=number], .st-field select, .st-field textarea {
  border: 1px solid var(--border, #e2e8f0);
  border-radius: 8px; padding: 8px 10px; font-size: 13px;
  background: var(--bg, #fff); color: var(--text, #0f172a);
  width: 100%; max-width: 100%; box-sizing: border-box;
}
.st-field textarea { resize: vertical; min-height: 60px; }
.st-field input[type=range] { width: 100%; max-width: 100%; }
.st-field input[type=file] { max-width: 100%; }
.st-hint { font-size: 11px; color: var(--text-3, #64748b); }

/* ---- The "upload a file" + "choose from library" row (Branding / Watermark) ----
   A plain `flex items-center gap-2` cannot wrap, and a native file input's
   intrinsic width follows the longest filename it happens to hold — so on a
   phone the pair ran past the right edge of the card and the library button sat
   OUTSIDE the panel it belongs to (the reported "not inside the visible box").
   The row wraps, and on a narrow screen each control takes its own full-width
   line, so both are always inside the card. */
.st-medialine {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  min-width: 0; max-width: 100%;
}
.st-medialine > input[type=file] {
  flex: 1 1 160px; min-width: 0; max-width: 100%; box-sizing: border-box;
}
.st-medialine > .st-btn { flex: 0 1 auto; max-width: 100%; }
.st-medialine > span { flex: 0 0 auto; }
@media (max-width: 560px) {
  /* Phone: the file input gets its own full-width line (a native file input is
     as wide as the filename it holds, and that is what pushed the pair out of the
     card), then the library button shares the next line with the ✅ chip. */
  .st-medialine > input[type=file] {
    flex: 1 1 100%; width: 100%; max-width: 100%; box-sizing: border-box;
  }
  .st-medialine > .st-btn {
    flex: 1 1 auto; min-width: 0; max-width: 100%;
    justify-content: center; min-height: 42px;
  }
  .st-medialine > span { flex: 0 0 auto; }
}

/* Subtitles › Find / Replace, on a phone.
   Each row is [input][Find][↑][↓] and [input][Replace][Replace All]. Measured on
   the real card: the input's `flex:1` held its natural width and pushed the
   buttons out — ↑/↓ were clipped and "Replace All" sat PAST the card edge where
   it could not be tapped at all (reported 2026-11-02 with a screenshot). The
   field now takes its own line and the buttons drop below it. The labels stay
   readable, so no icon-only guessing is needed; `flex-wrap` is the safety net for
   the longest translation.
   Verified by rendering this markup at 350px (see the numbers in the changelog):
   every button's right edge is inside the card and the row no longer scrolls. */
.st-srt-fr { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
.st-srt-fr > input[type=text] { flex: 1 1 140px; min-width: 0; }
.st-srt-fr > .st-btn { flex: 0 0 auto; }
@media (max-width: 560px) {
  .st-srt-fr > input[type=text] {
    flex: 1 1 100%; width: 100%; max-width: 100%; box-sizing: border-box;
  }
  .st-srt-fr > .st-btn { flex: 0 1 auto; min-width: 0; padding: 8px 10px; }
}

/* buttons
   Calm, theme-first button language. Before, every destructive-looking action
   ("Remove file", "Clear all", "✕") carried a saturated red border and Share /
   Send were a loud purple and cyan, so a card read as a wall of alarm colours.
   The palette is now the app's own: slate text on a white pill, the brand
   indigo for the primary action, and SOFT TINTS for the rest — the destructive
   red only deepens on hover, where the intent actually is. */
.st-btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--st-line, #e3e8ef);
  background: #fff; color: #3a4a5f;
  padding: 8px 14px; border-radius: 10px; font-size: 13px; font-weight: 600;
  cursor: pointer;
  box-shadow: 0 1px 1px rgba(15, 23, 42, .03);
  transition: background .18s ease, border-color .18s ease, color .18s ease,
              box-shadow .18s ease;
}
.st-btn:hover { background: #f5f8fc; border-color: #cfdae8; color: #1e293b; }
.st-btn.st-primary {
  background: linear-gradient(135deg, var(--st-accent, #6366f1), #4f46e5);
  border-color: transparent; color: #fff;
  box-shadow: 0 2px 6px rgba(79, 70, 229, .22);
}
.st-btn.st-primary:hover { box-shadow: 0 4px 12px rgba(79, 70, 229, .28); }
.st-btn.st-danger { color: #b42318; border-color: #ecd6d4; background: #fff; }
.st-btn.st-danger:hover { background: #fdf4f3; border-color: #dfb7b3; color: #912018; }
.st-btn.st-success { background: #f0fdf4; color: #15803d; border-color: #cfe9d9; }
.st-btn.st-success:hover { background: #e6f9ec; border-color: #b9dfc8; }
.st-btn.st-upload { background: #eef4fb; color: #1b5f97; border-color: #cfe0f0; }
.st-btn.st-upload:hover { background: #e3eefa; border-color: #bcd4ea; }
.st-btn:disabled { opacity: .5; cursor: not-allowed; }
/* STT engine picker (subtitle step) — a small select styled like a field/button */
select.st-btn.st-inp-sel,
select.st-inp-sel {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6'><path d='M0 0l5 6 5-6z' fill='%2364748b'/></svg>");
  background-repeat: no-repeat; background-position: right 8px center;
  padding-right: 20px;
}

/* ---- Tabs ---- */
.st-tabs { display: flex; gap: 6px; border-bottom: 1px solid var(--border, #e2e8f0); margin-bottom: 14px; }
.st-tab {
  padding: 8px 14px; border: none; background: transparent;
  font-size: 13px; font-weight: 600; color: var(--text-3, #64748b);
  cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.st-tab.st-active { color: var(--st-accent); border-bottom-color: var(--st-accent); }

/* ---- Clip list ---- */
.st-clips { display: flex; flex-direction: column; gap: 8px; }
.st-clip {
  display: flex; align-items: center; gap: 10px; flex-wrap: nowrap;
  border: 1px solid var(--border, #e2e8f0); border-radius: 10px;
  padding: 10px 12px; background: var(--bg, #fff);
  /* ONE line: title · actions · clocks · ⇄ switch · ▶ · ✕. When the viewport is
     genuinely too narrow the row scrolls sideways instead of breaking ▶/✕ onto a
     second line (asked 2026-11-10: "needs to all fit in one line"). */
  overflow-x: auto; overflow-y: hidden;
}
/* Keep a clip row from letting its ↕/↔ chips + ▶/✕ controls spill outside the
   card on narrow screens: let the trailing controls wrap onto their own line. */
.st-clip .st-clip-ctrls { flex: 0 0 auto; min-width: 0; margin-left: auto; }
.st-clip .st-clip-ctrls + .st-btn { flex: none; }
.st-clip > .st-btn { flex: none; white-space: nowrap; }
.st-clip.st-dragging { opacity: .5; }
.st-clip .st-clip-grip { cursor: grab; color: var(--text-3, #64748b); font-size: 16px; }
.st-clip .st-clip-info { flex: 1; min-width: 0; }
.st-clip .st-clip-title { font-weight: 700; font-size: 13px; color: var(--text, #0f172a); }
.st-clip .st-clip-times { font-size: 12px; color: var(--text-3, #64748b); }
/* Prominent, obviously-editable Start/End clocks on each timed clip row. */
.st-clip .st-clip-times { display: inline-flex; align-items: flex-end; gap: 6px; flex-wrap: nowrap; }
.st-clip-time { display: inline-flex; flex-direction: column; }
.st-clip-timelabel {
  font-size: 9px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-3,#64748b); margin: 0 2px 2px;
}
.st-clip-timeinputwrap {
  display: inline-flex; align-items: center; gap: 4px;
  border: 1.5px solid #cbd5e1; background: #f8fafc; border-radius: 9px;
  padding: 3px 8px; cursor: text;
}
.st-clip-timeinputwrap:focus-within { border-color: #1b74bc; background: #fff; box-shadow: 0 0 0 3px rgba(27,116,188,.12); }
.st-clip-timeinputwrap .st-clock {
  font-size: 13px; line-height: 1;
  border: none; background: none; padding: 0; margin: 0; cursor: pointer;
  color: #64748b; transition: color .15s, transform .1s;
}
.st-clip-timeinputwrap .st-clock:hover { color: #1b74bc; transform: scale(1.15); }
.st-clip-timeinputwrap input {
  border: none; background: transparent; outline: none;
  width: 64px; padding: 2px 0; font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 13px; font-weight: 600; color: #0f172a; text-align: center;
}
.st-clip-timeinputwrap input:hover { color: #1b74bc; }
.st-clip-time-sep { color: #94a3b8; font-size: 15px; align-self: center; padding-bottom: 3px; }
/* GUI time picker popup (Start/End clock) */
.st-tk-cols { user-select: none; }
.st-tk-cell {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  border: 1px solid #e2e8f0; border-radius: 12px; padding: 6px 8px; background: #fff;
}
.st-tk-cell .st-tk-val {
  font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 22px; font-weight: 700;
  color: #0f172a; min-width: 46px; text-align: center;
  /* The value is a REAL <input> now (type the digits, Tab to the next unit), so
     strip the field chrome and give it a hover/focus ring instead. */
  width: 56px; padding: 2px 0; box-sizing: border-box;
  border: 1px solid transparent; border-radius: 8px;
  background: transparent; outline: none;
  -moz-appearance: textfield; appearance: textfield;
}
.st-tk-cell .st-tk-val:hover { border-color: #cbd5e1; background: #f8fafc; }
.st-tk-cell .st-tk-val:focus {
  border-color: #1b74bc; background: #eaf3fb; color: #1b74bc;
  box-shadow: 0 0 0 3px rgba(27, 116, 188, .16);
}
.st-tk-cell .st-tk-val::selection { background: rgba(27, 116, 188, .25); }
.st-tk-cell .st-tk-btn {
  border: 1px solid #cbd5e1; background: #f8fafc; color: #334155;
  width: 34px; height: 28px; border-radius: 8px; cursor: pointer; font-size: 16px; line-height: 1;
}
.st-tk-cell .st-tk-btn:hover { border-color: #1b74bc; color: #1b74bc; background: #eaf3fb; }
.st-tk-colon { align-self: center; font-size: 22px; font-weight: 700; color: #94a3b8; }
.st-tk-now {
  display: block; width: 100%; margin: 4px 0 0; padding: 6px;
  border: 1px dashed #c7d2fe; color: #4338ca; background: #eef2ff; border-radius: 10px;
  cursor: pointer; font-size: 12.5px; font-weight: 600;
}
.st-tk-now:hover { background: #e0e7ff; }
/* Title | Start | Stop share ONE horizontal line on wide screens; wrap only on
   narrow/small screens. */
.st-clip-main {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-wrap: nowrap; align-items: center;
  column-gap: 10px; row-gap: 6px;
}
.st-clip-main .st-clip-title-input { flex: 1 1 120px; min-width: 70px; width: auto; max-width: none; }
.st-clip-main .st-clip-times { margin: 0; }
/* A phone cannot hold the row in one line: wrap there (the same rules the clip
   editor had before), and never clip a control. */
@media (max-width: 760px) {
  .st-clip { flex-wrap: wrap; overflow: visible; }
  .st-clip-main { flex-wrap: wrap; min-width: 180px; }
  .st-clip .st-clip-times { flex-wrap: wrap; }
}
.st-clip .st-clip-mode { font-size: 11px; padding: 2px 8px; border-radius: 99px; background: var(--bg-2,#f1f5f9); }
.st-clip select.st-clip-mode-sel { font-size: 12px; border: 1px solid var(--border,#e2e8f0); border-radius: 6px; padding: 3px 6px; }
.st-clip.st-overlap { border-color: var(--st-err); }

/* ---- 9-way grid (mirrors Photo Studio's 3x3 placement keypad) ---- */
.st-9way {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px;
  width: 158px; background: var(--bg-2, #f1f5f9);
  border: 1px solid var(--border, #e2e8f0); border-radius: 14px;
  padding: 8px; box-shadow: inset 0 1px 2px rgba(15,23,42,.04);
  box-sizing: border-box;
}
.st-9way button {
  width: 100%; min-width: 0; height: 42px; box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 9px; border: 1px solid transparent;
  background: transparent; color: var(--text-2, #334155);
  cursor: pointer; font-size: 17px; line-height: 1;
  transition: background .15s, color .15s, border-color .15s, transform .08s;
}
.st-9way button:hover {
  background: rgba(99,102,241,.08); color: #4f46e5; border-color: rgba(99,102,241,.25);
}
.st-9way button:active { transform: scale(.94); }
.st-9way button.st-sel {
  background: linear-gradient(180deg, var(--st-accent, #6366f1), #4f46e5);
  color: #fff; border-color: #4f46e5;
  box-shadow: 0 2px 6px rgba(99,102,241,.4), 0 0 0 3px rgba(99,102,241,.18);
}
@media (max-width: 480px) { .st-9way { width: 144px; } .st-9way button { height: 38px; font-size: 16px; } }

/* ---- SRT editor ---- */
.st-srt-wrap { display: flex; gap: 12px; }
.st-srt-editor { flex: 1; }
.st-srt-editor textarea,
textarea.st-srt-editor {
  width: 100%; min-height: 220px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px; line-height: 1.5; border: 1px solid var(--border,#e2e8f0);
  border-radius: 8px; padding: 10px; background: #0f172a; color: #e2e8f0;
  white-space: pre; resize: vertical;
}
.st-srt-search { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 8px; }
.st-srt-search .st-srt-row { display: flex; align-items: center; gap: 6px; flex: 1 1 200px; min-width: 0; }
.st-srt-search input { flex: 1 1 auto; min-width: 0; border: 1px solid var(--border,#e2e8f0); border-radius: 6px; padding: 6px 8px; font-size: 12px; }
.st-srt-search textarea,
.st-srt-editor textarea,
textarea.st-srt-editor {
  flex: 1; border: 1px solid var(--border,#e2e8f0); border-radius: 6px;
  padding: 8px; font-size: 12px; resize: vertical; min-height: 84px;
  background: var(--bg, #fff); color: var(--text, #0f172a);
}
.st-srt-search .st-btn { flex: none; white-space: nowrap; }
.st-srt-findresult { flex: 1 1 100%; font-size: 11px; font-weight: 600; min-height: 0; }
/* On narrow screens each find/replace group fills the row so nothing spills
   past the working area (no horizontal page scroll). */
@media (max-width: 560px) {
  .st-srt-search .st-srt-row { flex: 1 1 100%; }
}

/* ---- Preview / media ---- */
.st-preview {
  border: 1px solid var(--border, #e2e8f0); border-radius: 10px;
  background: #000; display: flex; align-items: center; justify-content: center;
  min-height: 160px; overflow: hidden; position: relative;
}
.st-preview video, .st-preview img {
  width: 100%; max-height: 520px; object-fit: contain;
}
.st-preview .st-preview-ph { color: #64748b; font-size: 13px; text-align: center; padding: 20px; }

/* ---- Preview while the source is still on its way ----------------------
   Until the video can decode a frame the stage is an empty black slab, and it
   is the sticky FIRST element of every step — on the Subtitles screen it pushed
   the SRT editor below the fold and looked like a dead player. `is-preview-wait`
   (toggled by _paintPreviewWait in studio.js) collapses it to a short card and
   names what it is waiting for; it expands again on its own once a frame is
   available.

   IT KEEPS ITS WIDTH. The wait state used to shrink the whole container to
   320 px and push it to the inline end, so the preview "jumped from the centre
   to the left" (and back) every time the poll flipped the state — and because
   `_fitLiveFrame` measures the parent, the video itself was re-fitted to a
   moving width. Only the HEIGHT changes now, so the stage never moves
   horizontally. */
.st-vidtop.is-preview-wait {
  position: sticky;              /* keep the collapse inside the sticky stage */
  top: 0;
  z-index: 7;
}
.st-vidtop.is-preview-wait .st-preview { min-height: 0; height: 84px; }
.st-vidtop.is-preview-wait .st-preview-ph { display: none; }
/* The Subtitles / Branding / Effects stages are plain divs inside a white card,
   not `.st-preview`, so they need the short height themselves. */
.st-vidtop.is-preview-wait #stSubsPreview,
.st-vidtop.is-preview-wait #stBrandPreview,
.st-vidtop.is-preview-wait #stEffectsPreview {
  height: 84px; min-height: 0 !important; overflow: hidden;
}
.st-vidtop.is-preview-wait video,
.st-vidtop.is-preview-wait img { max-height: 84px; opacity: .5; }
.st-vidtop.is-preview-wait .st-dockcap { display: none !important; }
.st-preview-wait-chip {
  position: absolute; inset-inline: 0; bottom: 0;
  padding: 4px 8px; font-size: 11px; font-weight: 700; line-height: 1.3;
  color: #e2e8f0; background: rgba(15, 23, 42, .78);
  text-align: center; pointer-events: none;
}
@media (max-width: 560px) {
  /* A phone keeps the full width too — only the stage gets shorter. */
  .st-vidtop.is-preview-wait .st-preview { height: 72px; }
  .st-vidtop.is-preview-wait #stSubsPreview,
  .st-vidtop.is-preview-wait #stBrandPreview,
  .st-vidtop.is-preview-wait #stEffectsPreview { height: 72px; }
  .st-vidtop.is-preview-wait video,
  .st-vidtop.is-preview-wait img { max-height: 72px; }
}

/* A project whose video upload never finished says so on its card. */
.st-card-warn {
  margin-top: 6px; padding: 6px 9px; border-radius: 8px;
  font-size: 12px; font-weight: 700; line-height: 1.35;
  color: #7f1d1d; background: rgba(239, 68, 68, .1);
  border: 1px solid rgba(239, 68, 68, .35);
}
.st-card-warn-why { font-weight: 500; opacity: .8; }

/* Larger clip preview (Step 3) */
.st-preview.st-preview-lg { min-height: 380px; width: 100%; }
.st-preview.st-preview-lg .st-preview-video { width: 100%; }
.st-preview.st-preview-lg video { width: 100%; max-height: 620px; object-fit: contain; }
.st-preview-video { display: flex; justify-content: center; width: 100%; }
.st-preview-video video { width: 100%; }

/* Live aspect-fitted frame: keep it centred wherever it appears (white-card
   hosts aren't flex), never stretch it wider than its host. */
.st-live-frame { margin: 0 auto; }
.st-live-frame video { object-fit: fill; }

/* Clip preview playback speed controls */
.st-clip-speed {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin-top: 10px; padding: 8px 12px;
  background: var(--accent-soft, rgba(99,102,241,.08));
  border-radius: 8px;
}
.st-clip-speed > span { font-size: 12px; font-weight: 600; color: var(--text-3, #64748b); margin-right: 2px; }
.st-speed-btn {
  font-size: 12px; font-weight: 700; line-height: 1;
  padding: 6px 11px; border-radius: 99px; border: 1px solid var(--border, #e2e8f0);
  background: var(--bg, #fff); color: var(--text-2, #334155); cursor: pointer;
  transition: all .12s ease;
}
.st-speed-btn:hover { border-color: var(--st-accent, #6366f1); }
.st-speed-btn.st-speed-active { background: var(--st-accent, #6366f1); color: #fff; border-color: var(--st-accent, #6366f1); }

/* Output-mode halo toggles (Vertical / Horizontal) */
.st-mode-toggle .st-mode-chip {
  border: 2px solid var(--border-2, #cbd5e1);
  background: var(--bg, #fff);
  color: var(--text-3, #64748b);
  border-radius: 999px;
  padding: 5px 12px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: all .15s ease;
  white-space: nowrap;
}
.st-mode-toggle .st-mode-chip:hover { border-color: var(--st-accent, #6366f1); color: var(--st-accent, #6366f1); }
.st-mode-toggle .st-mode-chip.st-on {
  border-color: var(--st-accent, #6366f1);
  background: var(--st-accent, #6366f1);
  color: #fff;
}

/* ---- Timeline slider ---- */
.st-timeline { position: relative; margin: 8px 0; }
.st-timeline input[type=range] { width: 100%; }

/* ---- Motion graphics list ---- */
.st-gfx-list { display: flex; flex-direction: column; gap: 6px; }
.st-gfx-item {
  display: flex; align-items: center; gap: 8px; padding: 6px 10px;
  border: 1px solid var(--border,#e2e8f0); border-radius: 8px; font-size: 12px;
}

/* ---- Progress / processing ---- */
.st-progress-card {
  border: 1px solid var(--border, #e2e8f0); border-radius: 14px;
  background: var(--card, #fff); padding: 18px 20px;
}
/* Generic in-panel progress bars (file upload, "Prepare Source", etc.). */
.st-progress-track {
  background: var(--border, #e2e8f0); border-radius: 99px; overflow: hidden;
}
.st-progress-fill {
  height: 100%; background: linear-gradient(90deg, var(--st-ok, #10b981), var(--st-accent, #6366f1));
  transition: width .3s ease; width: 0; display: block;
}
.st-progress-card .st-progress-track {
  height: 14px; background: var(--border, #e2e8f0); border-radius: 99px; overflow: hidden;
}
.st-progress-card .st-progress-fill {
  height: 100%; background: linear-gradient(90deg, var(--st-ok), var(--st-accent));
  transition: width .3s ease; width: 0;
}
/* Inline progress bar shown under a running project's name in the list */
.st-list-progress .st-progress-track {
  height: 8px; background: var(--border, #e2e8f0); border-radius: 99px; overflow: hidden;
  display: inline-block; vertical-align: middle;
}
.st-list-progress .st-progress-fill {
  height: 100%; background: linear-gradient(90deg, var(--st-ok), var(--st-accent));
  transition: width .3s ease; width: 0; display: block;
}
.st-log {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px; background: #0f172a; color: #cbd5e1;
  border-radius: 10px; padding: 12px; max-height: 220px; overflow-y: auto;
  white-space: pre-wrap; line-height: 1.5;
}

/* ---- Background-work strip -------------------------------------------------
   Shown on EVERY Studio step while a long job is running (source download,
   audio-to-text, translation, export) so the user always knows work is
   happening and that it is safe to keep going or leave. */
.st-bgstrip {
  margin: 12px 0 4px; padding: 12px 14px;
  border: 1px solid var(--border, #e2e8f0); border-left: 4px solid var(--brand-600, #4f46e5);
  border-radius: 12px; background: var(--surface-2, #f8fafc);
}
.st-bghead { display: flex; flex-wrap: wrap; gap: 8px 10px; align-items: center; margin-bottom: 8px; }
/* The poster frame of the video being worked on. Without it the strip was an
   anonymous progress bar with no way to tell WHICH video it was downloading
   (reported). Derived from the link, so it shows even when the metadata fetch
   could not run. */
.st-bg-thumb {
  flex: none; width: 72px; height: 40px; object-fit: cover;
  border-radius: 6px; background: #0f172a; border: 1px solid var(--border, #e2e8f0);
}
.st-bg-titles { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 200px; }
.st-bgtitle { font-size: 13px; font-weight: 700; color: var(--text-2, #334155); }
.st-bghint { font-size: 11.5px; color: var(--text-3, #94a3b8); }
.st-bgrow {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 5px 0; font-size: 12.5px; color: var(--text-2, #334155);
}
.st-bgico { flex: none; width: 18px; text-align: center; }
.st-bglabel { font-weight: 600; flex: none; }
.st-bgmsg { color: var(--text-3, #64748b); flex: 1 1 140px; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-bgtrack {
  flex: 0 0 120px; height: 7px; border-radius: 99px; overflow: hidden;
  background: var(--border, #e2e8f0);
}
.st-bgfill {
  display: block; height: 100%; width: 0;
  background: linear-gradient(90deg, var(--st-ok, #10b981), var(--st-accent, #6366f1));
  transition: width .3s ease;
}
.st-bgpct { flex: none; width: 38px; text-align: right; font-variant-numeric: tabular-nums;
  color: var(--text-3, #64748b); }
@media (max-width: 560px) {
  .st-bgmsg { flex-basis: 100%; }
  .st-bgtrack { flex-basis: 90px; }
}

/* ---- A background job that FAILED (acknowledged, not just dismissed) -------
   Background work exists so the user can leave; if it failed while they were
   away, the dialog that reported it died with the tab. The reason is stored on
   the project and shown here until the user presses OK (see
   bgErrorBannerHtml in studio.js) — a red banner, not a dialog, so it is
   impossible to miss on the way back in and never blocks the work. */
.st-bgerr {
  margin: 12px 0 4px; padding: 10px 12px;
  border: 1px solid rgba(239, 68, 68, .45);
  border-left: 4px solid #ef4444;
  border-radius: 12px; background: rgba(239, 68, 68, .07);
  display: flex; flex-direction: column; gap: 8px;
}
.st-bgerr-row {
  display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap;
  padding: 6px 0; border-top: 1px dashed rgba(239, 68, 68, .3);
}
.st-bgerr-row:first-child { border-top: 0; padding-top: 0; }
.st-bgerr-ico { flex: none; font-size: 16px; line-height: 1.4; }
.st-bgerr-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 220px; }
.st-bgerr-title { font-size: 13px; font-weight: 800; color: #b91c1c; }
.st-bgerr-why { font-size: 12.5px; color: var(--text-2, #334155); word-break: break-word; }
.st-bgerr-hint { font-size: 11.5px; color: var(--text-3, #94a3b8); }
.st-bgerr-retry, .st-bgerr-ok { flex: none; align-self: center; }
.st-bgerr-ok {
  border-color: #ef4444 !important; color: #b91c1c !important;
  background: #fff !important; font-weight: 700;
}
@media (max-width: 560px) {
  .st-bgerr-retry, .st-bgerr-ok { flex: 1 1 100%; }
}

/* The inline "paste the YouTube link" field that keeps the Step-②
   Auto-extract / DownSub actions usable when the project has no link
   (see subsActionsHtml in studio.js). */
.st-srt-ytfield {
  display: inline-flex; flex-direction: column; gap: 3px;
  flex: 1 1 260px; min-width: 0;
}
.st-srt-ytfield input {
  width: 100%; box-sizing: border-box;
  padding: 7px 10px; border: 1px solid var(--border, #e2e8f0);
  border-radius: 9px; font-size: 12.5px;
  background: var(--surface, #fff); color: var(--text, #0f172a);
}
.st-srt-ytfield .st-hint { display: block; }

/* The persistent "working on it" line for Step ② Auto-extract: a spinner plus
   the elapsed seconds, next to the button that started it. A toast fades; this
   stays until the server answers, so the wait is never an empty screen
   (reported twice as "how do I know it is trying to do anything?"). */
.st-srt-busy {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 12px; border-radius: 999px;
  border: 1px solid #c7d2fe; background: #eef2ff;
  font-size: 12.5px; font-weight: 600; color: #3730a3;
  font-variant-numeric: tabular-nums;
}
.st-srt-busy .st-spinner { width: 13px; height: 13px; border-width: 2px; }

/* Step ② — 🎤 Audio to text is ONE control.
   The action and the choices it will use — engine, model, the billed rate and
   the spoken language — are a single decision ("transcribe this audio, like
   this"), so they share ONE pill instead of sitting loose in the actions row as
   four unrelated controls (see subsActionsHtml in studio.js). The inner selects
   are deliberately FLAT: a pill inside a pill reads as two controls again. */
.st-stt-pill {
  display: inline-flex; align-items: center; flex-wrap: wrap;
  gap: 2px; padding: 3px; max-width: 100%;
  border: 1px solid var(--border, #e2e8f0); border-radius: 999px;
  background: var(--bg, #fff);
}
.st-stt-pill .st-stt-go {
  height: 30px; padding: 0 12px; border-radius: 999px; font-size: 13px;
}
.st-stt-pill .st-inp-sel {
  height: 30px; min-width: 0; padding: 0 18px 0 8px;
  border-color: transparent; background-color: transparent;
  border-radius: 999px; font-size: 12.5px; font-weight: 600;
  color: var(--text, #0f172a); cursor: pointer; max-width: 100%;
  /* A <select> is otherwise as wide as its LONGEST option, which leaves a dead
     gap between "nova-3" and its caret. Chrome sizes it to the visible value
     with this; browsers without support keep the old (perfectly usable) box. */
  field-sizing: content;
}
/* A hairline so the action reads as one control and its choices as the settings
   OF that control, without drawing a second set of borders inside the pill. */
.st-stt-pill .st-stt-go + .st-inp-sel {
  border-left: 1px solid var(--border, #e2e8f0);
  border-top-left-radius: 0; border-bottom-left-radius: 0;
}
.st-stt-pill .st-inp-sel:hover { background-color: var(--bg-2, #f1f5f9); }
.st-stt-pill .st-stt-rate {
  font-size: 11.5px; color: #b45309; padding: 0 8px 0 6px; white-space: nowrap;
}
/* The two/three pickers of a HAND-MADE bilingual pair ("Choose my own…"). They
   sit right of the language select inside the same pill, with one hairline, so
   they read as ONE choice ("mixed language") rather than three more controls. */
.st-stt-pill .st-stt-langpart { font-size: 12px; }
.st-stt-pill #stSttLang + .st-stt-langpart {
  border-left: 1px solid var(--border, #e2e8f0);
  border-top-left-radius: 0; border-bottom-left-radius: 0;
}
/* 👥 Speakers (Speaker Identification). A LABEL, not a pill: it is a plain
   on/off choice inside the control, so it must not introduce a second border. */
.st-stt-pill .st-stt-diarbox {
  display: inline-flex; align-items: center; gap: 5px; padding: 0 8px;
  font-size: 12.5px; font-weight: 600; color: var(--text, #0f172a);
  cursor: pointer; white-space: nowrap; user-select: none;
}
.st-stt-pill .st-stt-diarbox input { margin: 0; cursor: pointer; }
.st-stt-pill .st-stt-diarbox:has(input:disabled) {
  color: var(--text-3, #94a3b8); cursor: not-allowed;
}
@media (max-width: 560px) {
  /* Stays ONE unit on a phone. It may wrap internally, but it never stretches a
     single child to the full width the way a lone .st-btn does there. */
  .st-stt-pill { width: 100%; border-radius: 14px; }
  .st-stt-pill .st-stt-go { flex: 0 0 auto; justify-content: center; }
  /* No hairline once the pill wraps — on a new line it renders as a stray tick. */
  .st-stt-pill .st-stt-go + .st-inp-sel { border-left: 0; border-radius: 999px; }
}

/* "Still working on…" list inside the Export-guard modal. */
.st-bglist { display: flex; flex-direction: column; gap: 6px; }.st-bglistrow {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  padding: 8px 10px; border-radius: 10px; background: var(--surface-2, #f8fafc);
  border: 1px solid var(--border, #e2e8f0); font-size: 13px;
}
.st-bglistrow b { color: var(--text, #0f172a); }
.st-bglistrow span { color: var(--text-3, #64748b); font-size: 12px; }

/* ---- Site-wide "background work" pill -------------------------------------
   A small floating pill on EVERY view, INCLUDING the Studio. It used to be
   suppressed on the Studio view on the theory that the strip covered it — but
   the strip only covers the project that is OPEN, and it scrolls away, so the
   user could be looking at the Studio with work running and see nothing at all.
   The pill now stands down only while the strip for the open project is
   actually on screen (see `stripOnScreen()` in studio.js). */
.st-bgpill {
  position: fixed; right: 14px; bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  /* ABOVE the new-project wizard (z 80): the wizard's source screen is exactly
     where the automatic download starts, so the indicator must win there. */
  z-index: 90; display: none; align-items: center; gap: 7px;
  padding: 9px 14px; border: none; border-radius: 999px; cursor: pointer;
  font-size: 12.5px; font-weight: 600; color: #fff;
  background: linear-gradient(135deg, var(--brand-500, #6366f1), var(--accent-500, #06b6d4));
  box-shadow: 0 6px 20px rgba(15, 23, 42, .28);
}
.st-bgpill:hover { filter: brightness(1.06); }
.st-bgpill-ico { font-size: 14px; }
.st-bgpill-sub { font-weight: 500; opacity: .9; }
/* The ✕ on the pill. The pill itself is a <button>, so this is a span with a
   click handler (a button cannot nest a button); it stops the click from
   reaching the pill's own "open the project" handler. */
.st-bgpill-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; margin: 0 -6px 0 2px;
  border-radius: 50%; background: rgba(255, 255, 255, .18);
  font-size: 12px; line-height: 1; cursor: pointer;
}
.st-bgpill-x:hover { background: rgba(255, 255, 255, .34); }
@media (max-width: 560px) {
  /* Lift above the app's fixed bottom tab bar. */
  .st-bgpill { bottom: calc(64px + env(safe-area-inset-bottom, 0px)); right: 10px; }
  .st-bgpill-sub { display: none; }
}

/* ---- The New-project wizard ------------------------------------------------
   The guided popup that replaces the old "Create a video project" modal: it asks
   the ONE question that decides the rest ("how would you like to start?"), then
   shows only the screens that answer implies.

   It is a fixed overlay INSIDE #studioView so every `.st-*` rule above still
   applies (pills, hints, drop zone, progress bars) — which is only safe because
   the editor is emptied while it is open (no duplicate element ids).

   Desktop: a centred card. Phone (<=640px): a BOTTOM SHEET, so the two actions
   sit under the thumb and clear the keyboard. */
.st-wiz {
  position: fixed; inset: 0; z-index: 80;
  display: flex; align-items: center; justify-content: center; padding: 16px;
  background: rgba(15, 23, 42, .48);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.st-wiz-card {
  display: flex; flex-direction: column;
  width: 100%; max-width: 560px; max-height: min(86vh, 720px);
  background: #fff; border-radius: 18px; overflow: hidden;
  box-shadow: 0 24px 60px rgba(15, 23, 42, .28);
}
.st-wiz-head { display: flex; align-items: center; gap: 10px; padding: 14px 16px 0; }
.st-wiz-eyebrow {
  font-size: 12px; font-weight: 800; color: var(--text-3, #94a3b8); letter-spacing: .02em;
}
.st-wiz-x {
  margin-inline-start: auto; cursor: pointer; font-family: inherit;
  width: 32px; height: 32px; border-radius: 50%; border: none;
  background: #f1f5f9; color: var(--text-3, #64748b); font-size: 14px; line-height: 1;
}
.st-wiz-x:hover { background: #e2e8f0; color: var(--text, #0f172a); }
.st-wiz-prog { padding: 10px 16px 0; }
.st-wiz-stepline {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-top: 7px;
  font-size: 12.5px; color: var(--text-3, #64748b);
}
.st-wiz-stepline b { color: var(--brand-600, #4f46e5); font-size: 11.5px; letter-spacing: .02em; }
.st-wiz-stepline span { font-size: 14px; font-weight: 700; color: var(--text, #0f172a); }
.st-wiz-body {
  flex: 1 1 auto; overflow-y: auto; padding: 12px 16px 16px;
  -webkit-overflow-scrolling: touch;
}
/* The Branding / Effects screens reuse the editor's panels, whose live preview is
   `position: sticky`. The popup body is its OWN scroll container, so a sticky
   preview would cover the controls scrolling under it (the trap Photo Studio
   hit). Inside the wizard it scrolls with the panel instead. */
.st-wiz-body .st-video-top { position: static; }
.st-wiz-foot {
  display: flex; align-items: center; gap: 10px; flex: none;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--border, #e2e8f0); background: #fff;
}
.st-wiz-btn { flex: 1 1 auto; justify-content: center; min-height: 46px; font-size: 14.5px; }
.st-wiz-why {
  padding: 0 16px 12px; font-size: 11.5px; color: var(--text-3, #94a3b8); text-align: center;
}
.st-wiz-empty {
  padding: 18px 14px; text-align: center; font-size: 13px; line-height: 1.5;
  color: var(--text-3, #64748b); border: 2px dashed var(--border, #e2e8f0); border-radius: 14px;
}

/* The doors: full-width cards with a sentence each. Bigger than the editor's
   pills on purpose — this is the first screen and the difference between the
   options has to be UNDERSTOOD, not merely selected. */
.st-wdoors { display: flex; flex-direction: column; gap: 9px; }
.st-wdoor {
  display: grid; grid-template-columns: 38px 1fr; column-gap: 10px; row-gap: 2px;
  align-items: center; text-align: start; cursor: pointer; font-family: inherit;
  width: 100%; padding: 13px 15px; border-radius: 14px;
  border: 2px solid var(--border, #e2e8f0); background: #fff;
}
.st-wdoor:hover { border-color: var(--brand-500, #6366f1); background: #fbfbff; }
.st-wdoor.on { border-color: var(--brand-600, #4f46e5); background: #eef2ff; }
.st-wdoor-ico { grid-row: span 2; font-size: 22px; line-height: 1; text-align: center; }
.st-wdoor-t { font-size: 14.5px; font-weight: 800; color: var(--text, #0f172a); }
.st-wdoor.on .st-wdoor-t { color: var(--brand-600, #4f46e5); }
.st-wdoor-s { font-size: 12px; color: var(--text-3, #64748b); line-height: 1.45; }

/* The doors screen also offers the two ways in a video you already have, so step 1
   is not a choice with no source (see wizStartHtml). */
.st-wstart { margin-top: 16px; }
.st-wstart-or {
  display: flex; align-items: center; gap: 10px; margin: 0 0 11px;
  font-size: 11.5px; color: var(--text-3, #94a3b8);
}
.st-wstart-or::before, .st-wstart-or::after {
  content: ""; flex: 1 1 auto; height: 1px; background: var(--border, #e2e8f0);
}
.st-wstart-or span { flex: none; }
.st-wstart-btns { display: flex; gap: 9px; }
.st-wstart-btns .st-btn { flex: 1 1 auto; justify-content: center; min-height: 44px; }

/* The one-question-a-screen choices (subtitles? what to produce? which shape?). */
.st-wpicks { display: flex; flex-direction: column; gap: 9px; }
.st-wpick {
  display: grid; grid-template-columns: 34px 1fr; column-gap: 10px; row-gap: 2px;
  align-items: center; text-align: start; cursor: pointer; font-family: inherit;
  width: 100%; padding: 13px 14px; border-radius: 14px;
  border: 2px solid var(--border, #e2e8f0); background: #fff;
}
.st-wpick:hover { border-color: var(--brand-500, #6366f1); background: #fbfbff; }
.st-wpick.on { border-color: var(--brand-600, #4f46e5); background: #eef2ff; }
.st-wpick-ico { grid-row: span 2; font-size: 20px; line-height: 1; text-align: center; }
.st-wpick-t { font-size: 14px; font-weight: 700; color: var(--text, #0f172a); }
.st-wpick.on .st-wpick-t { color: var(--brand-600, #4f46e5); }
.st-wpick-s { font-size: 12px; color: var(--text-3, #64748b); line-height: 1.45; }
/* A choice this project cannot take is shown DISABLED, with its reason in place
   of the description — never hidden. The same rule the wizard's Next follows. */
.st-wpick-off { opacity: .55; cursor: not-allowed; }
.st-wpick-off:hover { border-color: var(--border, #e2e8f0); background: #fff; }
.st-wpick-off .st-wpick-s { color: #b45309; }

/* The inline options a chosen answer unfolds (the Audio-to-text engine/model/
   speaker/language pill, the Audio Studio transcript list, the .srt picker, the
   paste box). It drops down DIRECTLY under the card that opened it — the markup
   interleaves it (see wizSubHowHtml), and it is indented and tinted so it reads
   as belonging to that card rather than as one more choice. */
.st-wiz-opts {
  /* Pulled up under its card: the `.st-wpicks` flex gap is 9px, so -5px leaves a
     tight 4px and the panel clearly hangs off the answer above it. */
  margin: -5px 0 2px; padding: 12px;
  border: 1px solid var(--border, #e2e8f0); border-inline-start: 3px solid var(--brand-600, #4f46e5);
  border-radius: 12px; background: var(--surface-2, #f8fafc);
}
.st-wiz-optlabel {
  font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-3, #64748b); margin-bottom: 8px;
}
.st-wiz-opts .st-stt-pill { background: #fff; }
.st-wiz-opts .st-wtpls { margin-top: 2px; }
.st-wiz-opts .st-hint { display: block; }

/* Saved templates, as tappable rows. */
.st-wtpls { display: flex; flex-direction: column; gap: 8px; }
.st-wtpl {
  display: grid; grid-template-columns: 30px 1fr; column-gap: 10px;
  align-items: center; text-align: start; cursor: pointer; font-family: inherit;
  width: 100%; padding: 11px 13px; border-radius: 12px;
  border: 2px solid var(--border, #e2e8f0); background: #fff;
}
.st-wtpl:hover { border-color: var(--brand-500, #6366f1); background: #fbfbff; }
.st-wtpl.on { border-color: var(--brand-600, #4f46e5); background: #eef2ff; }
.st-wtpl-ico { font-size: 18px; line-height: 1; text-align: center; }
.st-wtpl-t { font-size: 13.5px; font-weight: 700; color: var(--text, #0f172a); }
.st-wtpl-s { grid-column: 2; font-size: 11.5px; color: var(--text-3, #94a3b8); }

/* The last screen's "here is what you chose" summary. */
.st-wiz-sum {
  margin-top: 14px; border: 1px solid var(--border, #e2e8f0); border-radius: 12px;
  background: var(--surface-2, #f8fafc); overflow: hidden;
}
.st-wsumrow {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 9px 12px; font-size: 12.5px; color: var(--text-2, #334155);
  border-bottom: 1px solid var(--border, #e2e8f0);
}
.st-wsumrow:last-child { border-bottom: none; }
.st-wsumrow b { color: var(--text, #0f172a); text-align: end; }

@media (max-width: 640px) {
  /* Bottom sheet: the two actions want to be where the thumb already is (the
     bottom edge), and a centred card puts them level with the keyboard as soon
     as the URL field on the source screen has focus. */
  .st-wiz { align-items: flex-end; padding: 0; }
  .st-wiz-card { max-width: 100%; border-radius: 18px 18px 0 0; max-height: 92vh; }
}

/* ---- Sticky Previous/Next footer (matches Photo Studio's sticky bar) ----
   Keeps step navigation reachable on desktop AND mobile — on phones it lifts
   just above the app's fixed bottom tab bar instead of hiding underneath it. */
.st-navbar {
  position: sticky;
  bottom: 0;
  z-index: 30;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  margin-top: 18px;
  padding: 10px 2px calc(10px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(to top, rgba(248,250,252,.98), rgba(248,250,252,.6));
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  border-top: 1px solid var(--border, #e2e8f0);
}
.st-navbar .st-nav-btn { min-width: 108px; margin: 0; }
.st-navbar .st-nav-arrow { display: none; }
.st-navbar .st-nav-next { margin-left: auto; }
.st-navbar .st-nav-prev { margin-right: auto; }

/* On phones collapse to round arrow buttons like the Photo Studio bar. */
@media (max-width: 480px) {
  .st-navbar .st-nav-label { display: none; }
  .st-navbar .st-nav-arrow { display: inline-block; }
  .st-navbar .st-nav-btn {
    min-width: 46px; width: 46px; height: 46px; padding: 0;
    border-radius: 50%; font-size: 24px; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
  }
  .st-navbar { padding: 8px 6px; }
  /* Final (Export) step: turn the bare › circle into a clear "Export" pill so
     the last action reads as a label instead of just an arrow. Scoped to the
     NEXT button only — the Previous button keeps its round ‹ icon like all
     other steps. */
  .st-navbar.st-nav-final .st-nav-next .st-nav-label { display: inline-block; }
  .st-navbar.st-nav-final .st-nav-next .st-nav-arrow { display: none; }
  .st-navbar.st-nav-final .st-nav-next {
    width: auto; min-width: 96px; height: 40px; padding: 0 16px;
    border-radius: 999px; font-size: 14px; font-weight: 700;
  }
}

/* ---- Project list: premium creative gallery ---- */

/* Toolbar (title + actions + search) */
.st-toolbar {
  background: linear-gradient(135deg, #1e1b4b 0%, #312e81 55%, #4c1d95 100%);
  border: none;
  color: #fff;
  justify-content: space-between;
  /* The bar's own contents must never poke out of the gradient: the brand and
     the actions are two flexible boxes that may WRAP, and neither may claim
     more width than the bar has (2026-10-25 — the search field and the view
     toggles ran past the rounded edge at a medium window width). */
  flex-wrap: wrap;
  gap: 10px 12px;
  min-width: 0;
}
.st-toolbar .st-btn { border-color: rgba(255,255,255,.25); color: #fff; background: rgba(255,255,255,.08); }
.st-toolbar .st-btn:hover:not(:disabled) { background: rgba(255,255,255,.18); }
.st-toolbar .st-btn.st-primary { background: var(--st-accent, #6366f1); border-color: var(--st-accent, #6366f1); }
.st-toolbar-brand { display: flex; align-items: center; gap: 12px; min-width: 0; }
.st-toolbar-logo {
  width: 44px; height: 44px; border-radius: 12px; font-size: 22px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,.14);
}
.st-toolbar-actions {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  justify-content: flex-end; min-width: 0; flex: 0 1 auto;
}
.st-search {
  border: 1px solid rgba(255,255,255,.25); border-radius: 10px;
  background: rgba(255,255,255,.08); color: #fff; padding: 8px 12px;
  font-size: 13px; outline: none;
  /* `border-box` matters: this field widens to fill a narrow bar (`width:
     100%` under 720px), and content-box then added the padding and the border
     on TOP of that — the box, and its border, sat outside the purple panel. */
  box-sizing: border-box;
  flex: 1 1 170px; min-width: 140px; max-width: 250px;
}
.st-search::placeholder { color: rgba(255,255,255,.55); }
.st-search:focus { border-color: #a5b4fc; background: rgba(255,255,255,.14); }
/* The grid/list pair reads as ONE segmented control: a single border around the
   two buttons, so their own borders never show as a second frame. */
.st-viewtoggle {
  display: inline-flex; align-items: center; flex: none; box-sizing: border-box;
  border: 1px solid rgba(255,255,255,.28); border-radius: 8px; overflow: hidden;
}
.st-viewtoggle .st-btn {
  border: 0; border-radius: 0; background: transparent; height: 34px;
  padding: 0 10px; line-height: 1;
}
.st-viewtoggle .st-btn.on { background: rgba(255,255,255,.22); }

/* Responsive grid of project cards. `min(330px, 100%)` keeps the track from
   demanding 330px on a 320px phone — with a bare `minmax(330px, 1fr)` the
   column's automatic minimum was 330px and the whole grid overflowed. */
.studio-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(330px, 100%), 1fr));
  gap: 16px;
  min-width: 0;
}
.studio-empty {
  grid-column: 1 / -1;
  text-align: center; color: var(--text-3, #94a3b8);
  background: var(--card, #fff); border: 1px dashed var(--border, #e2e8f0);
  border-radius: 14px; padding: 40px 16px;
}

/* Project card */
.studio-card {
  background: var(--card, #fff);
  border: 1px solid var(--border, #e2e8f0);
  border-radius: 16px;
  padding: 16px;
  display: flex; flex-direction: column; gap: 10px;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
  position: relative;
  min-width: 0;
}
.studio-card:hover {
  transform: translateY(-3px);
  border-color: var(--st-accent, #6366f1);
  box-shadow: 0 10px 28px rgba(99, 102, 241, 0.18);
}
.studio-card[data-status="completed"] { border-left: 4px solid #22c55e; }
.studio-card[data-status="running"] { border-left: 4px solid #f59e0b; }
.studio-card[data-status="draft"], .studio-card[data-status="failed"] { border-left: 4px solid #64748b; }

.studio-card-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; min-width: 0; }
.studio-card-title { display: flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }
.studio-card-icon { font-size: 20px; }
/* The rename pencil sits BEFORE the project name (it edits that name), and the
   Open action is a real, labelled button instead of a "•••" glyph that told the
   user nothing about what it did. */
.studio-card-rename {
  border: none; background: transparent; cursor: pointer; color: var(--text-3, #94a3b8);
  font-size: 15px; padding: 2px 4px; line-height: 1; border-radius: 8px; flex: none;
  transition: background .15s ease, color .15s ease;
}
.studio-card-rename:hover { color: var(--st-accent, #6366f1); background: #eef0fe; }
.studio-card-header .st-open { flex: none; height: 32px; padding: 0 14px; font-size: 12.5px; }
/* The project name is the main offender for horizontal overflow: it used to be
   `nowrap` + ellipsis, which made the card's min-content width equal to the
   full name on one line. It now wraps onto as many lines as it needs, so the
   card can always shrink to the phone's width. */
.studio-card-name {
  font-size: 18px; font-weight: 700; color: var(--text, #0f172a);
  min-width: 0; flex: 1 1 auto;
  overflow-wrap: anywhere; word-break: break-word;
}
/* (The old "•••" card menu is retired — the pencil renames and "Open" opens.) */
.studio-card-status {
  font-size: 11px; font-weight: 600; padding: 3px 10px; border-radius: 20px;
  text-transform: uppercase; letter-spacing: .5px; flex: none;
}
.status-completed { background: #22c55e; color: #fff; }
.status-running { background: #f59e0b; color: #1c1917; }
.status-draft { background: #64748b; color: #fff; }
.status-failed { background: #ef4444; color: #fff; }

.studio-card-meta {
  font-size: 12px; color: var(--text-3, #64748b);
  min-width: 0; overflow-wrap: anywhere;
}

/* The facts block: the meta line PLUS the retention badge, in ONE grid child.
   In LIST mode the card is `grid-template-areas: "head meta body"`, so a bare
   badge would be auto-placed on a second row and break the one-line layout. */
.studio-card-facts {
  display: flex; flex-direction: column; gap: 1px; min-width: 0;
}

/* "Deletes in N days" — the retention line every project/job card carries.
   The colour/weight come from the shared `.ss-del-in` (styles.css); this only
   puts it on its own line under the meta facts, so a long meta string can never
   push it out of view. */
.studio-card-del {
  font-size: 11.5px; line-height: 1.35; margin-top: -2px;
}

/* ---- "Keep after retention" switch (static/js/keep_forever.js) -----------
   ONE look for every studio's card. The real control stays the checkbox
   (keyboard, screen readers); the track is only how it looks. */
.kf {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  font: inherit; font-size: 11.5px; font-weight: 700; color: var(--text-3, #64748b);
  user-select: none; margin-top: 3px;
}
.kf-in {
  position: absolute; inline-size: 1px; block-size: 1px;
  opacity: 0; pointer-events: none;
}
.kf-track {
  position: relative; inline-size: 36px; block-size: 20px; flex: none;
  border-radius: 999px; background: #cbd5e1; transition: background .15s;
}
.kf-thumb {
  position: absolute; inset-block-start: 3px; inset-inline-start: 3px;
  inline-size: 14px; block-size: 14px; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 2px rgba(15, 23, 42, .35); transition: transform .15s;
}
.kf-in:checked + .kf-track { background: #16a34a; }
.kf-in:checked + .kf-track .kf-thumb { transform: translateX(16px); }
[dir="rtl"] .kf-in:checked + .kf-track .kf-thumb { transform: translateX(-16px); }
.kf-in:focus-visible + .kf-track { outline: 2px solid var(--brand, #4f46e5); outline-offset: 2px; }
/* A kept job has no deletion date to promise, so the red badge goes away — the
   switch itself says why. */
.kf-kept .studio-card-del,
.kf-kept .vm-card-del,
.kf-kept .sl-job-del,
.kf-kept .pe-proj-expire { display: none; }
/* List mode: the facts block is one grid cell, so the switch keeps its line. */
.studio-grid.st-list .studio-card-facts .kf { margin-top: 2px; }

/* Card body: preview strip + actions */
.studio-card-body { display: flex; flex-direction: column; gap: 12px; }
.studio-card-preview {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--bg-2, #f1f5f9); border-radius: 12px; padding: 10px 12px;
  font-size: 13px;
}
.studio-preview-empty { color: var(--text-3, #64748b); font-style: italic; }
.studio-dur-badge {
  font-size: 12px; font-weight: 700; padding: 3px 10px; border-radius: 20px;
  background: #eef0fe; color: #4338ca;
}
.studio-out-count { font-size: 12px; color: var(--text-2, #334155); font-weight: 600; }
.studio-preview-del {
  margin-left: auto; padding: 0 8px; height: 28px; flex: none;
  background: transparent; color: var(--text-3, #94a3b8); border-color: transparent;
}
.studio-preview-del:hover { background: rgba(239, 68, 68, .12); color: #ef4444; border-color: transparent; }

/* Action buttons */
.studio-card-actions {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
}
.studio-card-actions .st-btn { height: 36px; padding: 0 14px; font-size: 13px; font-weight: 500; }
/* Share / Send were a loud purple and cyan against a white card. Same actions,
   now soft theme tints that sit next to the neutral buttons instead of shouting
   over them. */
.studio-card-actions .st-btn.st-share { background: #eef0fe; color: #4338ca; border-color: #d8dbfa; }
.studio-card-actions .st-btn.st-share:hover { background: #e3e6fd; border-color: #c4c9f5; color: #3730a3; }
.studio-card-actions .st-btn.st-friend { background: #e8f6f9; color: #0e7490; border-color: #c9e6ee; }
.studio-card-actions .st-btn.st-friend:hover { background: #dcf0f5; border-color: #b3dbe6; color: #155e75; }
.studio-card-actions .st-btn.st-icon { padding: 0 10px; }

/* ---- List view ------------------------------------------------------------
   The toolbar's ▦/☰ toggle switches the SAME cards into one dense row each:
   name + status on the left, the meta line in the middle, the preview strip and
   the actions on the right. Nothing is re-generated for it — a list is a
   LAYOUT here, not a second renderer — so the two views can never drift apart
   in what they show or which actions they offer. */
.studio-grid.st-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.studio-grid.st-list .studio-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr) auto;
  grid-template-areas: "head meta body";
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 12px;
}
/* The lift is a card affordance; a row that jumps under the cursor is noise. */
.studio-grid.st-list .studio-card:hover { transform: none; }
.studio-grid.st-list .studio-card-header { grid-area: head; align-items: center; }
.studio-grid.st-list .studio-card-name { font-size: 15px; }
.studio-grid.st-list .studio-card-meta { margin: 0; }
/* The FACTS wrapper is the grid child (meta + retention badge inside it). */
.studio-grid.st-list .studio-card-facts { grid-area: meta; min-width: 0; }
.studio-grid.st-list .studio-card-del { margin-top: 0; }
.studio-grid.st-list .studio-card-body {
  grid-area: body;
  flex-direction: row; align-items: center; flex-wrap: nowrap; gap: 8px;
}
/* The preview strip is what makes a card TALL. In a row it flattens to inline
   text so the delete button and the running progress bar it carries stay put —
   hiding the block would have hidden them with it. */
.studio-grid.st-list .studio-card-preview {
  background: transparent; border: none; border-radius: 0; padding: 0;
  gap: 8px; flex-wrap: nowrap; overflow: hidden;
}
.studio-grid.st-list .studio-card-preview > span {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.studio-grid.st-list .studio-dur-badge { display: none; }
.studio-grid.st-list .studio-card-progress { flex: 1 1 auto; min-width: 150px; }
.studio-grid.st-list .studio-card-actions { flex-wrap: nowrap; }
/* THE DENSE ROW NEEDS ROOM. Measured on the real card: the name, the meta line
   and the action cluster only stop fighting once the CARD is about 1100px wide —
   with the 220px sidebar that is a ~1360px viewport. Below it the name column
   collapsed to a few pixels and wrapped ONE CHARACTER PER LINE (card 388px tall,
   name 9px wide at 860px), which is what a tablet or a half-width window saw.
   So: two columns here (name | meta on the first line, the actions on their own),
   then one column on a phone. */
@media (max-width: 1360px) {
  .studio-grid.st-list .studio-card {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: "head meta" "body body";
    row-gap: 8px;
  }
  /* The actions may take the second line, so they are allowed to wrap there. */
  .studio-grid.st-list .studio-card-body { flex-wrap: wrap; }
  .studio-grid.st-list .studio-card-actions { flex-wrap: wrap; }
}
@media (max-width: 760px) {
  /* One column again, in reading order: name, meta, then the actions. */
  .studio-grid.st-list .studio-card {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "head" "meta" "body";
    gap: 6px;
  }
  .studio-grid.st-list .studio-card-body { flex-wrap: wrap; }
  .studio-grid.st-list .studio-card-preview > span { white-space: normal; }
}

/* Running-state progress */
.studio-card-progress { display: flex; flex-direction: column; gap: 6px; }.studio-progress-head { display: flex; justify-content: space-between; font-size: 12px; color: var(--text-2, #334155); }
.studio-progress-head b { color: #f59e0b; }
.studio-progress-fill { background: linear-gradient(90deg, #f59e0b, #f97316); }
.studio-progress-sub { font-size: 11px; color: var(--text-3, #64748b); }

/* ---- Card activity: "what this project is doing right now" ----------------
   ONE line per running operation (upload / download / transcribe / translate /
   clips / export): an animated ring, the name of the operation, the % when the
   server knows it, and a slim track underneath. Painted by `stActHtml` in
   studio.js on EVERY card, in BOTH views (a list is a layout, not a second
   renderer) — deliberately soft, because a card can carry this for minutes and
   it sits next to the real actions. */
.st-act {
  display: flex; flex-direction: column; gap: 8px;
  padding: 9px 12px 10px;
  border-radius: 12px;
  background: linear-gradient(180deg, #f7f8ff, #f2f5fb);
  border: 1px solid #e4e8f5;
}
.st-act[hidden] { display: none !important; }
.st-act-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "ring label pct" "track track track";
  align-items: center;
  column-gap: 9px;
  row-gap: 7px;
}
.st-act-ring {
  grid-area: ring;
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid rgba(99, 102, 241, .22);
  border-top-color: #6366f1;
  animation: st-act-spin .85s linear infinite;
}
.st-act-label {
  grid-area: label;
  font-size: 12.5px; font-weight: 700; color: #3730a3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  letter-spacing: .1px;
}
.st-act-pct {
  grid-area: pct;
  font-size: 12px; font-weight: 800; color: #4f46e5;
  font-variant-numeric: tabular-nums; min-width: 30px; text-align: end;
}
.st-act-track {
  grid-area: track;
  display: block; height: 5px; border-radius: 99px;
  background: #e2e8f7; overflow: hidden;
}
.st-act-fill {
  display: block; height: 100%; width: 0; border-radius: 99px;
  background: linear-gradient(90deg, #6366f1, #8b5cf6 60%, #a855f7);
  transition: width .55s ease;
  position: relative; overflow: hidden;
}
/* The sheen is what makes it read as "moving", not "stuck at 42%". */
.st-act-fill::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .65), transparent);
  animation: st-act-sheen 1.5s ease-in-out infinite;
}
/* No percentage known (queued, or the server has not reported one yet): the
   bar slides instead of lying with "0%". */
.st-act-row.is-indet .st-act-fill {
  animation: st-act-slide 1.5s ease-in-out infinite;
  transition: none;
}
.st-act-more { font-size: 11px; font-weight: 600; color: #6d28d9; opacity: .85; }

@keyframes st-act-spin { to { transform: rotate(360deg); } }
@keyframes st-act-sheen {
  0% { transform: translateX(-100%); }
  60%, 100% { transform: translateX(100%); }
}
@keyframes st-act-slide {
  0% { transform: translateX(-110%); }
  100% { transform: translateX(320%); }
}
@media (prefers-reduced-motion: reduce) {
  .st-act-ring, .st-act-fill::after, .st-act-row.is-indet .st-act-fill { animation: none; }
  .st-act-fill { transition: none; }
}

/* In a dense LIST ROW the same block is one inline pill: ring + name + %. The
   track would add a second line to every row, which is the density a list exists
   to avoid — the % already carries the number. Only the first operation fits;
   the rest are named on the card once it is opened. */
.studio-grid.st-list .st-act {
  flex: 0 1 auto; min-width: 0; width: 235px;
  flex-direction: row; align-items: center;
  padding: 4px 11px; border-radius: 99px;
  background: #f4f6ff; border-color: #e2e7f8;
}
.studio-grid.st-list .st-act-row {
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "ring label pct";
  row-gap: 0; column-gap: 7px;
}
.studio-grid.st-list .st-act-row ~ .st-act-row,
.studio-grid.st-list .st-act-track,
.studio-grid.st-list .st-act-more { display: none; }
@media (max-width: 760px) {
  .studio-grid.st-list .st-act { width: auto; flex: 1 1 auto; }
}

/* ---- Queue view rows (reuses legacy card/badge classes) ---- */
.st-project-card {
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px;
  border: 1px solid var(--border, #e2e8f0); border-radius: 12px;
  padding: 14px 16px; background: var(--card, #fff);
  margin-bottom: 10px;
  min-width: 0;
}
.st-project-card > * { min-width: 0; }
.st-project-card .st-p-status {
  font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: 99px;
}
.st-badge-draft { background: #e2e8f0; color: #334155; }
.st-badge-processing { background: #fef3c7; color: #b45309; }
.st-badge-completed { background: #dcfce7; color: #166534; }
.st-badge-failed { background: #fee2e2; color: #991b1b; }
.st-badge-queued { background: #e2e8f0; color: #334155; }

/* Queue row selection: a tick box on the left, the "N selected / Delete" bar in
   the header, and a soft ring on the rows that are ticked. */
.st-q-check { display: inline-flex; align-items: center; flex: none; cursor: pointer; }
.st-q-check input { width: 17px; height: 17px; cursor: pointer; }
.st-project-card.st-q-sel {
  border-color: var(--st-accent, #6366f1);
  background: #f7f8ff;
  box-shadow: 0 0 0 2px rgba(99, 102, 241, .12);
}
.st-queue-selbar {
  align-items: center; gap: 8px;
  background: #f4f6fb; border: 1px solid #e3e8ef; border-radius: 999px;
  padding: 3px 6px 3px 12px; font-size: 12.5px; font-weight: 600; color: #475569;
}
.st-queue-selbar .st-btn { height: 28px; padding: 0 10px; font-size: 12px; }

/* responsive */
@media (max-width: 720px) {
  .st-grid { grid-template-columns: 1fr 1fr; }
  /* Workflow steps scroll horizontally on mobile (mirrors Photo Studio). */
  .st-flowchart.st-flow .st-steprow {
    justify-content: flex-start;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    flex-wrap: nowrap;
    width: 100%;
  }
  .st-flowchart.st-flow .st-steprow .st-step,
  .st-flowchart.st-flow .st-steprow .st-arrow { flex: 0 0 auto; }
  .st-srt-wrap { flex-direction: column; }
  .studio-grid { grid-template-columns: minmax(0, 1fr); }
  .st-toolbar { flex-direction: column; align-items: stretch; }
  .st-toolbar-actions { justify-content: center; }
  /* One full-width field on its own row; `max-width: none` because the desktop
     cap left a gap the buttons then had to justify around. */
  .st-search { width: 100%; max-width: none; flex: 1 1 100%; }
}

/* Tiny/narrow screens: each form field gets a full row so wide controls (file
   pickers, "Animation" select) fill the frame instead of spilling out to the
   right or splitting a field across two cramped columns. */
@media (max-width: 560px) {
  .st-grid { grid-template-columns: 1fr; }
  .st-panel { padding: 14px; }
}

/* ---- Export confirmation preview modal (replaces native confirm) ---- */
.st-confirm-overlay {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(8, 15, 30, .55);
  display: flex; align-items: center; justify-content: center;
  padding: 12px; backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.st-confirm-card {
  background: #fff; color: #0f172a;
  width: 100%; max-width: 560px; max-height: 92vh;
  border-radius: 16px; overflow: hidden;
  display: flex; flex-direction: column;
  box-shadow: 0 24px 60px rgba(2, 6, 23, .4);
}
.st-confirm-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid #eef2f6;
  font-size: 15px; font-weight: 800; color: #0f172a;
}
.st-confirm-x {
  border: none; background: transparent; cursor: pointer;
  font-size: 15px; line-height: 1; color: #64748b; padding: 4px 6px; border-radius: 6px;
}
.st-confirm-x:hover { background: #f1f5f9; color: #0f172a; }
.st-confirm-body { padding: 12px 16px; overflow-y: auto; }
.st-confirm-prev {
  background: #000; border-radius: 12px; overflow: hidden;
  border: 1px solid #0f172a; position: relative;
}
.st-confirm-prev video { width: 100%; max-height: 52vh; object-fit: contain; display: block; background: #000; border-radius: 12px; }
.st-confirm-durrow { margin: 8px 0 2px; font-size: 12.5px; font-weight: 700; color: #334155; }
.st-confirm-summary { margin: 4px 0 8px; }
.st-confirm-foot {
  display: flex; justify-content: flex-end; gap: 10px; padding: 10px 16px;
  border-top: 1px solid #eef2f6; background: #fafbfc;
}

/* ---- Sticky "▲ Preview" jump button (mobile Video Studio) ----
   Appears only on phones while the active step's preview has scrolled out of
   view; sits top-right (below the app's top bar), orange; tapping it jumps
   back up to the preview. */
.pe-prevjump {
  display:none;
  position: fixed;
  top: calc(70px + env(safe-area-inset-top, 0px));
  right: 12px;
  z-index: 120;
  align-items: center;
  gap: 6px;
  padding: 9px 15px;
  border-radius: 999px;
  border: 1px solid #ea6a0c;
  background: linear-gradient(180deg, #fb923c, #f97316);
  color: #fff;
  font-size: 13px;
  font-weight: 800;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 5px 16px rgba(249,115,22,.4);
  white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
}
.pe-prevjump:hover { filter: brightness(1.05); }
.pe-prevjump .pe-prevjump-ico {
  font-size: 9px; line-height: 1; transform: translateY(-1px);
  display: inline-flex;
}
.pe-prevjump:active { transform: scale(.96); }

@media (min-width: 561px) {
  .pe-prevjump { display: none !important; }
}

/* =========================================================================
   Subtitle compare popup (Original vs Translated)
   Responsive: side-by-side on desktop, toggleable tabs / stacked on mobile.
   The modal panel is resizable from its bottom-right corner.
   ========================================================================= */
.st-cmp {
  position: relative;
  /* Desktop: a real side-by-side comparison needs room — wider and taller than
     the old 920x560 box. Still drag-resizable from the bottom-right corner. */
  width: min(1240px, 94vw);
  height: min(82vh, 840px);
  max-width: 100vw;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  overflow: auto;           /* enables `resize` + a native corner handle */
  resize: both;             /* drag bottom-right to resize the panel */
}
.st-cmp-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 8px; padding: 2px 0 8px;
}
.st-cmp-mode { display: flex; gap: 6px; align-items: center; }
/* BASE button style. Only `.st-cmp-mode button.st-cmp-btn` and the `.is-on`
   variants existed, so "▶ Play audio" (which only set a background) and
   "↺ From start" (which set nothing) rendered as raw browser buttons. This is
   the same language as `.st-btn`: same radius, weight, padding and hover. */
.st-cmp-btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--border, #e2e8f0);
  background: var(--bg, #fff); color: var(--text, #0f172a);
  padding: 7px 13px; border-radius: 9px;
  font-size: 12.5px; font-weight: 600; font-family: inherit;
  cursor: pointer; transition: all .15s ease; white-space: nowrap;
}
.st-cmp-btn:hover { background: var(--bg-2, #f1f5f9); }
.st-cmp-btn:active { transform: translateY(1px); }
.st-cmp-btn[disabled] { opacity: .55; cursor: default; }
.st-cmp-mode button.st-cmp-btn {
  border-radius: 999px; padding: 3px 10px; font-size: 12px;
}
.st-cmp-mode button.st-cmp-btn.is-on {
  border-color: #1b74bc; color: #1b74bc; background: #eaf3fb;
}
.st-cmp-canvas { flex: 1; min-height: 0; display: flex; gap: 10px; }
/* Two equal columns side by side (desktop & when layout=side) */
.st-cmp-col {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column;
  border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden;
}
.st-cmp-col.is-trg { border-color: #c7d2fe; }
.st-cmp-col > .st-cmp-colhead {
  padding: 6px 10px; font-weight: 700; font-size: 12px; color: #334155;
  background: #f8fafc; border-bottom: 1px solid #e2e8f0;
}
.st-cmp-col.is-trg > .st-cmp-colhead { color: #4338ca; background: #eef2ff; border-bottom-color: #c7d2fe; }
.st-cmp-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 0 6px 8px; }
.st-cmp-row { padding: 5px 4px; border-bottom: 1px solid #eef2f6; cursor: pointer; }
.st-cmp-row .st-cmp-rowhead {
  font-size: 9.5px; color: #94a3b8;
  font-family: ui-monospace, Menlo, Consolas, monospace; margin-bottom: 2px;
}
.st-cmp-row textarea {
  width: 100%; box-sizing: border-box; resize: vertical; min-width: 0;
  border: 1px solid #e2e8f0; border-radius: 6px; padding: 5px 7px;
  font-size: 12.5px; line-height: 1.35; background: #fff; cursor: text;
  /* The box is grown to its own text by growArea() in studio.js so the WHOLE
     caption of the time slot is visible. These are only the floor (two reading
     lines) and a generous ceiling so one runaway paragraph cannot push the
     whole popup off screen. */
  min-height: 38px; max-height: 46vh; overflow-y: auto;
}
.st-cmp-row.is-trg textarea { border-color: #c7d2fe; background: #f5f6ff; }

/* Mobile default: stacked (columns above one another). Tabs pick which is shown. */
.st-cmp.st-cmp-stacked .st-cmp-canvas { flex-direction: column; }
.st-cmp.st-cmp-stacked .st-cmp-col { width: 100%; flex: 1 1 0; }
.st-cmp .st-cmp-single { display: none; flex: 1 1 0; flex-direction: column; }
.st-cmp.st-cmp-stacked.st-cmp-tab-src .st-cmp-col.is-src,
.st-cmp.st-cmp-stacked.st-cmp-tab-trg .st-cmp-col.is-trg { display: flex; }
.st-cmp.st-cmp-stacked .st-cmp-col { display: none; }
.st-cmp.st-cmp-side .st-cmp-col { display: flex; }

@media (max-width: 600px) {
  /* A phone gets the whole screen. The panel was 90vw x 60vh, so the two caption
     columns were squeezed into a sliver under the player bar and the instruction
     lines. */
  /* ...but it must LEAVE ROOM for the modal head + padding. At `92vh` the card
     (head + panel + padding) was taller than the viewport, so the Approve/Cancel
     footer sat BELOW the screen where no scroll could reach it. Height is the
     viewport MINUS that chrome, and the panel does not scroll itself (the columns
     do), so the footer is always on screen. `dvh` handles the mobile URL bar;
     `vh` is the fallback. */
  .st-cmp { width: 100%; height: calc(100vh - 104px); height: calc(100dvh - 104px); overflow: hidden; }
  .st-cmp-foot { flex: none; }
  /* LESS INSTRUCTION ON A PHONE: the long "compare each caption…" hint and the
     "press play…" note are hidden so the columns get the height instead. */
  .st-cmp .st-cmp-hint { display: none; }
  .st-cmp-now { display: none; }
  .st-cmp-toolbar { padding: 2px 0 6px; justify-content: center; }
  .st-cmp-playerbar { padding: 4px 2px 6px; margin-bottom: 4px; gap: 8px; }
  .st-cmp-video { width: 100%; height: 84px; }
  .st-cmp-playbtn { width: 100%; }
  .st-cmp-foot { flex-wrap: wrap; }
  .st-cmp-foot .st-btn { flex: 1 1 auto; justify-content: center; }
}

.st-cmp-foot {
  display: flex; justify-content: flex-end; gap: 10px;
  padding-top: 12px;
}
.st-cmp .st-cmp-hint { margin: 0 0 8px; color: #475569; font-size: 13px; line-height: 1.5; }
/* Tabs are a stacked/mobile concept: hide them when both columns show side by side. */
.st-cmp-tabs { display: none; gap: 6px; margin: 0 0 8px; }
.st-cmp.st-cmp-side .st-cmp-tabs { display: none; }
.st-cmp.st-cmp-stacked .st-cmp-tabs { display: flex; }
.st-cmp-tabs .st-cmp-btn.is-on { border-color: #1b74bc; color: #1b74bc; background: #eaf3fb; }

/* ---------------------------------------------------------------------------
   Subtitle compare: audio/video sync player + "now speaking" readout
   --------------------------------------------------------------------------- */
.st-cmp-playerbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 8px 4px 10px; border-bottom: 1px solid #eef2f6; margin-bottom: 6px;
}
/* Big enough to actually scrub, and matched to the card radius. */
.st-cmp-video {
  width: 260px; height: 96px; border-radius: 10px;
  background: #000; object-fit: contain; border: 1px solid var(--border, #e2e8f0);
}
.st-cmp-playbtn { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.st-cmp-btn.st-primary-btn {
  background: var(--st-accent, #1b74bc); color: #fff;
  border-color: var(--st-accent, #1b74bc);
}
.st-cmp-btn.st-primary-btn:hover {
  background: var(--st-accent, #1b74bc); filter: brightness(1.08);
}
.st-cmp-now { flex: 1 1 200px; min-width: 0; font-size: 11.5px; color: #64748b; line-height:1.4; }
.st-cmp-readout {
  border: 1px dashed #c7d2fe; background: #eef2ff; border-radius: 10px;
  padding: 6px 12px; margin: 0 0 8px;
}
.st-cmp-readlabel {
  display: block; font-size: 9px; font-weight: 800; letter-spacing: .12em;
  color: #4338ca; margin-bottom: 2px;
}
.st-cmp-readmain { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: 14px; }
.st-cmp-read-src { color:#334155; font-weight:600; }
.st-cmp-read-arrow { color:#94a3b8; }
.st-cmp-read-trg { color:#4338ca; font-weight:700; }
/* Active (currently-spoken) caption row highlight in both columns. */
.st-cmp-row.st-cmp-active { background:#fff7df; border-left:3px solid #f59e0b; border-radius:4px; margin:1px 0; }
.st-cmp-row.st-cmp-active.is-trg { background:#f3efff; border-left-color:#6366f1; }
@media (max-width: 480px) {
  .st-cmp-video { width: 120px; height: 48px; }
  .st-cmp-readmain { font-size: 12.5px; }
}



/* ---------------------------------------------------------------------------
   Subtitles editor 2-col layout: CONTENT (main, left) | STYLING (side, right)
   --------------------------------------------------------------------------- */
.st-subs-cols {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  gap: 14px;
  align-items: stretch;
}
.st-subs-main {
  min-width: 0;
  /* THE CARD SPANS THE ROW, and the row is sized by the STYLING column alone.
     Two wrong answers came before this one. (a) Plain grid stretch: the card took
     the taller column's height while the cue list stayed capped at 56vh, leaving
     ~450px of dead white INSIDE the card (reported 2026-11-13 "this space was
     supposed to have been removed"). (b) `align-self: start`: no dead white, but
     the card stopped halfway down a column that went on for another 500px, which
     is the grey gap in the next report ("both cue and srt options should go all
     the way down to the row where Remove [Speaker N] tags is").

     So the card is pinned to this column with `position: absolute; inset: 0`. An
     absolutely-positioned child adds NO height, so the grid row is exactly as
     tall as the styling column and both columns end on the same line; the card
     fills that box and the cue list scrolls inside it, so nothing is left empty
     either. No measurement at all — it cannot depend on the scroll position the
     way the old `_fitSubsCols()` did. The floor keeps the list usable when the
     styling column is short. Reset to static in the single-column phone layout. */
  position: relative;
  min-height: 520px;
}
/* SRT editor fills the left column: toolbar/stat/find rows keep their natural
   height and the editor (or the cue list) takes everything that is left. */
.st-subs-main section.st-subs-content-box {
  position: absolute;
  inset: 0;
  margin: 0;
  display: flex; flex-direction: column;
  min-height: 0;
  overflow: hidden;
}
.st-subs-main #stSrtEditor {
  flex: 1 1 auto;
  min-height: 0;
  resize: vertical;
}

/* ---- The SRT box's CUE LIST (the same text, one row per caption) ----------
   The look Audio Studio's transcript / translation columns have: a time box
   and an editable line per cue, the row being spoken highlighted and scrolled
   to. The time box is a real button — pressing it PLAYS the live preview from
   that caption, which is the whole reason this view exists. */
.st-srt-viewrow {
  display: flex;
  justify-content: flex-end;
  margin: 0 0 8px;
}
.st-srt-view {
  display: inline-flex;
  border: 1px solid #e2e8f0;
  border-radius: 999px;
  overflow: hidden;
  background: #fff;
}
.st-srt-view-btn {
  border: 0;
  background: #fff;
  color: #64748b;
  font-family: inherit;
  font-size: 12px;
  font-weight: 700;
  padding: 5px 11px;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.st-srt-view-btn:hover { background: #f1f5f9; }
.st-srt-view-btn.is-on { background: var(--st-accent, #1b74bc); color: #fff; }

/* ---- "Original / Current" — which wording the download buttons write ------
   Shown only while an APPLIED translation exists (it replaces the SRT text, so
   without this the pre-translation wording would be unreachable). Same pill
   idiom as the cues/SRT switch above it. */
.st-srt-ver {
  display: inline-flex;
  border: 1px solid #e2e8f0;
  border-radius: 999px;
  overflow: hidden;
  background: #fff;
}
.st-srt-ver button {
  border: 0;
  background: #fff;
  color: #64748b;
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 700;
  padding: 6px 11px;
  cursor: pointer;
}
.st-srt-ver button:hover { background: #f1f5f9; }
.st-srt-ver button.is-on { background: var(--st-accent, #1b74bc); color: #fff; }

.st-srt-wrap {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}
/* Exactly one view at a time; the raw box keeps its own id so every existing
   reader (`$("stSrtEditor").value`) is untouched. */
.st-srt-wrap[data-view="list"] #stSrtEditor { display: none; }
.st-srt-wrap[data-view="raw"] .st-srt-cues { display: none; }
/* In the LIST view the cues take the column's spare height, so the card lines up
   with the styling column beside it (the textarea's own rule no longer applies). */
.st-srt-wrap[data-view="list"] .st-srt-cues {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.st-srt-cues {
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  overflow: hidden;
  background: #fff;
}
.st-srt-cue-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 10px 6px 12px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #64748b;
  background: #f8fafc;
  border-bottom: 1px solid #eef2f6;
}
.st-srt-cue-hint {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: #94a3b8;
  text-align: end;
}
.st-srt-cue-scroll {
  overflow-y: auto;
  padding: 4px;
  min-height: 180px;
  max-height: 56vh;
}
/* The cue list takes ALL of the card's spare height, so it reaches the bottom
   edge the user pointed at (the styling column's last row). The card's height is
   fixed by the grid row, so there is no content-driven growth to cap. */
.st-srt-wrap[data-view="list"] .st-srt-cue-scroll {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}
.st-srt-cue {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 8px;
  transition: background .12s ease;
}
.st-srt-cue:hover { background: #f8fafc; }
.st-srt-cue.is-on {
  background: #e0f2fe;
  box-shadow: inset 3px 0 0 #0ea5e9;
}
/* The row Find/Replace is currently pointing at, in the 🎬 Cues view. Amber, so
   it cannot be confused with the blue "playing" row (.is-on). */
.st-srt-cue.is-hit {
  background: #fef9c3;
  box-shadow: inset 3px 0 0 #eab308;
}
.st-srt-cue.is-hit .st-srt-cue-text { background: #fef9c3; }
.st-srt-cue-time {
  flex: none;
  margin-top: 1px;
  border: 1px solid #e2e8f0;
  border-radius: 7px;
  background: #f8fafc;
  color: #0369a1;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 700;
  padding: 1px 6px;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.st-srt-cue-time:hover { background: #e0f2fe; border-color: #7dd3fc; }
/* ✨ Clean up speech: a real spinner while the server runs silencedetect, so a
   5–30 s wait reads as work instead of a frozen button. */
.st-btn.is-busy {
  opacity: .9;
  cursor: progress;
}
.st-btn.is-busy::before {
  content: "";
  display: inline-block;
  width: 11px;
  height: 11px;
  margin-right: 6px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  vertical-align: -1px;
  animation: st-busy-spin .7s linear infinite;
}
@keyframes st-busy-spin { to { transform: rotate(360deg); } }
/* Multi-speaker badge: the speaker's OWN colours (set inline), so the list reads
   like the video will. Metadata only — the export drops the `[Speaker N]` text. */
.st-srt-cue-spk {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 20px;
  margin-top: 1px;
  padding: 0 5px;
  border-radius: 6px;
  border: 1px solid rgba(15, 23, 42, .18);
  font-size: 10px;
  line-height: 1;
  letter-spacing: .02em;
  box-sizing: border-box;
}
.st-srt-cue-text {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  line-height: 1.5;
  color: #2c3e50;
  white-space: pre-line;
  word-break: break-word;
  border-radius: 6px;
  padding: 2px 4px;
  outline: none;
  transition: background .12s ease, box-shadow .12s ease;
}
.st-srt-cue-text:hover { background: #f8fafc; box-shadow: inset 0 0 0 1px #e2e8f0; }
.st-srt-cue-text:focus { background: #fff; box-shadow: inset 0 0 0 2px #38bdf8; }
.st-srt-cue-empty { padding: 12px; color: #94a3b8; font-size: 12.5px; }
@media (max-width: 760px) {
  /* On a phone the list should not be a fixed window inside the page. */
  .st-srt-cue-scroll { max-height: none; }
  .st-srt-cue-hint { display: none; }
}
.st-subs-side {
  min-width: 0;
  display: flex; flex-direction: column; gap: 12px;
  position: sticky; top: 8px;
}
/* Give the SRT text a nice, clearly-primary editor feel inside main. */
.st-subs-main section { border-radius: 14px; }
.st-subs-main #stSrtEditor { line-height: 1.5; }
.st-subs-side .st-subs-card { box-shadow: 0 0 0 1px rgba(226,232,240,.4); }

@media (max-width: 1100px) {
  .st-subs-cols { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
@media (max-width: 760px) {
  .st-subs-cols { grid-template-columns: 1fr; }
  .st-subs-side { position: static; }
  /* On mobile the editor should size to its content, not stretch full height. */
  .st-subs-main #stSrtEditor { flex: none; min-height: 180px; }
  /* ONE COLUMN = NO ROW TO SPAN. The card is pinned to the left column with
     `position: absolute` on desktop so the two columns end together; here that
     would collapse it to zero height, so it goes back into the flow. */
  .st-subs-main { position: static; min-height: 0; }
  .st-subs-main section.st-subs-content-box {
    position: static; inset: auto; overflow: visible;
  }
}

/* ---------------------------------------------------------------------------
   Font size: slider + exact number box side by side.
   The number box is a fixed width so the row can never push past the phone's
   width; below 560px the slider takes a full row and the box sits under it,
   because sharing one ~160px column left almost no usable track.
   --------------------------------------------------------------------------- */
.st-subs-size {
  display: flex; align-items: center; gap: 8px; min-width: 0;
}
/* border-box, or `width:100%` on the phone would come out 10px wider than its
   own container (padding + border added on top). Measured in a real browser. */
.st-subs-size input { box-sizing: border-box; }
.st-subs-size input[type="range"] {
  flex: 1 1 auto; min-width: 0;
}
.st-subs-size input[type="number"] {
  flex: 0 0 auto; width: 68px; text-align: center;
  padding: 7px 4px; border: 1px solid #e2e8f0; border-radius: 9px;
  font-size: 13px; font-weight: 700; background: #fff; color: #2c3e50;
}
.st-subs-size input[type="number"]:focus {
  outline: none; border-color: var(--st-accent, #1b74bc);
  box-shadow: 0 0 0 3px rgba(27, 116, 188, .15);
}
@media (max-width: 560px) {
  .st-subs-size { flex-wrap: wrap; }
  .st-subs-size input[type="range"] { flex: 1 1 100%; }
  .st-subs-size input[type="number"] { width: 100%; }
}

/* ---- Shared grid/list + multi-select (window.SSViews in shell.js) ----------
   ONE implementation of the ▦/☰ toggle, the "N selected" bar and the row
   checkboxes, used by every studio that lists jobs or projects. A studio opts in
   by putting the `st-list` class on its own container in list mode and calling
   SSViews.cb() once per row — the list is a LAYOUT of the same cards, never a
   second renderer, so the two views cannot drift apart in what they show. */
.ssv-toggle { display: inline-flex; gap: 4px; margin-inline-start: auto; }
.ssv-view {
  font-size: 13px; line-height: 1; padding: 5px 9px; border-radius: 8px;
  border: 1px solid var(--border, #e2e8f0); background: #fff;
  color: var(--text-2, #475569); cursor: pointer;
}
.ssv-view:hover { border-color: var(--brand-500, #6366f1); color: var(--brand-600, #4f46e5); }
.ssv-view.on {
  background: var(--brand-500, #6366f1); border-color: var(--brand-500, #6366f1);
  color: #fff;
}
/* THE BULK BAR IS A PILL AT THE BOTTOM OF THE SCREEN, not a strip under the
   toolbar. Asked 2026-10-25: *"when i tick one or more box - add a delete button
   on a sticky pill at the bottom of the screen"*. It used to sit between the
   toolbar and the cards, so a tick made three rows down put its own action
   off-screen ABOVE — which reads exactly like "there is no multi-delete".

   `position: sticky` and not `fixed`: it stays in flow, so it never covers the
   last row and needs no spacer. The Media Library's `.media-bulk-bar` taught us
   the two silent ways sticky dies (see /memories/repo/sticky-bulk-bar-*):
     1. an `overflow: hidden` ancestor becomes the scrollport and never scrolls —
        so the bar must not be inside a clipping card; and
     2. a containing block with no room to travel — so the bar is the LAST child
        of the list container it belongs to, which is what lets it hold itself
        14px above the bottom edge for the whole length of a long list. */
.ssv-bar {
  position: sticky; bottom: 14px; z-index: 22;
  display: none; align-items: center; gap: 10px; flex-wrap: wrap;
  /* A PILL, not a full-width strip: as wide as its own contents and centred, so
     it reads as a floating action and leaves the rows beside it visible. */
  width: -webkit-fit-content; width: -moz-fit-content; width: fit-content;
  max-width: 100%;
  margin: 10px auto 4px; padding: 9px 14px; border-radius: 999px;
  background: rgba(255, 255, 255, .97);
  -webkit-backdrop-filter: saturate(160%) blur(10px);
  backdrop-filter: saturate(160%) blur(10px);
  border: 1px solid var(--border, #e2e8f0);
  box-shadow: 0 14px 34px rgba(15, 23, 42, .20);
}
/* The element is hidden with an INLINE display:none that paintBar overrides; this
   rule is the belt for any future markup that uses the `hidden` attribute, where
   our own `display:flex` would otherwise defeat the browser's [hidden] rule. */
.ssv-bar[hidden] { display: none; }
.ssv-count { font-size: 12.5px; font-weight: 700; color: var(--brand-600, #4f46e5); }
.ssv-all, .ssv-cb {
  width: 16px; height: 16px; flex: 0 0 auto; cursor: pointer;
  accent-color: var(--brand-500, #6366f1);
}
.ssv-cb { align-self: center; margin: 0 2px 0 0; }
.ssv-del {
  background: var(--danger, #dc2626); border-color: var(--danger, #dc2626);
  color: #fff; font-weight: 700;
}
.ssv-del:hover { background: #b91c1c; border-color: #b91c1c; color: #fff; }
/* Phones: clear the fixed bottom tab bar (56px + its safe-area inset) so the two
   never stack, and give the delete press a 44px target. The pill may use the full
   width here — a compact one would wrap into three lines. */
@media (max-width: 640px) {
  .ssv-bar {
    bottom: calc(72px + env(safe-area-inset-bottom, 0px));
    width: auto; gap: 8px; padding: 7px 10px; border-radius: 16px;
    margin: 10px 0 4px;
  }
  .ssv-bar .st-btn { min-height: 44px; padding: 0 12px; }
  .ssv-count { margin-inline-end: auto; }
}

/* Anything that REMOVES something (“Delete”, “Remove”, “Clear all”, “Stop
   processing”) is red. `st-danger` is the app's older red-on-white chip — kept
   for the buttons that only explain a failure (“Why failed”) — while `st-del` is
   the filled red a delete action should look like. */
.st-btn.st-del {
  background: var(--danger, #dc2626); border-color: var(--danger, #dc2626);
  color: #fff; font-weight: 700;
}
.st-btn.st-del:hover { background: #b91c1c; border-color: #b91c1c; color: #fff; }

/* ---- Branding step: pinned preview + tabs (2026-11-10) --------------------
   The step held BOTH marks at once — each with type, upload, colour,
   typography, effects, background and placement — plus the template shelf, so
   it was a wall nobody could find a control in. The live preview is now the
   first element of the step and stays PINNED while the controls scroll, and the
   controls are split into Branding / Watermark / Templates tabs.

   `position: sticky` has to beat `.st-wiz-body .st-video-top { position:
   static }` (0,2,0), hence the second selector. The phone rule on
   `.st-vidtop` (max-width: 560px) still wins with !important: on a small screen
   a pinned preview would leave almost no room to edit.

   `top` is the HEIGHT OF `.topbar` (design.css: 62px, 56px on a phone), not 0:
   the bar is `position: sticky; top: 0; z-index: 40`, so a stage pinned at 0
   slid underneath it and only its bottom half stayed visible (measured on the
   live page). Inside the wizard the body is its own scrollport under the popup's
   own header, so 0 is correct there. */
.st-brand-pin,
.st-brand-pin.is-preview-wait { position: sticky; top: var(--st-topbar-h, 62px); z-index: 7; }
.st-wiz-body .st-brand-pin,
.st-wiz-body .st-brand-pin.is-preview-wait { position: sticky; top: 0; z-index: 7; }
@media (max-width: 600px) {
  .st-brand-pin { top: var(--st-topbar-h, 56px); }
}

/* A pinned stage must not eat the panel. The video is `object-fit: contain`, so
   capping the box letterboxes it instead of cropping the picture. */
.st-brand-pin #stBrandPreview,
.st-brand-pin .st-preview,
.st-brand-pin .st-preview video,
.st-brand-pin .st-preview img { max-height: min(38vh, 320px); }
/* The wizard popup has its own (smaller) scrollport under its own header, so the
   pinned stage gets a tighter cap there. */
.st-wiz-body .st-brand-pin #stBrandPreview,
.st-wiz-body .st-brand-pin .st-preview,
.st-wiz-body .st-brand-pin .st-preview video,
.st-wiz-body .st-brand-pin .st-preview img { max-height: min(28vh, 210px); }

/* The one placement hint, directly under the preview it describes. */
.st-brand-draghint {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 0 0 12px; font-size: 12.5px; line-height: 1.5;
  color: var(--text-3, #64748b);
}
.st-brand-draghint > span:first-child { flex: none; font-size: 15px; }

/* ---- Tabs ---------------------------------------------------------------- */
.st-tabs {
  display: flex; gap: 6px; flex-wrap: wrap;
  margin: 0 0 12px; padding: 5px;
  background: #f1f5f9; border: 1px solid var(--border, #e2e8f0);
  border-radius: 14px;
}
.st-tab {
  appearance: none; border: none; background: transparent; cursor: pointer;
  font: inherit; font-size: 13.5px; font-weight: 700; color: #475569;
  padding: 9px 14px; border-radius: 10px;
  display: inline-flex; align-items: center; gap: 6px; min-width: 0;
}
.st-tab:hover { background: #e2e8f0; color: #1e293b; }
.st-tab.is-on {
  background: #fff; color: var(--brand-600, #1b74bc);
  box-shadow: 0 1px 3px rgba(15, 23, 42, .12);
}
/* The green dot: this mark is switched ON (so a user on the Branding tab can see
   the watermark is live without opening its tab). */
.st-tab-live {
  width: 7px; height: 7px; border-radius: 50%; background: #16a34a;
  display: inline-block; flex: none;
}
.st-tabpanel[hidden] { display: none !important; }
@media (max-width: 560px) {
  .st-tabs { gap: 4px; padding: 4px; }
  .st-tab { flex: 1 1 0; justify-content: center; padding: 9px 8px; font-size: 12.5px; }
}

/* ---- Segmented control (Text / Logo / Both) ------------------------------
   Replaces three stacked cards: same choice, a fraction of the height. */
.st-seg {
  display: flex; gap: 4px; padding: 4px;
  background: #f1f5f9; border: 1px solid var(--border, #e2e8f0);
  border-radius: 12px;
}
.st-segbtn {
  flex: 1 1 0; min-width: 0;
  appearance: none; border: none; background: transparent; cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 700; color: #475569;
  padding: 9px 6px; border-radius: 9px;
  /* Wrap, do not ellipsize: "Text + Logo" has to stay readable in a third of a
     phone's width (it was clipped to "Text + L…" in a 390px measure). */
  white-space: normal; overflow: hidden; line-height: 1.15;
}
.st-segbtn:hover { background: #e2e8f0; }
.st-segbtn.is-on {
  background: linear-gradient(180deg, #1b74bc, #2f8fd6); color: #fff;
  box-shadow: 0 1px 3px rgba(27, 116, 188, .35);
}
@media (max-width: 420px) {
  .st-segbtn { font-size: 12px; padding: 9px 4px; }
}

/* ---- Templates as a GRID (was a chip scroller) --------------------------- */
.st-tplgrid {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
@media (min-width: 820px) { .st-tplgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 560px) { .st-tplgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
