.modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.35);
  backdrop-filter: blur(var(--blur-scrim-2));
  /* v04d — bump above .sequence-backdrop (z-index 200) so that a
     shot popup opened from inside a sequence popup renders on top.
     Closing the shot popup returns focus to the sequence below. */
  z-index: var(--z-backdrop);
  display: grid; place-items: center;
  padding: 24px;
  /* v07zz66 — Hugo: "we still have the ugly flicker of the
     background and the disappearing/reappering of the shot modal
     when moving from one to the other." The fade-in animation on
     the backdrop was the main culprit — every shot nav cycled
     opacity 0 → 1 on the whole backdrop. Drop the animation so
     the backdrop stays opaque through nav. */
}
@keyframes modalFade { from { opacity: 0; } to { opacity: 1; } }

.modal-card {
  width: 100%;
  /* v06r — Hugo: widen the panel so the version filmstrip fits ~5
     tiles without scrolling. Matches the .grid-detail-modal width
     used in the Media page. AssetReviewModal still has its own
     larger .arm-shell width for full-screen review. */
  max-width: 880px;
  height: auto;
  max-height: 86vh;
  overflow-y: auto;
  padding: 26px 28px 22px;
  position: relative;
  display: flex; flex-direction: column;
  gap: 14px;
  /* v07zz66 — modalPop animation removed. Was scale 0.96 → 1
     every shot nav, which combined with the backdrop fade made
     the modal visibly "pop" between shots. Now the card just
     appears at full scale immediately — no flash. */
  /* Aero glass scrollbar — translucent, soft, no jarring colors. */
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--tan-deep) 45%, transparent) transparent;
  scrollbar-gutter: stable;
}
.modal-card::-webkit-scrollbar { width: 10px; }
.modal-card::-webkit-scrollbar-track { background: transparent; }
.modal-card::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--hi) 55%, transparent) 0%,
    color-mix(in srgb, color-mix(in srgb, var(--nav-active) 78%, var(--mix-dark)) 45%, transparent) 45%,
    color-mix(in srgb, color-mix(in srgb, var(--tan-deep) 88%, var(--mix-light)) 40%, transparent) 100%);
  border-radius: var(--r-round);
  border: 3px solid transparent;
  background-clip: padding-box;
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--cream) 55%, transparent),
    inset 0 -1px 0 color-mix(in srgb, var(--shadow-ink-2) 10%, transparent);
}
.modal-card::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--cream) 75%, transparent) 0%,
    color-mix(in srgb, color-mix(in srgb, var(--nav-active) 87%, var(--mix-dark)) 65%, transparent) 45%,
    color-mix(in srgb, color-mix(in srgb, var(--nav-active) 69%, var(--mix-dark)) 55%, transparent) 100%);
  background-clip: padding-box;
  border: 3px solid transparent;
}
.modal-card > * { flex-shrink: 0; }
@keyframes modalPop { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }

.modal-close-btn {
  position: absolute; top: 14px; right: 14px;
  width: 30px; height: 30px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--mix-light) 60%, transparent); border: 1px solid var(--card-border);
  color: var(--ink); display: grid; place-items: center;
  cursor: pointer; z-index: 2;
}
.modal-close-btn:hover { background: color-mix(in srgb, var(--mix-light) 85%, transparent); }

/* v07zz51 — Prev/next arrow buttons on the shot detail modal. Match
   the visual weight of the close button but sit on the outer edges
   of the card so they don't crowd the header. */
.modal-nav-btn {
  position: absolute;
  top: 50%; transform: translateY(-50%);
  width: 38px; height: 56px;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--mix-light) 55%, transparent);
  border: 1px solid var(--card-border);
  color: var(--ink);
  display: grid; place-items: center;
  cursor: pointer; z-index: 2;
  transition: background var(--dur-1), border-color var(--dur-1), opacity var(--dur-1);
}
.modal-nav-btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--mix-light) 85%, transparent);
  border-color: color-mix(in srgb, var(--taupe-5) 55%, transparent);
}
.modal-nav-btn:disabled {
  opacity: 0.25; cursor: not-allowed;
}
.modal-nav-prev { left: -22px; }
.modal-nav-next { right: -22px; }
@media (max-width: 900px) {
  .modal-nav-prev { left: 6px; }
  .modal-nav-next { right: 6px; }
}

.modal-header {
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* v06s — reserve more room on the right for the hero pill + the
     close button. The pill is absolutely positioned at the top-right
     of this header so the existing flex stack stays untouched. */
  padding-right: 56px;
  position: relative;
}
.md-id { font-family: var(--font-display); font-size: var(--fs-36); line-height: 1; color: var(--ink); letter-spacing: var(--track-005); }
/* v06s/v06x — Hero pill in the cream popup header. v06x: Hugo wants
   it aligned with the SEQUENCE chip baseline (bottom row of the
   header), NOT the top edge. Anchor to bottom-right so it sits on
   the same line as .md-chip.
   v07d — equal vertical padding (was 6px top / 4px bottom which
   pushed text low). Integer font-size + font-smoothing kill the
   crunchy aliasing Hugo noted. */
/* v07perf — Generate-from-modal pill. Hugo (round 3): "move the
   generate button here where the red mark is" (bottom-right of the
   modal-header, immediately to the LEFT of the FRAME HERO pill).
   "Also make it the same styling as the green generate button in
   the generate page in terms of colour style." Adopts the
   gen-go-btn green liquid-glass look (.gen-go-btn in generate.css),
   scaled to pill proportions. Sits at bottom: 0 like .md-hero-pill,
   right-offset by ~135px to clear the FRAME HERO label. */
/* v07perf round 4 — Both pills live in this flex cluster at the
   bottom-right of the modal-header, so when the hero pill widens
   ("FRAME HERO" → "HERO THIS FRAME") the Generate pill slides left
   to make room. No more fixed `right` offset to keep in sync. */
.md-pill-cluster {
  position: absolute;
  bottom: 0;
  right: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}
.md-pill-cluster > * { pointer-events: auto; }

.md-generate-pill {
  display: inline-flex;
  align-items: center;
  padding: 4px 14px;
  border-radius: var(--r-round);
  border: 1px solid color-mix(in srgb, var(--leaf) 65%, transparent);
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--ivory) 35%, transparent) 0%,
      color-mix(in srgb, var(--ivory) 8%, transparent) 50%,
      color-mix(in srgb, var(--black) 5%, transparent) 100%),
    linear-gradient(180deg, color-mix(in srgb, var(--leaf) 78%, transparent) 0%, color-mix(in srgb, var(--olive-2) 85%, transparent) 100%);
  color: var(--ink-cream);
  font-size: var(--fs-11);
  font-weight: var(--fw-bold);
  letter-spacing: var(--track-08);
  text-transform: uppercase;
  cursor: pointer;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  text-shadow: 0 1px 1px color-mix(in srgb, var(--forest) 35%, transparent);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--ivory-2) 55%, transparent),
    inset 0 -1px 0 color-mix(in srgb, var(--surface-forest) 30%, transparent),
    0 3px 8px color-mix(in srgb, var(--surface-forest) 22%, transparent),
    0 1px 2px color-mix(in srgb, var(--surface-forest) 15%, transparent);
  z-index: 2;
  transition: filter var(--dur-1) ease, transform 90ms ease, box-shadow var(--dur-1) ease;
}
.md-generate-pill:hover {
  filter: brightness(1.08);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--ivory-2) 65%, transparent),
    inset 0 -1px 0 color-mix(in srgb, var(--surface-forest) 30%, transparent),
    0 5px 12px color-mix(in srgb, var(--surface-forest) 30%, transparent),
    0 1px 3px color-mix(in srgb, var(--surface-forest) 20%, transparent);
}
.md-generate-pill:active { transform: translateY(1px); }

.md-hero-pill {
  /* v07perf round 4 — was position:absolute bottom:0 right:0. Now lives
     inside .md-pill-cluster (flex). Static positioning lets the cluster
     handle layout + gap to the Generate pill on the left. */
  background: var(--pill-bg);
  cursor: pointer;
  pointer-events: auto;
  height: auto;
  padding: 4px 14px 4px 10px;
  border-width: 1.5px;
  /* Integer font-size (was 10.5px on the base .shot-pill → fractional
     pixel rounding made the glyphs look aliased/crunchy). Bumping
     weight + adding text-rendering + font-smoothing brings it in
     line with the cleaner row pills. */
  font-size: var(--fs-11);
  font-weight: var(--fw-bold);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  transition: background var(--dur-1) ease, transform 90ms ease, box-shadow var(--dur-1) ease;
}
.md-hero-pill .shot-pill-label {
  /* Match the dark pill's letter-spacing for visual consistency. */
  letter-spacing: var(--track-08);
}
.md-hero-pill:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 6px color-mix(in srgb, var(--shadow-ink) 10%, transparent);
}
.md-hero-pill.is-active {
  /* Soft tint of the border colour so the pill reads as "engaged"
     without changing its silhouette. */
  background: color-mix(in srgb, var(--leaf) 18%, transparent);
  box-shadow:
    0 0 0 var(--hairline-w) color-mix(in srgb, var(--leaf) 35%, transparent),
    0 0 10px color-mix(in srgb, var(--leaf) 20%, transparent);
}
/* v07c — star replaces the dot on the hero pill. Same coloured-by-
   stage palette as the dot was, using inline-style `color:` from JSX. */
.md-hero-pill-star {
  display: inline-grid;
  place-items: center;
  width: 11px;
  height: 11px;
  flex-shrink: 0;
  align-self: center;
}
.md-frame { font-size: var(--fs-16); color: var(--ink); font-weight: var(--fw-med); }
/* v07zz314 — click-to-rename the shot title */
.md-frame--editable { cursor: text; border-radius: 5px; padding: 1px 5px; margin: -1px -5px; transition: background var(--dur-1); }
.md-frame--editable:hover { background: color-mix(in srgb, var(--black) 5%, transparent); }
.md-frame-edit {
  font-size: var(--fs-16); color: var(--ink); font-weight: var(--fw-med); font-family: inherit;
  background: color-mix(in srgb, var(--mix-light) 70%, transparent); border: 1px solid var(--accent, var(--leaf));
  border-radius: 5px; padding: 1px 5px; margin: -1px -5px; outline: none; min-width: 240px; max-width: 100%;
}
.md-chip {
  align-self: flex-start;
  font-size: var(--fs-10); letter-spacing: var(--track-12); font-weight: var(--fw-semi);
  padding: 4px 10px; border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--mix-light) 55%, transparent); border: 1px solid var(--card-border);
  color: var(--ink-muted); margin-top: 2px;
}
/* v07zz289 — header chip row: SEQUENCE chip + Cut-from-edit toggle on one line. */
.md-chip-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.md-chip-row .md-chip { align-self: center; margin-top: 0; }
/* v07zz535 — Copy this shot's video folder path (paste into Kling/Seedance Save As).
   Small round icon button sitting beside the SEQUENCE chip. */
