/* =========================================================================
   AI Video Pipeline — design system
   -------------------------------------------------------------------------
   Rewritten around a real token layer. The previous sheet had 17 custom
   properties, all colour plus one shadow, and no tokens at all for spacing,
   type or radii - which is why nothing lined up with anything else. Every
   value below flows from a scale, so components compose instead of each
   re-inventing its own padding.

   Order: tokens -> reset -> primitives -> layout -> features -> responsive.
   Anything JS or auth.js reaches for by name (.style-card, .niche-card,
   #topbar-actions, #auth-menu-retention, [data-requires-publish]) keeps its
   name regardless of how it is styled here.
   ========================================================================= */

/* ------------------------------------------------------------- tokens -- */
:root {
  /* Brand. One gradient used everywhere something should feel like an
     action; the solid --brand is for text/borders where a gradient can't go. */
  --brand: #7c3aed;
  --brand-strong: #6d28d9;
  --brand-soft: #f5f3ff;
  --brand-soft-line: #ddd6fe;
  --gradient: linear-gradient(135deg, #7c3aed 0%, #a855f7 45%, #ec4899 100%);
  --gradient-dim: linear-gradient(135deg, #7c3aed22 0%, #ec489922 100%);

  /* Neutrals */
  --n-0: #ffffff;
  --n-25: #fcfcfd;
  --n-50: #fafafa;
  --n-100: #f4f4f5;
  --n-200: #e4e4e7;
  --n-300: #d4d4d8;
  --n-400: #a1a1aa;
  --n-500: #71717a;
  --n-600: #52525b;
  --n-700: #3f3f46;
  --n-900: #18181b;
  --n-950: #09090b;

  /* Semantic surfaces */
  --canvas: var(--n-50);
  --surface: var(--n-0);
  --surface-2: var(--n-100);
  --line: var(--n-200);
  --line-strong: var(--n-300);

  /* Semantic ink */
  --ink: var(--n-950);
  --ink-2: var(--n-600);
  --ink-3: var(--n-500);
  --ink-on-brand: #ffffff;

  /* Status */
  --ok: #059669;
  --ok-soft: #ecfdf5;
  --warn: #b45309;
  --warn-soft: #fffbeb;
  --danger: #dc2626;
  --danger-soft: #fef2f2;
  --info: #2563eb;
  --info-soft: #eff6ff;

  /* One knob for how dense the interface is. Everything below is derived
     from it, so the whole app tightens or loosens together instead of
     drifting apart as individual values get nudged. 1 was the original
     scale, which read as zoomed in - too much air per unit of content on a
     laptop, where most of this is used. */
  --ui: 0.86;

  /* Space — 4pt scale */
  --s-1: calc(4px * var(--ui));   --s-2: calc(8px * var(--ui));
  --s-3: calc(12px * var(--ui));  --s-4: calc(16px * var(--ui));
  --s-5: calc(20px * var(--ui));  --s-6: calc(24px * var(--ui));
  --s-8: calc(32px * var(--ui));  --s-10: calc(40px * var(--ui));
  --s-12: calc(48px * var(--ui)); --s-16: calc(64px * var(--ui));
  --s-20: calc(80px * var(--ui));

  /* Radii */
  --r-sm: 8px; --r-md: 12px; --r-lg: 16px; --r-xl: 20px; --r-2xl: 28px;
  --r-full: 999px;

  /* Type */
  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  /* Body sizes scale less than headings: 15px going to 12px would be hard
     to read, whereas a 44px display heading has room to lose. */
  --t-xs: calc(12px * 0.95); --t-sm: calc(13px * 0.95);
  --t-base: calc(15px * 0.95); --t-md: calc(16px * 0.95);
  --t-lg: calc(18px * var(--ui)); --t-xl: calc(22px * var(--ui));
  --t-2xl: calc(28px * var(--ui)); --t-3xl: calc(36px * var(--ui));
  --t-4xl: calc(44px * var(--ui));

  /* Elevation */
  --sh-xs: 0 1px 2px rgba(9, 9, 11, 0.05);
  --sh-sm: 0 1px 3px rgba(9, 9, 11, 0.08), 0 1px 2px rgba(9, 9, 11, 0.04);
  --sh-md: 0 4px 12px rgba(9, 9, 11, 0.08), 0 2px 4px rgba(9, 9, 11, 0.04);
  --sh-lg: 0 12px 32px rgba(9, 9, 11, 0.12), 0 4px 8px rgba(9, 9, 11, 0.04);
  --sh-brand: 0 6px 20px rgba(124, 58, 237, 0.30);

  /* Motion */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-1: 120ms; --dur-2: 220ms; --dur-3: 380ms;

  --z-nav: 20; --z-sticky: 30; --z-overlay: 100; --z-modal: 110;
}

@media (prefers-color-scheme: dark) {
  :root {
    --brand: #a78bfa;
    --brand-strong: #8b5cf6;
    --brand-soft: rgba(124, 58, 237, 0.16);
    --brand-soft-line: rgba(167, 139, 250, 0.32);

    --canvas: #0b0c0f;
    --surface: #141619;
    --surface-2: #1c1f24;
    --line: rgba(255, 255, 255, 0.10);
    --line-strong: rgba(255, 255, 255, 0.18);

    --ink: #f4f4f5;
    --ink-2: #a1a1aa;
    --ink-3: #8b8b93;

    --ok: #34d399;   --ok-soft: rgba(16, 185, 129, 0.14);
    --warn: #fbbf24; --warn-soft: rgba(217, 119, 6, 0.16);
    --danger: #f87171; --danger-soft: rgba(220, 38, 38, 0.16);
    --info: #60a5fa; --info-soft: rgba(37, 99, 235, 0.16);

    --sh-xs: none;
    --sh-sm: 0 1px 3px rgba(0, 0, 0, 0.5);
    --sh-md: 0 4px 14px rgba(0, 0, 0, 0.55);
    --sh-lg: 0 16px 40px rgba(0, 0, 0, 0.6);
    --sh-brand: 0 6px 20px rgba(124, 58, 237, 0.45);
  }
}

/* -------------------------------------------------------------- reset -- */
* { box-sizing: border-box; }

/* `hidden` is only display:none in the UA sheet, so any author rule setting
   display defeats it. Roughly twenty elements here are toggled through the
   attribute, so this guards the whole class of bug rather than one instance. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 { margin: 0; line-height: 1.2; letter-spacing: -0.02em; font-weight: 700; }
h1 { font-size: var(--t-3xl); }
h2 { font-size: var(--t-xl); }
h3 { font-size: var(--t-lg); }
p { margin: 0; }
img, svg, video { max-width: 100%; display: block; }
a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.muted { color: var(--ink-2); }
.tiny { font-size: var(--t-xs); color: var(--ink-3); }

/* --------------------------------------------------------- primitives -- */

/* Buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  font: inherit; font-size: var(--t-base); font-weight: 600; line-height: 1;
  padding: 0 var(--s-5); height: 42px;
  border-radius: var(--r-md); border: 1px solid transparent;
  background: var(--surface-2); color: var(--ink);
  cursor: pointer; white-space: nowrap;
  transition: transform var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease),
              background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              opacity var(--dur-1) var(--ease);
}
.btn:hover:not(:disabled) { transform: translateY(-1px); }
.btn:active:not(:disabled) { transform: translateY(0); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.btn--primary {
  background: var(--gradient); color: var(--ink-on-brand);
  box-shadow: var(--sh-brand);
}
.btn--primary:hover:not(:disabled) { box-shadow: 0 10px 26px rgba(124, 58, 237, 0.42); }

.btn--secondary { background: var(--surface); border-color: var(--line); color: var(--ink); box-shadow: var(--sh-xs); }
.btn--secondary:hover:not(:disabled) { border-color: var(--line-strong); background: var(--surface-2); }

.btn--ghost { background: transparent; color: var(--ink-2); }
.btn--ghost:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); }

.btn--danger { background: var(--danger-soft); color: var(--danger); border-color: transparent; }

.btn--lg { height: 52px; font-size: var(--t-md); padding: 0 var(--s-8); border-radius: var(--r-lg); }
.btn--sm { height: 34px; font-size: var(--t-sm); padding: 0 var(--s-3); border-radius: var(--r-sm); }
.btn--block { width: 100%; }
.btn svg { width: 18px; height: 18px; flex-shrink: 0; }

/* Fields */
/* The step body is a grid so narrow controls can sit two to a row. A
   recurring series has seven of them; stacked, the last was well below the
   fold and the step felt far longer than the decisions in it. */
.flow__fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--s-5); }
.flow__fields > .field { grid-column: 1 / -1; }
.flow__fields > .field--half { grid-column: span 1; }

