/* filmforge design system.

   Dark by default because the content is the subject - every colour decision
   here is made so a frame of someone's video is the brightest, most saturated
   thing on screen and the interface recedes behind it.

   Everything routes through CSS custom properties so the theme, accent and
   density are genuinely switchable at runtime rather than a rebuild. */

:root {
  /* Elevation: four steps, no more. Tools that use six end up with panels you
     cannot tell apart. */
  --bg-base:    #0a0a0c;
  --bg-raised:  #121216;
  --bg-overlay: #1a1a20;
  --bg-hover:   #22222a;

  --line:        #272730;
  --line-strong: #383843;

  --ink:      #f2f0ed;
  --ink-dim:  #ada79f;
  /* 4.3:1 was scraping the floor and read as unfinished on a near-black
     surface. This clears 5.5:1, which is the difference between "muted" and
     "greyed out". */
  --ink-mute: #837d75;

  /* Warm amber: the colour of tungsten light and film leader. It reads as
     "film" without a single skeuomorphic texture, and it sits far enough from
     skin tones not to fight the content. */
  --accent:      #e8a33d;
  --accent-soft: rgba(232, 163, 61, 0.14);
  --accent-line: rgba(232, 163, 61, 0.42);
  /* Near-black on the accent in BOTH themes, never white. Every accent
     offered is a mid-to-light tone: white on the amber default is 1.86:1,
     which is unreadable, while near-black on it is 11.3:1. The light theme
     used to flip this to white and made its own primary button illegible. */
  --accent-ink:  #1a1206;

  --danger:      #ff6b5a;
  --danger-soft: rgba(255, 107, 90, 0.12);
  --good:        #5ad48a;

  --radius:    12px;
  --radius-sm: 8px;
  --radius-lg: 18px;

  /* Density. Compact scales these down; nothing else changes. */
  --gap: 20px;
  --pad: 20px;

  --shadow:    0 18px 44px rgba(0, 0, 0, 0.5);
  --shadow-sm: 0 4px 14px rgba(0, 0, 0, 0.35);

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto,
          "Helvetica Neue", sans-serif;
  /* Technical values - durations, resolutions, credit counts - are tabular so
     they stop jittering as they update. */
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

:root[data-density="compact"] { --gap: 13px; --pad: 14px; }

:root[data-theme="light"] {
  --bg-base:    #f6f5f3;
  --bg-raised:  #ffffff;
  --bg-overlay: #f0eeea;
  --bg-hover:   #e7e4de;
  --line:        #ddd9d2;
  --line-strong: #c6c1b8;
  --ink:      #1a1815;
  --ink-dim:  #55504a;
  --ink-mute: #857f77;
  --shadow:    0 18px 44px rgba(0, 0, 0, 0.12);
  --shadow-sm: 0 4px 14px rgba(0, 0, 0, 0.08);
}

* { box-sizing: border-box; }

/* An author `display` rule outranks the UA stylesheet's [hidden] rule, so any
   element given `display:flex` here would ignore its own hidden attribute.
   That is how the sign-in dialog ended up covering the page on first load. */
[hidden] { display: none !important; }

html, body {
  margin: 0; padding: 0;
  background: var(--bg-base); color: var(--ink);
  font-family: var(--font); font-size: 15px; line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  /* iOS Safari inflates text it deems "primary" by up to ~150%, which blew
     the Create cards wider than the screen on iPhone - the cut-off layout in
     the 2026-08-09 phone screenshots. Declaring 100% turns the heuristic off. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
body.is-locked { overflow: hidden; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -0.01em; }
h1 { font-size: 1.55rem; }
h2 { font-size: 1.12rem; }
h3 { font-size: 0.95rem; }

.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* Motion is for orientation only: things entering, leaving, or changing
   state. Never anything that delays a click.

   The reduced-motion rule deliberately stops short of killing everything. The
   spec's concern is change in perceived size, shape or position; colour and
   opacity are not motion, and stripping them removes the feedback that tells
   someone their click registered. Progress keeps moving because it is status,
   not decoration - a frozen bar reads as a hung render. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  .look, .btn, .primary, .ghost, .danger, .chip, .format-btn, .tab, .dropzone {
    transition-property: background, border-color, color, opacity, outline-color !important;
    transition-duration: 120ms !important;
  }
  .bar > i { transition-duration: 200ms !important; }
}

/* ---------------------------------------------------------------- chrome */

/* Founding-member ribbon, shared by the landing page and the app shell. */
.offer-ribbon {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 9px 16px; text-align: center; font-size: 0.83rem;
  background: linear-gradient(90deg, var(--accent), #b8471f);
  color: #1a1206; font-weight: 500;
}
.offer-ribbon .gift { font-size: 1rem; }
.offer-ribbon .link, .offer-ribbon a {
  color: #1a1206; text-decoration: underline; font-weight: 600;
  background: none; border: 0; font: inherit; cursor: pointer; padding: 0;
}
.offer-ribbon .offer-x {
  margin-left: 6px; background: none; border: 0; color: #1a1206;
  font-size: 1.15rem; line-height: 1; cursor: pointer; opacity: 0.7; padding: 2px 4px;
}
.offer-ribbon .offer-x:hover { opacity: 1; }
@media (max-width: 560px) { .offer-ribbon { font-size: 0.76rem; padding: 8px 12px; } }

.topbar-login { padding: 6px 14px; font-size: 0.86rem; }

.topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 16px;
  padding: 0 22px; height: 58px;
  background: color-mix(in srgb, var(--bg-base) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}

.brand {
  display: flex; align-items: center; gap: 9px; flex-shrink: 0;
  font-weight: 600; font-size: 1.02rem; letter-spacing: -0.02em; color: var(--ink);
}
.brand-mark {
  width: 22px; height: 22px; border-radius: 6px; position: relative; flex-shrink: 0;
  background: linear-gradient(150deg, var(--accent), #b8471f);
}
/* Two sprocket holes. The whole logo is a few lines of CSS and it says "film"
   faster than a wordmark would. */
.brand-mark::before, .brand-mark::after {
  content: ""; position: absolute; left: 3.5px;
  width: 4px; height: 5px; border-radius: 1.5px; background: var(--bg-base);
}
.brand-mark::before { top: 3px; }
.brand-mark::after  { bottom: 3px; }

.tabs { display: flex; gap: 2px; margin-left: 4px; }
.tab {
  appearance: none; border: 0; background: transparent; font: inherit;
  color: var(--ink-dim); font-size: 0.9rem;
  padding: 8px 13px; border-radius: var(--radius-sm); cursor: pointer;
  transition: color 0.14s var(--ease), background 0.14s var(--ease);
}
.tab:hover { color: var(--ink); background: var(--bg-raised); }
.tab.is-active { color: var(--ink); background: var(--bg-overlay); }

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }

.credit {
  font-size: 0.79rem; color: var(--ink-dim); white-space: nowrap;
  padding: 6px 12px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--bg-raised);
}
.credit.is-low { color: var(--danger); border-color: var(--danger); }

.avatar {
  width: 30px; height: 30px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--line); background: var(--bg-overlay);
  color: var(--ink-dim); font-size: 0.74rem; font-weight: 600;
  display: grid; place-items: center;
}
.avatar:hover { border-color: var(--line-strong); color: var(--ink); }

/* ---------------------------------------------------------------- layout */

main { max-width: 1380px; margin: 0 auto; padding: 26px 22px 80px; }

.panel { display: none; }
.panel.is-active { display: block; animation: fade 0.18s var(--ease); }
@keyframes fade { from { opacity: 0; transform: translateY(4px); } }

.page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 18px; margin-bottom: var(--gap); flex-wrap: wrap;
}
.page-head p { margin: 5px 0 0; color: var(--ink-dim); font-size: 0.88rem; }