.md-vidpath-btn {
  flex-shrink: 0; width: 24px; height: 24px; border-radius: 50%; padding: 0;
  display: grid; place-items: center; cursor: pointer;
  background: color-mix(in srgb, var(--mix-light) 55%, transparent); border: 1px solid var(--card-border);
  color: var(--ink-muted);
  transition: background var(--dur-1) ease, color var(--dur-1) ease, border-color var(--dur-1) ease;
}
.md-vidpath-btn svg { pointer-events: none; }
.md-vidpath-btn:hover { background: color-mix(in srgb, var(--blue-5) 18%, transparent); color: var(--blue-5-ink); border-color: color-mix(in srgb, var(--blue-5) 55%, transparent); }
/* v07zz585 — reviewed chip + "Needs Reviewing" switch MATCH the .md-chip pill EXACTLY
   (Hugo: keep every pill in the header consistent — same size / radius / border / font
   as the SEQUENCE chip). Only the semantic accent differs: green ✓ for reviewed, a gold
   fill when the flag is on. */
.md-reviewed-chip, .md-needsreview-btn {
  display: inline-flex; align-items: center; gap: 5px;
  /* v07zz586 — a <button> does NOT inherit the page font (falls back to the UA
     system font) → the pill rendered in a different typeface than the .md-chip
     <div> beside it. Force the inherit + UPPERCASE to match the SEQUENCE pill. */
  font-family: inherit;
  font-size: var(--fs-10); letter-spacing: var(--track-12); font-weight: var(--fw-semi);
  text-transform: uppercase;
  padding: 4px 10px; border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--mix-light) 55%, transparent); border: 1px solid var(--card-border);
  color: var(--ink-muted);
}
.md-needsreview-btn { cursor: pointer; transition: background var(--dur-1) ease, color var(--dur-1) ease, border-color var(--dur-1) ease; }
.md-needsreview-btn:hover { background: color-mix(in srgb, var(--gold-10) 14%, transparent); color: var(--ink-gold-3); border-color: color-mix(in srgb, color-mix(in srgb, var(--gold-6) 93%, var(--mix-dark)) 45%, transparent); }
.md-needsreview-btn.is-on { background: color-mix(in srgb, var(--gold-10) 20%, transparent); color: var(--ink-gold-3); border-color: color-mix(in srgb, var(--gold-6) 93%, var(--mix-dark)); }
.md-needsreview-btn svg { pointer-events: none; opacity: 0.7; flex: 0 0 auto; }
.md-reviewed-chip svg { color: color-mix(in srgb, var(--olive-7) 89%, var(--mix-dark)); flex: 0 0 auto; }
.md-reviewed-chip .src-init {
  display: inline-grid; place-items: center; min-width: 15px; height: 15px; padding: 0 2px;
  border-radius: var(--r-round); background: color-mix(in srgb, color-mix(in srgb, var(--olive-7) 89%, var(--mix-dark)) 14%, transparent); border: 1px solid color-mix(in srgb, color-mix(in srgb, var(--olive-7) 89%, var(--mix-dark)) 35%, transparent);
  color: color-mix(in srgb, var(--ink-forest-3) 94%, var(--mix-light)); font-size: var(--fs-8-5); font-weight: var(--fw-heavy); letter-spacing: 0;
}
.md-cut-toggle {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-10); letter-spacing: var(--track-10); font-weight: var(--fw-bold); text-transform: uppercase;
  padding: 4px 10px; border-radius: var(--r-pill); cursor: pointer;
  background: color-mix(in srgb, var(--mix-light) 50%, transparent); border: 1px solid var(--card-border);
  color: var(--ink-muted); transition: background var(--dur-1) ease, color var(--dur-1) ease, border-color var(--dur-1) ease;
}
.md-cut-toggle:hover { color: var(--ink); border-color: color-mix(in srgb, color-mix(in srgb, var(--tan-deep) 89%, var(--mix-light)) 50%, transparent); }
.md-cut-toggle.is-on { background: color-mix(in srgb, var(--red-17) 14%, transparent); border-color: color-mix(in srgb, var(--red-17) 45%, transparent); color: var(--red-15); }
.md-cut-toggle.is-on:hover { background: color-mix(in srgb, var(--red-17) 20%, transparent); }
/* v07zz289 — link toggle reuses .md-cut-toggle but in blue (relationship, not a cut). */
.md-link-toggle:hover { color: var(--ink-blue-4); border-color: color-mix(in srgb, var(--blue) 50%, transparent); }
.md-link-toggle.is-on { background: color-mix(in srgb, var(--blue) 14%, transparent); border-color: color-mix(in srgb, var(--blue) 45%, transparent); color: var(--ink-blue-3); }
.md-link-toggle.is-on:hover { background: color-mix(in srgb, var(--blue) 20%, transparent); }

/* v07zz289 — FRAMES header row + "Add from library" button. */
.vs-label--row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.vs-addlib-btn {
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  font-size: var(--fs-10); font-weight: var(--fw-bold); letter-spacing: var(--track-04); text-transform: uppercase;
  padding: 4px 10px; border-radius: var(--r-round);
  border: 1px solid var(--card-border); background: color-mix(in srgb, var(--mix-light) 55%, transparent); color: var(--ink-muted);
  transition: background var(--dur-1) ease, color var(--dur-1) ease, border-color var(--dur-1) ease;
}
.vs-addlib-btn:hover { color: var(--ink-blue-3); border-color: color-mix(in srgb, var(--blue) 50%, transparent); background: color-mix(in srgb, var(--blue) 10%, transparent); }

/* v07zz466 — FRAMES strip filter: Pushed (only versions pushed to the shot) | All
   (every generation incl. un-pushed candidates). Sits left, next to the FRAMES label;
   margin-right:auto keeps "Add from library" pinned right. Fixed height → no layout shift. */
.vs-pubfilter { display: inline-flex; gap: 2px; margin-left: 10px; margin-right: auto; background: color-mix(in srgb, var(--umber-2) 8%, transparent); border: 1px solid var(--card-border); border-radius: var(--r-round); padding: 2px; }
.vs-pf-btn {
  border: none; background: transparent; cursor: pointer;
  font-size: var(--fs-9-5); font-weight: var(--fw-bold); letter-spacing: var(--track-05); text-transform: uppercase;
  color: var(--ink-muted); padding: 2px 9px; border-radius: var(--r-round); line-height: 1.5;
  transition: background var(--dur-1) ease, color var(--dur-1) ease;
}
.vs-pf-btn:hover { color: var(--ink); }
.vs-pf-btn.is-on { background: color-mix(in srgb, var(--gold) 85%, transparent); color: color-mix(in srgb, var(--ink-deep-2) 92%, var(--mix-dark)); }

/* v07zz289 — Add-from-library picker (archived frames + Green Echo trailer). */
.addlib-overlay { position: fixed; inset: 0; z-index: var(--z-addlib); background: color-mix(in srgb, var(--shade-9) 55%, transparent); display: grid; place-items: center; -webkit-backdrop-filter: blur(var(--blur-scrim-3)); backdrop-filter: blur(var(--blur-scrim-3)); padding: 24px; }
.addlib-modal { width: min(860px, 100%); height: min(660px, 88vh); display: flex; flex-direction: column; background: var(--card-bg-solid, color-mix(in srgb, var(--card-bg-light-solid) 93%, var(--mix-light))); border: 1px solid var(--card-border, color-mix(in srgb, var(--umber-2) 25%, transparent)); border-radius: var(--r-card); box-shadow: 0 28px 80px rgba(0,0,0,0.45); overflow: hidden; }
.addlib-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--card-border, color-mix(in srgb, var(--umber-2) 18%, transparent)); }
.addlib-title { font-size: var(--fs-15); font-weight: var(--fw-bold); color: var(--ink); }
.addlib-title b { font-family: var(--font-display); font-weight: 400; font-size: 19px; }
.addlib-x { background: transparent; border: 0; color: var(--ink-muted); cursor: pointer; display: grid; place-items: center; width: 28px; height: 28px; border-radius: var(--r-7); }
.addlib-x:hover { background: color-mix(in srgb, var(--black) 6%, transparent); color: var(--ink); }
/* v936 — Hugo: "i cant search for a shot anymore with all the pills." The v924/v925
   funnel pills (Good/Depth/Storyboard) made this nowrap row wider than the modal and
   the search input fell off the edge. The row wraps now, and the search keeps a
   guaranteed usable width (full second line when squeezed). */
.addlib-tabs { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; row-gap: 8px; padding: 10px 16px; border-bottom: 1px solid var(--card-border, color-mix(in srgb, var(--umber-2) 14%, transparent)); }
.addlib-tabs > button { border: 1px solid var(--card-border); background: color-mix(in srgb, var(--mix-light) 50%, transparent); color: var(--ink-muted); border-radius: var(--r-sm); padding: 6px 13px; font-size: var(--fs-12); font-weight: var(--fw-bold); cursor: pointer; }
.addlib-tabs > button.is-on { background: var(--blue-2); color: var(--white); border-color: var(--blue-2); }
.addlib-search { margin-left: auto; padding: 7px 11px; border-radius: var(--r-sm); border: 1px solid var(--card-border); background: color-mix(in srgb, var(--mix-light) 60%, transparent); font: inherit; font-size: var(--fs-12-5); color: var(--ink); width: 200px; flex: 1 1 160px; min-width: 150px; max-width: 260px; }
.addlib-search:focus { outline: none; border-color: color-mix(in srgb, var(--blue) 60%, transparent); }
/* v760 — the Hero|WIP funnel sits inline in this row. BOTH it and .addlib-search carry
   margin-left:auto, and flexbox splits the free space between two auto margins — which
   would strand the pills in the middle of the row. The funnel keeps the auto (so the pair
   hugs the right edge) and the search drops it, so they sit together. Also stops the
   search from shrinking the pills on the narrower 5-tab frame variant. */
