:root {
  --bg: #0e0613;
  --bg-2: #16091e;
  --panel: #1a0e23;
  --panel-2: #221031;
  --line: #2a1638;
  --text: #f3e7ff;
  --muted: #a387b6;
  --accent: #ff3ea5;
  --accent-2: #c7259f;
  --good: #4ade80;
  --warn: #fbbf24;
  --bad: #f87171;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: radial-gradient(ellipse at 15% -10%, #2a0d3a 0%, var(--bg) 55%);
  color: var(--text);
  font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  font-size: 14px;
  min-height: 100vh;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 22px;
  border-bottom: 1px solid var(--line);
  background: rgba(14, 6, 19, 0.7);
  backdrop-filter: blur(10px);
  position: sticky;
  top: 0;
  z-index: 10;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
  letter-spacing: 0.2px;
}
.brand .dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 12px var(--accent);
}
.badge {
  font-size: 10px;
  background: var(--panel);
  border: 1px solid var(--line);
  color: var(--muted);
  padding: 2px 6px;
  border-radius: 4px;
  letter-spacing: 1px;
}

/* === Top-bar pinned items + tab strip === */
/* flex-wrap so the bar flows onto extra rows ONLY when it can't fit on one
   line (narrow / not-fullscreen windows) instead of squishing + overlapping.
   On a wide screen everything still sits on a single row. */
.topbar { gap: 14px; flex-wrap: wrap; row-gap: 10px; }
.topbar-tabs {
  display: flex; align-items: center; gap: 6px;
  flex: 1; min-width: 0; /* allow tab strip to shrink */
  flex-wrap: wrap; row-gap: 6px;
}
.tb-btn {
  background: var(--panel); color: var(--text); border: 1px solid var(--line);
  border-radius: 8px; padding: 6px 12px; font: inherit; font-size: 12px;
  cursor: pointer; white-space: nowrap;
  transition: border-color .15s, background .15s;
}
.tb-btn:hover { border-color: var(--accent); }

/* Shift tracker — green-tinted when clocked in so the operator can see at
   a glance whether the counter is live. Bold the number so it reads as
   a metric, not a label. */
#shift-btn.clocked-in {
  background: rgba(74, 222, 128, 0.15);
  border-color: rgba(74, 222, 128, 0.5);
  color: var(--text);
  font-weight: 600;
}
#shift-btn.clocked-in:hover {
  background: rgba(74, 222, 128, 0.25);
  border-color: rgba(74, 222, 128, 0.7);
}
/* Project search — input + 🔍 fused into one pill, same height/skin as the
   .tb-btn row it sits in. Shakes red when the number doesn't exist. */
.tb-search { display: flex; align-items: stretch; margin: 0; }
.tb-search-input {
  width: 96px; background: var(--panel); color: var(--text); border: 1px solid var(--line);
  border-right: none; border-radius: 8px 0 0 8px; padding: 6px 10px; font: inherit; font-size: 12px;
  outline: none; transition: border-color .15s, width .15s;
}
.tb-search-input:focus { border-color: var(--accent); width: 120px; }
.tb-search-input::placeholder { color: var(--muted); }
.tb-search-go { border-radius: 0 8px 8px 0; padding: 6px 10px; }
.tb-search-input:focus + .tb-search-go { border-color: var(--accent); }
.tb-search.not-found .tb-search-input,
.tb-search.not-found .tb-search-go { border-color: var(--bad); animation: tb-search-shake .3s; }
@keyframes tb-search-shake { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-3px); } 75% { transform: translateX(3px); } }

.tb-dropdown { position: relative; }
.tb-menu {
  display: none; position: absolute; top: calc(100% + 6px); left: 0;
  min-width: 280px; max-height: 60vh; overflow-y: auto;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 14px 40px rgba(0,0,0,0.4); padding: 6px; z-index: 100;
}
.tb-dropdown.open .tb-menu { display: block; }
.tb-menu .menu-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px; border-radius: 6px; cursor: pointer;
}
.tb-menu .menu-item:hover { background: var(--panel); }
.tb-menu .menu-item img { width: 32px; height: 32px; border-radius: 4px; object-fit: cover; }
.tb-menu .menu-item .mi-label { font-size: 13px; color: var(--text); }
.tb-menu .menu-item .mi-sub { font-size: 11px; color: var(--muted); display: flex; align-items: center; gap: 4px; }
.tb-menu .menu-item .project-num {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  background: var(--panel);
  border: 1px solid var(--line);
  padding: 2px 7px;
  border-radius: 4px;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
.tb-menu .menu-item:hover .project-num { color: var(--text); }

/* Zernio status pill (scheduled / publishing / published / failed / deleted) */
.zernio-status {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 3px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  margin-left: 6px;
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--line);
}
.zernio-status.status-published { background: rgba(74, 222, 128, 0.18); color: var(--good); border-color: rgba(74, 222, 128, 0.3); }
.zernio-status.status-publishing { background: rgba(251, 191, 36, 0.18); color: var(--warn); border-color: rgba(251, 191, 36, 0.3); }
.zernio-status.status-scheduled { background: rgba(255, 62, 165, 0.12); color: var(--accent); border-color: rgba(255, 62, 165, 0.3); }
.zernio-status.status-failed { background: rgba(248, 113, 113, 0.18); color: var(--bad); border-color: rgba(248, 113, 113, 0.3); }
.zernio-status.status-deleted { background: var(--panel-2); color: var(--muted); border-color: var(--line); }
.zernio-status.status-unknown { background: var(--panel-2); color: var(--muted); border-color: var(--line); }
.zernio-status.status-suspect {
  background: rgba(248, 113, 113, 0.25);
  color: var(--bad);
  border-color: rgba(248, 113, 113, 0.6);
  font-weight: 800;
  animation: suspect-pulse 1.4s ease-in-out infinite;
}
@keyframes suspect-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(248, 113, 113, 0); }
  50% { box-shadow: 0 0 0 4px rgba(248, 113, 113, 0.18); }
}

/* AI-suggested candidate highlight — thick green border + glow + a clear
   "AI PICK" banner across the bottom of the tile. Loud on purpose so the
   operator can see it at a glance without hovering. The badge moved to
   bottom-left so it doesn't collide with the fullscreen button (top-right). */
.tile.ai-pick {
  outline: 3px solid var(--good);
  outline-offset: -3px;
  box-shadow: 0 0 0 2px rgba(46, 213, 115, 0.25), 0 0 22px rgba(46, 213, 115, 0.45);
}
.tile .ai-pick-badge {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: linear-gradient(180deg, transparent, rgba(46, 213, 115, 0.92) 30%);
  color: #0e0613;
  padding: 16px 8px 6px;
  font-size: 11px;
  font-weight: 900;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  text-align: center;
  text-shadow: 0 1px 2px rgba(0,0,0,0.2);
  z-index: 2;
  pointer-events: none;
}
.tile .ai-pick-badge::before { content: "✨ "; }
.tile.picked.ai-pick {
  /* User picked the AI's suggestion — combine pink (picked) + green glow */
  outline: 3px solid var(--accent);
  box-shadow: 0 0 0 2px rgba(255, 62, 165, 0.25), 0 0 22px rgba(46, 213, 115, 0.55);
}
.tile.picked.ai-pick .ai-pick-badge {
  background: linear-gradient(180deg, transparent, rgba(255, 62, 165, 0.92) 30%);
  color: #fff;
}

/* Image-generation progress (NBP / wan 2.7) — renders into the grid placeholder */
.gen-progress {
  padding: 24px 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: center;
  color: var(--muted);
  font-size: 12px;
}
.gen-progress-text { text-align: center; font-variant-numeric: tabular-nums; }
.gen-progress-bar {
  width: min(80%, 320px);
  height: 6px;
  background: var(--panel);
  border-radius: 3px;
  overflow: hidden;
  border: 1px solid var(--line);
}
.gen-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  transition: width 0.4s ease;
}
.gen-progress-dots {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
  max-width: min(80%, 320px);
}
.gen-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--panel);
  transition: background 0.2s ease, border-color 0.2s ease;
}
.gen-dot.state-pending { background: var(--panel); border-color: var(--line); }
.gen-dot.state-running {
  background: var(--warn);
  border-color: var(--warn);
  animation: gen-pulse 1.2s ease-in-out infinite;
}
.gen-dot.state-done { background: var(--good); border-color: var(--good); }
.gen-dot.state-failed { background: var(--bad); border-color: var(--bad); }
@keyframes gen-pulse {
  0%, 100% { opacity: 0.45; }
  50% { opacity: 1; }
}

