/* SAM design system — shared tokens + base components for the web UI.
 *
 * Lifted from the original lineup-page dark theme so every tool inherits
 * the same look. Pages link this file and may add tool-specific styles
 * after it. Type sizes are relative; never hard-coded in component CSS.
 *
 * Dark UI here is intentional — interactive screens. Generated HTML
 * reports (sheets/list/inspect/locators) use a separate light theme in
 * sam/render/html.py because they're forward-able / printable artifacts.
 */

:root {
  /* Tells the browser this UI is built for a dark color scheme. Reinforces
     the per-page <meta name="color-scheme" content="dark"> so form controls,
     scrollbars, and the macOS Safari fullscreen title bar honor the dark
     intent — meta-tag-only is ignored by Safari in fullscreen, leaving the
     toolbar/title chrome light when system appearance is Light.

     When the light theme ships (project_light_theme_reference), swap this
     to `light dark` and bind --sam-* tokens to a light palette under a
     theme-class on body. The browser then picks per-context. */
  color-scheme: dark;

  /* Colors — dark interactive theme */
  --sam-bg:           #1a1a1a;
  --sam-surface:      #232323;
  --sam-surface-2:    #2a2a2a;
  --sam-border:       #3a3a3a;
  --sam-border-soft:  #292929;

  --sam-text:         #e8e8e8;
  --sam-text-2:       #b0b0b0;
  --sam-text-dim:     #888;

  --sam-amber:        #F5A623;  /* branding primary (matches DSBX_AMBER) */
  --sam-amber-2:      #fbbf24;  /* lighter accent (existing tagline color) */
  --sam-amber-on:     #1a1a1a;  /* foreground on amber surfaces */

  --sam-banner-from:  #1a1a1a;
  --sam-banner-to:    #383838;

  --sam-ok:           #2eb872;
  --sam-warn:         #d4a017;
  --sam-bad:          #c44;
  --sam-info:         #5a9eb8;  /* cyan — informational state (banners, hints) */
  --sam-pending:      #777;

  /* Status pill / banner backgrounds (subtle tints).
   * One canonical shade per state — pills use these directly; banners
   * (full-width) use the same shade for visual consistency across tools.
   */
  --sam-ok-bg:        #1d4a30;
  --sam-warn-bg:      #3d3215;
  --sam-bad-bg:       #4a1d1d;
  --sam-info-bg:      #1e3a5f;
  --sam-pending-bg:   #2a2a2a;

  /* Status pill text colors — softer than --sam-ok / --sam-bad,
   * tuned for legibility on the matching -bg tint. Used by .sam-pill
   * variants and any "success indicator" / "warning text" in pages.
   * Keep these aligned with sam.css's pill set; pages should NOT
   * use ad-hoc greens/reds.
   */
  --sam-ok-text:      #5fd28d;
  --sam-bad-text:     #d47171;

  /* Fonts */
  --sam-font-ui:   -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --sam-font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* Type scale (relative; --sam-fs-base is the body size) */
  --sam-fs-xs:    9px;     /* uppercase section labels */
  --sam-fs-sm:    11px;    /* small caption / metadata */
  --sam-fs-base:  13px;    /* body */
  --sam-fs-md:    15px;    /* tagline */
  --sam-fs-lg:    18px;    /* section heading (h3) */
  --sam-fs-xl:    32px;    /* page heading (h1) */

  /* Spacing */
  --sam-radius:   6px;
  --sam-radius-sm: 3px;
}

/* ------------------------------------------------------------------ */
/* Canonical "light report" theme — the look used by sam lineup        */
/* rendered HTML, sam review playlist, and any embedded list/report    */
/* that shows DATA inside a SAM tool page.                             */
/*                                                                     */
/* SAM uses dark page chrome (banner, controls, panels) with light     */
/* report content (lists, tables, generated HTML) embedded inside.    */
/* The contrast separates "tool surface" from "deliverable content."   */
/*                                                                     */
/* The .sam-report scope rebinds color tokens to canonical light       */
/* values within its subtree only — page chrome above stays dark.      */
/* This is the same palette as the playlist export (the reference).    */
/* Used by .list-module (also auto-applied below) and any other        */
/* "report-like" surface inside a dark SAM page.                       */
/*                                                                     */
/* Future: a global light-theme toggle for the page chrome (planned    */
/* per task #27) re-uses this same variable contract — same layout,    */
/* different binding scope.                                            */
/* ------------------------------------------------------------------ */

.sam-report,
.list-module {
  /* Page-style surfaces */
  --sam-bg:           #e8eaed;
  --sam-surface:      #ffffff;
  --sam-surface-2:    #dadde2;
  --sam-border:       #cdd0d8;
  --sam-border-soft:  #e5e8ed;

  /* Text */
  --sam-text:         #1a1d24;
  --sam-text-2:       #4a5060;
  --sam-text-dim:     #6a7080;
  --sam-amber-on:     #ffffff;  /* white reads against amber on light */

  /* State colors — saturated darker variants legible on light bg */
  --sam-ok:           #1a6b2a;
  --sam-warn:         #7a3d00;
  --sam-bad:          #8a1a1a;
  --sam-info:         #0a4d6e;
  --sam-pending:      #6a7080;

  /* Background tints — pastel, paper-friendly (matches playlist) */
  --sam-ok-bg:        #d4edda;
  --sam-warn-bg:      #fde8cc;
  --sam-bad-bg:       #fde8e8;
  --sam-info-bg:      #cce5f0;
  --sam-pending-bg:   #e8eaed;

  /* Pill text — saturated state colors read directly on light tint */
  --sam-ok-text:      #1a6b2a;
  --sam-bad-text:     #8a1a1a;

  /* The wrapper itself uses the WHITE surface so title + table form
     one continuous light card on the dark page (matching the
     playlist canonical — no gray "frame" band around the content).
     Per-list color tones can still override --module-bg for the
     title bar specifically (TRIMS uses gray, etc). */
  background: var(--sam-surface);
  color: var(--sam-text);
  border: 1px solid var(--sam-border);
  border-radius: var(--sam-radius);
  overflow: hidden;
}

/* ------------------------------------------------------------------ */
/* Reset + base                                                        */
/* ------------------------------------------------------------------ */

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  background: var(--sam-bg);
  color: var(--sam-text);
  font: var(--sam-fs-base)/1.45 var(--sam-font-ui);
}

/* ------------------------------------------------------------------ */
/* Banner — dark gradient, centered logo + amber tagline + tool name.  */
/* Mirrors the pattern in sam/web/static/lineup.html (the logo IS the  */
/* wordmark; no separate "SAM" text). Tools add their own tool-name    */
/* sub-line below the product tagline.                                 */
/* ------------------------------------------------------------------ */

.sam-banner {
  background: linear-gradient(to bottom, var(--sam-banner-from) 0%, var(--sam-banner-to) 100%);
  color: var(--sam-text);
  padding: 14px 0 10px 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  border-bottom: 1px solid var(--sam-border);
}
.sam-banner-logo {
  display: flex; align-items: flex-start; justify-content: center;
}
.sam-banner-logo img {
  max-height: 90px; width: auto;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
}
/* When the logo is wrapped in an <a> (any tool page returning to the
 * /tools landing), give it a subtle hover hint. */