/* Controls in a fixed rail on the left, the footage taking everything else.

   This was the other way round to begin with - options got the wide column and
   the video got 420px - which is backwards for a tool whose entire output is
   visual. Judging grain, weave and colour needs size; reading a checkbox label
   does not. The rail is 400px because a two-column slider row needs about that
   and no more. */
.workspace {
  display: grid; grid-template-columns: 400px minmax(0, 1fr);
  gap: 24px; align-items: start;
}
@media (max-width: 1080px) { .workspace { grid-template-columns: 1fr; } }

/* No nested scrolling. The rail used to be its own scroll container, and a
   panel that scrolls inside a page that also scrolls is a hidden-state
   problem no amount of scrollbar styling fixes - Fabio never found the Melt
   format because he did not know the column continued. The page scrolls now,
   the rail is just tall, and the only thing pinned is the render dock.

   The padding underneath is what stops the dock covering a control: it always
   floats over reserved empty space rather than over the settings above it. */
.controls { display: flex; flex-direction: column; gap: var(--gap); }
.rail-wrap { display: flex; flex-direction: column; gap: var(--gap); padding-bottom: 150px; }
.rail-scroll { display: flex; flex-direction: column; gap: var(--gap); }
.rail-scroll .card + .card { margin-top: 0; }

.render-dock {
  position: sticky; bottom: 14px; z-index: 5;
  box-shadow: var(--shadow); background: var(--bg-overlay);
}
.dock-note { margin: 10px 0 0; font-size: 0.76rem; color: var(--ink-mute); line-height: 1.45; }

@media (max-width: 1080px) {
  .rail-wrap { padding-bottom: 0; }
  .render-dock { position: static; box-shadow: none; }
}
/* Landscape phones / small tablets (761-1080px): single column, and the
   render dock goes LAST - after the preview and looks - so the page reads
   set-up first, commit last. display:contents lifts the dock out of the
   controls box so flex order can place it. */
@media (min-width: 761px) and (max-width: 1080px) {
  .workspace { display: flex; flex-direction: column; }
  .controls { display: contents; }
  .rail-wrap { order: 1; }
  .stage { order: 2; }
  .render-dock { order: 3; }
}

.grid-note { margin: -4px 0 12px; font-size: 0.78rem; color: var(--ink-mute); }
.grid-note b { color: var(--ink-dim); font-weight: 500; }

.card {
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--pad);
}
.card + .card { margin-top: var(--gap); }
.card > h2 { margin-bottom: 3px; }
.card > .hint { margin: 0 0 14px; color: var(--ink-dim); font-size: 0.84rem; }

.step-label {
  display: flex; align-items: center; gap: 9px; margin-bottom: 12px;
  font-size: 0.73rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--ink-mute);
}
.step-label b {
  width: 20px; height: 20px; border-radius: 50%; letter-spacing: 0;
  background: var(--bg-overlay); border: 1px solid var(--line);
  color: var(--ink-dim); font-size: 0.7rem; font-weight: 600;
  display: grid; place-items: center;
}
.step-label.is-done b { background: var(--accent); color: var(--accent-ink); border-color: transparent; }

/* -------------------------------------------------------------- dropzone */

.dropzone {
  border: 1.5px dashed var(--line-strong); border-radius: var(--radius);
  background: var(--bg-overlay); padding: 34px 22px;
  text-align: center; cursor: pointer;
  transition: border-color 0.16s var(--ease), background 0.16s var(--ease);
}
.dropzone:hover, .dropzone.is-dragging { border-color: var(--accent); background: var(--accent-soft); }
.dropzone.has-file { padding: 15px; text-align: left; cursor: default; border-style: solid; }
.drop-title { font-weight: 600; margin: 0 0 4px; }
.drop-sub { margin: 0; color: var(--ink-dim); font-size: 0.83rem; }