/* Pod-output picker (import latest from pod) */
.pod-pick-prompt {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px 12px;
  margin-bottom: 10px;
  background: var(--bg-2);
}
.pod-pick-prompt.match {
  border-color: rgba(74, 222, 128, 0.45);
  background: rgba(74, 222, 128, 0.06);
}
.pod-pick-prompt .pp-head {
  display: flex; align-items: center; gap: 8px;
  font-size: 11px; color: var(--muted); margin-bottom: 8px;
  font-variant-numeric: tabular-nums;
}
.pod-pick-prompt .pp-head .pp-tag {
  background: var(--panel); border: 1px solid var(--line);
  padding: 2px 7px; border-radius: 4px; color: var(--text);
}
.pod-pick-prompt .pp-head .pp-match {
  background: rgba(74, 222, 128, 0.18);
  color: var(--good);
  border: 1px solid rgba(74, 222, 128, 0.3);
  padding: 2px 7px; border-radius: 4px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.4px; font-size: 9px;
}
.pod-pick-files { display: flex; flex-direction: column; gap: 6px; }
.pod-pick-file {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--panel); cursor: pointer;
}
.pod-pick-file:hover { border-color: var(--accent); background: var(--panel-2); }
.pod-pick-file .pf-name { flex: 1; font-size: 12px; font-variant-numeric: tabular-nums; }
.pod-pick-file .pf-node { font-size: 10px; color: var(--muted); }
.pod-pick-file video {
  width: 90px; height: 60px; object-fit: cover; border-radius: 4px;
  background: #000;
}
.step-pill {
  display: inline-block; padding: 1px 6px; border-radius: 3px;
  font-size: 9px; font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase;
  background: var(--panel); color: var(--text); border: 1px solid var(--line);
}
.step-pill.step-7 { background: rgba(74,222,128,0.18); color: var(--good); border-color: rgba(74,222,128,0.3); }
.step-pill.step-6 { background: rgba(199,37,159,0.18); color: var(--accent); border-color: rgba(199,37,159,0.3); }
.step-pill.step-5, .step-pill.step-4 { background: rgba(251,191,36,0.18); color: var(--warn); border-color: rgba(251,191,36,0.3); }
.step-pill.step-3, .step-pill.step-2 { background: rgba(255,62,165,0.12); color: var(--accent); border-color: rgba(255,62,165,0.2); }
.step-pill.step-1 { background: var(--panel-2); color: var(--muted); }
.tb-menu .menu-empty { padding: 16px; text-align: center; color: var(--muted); font-size: 12px; }

.tab-strip {
  display: flex; align-items: center; gap: 4px;
  flex: 1; min-width: 0; overflow-x: auto;
  scrollbar-width: thin;
}
/* Tab strip moved to the left-edge open-tabs sidebar; this element
   is now hidden but kept in the DOM for legacy renderTabStrip calls. */
.tab-strip.hidden { display: none; }
.tab-strip::-webkit-scrollbar { height: 6px; }
.tab-strip::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }
.tab {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 8px 5px 10px; border-radius: 6px;
  background: var(--panel); border: 1px solid var(--line);
  font-size: 12px; cursor: pointer; white-space: nowrap;
  max-width: 200px;
}
.tab.active {
  background: linear-gradient(135deg, rgba(255,62,165,0.18), rgba(199,37,159,0.06));
  border-color: var(--accent);
}
.tab .tab-label { overflow: hidden; text-overflow: ellipsis; max-width: 160px; }
.tab .tab-x {
  background: transparent; border: 0; color: var(--muted);
  font-size: 14px; line-height: 1; padding: 0 2px; cursor: pointer; border-radius: 3px;
}
.tab .tab-x:hover { color: var(--bad); background: rgba(248,113,113,0.18); }

/* Quick Topaz overlay modal */
.qt-overlay {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(0,0,0,0.65); backdrop-filter: blur(4px);
  display: flex; align-items: flex-start; justify-content: center;
  padding-top: 80px;
}
.qt-overlay.hidden { display: none; }
.qt-panel {
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  padding: 16px; min-width: 460px; max-width: 600px;
  box-shadow: 0 24px 60px rgba(0,0,0,0.5);
}
.qt-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; font-weight: 600; }

/* Quick Topaz indeterminate progress bar */
.qt-progress { margin-top: 12px; }
.qt-progress.hidden { display: none; }
.qt-progress-bar {
  position: relative; height: 8px; border-radius: 4px;
  background: var(--panel-2); overflow: hidden;
}
.qt-progress-bar::before {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg,
    transparent 0%,
    var(--accent) 30%,
    var(--accent-2) 60%,
    transparent 100%);
  animation: qt-slide 1.4s ease-in-out infinite;
  width: 50%;
}
@keyframes qt-slide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(300%); }
}
.qt-progress.done .qt-progress-bar::before {
  animation: none; transform: none; width: 100%;
  background: var(--good);
}
.qt-progress-label {
  margin-top: 6px; font-size: 11px; color: var(--muted); text-align: center;
}

/* Output overlay layout: left = video, right = schedule panel (slides in) */
.oo-panel { min-width: 520px; max-width: 720px; transition: max-width .2s ease; }
.oo-panel.with-schedule { max-width: 1140px; }
.oo-flex { display: flex; gap: 16px; align-items: stretch; }
.oo-left { flex: 1; min-width: 0; }
.oo-left video { width: 100%; max-height: 60vh; border-radius: 8px; background: #000; }
.oo-right {
  width: 380px; flex-shrink: 0;
  border-left: 1px solid var(--line);
  padding-left: 16px;
  display: flex; flex-direction: column; gap: 8px;
  max-height: 70vh; overflow-y: auto;
}
.oo-right.hidden { display: none; }
.oo-right-head {
  display: flex; align-items: center; justify-content: space-between;
  font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.6px;
  margin-bottom: 4px;
}
.btn.mini { padding: 2px 8px; font-size: 11px; }
.oo-right textarea, .oo-right input[type="datetime-local"], .oo-right select {
  background: var(--panel-2); color: var(--text); border: 1px solid var(--line);
  border-radius: 6px; padding: 6px 8px; font: inherit; font-size: 12px;
  color-scheme: dark; width: 100%;
}
.oo-right .field span { font-size: 11px; color: var(--muted); }

.topbar-right { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; row-gap: 8px; justify-content: flex-end; }
.topbar-right .btn { padding: 6px 10px; font-size: 12px; }
.topbar-right .btn.hidden { display: none; }
.pill {
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--panel);
  border: 1px solid var(--line);
  font-size: 12px;
  color: var(--muted);
}
.pill.ok { color: var(--good); border-color: rgba(74, 222, 128, 0.3); }
.pill.bad { color: var(--bad); border-color: rgba(248, 113, 113, 0.3); }

.layout {
  display: block;
  padding: 20px;
  max-width: 1100px;
  margin: 0 auto;
}

/* Quiet auto-picker pill — operator doesn't want a loud indicator that
   credits are out. Smaller, muted, no colored variants override. */
.pill.auto-picker-pill {
  font-size: 10px;
  padding: 2px 8px;
  opacity: 0.55;
  color: var(--muted);
  border-color: var(--line);
  background: transparent;
  cursor: pointer;
  transition: opacity 0.15s;
}
.pill.auto-picker-pill:hover { opacity: 1; }

/* Open-projects sidebar — fixed to the left edge so it lives in the empty
   margin space outside the centered .layout column. Auto-hides when there
   are no open tabs (.empty class). On narrow viewports (<1400px the column
   sits closer to the edge), the sidebar gets a translucent backdrop so it
   still reads as separate from the main content. */
.open-tabs-sidebar {
  position: fixed;
  top: 56px; /* sits below the topbar */
  left: 12px;
  width: 230px;
  max-height: calc(100vh - 72px);
  overflow-y: auto;
  /* Solid-ish background instead of backdrop-filter: blur(). The blur forced
     the compositor to re-run an 8px gaussian over the whole panel every time
     any tile inside it repainted — which, with the pulsing tiles, was every
     frame. Dropping it removes a large continuous GPU cost; the higher opacity
     keeps the panel visually separated from the content behind it. */
  background: rgba(15, 15, 20, 0.92);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px;
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.open-tabs-sidebar.empty { display: none; }
.open-tabs-head {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--muted);
  padding: 2px 4px 4px;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
/* Collapse (×) — half-opacity circle in the header, full on hover. */
.open-tabs-collapse {
  flex: none;
  width: 18px; height: 18px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
  opacity: 0.5;
  font-size: 13px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
}
.open-tabs-collapse:hover { opacity: 1; }
/* Collapsed → hide the whole sidebar (the .empty rule already hides it when
   there are no tabs; this is the operator-driven hide). */
.open-tabs-sidebar.collapsed { display: none; }
/* Reopen tab on the left edge — only visible while collapsed. */
.open-tabs-reopen {
  position: fixed;
  top: 64px;
  left: 0;
  z-index: 50;
  width: 22px; height: 46px;
  border: 1px solid var(--line);
  border-left: none;
  border-radius: 0 9px 9px 0;
  background: rgba(15, 15, 20, 0.7);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--text);
  opacity: 0.5;
  font-size: 17px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
}
.open-tabs-reopen:hover { opacity: 1; }
.open-tabs-reopen.hidden { display: none; }
.open-tabs-list { display: flex; flex-direction: column; gap: 6px; }
.open-tab-item {
  display: flex;
  gap: 8px;
  padding: 6px;
  border-radius: 6px;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid transparent;
  transition: background 0.1s, border-color 0.1s;
  position: relative;
}
.open-tab-item:hover { background: rgba(255, 255, 255, 0.06); }
.open-tab-item.active {
  background: rgba(255, 62, 165, 0.12);
  border-color: rgba(255, 62, 165, 0.4);
}