.sam-banner-logo a {
  display: inline-flex;
  cursor: pointer;
  transition: filter 0.12s, opacity 0.12s;
}
.sam-banner-logo a:hover { opacity: 0.85; }
.sam-banner-logo a:hover img { filter: drop-shadow(0 2px 6px rgba(245, 166, 35, 0.4)); }
.sam-tagline {
  color: var(--sam-amber-2);
  font-size: var(--sam-fs-base);
  font-weight: 500;
  line-height: 1;
  /* Blank line under the tagline so SANDBOX ASSET MANAGEMENT and the
     tool name beneath it don't crowd each other in the banner. */
  margin: 0 0 0.6em;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.sam-tool-name {
  color: var(--sam-text-2);
  font-size: var(--sam-fs-sm);
  font-weight: 500;
  line-height: 1;
  margin: 4px 0 0 0;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* ------------------------------------------------------------------ */
/* Common bits                                                         */
/* ------------------------------------------------------------------ */

.sam-mono { font-family: var(--sam-font-mono); }

/* "DETAILS", "STATUS", "NOTES" small caps section labels */
.sam-section-label {
  font-size: var(--sam-fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--sam-text-dim);
  font-weight: 600;
}

.sam-button {
  background: var(--sam-surface-2);
  color: var(--sam-text);
  border: 1px solid var(--sam-border);
  padding: 6px 12px;
  font-size: var(--sam-fs-sm);
  font-family: inherit;
  cursor: pointer;
  border-radius: var(--sam-radius-sm);
}
.sam-button:hover { background: #333; }
.sam-button.primary {
  background: var(--sam-amber);
  border-color: var(--sam-amber);
  color: var(--sam-amber-on);
  font-weight: 600;
}
.sam-button.primary:hover { background: var(--sam-amber-2); }
.sam-button:disabled { opacity: 0.4; cursor: not-allowed; }

.sam-input {
  background: var(--sam-surface-2);
  color: var(--sam-text);
  border: 1px solid var(--sam-border);
  padding: 5px 10px;
  font-size: var(--sam-fs-sm);
  border-radius: var(--sam-radius-sm);
  font-family: var(--sam-font-mono);
}

/* For path / value fields: use type="search" to get the native × clear
 * button, but suppress the magnifying-glass affordance and Safari's
 * default search-field chrome so the input still reads as a path field. */
.sam-input[type="search"] {
  -webkit-appearance: textfield;
  appearance: textfield;
}
.sam-input[type="search"]::-webkit-search-decoration,
.sam-input[type="search"]::-webkit-search-results-button,
.sam-input[type="search"]::-webkit-search-results-decoration {
  display: none;
}

.sam-pill {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 9px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.sam-pill.ok        { background: var(--sam-ok-bg);      color: var(--sam-ok-text); }
.sam-pill.warn      { background: var(--sam-warn-bg);    color: var(--sam-amber-2); }
.sam-pill.pending   { background: var(--sam-pending-bg); color: var(--sam-text-dim); }
.sam-pill.bad       { background: var(--sam-bad-bg);     color: var(--sam-bad-text); }

/* Submit / process button — full-width gray "do the thing" button.
   Used by /lineup PROCESS and /vfx-idgen PROCESS. Distinct shape
   from the regular .sam-button (chunkier, full-width, gray accent). */
.submit-btn {
  width: 100%;
  padding: 10px;
  background: var(--sam-text-dim);     /* #888 — neutral gray */
  color: #fff;
  border: none;
  border-radius: 4px;
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  transition: background 0.2s;
}
.submit-btn:hover:not(:disabled) { background: var(--sam-text-2); }
.submit-btn:disabled {
  background: var(--sam-border);
  cursor: not-allowed;
  opacity: 0.6;
}

/* ------------------------------------------------------------------ */
/* SAM footer — fixed-bottom zone present on every page. Holds the    */
/* build-stamp widget today; could carry other persistent chrome      */
/* (deploy info, status) later. Tools that need content clear of it   */
/* pad the same way (28px is enough headroom).                        */
/* ------------------------------------------------------------------ */
.sam-footer {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  background: var(--sam-bg);
  border-top: 1px solid var(--sam-border);
  padding: 3px 20px;
  height: 28px;
  text-align: center;
  z-index: 1000;
}

/* SAM build stamp — widget displaying the running deployment's build
 * time. Lives inside .sam-footer today but can drop in anywhere; the
 * font/colour rules apply wherever the widget is placed.
 *
 * Type voice matches the banner (.sam-tagline / .sam-tool-name): UI
 * sans, uppercase, tracked. Build info is data, but it's frame chrome
 * — keeping it in the banner's voice keeps the page frame consistent.
 * Mono is reserved for shot data (timecodes, filenames, IDs).
 *
 * Wire JS via wireBuildFooter(el) from /static/sam-utils.js — fetches
 * /api/health once at load and writes the result into the element. */
.sam-build {
  font-family: var(--sam-font-ui);
  font-size: var(--sam-fs-sm);
  font-weight: 500;
  color: var(--sam-text-2);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 22px;
}

/* ------------------------------------------------------------------ */
/* Upload / drop-zone — dashed-amber surface that swallows clicks and */
/* drag-drop into a hidden <input type="file">. Used by /vfx-idgen,   */
/* /lineup, and any future SAM tool that takes a file. Wire JS via    */
/* `wireDropZone(area, input, onFile)` from /static/sam-utils.js.     */
/*                                                                    */
/* The .secondary variant is smaller / dimmer for "optional" zones    */
/* (e.g. the QT reference uploader on /vfx-idgen).                    */
/* ------------------------------------------------------------------ */
.upload-area {
  border: 2px dashed var(--sam-amber);
  border-radius: var(--sam-radius);
  padding: 26px 18px;
  text-align: center;
  cursor: pointer;
  background: var(--sam-surface-2);
  transition: border-color 0.15s, background 0.15s;
}
.upload-area:hover,
.upload-area.dragover {
  border-color: var(--sam-amber-2);
  background: var(--sam-bg);
}
.upload-area strong {
  display: block;
  color: var(--sam-amber-2);
  font-size: var(--sam-fs-base);
  margin-bottom: 6px;
}
.upload-area p {
  color: var(--sam-text-dim);
  font-size: var(--sam-fs-sm);
  margin: 0;
}
.upload-area input[type=file] { display: none; }
.upload-area .filename {
  color: var(--sam-ok-text);
  font-size: var(--sam-fs-sm);
  margin-top: 10px;
  font-family: var(--sam-font-mono);
  word-break: break-all;
}

.upload-area.secondary {
  padding: 14px;
  border-color: var(--sam-border);
}
.upload-area.secondary strong { color: var(--sam-text-2); }
.upload-area.secondary p { font-size: var(--sam-fs-xs); }
.upload-area.secondary:hover { border-color: var(--sam-amber-2); }

/* ------------------------------------------------------------------ */
/* Filter widgets — color buttons & track toggles.                    */
/* Used by /lineup, /changes, /vfx-idgen and any future tool that     */
/* runs the lineup pipeline. Single source of truth so widget look    */
/* never drifts across tools (per consistency-first discipline).      */
/* ------------------------------------------------------------------ */

/* Locator color picker — multi-select; OR-logic on apply. */
.color-buttons {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
}
.color-btn {
  width: 24px;
  height: 24px;
  border: 2px solid var(--sam-amber-2);
  border-radius: var(--sam-radius-sm);
  cursor: pointer;
  transition: all 0.2s;
  flex-shrink: 0;
  position: relative;
  opacity: 0.5;
}
.color-btn:hover:not(:disabled) {
  border-color: var(--sam-amber-2);
  box-shadow: 0 0 8px rgba(251, 191, 36, 0.3);
  transform: scale(1.1);
  opacity: 1;
}
.color-btn.active {
  border-color: var(--sam-text-dim);
  box-shadow: 0 0 0 2px var(--sam-text-dim);
  opacity: 1;
}
.color-btn:disabled {
  opacity: 0.3;
  cursor: not-allowed;
  border-color: var(--sam-border);
}
.color-btn-label {
  font-size: 10px;
  font-weight: 600;
  position: absolute;
  bottom: 2px;
  left: 50%;
  transform: translateX(-50%);
  color: rgba(255, 255, 255, 0.7);
  pointer-events: none;
  display: none;
}
.color-btn:hover .color-btn-label:not(:disabled) { display: block; }

/* Track toggles — OMIT / MASTER track selectors. */
.track-toggles {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: flex-start;
}
.track-toggle {
  padding: 6px 12px;
  background: var(--sam-surface-2);
  color: var(--sam-text);
  border: 2px solid var(--sam-border);
  border-radius: var(--sam-radius-sm);
  cursor: pointer;
  transition: all 0.2s;
  font-size: 12px;
  font-weight: 500;
}
.track-toggle:hover {
  border-color: var(--sam-amber-2);
  background: var(--sam-border);
}
.track-toggle.active {
  background: var(--sam-text-dim);
  border-color: var(--sam-amber-2);
  color: #fff;
}

/* ------------------------------------------------------------------ */
/* List module — repeatable, parameterized list-view component.       */
/* Every list (ACTIVE, TRIMS, future bins, /vfx-idgen, etc.) renders  */
/* as one of these. View styling is window dressing; the data shape   */
/* is invariant. Per-list color comes through CSS custom properties   */
/* on the wrapping element:                                           */
/*   --module-bg, --module-fg, --module-border                        */
/* The .alt-on-light modifier (set by JS when bg luminance > ~aaa)    */
/* tints sort-action buttons for legibility on light module bgs.      */
/* ------------------------------------------------------------------ */

.list-module {
  /* Default banner is gray (--sam-surface-2 = #dadde2 in the light
     island), giving the title row a subtle separation from the white
     table area. Per-list bins (e.g. TRIMS) still set --module-bg
     directly to override (heavier gray, custom tones, etc.). */
  --module-bg:     var(--sam-surface-2);
  --module-fg:     var(--sam-text-2);
  --module-border: var(--sam-border);
  margin-bottom: 14px;
}
.list-module:last-child { margin-bottom: 0; }

.list-module-title {
  padding: 8px 12px;
  background: var(--module-bg);
  color: var(--module-fg);
  border-top: 2px solid var(--module-border);
  border-bottom: 2px solid var(--module-border);
  font-family: var(--sam-font-ui);
  font-size: var(--sam-fs-base);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  display: flex;
  align-items: baseline;
  gap: 12px;
}
.list-module-title .count {
  color: var(--module-fg);
  opacity: 0.85;
  font-family: var(--sam-font-mono);
  font-size: var(--sam-fs-sm);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  margin-right: auto;
}
.list-module-title .sort-actions {
  display: flex;
  gap: 4px;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
}
.list-module.alt-on-light .sort-actions .sam-button {
  background: rgba(0, 0, 0, 0.12);
  border-color: rgba(0, 0, 0, 0.3);
  color: #000;
}
.list-module.alt-on-light .sort-actions .sam-button:hover {
  background: rgba(0, 0, 0, 0.22);
}

/* Canonical table styling — matches sam review playlist (the reference
 * light-report look): amber header bar with white text, white rows
 * with subtle borders. Same look across every list-module instance —
 * lineup output, playlist, list-editor, vfx-idgen, future tools. */
table.list-module-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--sam-font-mono);
  font-size: var(--sam-fs-sm);
  table-layout: fixed;
  background: var(--sam-surface);
}
table.list-module-table th {
  background: var(--sam-amber);
  text-align: left;
  padding: 11px 12px;
  font-weight: 600;
  color: var(--sam-amber-on);
  border-bottom: 1px solid var(--sam-border);
  font-size: var(--sam-fs-sm);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-family: var(--sam-font-ui);
  white-space: nowrap;
}
table.list-module-table th.num { text-align: right; }
table.list-module-table td {
  padding: 8px 12px;
  border-bottom: 1px solid var(--sam-border);
  vertical-align: top;
  background: var(--sam-surface);
  color: var(--sam-text-2);
}
table.list-module-table td.num { text-align: right; }
table.list-module-table td.namecell,
table.list-module-table td.notescell {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
table.list-module-table td.namecell { color: var(--sam-text); font-weight: 600; }
table.list-module-table tr:last-child td { border-bottom: none; }
table.list-module-table tr:hover td { background: var(--sam-surface-2); }
table.list-module-table tr { cursor: grab; }
/* tr.selected — single-select / "primary" highlight (e.g. the row
   whose detail is showing in the stage-right widget).
   tr.bulk-selected — multi-select highlight; can coexist with .selected
   (the primary row inside a bulk selection has BOTH classes). Same
   visual today; kept as separate classes so future themes can
   distinguish them. */
table.list-module-table tr.selected td,
table.list-module-table tr.bulk-selected td {
  background: var(--sam-warn-bg);
  color: var(--sam-text);  /* explicit so the row stays readable
                              regardless of theme cascade quirks */
}
table.list-module-table tr.selected td.namecell,
table.list-module-table tr.bulk-selected td.namecell { color: var(--sam-text); }
table.list-module-table tr.dragging { opacity: 0.35; }
table.list-module-table tr.drop-target td { box-shadow: inset 0 2px 0 var(--sam-amber); }

/* Empty-list placeholder row — keeps the tbody drop-targetable when no
   items have been added yet (an empty tbody collapses to zero height,
   which kills the first drop). Hint text dims; cursor stays default
   since this row isn't draggable. */
table.list-module-table tr.list-module-empty-row { cursor: default; }
table.list-module-table tr.list-module-empty-row td {
  /* Match regular row geometry — same padding/font as data rows so the
     empty state reads as a peer "this is where rows would go", not as
     a separate banner. Italic + dim tint signals "not real content". */
  text-align: center;
  color: var(--sam-text-dim);
  font-style: italic;
  padding: 8px 12px;
  background: var(--sam-surface);
}
table.list-module-table tr.list-module-empty-row:hover td { background: var(--sam-surface); }
table.list-module-table tr.list-module-empty-row.drop-target td {
  box-shadow: inset 0 0 0 2px var(--sam-amber);
  color: var(--sam-text);
}

/* Whole-section drop highlight — applied when a drag is over the
   section's chrome (title bar / column header / padding) rather than
   over a specific row. Communicates "drop here = append to list". */
.list-module.drop-target-section {
  outline: 2px solid var(--sam-amber);
  outline-offset: -2px;
}

/* Column widths.
 *   Numeric columns hold 6-7 digit frame numbers — narrow + fixed.
 *   Name and notes are flexible by default — share the remaining
 *   width so VFX names (often 30-50+ chars) aren't clipped.
 *   A list-module config can override col-name to a fixed pixel width
 *   if a particular view needs predictable layout. */
.col-idx     { width: 44px; }
.col-name    { /* flexible — distributes with col-notes */ }
.col-frame   { width: 80px; }
.col-frames  { width: 70px; }
.col-notes   { /* flexible — distributes with col-name */ }
.col-status  { width: 84px; }

.notescell {
  color: var(--sam-text-2);
  font-style: italic;
  font-family: var(--sam-font-ui);
}

/* Pull / Grab action button overlays the row's # cell on hover.
 * Default state: # number visible, button hidden.
 * Hover state:   button overlays the #, button is the click target. */
table.list-module-table td.idx-cell {
  position: relative;
  cursor: default;
}
.idx-num { display: inline-block; transition: opacity 0.1s; }
.row-action {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 12px;
  font-family: var(--sam-font-ui);
  font-size: 14px;
  font-weight: 600;
  color: var(--sam-text-dim);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.1s, color 0.1s;
  user-select: none;
}
table.list-module-table tr:hover .row-action { opacity: 1; }
table.list-module-table tr:hover .idx-num    { opacity: 0; }
.row-action:hover { color: var(--sam-amber); }

/* ------------------------------------------------------------------ */
/* Print stylesheet — Cmd+P should yield just the list, not the       */
/* whole interactive UI. Applies to every SAM page that loads sam.css */
/* (vfx-idgen, list-editor, lineup) since the chrome class names are  */
/* shared.                                                             */
/*                                                                     */
/* Strategy:                                                            */
/*   1. Hide page chrome (banner, controls, action bars, detail pane,  */
/*      build footer) — none of it is meaningful on paper.              */
/*   2. Flatten the side-by-side grid (.main / .split) into a single   */
/*      column so the list fills the page width.                        */
/*   3. Force a white-paper background; the .list-module subtree       */
/*      already binds light-scope tokens so the inside is print-ready. */
/*   4. Avoid splitting a row across pages (page-break-inside: avoid). */
/*                                                                     */
/* If a page needs to keep a specific element visible on print (e.g. a */
/* future "print header" with project name + date), it can opt in by   */
/* tagging the element with .sam-print-only — defined as display:none  */
/* in the screen scope and unset here.                                  */
/* ------------------------------------------------------------------ */

.sam-print-only { display: none; }

@media print {
  @page { margin: 0.4in; }

  body {
    background: #fff !important;
    color: #000;
  }

  /* Hide all interactive chrome. */
  .sam-banner,
  .sam-footer,
  .assign-bar,
  .export-bar,
  .bulk-bar,
  .detail-pane,
  .results-stats,
  .results-empty,
  .pane-empty,
  .upload-area,
  .status-message,
  .sam-section-label,
  /* Whole controls panel — vfx-idgen has it as the FIRST .panel inside */
  /* .main; list-editor uses a similar shape. The list panel is the     */
  /* SECOND child, so we keep that one and hide the first.              */
  .main > .panel:first-child,
  .split > aside,
  .split > .controls,
  /* Common form controls inside any remaining panels become noise.    */
  input[type="file"],
  button.sam-button,
  .sam-input {
    display: none !important;
  }

  /* Flatten the grid so the surviving list panel takes the full width. */
  .main,
  .split,
  .results-split {
    display: block !important;
    grid-template-columns: none !important;
    gap: 0 !important;
  }

  /* The container card loses its dark surface — it's printing on white. */
  .panel,
  #resultsPanel {
    background: transparent !important;
    border: none !important;
    box-shadow: none !important;
    padding: 0 !important;
    margin: 0 !important;
    overflow: visible !important;
    height: auto !important;
    min-height: 0 !important;
  }

  /* Lists themselves: keep light theme + ensure they print whole. */
  .list-module {
    border: 1px solid #999 !important;
    box-shadow: none !important;
    overflow: visible !important;
    height: auto !important;
    max-height: none !important;
    page-break-inside: auto;
  }

  .list-module .lm-row,
  .list-module tbody tr {
    page-break-inside: avoid;
    break-inside: avoid;
  }

  /* Sticky / fixed bits stop being sticky on paper. */
  .list-module thead,
  .list-module .lm-header,
  .list-module .sticky {
    position: static !important;
  }

  /* Hover-only affordances (drag handles, row actions) stay invisible. */
  .row-action,
  .lm-drag-handle {
    display: none !important;
  }

  /* Pills + state colors keep their light-scope tones; ensure backgrounds
     actually print. Most browsers omit backgrounds in print by default;
     -webkit-print-color-adjust forces the color tints to render. */
  .sam-pill,
  .list-module {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  /* Print-header opt-in (any element a page tags with .sam-print-only). */
  .sam-print-only { display: block !important; }
}

/* Print-only page header — pages that opt in via <header class="sam-print-only sam-print-header"> get a
   minimal title block at the top of the printed output: tool name,
   source filename, and a wall-clock stamp. JS fills .ph-source / .ph-stamp at
   runtime; nothing renders on screen since the parent is .sam-print-only. */
@media print {
  .sam-print-header {
    margin: 0 0 16px;
    padding: 0 0 8px;
    border-bottom: 1px solid #999;
    font-family: var(--sam-font-ui);
    color: #000;
    line-height: 1.4;
  }
  .sam-print-header .ph-title  { font-size: 11px; letter-spacing: 1px; color: #555; }
  .sam-print-header .ph-source { font-size: 14px; font-weight: 600; }
  .sam-print-header .ph-stamp  { font-size: 11px; color: #555; }
}

/* ============================================================ */
/*  LIST-WORKBENCH layout system (.sam-*)                        */
/*                                                              */
/*  Shared layout shell for every list-centric SAM tool:        */
/*    list-editor, vfx-idgen (today)                            */
/*    verify, ingest, reviews (planned)                         */
/*                                                              */
/*  Memorable section names — full reference in memory file     */
/*  project_list_workbench_taxonomy.md. Quick reminder:         */
/*                                                              */
/*    BANNER   — tool identity strip                            */
/*    WIRE     — live job feed (station mode)                   */
/*    MASTHEAD — source ID + export verbs                       */
/*    DECK     — primary tool actions                           */
/*    FILTERS  — view filters (no data mutation)                */
/*    TICKER   — transient status messages                      */
/*    STAGE    — working area: three resizable panels           */
/*               (stage-left + stage-center + stage-right)      */
/*               Panels are spatial — they hold widgets.        */
/*    FOOTER   — build info, persistent                         */
/*                                                              */
/*  Widgets — NOT layout zones — are what go IN the panels:     */
/*  list-editor (hosts list-modules), bench (bulk-action bar),  */
/*  detail/media-view (selection-focused inspector + preview),  */
/*  pipeline-inputs (controls). Each page names its widgets for */
/*  what they do; convention is the `*-widget` ID suffix. The   */
/*  `loupe` name is reserved for a future interactive magnifier */
/*  tool — see `project_loupe_reserved`; today's still/video    */
/*  displays are passive media views, not loupes. Widgets use   */
/*  the .sam-widget shell (header + body) plus a couple of       */
/*  variant classes (.sam-bench has its own visual treatment).   */
/*                                                              */
/*  Design rules:                                                */
/*                                                              */
/*  1. Every zone is a token-bound block. No hard-coded colors  */
/*     anywhere in this section — light theme + future themes   */
/*     rebind tokens, layout stays constant.                    */
/*                                                              */
/*  2. STAGE is a CSS grid. Default 2 cols (center + right);   */
/*     adds a 3rd (left) when a `.sam-stage-left` element is in  */
/*     the DOM. Pages set --sam-stage-{left,center,right}-w to   */
/*     retune column proportions for the tool's needs.          */
/*                                                              */
/*  3. STAGE has gutters between panels for live drag-resize,   */
/*     and a vertical grip on the top + bottom for height       */
/*     resize. JS handles the math; CSS provides the visuals.   */
/*                                                              */
/*  4. WIRE through TICKER live in `.sam-sticky-cluster` so they */
/*     stay visible when the operator scrolls past BANNER.      */
/*     STAGE scrolls under the cluster. FOOTER is fixed at the  */
/*     viewport bottom.                                          */
/*                                                              */
/*  5. Each zone has predictable internal padding so a page can */
/*     drop content into <div class="sam-deck">…</div> without   */
/*     re-deriving spacing rules.                               */
/*                                                              */
/*  No page styling lives in this file — only the layout shell  */
/*  and widget-shell. Per-tool look-and-feel (which buttons in  */
/*  DECK, what's inside each widget) stays on the page or in a  */
/*  tool-specific stylesheet.                                    */
/* ============================================================ */

/* ---------- Page shell ---------- */

.sam-workbench {
  display: flex;
  flex-direction: column;
  /* Page grows with content; sticky cluster slides against page-level
     scroll. NO padding-bottom — the fixed FOOTER is meant to OVERLAY
     the bottom of STAGE rather than sit in a reserved gap below it.
     STAGE content slides BENEATH the footer as the operator scrolls;
     panels carry their own bottom padding so the final row remains
     readable when scrolled to the end. */
  min-height: 100vh;
  /* The vertical-stack rhythm. Every zone defaults to this gap from
     its sibling; pages or zones can override per zone if they want
     a tighter or looser separation. */
  --sam-zone-gap: 0;
  /* STAGE column proportions. Pages set these on .sam-workbench (or on
     .sam-stage directly) to retune for tools that need a fatter
     stage-right (reviews, side-by-side compare). */
  --sam-stage-center-w: 2fr;
  --sam-stage-right-w:  1fr;
  /* STAGE column gap — the resize handle sits in this gutter. */
  --sam-stage-gap: 16px;
  /* Default zone padding. Tools can override per zone. */
  --sam-zone-pad-x: 24px;
  --sam-zone-pad-y: 12px;
  background: var(--sam-bg);
  color: var(--sam-text);
  gap: var(--sam-zone-gap);
}

/* ---------- BANNER (tool identity) ---------- */

/* The .sam-banner is a vertical flex (logo / tagline / tool-name
   stacked center). Identity badge + mode tag are corner-anchored
   so they don't push the centered text around or overflow narrow
   viewports. Both via absolute positioning relative to the banner. */
.sam-banner { position: relative; }

/* Identity badge slot — populated in station mode by JS. Hidden
   when empty so lite-mode pages don't show a phantom block.
   Top-right corner of the banner. */
.sam-banner .sam-identity {
  position: absolute;
  top: 14px;
  right: 12px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px;
  font-size: 12px;
  color: var(--sam-text-2);
  border: 1px solid var(--sam-border);
  border-radius: 999px;
  background: var(--sam-surface);
}
.sam-banner .sam-identity:empty { display: none; }
.sam-banner .sam-identity .sam-identity-name { color: var(--sam-text); font-weight: 600; }
.sam-banner .sam-identity .sam-identity-station { color: var(--sam-text-dim); }

/* SAM-brand deploy tag — "SAM LITE" / "SAM WEB" / "SAM DESK" /
   "SAM STATION" / "SAM PROD" / "SAM DEV". Sits at the left of the
   banner as a prominent product-brand-with-deployment-level callout.
   The tag is the primary "where am I?" signal in the chrome —
   operators glance and know what capability surface they're on. */
.sam-banner .sam-mode-tag {
  position: absolute;
  top: 14px;
  left: 14px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  padding: 5px 10px;
  border-radius: 4px;
  background: var(--sam-bg);
  color: var(--sam-amber);
  border: 1px solid var(--sam-amber-2);
}
/* Per-deploy variants — STATION reads as live/active, LITE/WEB are
   neutral. PROD inherits the default (amber) since "production" is
   the canonical ship target. DEV gets a warning tone so the operator
   never confuses dev with real. */
.sam-banner .sam-mode-tag[data-mode="station"] {
  background: var(--sam-ok-bg);
  color: var(--sam-ok-text);
  border-color: var(--sam-ok);
}
.sam-banner .sam-mode-tag[data-mode="lite"],
.sam-banner .sam-mode-tag[data-mode="web"],
.sam-banner .sam-mode-tag[data-mode="desk"] {
  background: var(--sam-pending-bg);
  color: var(--sam-text-2);
  border-color: var(--sam-border);
}
.sam-banner .sam-mode-tag[data-mode="dev"] {
  background: var(--sam-warn-bg);
  color: var(--sam-warn);
  border-color: var(--sam-warn);
}

/* ---------- WIRE (live job feed) ---------- */

/* WIRE — live job feed at the top of the workbench (in flow, part
   of the sticky cluster below). Visible only when there are jobs
   to surface (station mode); display:flex on .sam-active. */
.sam-wire {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 6px var(--sam-zone-pad-x);
  background: var(--sam-surface-2);
  border-bottom: 1px solid var(--sam-border-soft);
  font-size: 12px;
  color: var(--sam-text-2);
  overflow-x: auto;
  white-space: nowrap;
}
.sam-wire.sam-active { display: flex; }
.sam-wire[data-align="left"]  { justify-content: flex-start; }
.sam-wire[data-align="right"] { justify-content: flex-end; }
.sam-wire[data-align="center"]{ justify-content: center; }

/* Sticky cluster — wraps the workbench zones that should stay visible
   when the operator scrolls down a long stage-center list. BANNER scrolls away
   naturally (it's above the cluster); cluster pins to top:0 as one
   unit; STAGE scrolls under it.

   Per-zone opt-out: a tool that wants to e.g. keep MASTHEAD scrolling
   along with BANNER moves it OUT of the cluster wrapper in markup.
   The cluster is the OPT-IN convention; place zones inside to stick
   them, outside to scroll them. Default profile per workbench-decisions
   #4 is WIRE through TICKER all sticky. */
.sam-sticky-cluster {
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--sam-bg);
  /* Subtle shadow when cluster is sticky (i.e., user has scrolled
     past BANNER) so the boundary between fixed cluster and scrolling
     STAGE is visible. */
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
.sam-wire .sam-wire-label {
  text-transform: uppercase;
  font-size: 10px;
  letter-spacing: 1px;
  color: var(--sam-text-dim);
}
.sam-wire .sam-wire-job {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 8px;
  border-radius: 3px;
  background: var(--sam-surface);
  border: 1px solid var(--sam-border);
}
.sam-wire .sam-wire-job[data-status="running"]  { color: var(--sam-info); }
.sam-wire .sam-wire-job[data-status="done"]     { color: var(--sam-ok); }
.sam-wire .sam-wire-job[data-status="failed"]   { color: var(--sam-bad); }
.sam-wire .sam-wire-job[data-status="cancelled"],
.sam-wire .sam-wire-job[data-status="interrupted"] { color: var(--sam-text-dim); }

/* ---------- MASTHEAD (source ID + export verbs) ---------- */

.sam-masthead {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px var(--sam-zone-pad-x);
  /* Distinct surface so MASTHEAD reads as "what you're working on"
     and doesn't visually merge into the DECK below. The page bg
     (--sam-bg) and the DECK surface above and below were both dark
     and unbroken; --sam-surface gives the MASTHEAD a card-like band
     at the top of the working stack. */
  background: var(--sam-surface);
  border-top: 1px solid var(--sam-border-soft);
  border-bottom: 2px solid var(--sam-border);
}
/* MASTHEAD supports 1..N slots. Each slot is .sam-masthead-slot
   carrying its own .sam-source-name + .sam-source-meta (and optional
   per-slot affordances). Tools that take two sources (verify:
   expected + delivery; compare: cut A + cut B) drop in two slots;
   tools with one source use one. The legacy .sam-source class is kept
   as an alias so existing pages render unchanged. */
.sam-masthead {
  flex-wrap: wrap;     /* responsive: slots wrap to stacked when tight */
}
.sam-masthead .sam-masthead-slot,
.sam-masthead .sam-source {
  flex: 1 1 240px;     /* grow + shrink, but stay >= 240 before wrapping */
  display: flex;
  flex-direction: column;
  min-width: 0;        /* allow ellipsis */
  /* Visual divider between adjacent slots — pseudo-element so we
     don't have to render <span> separators in the markup. */
  position: relative;
  padding-right: 16px;
}
.sam-masthead .sam-masthead-slot + .sam-masthead-slot,
.sam-masthead .sam-masthead-slot + .sam-source,
.sam-masthead .sam-source + .sam-masthead-slot {
  border-left: 1px solid var(--sam-border-soft);
  padding-left: 16px;
}

/* Explicit stack modifier — forces vertical even at wide widths.
   Use when slots have long paths or dense per-slot detail. */
.sam-masthead.sam-stacked {
  flex-direction: column;
  align-items: stretch;
}
.sam-masthead.sam-stacked .sam-masthead-slot,
.sam-masthead.sam-stacked .sam-source {
  flex: none;
  padding-right: 0;
}
.sam-masthead.sam-stacked .sam-masthead-slot + .sam-masthead-slot,
.sam-masthead.sam-stacked .sam-masthead-slot + .sam-source,
.sam-masthead.sam-stacked .sam-source + .sam-masthead-slot {
  border-left: none;
  border-top: 1px solid var(--sam-border-soft);
  padding-left: 0;
  padding-top: 8px;
  margin-top: 8px;
}

.sam-masthead .sam-source-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--sam-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sam-masthead .sam-source-meta {
  font-size: 11px;
  color: var(--sam-text-dim);
  font-family: var(--sam-font-mono);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Per-slot label — small uppercase tag at the top of the slot
   ("EXPECTED", "DELIVERY", "CUT A", "CUT B"). Optional; tools
   that have a single slot don't include it. */
.sam-masthead .sam-slot-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--sam-text-dim);
  margin-bottom: 2px;
}
.sam-masthead .sam-export {
  display: flex;
  gap: 8px;
  align-items: center;
  /* Push exports to the end of the masthead regardless of how many
     slots precede them. When 2+ slots are present, exports are
     tool-level (not per-slot) per the locked design rule. */
  flex: 0 0 auto;
}
.sam-masthead .sam-export-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--sam-text-dim);
}

/* ---------- DECK (primary tool actions) ---------- */

.sam-deck {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  padding: var(--sam-zone-pad-y) var(--sam-zone-pad-x);
  border-bottom: 1px solid var(--sam-border-soft);
}
.sam-deck .sam-deck-group {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-right: 12px;
  border-right: 1px solid var(--sam-border-soft);
}
.sam-deck .sam-deck-group:last-child { border-right: none; padding-right: 0; }
.sam-deck label {
  color: var(--sam-text-2);
  font-size: var(--sam-fs-sm);
}

/* ---------- FILTERS (view filters) ---------- */

.sam-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  padding: var(--sam-zone-pad-y) var(--sam-zone-pad-x);
  background: var(--sam-surface-2);
  border-bottom: 1px solid var(--sam-border-soft);
  font-size: var(--sam-fs-sm);
  color: var(--sam-text-2);
}
.sam-filters .sam-filters-group {
  display: flex;
  align-items: center;
  gap: 6px;
}
.sam-filters .sam-filters-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--sam-text-dim);
}