.file-row { display: flex; align-items: center; gap: 13px; }
.file-thumb {
  width: 78px; height: 52px; border-radius: var(--radius-sm); flex-shrink: 0;
  background: #000; object-fit: cover; border: 1px solid var(--line);
}
.file-name { font-weight: 600; font-size: 0.89rem; word-break: break-all; }
.file-meta { color: var(--ink-dim); font-size: 0.79rem; }

/* --------------------------------------------------------------- formats */

.format-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; }
.format-btn {
  text-align: left; cursor: pointer; font: inherit; color: var(--ink);
  padding: 0 0 12px; border-radius: var(--radius-sm); overflow: hidden;
  background: var(--bg-overlay); border: 1px solid var(--line);
  transition: border-color 0.14s var(--ease), background 0.14s var(--ease);
}
.format-btn:hover { border-color: var(--line-strong); background: var(--bg-hover); }
.format-btn.is-active { border-color: var(--accent); background: var(--accent-soft); }
/* Four physically different machines were four identical grey rectangles
   distinguished only by a paragraph. This is the same frame through each. */
.format-cover {
  display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  margin-bottom: 10px; background: #000;
}
.format-btn b { display: block; font-size: 0.89rem; margin: 0 13px 3px; }
.format-btn span { display: block; color: var(--ink-dim); font-size: 0.77rem; line-height: 1.4; margin: 0 13px; }
.format-btn:not(:has(.format-cover)) { padding-top: 13px; }

/* ------------------------------------------------------------ the look grid

   The core interaction. Tiles are deliberately large: the most consistent
   complaint about competing film-emulation products is that their preset
   thumbnails are too small to judge. */

/* 168px is the floor. Below roughly 150 you cannot tell two similar stocks
   apart and the grid becomes decorative - measured against Dehancer, which
   ships a fixed 180px four-column grid for the same job. */
.looks-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); gap: 12px; }
@media (max-width: 560px) { .looks-grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); } }
:root[data-density="compact"] .looks-grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 8px; }

.look {
  position: relative; cursor: pointer; padding: 0; border: 0; background: none;
  font: inherit; text-align: left; border-radius: var(--radius-sm); overflow: hidden;
  outline: 1.5px solid var(--line); outline-offset: -1.5px;
  transition: outline-color 0.12s var(--ease);
}
/* Colour only on hover, never transform. Twenty-five tiles under a moving
   cursor means a permanent ripple of scale animations across the grid. */
.look:hover { outline-color: var(--line-strong); }
.look.is-active { outline: 2px solid var(--accent); outline-offset: -2px; }
/* The ring cannot be the only signal - that fails "use of colour" for anyone
   who cannot see the amber. The tick says the same thing in a second channel. */
.look.is-active::after {
  content: "✓"; position: absolute; right: 6px; bottom: 6px;
  width: 19px; height: 19px; border-radius: 50%;
  background: var(--accent); color: var(--accent-ink);
  font-size: 0.68rem; font-weight: 700; display: grid; place-items: center;
}
/* 16:9, matching the footage. A square crop of a 16:9 frame hides the
   composition the user is actually judging. */
.look-swatch { display: block; width: 100%; aspect-ratio: 16 / 9; background-size: cover; background-position: center; }
.look-swatch img { width: 100%; height: 100%; object-fit: cover; display: block; }
.look-name {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 15px 8px 6px;
  font-size: 0.75rem; font-weight: 500; color: #fff;
  background: linear-gradient(transparent, rgba(0,0,0,0.82));
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 28px, not the 16px the glyph occupies: WCAG 2.2 puts the floor at 24.
   Faintly visible at rest rather than hidden - the gallery's own instructions
   tell people to star things, so hiding the control it names is needless. */
.look-star {
  position: absolute; top: 4px; right: 4px; width: 28px; height: 28px;
  border-radius: 50%; border: 0; cursor: pointer; opacity: 0.45;
  background: rgba(0,0,0,0.45); color: rgba(255,255,255,0.72);
  font-size: 0.78rem; line-height: 1; display: grid; place-items: center;
  transition: opacity 0.12s var(--ease), color 0.12s var(--ease);
}
.look:hover .look-star, .look-star.is-on { opacity: 1; }
.look-star.is-on { color: var(--accent); }
/* Premium is a small mark, never a lock and never a blur. Hiding the result
   is what makes an upsell feel hostile. */
.look-tag {
  position: absolute; top: 6px; left: 6px; font-weight: 600;
  font-size: 0.61rem; letter-spacing: 0.06em; text-transform: uppercase;
  padding: 2px 6px; border-radius: 4px;
  background: rgba(0,0,0,0.5); color: var(--accent);
}

.looks-filter { display: flex; gap: 6px; margin-bottom: 12px; flex-wrap: wrap; }
.chip {
  font: inherit; font-size: 0.79rem; cursor: pointer; padding: 5px 11px;
  border-radius: 999px; background: var(--bg-overlay);
  border: 1px solid var(--line); color: var(--ink-dim);
}
.chip:hover { color: var(--ink); border-color: var(--line-strong); }
.chip.is-active { background: var(--accent-soft); border-color: var(--accent-line); color: var(--ink); }

/* ---------------------------------------------------- fine-tune rail
   One horizontal strip of groups; the active group's controls show below.
   Scrolls sideways on narrow screens rather than wrapping into a wall. */
.adjust-rail {
  display: flex; gap: 6px; margin-bottom: 14px;
  overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.adjust-rail::-webkit-scrollbar { display: none; }
.adjust-chip {
  font: inherit; font-size: 0.79rem; cursor: pointer; padding: 5px 11px;
  border-radius: 999px; background: var(--bg-overlay);
  border: 1px solid var(--line); color: var(--ink-dim);
  white-space: nowrap; flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 6px;
}
.adjust-chip:hover { color: var(--ink); border-color: var(--line-strong); }
.adjust-chip.is-active {
  background: var(--accent-soft); border-color: var(--accent-line); color: var(--ink);
}
.adjust-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent); flex: 0 0 auto;
}
/* The user-picked accent is a CSS variable, so light mode darkens whatever
   colour is in it rather than assuming amber - a 6px amber dot on near-white
   was ~2:1 contrast, invisible to exactly the people it signals to. */