.field { margin-bottom: var(--s-4); }
.field:last-child { margin-bottom: 0; }
.field__label {
  display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap;
  font-size: var(--t-sm); font-weight: 600; color: var(--ink);
  margin-bottom: var(--s-2);
}
.field__hint { margin-top: var(--s-2); font-size: var(--t-xs); color: var(--ink-3); line-height: 1.5; }

.input, .select, .textarea {
  width: 100%; font: inherit; font-size: var(--t-base); color: var(--ink);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 11px var(--s-3);
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
.textarea { resize: vertical; min-height: 92px; line-height: 1.6; }
.input:hover, .select:hover, .textarea:hover { border-color: var(--line-strong); }
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft);
}
.input::placeholder, .textarea::placeholder { color: var(--ink-3); }
.select {
  appearance: none; padding-right: var(--s-10); cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--s-3) center;
}
input[type="file"] {
  width: 100%; font: inherit; font-size: var(--t-sm); color: var(--ink-2);
  padding: var(--s-3); border: 1px dashed var(--line-strong);
  border-radius: var(--r-md); background: var(--surface-2); cursor: pointer;
}
input[type="file"]:hover { border-color: var(--brand); background: var(--brand-soft); }

/* Radio list -----------------------------------------------------------
   For choices whose meaning is entirely in the words. Styled from
   :checked rather than a JS-applied class so the selected state is correct
   the instant the browser flips the radio, with no re-render in between. */
.radiolist {
  display: grid; gap: var(--s-2);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.radiolist__item {
  display: flex; align-items: flex-start; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); cursor: pointer;
  transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.radiolist__item:hover { border-color: var(--line-strong); background: var(--surface-2); }
/* The native input still exists and still receives focus and keystrokes -
   it is only moved out of sight, never display:none, which would take it
   out of the tab order and off the accessibility tree. */
.radiolist__item input {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; clip: rect(0 0 0 0); overflow: hidden; white-space: nowrap;
}
.radiolist__dot {
  flex: none; width: 18px; height: 18px; margin-top: 1px; border-radius: 50%;
  border: 2px solid var(--line-strong); background: var(--surface);
  display: grid; place-items: center;
  transition: border-color var(--dur-1) var(--ease);
}
.radiolist__dot::after {
  content: ""; width: 8px; height: 8px; border-radius: 50%;
  background: var(--brand); transform: scale(0);
  transition: transform var(--dur-1) var(--ease);
}
.radiolist__item:has(input:checked) { border-color: var(--brand); background: var(--brand-soft); }
.radiolist__item input:checked ~ .radiolist__dot { border-color: var(--brand); }
.radiolist__item input:checked ~ .radiolist__dot::after { transform: scale(1); }
.radiolist__item input:focus-visible ~ .radiolist__dot { box-shadow: 0 0 0 3px var(--brand-soft-line); }
.radiolist__text { display: grid; gap: 2px; }
.radiolist__title { font-weight: 600; font-size: var(--t-base); color: var(--ink); }
.radiolist__desc { font-size: var(--t-xs); color: var(--ink-2); line-height: 1.5; }

/* File field ------------------------------------------------------------
   The native control is the one element a stylesheet cannot reach, so it is
   hidden inside the label and the visible surface is ours. */
.filefield {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-4); cursor: pointer;
  border: 1px dashed var(--line-strong); border-radius: var(--r-md);
  background: var(--surface-2);
  transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.filefield:hover { border-color: var(--brand); background: var(--brand-soft); }
.filefield__input {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; clip: rect(0 0 0 0); overflow: hidden; white-space: nowrap;
}
.filefield__input:focus-visible ~ .filefield__cue { box-shadow: 0 0 0 3px var(--brand-soft-line); }
.filefield__cue {
  flex: none; width: 36px; height: 36px; padding: 8px; border-radius: var(--r-md);
  background: var(--surface); color: var(--brand); border: 1px solid var(--line);
}
.filefield__copy { display: grid; gap: 2px; }
.filefield__cta { font-weight: 600; font-size: var(--t-sm); color: var(--ink); }
.filefield__hint { font-size: var(--t-xs); color: var(--ink-3); }
.filefield__chosen {
  display: flex; align-items: center; gap: var(--s-3); margin-top: var(--s-2);
  padding: var(--s-3); border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface);
}
.filefield__icon { flex: none; width: 20px; height: 20px; color: var(--brand); }
.filefield__meta { display: grid; gap: 1px; min-width: 0; flex: 1; }
.filefield__name {
  font-size: var(--t-sm); font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.filefield__size { font-size: var(--t-xs); color: var(--ink-3); }
.filefield__clear {
  flex: none; font: inherit; font-size: var(--t-xs); font-weight: 600;
  color: var(--ink-2); background: none; border: 0; cursor: pointer;
  padding: var(--s-2); border-radius: var(--r-sm);
}
.filefield__clear:hover { color: var(--ink); background: var(--surface-2); }

/* Date field ------------------------------------------------------------
   datetime-local draws its own placeholder and picker button and will not
   be restyled; what is fixable is the frame around it and the font, so it
   stops looking like a control borrowed from another page. */
.datefield { position: relative; display: flex; align-items: center; }
.datefield__icon {
  position: absolute; left: var(--s-3); width: 18px; height: 18px;
  color: var(--ink-3); pointer-events: none;
}
.datefield__input { padding-left: calc(var(--s-3) * 2 + 18px); }
.datefield__input::-webkit-calendar-picker-indicator {
  cursor: pointer; opacity: 0.55; border-radius: var(--r-sm); padding: 2px;
}
.datefield__input::-webkit-calendar-picker-indicator:hover { opacity: 1; background: var(--surface-2); }

.inline-fields { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
/* min-width:0 matters: a flex item's default min-width is its content size,
   so an input refuses to shrink below its intrinsic width and pushes the row
   past the viewport instead of wrapping. */
.inline-fields .input, .inline-fields .select { width: auto; min-width: 0; flex: 1 1 130px; }
.inline-fields > .btn { flex: 0 1 auto; }
.inline-fields__suffix { font-size: var(--t-sm); color: var(--ink-2); white-space: nowrap; }

/* Badges */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--t-xs); font-weight: 600; line-height: 1;
  padding: 5px var(--s-2); border-radius: var(--r-full);
  background: var(--surface-2); color: var(--ink-2);
}
.badge--brand { background: var(--brand-soft); color: var(--brand); }
.badge--info { background: var(--info-soft); color: var(--info); }
.badge--ok { background: var(--ok-soft); color: var(--ok); }
.badge--warn { background: var(--warn-soft); color: var(--warn); }
.badge--danger { background: var(--danger-soft); color: var(--danger); }

/* Cards */
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-xl); padding: var(--s-6); box-shadow: var(--sh-sm);
}