/* ---------- TICKER (transient status) ---------- */

.sam-ticker {
  padding: 8px var(--sam-zone-pad-x);
  font-size: var(--sam-fs-sm);
  border-bottom: 1px solid var(--sam-border-soft);
  /* Ticker is always present in markup; CSS hides empty so the
     surrounding stack doesn't reserve space when there's no message. */
  /* Center default — same reasoning as WIRE; this is a status banner,
     not a left-anchored field. Override per element via data-align. */
  text-align: center;
}
.sam-ticker:empty { display: none; }
.sam-ticker[data-align="left"]   { text-align: left; }
.sam-ticker[data-align="right"]  { text-align: right; }
.sam-ticker[data-align="center"] { text-align: center; }
.sam-ticker.sam-ticker-loading { color: var(--sam-text-2); }
.sam-ticker.sam-ticker-success { color: var(--sam-ok); }
.sam-ticker.sam-ticker-warn    { color: var(--sam-warn); }
.sam-ticker.sam-ticker-error   { color: var(--sam-bad); }

/* Tone variants — prominent background tint instead of subtle text
   color. Use for action-result notifications that need to be seen
   ("load failed", "export complete"); use the .sam-ticker-* text-
   color variants above for low-key live status updates. Both idioms
   live in the same zone; pages pick by message intent. */