:root[data-theme="light"] .adjust-dot {
  background: color-mix(in srgb, var(--accent) 55%, black);
}
:root[data-theme="light"] .adjust-chip.is-active {
  border-color: color-mix(in srgb, var(--accent) 50%, black);
}
.adjust-panel { min-height: 96px; }
.seed-row { display: flex; gap: 8px; align-items: center; }
.seed-row input { flex: 1; }
.seed-row .ghost { flex: 0 0 auto; }

/* -------------------------------------------------------------- controls */

.field { margin-bottom: 14px; }
.field:last-child { margin-bottom: 0; }
.field label { display: block; font-size: 0.82rem; color: var(--ink-dim); margin-bottom: 6px; }
.field .desc { margin: 6px 0 0; font-size: 0.77rem; color: var(--ink-mute); line-height: 1.45; }

select, input[type="text"], input[type="email"], input[type="password"],
input[type="number"], input[type="datetime-local"], input[type="search"] {
  width: 100%; font: inherit; font-size: 0.88rem; padding: 9px 11px;
  border-radius: var(--radius-sm); background: var(--bg-base);
  border: 1px solid var(--line); color: var(--ink);
}
select:focus, input:focus { outline: 2px solid var(--accent-line); outline-offset: 1px; border-color: var(--accent); }
input[type="range"] { width: 100%; accent-color: var(--accent); }
.range-ends {
  display: flex; justify-content: space-between; margin-top: 2px;
  font-size: 0.72rem; color: var(--ink-mute);
}

.switch { display: flex; gap: 11px; align-items: flex-start; padding: 9px 0; cursor: pointer; }
.switch input { margin: 2px 0 0; accent-color: var(--accent); width: 16px; height: 16px; flex-shrink: 0; }
.switch b { display: block; font-size: 0.86rem; font-weight: 500; }
.switch span span { display: block; color: var(--ink-mute); font-size: 0.77rem; line-height: 1.45; }

/* --------------------------------------------------------------- buttons */

.btn, .primary, .ghost, .danger {
  font: inherit; font-size: 0.87rem; font-weight: 500;
  padding: 10px 17px; border-radius: var(--radius-sm);
  cursor: pointer; border: 1px solid transparent;
  transition: background 0.14s var(--ease), border-color 0.14s var(--ease), filter 0.14s var(--ease);
}
.primary { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.primary:hover:not(:disabled) { filter: brightness(1.08); }
.primary:disabled { opacity: 0.4; cursor: not-allowed; }
.ghost { background: var(--bg-overlay); border-color: var(--line); color: var(--ink); }
.ghost:hover { background: var(--bg-hover); border-color: var(--line-strong); }
.danger { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.danger:hover { background: color-mix(in srgb, var(--danger) 22%, transparent); }
.link {
  background: none; border: 0; color: var(--accent); font: inherit;
  font-size: 0.84rem; cursor: pointer; padding: 0; text-decoration: underline;
}

.render-row { display: flex; align-items: center; gap: 12px; margin-top: 6px; }
.render-row .primary { flex: 1; padding: 13px; font-size: 0.94rem; }
.cost { font-size: 0.79rem; color: var(--ink-dim); text-align: right; line-height: 1.35; }

/* --------------------------------------------------------------- preview */

.preview {
  background: #000; border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; aspect-ratio: 4 / 3; display: grid; place-items: center;
  position: relative;
}
.preview img, .preview video { width: 100%; height: 100%; object-fit: contain; display: block; }
.preview-empty { color: var(--ink-dim); font-size: 0.87rem; text-align: center; padding: 26px; }
.preview-empty p { margin: 0 0 8px; }
.preview-empty b { color: var(--ink); font-size: 1.02rem; }
#preview-note { margin: 10px 0 0; font-size: 0.79rem; color: var(--ink-mute); }
.preview-tag {
  position: absolute; top: 9px; left: 9px; font-size: 0.7rem;
  padding: 3px 8px; border-radius: 5px; background: rgba(0,0,0,0.6); color: #fff;
}

.summary { margin: 0; font-size: 0.82rem; }
.summary div { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; }
.summary div + div { border-top: 1px solid var(--line); }
.summary dt { color: var(--ink-mute); }
.summary dd { margin: 0; color: var(--ink-dim); text-align: right; }

/* --------------------------------------------------------------- library */

.jobs-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(246px, 1fr)); gap: 16px; }
.job {
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden; display: flex; flex-direction: column;
}
.job-thumb { aspect-ratio: 16 / 10; background: #000; position: relative; display: grid; place-items: center; overflow: hidden; }
.job-thumb img { width: 100%; height: 100%; object-fit: cover; }
.job-badge {
  position: absolute; top: 8px; left: 8px; font-size: 0.67rem;
  padding: 3px 7px; border-radius: 5px; background: rgba(0,0,0,0.62);
  color: #fff; letter-spacing: 0.03em;
}
.job-expiry {
  position: absolute; bottom: 8px; right: 8px; font-size: 0.67rem;
  padding: 3px 7px; border-radius: 5px; background: rgba(0,0,0,0.62); color: #cfc9c1;
}
.job-expiry.is-soon { color: var(--accent); }
.job-body { padding: 12px 13px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.job-title { font-size: 0.87rem; font-weight: 500; word-break: break-word; }
.job-sub { font-size: 0.76rem; color: var(--ink-mute); }
.job-actions { display: flex; gap: 6px; margin-top: auto; }
.job-actions .btn { padding: 7px 11px; font-size: 0.79rem; }

.bar { height: 4px; border-radius: 2px; background: var(--bg-overlay); overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--accent); transition: width 0.4s var(--ease); }

.empty {
  text-align: center; padding: 60px 22px;
  border: 1px dashed var(--line); border-radius: var(--radius); background: var(--bg-raised);
}
.empty h3 { margin-bottom: 6px; }
.empty p { color: var(--ink-dim); margin: 0 0 18px; font-size: 0.88rem; }

/* -------------------------------------------------------------- settings */

.settings-grid { display: grid; gap: var(--gap); grid-template-columns: repeat(auto-fit, minmax(288px, 1fr)); }
.settings-grid .card dl { margin: 0 0 14px; display: grid; grid-template-columns: auto 1fr; gap: 6px 14px; }
.settings-grid .card dt { color: var(--ink-mute); font-size: 0.81rem; }
.settings-grid .card dd { margin: 0; font-size: 0.84rem; }

.swatch-row { display: flex; gap: 8px; flex-wrap: wrap; }
.swatch { width: 30px; height: 30px; border-radius: 50%; cursor: pointer; border: 2px solid transparent; outline: 1px solid var(--line); }
.swatch.is-active { border-color: var(--bg-raised); outline: 2px solid var(--ink); }

.seg { display: flex; gap: 4px; background: var(--bg-overlay); padding: 3px; border-radius: var(--radius-sm); }
.seg button {
  flex: 1; font: inherit; font-size: 0.82rem; padding: 7px 10px;
  border: 0; border-radius: 6px; background: transparent; color: var(--ink-dim); cursor: pointer;
}
.seg button.is-active { background: var(--bg-raised); color: var(--ink); box-shadow: var(--shadow-sm); }

/* ------------------------------------------------------------------ gate */

.gate {
  position: fixed; inset: 0; z-index: 100; display: grid; place-items: center;
  padding: 24px; background: rgba(6, 6, 8, 0.76); backdrop-filter: blur(8px);
}
.gate-card {
  width: min(400px, 100%); background: var(--bg-raised);
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 30px 26px; box-shadow: var(--shadow);
}
.gate-card h2 { margin-bottom: 4px; }
.gate-card .hint { color: var(--ink-dim); font-size: 0.86rem; margin: 0 0 20px; }
.gate-card label { display: block; font-size: 0.81rem; color: var(--ink-dim); margin: 13px 0 6px; }
.gate-card .primary { width: 100%; margin-top: 18px; padding: 12px; }
.gate-switch { display: flex; gap: 4px; margin-bottom: 6px; }
.gate-error {
  margin: 14px 0 0; padding: 9px 11px; border-radius: var(--radius-sm);
  background: var(--danger-soft); border: 1px solid var(--danger);
  color: var(--danger); font-size: 0.83rem;
}
.gate-note { margin: 16px 0 0; color: var(--ink-mute); font-size: 0.78rem; text-align: center; }

/* --------------------------------------------- render confirm + uploading */

.confirm-card .summary { margin-top: 14px; }
.confirm-actions { display: flex; gap: 10px; margin-top: 20px; }
.confirm-actions .primary { flex: 1; }

.uploading-card { text-align: center; }
.uploading-card h2 { margin-bottom: 8px; }
.uploading-card .hint { margin: 0; }
.spinner {
  width: 34px; height: 34px; margin: 4px auto 16px; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--accent);
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* Motion-as-status: the spinner is the only sign the upload is alive, so it
   keeps turning under prefers-reduced-motion, just slower. */
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2s; } }