.addlib-tabs .gen-refs-shotfilter--inline ~ .addlib-search { margin-left: 0; }
.addlib-tabs .gen-refs-shotfilter--inline { flex: 0 0 auto; margin-block: 0; }
/* The pills cost ~137px of row width, which was enough to make "★ Favourites" and
   "Green Echo trailer" wrap onto a SECOND line — a 9px taller row on exactly the tabs
   that show the funnel, so the grid jumped down when you switched tabs. Tab labels never
   wrap now and keep their natural width; the search box absorbs the squeeze instead.
   Invariant #20: no control may change the height of anything above the content. */
.addlib-tabs > button { white-space: nowrap; flex: 0 0 auto; }
.addlib-tabs .addlib-search { flex: 0 1 200px; min-width: 96px; }
.addlib-grid { flex: 1 1 0; min-height: 0; overflow-y: auto; padding: 14px 16px; display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; align-content: start; }
.addlib-cell { display: flex; flex-direction: column; gap: 5px; padding: 0; border: 0; background: transparent; cursor: pointer; font: inherit; text-align: left; transition: transform var(--dur-1) ease; }
.addlib-cell:hover:not(:disabled) { transform: translateY(-1px); }
.addlib-cell:disabled { opacity: 0.5; cursor: default; }
.addlib-thumb { width: 100%; aspect-ratio: var(--project-aspect, 21 / 9); border-radius: var(--r-sm); background-size: cover; background-position: center; background-color: color-mix(in srgb, var(--ink-muted) 18%, transparent); border: 1px solid var(--card-border); transition: box-shadow var(--dur-1) ease; position: relative; overflow: hidden; }   /* v07zz356 — match the project aspect ratio (was fixed 16/9) */
.addlib-cell:hover .addlib-thumb { box-shadow: 0 0 0 var(--ring-w) color-mix(in srgb, var(--blue) 70%, transparent); }
/* v07zz321 — video library tiles (Add from library → video mode): dark tile + play glyph. */
.addlib-thumb--video { display: grid; place-items: center; background: linear-gradient(150deg, var(--shade-56), var(--shade-57)); }
.addlib-play { color: color-mix(in srgb, var(--white) 92%, transparent); font-size: var(--fs-22); line-height: 1; text-shadow: 0 1px 8px rgba(0,0,0,0.75); }
.addlib-cap { font-size: var(--fs-10-5); color: var(--ink-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.addlib-empty { grid-column: 1 / -1; padding: 40px; text-align: center; color: var(--ink-muted); font-size: var(--fs-13); }
.addlib-foot { padding: 10px 16px; border-top: 1px solid var(--card-border, color-mix(in srgb, var(--umber-2) 14%, transparent)); font-size: var(--fs-11-5); color: var(--ink-muted); }
/* v07zz575 — Locations tab: location CARDS (level 1) → drill into that location's images
   (level 2), with a breadcrumb back-bar. Cards reuse .addlib-cell + a count badge. */
.addlib-cell--loc .addlib-cap { color: var(--ink); font-weight: var(--fw-bold); font-size: var(--fs-11-5); }
.addlib-loc-count { position: absolute; bottom: 6px; right: 6px; padding: 2px 8px; border-radius: var(--r-round); background: color-mix(in srgb, var(--shade-24) 78%, transparent); color: color-mix(in srgb, var(--gold-10) 24%, var(--mix-light)); font-size: var(--fs-9-5); font-weight: var(--fw-bold); letter-spacing: var(--track-02); pointer-events: none; }
.addlib-crumb { display: flex; align-items: center; gap: 10px; padding: 9px 16px 0; }
.addlib-crumb-back { display: inline-flex; align-items: center; gap: 5px; border: 1px solid var(--card-border); background: color-mix(in srgb, var(--mix-light) 55%, transparent); color: var(--ink); border-radius: var(--r-sm); padding: 5px 12px 5px 9px; font: inherit; font-size: var(--fs-12); font-weight: var(--fw-bold); cursor: pointer; transition: background var(--dur-1) ease; }
.addlib-crumb-back:hover { background: var(--mix-light); }
.addlib-crumb-here { font-size: var(--fs-13); font-weight: var(--fw-heavy); color: var(--ink); }

/* v06o — header row holds the shot id on the left + status cluster on
   the right (status pill + hover-reveal SQA bar). The cluster keeps a
   reserved width equal to the full bar so hovering doesn't shift other
   content; the bar slides in from the right behind the pill. */
.md-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
}
.md-stage-cluster {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  position: relative;
  /* Place the SQA bar to the LEFT of the pill, hidden behind it
     until hover. The bar slides in (translate + fade) on hover. */
  padding-left: 0;
}
.md-quick-actions {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  opacity: 0;
  transform: translateX(12px);
  pointer-events: none;
  transition: opacity 160ms ease, transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* Reveal the bar when the cluster is hovered. Right-to-left slide:
   buttons start nudged to the right and ease back into place. */
.md-stage-cluster:hover .md-quick-actions,
.md-stage-cluster:focus-within .md-quick-actions {
  opacity: 1;
  transform: translateX(0);
  pointer-events: auto;
}
/* Slightly larger buttons than the shot-row SQA so they read well in
   the modal header (which has more breathing room). */
.md-quick-actions .sqa-btn {
  width: 28px;
  height: 28px;
}
.md-status-pill {
  /* The pill keeps its row-style look; just nudge it up a hair so it
     visually centres against the 36px shot id. */
  align-self: center;
  margin-top: 0;
  /* Re-enable pointer events — the row's static pill turns these off
     so the row itself can swallow the click, but here we want clean
     hover behaviour for the cluster. The pill itself is still static
     (no chevron / no menu). */
  pointer-events: auto;
}

.modal-hero-image {
  width: 100%;
  /* v06q — project aspect token, switched per-project / per-user. */
  aspect-ratio: var(--project-aspect, 21 / 9);
  border-radius: var(--r-md);
  /* v07zz215 — Hugo (Railway): the color-mix(in srgb, var(--paper) 97%, var(--mix-dark)) placeholder read LIGHTER than the
     glass panel, so the empty hero looked like a mismatched cream block with a
     border. Use `transparent` so the placeholder IS the panel surface (exact
     match, no edge) and the image materialises on top with no border. */
  position: relative; overflow: hidden;
  flex-shrink: 0;
  background: transparent;
}
.mhi-tag {
  position: absolute; bottom: 10px; left: 12px;
  font-family: var(--font-mono); font-size: var(--fs-10); color: color-mix(in srgb, var(--white) 85%, transparent);
  background: rgba(0,0,0,0.35); padding: 3px 7px; border-radius: 4px;
  letter-spacing: var(--track-04);
}

/* v07zz69 — Hugo: "the panel height is changing due to the VO line
   section being absent for certain shots. Same with the shot
   description line. Make sure those spaces are constant and do not
   change the modal height when it's empty." Each block now reserves
   a min-height matching its filled-state so empty shots keep the
   exact same modal height as fully-populated ones. */
.modal-action {
  font-style: italic;
  font-size: var(--fs-13-5);
  color: var(--ink);
  margin: 0;
  line-height: 1.55;
  text-wrap: pretty;
  /* v07zz291 — Hugo: panel still shifts when the description is missing vs a
     longer one. Reserve AND cap at a fixed 3 lines (13.5 × 1.55 ≈ 21 px/line)
     so empty, 1-line and 3-line descriptions all occupy exactly the same box —
     no shift of anything below. Longer actions clamp (full text is in the
     prompt block). */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  min-height: 63px;
}
.modal-action:empty::before {
  content: " ";
  display: inline-block;
}
.modal-vo {
  margin: 0;
  font-style: italic;
  font-size: var(--fs-13-5);
  color: var(--ink);
  background: color-mix(in srgb, var(--amber) 12%, transparent);
  border-left: 2px solid var(--amber);
  padding: 10px 14px;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  min-height: 41px; /* matches padding 10*2 + 1 line of italic text */
  box-sizing: border-box;
}
.modal-vo-empty {
  /* Mirror .modal-vo's box exactly so the empty + filled states
     have identical bounding boxes. Only the text style differs. */
  font-size: var(--fs-12);
  color: var(--ink-soft);
  font-style: italic;
  background: color-mix(in srgb, var(--amber) 5%, transparent);
  border-left: 2px solid color-mix(in srgb, var(--amber) 40%, transparent);
  padding: 10px 14px;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  min-height: 41px;
  box-sizing: border-box;
}

/* Stepper — 7 stages (prompt, first pass, concept wip, concept, video wip, video, 4k) → 7 steps + 6 lines = 13 cols */
.pipeline-stepper { display: grid; grid-template-columns: repeat(13, auto); align-items: flex-start; gap: 0; padding: 8px 0; }
.pipeline-stepper .stepper-step { display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 56px; }
/* v06p — clickable stepper. Each step is a real <button> with the
   default button chrome stripped so it inherits the stage-pill look
   from above. Hover lifts the circle slightly + grows the shadow so
   the affordance reads. Active state (mousedown) presses it back. */
.pipeline-stepper button.stepper-step {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  transition: transform 90ms ease;
}
.pipeline-stepper button.stepper-step:hover .step-circle {
  transform: translateY(-1px);
  box-shadow: 0 3px 8px color-mix(in srgb, var(--shadow-ink) 20%, transparent);
}
.pipeline-stepper button.stepper-step:active { transform: scale(0.96); }
.pipeline-stepper button.stepper-step:focus-visible .step-circle {
  outline: var(--ring-w) solid var(--leaf);
  outline-offset: var(--ring-offset);
}
.step-circle {
  width: 22px; height: 22px; border-radius: 50%;
  background: color-mix(in srgb, var(--mix-light) 55%, transparent);
  border: 1.5px solid var(--ink-soft);
  color: var(--white); display: grid; place-items: center;
}
.stepper-step.done .step-circle { background: var(--ok); border-color: var(--ok); color: var(--white); }
.stepper-step.current .step-circle {
  background: var(--warn); border-color: var(--warn);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--warn) 25%, transparent);
  animation: pulseAmber var(--dur-pulse) ease-in-out infinite;
}
@keyframes pulseAmber { 0%,100% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--warn) 25%, transparent); } 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--warn) 10%, transparent); } }
.stepper-step.future .step-circle { background: color-mix(in srgb, var(--mix-light) 40%, transparent); border-color: var(--ink-soft); }
.step-label { font-size: var(--fs-10-5); color: var(--ink-muted); text-align: center; }
.stepper-step.done .step-label { color: var(--ink); }
.stepper-step.current .step-label { color: var(--ink); font-weight: var(--fw-semi); }