.sam-ticker[data-tone="info"] { background: var(--sam-info-bg); }
.sam-ticker[data-tone="ok"]   { background: var(--sam-ok-bg); }
.sam-ticker[data-tone="warn"] { background: var(--sam-warn-bg); }
.sam-ticker[data-tone="bad"]  { background: var(--sam-bad-bg); }

/* Inline key/value pairs inside the ticker (e.g. "loaded · 42 items").
   Optional — pages opt in by wrapping with .k / .v spans. */
.sam-ticker .k { color: var(--sam-text-dim); margin-right: 4px; }
.sam-ticker .v { color: var(--sam-text); font-family: var(--sam-font-mono); margin-right: 16px; }

/* ---------- STAGE (resizable panel holders) ----------

   STAGE is a CSS Grid with up to 5 tracks: stage-left, gutter,
   stage-center, gutter, stage-right. Each gutter is a real DOM
   child (`.sam-stage-gutter`) not just CSS `gap`, so we can attach
   pointer-event listeners to it for resize. CSS `:has()` adapts
   column templates based on which panels are actually in the DOM.

   Layouts emerge from which panels exist:

     - 1-col:     <stage><stage-center/></stage>
     - 2-col:     <stage><stage-center/><gutter/><stage-right/></stage>
     - 3-col:     <stage><stage-left/><gutter/><stage-center/>
                         <gutter/><stage-right/></stage>
     - 2-col-L:   <stage><stage-left/><gutter/><stage-center/></stage>

   No `data-layout` attribute. STAGE shape is purely DOM-derived.

   Resize is wired by JS on the `.sam-stage-gutter` elements; the JS
   sets --sam-stage-{left,center,right}-w on the .sam-workbench element
   to adjust live (continuous reflow). On pointerup, widths persist
   to localStorage at sam.workbench.<page>.stage.widths.

   stage-center never collapses (always-present working area).
   stage-left and stage-right collapse to a 24px mini-strip on click
   of their .sam-collapse-btn; click the strip to restore.
*/