/* --------------------------------------------------------- in-page player */

.player-card {
  position: relative; width: min(860px, 100%);
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 14px; box-shadow: var(--shadow);
}
.player-card video {
  display: block; width: 100%; max-height: 72vh;
  background: #000; border-radius: var(--radius-sm);
}
.player-close {
  position: absolute; top: 22px; right: 22px; z-index: 2;
  width: 32px; height: 32px; border: 0; border-radius: 50%; cursor: pointer;
  background: rgba(0, 0, 0, 0.6); color: #fff; font-size: 1.05rem; line-height: 1;
}
.player-close:hover { background: rgba(0, 0, 0, 0.85); }
.player-note {
  margin: 10px 0 0; font-size: 0.78rem; color: var(--ink-mute);
  text-align: center;
}

/* ---------------------------------------------------------- job card menu */

.job-menu { position: relative; margin-left: auto; }
.job-menu-list {
  position: absolute; right: 0; bottom: calc(100% + 6px); z-index: 20;
  min-width: 160px; padding: 4px; display: flex; flex-direction: column;
  background: var(--bg-overlay); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm); box-shadow: var(--shadow);
}
.job-menu-list button {
  font: inherit; font-size: 0.82rem; text-align: left; padding: 8px 10px;
  border: 0; border-radius: 6px; background: none; color: var(--ink);
  cursor: pointer;
}
.job-menu-list button:hover { background: var(--bg-hover); }

#folder-filter { margin-bottom: 14px; }

/* ------------------------------------------------------------------ misc */

.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%); z-index: 200;
  padding: 11px 18px; border-radius: 999px;
  background: var(--bg-overlay); border: 1px solid var(--line-strong);
  color: var(--ink); font-size: 0.86rem; box-shadow: var(--shadow);
  animation: rise 0.2s var(--ease);
}
.toast.error { border-color: var(--danger); color: var(--danger); }
@keyframes rise { from { opacity: 0; transform: translate(-50%, 8px); } }

.demo-banner {
  background: var(--accent-soft); border-bottom: 1px solid var(--accent-line);
  color: var(--ink); font-size: 0.81rem; padding: 8px 22px; text-align: center;
}