/* === Attention states for open-projects sidebar tiles ===
   - state-needs-attention: human decision required → gentle glow pulse.
     Per-step color comes from the .step-N modifier sitting on the same
     element (each step's CSS custom property --tab-glow sets the hue).
   - state-ready-to-post: special case of needs-attention — money step,
     overrides per-step color with a louder green pulse.
   - state-working: autonomous run in progress → steady amber tint, no
     animation. Tells the operator "this is cooking, leave it alone."
   - state-done: posted to Zernio → default styling, nothing to do.
   Animation is intentionally slow (2.4s cycle) — the operator catches it
   with peripheral vision but it doesn't get distracting on a screen with
   10 open projects. */
/* Attention states are STATIC highlights — no animation. The old version
   animated box-shadow (a non-composited paint property) on an infinite loop,
   so every tile on a decision step repainted ~60x/sec forever. With ~100 open
   tabs that pegged the main thread and made the whole UI — including the
   step-1 scrubber drag — lag. A frozen glow + border carries the same
   at-a-glance signal at zero ongoing cost. */
.open-tab-item.state-needs-attention {
  /* fallback color if no step-N modifier matched; gets overridden below */
  --tab-glow: rgba(255, 62, 165, 0.45);
  --tab-glow-border: rgba(255, 62, 165, 0.6);
  border-color: var(--tab-glow-border);
  box-shadow: 0 0 10px 1px var(--tab-glow);
}
/* Per-step glow colors mirror the existing .step-pill.step-N palette so the
   visual association the operator already has carries over. */
.open-tab-item.state-needs-attention.step-2 { --tab-glow: rgba(255, 165, 0, 0.45); --tab-glow-border: rgba(255, 165, 0, 0.65); }   /* orange — pick NBP */
.open-tab-item.state-needs-attention.step-3 { --tab-glow: rgba(168, 85, 247, 0.5); --tab-glow-border: rgba(168, 85, 247, 0.7); }   /* purple — pick wan 2.7 */
.open-tab-item.state-needs-attention.step-4 { --tab-glow: rgba(56, 189, 248, 0.5); --tab-glow-border: rgba(56, 189, 248, 0.7); }   /* cyan — pick motion engine */
.open-tab-item.state-needs-attention.step-6 { --tab-glow: rgba(251, 191, 36, 0.55); --tab-glow-border: rgba(251, 191, 36, 0.75); } /* amber — click run topaz */
/* Ready-to-post is the money step — solid green, overrides per-step coloring. */
.open-tab-item.state-ready-to-post {
  --tab-glow: rgba(74, 222, 128, 0.55);
  --tab-glow-border: rgba(74, 222, 128, 0.85);
  border-color: var(--tab-glow-border);
  box-shadow: 0 0 12px 1px var(--tab-glow);
}
/* Working state — autonomous, steady soft amber border, no pulse. */
.open-tab-item.state-working {
  border-color: rgba(251, 191, 36, 0.25);
  background: rgba(251, 191, 36, 0.04);
}
/* Done state — no special styling; let default + .active rules apply. */
.open-tab-item.state-done { /* intentionally empty */ }
.open-tab-item img,
.open-tab-item .open-tab-thumb-placeholder {
  width: 44px;
  height: 44px;
  border-radius: 4px;
  object-fit: cover;
  flex-shrink: 0;
  background: #222;
}
.open-tab-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.open-tab-label {
  font-size: 12px;
  color: var(--text);
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}
.open-tab-handle {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 1;
}
.open-tab-num {
  font-size: 10px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
  /* ids are 5 digits now (10000+) — never clip the number, and keep clear
     of the hover ✕ in the tile's top-right corner. */
  white-space: nowrap;
  overflow: visible;
  margin-right: 14px;
}
.open-tab-step {
  font-size: 10px;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 4px;
}
.open-tab-close {
  position: absolute;
  top: 2px;
  right: 4px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.4);
  color: rgba(255, 255, 255, 0.7);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  display: none;
}
.open-tab-item:hover .open-tab-close,
.open-tab-item.active .open-tab-close { display: block; }
.open-tab-close:hover {
  background: rgba(248, 113, 113, 0.6);
  color: white;
}
@media (max-width: 1400px) {
  /* on narrower screens the sidebar would overlap the centered .layout —
     drop the fixed position and let it just be hidden; operators use
     desktops, but mobile/small windows shouldn't render a broken overlay. */
  .open-tabs-sidebar { display: none; }
}
.content { display: flex; flex-direction: column; gap: 16px; }
/* tiny visual touches for engine pills inside outputs dropdown */
.tb-menu .engine {
  display: inline-block; margin-left: 6px; padding: 1px 6px;
  border-radius: 4px; font-size: 9px; font-weight: 700; letter-spacing: 0.5px;
  text-transform: uppercase;
}
.tb-menu .engine.kling { background: rgba(251,191,36,0.18); color: var(--warn); }
.tb-menu .engine.wan { background: rgba(74,222,128,0.18); color: var(--good); }
.tb-menu .engine.quick { background: rgba(255,62,165,0.18); color: var(--accent); }
/* topaz history collapsed dropdown */
.side-card.collapsed { padding: 10px 14px; }
.side-card.collapsed summary {
  cursor: pointer;
  list-style: none;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.6px;
  padding: 4px 0;
}
.side-card.collapsed summary::after { content: " ▾"; opacity: 0.6; }
.side-card.collapsed[open] summary::after { content: " ▴"; }
.side-card.collapsed[open] { padding-bottom: 14px; }

.content { display: flex; flex-direction: column; gap: 16px; }
.sidebar { display: flex; flex-direction: column; gap: 16px; }

.card, .side-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 18px;
}
.card.hidden { display: none; }

.step-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}
.step-head h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.2px;
}
.step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  background: var(--accent);
  color: #18001a;
  border-radius: 8px;
  font-weight: 700;
  font-size: 13px;
  box-shadow: 0 0 16px rgba(255, 62, 165, 0.35);
}
.caption {
  margin-left: auto;
  font-size: 12px;
  color: var(--muted);
  max-width: 50%;
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 12px;
}
.field span {
  font-size: 12px;
  color: var(--muted);
  text-transform: lowercase;
  letter-spacing: 0.2px;
}
.field input {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  padding: 10px 12px;
  font: inherit;
  width: 100%;
}
.field input:focus { outline: none; border-color: var(--accent); }

.actions { display: flex; gap: 8px; align-items: center; margin-top: 4px; }

.btn {
  border: 1px solid var(--line);
  background: var(--bg-2);
  color: var(--text);
  padding: 10px 16px;
  border-radius: 10px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.04s ease, background 0.15s ease, box-shadow 0.15s ease;
}
.btn:hover { background: var(--panel-2); }
.btn:active { transform: translateY(1px); }
.btn.primary {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  border: none;
  color: #170018;
  box-shadow: 0 6px 24px -8px var(--accent);
}
.btn.ghost { background: transparent; border-color: var(--line); color: var(--muted); }
.btn.ghost.toggled {
  background: rgba(255, 62, 165, 0.12);
  border-color: var(--accent);
  color: var(--text);
}

/* Segmented engine selector for step 6.5 + step 7 */
.engine-toggle {
  display: inline-flex;
  margin-left: auto;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  background: var(--bg-2);
}
.engine-toggle .engine-btn {
  background: transparent;
  border: 0;
  border-left: 1px solid var(--line);
  color: var(--muted);
  padding: 6px 12px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.engine-toggle .engine-btn:first-child { border-left: 0; }
.engine-toggle .engine-btn:hover:not([disabled]) { background: var(--panel-2); color: var(--text); }
.engine-toggle .engine-btn.active {
  background: rgba(255, 62, 165, 0.18);
  color: var(--text);
}
.engine-toggle .engine-btn[disabled] {
  opacity: 0.35;
  cursor: not-allowed;
}
.btn[disabled] { opacity: 0.5; cursor: not-allowed; }

.status {
  margin-top: 10px;
  font-size: 12px;
  color: var(--muted);
  min-height: 16px;
}
.status.err { color: var(--bad); }
.status.ok { color: var(--good); }

.picker-row {
  display: grid;
  grid-template-columns: 160px 1fr;
  gap: 14px;
  align-items: start;
}
@media (max-width: 700px) {
  .picker-row { grid-template-columns: 1fr; }
  .reference-pane { display: flex; gap: 10px; align-items: center; }
}

.reference-pane { display: flex; flex-direction: column; gap: 6px; position: sticky; top: 80px; }
.ref-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--accent);
  font-weight: 600;
}
.ref-img {
  width: 100%;
  aspect-ratio: 9 / 16;
  object-fit: cover;
  border-radius: 12px;
  border: 2px solid var(--accent);
  background: black;
  box-shadow: 0 0 16px -4px rgba(255, 62, 165, 0.4);
}
/* Stacked source frames — one per batch when a re-run used a different
   starting frame. The first frame keeps .ref-img; extras sit beneath it,
   slightly dimmed with the frame number so it's obvious which batch of
   candidates came from which frame. */
#nbp-source-extra { display: flex; flex-direction: column; gap: 6px; }
#nbp-source-extra .ref-frame { position: relative; margin-top: 6px; }
#nbp-source-extra .ref-frame img { opacity: 0.92; }
#nbp-source-extra .ref-frame-tag {
  position: absolute; top: 6px; left: 6px;
  padding: 2px 7px; border-radius: 10px;
  background: rgba(0,0,0,0.72); color: #fff;
  font-size: 10px; font-weight: 600; letter-spacing: 0.3px;
  border: 1px solid rgba(255,255,255,0.18);
}
@media (max-width: 700px) {
  .ref-img { width: 90px; height: auto; }
}

.grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
}
@media (max-width: 700px) { .grid { grid-template-columns: repeat(2, 1fr); } }

.tile {
  position: relative;
  border-radius: 12px;
  overflow: hidden;
  border: 2px solid transparent;
  background: var(--bg-2);
  cursor: pointer;
  aspect-ratio: 9 / 16;
  transition: border-color 0.15s ease, transform 0.04s ease;
}
.tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tile:hover { border-color: var(--accent-2); }
.tile.picked { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(255, 62, 165, 0.25); }
.tile .tag {
  position: absolute;
  top: 6px;
  left: 6px;
  background: rgba(0, 0, 0, 0.55);
  border-radius: 6px;
  padding: 2px 6px;
  font-size: 11px;
  font-weight: 700;
}
.tile.picked .tag { background: var(--accent); color: #170018; }
.tile .checkbox {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  border: 2px solid rgba(255, 255, 255, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  color: transparent;
}
.tile.picked .checkbox {
  background: var(--accent);
  border-color: var(--accent);
  color: #170018;
}
.tile.picked .checkbox::after { content: "✓"; font-weight: 900; }

/* AI pick reasoning line — sits right below the candidate grid so the
   operator sees what the auto-picker thought without hovering. */
.ai-pick-reasoning {
  margin-top: 10px;
  padding: 8px 12px;
  background: rgba(124, 58, 237, 0.08);
  border-left: 3px solid var(--accent-2);
  border-radius: 4px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text);
}
.ai-pick-tag {
  display: inline-block;
  background: var(--accent-2);
  color: #fff;
  padding: 1px 6px;
  border-radius: 3px;
  font-weight: 700;
  margin-right: 6px;
  font-size: 11px;
}

/* Fullscreen button — tile top-right corner. Always visible (not hover-only)
   so it's discoverable even on touch devices, but with a low base opacity so
   it doesn't fight the image. Hover lights it up. The checkbox indicator is
   shifted left to make room. */
.tile .fullscreen-btn {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 26px;
  height: 26px;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.55);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #fff;
  opacity: 0.7;
  transition: opacity 0.12s ease, background 0.12s ease, transform 0.06s ease;
  z-index: 2;
  padding: 0;
}
.tile .fullscreen-btn:hover { opacity: 1; background: rgba(0, 0, 0, 0.75); transform: scale(1.06); }
.tile .fullscreen-btn:active { transform: scale(0.94); }
.tile .fullscreen-btn svg { width: 14px; height: 14px; }
/* Shift checkbox left so it doesn't overlap the fullscreen button. */
.tile .checkbox { right: 38px; }

/* === Lightbox modal (fullscreen image viewer with prev/next navigation) === */
#lightbox {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
}
#lightbox.hidden { display: none; }
/* There is NO bare `.hidden { display: none }` rule in this stylesheet — every
   hide rule is compound (.card.hidden, .qt-overlay.hidden, …). #source-preview
   carries a plain class="hidden" (index.html), so it was never actually hidden:
   the step-1 panel sat on every dashboard from page load showing its default
   "loading…" text over an empty black <video>, and the × / URL-change handlers
   that call classList.add("hidden") did nothing. VAs read that as "stuck on
   loading". Scoped to this id on purpose — a global .hidden rule would newly
   hide ~13 other elements that have quietly relied on the broken behaviour. */
#source-preview.hidden { display: none; }
.lightbox-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(8, 0, 12, 0.92);
  backdrop-filter: blur(4px);
}
.lightbox-stage {
  position: relative;
  max-width: 92vw;
  max-height: 88vh;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}
.lightbox-stage figure { margin: 0; }
/* Default (non-compare) layout: main pane is display:contents so the <img>
   acts as a direct flex child of .lightbox-stage (preserves the original
   single-image lightbox layout for step 3 / phase7 / etc). The compare
   pane is fully hidden — including its <img>, which would otherwise
   render its alt text since it has no src outside compare mode. */
.lightbox-stage .lightbox-main-pane { display: contents; }
.lightbox-stage .lightbox-compare-pane { display: none; }
.lightbox-stage img {
  max-width: 92vw;
  max-height: 88vh;
  object-fit: contain;
  border-radius: 8px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.7);
}
/* Compare mode (step 2 NBP grid only). #lightbox[data-compare="1"] flips
   the stage to a two-column layout: source frame left, candidate right. */
#lightbox[data-compare="1"] .lightbox-stage {
  gap: 16px;
  max-width: 96vw;
}
#lightbox[data-compare="1"] .lightbox-stage .lightbox-main-pane,
#lightbox[data-compare="1"] .lightbox-stage .lightbox-compare-pane {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
#lightbox[data-compare="1"] .lightbox-stage img {
  max-width: 46vw;
  max-height: 84vh;
}
#lightbox[data-compare="1"] figcaption {
  color: rgba(255, 255, 255, 0.7);
  font-size: 12px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}
.lightbox-close,
.lightbox-prev,
.lightbox-next {
  position: absolute;
  z-index: 2;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: #fff;
  cursor: pointer;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.12s ease, border-color 0.12s ease, transform 0.06s ease;
}
.lightbox-close:hover,
.lightbox-prev:hover,
.lightbox-next:hover {
  background: rgba(255, 62, 165, 0.18);
  border-color: rgba(255, 62, 165, 0.5);
}
.lightbox-close:active,
.lightbox-prev:active,
.lightbox-next:active { transform: scale(0.92); }
.lightbox-close {
  top: 24px;
  right: 24px;
  width: 44px;
  height: 44px;
  font-size: 24px;
  line-height: 1;
  font-weight: 300;
}
.lightbox-prev,
.lightbox-next {
  top: 50%;
  transform: translateY(-50%);
  width: 56px;
  height: 56px;
  font-size: 36px;
  line-height: 1;
  font-weight: 300;
}
.lightbox-prev { left: 24px; }
.lightbox-next { right: 24px; }
.lightbox-prev:active { transform: translateY(-50%) scale(0.92); }
.lightbox-next:active { transform: translateY(-50%) scale(0.92); }
.lightbox-caption {
  position: absolute;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  padding: 6px 14px;
  border-radius: 18px;
  font-size: 13px;
  letter-spacing: 0.5px;
}

/* === Pending tile (Higgsfield-style "loading rectangle" in the grid) ===
   Placeholder shown while a NBP / wan-2.7 candidate is generating. Same
   aspect ratio as a finished tile so the grid layout doesn't reflow when it
   flips to the real image. Per-slot mini progress bar fills based on
   elapsed time vs the model's typical duration. */
.tile.pending-tile {
  cursor: default;
  background: linear-gradient(135deg, var(--bg-2) 0%, var(--bg-1) 100%);
  border-color: rgba(255, 62, 165, 0.18);
}
.tile.pending-tile:hover { border-color: rgba(255, 62, 165, 0.18); /* don't react to hover, it's not clickable */ }
.tile.pending-tile .tag { background: rgba(255, 62, 165, 0.65); color: #170018; }
/* Failed state — red border + dimmed icon so the operator sees AT A GLANCE
   that the AI gave up on this slot (and they should try a different video
   or different frame). */
.tile.pending-tile[data-state="failed"] {
  border-color: rgba(255, 80, 80, 0.55);
  background: linear-gradient(135deg, rgba(60, 0, 0, 0.4) 0%, var(--bg-1) 100%);
}
.tile.pending-tile[data-state="failed"] .pending-icon { animation: none; opacity: 0.35; color: rgba(255, 100, 100, 0.6); }
.tile.pending-tile[data-state="failed"] .pending-label { color: rgba(255, 130, 130, 0.95); font-weight: 600; }
.tile.pending-tile[data-state="failed"] .pending-fill { background: rgba(255, 80, 80, 0.4); animation: none; }
.pending-body {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 16px;
  text-align: center;
  color: var(--muted);
}
.pending-icon {
  color: rgba(255, 62, 165, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  animation: pending-pulse 1.6s ease-in-out infinite;
}
@keyframes pending-pulse {
  0%, 100% { opacity: 0.45; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.05); }
}
.pending-label {
  font-size: 11px;
  letter-spacing: 0.3px;
  opacity: 0.85;
}
.pending-bar {
  width: 80%;
  height: 4px;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 2px;
  overflow: hidden;
}
.pending-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  border-radius: 2px;
  transition: width 0.18s linear;
  /* Indeterminate shimmer overlay so the bar doesn't look stalled if the
     model takes longer than NBP_ETA_SEC. */
  background-size: 200% 100%;
  animation: pending-shimmer 1.4s linear infinite;
}
@keyframes pending-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* === ComfyUI-style live progress bar (step 5 wan-animate) === */
.comfy-progress {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 4px 0;
}
.comfy-progress-text {
  font-size: 12px;
  letter-spacing: 0.3px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.comfy-progress-bar {
  width: 100%;
  height: 6px;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 3px;
  overflow: hidden;
}
.comfy-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  border-radius: 3px;
  transition: width 0.25s ease;
}
/* Indeterminate stripe shows while we're waiting for the first WS update
   ("queuing…" state). Slides forever so user sees something is happening. */