.sam-stage {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, var(--sam-stage-center-w)) var(--sam-stage-gap) minmax(0, var(--sam-stage-right-w));
  padding: 0 var(--sam-zone-pad-x);
  /* STAGE height is operator-controllable via the .sam-stage-vgrip
     handle above it. Default min-height ensures STAGE is always
     workable; the operator drags the grip to expand or shrink. The
     page grows when STAGE > available space and scrolls naturally. */
  min-height: var(--sam-stage-min-h, max(60vh, 400px));
}

/* Horizontal grab handles for STAGE — TOP + BOTTOM grips. Both
   resize STAGE's height. Top: drag UP expands. Bottom: drag DOWN
   expands. Operator can grab whichever is more accessible. The grips
   are visually obvious — small "═" texture in the center so they're
   findable, hover thickens, active drag turns amber. */
.sam-stage-vgrip {
  height: 8px;
  cursor: row-resize;
  background: var(--sam-border-soft);
  margin: 4px var(--sam-zone-pad-x);
  border-radius: 4px;
  position: relative;
  touch-action: none;
  transition: background 0.1s, height 0.1s;
  flex-shrink: 0;
  /* Tactile texture — three short bars centered horizontally so the
     grip reads as "drag me" without a separate handle icon. */
  background-image:
    linear-gradient(var(--sam-border) 0 0),
    linear-gradient(var(--sam-border) 0 0),
    linear-gradient(var(--sam-border) 0 0);
  background-repeat: no-repeat;
  background-position: calc(50% - 24px) 50%, 50% 50%, calc(50% + 24px) 50%;
  background-size: 16px 2px;
}
.sam-stage-vgrip:hover  { background-color: var(--sam-border); height: 10px; }
.sam-stage-vgrip.sam-resizing {
  background-color: var(--sam-amber);
  height: 10px;
}