.prose { max-width: 68ch; color: var(--ink-dim); font-size: 0.92rem; }
.prose h2 { color: var(--ink); margin: 26px 0 8px; }
.prose h3 { color: var(--ink); margin: 20px 0 6px; }
.prose p { margin: 0 0 12px; }
.prose ul { margin: 0 0 12px; padding-left: 20px; }
.prose li { margin-bottom: 5px; }
.prose code {
  font-family: var(--mono); font-size: 0.85em;
  background: var(--bg-overlay); padding: 2px 5px; border-radius: 4px;
}

.legal-nav { display: flex; gap: 6px; margin-bottom: 18px; flex-wrap: wrap; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A ring that lands on arbitrary imagery cannot rely on one colour holding
   contrast: amber on a sunset is invisible. White inner ring plus a dark halo
   is the only combination that survives any frame underneath it. */
.look:focus-visible, .job-thumb :focus-visible, .preview :focus-visible {
  outline: 2px solid #fff; outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.75);
}

/* ------------------------------------------------------------ compare grip

   Hold to see the original. In a grading tool this is not a preference, it is
   how the decision actually gets made. */
.compare {
  position: absolute; right: 9px; bottom: 9px; z-index: 2;
  font: inherit; font-size: 0.72rem; padding: 5px 10px; border-radius: 999px;
  background: rgba(0, 0, 0, 0.58); border: 1px solid rgba(255, 255, 255, 0.18);
  color: #fff; cursor: pointer; user-select: none;
}
.compare:hover { background: rgba(0, 0, 0, 0.78); }

/* The look grid is one choice out of many, so it is a radio group. That gets
   arrow-key traversal and the right announcement for free. */
.looks-grid[role="radiogroup"] { outline: none; }

/* ------------------------------------------------------------ the stack

   Medium → preset → treatment → physical options, read left to right, with
   an × on every optional link. This is the one element that makes the
   layering model legible: without it there is nothing on the page saying
   whether a treatment replaces a preset or sits on top of it. */
.stack {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  margin-bottom: 12px; font-size: 0.78rem;
}
.stack-arrow { color: var(--ink-mute); }
.stack-link {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 9px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--bg-overlay); color: var(--ink-dim);
}
.stack-link.is-base { color: var(--ink); border-color: var(--line-strong); }
.stack-link.is-grade { color: var(--accent); border-color: var(--accent-line); background: var(--accent-soft); }
.stack-off {
  border: 0; background: none; cursor: pointer; padding: 0 0 0 2px;
  color: inherit; opacity: 0.55; font-size: 0.95rem; line-height: 1;
}
.stack-off:hover { opacity: 1; }

/* The render-time estimate. Stated before the click, because an unstated
   render time is how a working render looks hung. */
.dock-eta { display: block; color: var(--ink-dim); margin-bottom: 6px; }

/* Indeterminate, for the window before the first frame exists. Motion here is
   status rather than decoration - it is the only thing distinguishing "still
   setting up" from "hung", so it survives prefers-reduced-motion. */
.bar.is-waiting { position: relative; }
.bar.is-waiting > i {
  position: absolute; left: 0; width: 38%;
  animation: sweep 1.5s ease-in-out infinite;
}
@keyframes sweep {
  0%   { left: -38%; }
  100% { left: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .bar.is-waiting > i { animation-duration: 1.5s !important; }
}

/* ---------------------------------------------------------------- help

   Search-first, because forty answers is far more than anyone will scan and
   the people who most need help are the least likely to read a wall of them.
   Details/summary rather than a JS accordion: it is keyboard operable, it is
   announced correctly by screen readers, and browser find-in-page can open
   a closed answer. */
.faq-search { max-width: 620px; margin-bottom: 14px; }
.faq-search input { font-size: 0.95rem; padding: 11px 13px; }
.faq-search .desc { margin: 7px 0 0; font-size: 0.79rem; color: var(--ink-mute); }

.faq-filter { display: flex; gap: 6px; margin-bottom: 18px; flex-wrap: wrap; }

.faq-group { margin-bottom: 26px; max-width: 780px; }
.faq-group h2 {
  font-size: 0.75rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--ink-mute); margin-bottom: 8px;
}

.faq-item {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--bg-raised); margin-bottom: 7px;
}
.faq-item[open] { border-color: var(--line-strong); }
.faq-item summary {
  cursor: pointer; padding: 13px 15px; font-size: 0.92rem; font-weight: 500;
  list-style: none; display: flex; gap: 10px; align-items: flex-start;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::before {
  content: "+"; color: var(--accent); font-weight: 600;
  line-height: 1.35; flex-shrink: 0;
}
.faq-item[open] summary::before { content: "\2212"; }
.faq-item summary:hover { color: var(--accent); }
.faq-answer {
  padding: 0 15px 15px 38px; color: var(--ink-dim);
  font-size: 0.89rem; line-height: 1.6; max-width: 68ch;
}
/* Short measure. Long lines are where dyslexic readers lose their place, and
   this is the one screen someone reads when they are already stuck. */
.faq-answer p { margin: 10px 0 0; }
.faq-answer p:last-child { margin-bottom: 0; }
.faq-answer strong { color: var(--ink); font-weight: 600; }

.faq-empty { color: var(--ink-dim); padding: 22px 0; }
.faq-empty b { color: var(--ink); }

/* Visible only to assistive tech. Used for the render-completion announcement,
   which otherwise happens in total silence minutes after you walked away. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.stage-heading {
  font-size: 0.73rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--ink-mute); margin-bottom: 10px; font-weight: 600;
}

.job-recipe {
  font-size: 0.75rem; color: var(--ink-mute); margin-top: -4px;
  word-break: break-word;
}

.gate-rule { margin: 6px 0 0; font-size: 0.77rem; color: var(--ink-mute); }

.dock-eta.is-long { color: var(--accent); }
.dock-eta.is-long b { color: var(--accent); }

/* The accent swatches carried no name and no state, which is nothing at all
   to a screen reader and not much to anyone who cannot see the hue. */
.swatch span {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%);
}
.swatch { position: relative; }