.comfy-progress-fill.comfy-indeterminate {
  width: 30% !important;
  animation: comfy-indeterminate 1.6s linear infinite;
  background: linear-gradient(90deg, transparent 0%, var(--accent) 50%, transparent 100%);
}
@keyframes comfy-indeterminate {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(400%); }
}

.commit-row {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
  margin-top: 14px;
}
.commit-row .hint { font-size: 12px; color: var(--muted); margin-right: auto; }

.reframe-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  font-size: 12px;
  color: var(--muted);
}
.reframe-row input {
  width: 64px; padding: 6px; background: var(--bg-2); border: 1px solid var(--line);
  color: var(--text); border-radius: 6px; text-align: center; font: inherit;
  font-variant-numeric: tabular-nums;
}
/* Hide native browser spinner — we provide our own ▲▼ buttons that auto-reframe. */
.reframe-row input::-webkit-outer-spin-button,
.reframe-row input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.reframe-row input[type=number] { -moz-appearance: textfield; }
/* Big chunky step buttons that are obvious targets, unlike the native spinner. */
.frame-step-btn {
  min-width: 30px; padding: 6px 8px !important; font-size: 14px !important;
  line-height: 1; font-weight: 700;
}
.frame-step-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.engines-row { display: flex; gap: 16px; margin-bottom: 8px; font-size: 12px; color: var(--muted); }
.engines-row label { display: flex; align-items: center; gap: 6px; }

.results-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 700px) { .results-row { grid-template-columns: 1fr; } }
.result-col { display: flex; flex-direction: column; gap: 6px; }
.result-title { font-size: 12px; color: var(--muted); text-transform: lowercase; letter-spacing: 0.2px; }
.result-box {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px;
  min-height: 80px;
  font-size: 12px;
  color: var(--muted);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.result-box video { width: 100%; border-radius: 8px; background: black; }
.result-box.done { color: var(--good); }
.result-box.err { color: var(--bad); }

.side-card h3 { margin: 0 0 12px; font-size: 13px; font-weight: 600; letter-spacing: 0.4px; text-transform: uppercase; color: var(--muted); }
.projects { display: flex; flex-direction: column; gap: 8px; max-height: 600px; overflow-y: auto; }
.project-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: 8px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  font-size: 12px;
}
.project-item img { width: 38px; height: 38px; object-fit: cover; border-radius: 6px; flex-shrink: 0; background: black; }
.project-item .pid { font-weight: 700; color: var(--text); }
.project-item .pcap { color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.project-item .pmark { margin-left: auto; font-size: 11px; padding: 2px 6px; border-radius: 4px; background: var(--panel-2); color: var(--muted); }
.project-item .pmark.done { background: rgba(74, 222, 128, 0.15); color: var(--good); }

.history-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 400px;
  overflow-y: auto;
}
.history-item {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  cursor: pointer;
}
.history-item:hover { border-color: var(--accent-2); }
.history-item .row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 11px;
  color: var(--muted);
  gap: 6px;
}
.history-item .row .pid { color: var(--text); font-weight: 700; }
.history-item .row .engine {
  text-transform: uppercase;
  font-size: 10px;
  letter-spacing: 0.4px;
  padding: 2px 5px;
  border-radius: 4px;
  background: var(--panel-2);
}
.history-item .row .engine.kling { color: #fbbf24; }
.history-item .row .engine.wan { color: #4ade80; }
.history-item video {
  width: 100%;
  aspect-ratio: 9/16;
  background: black;
  border-radius: 6px;
  object-fit: cover;
  display: none;
}
.history-item.expanded video { display: block; }
.project-item { cursor: pointer; transition: border-color 0.15s ease; }
.project-item:hover { border-color: var(--accent-2); }

/* Project detail panel */
.folder-section {
  margin-bottom: 18px;
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
}
.folder-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  background: var(--bg-2);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--accent);
}
.folder-header .count { color: var(--muted); font-weight: 400; text-transform: none; letter-spacing: 0; }
.folder-body { padding: 12px; display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
.file-card {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.file-card img, .file-card video { width: 100%; aspect-ratio: 9/16; object-fit: cover; background: black; display: block; }
.file-card .file-meta {
  padding: 6px 8px;
  font-size: 11px;
  color: var(--muted);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 6px;
}
.file-card .file-name { color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; min-width: 0; }
.file-card.video-card video { aspect-ratio: 9/16; }
.file-card.json-card { padding: 10px; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11px; color: var(--muted); white-space: pre-wrap; max-height: 280px; overflow: auto; aspect-ratio: unset; }
.file-card a { color: var(--accent); text-decoration: none; }
.file-card a:hover { text-decoration: underline; }

/* === Multi-project: new-project button + active highlight === */
.newproject-row { display: flex; align-items: center; gap: 12px; margin-bottom: -4px; }
.newproject-row .btn { padding: 8px 14px; font-size: 13px; }
.newproject-row .caption { color: var(--muted); font-size: 12px; }
.project-item.active {
  background: linear-gradient(135deg, rgba(255,62,165,0.18), rgba(199,37,159,0.06));
  border: 1px solid rgba(255,62,165,0.55);
}
.project-del {
  background: transparent; color: var(--muted); border: 0;
  font-size: 16px; line-height: 1; padding: 4px 6px; cursor: pointer;
  border-radius: 4px; opacity: 0.4; transition: opacity .15s, background .15s, color .15s;
}
.project-item:hover .project-del { opacity: 1; }
.project-del:hover { background: rgba(248,113,113,0.18); color: var(--bad); }
/* NBP-as-pick tile in the wan 2.7 grid (skip enlargement) */
.nbp-tile { border: 1px dashed var(--accent); position: relative; }
.nbp-tile .tag { background: var(--accent); color: #fff; font-weight: 600; }
.nbp-tile-label {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: linear-gradient(transparent, rgba(0,0,0,0.85));
  color: #fff; font-size: 11px; padding: 14px 6px 6px; text-align: center;
  pointer-events: none; letter-spacing: 0.4px;
}

/* === Platform chips (Step 7) === */
.platform-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.platform-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 12px; border-radius: 999px;
  background: var(--panel-2); color: var(--muted);
  border: 1px solid var(--line); cursor: pointer;
  font: inherit; font-size: 12px;
  transition: border-color .15s, color .15s, background .15s, opacity .15s;
  opacity: 0.55;
}
.platform-chip:hover { opacity: 1; }
.platform-chip .chip-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--panel); font-weight: 600; font-size: 10px;
  color: var(--text);
}
.platform-chip .chip-check { display: none; color: var(--good); font-weight: 700; }
.platform-chip.selected {
  opacity: 1; color: var(--text);
  border-color: var(--accent);
  background: linear-gradient(135deg, rgba(255,62,165,0.12), rgba(199,37,159,0.06));
}
.platform-chip.selected .chip-check { display: inline; }

/* === Quick Topaz mini card (lives in .left-utility) === */
.mini-card { padding: 10px 12px; opacity: 0.85; }
.mini-card:hover { opacity: 1; }
.mini-head {
  font-size: 11px; letter-spacing: 0.6px; text-transform: uppercase;
  color: var(--muted); margin-bottom: 6px;
}

/* Quick Topaz drop zone — compact variant */
.drop-zone {
  border: 1px dashed var(--line);
  border-radius: 8px;
  padding: 10px;
  text-align: center;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  background: rgba(255,255,255,0.01);
  transition: border-color .15s, background .15s;
  cursor: default;
  font-size: 12px; color: var(--muted);
}
.drop-zone.dragging { border-color: var(--accent); background: rgba(255,62,165,0.06); color: var(--text); }
.drop-zone.mini .btn { padding: 3px 8px; font-size: 11px; }
.btn.mini { padding: 3px 8px; font-size: 11px; }
.status.mini { font-size: 11px; min-height: 0; padding: 2px 0; }
#topaz-direct-result { margin-top: 8px; }
#topaz-direct-result video { width: 100%; border-radius: 6px; background: #000; }

/* === Step 7 (Zernio post) === */
.phase7-row { display: grid; grid-template-columns: 220px 1fr 1fr; gap: 16px; align-items: start; }
.phase7-col { display: flex; flex-direction: column; gap: 10px; }
/* Step 7 card itself gets positioned so children can absolute-anchor to it. */
#phase7-card { position: relative; }

/* High-quality preview panel — absolutely positioned just outside the
   step 7 card's right edge so it sits in the empty right margin AND
   scrolls together with the card itself (instead of floating fixed at
   the top of the viewport, which made it visually disconnected from
   step 7 when scrolled). */
.phase7-hq-sidebar {
  position: absolute;
  top: 12px;
  left: calc(100% + 16px);
  width: 260px;
  background: rgba(15, 15, 20, 0.55);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px;
  z-index: 50;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.phase7-hq-sidebar.empty { display: none; }
.phase7-hq-head {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 2px 4px 4px;
  border-bottom: 1px solid var(--line);
}
.phase7-hq-head > span:first-child {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--accent);
  font-weight: 600;
}
.phase7-hq-sub {
  font-size: 9px;
  color: var(--muted);
  opacity: 0.7;
}
.phase7-hq-sidebar video {
  width: 100%;
  max-height: 60vh;
  border-radius: 6px;
  background: #000;
}
@media (max-width: 1500px) {
  /* under 1500px the centered .layout starts crowding the right margin;
     hide the HQ panel so it doesn't overlap the main column. Operator can
     still download the HQ file via the ⬇ download button. */
  .phase7-hq-sidebar { display: none; }
}