/* Bottom vresize grip — pinned to viewport bottom, just ABOVE the
   fixed FOOTER. Without this, the grip ends at the page's natural
   bottom edge, which (when content fits viewport) is exactly at
   viewport bottom — and the footer (z-index 1000) overlays it.
   Same fixed-positioning trick as the footer; sits 22px (footer
   height) above the bottom of the viewport. */
.sam-stage-vgrip[data-position="bottom"] {
  position: fixed;
  bottom: 28px;
  left: var(--sam-zone-pad-x);
  right: var(--sam-zone-pad-x);
  margin: 0;
  z-index: 999;
}
body.sam-resize-v { cursor: row-resize !important; user-select: none; }
body.sam-resize-v * { cursor: row-resize !important; }

/* Hiding a panel — two patterns:
 *
 *   1. DOM-removal (static layout choice): a page that never wants
 *      stage-left omits both the panel AND its adjacent gutter from
 *      the markup. The :has() rules adapt to the resulting layout.
 *      Use this when the choice is fixed for the page (e.g., "this
 *      tool has no inspector").
 *
 *   2. [hidden] attribute (runtime data-driven toggle): the panel
 *      stays in the DOM but is marked [hidden]. The :has() rules
 *      below explicitly ignore [hidden] panels when picking the
 *      grid-template-columns so the layout adapts as if the panel
 *      were absent. The rule on .sam-stage-gutter further down hides
 *      the gutter adjacent to a [hidden] panel so it doesn't leave a
 *      lonely strip behind. Use this when the panel comes and goes
 *      based on data state (e.g., TRIMS bin empty/not-empty in
 *      /list-editor).
 *
 * Both patterns produce the same visual result; pick by what the page
 * actually wants to express.
 */

/* 1-col — center only. Matches when neither side panel is present (or
 * both are [hidden]). */
.sam-stage:not(:has(.sam-stage-right:not([hidden]))):not(:has(.sam-stage-left:not([hidden]))) {
  grid-template-columns: 1fr;
}

/* 2-col left — LEFT + center (no right). For tools with controls in
   LEFT but no inspector. */
.sam-stage:has(.sam-stage-left:not([hidden])):not(:has(.sam-stage-right:not([hidden]))) {
  grid-template-columns: minmax(0, var(--sam-stage-left-w)) var(--sam-stage-gap) minmax(0, var(--sam-stage-center-w));
}

/* 3-col — full tri-view. minmax(0, …) lets each panel shrink below
   its content's intrinsic min-width when the viewport scales (Cmd+/-)
   so the three panels scale proportionally instead of one panel
   hitting min-content and the others overflowing. Each panel's own
   overflow handling (.sam-stage-* { overflow-y: auto }) catches any
   content that doesn't fit. */
.sam-stage:has(.sam-stage-left:not([hidden])):has(.sam-stage-right:not([hidden])) {
  grid-template-columns:
    minmax(0, var(--sam-stage-left-w))   var(--sam-stage-gap)
    minmax(0, var(--sam-stage-center-w)) var(--sam-stage-gap)
    minmax(0, var(--sam-stage-right-w));
}