/* The legal documents existed but nothing linked to them, so terms, privacy
   and the content policy were unreachable from anywhere in the interface. */
.footer {
  max-width: 1380px; margin: 0 auto; padding: 22px;
  border-top: 1px solid var(--line);
  display: flex; gap: 18px; flex-wrap: wrap;
  align-items: center; justify-content: space-between;
  font-size: 0.79rem; color: var(--ink-mute);
}
.footer nav { display: flex; gap: 16px; flex-wrap: wrap; }
.footer .link { font-size: 0.79rem; color: var(--ink-dim); text-decoration: none; }
.footer .link:hover { color: var(--accent); text-decoration: underline; }

/* --------------------------------------------------------------- pricing */

/* The founding-price banner is the device that makes it safe to publish a
   number before it is final: it locks the rate for early customers, so a
   later rise is something they were told about rather than something done to
   them. */
.founding {
  border: 1px solid var(--accent-line); background: var(--accent-soft);
  border-radius: var(--radius); padding: 13px 16px; margin-bottom: var(--gap);
  font-size: 0.86rem; color: var(--ink-dim); max-width: 760px;
}
.founding b { color: var(--accent); }

.plans { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); align-items: start; }
.plan {
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 22px; position: relative;
  display: flex; flex-direction: column; gap: 12px;
}
.plan.is-featured { border-color: var(--accent-line); background: var(--bg-overlay); }
.plan-tag {
  position: absolute; top: -9px; left: 20px;
  background: var(--accent); color: var(--accent-ink);
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.05em;
  text-transform: uppercase; padding: 3px 9px; border-radius: 999px;
}
.plan h2 { font-size: 1rem; }
.plan-price { margin: 0; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.plan-price b { font-size: 1.9rem; font-weight: 600; letter-spacing: -0.02em; }
.plan-price span { color: var(--ink-mute); font-size: 0.85rem; }
.plan-alt { margin: -8px 0 0; font-size: 0.78rem; color: var(--ink-mute); }
.plan ul { margin: 0; padding-left: 18px; font-size: 0.85rem; color: var(--ink-dim); line-height: 1.6; }
.plan li { margin-bottom: 5px; }
.plan li b { color: var(--ink); font-weight: 600; }
.plan button { margin-top: auto; width: 100%; }
.plan button:disabled { opacity: 0.45; cursor: default; }

.plans-note {
  margin: 16px 0 0; font-size: 0.87rem; color: var(--ink-dim);
  padding: 14px 16px; border: 1px dashed var(--line-strong);
  border-radius: var(--radius); max-width: 760px;
}
.plans-note b { color: var(--ink); }

/* The credit chip is the natural place to ask "what does this cost?", and
   nine of thirteen testers did exactly that and found no answer. */
.credit { cursor: pointer; }
.credit:hover { border-color: var(--accent-line); color: var(--ink); }

/* The live preview is the product's best feature and it was discoverable only
   by accident - somebody had to happen to move the mouse over a tile. */
.grid-lede { margin: -6px 0 14px; font-size: 0.83rem; color: var(--ink-dim); }
.grid-lede b { color: var(--accent); font-weight: 600; }

.stack-label {
  font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-mute); margin-right: 2px;
}

/* ------------------------------------------------------------ saved recipes

   One chip per recipe under the format grid: a saved look is a variant of
   picking a format, so it lives where formats are picked. Empty state is just
   the save button - a heading over nothing would be clutter. */
.recipes {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  margin-top: 12px;
}
.recipe-chip { display: inline-flex; align-items: center; gap: 5px; }
.recipe-del {
  border: 0; background: none; color: inherit; cursor: pointer;
  padding: 0 0 0 2px; opacity: 0.55; font-size: 0.95rem; line-height: 1;
}
.recipe-del:hover, .recipe-del:focus-visible { opacity: 1; color: var(--danger); }
.recipe-save { padding: 5px 11px; font-size: 0.79rem; border-radius: 999px; }

/* ------------------------------------------------------------- batch queue */

.batch-list { margin-top: 12px; display: flex; flex-direction: column; gap: 6px; }
.batch-head { font-size: 0.78rem; color: var(--ink-mute); }
.batch-item {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--bg-overlay); font-size: 0.82rem;
}
.batch-item .b-name {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.batch-item .b-dur { color: var(--ink-mute); font-size: 0.75rem; flex-shrink: 0; }

/* Shared row-dismiss button (batch rows, reel rows). */
.row-x {
  border: 0; background: none; color: var(--ink-mute); cursor: pointer;
  font-size: 0.95rem; line-height: 1; padding: 2px 4px; flex-shrink: 0;
}
.row-x:hover, .row-x:focus-visible { color: var(--danger); }

/* ------------------------------------------------------------ reel builder */

.reel-list { margin-top: 12px; display: flex; flex-direction: column; }
.reel-item {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--bg-overlay); font-size: 0.82rem;
}
.reel-item.is-card {
  background: var(--accent-soft); border-color: var(--accent-line);
  align-items: flex-start;
}
.reel-ord {
  width: 16px; text-align: right; color: var(--ink-mute);
  font-size: 0.72rem; flex-shrink: 0;
}
.reel-name {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.reel-dur { color: var(--ink-mute); font-size: 0.75rem; flex-shrink: 0; }
.reel-move { display: flex; gap: 3px; flex-shrink: 0; }
.reel-move button {
  width: 24px; height: 24px; padding: 0; cursor: pointer;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--bg-raised); color: var(--ink-dim);
  font-size: 0.72rem; line-height: 1;
}
.reel-move button:hover:not(:disabled) { color: var(--ink); border-color: var(--line-strong); }
.reel-move button:disabled { opacity: 0.3; cursor: default; }

.reel-item.is-card .card-fields {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px;
}
/* The sub-line is visibly the smaller of the two, matching what it renders. */
.reel-item.is-card .card-sub-input { font-size: 0.78rem; padding: 7px 11px; }