.stepper-line { height: 1.5px; background: var(--ink-soft); align-self: center; margin-top: -16px; flex: 1; min-width: 18px; }
.stepper-line.done { background: var(--ok); }

.meta-block { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px;
  background: color-mix(in srgb, var(--mix-light) 40%, transparent); border: 1px solid var(--card-border); border-radius: var(--r-sm); }
.meta-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; font-size: var(--fs-12); }
.meta-k { color: var(--ink-muted); font-weight: var(--fw-med); }
/* v07zz209 — reference lock toggle */
.ref-lock-btn { margin-left: 8px; font: inherit; font-size: var(--fs-10); font-weight: var(--fw-bold); letter-spacing: var(--track-04); padding: 2px 8px; border-radius: 99px; border: 1px solid color-mix(in srgb, var(--purple) 40%, transparent); background: color-mix(in srgb, var(--purple) 10%, transparent); color: var(--ink-purple-4); cursor: pointer; vertical-align: middle; transition: background var(--dur-1) ease, border-color var(--dur-1) ease; }
.ref-lock-btn:hover { background: color-mix(in srgb, var(--purple) 20%, transparent); }
.ref-lock-btn.is-locked { background: color-mix(in srgb, var(--purple) 22%, transparent); border-color: color-mix(in srgb, var(--purple) 60%, transparent); }
.ref-lock-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.meta-v { color: var(--ink); }
.meta-v.mono { font-family: var(--font-mono); font-size: var(--fs-11); }
/* v07zz413 — Shot type + Location rows are always shown + click-to-edit. */
.meta-v--editable { cursor: text; border-radius: 4px; padding: 1px 4px; margin: -1px -4px; transition: background var(--dur-1) ease; text-align: right; }
.meta-v--editable:hover { background: color-mix(in srgb, var(--card-border) 22%, transparent); }
.meta-v--empty { color: var(--ink-muted); font-style: italic; opacity: 0.8; }
.meta-edit-input {
  font: inherit; font-size: var(--fs-12); text-align: right; color: var(--ink);
  background: color-mix(in srgb, var(--cream) 90%, transparent); border: 1px solid color-mix(in srgb, var(--card-border) 70%, transparent);
  border-radius: 5px; padding: 2px 6px; min-width: 0; flex: 1 1 auto; max-width: 70%;
}
.meta-edit-input:focus { outline: none; border-color: var(--accent, color-mix(in srgb, var(--moss) 81%, var(--mix-light))); }
/* v06f — Hugo: references row is now a single flat strip with NO
   scroll. Every linked ref is laid out side-by-side; if there are too
   many to fit on one line the row wraps naturally to a second line
   (no horizontal scrollbar, no `< >` paginator buttons). The hover
   preview popup grew from a 56px chip to a proper "large version"
   (240×240) — see .ref-tip / .ref-tip-img below. */
.ref-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-width: 100%;
  /* overflow visible so the hover popup (.ref-tip) — which extends
     OUTSIDE the row's vertical bounds — isn't clipped at the row edge. */
  overflow: visible;
  padding-bottom: 2px;
  /* The parent .meta-row uses justify-content: space-between, which
     pushes this strip to the right edge. Letting it grow towards the
     left lets a wrapped second line stay right-aligned too. */
  justify-content: flex-end;
}
.ref-thumb { width: 26px; height: 26px; border-radius: 50%; border: 2px solid var(--card-bg-solid); margin-left: -6px; }
.ref-thumb:first-child { margin-left: 0; }

/* When the References row wraps, the label "References" should align
   to the FIRST line of refs (not the vertical centre of the whole
   wrapped block). Targeting only meta-rows that actually contain refs
   via :has() keeps the unrelated Model/Active-version rows on their
   default center alignment. */
.meta-row:has(.ref-thumbs) {
  align-items: flex-start;
  /* v06m — Hugo: row height was collapsing when no refs were linked
     (empty .ref-thumbs took zero height; "No references linked" text
     was shorter than a 26px thumb). Locking a min-height keeps the
     row identical whether refs are present or not. */
  min-height: 30px;
}
.meta-row:has(.ref-thumbs) .meta-k {
  /* Nudge the label down a hair so it lines up with the centre of the
     26px thumbs on the same row. */
  padding-top: 4px;
}
/* Also size the strip itself so the row contents always reach the
   same height. */
.ref-thumbs { min-height: 26px; }

.prompt-block { display: flex; flex-direction: column; gap: 6px; }
/* v07z7 — Prompt-block: BOX always visible at full opacity, only
   the TEXT inside fades in. No box-level opacity animation (was
   causing the whole block to disappear then re-fade on Hugo's
   Railway). The label + code text transition from transparent to
   their real colors when dataReady flips. */
.prompt-block--loading {
  pointer-events: none;
}
.prompt-block .pb-label {
  transition: color var(--dur-6) ease-out;
}
.prompt-block .pb-code {
  transition: color var(--dur-6) ease-out;
}
.prompt-block--loading .pb-label {
  color: transparent;
}
.prompt-block--loading .pb-code {
  color: transparent;
  user-select: none;
}
/* v07zb — Same color-fade gate for the Info block (Model / Active
   version / Created / References). Box stays visible at full
   opacity, only the row text fades in once stripReady flips. Keeps
   the loading order top-down so users perceive "structure first,
   info second" rather than the previous cheap "everything pops at
   once" feeling. */
.meta-block--loading {
  pointer-events: none;
}
.meta-block .meta-k,
.meta-block .meta-v,
.meta-block .ref-empty {
  transition: color var(--dur-6) ease-out;
}
.meta-block--loading .meta-k,
.meta-block--loading .meta-v,
.meta-block--loading .ref-empty {
  color: transparent;
  user-select: none;
}
/* Hide reference thumbnails completely until ready — otherwise they
   appear as solid color tiles while the labels are transparent,
   which looks broken. */
.meta-block--loading .ref-thumbs {
  opacity: 0;
  transition: opacity var(--dur-6) ease-out;
}
.meta-block--ready .ref-thumbs {
  opacity: 1;
  transition: opacity var(--dur-6) ease-out;
}
.pb-head { display: flex; justify-content: space-between; align-items: center; }
.pb-label { font-size: var(--fs-10-5); letter-spacing: var(--track-14); font-weight: var(--fw-semi); color: var(--ink-muted); text-transform: uppercase; }
.copy-btn { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-11);
  background: color-mix(in srgb, var(--mix-light) 50%, transparent); border: 1px solid var(--card-border); border-radius: var(--r-pill);
  padding: 3px 9px; color: var(--ink); cursor: pointer; }
.pb-code {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-11-5);
  line-height: 1.55;
  background: var(--bg);
  color: color-mix(in srgb, var(--card-border) 63%, var(--mix-light));
  padding: 12px 14px;
  border-radius: var(--r-sm);
  /* Fixed height — prevents the modal from resizing when prompt text length
     differs between versions (short prompt no longer collapses the box). */
  height: 140px;
  overflow-y: scroll;
  white-space: pre-wrap;
  border: 1px solid color-mix(in srgb, var(--card-border) 25%, transparent);
  /* Aero glass scrollbar — translucent, no jarring colors. */
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--hi) 30%, transparent) transparent;
  scrollbar-gutter: stable;
}
.pb-code::-webkit-scrollbar { width: 10px; }
.pb-code::-webkit-scrollbar-track { background: transparent; }
.pb-code::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--hi) 40%, transparent) 0%,
    color-mix(in srgb, color-mix(in srgb, var(--gold-11) 58%, var(--mix-light)) 35%, transparent) 50%,
    color-mix(in srgb, color-mix(in srgb, var(--foil) 85%, var(--mix-dark)) 30%, transparent) 100%);
  border-radius: var(--r-round);
  border: 3px solid transparent;
  background-clip: padding-box;
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 45%, transparent);
}
.pb-code::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--cream) 55%, transparent) 0%,
    color-mix(in srgb, color-mix(in srgb, var(--gold-17) 54%, var(--mix-light)) 50%, transparent) 50%,
    color-mix(in srgb, color-mix(in srgb, var(--gold-9) 71%, var(--mix-light)) 45%, transparent) 100%);
  background-clip: padding-box;
}

.version-section { display: flex; flex-direction: column; gap: 8px; }
.vs-label { font-size: var(--fs-10-5); letter-spacing: var(--track-14); font-weight: var(--fw-semi); color: var(--ink-muted); text-transform: uppercase; }
/* v06l — Hugo wants the shot-panel filmstrips to mirror the asset
   panel: drag-scroll + end arrows. Each scroller is wrapped in a
   `.version-strip-wrap` that holds the arrows on either side. Inner
   strip keeps drag-scroll behaviour; the wrap-level padding leaves
   room for the arrows so they don't crowd the tiles. */
.version-strip-wrap {
  position: relative;
}
.version-strip-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--cream-2) 95%, transparent);
  border: 1px solid color-mix(in srgb, var(--tan-deep) 32%, transparent);
  color: var(--ink);
  font-family: var(--font-display, sans-serif);
  font-size: var(--fs-20);
  line-height: 1;
  display: grid;
  place-items: center;
  cursor: pointer;
  opacity: 0.78;
  z-index: 3;
  transition: opacity var(--dur-1) ease, background var(--dur-1) ease, transform var(--dur-1) ease;
}
.version-strip-arrow:hover {
  opacity: 1;
  background: var(--cream);
  transform: translateY(-50%) scale(1.05);
}
.version-strip-arrow--left  { left: -4px; }
.version-strip-arrow--right { right: -4px; }
/* v06m — Hide arrows when there's nothing to scroll. The useDragScroll
   hook tags the scroller with `.has-overflow` when scrollWidth >
   clientWidth; arrows opt-in to display only on that signal. */
.version-strip-wrap:has(.version-scroller:not(.has-overflow)) .version-strip-arrow {
  display: none;
}