/* Gutter sibling of a [hidden] panel hides itself, so it doesn't
   render as a lonely strip when the panel it pairs with is absent. */
.sam-stage-left[hidden] + .sam-stage-gutter,
.sam-stage-gutter:has(+ .sam-stage-right[hidden]) {
  display: none;
}

/* ---------- stage-left panel ----------

   Mirror of stage-right structurally — surface-2 background,
   scrollable, self-contained DOM so it can be popped out per the
   locked design. Tools fill it with whatever they need (DECK-style
   controls in the left-panel pattern, or secondary content like saved
   manifests / comparison source). */
.sam-stage-left {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow-y: auto;
  padding: 16px 16px 32px;
  background: var(--sam-surface-2);
  border-right: 1px solid var(--sam-border);
  border-radius: 0;
  /* Width tokens — separate from stage-center / stage-right so each is
     independently adjustable. Pages or the resize JS set these on
     .sam-workbench. */
  --sam-stage-left-min: 120px;  /* hard floor — table layouts break below */
}
.sam-workbench { --sam-stage-left-w: 1fr; }   /* default when stage-left is present */

/* The gutter — a real DOM child of STAGE that acts as both visual
   separator and pointer-event target for resize. Modern drag feedback:
   col-resize cursor on hover, soft background, shifts darker on hover
   and again darker while actively dragging. No ghost line — the live
   reflow of the columns IS the feedback. */
.sam-stage-gutter {
  width: var(--sam-stage-gap, 16px);
  cursor: col-resize;
  background: transparent;
  border-radius: 3px;
  position: relative;
  /* Touch-friendly: ensure pointer events fire even though the visual
     gutter is thin. Larger interactive area via a transparent ::before
     would be better on touch — leaving as a follow-up if iPad ops
     report difficulty grabbing the gutter. */
  touch-action: none;
}
.sam-stage-gutter::before {
  /* Visible bar inside the gutter track — narrower than the track
     itself so the cursor target is generous. */
  content: "";
  position: absolute;
  top: 12px;
  bottom: 12px;
  left: 50%;
  transform: translateX(-50%);
  width: 2px;
  background: var(--sam-border-soft);
  border-radius: 1px;
  transition: background 0.1s, width 0.1s;
}
.sam-stage-gutter:hover::before { background: var(--sam-border); width: 4px; }
.sam-stage-gutter.sam-resizing::before,
.sam-stage-gutter:active::before {
  background: var(--sam-amber);
  width: 4px;
}
/* Body-level cursor lock during drag — set by JS via .sam-resize-h
   so the col-resize cursor stays visible even when the pointer leaves
   the gutter mid-drag. */
body.sam-resize-h { cursor: col-resize !important; user-select: none; }
body.sam-resize-h * { cursor: col-resize !important; }

/* ---------- Collapse buttons + edge affordances ----------

   Each collapsible STAGE column (stage-left, stage-right) carries
   a small chevron button in its header. Clicking the button hides
   the column and reveals a thin edge bar (the "edge affordance")
   with a chevron pointing the other way; clicking the edge bar
   restores the column. State persists at
   sam.workbench.<page>.stage.collapsed.

   stage-center never collapses — it's the always-present center.
*/

/* Panel-level collapse button. Sits in the top-right of stage-left
   or stage-right (the panel itself is position: relative for this).
   Distinct from anything inside a widget — collapsing is a panel
   concern, not a widget concern. */
.sam-stage-left,
.sam-stage-right {
  position: relative;
}
.sam-stage-left > .sam-collapse-btn,
.sam-stage-right > .sam-collapse-btn,
.sam-collapse-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 1px solid var(--sam-border);
  border-radius: 3px;
  background: var(--sam-surface);
  color: var(--sam-text-2);
  font-size: 12px;
  cursor: pointer;
  user-select: none;
}
.sam-stage-left > .sam-collapse-btn,
.sam-stage-right > .sam-collapse-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 5;
}
.sam-collapse-btn:hover {
  background: var(--sam-surface-2);
  color: var(--sam-text);
  border-color: var(--sam-border);
}

/* Collapsed column — the column itself shrinks to 24px and renders
   only its `.sam-edge-content` child (vertical label + chevron),
   visually reading as a "mini-strip". All other children are
   hidden in collapsed mode. The whole column is clickable to restore.
   One DOM element, two visual modes — no separate affordance sibling
   to confuse the grid layout. */
.sam-stage-left.sam-collapsed,
.sam-stage-right.sam-collapsed {
  padding: 12px 0;
  cursor: pointer;
  align-items: center;
  justify-content: flex-start;
  overflow: hidden;
}
/* Hide every child of the collapsed column EXCEPT the edge-content. */
.sam-stage-left.sam-collapsed > *,
.sam-stage-right.sam-collapsed > * {
  display: none !important;
}
.sam-stage-left .sam-edge-content,
.sam-stage-right .sam-edge-content {
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  color: var(--sam-text-dim);
  font-size: 11px;
}
.sam-stage-left.sam-collapsed > .sam-edge-content,
.sam-stage-right.sam-collapsed > .sam-edge-content {
  display: flex !important;
}
.sam-stage-left.sam-collapsed:hover,
.sam-stage-right.sam-collapsed:hover {
  background: var(--sam-surface);
}
.sam-stage-left.sam-collapsed:hover .sam-edge-chevron,
.sam-stage-right.sam-collapsed:hover .sam-edge-chevron {
  color: var(--sam-amber);
}
.sam-edge-chevron {
  font-size: 12px;
  flex: 0 0 auto;
  transition: color 0.1s;
}
.sam-edge-label {
  writing-mode: vertical-rl;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  font-size: 10px;
  font-weight: 600;
  flex: 0 0 auto;
}

/* When a column is collapsed, hide its adjacent gutter — there's
   nothing to resize between stage-center and a 24px sliver. */
.sam-stage-left.sam-collapsed + .sam-stage-gutter,
.sam-stage-gutter:has(+ .sam-stage-right.sam-collapsed) {
  display: none !important;
}

/* Grid template adapts when a column collapses — the column track
   shrinks to 24px (the mini-strip width); the matching gutter is
   hidden by display:none above so we drop it from the template.
   stage-center becomes greedy (1fr) so it absorbs ALL freed space
   — the surviving non-collapsed column keeps its pre-collapse width
   (snapshotted in pixels by the JS collapse handler).
*/
/* All collapse rules also gate on :not([hidden]) so a panel that's
   fully hidden by [hidden] doesn't trigger the mini-strip layout. */
.sam-stage:has(.sam-stage-left:not([hidden]).sam-collapsed):not(:has(.sam-stage-right:not([hidden]))) {
  grid-template-columns: 24px 1fr;
}
.sam-stage:has(.sam-stage-right:not([hidden]).sam-collapsed):not(:has(.sam-stage-left:not([hidden]))) {
  grid-template-columns: 1fr 24px;
}
.sam-stage:has(.sam-stage-left:not([hidden]).sam-collapsed):has(.sam-stage-right:not([hidden])):not(:has(.sam-stage-right:not([hidden]).sam-collapsed)) {
  grid-template-columns:
    24px
    minmax(0, 1fr) var(--sam-stage-gap)
    minmax(0, var(--sam-stage-right-w));
}
.sam-stage:has(.sam-stage-left:not([hidden])):not(:has(.sam-stage-left:not([hidden]).sam-collapsed)):has(.sam-stage-right:not([hidden]).sam-collapsed) {
  grid-template-columns:
    minmax(0, var(--sam-stage-left-w)) var(--sam-stage-gap)
    minmax(0, 1fr)
    24px;
}
.sam-stage:has(.sam-stage-left:not([hidden]).sam-collapsed):has(.sam-stage-right:not([hidden]).sam-collapsed) {
  grid-template-columns: 24px 1fr 24px;
}

/* ---------- stage-center (the data column) ---------- */

.sam-stage-center {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
  overflow-y: auto;
  /* Bottom padding clears the fixed footer so the last list row is
     readable when scrolled to the end of stage-center. (Footer is
     22px; padding 32px gives a comfortable margin above it.) */
  padding-bottom: 32px;
}