/* The insertion points between rows. Quiet at rest; they are an offer, not a
   demand. */
.reel-insert { display: flex; justify-content: center; padding: 1px 0; }
.reel-insert button {
  border: 0; background: none; cursor: pointer; padding: 2px 10px;
  border-radius: 999px; color: var(--ink-mute); font-size: 0.72rem; opacity: 0.75;
}
.reel-insert button:hover, .reel-insert button:focus-visible {
  color: var(--accent); background: var(--accent-soft); opacity: 1;
}

.reel-note { margin: 10px 0 0; font-size: 0.77rem; color: var(--ink-mute); line-height: 1.45; }
.reel-warn { margin: 10px 0 0; font-size: 0.78rem; color: var(--accent); line-height: 1.45; }
:root[data-theme="light"] .reel-warn {
  color: color-mix(in srgb, var(--accent) 55%, black);
}

/* ============================================================ mobile

   The app was desktop-first. On a phone the tab bar ran off the right edge,
   the page scrolled sideways, and format cards were full-width billboards.
   This block makes it a real phone layout: a bottom tab bar (where a thumb
   actually reaches), horizontally-scrolling format cards, and a preview that
   sits above the controls rather than beside them. */

/* Nothing may cause a sideways scroll. This was the horizontal scrollbar. */
html, body { max-width: 100%; overflow-x: hidden; }

@media (max-width: 760px) {
  main { padding: 16px 14px 92px; }          /* room for the bottom bar */

  /* App top bar keeps only brand + credit + avatar; its nav moves to the
     bottom. The landing top bar keeps its own inline nav.
     backdrop-filter must be OFF here: a filtered element becomes the
     containing block for its position:fixed descendants, so with the blur on,
     the fixed bottom nav pinned to the BOTTOM OF THE HEADER (top of screen)
     instead of the viewport bottom - the tab bar sat under the header on
     every phone. A solid header on mobile is a fine trade for a tab bar that
     is actually at the bottom. */
  .topbar {
    padding: 0 14px; height: 52px;
    backdrop-filter: none; background: var(--bg-base);
  }
  .storepage .topbar, body:has(.landing-hero) .topbar { height: auto; flex-wrap: wrap; }
  /* Only the app's real tab bar becomes a bottom bar. The landing page uses
     anchor tabs in its top bar and must keep them there. */
  nav.tabs:has(.tab[data-tab]) {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
    margin: 0; gap: 0; height: 58px;
    background: color-mix(in srgb, var(--bg-base) 94%, transparent);
    backdrop-filter: blur(16px);
    border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom);
    justify-content: space-around;
  }
  .tab[data-tab] {
    flex: 1; display: flex; flex-direction: column; align-items: center;
    justify-content: center; gap: 3px; border-radius: 0;
    font-size: 0.62rem; letter-spacing: 0.01em; padding: 6px 2px;
    text-align: center; min-width: 0;
  }
  .tab[data-tab].is-active { background: transparent; color: var(--accent); }
  /* A small glyph above each label, drawn in CSS from the tab name. */
  .tab[data-tab]::before {
    content: ""; width: 20px; height: 20px; border-radius: 5px;
    border: 1.6px solid currentColor; opacity: 0.9;
  }
  .tab[data-tab="create"]::before { border-radius: 50%; }
  .tab[data-tab="library"]::before { border-radius: 3px; border-width: 1.6px; }
  .tab[data-tab="looks"]::before { border-radius: 5px; background: currentColor; opacity: 0.35; }
  .tab[data-tab="pricing"]::before { border-radius: 5px; border-style: dashed; }
  .tab[data-tab="help"]::before { border-radius: 50%; border-style: dashed; }
  .tab[data-tab="about"] { display: none; }   /* reachable from Help/footer */

  /* Preview first, then controls: on a phone you want to SEE the thing. */
  .workspace { display: flex; flex-direction: column; gap: 14px; }
  .stage { order: -1; position: static; }
  .controls { position: static; }
  .rail-wrap { padding-bottom: 0; }
  .render-dock {
    position: fixed; left: 0; right: 0; bottom: 58px; z-index: 55;
    margin: 0; border-radius: 0; border-left: 0; border-right: 0;
    padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
    box-shadow: 0 -8px 24px rgba(0,0,0,0.4);
  }
  .render-dock .dock-note { display: none; }   /* the reassurance lives elsewhere */
  .render-dock .summary { display: none; }      /* the stack strip already shows it */

  /* Format cards scroll sideways instead of stacking into billboards. */
  .format-grid {
    display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x mandatory;
    margin: 0 -14px; padding: 0 14px 4px;
    scrollbar-width: none;
  }
  .format-grid::-webkit-scrollbar { display: none; }
  .format-btn { flex: 0 0 76%; scroll-snap-align: start; }
  .format-cover { aspect-ratio: 16/9; }

  .card { padding: 15px; }
  .looks-grid { grid-template-columns: repeat(2, 1fr); }
  .page-head { flex-direction: column; align-items: flex-start; gap: 8px; }
  .plans { grid-template-columns: 1fr; }
  h1 { font-size: 1.3rem; }

  /* The preview must never be taller than the screen on a phone. */
  .preview { max-height: 42vh; }
}

/* Very small phones: even tighter. */
@media (max-width: 380px) {
  .tab { font-size: 0.56rem; }
  .format-btn { flex-basis: 84%; }
}

/* The preview's empty state is a real control, not a label: on a phone there
   is no drag-and-drop, so an inert "Drop a clip" target is a dead end. */
.preview-pick {
  font: inherit; font-weight: 600; font-size: 1.02rem; cursor: pointer;
  color: var(--ink); background: var(--bg-overlay);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  padding: 11px 20px; min-height: 44px;
}
.preview-pick:hover { border-color: var(--accent-line); color: var(--accent); }