/* v07zw — Edge fades on the filmstrip so version tiles at the
   left + right edges read as "scrollable" instead of "cut off".
   Two pseudo-element overlays on the wrap, anchored to its
   left + right edges, painted in the same cream as the modal
   so they blend into the surrounding panel. pointer-events:none
   so they don't block clicks on the underlying tiles. */
.version-strip-wrap {
  position: relative;
}
/* v07zz674 — Hugo: "the weird glow cut off on the video strip."
   The fade is meant to dissolve the end tiles INTO the panel, which only works if it
   is painted in the panel's own colour. It wasn't. Measured live: the modal card
   behind the strip is var(--card-bg-solid) at partial alpha — a greige — while the fade was hard-coded
   rgba(252,247,230,…), a cream roughly 30 points lighter on every channel. So instead
   of disappearing it painted a pale rectangle over the dark video tiles, with a hard
   outer edge where the pseudo-element stops. On the FRAMES strip nobody noticed
   because it only appears when a strip overflows, and frames rarely do.
   The colour now comes from a variable that defaults to the card's own background, so
   it blends here and can be re-pointed anywhere else this strip is reused. */
/* v07zz675 — EDGE FADES REMOVED. Do not reintroduce them.
   Hugo, twice: "the weird glow cut off on the video strip", then "we still have a
   new extra shape here that we didnt have before and dont have on the frames."
   He was pointing at the fade both times.
   The idea was to dissolve the end tiles into the panel, and it can't be done with a
   painted overlay here. The panel is a GLASS card — color-mix(in srgb, var(--card-bg-solid) 92%, transparent) composited
   over a dark backdrop — so what you see is darker than its own declared rgb. v674
   matched the declared value and produced a rectangle that was lighter than the panel
   instead of invisible: a pale box with hard edges, exactly what it was meant to hide.
   Matching a composited colour from CSS means duplicating the compositing, which
   breaks again on any backdrop or theme change.
   The affordance was never lost: .version-strip-arrow only renders when the scroller
   has .has-overflow, so the arrows already say "there's more". The fades added a
   colour-matching liability and nothing else. Removed rather than tuned a third time. */
/* v07zz675 — the "hide the fades when nothing scrolls" rule went with the fades. Left
   behind it would hide pseudo-elements that no longer exist, and quietly suppress any
   ::before/::after a future rule adds to this wrap on non-overflowing strips. */
.version-scroller {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  padding: 6px 32px 10px;       /* room for the arrows */
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  touch-action: pan-x;
  /* v06r — Hugo: drag-scroll is now the primary nav (+ left/right
     edge arrows for hover-to-scroll), so hide the horizontal
     scrollbar entirely. The strip still scrolls — just invisibly. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.version-scroller::-webkit-scrollbar { display: none; width: 0; height: 0; }
.version-scroller.is-dragging { cursor: grabbing; }
/* v07w/v07y/v07z — empty placeholder strip. Tile is 140×60 (21:9)
   + 6/10 vertical padding on the scroller = 76 px. Match exactly
   so the panel doesn't snap-grow when the fetch resolves. */
.version-scroller--empty {
  min-height: 76px;
  cursor: default;
}
/* v07z7 — Strip preload gate. The empty placeholder is always in
   flow; the REAL scroller is absolutely overlaid on top. When
   stripReady flips, the overlay transitions opacity 0 → 1 with a
   CSS transition (not a one-shot animation) so the empty
   placeholder underneath stays visible the whole time — no
   "disappear then re-fade" glitch on Railway. */
.version-strip-wrap {
  position: relative;
}
.version-scroller--overlay {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--dur-6) ease-out;
  pointer-events: none;
}
.version-scroller--overlay.is-ready {
  opacity: 1;
  pointer-events: auto;
}
@keyframes vt-strip-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* v07za — Tile fade-in via keyframe animation. Applies to BOTH
   image tiles (.vt-img) and video tiles (.vt-video) so frames and
   videos animate identically. */
.version-tile .vt-img,
.version-tile .vt-video {
  opacity: 0;
}
.version-tile .vt-img.vt-img-loaded,
.version-tile .vt-video.vt-img-loaded {
  animation: vt-img-fade-in var(--dur-5) ease forwards;   /* v07zz218 — smooth fade (90ms read as a flicker) */
}
/* v07zz41 — Warm tiles (cached/prefetched, decoded at mount) reveal INSTANTLY
   with no fade. The callback ref adds .vt-img-warm synchronously before paint,
   so re-opening a shot whose strip is already cached no longer re-fades every
   tile every time — only genuinely cold tiles run the 280ms fade above. */
.version-tile .vt-img.vt-img-warm,
.version-tile .vt-video.vt-img-warm {
  opacity: 1;
  animation: none;
}
@keyframes vt-img-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* v07zz291 — Hero image: on a COLD (first) open it had no fade CSS at all (the JS
   toggles .mhi-loaded/.mhi-warm but nothing styled them), so the placeholder→image
   swap popped in and read as a flicker. Fade it with the EXACT same 240ms ease as
   the version thumbnails. Warm (cached / prefetched) heroes still reveal instantly. */
.modal-hero-image .mhi-img { opacity: 0; }
.modal-hero-image .mhi-img.mhi-loaded { animation: vt-img-fade-in var(--dur-6) ease-out forwards; }   /* v07zz344 — match the FRAMES strip reveal (.version-scroller--overlay, 320ms ease-out), not the 240ms per-tile fade: the strip's overlay transition is the fade Hugo actually sees, and 240ms read as too fast on the large hero. */
.modal-hero-image .mhi-img.mhi-warm { opacity: 1; animation: none; }
.version-scroller img,
.version-scroller video {
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
  pointer-events: none;
}
.version-scroller .version-tile {
  user-select: none;
  -webkit-user-select: none;
}
/* v07zz526 — "Add from library" tile: an empty version-frame with a big + in the middle
   (replaces the old header pill). Same 140px tile footprint so the strip height is stable. */
.version-tile--add {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  border: 1.5px dashed color-mix(in srgb, color-mix(in srgb, var(--tan-deep) 83%, var(--mix-light)) 50%, transparent);
  background: color-mix(in srgb, var(--cream) 35%, transparent);
  box-shadow: none;
  color: var(--ink-muted, color-mix(in srgb, var(--ink-umber-4) 60%, transparent));
  transition: border-color var(--dur-2) ease, background var(--dur-2) ease, color var(--dur-2) ease;
}
.version-tile--add:hover {
  border-color: color-mix(in srgb, color-mix(in srgb, var(--tan-deep) 83%, var(--mix-light)) 90%, transparent);
  background: color-mix(in srgb, color-mix(in srgb, var(--hi) 85%, var(--mix-light)) 60%, transparent);
  color: var(--ink, var(--ink-umber));
}
.version-tile--add::after { display: none !important; }   /* kill the video play-badge overlay on the add tile */
.version-tile--add .vt-add-label {
  font-size: var(--fs-9); font-weight: var(--fw-bold); letter-spacing: var(--track-05); text-transform: uppercase; line-height: 1;
}
/* v07zz399 — Hugo: keep the WIDER modal + the bigger hero, but put EVERYTHING ELSE back
   to its original size — the enlarged 360px FRAMES tiles were wrong. The version tiles now
   inherit the base 140px (no override). Cap the hero so the wider/taller modal fits in the
   viewport without a scrollbar or a cropped bottom. Scoped to .modal-card--shot. */
.modal-card--shot { max-width: 1180px; max-height: 92vh; }
.modal-card--shot .modal-hero-image { max-height: 46vh; }
.version-tile { width: 140px; aspect-ratio: var(--project-aspect, 21 / 9); flex: 0 0 140px;
  border-radius: var(--r-sm); border: 1.5px solid transparent; position: relative;
  display: flex; flex-direction: column; justify-content: flex-end; padding: 8px; cursor: pointer;
  overflow: hidden;
  /* v07zz215 — Hugo (Railway): the color-mix(in srgb, var(--paper) 97%, var(--mix-dark)) placeholder was lighter than the
     panel, so empty tiles looked like bordered cream boxes. `transparent` makes
     the placeholder the panel surface (no edge); the frame materialises on top. */
  background: transparent;
  /* v07d — soft drop shadow so LOADED thumbs read as physical objects on the
     cream card. Softened so empty placeholders don't read as bordered boxes. */
  box-shadow:
    0 1px 3px color-mix(in srgb, var(--shadow-ink) 7%, transparent);
  /* Kill any browser-side native drag affordance — pressing the star
     was triggering an HTML5 drag preview of the underlying img on
     some browsers (the "grid appears on hold" bug). */
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
}
.version-tile, .version-tile * {
  -webkit-user-drag: none;
}
.version-tile .vt-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  z-index: 0;
  background: var(--black);
  pointer-events: none;          /* don't intercept the tile-button click */
}
/* v07a/v07d — Hugo: gold-foil border on the SELECTED tile. Bumped
   to 2px border + brighter halo + retained base drop shadow so the
   selected state is clearly visible against the cream card. */
.version-tile.active {
  border-width: 2px;
  border-color: var(--gold-2);
  box-shadow:
    0 0 0 var(--ring-w) color-mix(in srgb, var(--gold-2) 55%, transparent),
    0 0 16px color-mix(in srgb, var(--gold-2) 55%, transparent),
    0 1px 2px color-mix(in srgb, var(--shadow-ink) 10%, transparent),
    0 4px 10px color-mix(in srgb, var(--shadow-ink) 10%, transparent);
}
/* v06p — hero star button on each version tile. Sits in the top-right
   corner. Outline-only by default; fills with the leaf colour once the
   version is the hero. Tile-level `.is-hero` class also adds a subtle
   amber glow so heroed tiles stand out from regulars. */
/* v07e — child SVG is pointer-events:none so clicks ALWAYS land on
   the span (the element we attach our handlers to). Without this,
   the SVG sometimes "swallowed" the click target which let the
   parent button's drag-source behaviour leak through. */