/* Empty-state typography for a widget with no content yet. Tools
   opt in by including <div class="sam-widget-empty-large">…</div>. */
.sam-widget-empty-large {
  padding: 48px 24px;
  text-align: center;
  color: var(--sam-text-dim);
}
.sam-widget-empty-large h2 {
  margin: 0 0 12px;
  font-size: 18px;
  font-weight: 500;
  color: var(--sam-text-2);
}
.sam-widget-empty-large p {
  margin: 4px 0;
  font-size: var(--sam-fs-sm);
}

/* ---------- stage-right (selection-focused panel) ---------- */

.sam-stage-right {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow-y: auto;
  padding: 16px 16px 32px;
  background: var(--sam-surface-2);
  border-left: 1px solid var(--sam-border);
  border-radius: 0;
}

/* ---------- Widgets — what goes IN the panels ----------

   Panels (.sam-stage-left / -center / -right) are spatial containers,
   nothing more. They hold widgets and let the operator resize /
   collapse them. They have no semantics of their own; just resizable
   holders.

   Widgets are the content primitives that LIVE in panels: a list-
   editor widget (which hosts list-modules), a bench widget (bulk
   action bar), a detail widget (selection-focused inspector + media-
   view preview), a pipeline-inputs widget (controls), and whatever
   else tools build. Tools place widgets in panels; widgets can be
   moved between panels or popped out (per the Document PiP plan in
   section 7 of the workbench-decisions memory note).

   Naming: `loupe` is reserved for a future interactive magnifier
   tool — see `project_loupe_reserved`. Today's still / video frames
   inside a detail widget are passive media views, not loupes. */

   Every widget shares the `.sam-widget` shell:

     .sam-widget                       — container, vertical flex
       .sam-widget-header              — title bar (with optional actions)
         .sam-widget-title             — h2-ish text
         (per-widget action buttons)
       .sam-widget-body                — content area (scrollable if tall)

   The only widget-variant CSS class that earns its own rules today is
   `.sam-bench` (visual treatment for the bulk-action bar). Other
   widgets just use `.sam-widget` plus their own custom interior.

   Note: a few `.sam-stage-right`-scoped helpers below (dl/dt/dd
   typography, the .sam-widget-preview frame) live where stage-right
   widgets put metadata + a preview. They're scoped to stage-right
   today because that's where the typical detail widget lands; they're
   not bound to that panel by the chrome contract. */
.sam-widget {
  display: flex;
  flex-direction: column;
  min-height: 0;
  margin-bottom: 12px;
}
.sam-widget:last-child { margin-bottom: 0; }

.sam-widget-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--sam-border-soft);
}
.sam-widget-title {
  flex: 1;
  font-size: 16px;
  font-weight: 600;
  color: var(--sam-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sam-widget-body {
  display: flex;
  flex-direction: column;
  gap: 16px;
  flex: 1;
}

/* Empty-state typography for a stage-right detail widget — used when
   nothing is selected. Lower-key than .sam-widget-empty-large
   (smaller, no h2). */
.sam-widget-empty {
  padding: 24px 0;
  text-align: center;
  color: var(--sam-text-dim);
  font-style: italic;
  font-size: var(--sam-fs-sm);
}

/* Standard metadata stack inside a stage-right detail widget —
   dl/dt/dd pattern that list-editor and vfx-idgen will both want.
   Tools that don't use it just don't include the markup. */
.sam-stage-right dl {
  display: grid;
  grid-template-columns: minmax(80px, max-content) 1fr;
  column-gap: 12px;
  row-gap: 6px;
  margin: 0;
}
.sam-stage-right dt {
  color: var(--sam-text-dim);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.sam-stage-right dd {
  margin: 0;
  font-family: var(--sam-font-mono);
  color: var(--sam-text);
  word-break: break-word;
}
.sam-stage-right dd.sam-notes {
  white-space: pre-wrap;
  font-style: italic;
  font-family: var(--sam-font-ui);
  color: var(--sam-text-2);
}

/* Media-view frame inside a detail widget — fixed aspect, light
   border, room for the strategy tag (verify) or scrubber (reviews).
   When the future loupe tool ships, it gets its own widget shell;
   this one stays the passive-preview slot. */
.sam-widget-preview {
  position: relative;
  background: #000;
  border: 1px solid var(--sam-border);
  border-radius: var(--sam-radius);
  overflow: hidden;
  aspect-ratio: 16 / 9;
}
.sam-widget-preview img,
.sam-widget-preview video {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.sam-widget-preview .sam-widget-preview-empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--sam-text-dim);
  font-size: var(--sam-fs-sm);
  font-style: italic;
}

/* ---------- BENCH (bulk-operations widget) ----------

   BENCH is a widget — tools place it in whatever panel makes sense.
   Common placements:

     - In stage-center after the list-editor widget: BENCH appears
       below the list when the operator selects rows for bulk action.
       Most common pattern (vfx-idgen does this).
     - In stage-left or stage-right: tool-specific bench-style verbs
       (e.g. a "saved-search" bench in stage-left, or a "review-actions"
       bench in stage-right alongside the detail widget).

   Visual treatment: distinct from neutral widgets — top border, surface
   tint, optional variant tone — so when BENCH activates it reads as
   "actions available here right now" rather than just more content.
   Height = exactly what its content needs (single row of buttons).

   The widget does NOT float / overlay. It sits in normal flow in
   whatever panel placed it. Panels with internal scroll handle BENCH
   the same way they handle any tall widget.
*/
.sam-bench {
  display: none;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: var(--sam-surface-2);
  border-top: 1px solid var(--sam-border);
  /* No min-height — BENCH height = content height. */
}
.sam-bench.sam-active { display: flex; }
.sam-bench .sam-bench-spacer { flex: 1; }
.sam-bench .sam-bench-count {
  font-weight: 600;
  color: var(--sam-text);
}

/* Variants for the BENCH — different bars in vfx-idgen are all
   bench variants; verify will get its own (apply-fixes), ingest
   gets one (commit-batch). The variant adds a subtle tone so the
   operator sees which mode of bench they're in. */
.sam-bench[data-variant="select"]  { border-top-color: var(--sam-border); }
.sam-bench[data-variant="assign"]  {
  background: var(--sam-ok-bg);
  border-top-color: var(--sam-ok);
}
.sam-bench[data-variant="export"]  { border-top-color: var(--sam-info); }
.sam-bench[data-variant="warn"]    {
  background: var(--sam-warn-bg);
  border-top-color: var(--sam-warn);
}

/* ---------- STAGE responsive fallback ---------- */

/* Below ~900px the side-by-side STAGE breaks down — stage-center
   becomes unreadably narrow before stage-right does. Stack vertically
   instead. Tools with rich stage-right widgets (reviews) can override
   per their needs. */
@media (max-width: 900px) {
  .sam-stage {
    grid-template-columns: 1fr;
  }
  .sam-stage-right {
    border-left: none;
    border-top: 1px solid var(--sam-border);
  }
}

/* ---------- Print cooperation ---------- */

/* The existing @media print block already hides controls and shows
   the list-module. Workbench zones cooperate by using the same
   class conventions that block already targets (.upload-area,
   .sam-button, .sam-input). The only zone-specific addition: hide
   the side panels on print so stage-center fills the page width. */
@media print {
  .sam-stage-right,
  .sam-stage-left,
  .sam-wire,
  .sam-bench,
  .sam-deck,
  .sam-filters,
  .sam-ticker,
  .sam-stage-gutter,
  .sam-stage-vgrip {
    display: none !important;
  }
  .sam-stage {
    grid-template-columns: 1fr !important;
    padding: 0 !important;
  }
  .sam-masthead {
    border-bottom: 1px solid #999 !important;
  }
  /* Force white-paper background through every workbench layer that
     would otherwise carry a dark surface. The list-module subtree
     already uses light-scope tokens, but the surrounding
     .sam-workbench / .sam-stage / .sam-stage-center inherit dark
     surface variables that read as "dark around the list" on paper.
     Strip those backgrounds + force light text where it would
     otherwise stay dark-mode pale. */
  body,
  .sam-workbench,
  .sam-stage,
  .sam-stage-center,
  .sam-banner,
  .sam-masthead {
    background: #fff !important;
    color: #000 !important;
  }
}