/* === Step 7 day calendar === */
/* Extra calendars sidebar — mirror of .phase7-hq-sidebar but on the LEFT
   side of the step 7 card. Absolutely positioned outside the card's
   left edge so it floats in the empty margin without disturbing the
   centered .layout. Stacks one .phase7-cal-instance per extra Zernio
   profile (cap 2 to keep sidebar height manageable).
   #phase7-card already has position:relative for the HQ sidebar, so
   absolute positioning works against it for this aside too. */
.phase7-cal-extras-sidebar {
  position: absolute;
  top: 12px;
  right: calc(100% + 16px);
  width: 240px;
  background: rgba(15, 15, 20, 0.55);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px;
  z-index: 50;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.phase7-cal-extras-sidebar.empty { display: none; }
.phase7-cal-instance {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  border-left: 3px solid transparent;
  padding-left: 8px;
}
.phase7-cal-instance-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--accent);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.phase7-cal-instance .cal-grid {
  min-width: 0;
  height: 672px;
}
/* Tint colors for extra calendars — left-border accent + inset shadow
   on the cal-block backgrounds so blocks are distinguishable from
   primary pink. */
.extra-tint-1 { border-left-color: rgba(56, 189, 248, 0.6); }   /* cyan */
.extra-tint-1 .cal-block { box-shadow: inset 3px 0 0 rgba(56, 189, 248, 0.6); }
.extra-tint-2 { border-left-color: rgba(168, 85, 247, 0.6); }   /* purple */
.extra-tint-2 .cal-block { box-shadow: inset 3px 0 0 rgba(168, 85, 247, 0.6); }
.extra-tint-3 { border-left-color: rgba(74, 222, 128, 0.6); }   /* green */
.extra-tint-3 .cal-block { box-shadow: inset 3px 0 0 rgba(74, 222, 128, 0.6); }
/* Below 1500px the centered .layout starts crowding the left margin;
   hide the extras sidebar so it doesn't overlap the main column.
   Operator can still see extra-profile scheduled posts in Zernio's
   own dashboard. */
@media (max-width: 1500px) {
  .phase7-cal-extras-sidebar { display: none; }
}

.phase7-cal-col {
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px;
  min-width: 0;
}
.cal-header {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text);
  font-weight: 600;
}
.cal-header .mini { padding: 2px 8px; font-size: 11px; }
.cal-header #cal-day-label {
  flex: 1;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.cal-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-size: 10px;
  color: var(--muted);
  margin-bottom: 4px;
}
.cal-legend-item { display: inline-flex; align-items: center; gap: 4px; }
.cal-swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: var(--accent);
}
.cal-swatch.trial-manual { background: var(--warn); }
.cal-swatch.trial-auto { background: var(--good); }
/* Each hour row is 28px tall = 24 * 28 = 672px total grid height */
.cal-grid {
  position: relative;
  height: 672px;
  border-top: 1px solid var(--line);
  background: linear-gradient(to bottom, transparent calc(28px - 1px), var(--line) calc(28px - 1px), var(--line) 28px) 0 0 / 100% 28px;
  overflow: hidden;
}
.cal-hour {
  position: absolute;
  left: 0;
  right: 0;
  height: 28px;
  display: flex;
  align-items: flex-start;
  padding-top: 1px;
  font-size: 9px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.cal-hour-label {
  width: 36px;
  padding-left: 4px;
  flex-shrink: 0;
}
.cal-now-line {
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--accent);
  box-shadow: 0 0 4px var(--accent);
  z-index: 5;
  pointer-events: none;
}
.cal-now-line::before {
  content: "";
  position: absolute;
  left: 32px;
  top: -3px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
}
.cal-block {
  position: absolute;
  left: 40px;
  right: 6px;
  background: rgba(255, 62, 165, 0.18);
  border: 1px solid var(--accent);
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  padding: 3px 6px;
  font-size: 11px;
  color: var(--text);
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: transform 0.08s ease;
  font-variant-numeric: tabular-nums;
}
.cal-block:hover {
  transform: translateX(-1px);
  background: rgba(255, 62, 165, 0.28);
}
.cal-block.trial-manual {
  background: rgba(251, 191, 36, 0.18);
  border-color: var(--warn);
  border-left-color: var(--warn);
}
.cal-block.trial-manual:hover { background: rgba(251, 191, 36, 0.28); }
.cal-block.trial-auto {
  background: rgba(74, 222, 128, 0.16);
  border-color: var(--good);
  border-left-color: var(--good);
}
.cal-block.trial-auto:hover { background: rgba(74, 222, 128, 0.28); }

/* PHT operator legend (2026-05-29):
     trial reel  → yellow (IG) + GREY (TT)
     normal reel → ALL PINK (IG and TT both pink)
     story       → blue (IG)
   TT color encodes the mode: grey for a trial reel's TikTok side, pink for
   a normal reel (so a normal reel reads as "all pink"). A TT-only block
   inherits its trial state from the IG sibling at the same time (see the
   inheritTrialFromSiblings logic in buildCalendarHtml / buildCalGridHtml). */