.version-tile .vt-hero-btn svg { pointer-events: none; }
.version-tile .vt-hero-btn {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--shade-7) 65%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--hi) 55%, transparent);
  color: color-mix(in srgb, var(--ink-hi) 95%, transparent);
  cursor: pointer;
  z-index: 2;
  /* v06s — Hugo: revert to hover-only. The star button only appears
     when the user hovers the tile. EXCEPTION: when this tile IS the
     locked hero, the gold-filled star stays visible permanently so
     you can spot the hero at a glance. */
  opacity: 0;
  transition: opacity var(--dur-1) ease, background var(--dur-1) ease, color var(--dur-1) ease, transform 90ms ease, box-shadow var(--dur-1) ease;
}
.version-tile:hover .vt-hero-btn,
.version-tile .vt-hero-btn.is-active { opacity: 1; }
.version-tile .vt-hero-btn:hover {
  background: color-mix(in srgb, var(--gold-2) 85%, transparent);
  color: color-mix(in srgb, var(--ink-deep) 90%, var(--mix-dark));
  border-color: color-mix(in srgb, var(--hi-3) 95%, transparent);
  transform: scale(1.05);
}
.version-tile .vt-hero-btn.is-active {
  background: color-mix(in srgb, var(--gold-2) 98%, transparent);
  color: color-mix(in srgb, var(--ink-deep) 90%, var(--mix-dark));
  border-color: var(--hi-3);
  box-shadow:
    0 0 0 var(--hairline-w) color-mix(in srgb, var(--hi-3) 65%, transparent),
    0 0 12px color-mix(in srgb, var(--gold-2) 75%, transparent);
}
/* v07zz17 — Open-grid button. Same chrome as vt-hero-btn but sits
   in the TOP-LEFT corner so the two pills don't overlap, and uses a
   neutral white outline (not amber — gold is reserved for the hero
   indicator). Visible on hover only. */
.version-tile .vt-grid-btn svg { pointer-events: none; }
.version-tile .vt-grid-btn {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--shade-7) 65%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--hi) 55%, transparent);
  color: color-mix(in srgb, var(--ink-hi) 95%, transparent);
  cursor: pointer;
  z-index: 2;
  opacity: 0;
  transition: opacity var(--dur-1) ease, background var(--dur-1) ease, color var(--dur-1) ease, transform 90ms ease;
}
.version-tile:hover .vt-grid-btn { opacity: 1; }
.version-tile .vt-grid-btn:hover {
  background: color-mix(in srgb, var(--leaf) 85%, transparent);
  color: var(--ink-hi);
  border-color: color-mix(in srgb, var(--hi) 95%, transparent);
  transform: scale(1.05);
}
/* v07zz — Lock = "set as current" toggle on each tile (top-LEFT, opposite
   the hero star). Teal when this version drives the shot-card thumbnail.
   The grid button moves to the BOTTOM-RIGHT so it doesn't collide with the
   lock (the version label sits bottom-left). */
.version-tile .vt-current-btn svg { pointer-events: none; }
.version-tile .vt-current-btn {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--shade-7) 65%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--hi) 55%, transparent);
  color: color-mix(in srgb, var(--ink-hi) 95%, transparent);
  cursor: pointer;
  z-index: 2;
  opacity: 0;
  transition: opacity var(--dur-1) ease, background var(--dur-1) ease, color var(--dur-1) ease, transform 90ms ease;
}
.version-tile:hover .vt-current-btn,
.version-tile .vt-current-btn.is-active { opacity: 1; }
.version-tile .vt-current-btn:hover {
  background: color-mix(in srgb, var(--lock) 85%, transparent);
  color: var(--lock-ink);
  border-color: color-mix(in srgb, var(--lock-line) 95%, transparent);
  transform: scale(1.05);
}
.version-tile .vt-current-btn.is-active {
  background: color-mix(in srgb, var(--lock) 97%, transparent);
  color: var(--lock-ink);
  border-color: var(--lock-line);
  box-shadow: 0 0 0 var(--hairline-w) color-mix(in srgb, var(--lock-line) 60%, transparent), 0 0 12px color-mix(in srgb, var(--lock) 60%, transparent);
}
.version-tile .vt-grid-btn { top: auto; bottom: 6px; left: auto; right: 34px; }
/* v07zz291 — discard (trash) on each frame tile, bottom-right (grid shifts left to
   make room). Hover-reveal like the other tile buttons; reddish on hover. */
.version-tile .vt-discard-btn {
  position: absolute; bottom: 6px; right: 6px; width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer; z-index: 2; opacity: 0;
  background: color-mix(in srgb, var(--shade-7) 65%, transparent); border: 1.5px solid color-mix(in srgb, var(--hi) 55%, transparent); color: color-mix(in srgb, var(--ink-hi) 95%, transparent);
  transition: opacity var(--dur-1) ease, background var(--dur-1) ease, color var(--dur-1) ease, transform 90ms ease;
}
.version-tile .vt-discard-btn svg { pointer-events: none; }
.version-tile:hover .vt-discard-btn { opacity: 1; }
.version-tile .vt-discard-btn:hover { background: color-mix(in srgb, var(--danger-soft) 92%, transparent); color: var(--ink-hi); border-color: color-mix(in srgb, var(--hi) 95%, transparent); transform: scale(1.05); }

/* v07zz534 — FRAMES funnel action cluster (top-right). Replaces the old bin + hero
   star. Up to two buttons per tab: ★ push-to-HERO (gold), send-to-WIP (blue),
   send-to-Archive (slate). Hover-reveal like the other tile buttons. */
.version-tile .vt-actions {
  position: absolute; top: 6px; right: 6px; z-index: 3;
  display: flex; gap: 5px; opacity: 0;
  transition: opacity var(--dur-1) ease;
}
.version-tile:hover .vt-actions { opacity: 1; }
.version-tile .vt-move-btn {
  width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: color-mix(in srgb, var(--shade-7) 72%, transparent); border: 1.5px solid color-mix(in srgb, var(--hi) 55%, transparent);
  color: color-mix(in srgb, var(--ink-hi) 95%, transparent);
  transition: background var(--dur-1) ease, color var(--dur-1) ease, transform 90ms ease, box-shadow var(--dur-1) ease;
}
.version-tile .vt-move-btn svg { pointer-events: none; }
.version-tile .vt-move-btn:hover { transform: scale(1.08); }
.version-tile .vt-move-hero:hover { background: color-mix(in srgb, var(--gold-2) 95%, transparent); color: color-mix(in srgb, var(--ink-deep) 90%, var(--mix-dark)); border-color: color-mix(in srgb, var(--hi-3) 95%, transparent); box-shadow: 0 0 10px color-mix(in srgb, var(--gold-2) 60%, transparent); }
.version-tile .vt-move-wip:hover  { background: color-mix(in srgb, var(--blue-5) 95%, transparent); color: var(--ink-blue-37); border-color: color-mix(in srgb, var(--blue-38) 95%, transparent); box-shadow: 0 0 10px color-mix(in srgb, var(--blue-5) 55%, transparent); }
.version-tile .vt-move-arch:hover { background: color-mix(in srgb, var(--grey-1) 95%, transparent); color: var(--grey-1-ink); border-color: color-mix(in srgb, var(--grey-2) 90%, transparent); }
/* v856 — ✓ mark GOOD, the tier between WIP and Hero. Green so the funnel reads as a
   progression on hover: blue WIP → green Good → gold Hero → grey Archived. Same shape and
   sizing as its neighbours, inheriting .vt-move-btn — only the hover colour differs. */
.version-tile .vt-move-good:hover { background: color-mix(in srgb, var(--ok) 95%, transparent); color: color-mix(in srgb, var(--ink-forest-2) 46%, var(--mix-dark)); border-color: color-mix(in srgb, color-mix(in srgb, var(--leaf-bright) 33%, var(--mix-light)) 95%, transparent); box-shadow: 0 0 10px color-mix(in srgb, var(--ok) 55%, transparent); }
/* v807 — 📁 move this image to another section (Frames / Storyboards / 3D). */
.version-tile .vt-move-section:hover { background: color-mix(in srgb, color-mix(in srgb, var(--leaf) 84%, var(--mix-light)) 95%, transparent); color: color-mix(in srgb, var(--leaf-bright) 16%, var(--mix-dark)); border-color: color-mix(in srgb, color-mix(in srgb, var(--leaf-bright) 18%, var(--mix-light)) 95%, transparent); box-shadow: 0 0 10px color-mix(in srgb, color-mix(in srgb, var(--leaf) 84%, var(--mix-light)) 55%, transparent); }
/* Storyboard + 3D tiles have no other overlay controls, so their single action
   button sits at the same top-right corner without the video tile's offset. */
.version-tile--storyboard .vt-actions { right: 6px; }

/* v807 — the move-to-section picker inside the styled confirm modal. Fixed-height
   rows and an always-present hint line so the list never changes height between
   tiles (invariant #20) — the current section is disabled, never removed. */
.move-section-picker { display: grid; gap: 8px; margin-top: 10px; }
/* v834 — Hugo: "cant see at all where to send". These were CREAM text on a
   near-transparent CREAM fill — written for a dark surface, but the host is
   .confirm-delete-modal, which is a light panel (color-mix(in srgb, var(--cream-2) 96%, transparent)). Cream on
   cream is invisible; the options were there the whole time, just unreadable.
   Repainted against the same tokens the rest of that modal uses: --ink for the
   name, --ink-muted for the hint, and the .confirm-delete-list fill for the row. */
.move-section-opt {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  width: 100%; min-height: 44px; padding: 9px 14px; text-align: left;
  border-radius: var(--r-sm); cursor: pointer;
  background: color-mix(in srgb, color-mix(in srgb, var(--gold-10) 25%, var(--mix-light)) 55%, transparent);
  border: 1px solid var(--card-border);
  color: var(--ink);
  transition: background var(--dur-1) ease, border-color var(--dur-1) ease;
}
.move-section-opt:hover:not(:disabled) {
  background: color-mix(in srgb, var(--gold-2) 30%, transparent);
  border-color: color-mix(in srgb, color-mix(in srgb, var(--gold) 92%, var(--mix-dark)) 75%, transparent);
}
/* The section it's already in stays listed (never removed — the list must not
   change height, invariant #20) but reads clearly as unavailable. */