/* Toggle switch */
.switch-row {
  display: flex; align-items: flex-start; gap: var(--s-4);
  padding: var(--s-4); border: 1px solid var(--line);
  border-radius: var(--r-lg); background: var(--surface); cursor: pointer;
  transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.switch-row:hover { border-color: var(--line-strong); }
.switch-row:has(input:checked) { border-color: var(--brand-soft-line); background: var(--brand-soft); }
.switch-row + .switch-row { margin-top: var(--s-2); }
.switch-row__text { flex: 1; min-width: 0; }
.switch-row__title { display: block; font-size: var(--t-base); font-weight: 600; }
.switch-row__desc { display: block; font-size: var(--t-xs); color: var(--ink-3); line-height: 1.5; margin-top: 2px; }

.switch { position: relative; width: 44px; height: 26px; flex-shrink: 0; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch__track {
  position: absolute; inset: 0; border-radius: var(--r-full);
  background: var(--n-300); transition: background var(--dur-2) var(--ease);
  pointer-events: none;
}
.switch__track::before {
  content: ""; position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px; border-radius: 50%; background: #fff;
  box-shadow: var(--sh-sm); transition: transform var(--dur-2) var(--ease);
}
.switch input:checked + .switch__track { background: var(--brand); }
.switch input:checked + .switch__track::before { transform: translateX(18px); }
.switch input:focus-visible + .switch__track { outline: 2px solid var(--brand); outline-offset: 2px; }
.switch input:disabled + .switch__track { opacity: 0.45; }

/* Chips (multi/single select pills) */
.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip {
  position: relative; display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-sm); font-weight: 500; line-height: 1;
  padding: 9px var(--s-4); border-radius: var(--r-full);
  border: 1px solid var(--line); background: var(--surface);
  color: var(--ink-2); cursor: pointer;
  transition: all var(--dur-1) var(--ease);
}
.chip input { position: absolute; opacity: 0; pointer-events: none; }
.chip:hover { border-color: var(--line-strong); color: var(--ink); }
.chip:has(input:checked) {
  background: var(--brand-soft); border-color: var(--brand-soft-line);
  color: var(--brand); font-weight: 600;
}
.chip:has(input:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }

/* Pick cards — the art-tile grid used by niche/style/category/theme pickers.
   `.style-card` and `.niche-card` are aliases kept because app.js and older
   markup reach for those names. */
.pickgrid { display: grid; gap: var(--s-3); grid-template-columns: repeat(4, 1fr); }
.pickgrid--wide { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.pickgrid--portrait { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); }

/* One scrollable row. The options that do not fit are not hidden - the last
   visible card is deliberately cut by the right edge, which is what tells
   someone there is more without needing an arrow or a caption. */
.pickgrid--rail {
  display: flex; gap: var(--s-3);
  overflow-x: auto; overflow-y: hidden;
  /* Room for the focus ring and shadow, which a plain overflow clips. */
  padding: 4px 0 var(--s-3);
  scroll-snap-type: x proximity;
  /* Thin, and only on the axis that scrolls. */
  scrollbar-width: thin;
  /* Fades the right edge so the cut-off card reads as intentional rather
     than as a layout bug. mask rather than a gradient overlay: an overlay
     would sit above the cards and swallow clicks on the last one. */
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 48px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 48px), transparent);
}
/* flex items shrink by default; these must keep their width and scroll. */
.pickgrid--rail > * { flex: 0 0 148px; scroll-snap-align: start; }
.pickgrid--rail::-webkit-scrollbar { height: 8px; }
.pickgrid--rail::-webkit-scrollbar-thumb {
  background: var(--line-strong); border-radius: var(--r-full);
}
.pickgrid--rail::-webkit-scrollbar-track { background: transparent; }
/* The mask would hide the rightmost card entirely once scrolled to the end,
   so it is dropped when there is nothing further to reveal. */
.pickgrid--rail.is-end {
  -webkit-mask-image: none; mask-image: none;
}

@media (max-width: 640px) {
  .pickgrid--rail > * { flex-basis: 128px; }
}

.pickcard, .style-card {
  position: relative; display: flex; flex-direction: column; justify-content: flex-end;
  aspect-ratio: 1; overflow: hidden; cursor: pointer;
  border-radius: var(--r-lg); border: 2px solid var(--line);
  background: var(--surface-2); padding: 0;
  transition: transform var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              box-shadow var(--dur-1) var(--ease);
}
.pickcard:hover, .style-card:hover { transform: translateY(-2px); box-shadow: var(--sh-md); }
.pickcard:has(input:checked), .style-card:has(input:checked) {
  border-color: var(--brand); box-shadow: 0 0 0 4px var(--brand-soft);
}
.pickcard:has(input:focus-visible), .style-card:has(input:focus-visible) {
  outline: 2px solid var(--brand); outline-offset: 2px;
}
.pickcard input, .style-card input { position: absolute; opacity: 0; pointer-events: none; }
.pickcard--portrait { aspect-ratio: 2 / 3; }

.pickcard__art {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
  transition: transform var(--dur-3) var(--ease);
}
/* <img> variant - object-fit gives the same crop as background-size:cover
   while still being a lazily-loadable resource. */
.pickcard__img { width: 100%; height: 100%; object-fit: cover; }
.pickcard:hover .pickcard__art { transform: scale(1.06); }
.pickcard__label {
  position: relative; z-index: 2; padding: var(--s-5) var(--s-3) var(--s-3);
  font-size: var(--t-sm); font-weight: 700; color: #fff; text-align: left;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
  background: linear-gradient(to top, rgba(0, 0, 0, 0.72), rgba(0, 0, 0, 0));
}
.pickcard__check {
  position: absolute; top: var(--s-2); right: var(--s-2); z-index: 3;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--gradient); color: #fff;
  display: none; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700; box-shadow: var(--sh-sm);
}
.pickcard:has(input:checked) .pickcard__check,
.style-card:has(input:checked) .pickcard__check { display: flex; }

/* Generated category art — pure CSS gradients, zero bytes over the wire.
   Replaces the 1.2 MB of photographic thumbnails the old build shipped. */