/* TikTok-only post. Normal = pink (keeps a normal reel "all pink"). */
.cal-block.tiktok {
  background: rgba(255, 62, 165, 0.18);
  border-color: var(--accent);
  border-left-color: var(--accent);
}
.cal-block.tiktok:hover { background: rgba(255, 62, 165, 0.28); }
/* Trial reel's TikTok side → grey. */
.cal-block.tiktok.trial-manual,
.cal-block.tiktok.trial-auto {
  background: rgba(148, 163, 184, 0.30);
  border-color: #94a3b8;
  border-left-color: #94a3b8;
}
.cal-block.tiktok.trial-manual:hover,
.cal-block.tiktok.trial-auto:hover { background: rgba(148, 163, 184, 0.44); }
.cal-swatch.tiktok { background: #94a3b8; }

/* Single Zernio post that posts to BOTH IG + TT at once.
   `background` here wins over the solid-color rules above. */
.cal-block.multi-ig-tt {
  /* Normal reel · IG+TT → solid pink ("all pink"). */
  background: rgba(255, 62, 165, 0.18);
}
.cal-block.multi-ig-tt:hover { background: rgba(255, 62, 165, 0.28); }
.cal-block.multi-ig-tt.trial-manual {
  /* Trial reel (manual) · IG+TT → yellow (IG) half + grey (TT) half */
  background: linear-gradient(
    to right,
    rgba(251, 191, 36, 0.22) 50%,
    rgba(148, 163, 184, 0.30) 50%
  );
}
.cal-block.multi-ig-tt.trial-manual:hover {
  background: linear-gradient(
    to right,
    rgba(251, 191, 36, 0.32) 50%,
    rgba(148, 163, 184, 0.44) 50%
  );
}
.cal-block.multi-ig-tt.trial-auto {
  /* Trial reel (auto) · IG+TT → green (IG) half + grey (TT) half */
  background: linear-gradient(
    to right,
    rgba(74, 222, 128, 0.20) 50%,
    rgba(148, 163, 184, 0.30) 50%
  );
}
.cal-block.multi-ig-tt.trial-auto:hover {
  background: linear-gradient(
    to right,
    rgba(74, 222, 128, 0.32) 50%,
    rgba(148, 163, 184, 0.44) 50%
  );
}
.cal-block.story {
  background: rgba(59, 130, 246, 0.20);
  border-color: #3b82f6;
  border-left-color: #3b82f6;
}
.cal-block.story:hover { background: rgba(59, 130, 246, 0.32); }
.cal-swatch.story { background: #3b82f6; }
/* Carousels — orange/amber, distinct from pink (reel) and blue (story).
   Operator request 2026-05-27: stories + carousels should NOT show up
   as pink "normal reel" blocks. Story already had its own color; this
   adds the same treatment for carousels. */
.cal-block.carousel {
  background: rgba(245, 158, 11, 0.20);
  border-color: #f59e0b;
  border-left-color: #f59e0b;
}
.cal-block.carousel:hover { background: rgba(245, 158, 11, 0.32); }
.cal-swatch.carousel { background: #f59e0b; }
.cal-block.published { opacity: 0.55; }
.cal-block.suspect {
  background: rgba(248, 113, 113, 0.22);
  border-color: var(--bad);
  border-left-color: var(--bad);
}
.cal-block-time { font-weight: 700; margin-right: 4px; }
.cal-block-id { color: var(--muted); margin-left: auto; }
.cal-empty {
  text-align: center;
  color: var(--muted);
  font-size: 11px;
  padding: 14px;
}
.phase7-col textarea {
  background: var(--panel-2); color: var(--text); border: 1px solid var(--line);
  border-radius: 8px; padding: 8px 10px; font: inherit; resize: vertical;
}
.phase7-col input[type="datetime-local"],
.phase7-col select,
.wan-pod-select {
  background: var(--panel-2); color: var(--text); border: 1px solid var(--line);
  border-radius: 8px; padding: 8px 10px; font: inherit;
  color-scheme: dark;
  width: 100%;
  cursor: pointer;
}
.wan-pod-select:hover { border-color: var(--accent); }
.wan-pod-select:focus { outline: none; border-color: var(--accent); }
/* Option styling — works on most modern browsers when color-scheme:dark
   is set on the select. Firefox honors these explicitly. */
.wan-pod-select option {
  background: var(--panel-2);
  color: var(--text);
  padding: 6px;
}
#phase7-video { width: 100%; max-height: 360px; border-radius: 8px; background: #000; }
#phase7-thumb-grid { grid-template-columns: repeat(5, 1fr); }
@media (max-width: 900px) {
  .phase7-row { grid-template-columns: 1fr; }
  #phase7-thumb-grid { grid-template-columns: repeat(3, 1fr); }
}
/* Step 6.5 trim controls */
#phase6_5-video { width: 100%; max-height: 360px; border-radius: 8px; background: #000; margin-top: 6px; }
.trim-scrubber { margin-top: 14px; user-select: none; }
.trim-track {
  position: relative; height: 22px;
  background: var(--panel-2); border-radius: 6px;
  cursor: pointer;
}
.trim-keep {
  position: absolute; top: 0; bottom: 0;
  background: linear-gradient(135deg, rgba(255,62,165,0.35), rgba(199,37,159,0.18));
  border: 1px solid var(--accent);
  pointer-events: none;
  border-radius: 6px;
}
.trim-handle {
  position: absolute; top: -4px; bottom: -4px;
  width: 14px; margin-left: -7px;
  background: var(--accent);
  border: 2px solid #fff;
  border-radius: 4px;
  cursor: ew-resize;
  box-shadow: 0 2px 6px rgba(0,0,0,0.5);
  z-index: 2;
}
.trim-handle:hover { background: var(--accent-2); }
.trim-handle::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 2px; height: 12px; background: #fff; opacity: 0.8;
  transform: translate(-50%, -50%);
}
.trim-times {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 8px; font-size: 11px; color: var(--muted);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
}
.trim-times strong { color: var(--text); font-weight: 600; }
.trim-times-mid { color: var(--accent); }

.thumb-controls {
  display: flex; align-items: end; gap: 10px; margin-top: 6px;
}
.thumb-controls .field.tight { gap: 4px; }
.thumb-controls .field.tight span { font-size: 11px; color: var(--muted); }
.thumb-controls input[type="number"] {
  background: var(--panel-2); color: var(--text); border: 1px solid var(--line);
  border-radius: 6px; padding: 6px 8px; font: inherit; width: 90px;
}
.thumb-controls .btn { padding: 7px 14px; font-size: 12px; align-self: end; }
.thumb-tile { position: relative; }
.thumb-time {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: linear-gradient(transparent, rgba(0,0,0,0.85));
  color: #fff; font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 10px; padding: 14px 6px 5px; text-align: center;
  pointer-events: none;
}
.thumb-delta { color: var(--muted); }

/* Step 1 source-video frame scrubber — single-handle position picker (NOT
   the two-handle trim-style scrubber from step 6.5). Drag the handle across
   the timeline; wherever it lands is the chosen source frame. */
.frame-scrubber {
  user-select: none;
  -webkit-user-select: none;
}
.frame-scrubber-track {
  position: relative;
  height: 28px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--line);
  cursor: pointer;
}
.frame-scrubber-handle {
  position: absolute;
  top: 50%;
  left: 0;
  transform: translate(-50%, -50%);
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent, #818cf8);
  border: 2px solid #fff;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
  cursor: grab;
  z-index: 2;
}
.frame-scrubber-handle:active { cursor: grabbing; }
.frame-scrubber-times {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-top: 6px;
  font-size: 12px;
  color: var(--muted);
}
.frame-scrubber-times strong { color: var(--text); font-weight: 600; }

/* Wan-animate pod queue sidebar — pinned to the right of step 5's card so
   the operator can see "project 0068 running 47% · 0069 next · 0070 in
   queue" no matter which project they're currently viewing. Hidden when
   the queue is empty. */
#phase5-card { position: relative; }
.wan-queue-sidebar {
  position: absolute;
  top: 12px;
  left: calc(100% + 12px);
  width: 220px;
  max-height: calc(100% - 24px);
  overflow-y: auto;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg-2);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.25);
  font-size: 11px;
  color: var(--text);
}
.wan-queue-head {
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
}
.wan-queue-row {
  padding: 6px 8px;
  margin-bottom: 4px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.03);
  border-left: 2px solid transparent;
}
.wan-queue-row.running { border-left-color: #22c55e; background: rgba(34, 197, 94, 0.07); }
.wan-queue-row.pending { border-left-color: #fbbf24; background: rgba(251, 191, 36, 0.05); }
.wan-queue-row.is-current {
  outline: 1px solid var(--accent, #818cf8);
  background: rgba(129, 140, 248, 0.12);
}
.wan-queue-label {
  font-weight: 600;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 6px;
}
.wan-queue-progress {
  margin-top: 4px;
  height: 3px;
  background: rgba(255, 255, 255, 0.08);
  border-radius: 2px;
  overflow: hidden;
}
.wan-queue-progress-fill {
  height: 100%;
  background: #22c55e;
  transition: width 0.4s ease;
}
.wan-queue-msg {
  font-size: 10px;
  color: var(--muted);
  margin-top: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.wan-queue-empty {
  font-size: 11px;
  color: var(--muted);
  padding: 6px 4px;
}
/* On narrow viewports — hide instead of overflowing/overlapping. */
@media (max-width: 1380px) {
  .wan-queue-sidebar { display: none; }
}

/* Per-tile cancel button — small ✕ in the top-right corner of pending NBP
   tiles. Lets the operator kill a single stuck generation (e.g. nano-banana-pro
   keeps failing the safety filter on attempt 7) without affecting siblings. */
.pending-cancel-btn {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 22px;
  height: 22px;
  padding: 0;
  border-radius: 11px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(0, 0, 0, 0.55);
  color: rgba(255, 255, 255, 0.85);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease;
}
.pending-cancel-btn:hover {
  background: #ef4444;
  color: #fff;
  border-color: #ef4444;
  transform: scale(1.08);
}
.pending-tile { position: relative; }
/* Cancelled tile — desaturated tombstone, no animation, no actions. */
.pending-tile[data-state="cancelled"] {
  opacity: 0.55;
  background: rgba(255, 255, 255, 0.03);
}
.pending-tile[data-state="cancelled"] .pending-fill {
  background: #6b7280;
  width: 100% !important;
}
.pending-tile[data-state="cancelled"] .pending-icon { opacity: 0.3; }

/* Pods dropdown — pulsing border on the status banner while a pod is in a
   transitional state (STARTING / LOADING MODELS). Makes the operator's eye
   catch the change between renders. */
.pod-status-pulse {
  animation: pod-status-pulse 1.4s ease-in-out infinite;
}
@keyframes pod-status-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}
@keyframes pod-toast-in {
  0% { transform: translate(-50%, -20px); opacity: 0; }
  100% { transform: translate(-50%, 0); opacity: 1; }
}

/* === scroll panel — left-side swipe iframe ===
   Fixed to the left edge so it lives in the empty space next to the centered
   pipeline UI without pushing or blocking it. Upvotes inside the iframe
   postMessage to the parent which feeds Phase 1. */
.scroll-panel {
  position: fixed;
  top: 56px; /* sits below the topbar */
  left: 0;
  bottom: 0;
  width: clamp(440px, 48vw, 720px);
  background: var(--bg-2);
  border-right: 1px solid rgba(255, 62, 165, 0.22);
  box-shadow: 4px 0 32px rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
  transform: translateX(0);
  transition: transform 0.22s ease;
  z-index: 50;
}
.scroll-panel.hidden {
  transform: translateX(-105%);
  pointer-events: none;
}
.scroll-panel-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid rgba(255, 62, 165, 0.18);
  background: rgba(20, 9, 30, 0.7);
}
.scroll-panel-title {
  font-weight: 600;
  font-size: 13px;
  color: var(--accent);
}
.scroll-panel-status {
  flex: 1 1 auto;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.55);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.scroll-panel-status.ok { color: #3dff8a; }
.scroll-panel-status.err { color: #ff4470; }
.scroll-panel-iframe {
  flex: 1 1 auto;
  width: 100%;
  border: 0;
  background: var(--bg);
}
.scroll-panel-error {
  position: absolute;
  inset: 56px 0 0 0;
  background: var(--bg);
  padding: 28px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  color: rgba(255, 255, 255, 0.7);
  font-size: 13px;
  text-align: center;
}
.scroll-panel-error.hidden { display: none; }
.scroll-panel-error code {
  display: block;
  background: rgba(0, 0, 0, 0.45);
  padding: 10px 12px;
  border-radius: 6px;
  font-size: 11px;
  word-break: break-all;
  color: #f5e9ff;
}
.scroll-panel-hint { font-size: 11px; color: rgba(255, 255, 255, 0.45); }
#tb-scroll.active {
  background: var(--accent);
  color: #1a0a1f;
  border-color: var(--accent);
}

/* === scroll analytics — right-side panel ===
   Mirror of .scroll-panel on the right. Toggles together with the left panel.
   Populated from postMessage events emitted by the embedded swipe iframe. */
.scroll-analytics {
  position: fixed;
  top: 56px;
  right: 0;
  bottom: 0;
  width: clamp(320px, 28vw, 420px);
  background: var(--bg-2);
  border-left: 1px solid rgba(255, 62, 165, 0.22);
  box-shadow: -4px 0 32px rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
  transform: translateX(0);
  transition: transform 0.22s ease;
  z-index: 50;
  overflow-y: auto;
  padding: 16px 18px;
}
.scroll-analytics.hidden {
  transform: translateX(105%);
  pointer-events: none;
}

/* === mobile: swipe goes full-screen, analytics panel gets out of the way ===
   Both panels have fixed min-widths (440px / 320px) that exceed a phone screen,
   so on mobile they stacked on top of each other — the analytics panel was
   literally covering the scraper. Phone = swipe full-screen (its own × closes
   it); the analytics mirror is desktop-only (the swipe UI already shows 🔥/📈
   per-vid stats inline). */
@media (max-width: 700px) {
  .scroll-panel {
    top: 0;
    width: 100vw;
    z-index: 120;        /* above the topbar, which can wrap taller on phones */
    border-right: 0;
  }
  .scroll-analytics { display: none !important; }
}
.scroll-analytics-section { margin-bottom: 18px; }
.scroll-analytics-section h3 {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  color: var(--accent);
  margin: 0 0 10px;
  font-weight: 600;
}
.scroll-analytics-meta .row {
  display: flex;
  justify-content: space-between;
  padding: 6px 0;
  border-bottom: 1px dashed rgba(255, 62, 165, 0.12);
  font-size: 13px;
  gap: 12px;
}
.scroll-analytics-meta .row .k { color: rgba(255,255,255,0.55); }
.scroll-analytics-meta .row .v {
  font-variant-numeric: tabular-nums;
  text-align: right;
  word-break: break-all;
}
.scroll-analytics-meta .row .v.big { color: #4ade80; font-weight: 600; }
.scroll-analytics-meta a { color: var(--accent); text-decoration: none; }
.scroll-analytics-meta a:hover { text-decoration: underline; }
.scroll-analytics-meta .desc {
  color: rgba(255,255,255,0.55);
  font-size: 12px;
  margin-top: 8px;
  line-height: 1.4;
  word-break: break-word;
}
.scroll-analytics-meta .tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 8px;
}
.scroll-analytics-meta .tag {
  background: rgba(255, 62, 165, 0.1);
  border: 1px solid rgba(255, 62, 165, 0.22);
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  color: var(--accent);
}
.scroll-analytics-weights .row {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  padding: 4px 0;
  color: rgba(255,255,255,0.55);
}
.scroll-analytics-weights .row .name { color: rgba(255,255,255,0.85); }
.scroll-analytics-weights .row .w {
  color: #4ade80;
  font-variant-numeric: tabular-nums;
}
.scroll-analytics-weights .sep { height: 8px; }
.scroll-analytics-empty {
  color: rgba(255,255,255,0.4);
  font-size: 12px;
  font-style: italic;
}



/* === Viewport-loaded images (lazyScan / lazyImgTag in app.js) ===
   The element carries a blurred LQIP as its background-image, so the slot is
   never an empty grey box — it paints a colour-accurate blur instantly with
   zero network cost, then the real image fades in on top once the
   IntersectionObserver decides it's near the viewport. Same perceived-speed
   trick Pinterest/Medium use. */
img.lazy-img {
  opacity: 0;
  transition: opacity 220ms ease;
}
img.lazy-img.lazy-loaded {
  opacity: 1;
}
/* Never animate for someone who asked not to. */
@media (prefers-reduced-motion: reduce) {
  img.lazy-img { transition: none; }
}

/* === Phone mode — main dashboard (2026-08-26) ============================
   Opt-in via the computer/phone toggle in the topbar (app.js sets
   body.phone-mode; default is ALWAYS computer, persisted per browser).
   Same philosophy as tinder's: a working single-column surface with
   finger-sized controls, desktop-only chrome hidden. */
body.phone-mode .topbar {
  flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
  padding: 8px 10px; gap: 8px; scrollbar-width: none;
}
body.phone-mode .topbar::-webkit-scrollbar { display: none; }
body.phone-mode .tb-btn, body.phone-mode .topbar .pill {
  flex: 0 0 auto; white-space: nowrap; padding: 8px 12px; font-size: 13px;
}
body.phone-mode .layout { padding: 10px 8px; max-width: 100%; }
body.phone-mode .card, body.phone-mode .side-card { padding: 12px 10px; border-radius: 12px; }
/* The fixed left-edge project sidebar overlaps everything at phone width —
   hide it; the projects ▾ dropdown covers switching and is touch-friendly. */
body.phone-mode .open-tabs-sidebar { display: none !important; }
body.phone-mode .layout button, body.phone-mode .layout select {
  min-height: 42px; font-size: 14px;
}
body.phone-mode .layout input[type="text"], body.phone-mode .layout textarea {
  font-size: 16px; /* 16px stops iOS Safari from zoom-jumping on focus */
}
body.phone-mode video { max-height: 60vh; width: 100%; object-fit: contain; }
body.phone-mode .actions { flex-wrap: wrap; }
body.phone-mode .grid { grid-template-columns: repeat(2, 1fr); }

/* Tile favorites — shortlist highlight, independent of the pink pick ring. */
.tile .fav-btn {
  position: absolute;
  top: 6px;
  /* third slot in the corner row: fullscreen@6, checkbox@38 (moved there for
     the fullscreen btn), star@70 — it was at 38 and sat ON the checkbox. */
  right: 70px;
  width: 26px; height: 26px;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.55);
  border: none;
  color: #fff;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.75;
  z-index: 3;
  display: flex; align-items: center; justify-content: center;
}
.tile .fav-btn:hover { opacity: 1; background: rgba(0, 0, 0, 0.75); transform: scale(1.06); }
.tile.faved { outline: 3px solid #f5c518; outline-offset: -1px; border-radius: 8px; }
.tile.faved .fav-btn { opacity: 1; }
/* Faved AND picked: both rings visible — gold outline outside the pink pick. */
.tile.faved.picked { outline-offset: 2px; }

/* Topbar resize grab bar — lives on the bar's bottom edge like a window
   frame. Invisible until hover so it adds no visual noise. */
#topbar-resize-handle {
  position: absolute;
  left: 0; right: 0; bottom: -3px;
  height: 7px;
  cursor: ns-resize;
  z-index: 12;
}
#topbar-resize-handle:hover,
#topbar-resize-handle:active {
  background: linear-gradient(to bottom, transparent 40%, var(--accent) 45%, var(--accent) 55%, transparent 60%);
  opacity: 0.8;
}
.topbar { position: sticky; } /* (already sticky) ensure abs child anchors */

/* Star inside the fullscreen viewer — sits left of the close button. */
.lightbox-fav {
  position: fixed;
  top: 14px; right: 64px;
  width: 40px; height: 40px;
  border-radius: 10px;
  background: rgba(0,0,0,0.55);
  border: 1px solid rgba(255,255,255,0.25);
  color: #fff; font-size: 20px; line-height: 1;
  cursor: pointer; z-index: 10001;
  display: flex; align-items: center; justify-content: center;
}
.lightbox-fav:hover { background: rgba(0,0,0,0.8); transform: scale(1.06); }

/* Step-2 favorites shelf — the starred shortlist in one row above the grid. */
#nbp-favs-shelf {
  display: block;
  padding: 10px; margin-bottom: 10px;
  background: rgba(245, 197, 24, 0.06);
  border: 1px solid rgba(245, 197, 24, 0.35);
  border-radius: 10px;
}
/* Same 4-up sizing as the generations grid — favorites render full-size. */
#nbp-favs-shelf .shelf-row {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
}
@media (max-width: 700px) { #nbp-favs-shelf .shelf-row { grid-template-columns: repeat(2, 1fr); } }
#nbp-favs-shelf.hidden { display: none; }
#nbp-favs-shelf .shelf-label {
  font-size: 10px; font-weight: 700; margin-bottom: 8px;
  text-transform: uppercase; letter-spacing: 0.5px; color: #f5c518;
}
#nbp-favs-shelf .shelf-tile {
  position: relative;
  border-radius: 8px; overflow: hidden;
  border: 2px solid rgba(245, 197, 24, 0.55); cursor: pointer;
}
#nbp-favs-shelf .shelf-tile img { width: 100%; height: auto; display: block; }
#nbp-favs-shelf .shelf-tile .idx {
  position: absolute; top: 3px; left: 3px;
  background: rgba(0,0,0,0.65); color: #f5c518;
  font-size: 10px; font-weight: 700; padding: 1px 5px; border-radius: 4px;
}
#nbp-favs-shelf .shelf-tile .unfav {
  position: absolute; top: 3px; right: 3px;
  width: 18px; height: 18px; border: 0; border-radius: 50%;
  background: rgba(0,0,0,0.65); color: #fff; font-size: 11px; line-height: 1;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
}
#nbp-favs-shelf .shelf-tile.picked { border-color: var(--accent); }

/* Fork button on shelf favorites — bottom strip of the tile. */
#nbp-favs-shelf .shelf-tile .fork-btn {
  position: absolute; left: 0; right: 0; bottom: 0;
  border: 0; padding: 6px 4px;
  background: rgba(0,0,0,0.7); color: #f5c518;
  font-size: 11px; font-weight: 600; cursor: pointer;
}
#nbp-favs-shelf .shelf-tile .fork-btn:hover { background: rgba(0,0,0,0.9); }