.move-section-opt:disabled { opacity: 0.55; cursor: default; background: color-mix(in srgb, var(--black) 3%, transparent); }
.move-section-opt .mso-name { font-weight: var(--fw-bold); font-size: var(--fs-13-5); letter-spacing: var(--track-02); color: var(--ink); }
.move-section-opt .mso-hint { font-size: var(--fs-11-5); color: var(--ink-muted, color-mix(in srgb, var(--ink-umber-2) 81%, var(--mix-light))); }
/* v07zz553 — VIDEO tiles keep the hero-video ★ (hero_video_label) at top-right on the
   HERO tab; the funnel actions row sits to its LEFT so the two never overlap. */
.version-tile--video .vt-actions { right: 36px; }

/* v07zz — HERO star + CURRENT lock on the big hero image (top-left).
   Always visible — these are the primary version controls now that the
   header HERO/CURRENT pills are gone. Star = hero (amber when active);
   lock = current shot-card thumbnail (teal when active). */
.mhi-hero-tools { position: absolute; top: 12px; left: 12px; display: inline-flex; gap: 8px; z-index: 4; }
.mhi-tool-btn {
  width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: color-mix(in srgb, var(--shade-7) 62%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--hi) 55%, transparent);
  color: color-mix(in srgb, var(--ink-hi) 95%, transparent);
  transition: background var(--dur-1) ease, color var(--dur-1) ease, transform 90ms ease, box-shadow var(--dur-1) ease;
}
.mhi-tool-btn svg { pointer-events: none; }
.mhi-tool-btn:hover { transform: scale(1.06); }
.mhi-tool-btn:disabled { opacity: 0.55; cursor: default; transform: none; }
.mhi-hero-star:hover { background: color-mix(in srgb, var(--gold-2) 85%, transparent); color: color-mix(in srgb, var(--ink-deep) 90%, var(--mix-dark)); border-color: color-mix(in srgb, var(--hi-3) 95%, transparent); }
.mhi-hero-star.is-active {
  background: color-mix(in srgb, var(--gold-2) 98%, transparent); color: color-mix(in srgb, var(--ink-deep) 90%, var(--mix-dark)); border-color: var(--hi-3);
  box-shadow: 0 0 0 var(--hairline-w) color-mix(in srgb, var(--hi-3) 65%, transparent), 0 0 12px color-mix(in srgb, var(--gold-2) 70%, transparent);
}
.mhi-current-lock:hover { background: color-mix(in srgb, var(--lock) 85%, transparent); color: var(--lock-ink); border-color: color-mix(in srgb, var(--lock-line) 95%, transparent); }
.mhi-current-lock.is-active {
  background: color-mix(in srgb, var(--lock) 97%, transparent); color: var(--lock-ink); border-color: var(--lock-line);
  box-shadow: 0 0 0 var(--hairline-w) color-mix(in srgb, var(--lock-line) 60%, transparent), 0 0 12px color-mix(in srgb, var(--lock) 60%, transparent);
}
/* v06s — round .mhi-hero-btn on the hero-image cluster is GONE. The
   HERO toggle now lives as a pill in the modal header (see
   .md-hero-pill rule below). */
/* v06z — Hugo: drop the gold border / glow on .is-hero tiles. The
   gold-foil border is reserved for the SELECTED (active) tile. The
   star alone communicates hero status. */
.vt-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  z-index: 0;
}
.vt-label { font-family: var(--font-display); font-size: var(--fs-14); color: var(--white); position: relative; z-index: 1; text-shadow: 0 1px 3px rgba(0,0,0,0.65); }
.vt-time { font-size: var(--fs-10-5); color: color-mix(in srgb, var(--white) 85%, transparent); position: relative; z-index: 1; text-shadow: 0 1px 3px rgba(0,0,0,0.65); }

/* ─── Version history — video row variant ─────────────────────────────
   Frames vs Video are rendered as two separate scrollers (FRAMES on
   top, VIDEO underneath). The video tiles re-use the same thumbnail
   as their matching frame-hero so the eye can match a video back to
   the still it was generated from. A play glyph + a faint dark scrim
   distinguish them from the still tiles in the row above. */
.version-tile--video::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--blue-39) 10%, transparent) 0%,
    color-mix(in srgb, var(--blue-39) 45%, transparent) 100%);
  z-index: 0;
  pointer-events: none;
}
.version-tile--video .vt-img { z-index: 0; }
.version-tile--video .vt-label,
.version-tile--video .vt-time { z-index: 2; }
/* v07e — match the frame tile's gold-foil treatment. The old blue
   border was a holdover from when video tiles had a separate accent. */
.version-tile--video.active {
  border-width: 2px;
  border-color: var(--gold-2);
  box-shadow:
    0 0 0 var(--ring-w) color-mix(in srgb, var(--gold-2) 55%, transparent),
    0 0 16px color-mix(in srgb, var(--gold-2) 55%, transparent),
    0 1px 2px color-mix(in srgb, var(--shadow-ink) 10%, transparent),
    0 4px 10px color-mix(in srgb, var(--shadow-ink) 10%, transparent);
}
/* Centred play glyph — small frosted-glass disc with a triangle play
   icon. Sits on top of the thumbnail; falls back gracefully when no
   thumbnail is present (gradient background still shows the disc). */
.vt-play-overlay {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--blue-39) 55%, transparent);
  backdrop-filter: blur(var(--blur-scrim));
  -webkit-backdrop-filter: blur(var(--blur-scrim));
  border: 1px solid color-mix(in srgb, var(--white) 35%, transparent);
  color: var(--white);
  font-size: var(--fs-12);
  line-height: 1;
  padding-left: 2px;          /* nudge ▶ glyph optically centred */
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
  z-index: 2;
  pointer-events: none;
}

.modal-footer-link { text-align: right; padding-top: 4px; }
.modal-footer-link a {
  font-size: var(--fs-12); color: var(--ink-muted); text-decoration: none;
  display: inline-flex; align-items: center; gap: 4px;
}
.modal-footer-link a:hover { color: var(--ink); }

/* ─── v01i — Reference cards w/ hover tooltip ────────────────────────
   Each .ref-card wraps a circular thumbnail (legacy .ref-thumb) and a
   .ref-tip that pops on hover with a larger preview + the asset name
   and type. Pure CSS — no React state, no extra panel. */
.ref-card {
  position: relative;
  display: inline-flex;
  margin-left: -6px;
}
.ref-card:first-child { margin-left: 0; }
.ref-card .ref-thumb { margin-left: 0; cursor: pointer; }
.ref-empty {
  font-size: var(--fs-12);
  color: var(--ink-muted);
  font-style: italic;
}
/* v06h — Hugo: the previous center-aligned popup extended ~120px past
   each side of the trigger thumb. When refs sit in the right column of
   the meta-row (which they always do — that row is justify-content:
   space-between), the rightward half was getting clipped by the
   modal's right edge. RIGHT-aligning the popup keeps its right edge
   pinned to the thumb's right edge and lets the popup grow only
   leftward, where there's always room. */
.ref-tip {
  position: absolute;
  bottom: calc(100% + 10px);
  right: 0;
  left: auto;
  transform: translateY(2px);
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  background: color-mix(in srgb, var(--cream-2) 97%, transparent);
  border: 1px solid color-mix(in srgb, var(--card-border) 65%, transparent);
  border-radius: var(--r-md);
  box-shadow:
    0 12px 32px color-mix(in srgb, var(--shadow-ink) 32%, transparent),
    0 4px 10px color-mix(in srgb, var(--shadow-ink) 18%, transparent),
    inset 0 1px 0 color-mix(in srgb, var(--cream) 75%, transparent);
  pointer-events: none;
  opacity: 0;
  transition: opacity 160ms ease, transform 160ms ease;
  z-index: 60;
}
.ref-card:hover .ref-tip,
.ref-card:focus-within .ref-tip {
  opacity: 1;
  transform: translateY(0);
}
/* v06i — Hugo: ref preview now matches the actual image's aspect ratio
   so portraits stay tall and landscapes stay wide. Implemented via a
   real <img> tag (the JSX was switched to render an <img> instead of
   a background-image div) capped at max-width / max-height 320 so the
   popup doesn't blow up if a 4K ref is linked. */
.ref-tip-img {
  max-width: 320px;
  max-height: 320px;
  width: auto;
  height: auto;
  display: block;
  border-radius: var(--r-sm);
  background-color: color-mix(in srgb, var(--tan-deep) 10%, transparent);
  box-shadow: inset 0 0 0 var(--hairline-w) color-mix(in srgb, var(--tan-deep) 18%, transparent);
  /* Old `.ref-tip-img` background-size etc. are inert when this is
     rendered as an actual <img>; left in place so legacy callers
     using the background-style shorthand still get a sensible look. */
  background-size: cover;
  background-position: center;
}
.ref-tip-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* Cap meta width to the same max as the image so long names wrap
     inside the popup instead of pushing it sideways. */
  max-width: 320px;
}
.ref-tip-name {
  font-size: var(--fs-13);
  font-weight: var(--fw-semi);
  color: var(--ink);
  line-height: 1.3;
  /* Removed the ellipsis truncate — names can wrap to a second line
     now there's room. */
}
.ref-tip-type {
  font-size: var(--fs-10);
  font-weight: var(--fw-bold);
  letter-spacing: var(--track-08);
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* ─── v06i — Grid detail modal (slice picker row) ───────────────────
   Sits between the hero image and the meta-block. 5 equal-width tiles:
   the first shows the full grid, the next four show frame candidates
   F1-F4 (or a placeholder when the candidate hasn't been generated). */
/* v07zz417 — Hugo: grid modal should be the SAME size as the shot modal (was a narrow 880px),
   with the big image hero. Match .modal-card--shot's footprint; the 21:9 hero scales up with it. */
.grid-detail-modal { max-width: 1180px; max-height: 92vh; }
.grid-detail-modal .modal-hero-image {
  /* v07zb — Hugo: Grid + Frames popups grew taller when the image
     loaded because aspect-ratio: auto + min-height: 320 px gave the
     container one size at empty state and a different one at loaded.
     Pin to the project aspect (21:9 — grids ARE 21:9 quads, and
     individual slices are also 21:9) so the container is the same
     height in BOTH states. No more reflow once the image paints. */
  aspect-ratio: var(--project-aspect, 21 / 9);
  max-height: 60vh;
}
/* v06p — Hugo: full-width gradient at the bottom of the grid hero so
   the version label pill sits inside a darkened band instead of
   leaving a 1-2px sliver of background showing through below it. */
.grid-detail-modal .modal-hero-image::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 56px;
  background: linear-gradient(0deg, color-mix(in srgb, var(--shade-6) 78%, transparent) 0%, color-mix(in srgb, var(--shade-6) 45%, transparent) 45%, transparent 100%);
  pointer-events: none;
  z-index: 1;
}
.grid-detail-modal .mhi-tag {
  /* Sit above the new gradient overlay and align flush with the
     bottom so there's no visible gap below the label. */
  z-index: 2;
  bottom: 8px;
}
.grid-slice-row {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
}
.grid-slice {
  position: relative;
  /* v06q — use project aspect (21:9 default, configurable via the
     --project-aspect :root token) so slice thumbs match every other
     frame thumbnail across the app. */
  aspect-ratio: var(--project-aspect, 21 / 9);
  border-radius: var(--r-xs);
  border: 2px solid transparent;
  background: rgba(0, 0, 0, 0.25);
  cursor: pointer;
  overflow: hidden;
  padding: 0;
  transition: border-color var(--dur-1) ease, transform var(--dur-1) ease, opacity var(--dur-1) ease;
}
.grid-slice img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}
.grid-slice:hover:not(:disabled) { transform: translateY(-1px); }
.grid-slice.is-active {
  border-color: color-mix(in srgb, var(--nav-active) 85%, transparent);
  box-shadow: 0 0 0 var(--hairline-w) color-mix(in srgb, var(--nav-active) 40%, transparent), 0 0 12px color-mix(in srgb, var(--nav-active) 25%, transparent);
}
.grid-slice.is-empty,
.grid-slice:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.grid-slice-placeholder {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-size: var(--fs-16);
  color: color-mix(in srgb, var(--ink-cream) 50%, transparent);
}
.grid-slice-label {
  position: absolute;
  /* v06q — Hugo: the 2px gap at the bottom of each slice was leaving
     a visible strip of background showing through below the gradient.
     Anchor the label flush with the bottom edge instead. */
  bottom: 0;
  left: 0;
  right: 0;
  font-family: var(--font-display);
  font-size: var(--fs-9-5);
  letter-spacing: var(--track-08);
  text-align: center;
  color: var(--ink-cream-3);
  background: linear-gradient(0deg, color-mix(in srgb, var(--shade-6) 85%, transparent) 0%, color-mix(in srgb, var(--shade-6) 55%, transparent) 50%, transparent 100%);
  padding: 4px 2px 4px;
}