.art { position: absolute; inset: 0; }
.art::after {
  content: ""; position: absolute; inset: 0;
  background-image:
    radial-gradient(circle at 22% 26%, rgba(255, 255, 255, 0.42), transparent 45%),
    radial-gradient(circle at 78% 74%, rgba(255, 255, 255, 0.22), transparent 50%);
}
.art--animate      { background: linear-gradient(135deg, #38bdf8, #6366f1 55%, #a855f7); }
.art--story        { background: linear-gradient(135deg, #fb923c, #ec4899 55%, #8b5cf6); }
.art--knowledge    { background: linear-gradient(135deg, #22d3ee, #3b82f6 55%, #6366f1); }
.art--film         { background: linear-gradient(135deg, #f472b6, #a855f7 55%, #4f46e5); }
.art--kids         { background: linear-gradient(135deg, #fbbf24, #fb7185 55%, #c084fc); }
.art--motivation   { background: linear-gradient(135deg, #34d399, #06b6d4 55%, #3b82f6); }

.art__mark {
  position: absolute; z-index: 1; top: 50%; left: 50%;
  transform: translate(-50%, -60%);
  width: 38%; height: 38%; color: #fff; opacity: 0.95;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.28));
}

/* Progress */
.progress { display: flex; gap: var(--s-1); }
.progress__seg {
  flex: 1; height: 5px; border-radius: var(--r-full);
  background: var(--line); transition: background var(--dur-3) var(--ease);
}
.progress__seg.is-done { background: var(--gradient); }

/* Spinner + skeleton */
.spinner {
  width: 18px; height: 18px; flex-shrink: 0; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--brand);
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.skeleton {
  border-radius: var(--r-md);
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--line) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s ease infinite;
}
@keyframes shimmer { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }

/* Empty states */
.empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--s-3); padding: var(--s-12) var(--s-6); color: var(--ink-2);
}
.empty__icon {
  width: 56px; height: 56px; border-radius: var(--r-lg);
  display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand);
}
.empty__icon svg { width: 26px; height: 26px; }
.empty h3 { color: var(--ink); }

/* Notices */
.notice {
  display: flex; gap: var(--s-3); align-items: flex-start;
  padding: var(--s-3) var(--s-4); border-radius: var(--r-md);
  background: var(--info-soft); color: var(--info);
  font-size: var(--t-sm); line-height: 1.55;
}
.notice--warn { background: var(--warn-soft); color: var(--warn); }
.row__error { margin: 6px 0 0; font-size: var(--t-sm); color: var(--danger); line-height: 1.45; overflow-wrap: anywhere; }
.notice--danger { background: var(--danger-soft); color: var(--danger); }
/* Edit mode on a series: says so above every step, with the way out. */
.notice--edit { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; background: var(--brand-soft); color: var(--brand-strong); border: 1px solid var(--brand-soft-line); }
.notice--edit .btn { margin-left: auto; }
.notice code {
  font-family: ui-monospace, "Cascadia Code", "SFMono-Regular", Consolas, monospace;
  font-size: 0.92em; padding: 1px 5px; border-radius: 5px;
  background: rgba(0, 0, 0, 0.06);
}

/* ------------------------------------------------------------- layout -- */
.shell { display: flex; min-height: 100vh; }

.side {
  position: fixed; inset: 0 auto 0 0; width: calc(268px * var(--ui)); z-index: var(--z-nav);
  display: flex; flex-direction: column; gap: var(--s-6);
  padding: var(--s-6) var(--s-4);
  background: var(--surface); border-right: 1px solid var(--line);
  overflow-y: auto;
}
.side__top { display: flex; align-items: center; gap: var(--s-3); }

.brand {
  display: flex; align-items: center; gap: var(--s-3); flex: 1;
  font: inherit; background: none; border: none; padding: 0;
  color: inherit; cursor: pointer; text-align: left;
}
.brand__mark {
  width: 32px; height: 32px; border-radius: var(--r-sm);
  background: var(--gradient); flex-shrink: 0; box-shadow: var(--sh-brand);
}
.brand__name { font-weight: 700; font-size: var(--t-md); letter-spacing: -0.02em; }
.brand:hover .brand__name { color: var(--brand); }

.hamburger {
  display: none; flex-direction: column; justify-content: center; gap: 4px;
  width: 40px; height: 40px; padding: 0 10px; flex-shrink: 0;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface); cursor: pointer;
}
.hamburger span { display: block; height: 2px; background: var(--ink); border-radius: 2px;
  transition: transform var(--dur-2) var(--ease), opacity var(--dur-1) var(--ease); }
.hamburger[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.hamburger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.hamburger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.nav { display: flex; flex-direction: column; gap: 2px; }
.nav__label {
  font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-3);
  padding: 0 var(--s-3); margin-bottom: var(--s-2);
}
.nav__back {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-sm); font-weight: 600; color: var(--ink-2);
  padding: var(--s-2) var(--s-3); border-radius: var(--r-sm); margin-bottom: var(--s-2);
}
.nav__back:hover { color: var(--brand); background: var(--surface-2); text-decoration: none; }
.nav-link {
  display: flex; align-items: center; gap: var(--s-3);
  font: inherit; font-size: var(--t-base); font-weight: 500; text-align: left;
  background: none; border: none; color: var(--ink-2);
  padding: 10px var(--s-3); border-radius: var(--r-md); cursor: pointer;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.nav-link svg { width: 18px; height: 18px; flex-shrink: 0; opacity: 0.75; }
.nav-link:hover:not(.active) { background: var(--surface-2); color: var(--ink); }
.nav-link.active { background: var(--brand-soft); color: var(--brand); font-weight: 600; }
.nav-link.active svg { opacity: 1; }

/* margin-left must track the sidebar width exactly - the sidebar is fixed,
   so this is what stops the content sitting underneath it. It was a literal
   268px while the sidebar scaled with --ui, leaving a dead gap down the
   left of every page. */
.main {
  flex: 1; min-width: 0;
  margin-left: calc(268px * var(--ui));
  /* Less at the bottom than it looks: the action bar is sticky and brings
     its own padding, so the old 80px was empty space under a bar that was
     already pinned there. */
  padding: var(--s-8) var(--s-8) var(--s-8);
}
.wrap { max-width: calc(900px / var(--ui)); margin: 0 auto; }
.wrap--wide { max-width: 1120px; }

.page-head { margin-bottom: var(--s-6); }
.eyebrow {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--brand);
  background: var(--brand-soft); padding: 5px var(--s-3);
  border-radius: var(--r-full); margin-bottom: var(--s-3);
}
.page-head h1 { margin-bottom: var(--s-3); }
.lede { font-size: var(--t-md); color: var(--ink-2); line-height: 1.65; max-width: 68ch; }

/* ------------------------------------------------------------ landing -- */
.landing { min-height: 100vh; }
.landing__bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-5) var(--s-8); max-width: 1200px; margin: 0 auto;
}
.landing__actions { display: flex; align-items: center; gap: var(--s-3); }

.hero {
  position: relative; overflow: hidden;
  max-width: 1200px; margin: 0 auto;
  padding: var(--s-16) var(--s-8) var(--s-12);
  text-align: center;
}
.hero::before {
  content: ""; position: absolute; z-index: 0;
  top: -220px; left: 50%; transform: translateX(-50%);
  width: 760px; height: 560px; pointer-events: none;
  background: radial-gradient(circle, rgba(168, 85, 247, 0.20), transparent 62%);
}
.hero > * { position: relative; z-index: 1; }
.hero h1 { font-size: var(--t-4xl); margin-bottom: var(--s-5); letter-spacing: -0.035em; }
.hero__grad { background: var(--gradient); -webkit-background-clip: text; background-clip: text; color: transparent; }
.hero__sub { font-size: var(--t-lg); color: var(--ink-2); max-width: 56ch; margin: 0 auto var(--s-8); }
.hero__cta { display: flex; justify-content: center; gap: var(--s-3); flex-wrap: wrap; }
.hero__note { margin-top: var(--s-5); font-size: var(--t-sm); color: var(--ink-3); }

.platforms { display: inline-flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-8); }
.platforms__label { font-size: var(--t-sm); color: var(--ink-3); }
.pm {
  width: 30px; height: 30px; border-radius: var(--r-sm);
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: var(--sh-sm);
}
.pm svg { width: 16px; height: 16px; }
.pm--ig { background: linear-gradient(45deg, #f9ce34, #ee2a7b 50%, #6228d7); }
.pm--tiktok { background: #010101; }
.pm--youtube { background: #ff0000; }

.section { max-width: 1120px; margin: 0 auto; padding: var(--s-12) var(--s-8) var(--s-20); }
.section__title { text-align: center; font-size: var(--t-2xl); margin-bottom: var(--s-2); }
.section__sub { text-align: center; color: var(--ink-2); margin-bottom: var(--s-8); }

/* Niche cards (landing + in-app picker share one look) */
.niches { display: grid; gap: var(--s-4); grid-template-columns: repeat(3, 1fr); }
.niche-card {
  position: relative; display: flex; flex-direction: column; text-align: left;
  overflow: hidden; cursor: pointer; padding: 0;
  border: 1px solid var(--line); border-radius: var(--r-xl);
  background: var(--surface); box-shadow: var(--sh-sm);
  transition: transform var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease),
              border-color var(--dur-2) var(--ease);
}
.niche-card:hover { transform: translateY(-4px); box-shadow: var(--sh-lg); border-color: var(--brand-soft-line); }
.niche-card__art { position: relative; height: 128px; overflow: hidden; }
.niche-card:hover .art__mark { transform: translate(-50%, -60%) scale(1.08); }
.art__mark { transition: transform var(--dur-3) var(--ease); }
.niche-card__body { padding: var(--s-4) var(--s-5) var(--s-5); }
.niche-card__title { display: flex; align-items: center; gap: var(--s-2); font-size: var(--t-md); font-weight: 700; }
.niche-card__blurb { margin-top: 4px; font-size: var(--t-sm); color: var(--ink-2); line-height: 1.5; }

.niche-card--locked { cursor: default; }
.niche-card--locked:hover { transform: none; box-shadow: var(--sh-sm); border-color: var(--line); }
.niche-card--locked .niche-card__art { filter: saturate(0.35); opacity: 0.6; }
.niche-card--locked .niche-card__title { color: var(--ink-2); }

.landing__footer {
  display: flex; justify-content: center; gap: var(--s-6); flex-wrap: wrap;
  padding: var(--s-8); border-top: 1px solid var(--line);
  font-size: var(--t-sm);
}
.landing__footer a { color: var(--ink-3); }
.landing__footer a:hover { color: var(--brand); }

/* --------------------------------------------------------------- flow -- */
.flow__head { margin-bottom: var(--s-6); }
.flow__meta {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); margin-top: var(--s-3);
  font-size: var(--t-xs); font-weight: 600; color: var(--ink-3);
}
/* It is an h1 so the page has a heading, but it is a label in the
   progress line, not a banner. Everything the h1 rule brings - 36px,
   bold, tight letter-spacing, its own margin - has to be undone, or
   marking it up correctly costs the space removing the old title just
   saved. */
.flow__stepname {
  color: var(--brand);
  font-size: inherit; font-weight: inherit;
  letter-spacing: normal; line-height: inherit; margin: 0;
}

.flow__stage { position: relative; overflow: hidden; }
.flow__step { animation: stepIn var(--dur-3) var(--ease); }
@keyframes stepIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
.flow__title { font-size: var(--t-xl); margin-bottom: var(--s-2); }
.flow__hint { font-size: var(--t-sm); color: var(--ink-2); margin-bottom: var(--s-6); }

/* Sticky, because the way forward should never be the one thing off the
   screen. Step content varies from four fields to a grid of thirty-three
   thumbnails, so no amount of trimming the page keeps Back and Continue
   reliably above the fold - pinning them does, at every step and every
   height. It scrolls with the page until it reaches the bottom edge, so on
   a short step it still sits under the content rather than floating. */
.flow__nav {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  position: sticky; bottom: 0; z-index: var(--z-sticky);
  margin-top: var(--s-8); padding: var(--s-4) 0;
  border-top: 1px solid var(--line);
  /* Opaque: the content scrolls underneath it. */
  background: var(--canvas);
}
.flow__nav-right { display: flex; align-items: center; gap: var(--s-3); margin-left: auto; }

/* --------------------------------------------------------- job output -- */
.job { margin-top: var(--s-8); }
.job__status {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-4) var(--s-5); border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--sh-sm);
  font-size: var(--t-base);
}
.job__status.is-error { background: var(--danger-soft); border-color: transparent; color: var(--danger); }

.stages { display: flex; flex-direction: column; gap: var(--s-2); margin-top: var(--s-4); }
.stage {
  display: flex; align-items: center; gap: var(--s-3);
  font-size: var(--t-sm); color: var(--ink-3);
}
.stage__dot {
  width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0;
  border: 2px solid var(--line); display: grid; place-items: center;
  font-size: 11px; font-weight: 700;
}
.stage.is-done { color: var(--ink-2); }
.stage.is-done .stage__dot { background: var(--ok); border-color: var(--ok); color: #fff; }
.stage.is-active { color: var(--ink); font-weight: 600; }
.stage.is-active .stage__dot { border-color: var(--brand); border-top-color: transparent; animation: spin 0.9s linear infinite; }

.result { margin-top: var(--s-6); }
.result video {
  width: 100%; max-width: 340px; margin: 0 auto;
  border-radius: var(--r-xl); background: #000; box-shadow: var(--sh-lg);
}
.result__actions { display: flex; justify-content: center; gap: var(--s-3); flex-wrap: wrap; margin-top: var(--s-5); }

.publish-panel { margin-top: var(--s-6); }
.publish-panel__title { font-size: var(--t-md); font-weight: 700; margin-bottom: var(--s-4); }
.publish-result { margin-top: var(--s-3); padding: var(--s-3) var(--s-4); border-radius: var(--r-md); font-size: var(--t-sm); background: var(--surface-2); }
.publish-result--ok { background: var(--ok-soft); color: var(--ok); }
.publish-result--err { background: var(--danger-soft); color: var(--danger); }

/* ------------------------------------------------------------- series -- */
.list { display: flex; flex-direction: column; gap: var(--s-3); }

/* Grouped lists (Running series, My videos): one collapsible section per
   niche and subject, one open at a time. */
.groups { display: flex; flex-direction: column; gap: var(--s-3); }
.group { border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface-2); }
.group__head {
  display: flex; align-items: center; gap: var(--s-3); width: 100%;
  padding: var(--s-4) var(--s-5); background: none; border: 0; color: var(--ink);
  font: inherit; text-align: left; cursor: pointer; border-radius: var(--r-lg);
}
.group__head:focus-visible { outline: 2px solid var(--line-strong); outline-offset: 2px; }
.group__title { font-weight: 700; font-size: var(--t-base); min-width: 0; }
.group__meta { margin-left: auto; font-size: var(--t-xs); color: var(--ink-3); white-space: nowrap; }
.group__chev { display: inline-flex; color: var(--ink-3); transition: transform 0.18s ease; }
.group__chev svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 2; }
.group.is-open .group__chev { transform: rotate(180deg); }
.group__body { padding: 0 var(--s-4) var(--s-4); }
@media (max-width: 640px) {
  .group__head { flex-wrap: wrap; padding: var(--s-3) var(--s-4); }
  .group__meta { margin-left: 0; order: 3; width: 100%; white-space: normal; }
}
@media (prefers-reduced-motion: reduce) { .group__chev { transition: none; } }

/* Stat tiles: the series overview and each group's header. */
.groups-page { display: flex; flex-direction: column; gap: var(--s-4); }
.overview { padding: var(--s-4) var(--s-5); }
.stats { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.stat {
  display: inline-flex; flex-direction: column; gap: 2px; min-width: 92px;
  padding: var(--s-2) var(--s-3); border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface);
}
.stat__label { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-3); white-space: nowrap; }
.stat__value { font-weight: 700; font-size: var(--t-base); color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.stat__bar { display: block; height: 4px; margin-top: 4px; border-radius: 2px; background: var(--line); overflow: hidden; }
.stat__fill { display: block; height: 100%; border-radius: 2px; background: var(--ok); }
.stats--full .stat { flex: 1 1 140px; }
.stats--full .stat__value { font-size: var(--t-lg, 1.25rem); }
/* In a group header the tiles replace the plain "4 series · next in 2h". */
.group__meta .stats { justify-content: flex-end; }
.group__meta .stat { min-width: 78px; padding: 6px var(--s-2); }
@media (max-width: 900px) {
  .group__head { flex-wrap: wrap; }
  .group__meta { margin-left: 0; order: 3; width: 100%; }
  .group__meta .stats { justify-content: flex-start; }
}
.row {
  padding: var(--s-4) var(--s-5); border: 1px solid var(--line);
  border-radius: var(--r-lg); background: var(--surface); box-shadow: var(--sh-xs);
}
.row__head { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.row__title { font-weight: 700; font-size: var(--t-base); }
.row__meta { margin-top: var(--s-1); font-size: var(--t-xs); color: var(--ink-3); }
.row__stats {
  display: flex; flex-wrap: wrap; gap: var(--s-1) var(--s-4);
  margin-top: var(--s-2); font-size: var(--t-xs); color: var(--ink-2);
}
.row__stat strong { color: var(--ink); font-weight: 700; }
.row__stat--bad strong { color: var(--danger); }
.row__actions { display: flex; gap: var(--s-2); margin-top: var(--s-3); flex-wrap: wrap; }

/* One column of wide rows, not a poster grid. A finished video was
   rendering as a tall 9:16 card while a running one was a wide row, so an
   item changed shape the moment it succeeded and the list read as two
   unrelated things. Same row throughout; only the contents differ. */
.lib { display: grid; gap: var(--s-3); grid-template-columns: 1fr; }
.lib__item { border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; background: var(--surface); box-shadow: var(--sh-xs); }
/* min-width:0 and width:100% both matter: a grid item defaults to its
   min-content width, and a <video> carries an intrinsic 300x150, so without
   these the thumbnail pushes itself to 150px inside a 96px track and spills
   over the row. */
.lib__thumb {
  width: 100%; min-width: 0; aspect-ratio: 9 / 16;
  background: var(--surface-2); display: grid; place-items: center; overflow: hidden;
}
.lib__thumb video,
.lib__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lib__body { padding: var(--s-3) var(--s-4) var(--s-4); }
.lib__title { font-size: var(--t-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Every item is the same row: a square thumb and a body that says what
   is going on - progress, or a download and how long is left. */
.lib__item {
  display: grid; grid-template-columns: 96px 1fr; align-items: stretch;
}
.lib__thumb { aspect-ratio: 1; }
.lib__item.is-running { border-color: var(--brand-soft-line); background: var(--brand-soft); }
.lib__item.is-failed { border-color: var(--danger); background: var(--danger-soft); }
.lib__thumb-icon { width: 28px; height: 28px; color: var(--ink-3); }
.lib__stage { margin-top: 2px; font-size: var(--t-xs); font-weight: 600; color: var(--brand); }
.lib__error { margin: var(--s-2) 0 0; font-size: var(--t-xs); color: var(--danger); line-height: 1.5; }
/* The thumbnail is a button now, so it needs the button reset - a <button>
   brings its own border, padding and background that would box the video. */
.lib__thumb--play {
  position: relative; padding: 0; border: 0; cursor: pointer; font: inherit;
  overflow: hidden;
}
.lib__play {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: #fff; background: rgba(9, 12, 20, 0.34);
  transition: background var(--dur-1) var(--ease);
}
.lib__play svg { width: 26px; height: 26px; filter: drop-shadow(0 1px 3px rgba(0,0,0,.5)); }
.lib__thumb--play:hover .lib__play { background: rgba(9, 12, 20, 0.12); }
.lib__thumb--play:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* ------------------------------------------------------------- player -- */
.player { position: fixed; inset: 0; z-index: var(--z-modal); display: grid; place-items: center; padding: var(--s-5); }
.player__backdrop { position: absolute; inset: 0; background: rgba(9, 12, 20, 0.72); backdrop-filter: blur(3px); }
.player__card {
  position: relative; display: grid; gap: var(--s-3);
  width: 100%; max-width: 420px;
  padding: var(--s-4); border-radius: var(--r-xl);
  background: var(--surface); box-shadow: var(--sh-lg);
  animation: stepIn var(--dur-3) var(--ease);
}
.player__head { display: flex; align-items: center; gap: var(--s-3); }
.player__title {
  font-weight: 700; font-size: var(--t-sm); margin-right: auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.player__close {
  flex: none; width: 28px; height: 28px; border-radius: var(--r-full);
  border: 1px solid var(--line); background: var(--surface);
  color: var(--ink-2); cursor: pointer; font-size: 13px; line-height: 1;
}
.player__close:hover { color: var(--ink); background: var(--surface-2); }
/* The videos are 9:16. Capped against the viewport so a tall phone clip
   cannot push its own controls off the bottom of a laptop screen. */
.player__video {
  width: 100%; aspect-ratio: 9 / 16; max-height: 68vh;
  border-radius: var(--r-md); background: #000; object-fit: contain;
}
.player__actions { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.player__expiry { font-size: var(--t-xs); color: var(--ink-3); }
.player__expiry strong { color: var(--ink); font-variant-numeric: tabular-nums; }

.lib__actions { margin-top: var(--s-3); display: flex; gap: var(--s-2); flex-wrap: wrap; }
.lib__expiry { margin-top: var(--s-2); font-size: var(--t-xs); color: var(--ink-2); }
/* Tabular figures stop the row twitching as the seconds change width. */
.lib__countdown { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.lib__expiry-note { color: var(--ink-3); }
/* Horizontal on wide rows, so five stages read as a track rather than a
   column that pushes everything below it off the fold. */
.stages--compact { margin-top: var(--s-3); display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--s-2) var(--s-4); }
.stages--compact .stage { font-size: var(--t-xs); }

@media (max-width: 640px) {
  /* Two columns of controls is too tight to read on a phone. */
  .flow__fields { grid-template-columns: minmax(0, 1fr); }
  /* Already one column; nothing to narrow. */
  /* 96px of thumbnail is a third of a phone screen; shrink it rather than
     stacking, so the title and countdown stay on the same line as the art. */
  .lib__item { grid-template-columns: 72px 1fr; }
  .stages--compact { flex-direction: column; gap: var(--s-2); }
}

/* ---------------------------------------------------- publish targets -- */
.accounts { display: grid; gap: var(--s-2); }
.account {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-3); cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
  transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.account:hover { border-color: var(--line-strong); background: var(--surface-2); }
/* Hidden from sight, never from the tab order. */
.account input {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; clip: rect(0 0 0 0); overflow: hidden; white-space: nowrap;
}
/* A square, not a circle: these are independent choices, and the shape is
   what tells someone they can pick more than one. */
.account__box {
  flex: none; width: 16px; height: 16px; border-radius: 4px;
  border: 2px solid var(--line-strong); display: grid; place-items: center;
}
.account__box::after {
  content: ""; width: 8px; height: 8px; border-radius: 1px;
  background: var(--brand); transform: scale(0);
  transition: transform var(--dur-1) var(--ease);
}
.account:has(input:checked) { border-color: var(--brand); background: var(--brand-soft); }
.account input:checked ~ .account__box { border-color: var(--brand); }
.account input:checked ~ .account__box::after { transform: scale(1); }
.account input:focus-visible ~ .account__box { box-shadow: 0 0 0 3px var(--brand-soft-line); }
.account__text { display: grid; gap: 1px; min-width: 0; }
.account__name { font-size: var(--t-sm); font-weight: 600; color: var(--ink); }
.account__meta { font-size: var(--t-xs); color: var(--ink-3); }

/* ------------------------------------------------------------- voices -- */
/* Scrolls rather than growing without limit: an account can have dozens of
   voices, and a list that long would push everything else off the step. */
.voices { display: grid; gap: var(--s-2); max-height: 232px; overflow-y: auto; padding-right: var(--s-2); }
.voice {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-3); cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
  transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.voice:hover { border-color: var(--line-strong); background: var(--surface-2); }
/* Visually hidden, never display:none - it still takes focus and keystrokes. */
.voice input {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; clip: rect(0 0 0 0); overflow: hidden; white-space: nowrap;
}
.voice__dot {
  flex: none; width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--line-strong); display: grid; place-items: center;
}
.voice__dot::after {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: var(--brand); transform: scale(0);
  transition: transform var(--dur-1) var(--ease);
}
.voice:has(input:checked) { border-color: var(--brand); background: var(--brand-soft); }
.voice input:checked ~ .voice__dot { border-color: var(--brand); }
.voice input:checked ~ .voice__dot::after { transform: scale(1); }
.voice input:focus-visible ~ .voice__dot { box-shadow: 0 0 0 3px var(--brand-soft-line); }
.voice__text { display: grid; gap: 1px; margin-right: auto; min-width: 0; }
.voice__name { font-size: var(--t-sm); font-weight: 600; color: var(--ink); }
.voice__meta { font-size: var(--t-xs); color: var(--ink-3); text-transform: capitalize; }
.voice__play {
  flex: none; width: 28px; height: 28px; border-radius: var(--r-full);
  border: 1px solid var(--line); background: var(--surface);
  color: var(--brand); font-size: 11px; line-height: 1; cursor: pointer;
}
.voice__play:hover { border-color: var(--brand); background: var(--brand-soft); }

/* ------------------------------------------------------ social accounts -- */
/* Each platform in its own colour. A brand mark in a neutral grey is just a
   shape; the colour is most of what makes it recognisable at a glance. */
.brandmark { display: inline-grid; place-items: center; width: 22px; height: 22px; flex: none; }
.brandmark svg { width: 100%; height: 100%; }
.brandmark--instagram { color: #e1306c; }
.brandmark--facebook  { color: #1877f2; }
.brandmark--tiktok    { color: #111827; }
.brandmark--youtube   { color: #ff0000; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .brandmark--tiktok { color: #f4f4f5; }
}
:root[data-theme="dark"] .brandmark--tiktok { color: #f4f4f5; }

.platgrid {
  display: grid; gap: var(--s-3); margin: var(--s-4) 0;
  grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
}
.plat {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); cursor: pointer;
  font: inherit; font-size: var(--t-sm); font-weight: 600; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
  transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.plat:hover { border-color: var(--line-strong); background: var(--surface-2); }
.plat.is-on { border-color: var(--brand); background: var(--brand-soft); }
/* Still clickable - it explains why it cannot be connected, which a disabled
   button cannot do. */
.plat.is-soon { opacity: 0.72; }
.plat__label { margin-right: auto; }

/* A paused account is still listed, just visibly not in play. */
.row.is-off { opacity: 0.6; }
.row__head .brandmark { margin-right: var(--s-1); }

/* --------------------------------------------------------------- auth -- */
.auth-user { position: fixed; top: var(--s-4); right: var(--s-5); z-index: var(--z-sticky); }
/* Inside the app the profile sits at the foot of the nav column, which is
   where account controls are conventionally looked for, and leaves the top
   right free for the page itself. Positioned rather than re-parented: the
   element is fixed to the viewport in both views, so a class is enough and
   no DOM node moves between containers. 268px is the sidebar width. */
body.in-app .auth-user {
  top: auto; right: auto;
  bottom: var(--s-4); left: var(--s-4);
  width: calc(268px * var(--ui) - var(--s-4) * 2);
}
/* Opens upward here, or it would fall off the bottom of the screen. */
/* Pinned to both edges of the widget instead of given a width: the nav
   column is narrower than the 248px the landing page uses, so a fixed
   width made the panel wider than the bar it hangs off. */
body.in-app .auth-menu {
  top: auto; bottom: calc(100% + 8px);
  left: 0; right: 0; width: auto;
}
body.in-app .auth-avatar-btn { width: 100%; }

.auth-avatar-btn {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 6px var(--s-3) 6px 6px; border-radius: var(--r-full);
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--sh-md); cursor: pointer; font: inherit; color: inherit;
}
.auth-avatar-btn:hover { border-color: var(--line-strong); }
.auth-avatar {
  width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
  display: grid; place-items: center;
  background: var(--gradient); background-size: cover; background-position: center;
  color: #fff; font-size: var(--t-xs); font-weight: 700;
}
.auth-avatar.has-photo { background-color: transparent; }
.auth-avatar-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.auth-user-name { font-size: var(--t-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.auth-user-sub { font-size: var(--t-xs); color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.auth-chevron { width: 15px; height: 15px; color: var(--ink-3); transition: transform var(--dur-2) var(--ease); }
.auth-avatar-btn[aria-expanded="true"] .auth-chevron { transform: rotate(180deg); }

.auth-menu {
  position: absolute; top: calc(100% + 8px); right: 0; width: 248px; max-width: 100%;
  padding: var(--s-4); border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--sh-lg);
}
.auth-menu-head { padding-bottom: var(--s-3); border-bottom: 1px solid var(--line); margin-bottom: var(--s-3); }
.auth-menu-name { display: block; font-weight: 700; font-size: var(--t-sm); }
.auth-menu-email { display: block; font-size: var(--t-xs); color: var(--ink-3); }
.auth-menu-facts { margin: 0 0 var(--s-3); display: flex; flex-direction: column; gap: 6px; }
.auth-menu-facts > div { display: flex; justify-content: space-between; gap: var(--s-3); font-size: var(--t-xs); }
.auth-menu-facts dt { color: var(--ink-3); }
.auth-menu-facts dd { margin: 0; font-weight: 600; }
.auth-menu-actions { display: flex; flex-direction: column; gap: 2px; padding-bottom: var(--s-3); border-bottom: 1px solid var(--line); margin-bottom: var(--s-3); }
.auth-menu-link { font-size: var(--t-sm); color: var(--ink-2); padding: 6px var(--s-2); border-radius: var(--r-sm); }
.auth-menu-link:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
.auth-signout {
  width: 100%; font: inherit; font-size: var(--t-sm); font-weight: 600;
  padding: 9px; border-radius: var(--r-md); cursor: pointer;
  background: var(--danger-soft); color: var(--danger); border: none;
}

.auth-modal { position: fixed; inset: 0; z-index: var(--z-modal); display: grid; place-items: center; padding: var(--s-5); }
.auth-modal__backdrop { position: absolute; inset: 0; background: rgba(9, 12, 20, 0.55); backdrop-filter: blur(3px); }
.auth-card {
  position: relative; width: 100%; max-width: 400px;
  padding: var(--s-8); border-radius: var(--r-2xl);
  background: var(--surface); box-shadow: var(--sh-lg); text-align: center;
  animation: stepIn var(--dur-3) var(--ease);
}
.auth-card h2 { margin-bottom: var(--s-2); }
.auth-card__sub { font-size: var(--t-sm); color: var(--ink-2); margin-bottom: var(--s-6); }
.auth-card__close {
  position: absolute; top: var(--s-4); right: var(--s-4);
  width: 32px; height: 32px; border-radius: 50%; border: none;
  background: var(--surface-2); color: var(--ink-2); cursor: pointer; font-size: 17px; line-height: 1;
}
.auth-card__close:hover { background: var(--line); color: var(--ink); }
.auth-or { display: flex; align-items: center; gap: var(--s-3); margin: var(--s-5) 0; color: var(--ink-3); font-size: var(--t-xs); }
.auth-or::before, .auth-or::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.auth-error { margin-bottom: var(--s-4); font-size: var(--t-sm); color: var(--danger); }
.auth-fineprint { margin-top: var(--s-5); font-size: var(--t-xs); color: var(--ink-3); }
.g-mark {
  width: 18px; height: 18px; border-radius: 50%; background: #fff;
  display: inline-grid; place-items: center; font-weight: 700; font-size: 12px;
  color: #4285f4; box-shadow: inset 0 0 0 1px var(--line);
}

/* ------------------------------------------------------ policy pages -- */
.policy { max-width: 760px; margin: 0 auto; padding: var(--s-12) var(--s-6) var(--s-20); }
.policy h1 { margin-bottom: var(--s-3); }
.policy h2 { font-size: var(--t-lg); margin: var(--s-8) 0 var(--s-3); }
.policy p, .policy li { color: var(--ink-2); line-height: 1.7; }
.policy ul { padding-left: var(--s-5); }
.policy a { font-weight: 600; }

/* --------------------------------------------------------- responsive -- */
/* Three breakpoints, not the previous four-with-one-repeated-three-times. */
@media (max-width: 1200px) {
  .pickgrid { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 900px) {
  /* The sidebar is position:fixed on desktop, so it sits outside the flex
     row and .shell only ever lays out .main. Putting it back in flow for
     mobile also puts it back in that row - so the axis has to flip here, or
     the nav and the page render as two side-by-side columns. */
  .shell { flex-direction: column; }
  .side {
    /* inset:auto first, then top - the shorthand resets top, so the reverse
       order silently cancels the sticky offset. */
    position: sticky; inset: auto; top: 0; width: auto; height: auto;
    flex-direction: row; align-items: center; gap: var(--s-4);
    padding: var(--s-3) var(--s-4);
    border-right: none; border-bottom: 1px solid var(--line);
    overflow: visible;
  }
  .side__top { flex: 1; }
  /* On mobile the nav is a top bar, so the foot of a column does not exist
     to sit at. A full-width bar pinned to the bottom would cover the
     Continue/Generate buttons, which are the whole point of these screens,
     so it shrinks to a floating avatar in the bottom-left: reachable by
     thumb, clear of the primary action on the right, and out of the way of
     the content between them. */
  /* The action bar is now sticky along the bottom edge, so the foot of the
     screen is taken. On mobile the nav IS the top bar, so the profile goes
     into it - left of the hamburger, which is the only free corner and is
     still "in the nav". Bottom-left would sit underneath Back. */
  body.in-app .auth-user {
    width: auto;
    bottom: auto; left: auto;
    top: 10px; right: calc(var(--s-4) + 46px);
  }
  body.in-app .auth-avatar-btn { width: auto; padding: 4px; box-shadow: none; border-color: transparent; }
  /* Opens downward again here: it hangs off the top bar, not off a footer. */
  /* Here the widget is a bare avatar, so matching its width would give a
     42px panel. It gets a real width again, capped to the viewport so it
     cannot run off the edge of a phone. */
  body.in-app .auth-menu {
    top: calc(100% + 8px); bottom: auto; left: auto; right: 0;
    width: 248px; max-width: calc(100vw - var(--s-8));
  }
  /* The name and role are in the menu this opens - repeating them in a
     floating pill costs the width the page needs. */
  body.in-app .auth-avatar-text, body.in-app .auth-chevron { display: none; }

  .hamburger { display: flex; }
  .nav {
    position: absolute; top: 100%; left: 0; right: 0; z-index: var(--z-nav);
    padding: var(--s-3); gap: var(--s-1);
    background: var(--surface); border-bottom: 1px solid var(--line);
    box-shadow: var(--sh-md);
  }
  .nav[hidden] { display: none !important; }
  .main { margin-left: 0; padding: var(--s-6) var(--s-4) var(--s-16); }
  .niches { grid-template-columns: repeat(2, 1fr); }
  .hero { padding: var(--s-12) var(--s-5) var(--s-8); }
  .hero h1 { font-size: var(--t-3xl); }
  .hero__sub { font-size: var(--t-md); }
  .section { padding: var(--s-8) var(--s-5) var(--s-16); }
  .landing__bar { padding: var(--s-4) var(--s-5); }
  /* Keep the corner avatar clear of the sticky header row. */
  .auth-user { top: var(--s-2); right: var(--s-3); }
  .auth-avatar-text, .auth-chevron { display: none; }
  .auth-avatar-btn { padding: 5px; }
}

@media (max-width: 640px) {
  :root { --t-3xl: 30px; --t-4xl: 34px; --t-2xl: 24px; }
  .pickgrid { grid-template-columns: repeat(3, 1fr); gap: var(--s-2); }
  .niches { grid-template-columns: 1fr; }
  .card { padding: var(--s-5); border-radius: var(--r-lg); }
  /* Back sits on its own row and the action group takes the next one in
     full. Flexing only the direct .btn children used to stretch Back and
     shove the (nested) Continue button clean off the right edge. */
  .flow__nav { gap: var(--s-2); flex-wrap: wrap; }
  .flow__nav > .btn { flex: 1 1 auto; }
  .flow__nav-right { flex: 1 1 100%; margin-left: 0; }
  .flow__nav-right .btn { width: 100%; }
  .hero__cta .btn { width: 100%; }
  .result video { max-width: 100%; }
  .auth-card { padding: var(--s-6); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ------------------------------------------------------------- settings --
   Provider cards, quota bars and the per-video cost editor. Built from the
   existing tokens so this page inherits the rest of the app's density and
   dark-mode handling rather than defining its own. */
.prov__grid { display: grid; gap: calc(16px * var(--ui)); }
.prov { display: grid; gap: calc(12px * var(--ui)); }
.prov__head { display: flex; gap: calc(12px * var(--ui)); align-items: flex-start;
  justify-content: space-between; flex-wrap: wrap; }
.prov__title { display: flex; align-items: center; gap: calc(8px * var(--ui));
  font-weight: 650; font-size: calc(1.02rem * var(--ui)); }
.prov__src { flex: 0 0 auto; }
.prov__note { font-size: calc(0.86rem * var(--ui)); color: var(--muted); margin: 0; }
.prov__note.is-warn { color: var(--warn, #f59e0b); font-weight: 600; }
.prov__link { color: var(--brand); text-decoration: none; }
.prov__link:hover { text-decoration: underline; }

/* The key row: masked by default, and wide enough that a revealed key wraps
   instead of pushing the card sideways. */
.prov__keyrow { display: flex; align-items: center; gap: calc(8px * var(--ui)); flex-wrap: wrap; }
.prov__key { flex: 1 1 220px; min-width: 0; overflow-wrap: anywhere;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: calc(0.82rem * var(--ui)); padding: calc(6px * var(--ui)) calc(10px * var(--ui));
  border-radius: 8px; background: var(--surface-2, rgba(255,255,255,0.04));
  border: 1px solid var(--line); }

.prov__edit > summary { cursor: pointer; font-size: calc(0.88rem * var(--ui));
  color: var(--muted); }
.prov__form { display: grid; gap: calc(8px * var(--ui)); margin-top: calc(10px * var(--ui)); }
.prov__actions { display: flex; gap: calc(8px * var(--ui)); flex-wrap: wrap; }

.quota__head { display: flex; justify-content: space-between; align-items: baseline;
  gap: calc(8px * var(--ui)); flex-wrap: wrap; }
.quota__of { color: var(--muted); font-size: calc(0.84rem * var(--ui)); }
.quota__track { height: calc(8px * var(--ui)); border-radius: 999px; overflow: hidden;
  background: var(--surface-2, rgba(255,255,255,0.08)); margin: calc(6px * var(--ui)) 0; }
.quota__fill { height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--brand), var(--brand-2, var(--brand))); }
.quota__track.is-low .quota__fill { background: #ef4444; }

.prov__videos { border-top: 1px solid var(--line); padding-top: calc(10px * var(--ui)); }
.prov__videos-head { margin: 0 0 calc(6px * var(--ui)); font-size: calc(0.84rem * var(--ui));
  color: var(--muted); }
.vidrow { display: flex; justify-content: space-between; align-items: baseline;
  gap: calc(8px * var(--ui)); padding: calc(3px * var(--ui)) 0;
  font-size: calc(0.9rem * var(--ui)); }
.vidrow__cost { color: var(--muted); font-size: calc(0.8rem * var(--ui)); }

.estrow { display: flex; align-items: flex-end; gap: calc(10px * var(--ui));
  flex-wrap: wrap; padding: calc(8px * var(--ui)) 0; border-top: 1px solid var(--line); }
.estrow__name { flex: 1 1 160px; font-weight: 600; font-size: calc(0.92rem * var(--ui)); }
.estrow__f { display: grid; gap: 2px; font-size: calc(0.76rem * var(--ui)); color: var(--muted); }
.input--sm { width: calc(96px * var(--ui)); padding: calc(5px * var(--ui)) calc(8px * var(--ui)); }

.notice.is-error { border-color: #ef4444; color: #ef4444; }

@media (max-width: 640px) {
  .estrow { gap: calc(8px * var(--ui)); }
  .estrow__name { flex-basis: 100%; }
}

/* ----------------------------------------------- library: post + delete --
   Whether a video has already gone out, so the same Reel is not posted twice
   by accident, and the account picker reusing the flow's account rows. */
.lib__posted { font-size: calc(0.8rem * var(--ui)); font-weight: 600;
  margin: calc(4px * var(--ui)) 0; color: var(--muted); }
.lib__posted--posted { color: #22c55e; }
.lib__posted--failed { color: #ef4444; }
.lib__posted--posting, .lib__posted--scheduled { color: var(--brand); }
.lib__actions { flex-wrap: wrap; }
.lib__delete { color: var(--muted); }
.lib__delete:hover { color: #ef4444; }

.post-dialog { max-width: calc(460px * var(--ui)); width: 100%;
  display: grid; gap: calc(12px * var(--ui)); }
.post-dialog .accounts { display: grid; gap: calc(8px * var(--ui));
  max-height: 50vh; overflow-y: auto; }
.flow__publish-note { margin-top: calc(4px * var(--ui)); }

/* ------------------------------------------- social media account cards --
   How long each token is good for, the result of the last test, and the
   inline editor. */
.row__validity { margin: calc(2px * var(--ui)) 0; font-size: calc(0.84rem * var(--ui)); color: var(--muted); }
.row__validity.is-warn { color: var(--warn, #f59e0b); font-weight: 600; }
.row__problem { margin: calc(4px * var(--ui)) 0; font-size: calc(0.84rem * var(--ui)); color: #ef4444; }
.row__note.is-bad { color: #ef4444; }
.row__note.is-good { color: #22c55e; }
.row__actions { flex-wrap: wrap; }
.row__edit { display: grid; gap: calc(10px * var(--ui)); margin-top: calc(12px * var(--ui));
  padding-top: calc(12px * var(--ui)); border-top: 1px solid var(--line); }