/* v07zz318 — "Edit shot" form overlay: every editable shot field as a labelled text box.
   Floats above the shot modal (matches the .addlib-overlay z-index pattern). */
.shotedit-overlay { position: fixed; inset: 0; z-index: var(--z-shotedit); background: color-mix(in srgb, var(--shade-9) 50%, transparent); display: grid; place-items: center; -webkit-backdrop-filter: blur(var(--blur-scrim-3)); backdrop-filter: blur(var(--blur-scrim-3)); padding: 24px; }
.shotedit-modal { width: min(560px, 100%); max-height: 88vh; display: flex; flex-direction: column; background: var(--card-bg-solid, color-mix(in srgb, var(--card-bg-light-solid) 93%, var(--mix-light))); border: 1px solid var(--card-border, color-mix(in srgb, var(--umber-2) 25%, transparent)); border-radius: var(--r-card); box-shadow: 0 28px 80px rgba(0,0,0,0.45); overflow: hidden; }
.shotedit-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 14px 18px; border-bottom: 1px solid var(--card-border, color-mix(in srgb, var(--umber-2) 18%, transparent)); flex: 0 0 auto; }
.shotedit-title { font-size: var(--fs-15); font-weight: var(--fw-bold); color: var(--ink); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shotedit-title b { font-family: var(--font-display); font-weight: 400; letter-spacing: var(--track-01); font-size: 19px; }
.shotedit-sub { color: var(--ink-muted); font-weight: var(--fw-med); }
.shotedit-x { background: transparent; border: 0; color: var(--ink-muted); cursor: pointer; display: grid; place-items: center; width: 28px; height: 28px; border-radius: var(--r-7); flex: 0 0 auto; }
.shotedit-x:hover { background: color-mix(in srgb, var(--black) 6%, transparent); color: var(--ink); }
.shotedit-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px 18px; display: flex; flex-direction: column; gap: 13px; }
.shotedit-field { display: flex; flex-direction: column; gap: 5px; }
.shotedit-field--seq { max-width: 150px; }
.shotedit-label { font-size: var(--fs-10-5); font-weight: var(--fw-bold); letter-spacing: var(--track-05); text-transform: uppercase; color: var(--ink-muted); }
.shotedit-input { font: inherit; font-size: var(--fs-13-5); color: var(--ink); background: color-mix(in srgb, var(--mix-light) 60%, transparent); border: 1px solid var(--card-border); border-radius: var(--r-9); padding: 8px 11px; width: 100%; box-sizing: border-box; }
.shotedit-input:focus { outline: none; border-color: color-mix(in srgb, var(--blue) 60%, transparent); background: var(--mix-light); }
textarea.shotedit-input { resize: vertical; line-height: 1.45; min-height: 40px; }
.shotedit-err { padding: 0 18px 6px; color: var(--red-2); font-size: var(--fs-12-5); font-weight: var(--fw-semi); }
.shotedit-foot { display: flex; justify-content: flex-end; gap: 10px; padding: 12px 18px; border-top: 1px solid var(--card-border, color-mix(in srgb, var(--umber-2) 18%, transparent)); flex: 0 0 auto; }
.shotedit-cancel { font: inherit; font-size: var(--fs-13); font-weight: var(--fw-semi); padding: 8px 16px; border-radius: var(--r-9); border: 1px solid var(--card-border); background: transparent; color: var(--ink-muted); cursor: pointer; }
.shotedit-cancel:hover { background: color-mix(in srgb, var(--black) 5%, transparent); color: var(--ink); }
.shotedit-save { font: inherit; font-size: var(--fs-13); font-weight: var(--fw-bold); padding: 8px 18px; border-radius: var(--r-9); border: 0; background: var(--blue-2); color: var(--white); cursor: pointer; }
.shotedit-save:hover { background: var(--blue-3); }
.shotedit-save:disabled, .shotedit-cancel:disabled { opacity: 0.55; cursor: default; }
/* v07zz319 — narrow variant (Duplicate shot) + dup note + Add-a-frame "Show more" button. */
.shotedit-modal--narrow { width: min(440px, 100%); }
.shotedit-dupnote { font-size: var(--fs-12); line-height: 1.5; color: var(--ink-muted); }
.addlib-more { grid-column: 1 / -1; margin: 8px auto 4px; padding: 9px 20px; border-radius: var(--r-9); border: 1px solid var(--card-border); background: color-mix(in srgb, var(--mix-light) 55%, transparent); color: var(--ink); font: inherit; font-size: var(--fs-12-5); font-weight: var(--fw-bold); cursor: pointer; }
.addlib-more:hover { background: var(--mix-light); border-color: color-mix(in srgb, var(--blue) 50%, transparent); }
.addlib-more:disabled { opacity: 0.5; cursor: default; }
/* v07zz320 — inline-edit the modal's action + VO entries (click to edit, like the title). */
.modal-action--editable, .modal-vo--editable { cursor: text; border-radius: var(--r-xs); transition: background var(--dur-1), box-shadow var(--dur-1); }
.modal-action--editable:hover, .modal-vo--editable:hover { background: color-mix(in srgb, var(--black) 4.5%, transparent); box-shadow: inset 0 0 0 var(--hairline-w) var(--card-border, color-mix(in srgb, var(--umber-2) 22%, transparent)); }
.modal-action-edit, .modal-vo-edit { width: 100%; box-sizing: border-box; font: inherit; color: var(--ink); background: var(--mix-light); border: 1px solid color-mix(in srgb, var(--blue) 60%, transparent); border-radius: var(--r-sm); padding: 7px 10px; resize: vertical; line-height: 1.45; }
.modal-action-edit:focus, .modal-vo-edit:focus { outline: none; border-color: color-mix(in srgb, var(--blue) 85%, transparent); }
.modal-vo-edit { font-style: italic; }

/* ── EditStillButton (v1020) ───────────────────────────────────────────────
   "Pull the still for this shot out of the cut." Lives hard-right of the FRAMES
   header in the shot modal, and in the shotlist's batch action bar. Fixed
   min-width and a message line that is always present, so neither row changes
   size as the button moves between its three faces. */
.esb-wrap { display: inline-flex; align-items: center; gap: 8px; }
.esb-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 132px;
  height: 24px;
  justify-content: center;
  padding: 0 10px;
  font-size: var(--fs-10-5);
  font-weight: var(--fw-bold);
  letter-spacing: var(--track-06);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink-muted);
  background: color-mix(in srgb, var(--cream-12) 70%, transparent);
  border: 1px solid var(--card-border);
  border-radius: var(--r-round);
  cursor: pointer;
  transition: color var(--dur-1) ease, border-color var(--dur-1) ease, background var(--dur-1) ease;
}
.esb-btn:hover:not(:disabled) { color: var(--ink-forest-3); border-color: color-mix(in srgb, var(--olive) 60%, transparent); background: color-mix(in srgb, var(--olive) 14%, transparent); }
.esb-btn:disabled { opacity: 0.55; cursor: default; }
.esb-btn.is-running { color: var(--ink-tan-deep); border-color: color-mix(in srgb, var(--gold-14) 60%, transparent); background: color-mix(in srgb, color-mix(in srgb, var(--gold-10) 90%, var(--mix-light)) 16%, transparent); }
.esb-msg {
  min-height: 13px;        /* holds the line even when empty */
  max-width: 260px;
  font-size: var(--fs-10-5);
  color: var(--ink-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* on the dark batch bar the cream pill would glare */
.esb-btn--onbar {
  color: color-mix(in srgb, var(--card-border) 42%, var(--mix-light));
  background: color-mix(in srgb, var(--mix-light) 10%, transparent);
  border-color: color-mix(in srgb, var(--ink-on-accent) 35%, transparent);
}
.esb-btn--onbar:hover:not(:disabled) { color: var(--white); background: color-mix(in srgb, var(--leaf) 30%, transparent); border-color: color-mix(in srgb, var(--leaf) 65%, transparent); }
.batch-action-bar .esb-msg { color: color-mix(in srgb, var(--ink-on-accent) 75%, transparent); }
