/* ============================================================================
   Clearview "Console" — Media cluster components
   ----------------------------------------------------------------------------
   Requires tokens/tokens.css + css/clearview-refresh.css.

   Namespaced `cvm-`. These are the Media-page-specific pieces that don't belong
   in the shared component layer: the app shell, the file browser, and the
   editor's stage/timeline/meters.

   On migration these rules replace the ~290 selectors the cluster currently
   owns in styles.css (.media-* 96, .fc-* 115, .timeline* 33, .upload-* 19,
   .transfer-* 11, .editor-* 9, .waveform* 7).
   ============================================================================ */

/* ================================================================ app shell */

.cvm-shell { display: grid; grid-template-columns: var(--cv-sidebar-w) 1fr; height: 100vh; }

.cvm-side {
  display: flex; flex-direction: column;
  background: var(--cv-bg-surface);
  border-right: 1px solid var(--cv-border);
  overflow: hidden;
}
.cvm-brand {
  display: flex; align-items: center; gap: var(--cv-space-2);
  height: var(--cv-header-h);
  padding: 0 var(--cv-space-3);
  border-bottom: 1px solid var(--cv-border-subtle);
  font-weight: var(--cv-weight-semibold);
}
.cvm-brand-mark {
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border-radius: var(--cv-radius-sm);
  background: var(--cv-accent); color: var(--cv-on-accent);
}
.cvm-nav { flex: 1; overflow-y: auto; padding: var(--cv-space-2); display: grid; gap: 1px; align-content: start; }
.cvm-nav-item {
  display: flex; align-items: center; gap: var(--cv-space-2);
  height: var(--cv-row); padding: 0 var(--cv-space-2);
  border: none; border-radius: var(--cv-radius-sm);
  background: none; color: var(--cv-text-secondary);
  font: inherit; text-align: left; width: 100%; cursor: pointer;
}
.cvm-nav-item:hover { background: var(--cv-bg-hover); color: var(--cv-text); }
.cvm-nav-item[aria-current="page"] {
  background: var(--cv-accent-wash);
  color: var(--cv-accent);
  box-shadow: inset 2px 0 0 var(--cv-accent);
}
.cvm-nav-sep { height: 1px; margin: var(--cv-space-2) var(--cv-space-2); background: var(--cv-border-subtle); }
.cvm-nav-label {
  padding: var(--cv-space-2) var(--cv-space-2) var(--cv-space-1);
  font-size: var(--cv-text-2xs); font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
  color: var(--cv-text-disabled);
}
.cvm-user {
  display: flex; align-items: center; gap: var(--cv-space-2);
  padding: var(--cv-space-2) var(--cv-space-3);
  border-top: 1px solid var(--cv-border-subtle);
}
.cvm-avatar {
  width: 26px; height: 26px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--cv-radius-full);
  background: var(--cv-bg-active); color: var(--cv-text);
  font-size: var(--cv-text-xs); font-weight: var(--cv-weight-semibold);
}

.cvm-main { display: flex; flex-direction: column; min-width: 0; overflow: hidden; }
.cvm-head {
  display: flex; align-items: center; gap: var(--cv-space-3);
  height: var(--cv-header-h); flex: none;
  padding: 0 var(--cv-space-4);
  border-bottom: 1px solid var(--cv-border);
}
.cvm-title { font-size: var(--cv-text-xl); font-weight: var(--cv-weight-semibold); letter-spacing: var(--cv-tracking-tight); }
.cvm-body { flex: 1; overflow-y: auto; padding: 0 var(--cv-space-4) var(--cv-space-6); }

/* Tab strip sits directly under the header and sticks. */
.cvm-tabstrip {
  position: sticky; top: 0; z-index: var(--cv-z-toolbar);
  background: var(--cv-bg-page);
  padding-top: var(--cv-space-1);
}

/* ============================================================ scope control
   The current Media tab silently represents three sub-views — general / site /
   external — all highlighting the same tab, so the breadcrumb is the only clue
   where you are. Scope becomes an explicit, always-visible control. */
.cvm-scope { display: flex; align-items: center; gap: var(--cv-space-2); flex-wrap: wrap; }
.cvm-scope-btn {
  display: inline-flex; align-items: center; gap: var(--cv-space-2);
  height: var(--cv-control); padding: 0 var(--cv-space-3);
  border: 1px solid var(--cv-border); border-radius: var(--cv-radius);
  background: var(--cv-bg-surface); color: var(--cv-text-secondary);
  font: inherit; cursor: pointer;
  transition: background var(--cv-dur-fast) var(--cv-ease), color var(--cv-dur-fast) var(--cv-ease);
}
.cvm-scope-btn:hover { background: var(--cv-bg-hover); color: var(--cv-text); }
.cvm-scope-btn[aria-pressed="true"] {
  background: var(--cv-accent-wash);
  border-color: var(--cv-accent-edge);
  color: var(--cv-accent);
}
.cvm-scope-btn .cv-chip-count { color: inherit; opacity: 0.7; }

.cvm-infobar {
  display: flex; align-items: center; gap: var(--cv-space-3);
  padding: var(--cv-space-2) 0;
  font-size: var(--cv-text-xs); color: var(--cv-text-muted);
  border-bottom: 1px solid var(--cv-border-subtle);
}

/* ========================================================== file browser */

.cvm-files { display: grid; gap: var(--cv-space-2); padding: var(--cv-space-3) 0; }
.cvm-files[data-view="grid"] { grid-template-columns: repeat(auto-fill, minmax(196px, 1fr)); }
/* Grid thumbnail size (S/M/L) — controls the column min-width so folders and
   files render as smaller or larger tiles. */
.cvm-files[data-view="grid"][data-size="sm"] { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); }
.cvm-files[data-view="grid"][data-size="md"] { grid-template-columns: repeat(auto-fill, minmax(196px, 1fr)); }
.cvm-files[data-view="grid"][data-size="lg"] { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.cvm-files[data-view="list"] { grid-template-columns: 1fr; gap: 0; }

.cvm-file {
  position: relative;
  display: flex; flex-direction: column;
  border: 1px solid var(--cv-border);
  border-radius: var(--cv-radius-lg);
  background: var(--cv-bg-surface);
  overflow: hidden;
  cursor: pointer;
  transition: border-color var(--cv-dur-fast) var(--cv-ease), background var(--cv-dur-fast) var(--cv-ease);
}
.cvm-file:hover { border-color: var(--cv-border-strong); background: var(--cv-bg-hover); }
.cvm-file[aria-selected="true"] { border-color: var(--cv-accent); background: var(--cv-accent-wash); }

.cvm-thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  display: grid; place-items: center;
  background: var(--cv-bg-inset);
  border-bottom: 1px solid var(--cv-border-subtle);
  color: var(--cv-text-disabled);
}
.cvm-thumb .cv-i { width: 26px; height: 26px; }
/* Image thumbnails fill the 16:9 frame WITHOUT distortion — the browser default
   object-fit is `fill`, which stretched any non-16:9 image. `cover` keeps the
   aspect ratio and crops to fit. */
.cvm-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Duration sits on the thumbnail, mono + tabular, like every video tool. */
.cvm-dur {
  position: absolute; right: var(--cv-space-1); bottom: var(--cv-space-1);
  padding: 0 4px;
  border-radius: var(--cv-radius-sm);
  background: rgba(0, 0, 0, 0.72);
  color: #fff;
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-2xs);
}
.cvm-file-meta { padding: var(--cv-space-2); display: grid; gap: 2px; min-width: 0; }
.cvm-file-name { font-size: var(--cv-text-xs); font-weight: var(--cv-weight-medium); }
.cvm-file-sub  { font-size: var(--cv-text-2xs); color: var(--cv-text-muted); display: flex; gap: var(--cv-space-2); }

/* Selection checkbox and actions ride on the thumbnail, revealed on hover —
   but always shown on touch, on keyboard focus, and when selected. The current
   grid renders up to seven always-visible 11px buttons per card, wrapping. */
.cvm-file-pick { position: absolute; top: var(--cv-space-1); left: var(--cv-space-1); z-index: 1; }
.cvm-file-acts {
  position: absolute; top: var(--cv-space-1); right: var(--cv-space-1); z-index: 1;
  display: flex; gap: 2px;
  padding: 2px; border-radius: var(--cv-radius);
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(3px);
  opacity: 0; transition: opacity var(--cv-dur-fast) var(--cv-ease);
}
.cvm-file-pick { opacity: 0; transition: opacity var(--cv-dur-fast) var(--cv-ease); }
.cvm-file:hover .cvm-file-acts,
.cvm-file:hover .cvm-file-pick,
.cvm-file:focus-within .cvm-file-acts,
.cvm-file:focus-within .cvm-file-pick,
.cvm-file[aria-selected="true"] .cvm-file-pick { opacity: 1; }
.cvm-file-acts .cv-btn { color: #fff; }
@media (hover: none) { .cvm-file-acts, .cvm-file-pick { opacity: 1; } }

/* List view reuses the same element, re-laid-out — one renderer, two shapes.
   The current app has four independent grid/list renderer pairs producing
   near-identical markup, so any card change has to be made four times. */
.cvm-files[data-view="list"] .cvm-file {
  flex-direction: row; align-items: center; gap: var(--cv-space-2);
  border-radius: 0; border-width: 0 0 1px 0;
  padding: 0 var(--cv-space-2);
  min-height: var(--cv-row);
}
.cvm-files[data-view="list"] .cvm-thumb {
  width: 44px; aspect-ratio: 16 / 9; flex: none;
  border: none; border-radius: var(--cv-radius-sm);
}
.cvm-files[data-view="list"] .cvm-thumb .cv-i { width: 15px; height: 15px; }
.cvm-files[data-view="list"] .cvm-dur { display: none; }
.cvm-files[data-view="list"] .cvm-file-meta { flex: 1; padding: 0; grid-auto-flow: column; align-items: center; justify-content: start; gap: var(--cv-space-3); }
.cvm-files[data-view="list"] .cvm-file-sub { justify-content: flex-end; }
.cvm-files[data-view="list"] .cvm-file-pick,
.cvm-files[data-view="list"] .cvm-file-acts { position: static; opacity: 1; background: none; backdrop-filter: none; padding: 0; }
.cvm-files[data-view="list"] .cvm-file-acts { opacity: 0; }
.cvm-files[data-view="list"] .cvm-file:hover .cvm-file-acts,
.cvm-files[data-view="list"] .cvm-file:focus-within .cvm-file-acts { opacity: 1; }
.cvm-files[data-view="list"] .cvm-file-acts .cv-btn { color: var(--cv-text-secondary); }

/* Drop zone */
.cvm-drop {
  display: grid; place-items: center; gap: var(--cv-space-2);
  padding: var(--cv-space-6);
  border: 1px dashed var(--cv-border-strong);
  border-radius: var(--cv-radius-lg);
  color: var(--cv-text-muted);
  text-align: center;
}
.cvm-drop[data-over="true"] { border-color: var(--cv-accent); background: var(--cv-accent-wash); color: var(--cv-accent); }

/* ============================================================ file compare */

.cvm-summary { display: grid; gap: var(--cv-space-2); grid-template-columns: repeat(auto-fit, minmax(112px, 1fr)); }
.cvm-stat {
  display: grid; gap: 2px;
  padding: var(--cv-space-2) var(--cv-space-3);
  border: 1px solid var(--cv-border);
  border-radius: var(--cv-radius);
  background: var(--cv-bg-surface);
  cursor: pointer;
  transition: border-color var(--cv-dur-fast) var(--cv-ease), background var(--cv-dur-fast) var(--cv-ease);
}
.cvm-stat:hover { background: var(--cv-bg-hover); }
/* The verdict tiles ARE the filter. One control, not a summary plus a separate
   chip row that can disagree with it. */
.cvm-stat[aria-pressed="true"] { border-color: var(--cv-accent-edge); background: var(--cv-accent-wash); }
.cvm-stat-n {
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-2xl); font-weight: var(--cv-weight-medium);
  line-height: 1;
}
.cvm-stat-l { display: flex; align-items: center; gap: var(--cv-space-1); font-size: var(--cv-text-2xs); color: var(--cv-text-muted); }
.cvm-stat-match   .cvm-stat-n, .cvm-stat-match   .cvm-stat-l .cv-i { color: var(--cv-match); }
.cvm-stat-short   .cvm-stat-n, .cvm-stat-short   .cvm-stat-l .cv-i { color: var(--cv-short); }
.cvm-stat-long    .cvm-stat-n, .cvm-stat-long    .cvm-stat-l .cv-i { color: var(--cv-long); }
.cvm-stat-missing .cvm-stat-n, .cvm-stat-missing .cvm-stat-l .cv-i { color: var(--cv-missing); }
.cvm-stat-unscheduled .cvm-stat-n, .cvm-stat-unscheduled .cvm-stat-l .cv-i { color: var(--cv-unscheduled); }

.cvm-lock {
  display: inline-flex; align-items: center; gap: var(--cv-space-1);
  padding: 0 var(--cv-space-1);
  border-radius: var(--cv-radius-sm);
  background: var(--cv-warning-wash);
  color: var(--cv-warning);
  font-size: var(--cv-text-2xs);
}

/* ================================================================== editor */

/* ================================================================ editor layouts
   Two arrangements of the same four blocks — stage, timeline, files, audio —
   switched from the editor top bar. Everything below is CSS only; no DOM is
   re-parented, so nothing re-renders and no state is lost on a switch. That
   matters here: the Web Audio graph and the video element must survive
   (renderEditor() at editor.js:345 tears both down on any re-render).

   SPLIT — stage over timeline, inspector rail alongside, files and audio
   tabbed. The working default, and the only one that fits a laptop.

   SUITE — the arrangement every NLE settled on: browser, viewer and inspector
   across the top, timeline full width beneath. On a 1920 external — which is
   what venue operators actually run — the waveform gains roughly 500px, and
   timeline width is precision when you are setting an in-point inside an
   hour-long clip. Nothing is behind a tab. */
.cvm-ed { display: grid; gap: var(--cv-space-3); align-items: start; }

.cvm-ed[data-layout="split"] {
  grid-template-columns: minmax(0, 1fr) 344px;
  grid-template-areas: "stage side" "timeline side";
}
.cvm-ed[data-layout="split"] .cvm-ed-stage    { grid-area: stage; }
.cvm-ed[data-layout="split"] .cvm-ed-timeline { grid-area: timeline; }
.cvm-ed[data-layout="split"] .cvm-ed-side     { grid-area: side; }

.cvm-ed[data-layout="suite"] {
  grid-template-columns: 300px minmax(0, 1fr) 336px;
  /* The top row is BOUNDED and its panels scroll internally. Without this the
     row grows to the tallest panel — the audio strip at ~1030px — leaving the
     stage column mostly empty and pushing the timeline a full screen down.
     Every NLE constrains the top row for the same reason: the timeline has to
     stay anchored in view. */
  grid-template-rows: minmax(320px, 58vh) auto;
  grid-template-areas: "files stage audio" "timeline timeline timeline";
  align-items: stretch;
}
.cvm-ed[data-layout="suite"] > *,
.cvm-ed[data-layout="suite"] .cvm-ed-files,
.cvm-ed[data-layout="suite"] .cvm-ed-audio { min-width: 0; min-height: 0; }
.cvm-ed[data-layout="suite"] .cvm-ed-stage,
.cvm-ed[data-layout="suite"] .cvm-ed-files,
.cvm-ed[data-layout="suite"] .cvm-ed-audio { overflow: auto; }
/* align-items:stretch on the grid would stretch the stage's children to the
   full row height — and .cvm-video has aspect-ratio:16/9, so a stretched height
   drives its WIDTH instead of the other way round and it blows out of the
   column. Packing the stage's contents to the start keeps width authoritative. */
.cvm-ed[data-layout="suite"] .cvm-ed-stage { align-content: start; }
/* display:contents dissolves the rail wrapper so its two panels become grid
   items in their own right — no duplicated markup for the second layout. */
.cvm-ed[data-layout="suite"] .cvm-ed-side   { display: contents; }
.cvm-ed[data-layout="suite"] .cvm-rail-tabs { display: none; }
.cvm-ed[data-layout="suite"] .cvm-ed-stage    { grid-area: stage; }
.cvm-ed[data-layout="suite"] .cvm-ed-timeline { grid-area: timeline; }
.cvm-ed[data-layout="suite"] .cvm-ed-files,
.cvm-ed[data-layout="suite"] .cvm-ed-audio {
  border: 1px solid var(--cv-border);
  border-radius: var(--cv-radius-lg);
  background: var(--cv-bg-surface);
}
.cvm-ed[data-layout="suite"] .cvm-ed-files { grid-area: files; }
.cvm-ed[data-layout="suite"] .cvm-ed-audio { grid-area: audio; }
/* In Suite the timeline is the full width of the page, so it can afford a
   taller waveform — more vertical resolution for the same horizontal gain. */
.cvm-ed[data-layout="suite"] .cvm-wave { height: 92px; }
.cvm-ed[data-layout="suite"] .cvm-tr-inline { max-height: 88px; }

/* Below the three-column threshold both layouts collapse to one column, so the
   switch simply has no effect rather than producing a broken narrow grid. */
@media (max-width: 1240px) {
  .cvm-ed[data-layout="split"],
  .cvm-ed[data-layout="suite"] {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "stage" "timeline" "files" "audio";
  }
  .cvm-ed[data-layout="suite"] .cvm-wave { height: 68px; }
}

/* ============================================================== inspector rail
   Two tabs rather than one long stack: FILES (room files + render queue) and
   AUDIO (channel strip + transcript). The left column stays purely the edit —
   stage, variants, timeline — and the rail is the inspector for it. */
.cvm-rail { border: 1px solid var(--cv-border); border-radius: var(--cv-radius-lg); background: var(--cv-bg-surface); overflow: hidden; }
.cvm-rail-tabs { display: flex; border-bottom: 1px solid var(--cv-border); }
.cvm-rail-tab {
  flex: 1;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--cv-space-2);
  height: 38px; padding: 0 var(--cv-space-2);
  border: none; border-bottom: 2px solid transparent;
  background: none; color: var(--cv-text-secondary);
  font: inherit; font-size: var(--cv-text-xs); font-weight: var(--cv-weight-medium);
  cursor: pointer;
  transition: color var(--cv-dur-fast) var(--cv-ease), border-color var(--cv-dur-fast) var(--cv-ease);
}
.cvm-rail-tab:hover { color: var(--cv-text); background: var(--cv-bg-hover); }
.cvm-rail-tab[aria-selected="true"] { color: var(--cv-accent); border-bottom-color: var(--cv-accent); }
.cvm-rail-body { padding: var(--cv-space-2); display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--cv-space-2); }

/* ================================================================ channel strip
   Vertical signal flow, the way a console strip reads: each processor is a band
   with its own response graph and controls, and the output fader sits at the
   bottom. Replaces the four side-by-side modules — same components, laid out
   the way an audio operator expects to scan them. */
.cvm-strip { display: grid; gap: 0; }
.cvm-strip .cvm-mods { grid-template-columns: 1fr; gap: 0; }
.cvm-strip .cvm-mod { border-width: 0 0 1px 0; border-radius: 0; background: none; }
.cvm-strip .cvm-mod:last-of-type { border-bottom: none; }
.cvm-strip .cvm-mod-head { border-bottom: none; padding-bottom: 0; }
.cvm-strip .cvm-mod-body { padding-top: var(--cv-space-1); }
/* Squashed vertically so four bands plus the output section fit a scan without
   a long scroll. Curves read fine non-uniformly scaled. */
.cvm-strip .cvm-graph svg { height: 64px; }

.cvm-strip-out {
  display: grid; grid-template-columns: auto 1fr; gap: var(--cv-space-3);
  padding: var(--cv-space-2);
  border-top: 1px solid var(--cv-border);
  background: var(--cv-bg-raised);
}
.cvm-fader { display: grid; justify-items: center; gap: 3px; }
.cvm-fader input[type=range] {
  writing-mode: vertical-lr; direction: rtl;      /* vertical throw */
  width: 22px; height: 92px; margin: 0;
  accent-color: var(--cv-accent); cursor: ns-resize;
}
.cvm-fader-num {
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-xs); color: var(--cv-text);
}
.cvm-fader-cap {
  font-size: 9px; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase; color: var(--cv-text-muted);
}

.cvm-stage { display: flex; gap: var(--cv-space-2); }
.cvm-video {
  position: relative; flex: 1; min-width: 0;
  aspect-ratio: 16 / 9;
  display: grid; place-items: center;
  background: var(--cv-bg-inset);
  border: 1px solid var(--cv-border);
  border-radius: var(--cv-radius);
  color: var(--cv-text-disabled);
}
.cvm-vu { display: flex; gap: 3px; align-items: stretch; }

/* Timeline. The trim range is a filled band with grab handles; everything
   outside it is masked, so the kept region is obvious at a glance. */
.cvm-tl { position: relative; user-select: none; }
.cvm-ruler {
  display: flex; justify-content: space-between;
  padding: 0 0 var(--cv-space-1);
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-2xs); color: var(--cv-text-muted);
}
.cvm-wave {
  position: relative;
  height: 68px;
  display: flex; align-items: center; gap: 1px;
  padding: 0 1px;
  background: var(--cv-bg-inset);
  border: 1px solid var(--cv-border);
  border-radius: var(--cv-radius);
  overflow: hidden;
}
.cvm-wave-bar { flex: 1; min-width: 1px; background: var(--cv-accent-dim); border-radius: 1px; }
.cvm-mask { position: absolute; top: 0; bottom: 0; background: var(--cv-bg-page); opacity: 0.72; pointer-events: none; }
.cvm-handle {
  position: absolute; top: 0; bottom: 0; width: 9px;
  background: var(--cv-accent);
  cursor: ew-resize;
  display: grid; place-items: center;
}
.cvm-handle::after { content: ""; width: 1px; height: 16px; background: var(--cv-on-accent); opacity: 0.75; }
.cvm-playhead { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--cv-text); pointer-events: none; }
.cvm-playhead::before {
  content: ""; position: absolute; top: -1px; left: 50%; translate: -50% 0;
  border: 5px solid transparent; border-top-color: var(--cv-text);
}

/* Editor waveform: real peaks rendered as prototype cvm-wave-bar flex spans.
   The bar layer + hint sit absolutely inside .cvm-wave so the trim overlay
   (masks / handles / playhead) can stack on top. The scroll wrapper lets the
   1× / 4× / 12× zoom widen the wave without growing the page. Flex bars reflow
   to the container automatically — no canvas sizing / squished-after-layout. */
.ed-wave-scroll { overflow-x: auto; overflow-y: hidden; }
.ed-wave-scroll::-webkit-scrollbar { height: 8px; }
.ed-wave-scroll::-webkit-scrollbar-track { background: var(--cv-bg-inset); border-radius: 4px; }
.ed-wave-scroll::-webkit-scrollbar-thumb { background: var(--cv-border); border-radius: 4px; }
.ed-wave-scroll::-webkit-scrollbar-thumb:hover { background: var(--cv-accent); }
.ed-wave-bars { position: absolute; inset: 0; display: flex; align-items: center; gap: 1px; padding: 0 1px; }
.cvm-wave-bar.ed-wb-warm { background: var(--cv-warning); }
.cvm-wave-bar.ed-wb-hot  { background: var(--cv-danger); }
.ed-wave-hint {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: var(--cv-text-2xs); color: var(--cv-text-disabled); pointer-events: none; text-align: center; padding: 0 var(--cv-space-2);
}

/* Transcript — click a word to seek, active word follows playback. */
.cvm-tr { max-height: 132px; overflow-y: auto; padding: var(--cv-space-2); background: var(--cv-bg-inset); border: 1px solid var(--cv-border); border-radius: var(--cv-radius); line-height: 1.9; }
.cvm-w { padding: 1px 3px; border-radius: var(--cv-radius-sm); cursor: pointer; }
.cvm-w:hover { background: var(--cv-bg-active); }
.cvm-w[aria-current="true"] { background: var(--cv-accent); color: var(--cv-on-accent); }

/* ===================================================== audio cleanup rack
   Design intent: each processor SHOWS ITS TRANSFER FUNCTION, not just its
   parameters. A compressor that draws its own curve and reports live gain
   reduction is legible to an audio operator in a way a labelled slider never
   is — and a response curve is information design, not skeuomorphism, so it
   stays inside the Swiss/minimal language. No brushed metal, no fake screws,
   no drop shadows. Geometry stays near-square; the only circles are the dials,
   drawn as plain arcs.

   Everything below is renderable from data the editor's existing Web Audio
   graph already produces (`_edEnsureVUPipeline`, editor.js:1372):
   AnalyserNode → spectrum, DynamicsCompressorNode.reduction → GR meter. */

.cvm-rack { display: grid; gap: var(--cv-space-2); }

/* ---- signal chain strip -------------------------------------------------
   Processing order is real and audible — EQ before compression is not the same
   as after. Today the chain is invisible and the operator has to infer it. */
.cvm-chain {
  display: flex; align-items: center; gap: var(--cv-space-1); flex-wrap: wrap;
  padding: var(--cv-space-2);
  background: var(--cv-bg-inset);
  border: 1px solid var(--cv-border-subtle);
  border-radius: var(--cv-radius);
}
.cvm-chain-node {
  display: inline-flex; align-items: center; gap: var(--cv-space-1);
  padding: 2px var(--cv-space-2);
  border: 1px solid var(--cv-border);
  border-radius: var(--cv-radius-sm);
  background: var(--cv-bg-surface);
  font-size: var(--cv-text-2xs);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
  color: var(--cv-text-muted);
  white-space: nowrap;
}
.cvm-chain-node[data-on="true"] { color: var(--cv-text); border-color: var(--cv-border-strong); }
.cvm-chain-node[data-on="true"] .cvm-chain-led { background: var(--cv-accent); }
.cvm-chain-node[data-on="false"] { opacity: 0.5; }
.cvm-chain-node[data-fixed="true"] { border-style: dashed; }
.cvm-chain-led { width: 5px; height: 5px; border-radius: var(--cv-radius-full); background: var(--cv-text-disabled); flex: none; }
.cvm-chain-arrow { color: var(--cv-text-disabled); flex: none; }

/* ---- module -------------------------------------------------------------- */
.cvm-mods { display: grid; gap: var(--cv-space-2); grid-template-columns: repeat(auto-fit, minmax(236px, 1fr)); }

.cvm-mod {
  display: flex; flex-direction: column;
  border: 1px solid var(--cv-border);
  border-radius: var(--cv-radius-lg);
  background: var(--cv-bg-surface);
  overflow: hidden;
}
.cvm-mod[data-on="true"] { border-color: var(--cv-border-strong); }
.cvm-mod-head {
  display: flex; align-items: center; gap: var(--cv-space-2);
  padding: var(--cv-space-2);
  border-bottom: 1px solid var(--cv-border-subtle);
}
.cvm-mod-name {
  font-size: var(--cv-text-2xs); font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
}
/* LIVE vs ON RENDER is invisible today, yet it is the most consequential fact
   about these four modules: EQ and the compressor audition through Web Audio as
   you drag; the gate and de-noiser only apply at render. An operator who does
   not know that will try to tune a gate by ear and hear nothing change. */
.cvm-mod-when {
  padding: 0 4px; border-radius: var(--cv-radius-sm);
  font-size: 9px; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
}
.cvm-mod-when[data-when="live"]   { background: var(--cv-success-wash); color: var(--cv-success); }
.cvm-mod-when[data-when="render"] { background: var(--cv-neutral-wash); color: var(--cv-neutral); }

.cvm-mod-body { padding: var(--cv-space-2); display: grid; gap: var(--cv-space-2); }
.cvm-mod[data-on="false"] .cvm-mod-body { opacity: 0.38; pointer-events: none; }
.cvm-mod[data-on="false"] .cvm-graph { filter: grayscale(1); }

/* ---- graph --------------------------------------------------------------- */
.cvm-graph {
  position: relative;
  background: var(--cv-bg-inset);
  border: 1px solid var(--cv-border-subtle);
  border-radius: var(--cv-radius-sm);
  overflow: hidden;
}
.cvm-graph svg { display: block; width: 100%; height: auto; }
.cvm-g-grid   { stroke: var(--cv-border); stroke-width: 1; }
.cvm-g-axis   { stroke: var(--cv-border-strong); stroke-width: 1; stroke-dasharray: 2 3; }
.cvm-g-spec   { fill: var(--cv-accent); opacity: 0.16; }
.cvm-g-curve  { stroke: var(--cv-accent); stroke-width: 2; fill: none; stroke-linejoin: round; stroke-linecap: round; }
.cvm-g-unity  { stroke: var(--cv-text-disabled); stroke-width: 1; stroke-dasharray: 3 3; fill: none; }
.cvm-g-fill   { fill: var(--cv-accent); opacity: 0.10; }
.cvm-g-cut    { fill: var(--cv-danger); opacity: 0.14; }
.cvm-g-thresh { stroke: var(--cv-warning); stroke-width: 1.5; stroke-dasharray: 3 2; }
.cvm-g-label  { fill: var(--cv-text-disabled); font-family: var(--cv-font-mono); font-size: 7px; }
.cvm-g-node   { fill: var(--cv-accent); stroke: var(--cv-bg-inset); stroke-width: 2; cursor: grab; }
.cvm-g-node:active { cursor: grabbing; }

/* Live readout floated over a graph corner — a value you watch, not one you set. */
.cvm-g-live {
  position: absolute; top: 3px; right: 4px;
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-2xs); color: var(--cv-text-secondary);
}
.cvm-graph[data-gr="true"] .cvm-g-live { right: 16px; }

/* Gain-reduction meter pinned to the compressor graph. Reads DOWNWARD from
   0 dB, the way every hardware GR meter does. */
.cvm-gr { position: absolute; top: 0; right: 0; bottom: 0; width: 12px; border-left: 1px solid var(--cv-border-subtle); background: var(--cv-bg-page); }
.cvm-gr-fill { position: absolute; top: 0; left: 0; right: 0; background: var(--cv-warning); opacity: 0.85; transition: height var(--cv-dur-fast) linear; }
.cvm-gr-cap  { position: absolute; left: 0; right: 0; height: 1px; background: var(--cv-danger); }

/* ---- dial ---------------------------------------------------------------
   One arc dial per module, for the headline parameter. Circles are fine in this
   language — near-square is about corners, not about banning curves — and an
   arc reads as audio gear with no skeuomorphic texture.

   The native range input underneath is the REAL control, visually collapsed but
   still focusable — so keyboard, screen readers and drag all work for free
   rather than needing a custom pointer implementation. */
.cvm-dial { position: relative; display: grid; justify-items: center; gap: 2px; }
.cvm-dial svg { width: 46px; height: 46px; display: block; }
/* The input covers the whole dial face, so you drag the dial itself rather than
   a separate strip. It stays a native range input — keyboard arrows, Home/End
   and screen-reader value announcements all work without custom code. */
.cvm-dial:has(:focus-visible) { border-radius: var(--cv-radius); box-shadow: var(--cv-focus-ring); }
.cvm-dial-track { fill: none; stroke: var(--cv-bg-active); stroke-width: 4; stroke-linecap: round; }
.cvm-dial-val   { fill: none; stroke: var(--cv-accent); stroke-width: 4; stroke-linecap: round;
                  transition: stroke-dasharray var(--cv-dur-fast) var(--cv-ease); }
.cvm-dial-num {
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-xs); font-weight: var(--cv-weight-medium);
  fill: var(--cv-text);
}
.cvm-dial-cap {
  font-size: var(--cv-text-2xs); color: var(--cv-text-muted);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
}
.cvm-dial input {
  position: absolute; top: 0; left: 50%; translate: -50% 0;
  width: 46px; height: 46px; margin: 0;
  opacity: 0; cursor: ew-resize;
}

/* ---- parameter row ------------------------------------------------------- */
.cvm-params { display: grid; gap: var(--cv-space-2); grid-template-columns: auto 1fr; align-items: start; }
.cvm-plist { display: grid; gap: 4px; align-content: start; }
.cvm-p {
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: var(--cv-space-2);
  font-size: var(--cv-text-2xs); color: var(--cv-text-muted);
}
.cvm-p b {
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-weight: var(--cv-weight-normal); color: var(--cv-text-secondary);
}
.cvm-p input[type=range] { grid-column: 1 / -1; width: 100%; height: 3px; accent-color: var(--cv-accent); }

/* ---- master strip -------------------------------------------------------- */
.cvm-master { display: flex; align-items: center; gap: var(--cv-space-3); flex-wrap: wrap; }
.cvm-lufs { display: flex; gap: var(--cv-space-3); font-size: var(--cv-text-2xs); color: var(--cv-text-muted); }
.cvm-lufs b { font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums; font-weight: var(--cv-weight-normal); color: var(--cv-text-secondary); }
.cvm-lufs [data-warn="true"] { color: var(--cv-warning); }

/* Shared slider — also used by File Compare's tolerance / hard-limit controls. */
.cvm-slider { display: grid; grid-template-columns: 1fr auto; gap: var(--cv-space-2); align-items: center; font-size: var(--cv-text-2xs); color: var(--cv-text-muted); }
.cvm-slider input { width: 100%; accent-color: var(--cv-accent); }
.cvm-slider b { font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums; font-weight: var(--cv-weight-normal); color: var(--cv-text-secondary); }

/* ================================================================ transfer */

.cvm-tp { display: grid; gap: var(--cv-space-1); padding: var(--cv-space-2) 0; }
.cvm-tp-row { display: grid; grid-template-columns: 1fr 90px 74px; gap: var(--cv-space-2); align-items: center; font-size: var(--cv-text-xs); }
.cvm-rate { font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums; font-size: var(--cv-text-2xs); color: var(--cv-text-muted); text-align: right; }

/* ============================================================ cloud upload */

.cvm-site {
  display: flex; align-items: center; gap: var(--cv-space-2);
  padding: var(--cv-space-2);
  border: 1px solid var(--cv-border); border-radius: var(--cv-radius);
  background: var(--cv-bg-surface); cursor: pointer;
}
.cvm-site[aria-selected="true"] { border-color: var(--cv-accent); background: var(--cv-accent-wash); }
.cvm-q-row {
  display: grid; grid-template-columns: auto 1fr 200px auto; gap: var(--cv-space-2); align-items: center;
  min-height: var(--cv-row); padding: 0 var(--cv-space-2);
  border-bottom: 1px solid var(--cv-border-subtle);
}
.cvm-q-row:last-child { border-bottom: none; }
@media (max-width: 860px) { .cvm-q-row { grid-template-columns: auto 1fr auto; } .cvm-q-row .cv-select { grid-column: 2 / -1; } }

/* ==================================================== VU meters with readouts
   Scale down the left, peak-hold tick, numeric dBFS under each channel and a
   clip LED above it. The app currently has three unconnected VU meters (OBS
   card, OBS preview modal, Captions) all mapping the same -60..0 dB range with
   the same 7 ticks, and none of them shows a number — so an operator can see
   that a channel is hot but not by how much. */
.cvm-vu { display: flex; gap: 3px; align-items: stretch; }
.cvm-vu-scale {
  display: flex; flex-direction: column; justify-content: space-between;
  padding-bottom: 26px;                       /* clears the numbers + labels */
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: 8px; line-height: 1; color: var(--cv-text-disabled); text-align: right;
}
.cvm-vu-ch { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.cvm-vu-ch .cv-meter { flex: 1; width: 10px; position: relative; }
.cvm-vu-led {
  width: 10px; height: 3px; border-radius: 1px;
  background: var(--cv-bg-active);
}
.cvm-vu-led[data-clip="true"] { background: var(--cv-danger); }
.cvm-vu-peak { position: absolute; left: 0; right: 0; height: 1px; background: var(--cv-text); opacity: 0.8; }
.cvm-vu-num {
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: 9px; line-height: 1; color: var(--cv-text-secondary);
  /* Fixed width so the changing dBFS readout (e.g. -40.0 → +5.0 → –∞) never
     resizes the VU channel / stage / window as levels move. */
  min-width: 34px; width: 34px; text-align: center; box-sizing: border-box;
}
.cvm-vu-num[data-hot="true"] { color: var(--cv-warning); }
.cvm-vu-lbl {
  font-size: 9px; line-height: 1; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); color: var(--cv-text-muted);
}

/* ===================================================== source variant switcher
   A capture session is normally several files — MAIN, BACKUP and one or more
   ISOs (camera, graphics). Switching between them keeps the playhead and every
   trim decision, so you can check a suspected dropout against the backup
   without losing your work. */
.cvm-vars { display: flex; gap: var(--cv-space-1); flex-wrap: wrap; }
.cvm-var {
  display: grid; gap: 1px;
  padding: var(--cv-space-1) var(--cv-space-2);
  border: 1px solid var(--cv-border);
  border-radius: var(--cv-radius);
  background: var(--cv-bg-surface);
  color: var(--cv-text-secondary);
  font: inherit; text-align: left; cursor: pointer;
  max-width: 210px; min-width: 0;
  transition: background var(--cv-dur-fast) var(--cv-ease), border-color var(--cv-dur-fast) var(--cv-ease);
}
.cvm-var:hover { background: var(--cv-bg-hover); }
.cvm-var[aria-pressed="true"] { border-color: var(--cv-accent); background: var(--cv-accent-wash); color: var(--cv-text); }
.cvm-var-k {
  display: flex; align-items: center; gap: var(--cv-space-1);
  font-size: 9px; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
}
.cvm-var-d {
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-2xs); color: var(--cv-text-muted);
}
.cvm-var[data-status="missing"] { opacity: 0.45; cursor: not-allowed; border-style: dashed; }
.cvm-var[data-status="short"] .cvm-var-d { color: var(--cv-warning); }

/* ========================================================= multi-segment cut
   Trim handles bound the clip; cut regions remove spans from the middle and the
   remainder is joined. Kept segments are numbered on the timeline and listed
   below with their own durations, so the output length is never a surprise. */
.cvm-cut {
  position: absolute; top: 0; bottom: 0;
  background: var(--cv-danger-wash);
  border-left: 1px dashed var(--cv-danger);
  border-right: 1px dashed var(--cv-danger);
  cursor: grab;
}
.cvm-cut::after {
  content: attr(data-len);
  position: absolute; top: 2px; left: 50%; translate: -50% 0;
  font-family: var(--cv-font-mono); font-size: 8px; color: var(--cv-danger);
  white-space: nowrap;
}
.cvm-segno {
  position: absolute; bottom: 2px;
  padding: 0 3px; border-radius: var(--cv-radius-sm);
  background: var(--cv-accent); color: var(--cv-on-accent);
  font-family: var(--cv-font-mono); font-size: 8px; line-height: 1.5;
  pointer-events: none;
}
.cvm-segs { display: grid; gap: 2px; }
.cvm-seg-row {
  display: grid; grid-template-columns: 22px 1fr auto auto; gap: var(--cv-space-2);
  align-items: center;
  min-height: 24px; padding: 0 var(--cv-space-2);
  border-radius: var(--cv-radius-sm);
  font-size: var(--cv-text-2xs);
}
.cvm-seg-row:hover { background: var(--cv-bg-hover); }
.cvm-seg-row[data-kind="keep"] .cvm-seg-n {
  display: grid; place-items: center; height: 15px;
  border-radius: var(--cv-radius-sm);
  background: var(--cv-accent); color: var(--cv-on-accent);
  font-family: var(--cv-font-mono); font-size: 9px;
}
.cvm-seg-row[data-kind="cut"] { color: var(--cv-danger); }
.cvm-seg-row[data-kind="cut"] .cvm-seg-n { display: grid; place-items: center; color: var(--cv-danger); }
.cvm-seg-t { font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums; color: var(--cv-text-secondary); }
.cvm-seg-row[data-kind="cut"] .cvm-seg-t { color: inherit; text-decoration: line-through; }

/* ================================================= shared file-kind filters
   Used by BOTH the editor's room-files panel and File Compare, so the vocabulary
   an operator learns in one place carries to the other. */
.cvm-kind-filters { display: flex; flex-wrap: wrap; gap: 2px; align-items: center; }
.cvm-kind-tog {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 2px 5px;
  border: 1px solid var(--cv-border);
  border-radius: var(--cv-radius-sm);
  font-size: 9px; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
  color: var(--cv-text-muted);
  cursor: pointer; user-select: none;
}
.cvm-kind-tog input { width: 10px; height: 10px; margin: 0; accent-color: var(--cv-accent); }
.cvm-kind-tog:has(input:checked) { color: var(--cv-text); border-color: var(--cv-border-strong); background: var(--cv-bg-raised); }
.cvm-rf-sess {
  padding: var(--cv-space-1) var(--cv-space-2) 2px;
  font-size: 9px; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
  color: var(--cv-text-disabled);
}
.cvm-rf-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--cv-space-1);
  align-items: center;
  min-height: 22px; padding: 0 var(--cv-space-2);
  border-radius: var(--cv-radius-sm); cursor: pointer;
}
.cvm-rf-row:hover { background: var(--cv-bg-hover); }
.cvm-rf-row[aria-current="true"] { background: var(--cv-accent-wash); box-shadow: inset 2px 0 0 var(--cv-accent); }
.cvm-rf-kind {
  padding: 0 3px; border-radius: var(--cv-radius-sm);
  font-family: var(--cv-font-mono); font-size: 8px;
  background: var(--cv-bg-active); color: var(--cv-text-muted);
}
.cvm-rf-row[data-kind="main"]   .cvm-rf-kind { background: var(--cv-accent-wash);  color: var(--cv-accent); }
.cvm-rf-row[data-kind="backup"] .cvm-rf-kind { background: var(--cv-warning-wash); color: var(--cv-warning); }
.cvm-rf-row[data-kind="iso"]    .cvm-rf-kind { background: var(--cv-info-wash);    color: var(--cv-info); }
.cvm-rf-row[data-kind="edited"] .cvm-rf-kind { background: var(--cv-success-wash); color: var(--cv-success); }

/* ======================================================== transcript search
   Find-in-transcript with match cycling. Every hit is marked; the active hit is
   inverted and its timestamp shown, so cycling doubles as a way to navigate the
   clip by content rather than by scrubbing. */
.cvm-tr-bar { display: grid; grid-template-columns: 1fr auto; gap: var(--cv-space-1); align-items: center; }
.cvm-tr-nav { display: flex; align-items: center; gap: 2px; }
.cvm-tr-count {
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-2xs); color: var(--cv-text-muted);
  min-width: 44px; text-align: right;
}
.cvm-tr-count[data-none="true"] { color: var(--cv-danger); }
.cvm-w[data-hit="true"] { background: var(--cv-warning-wash); box-shadow: inset 0 -1px 0 var(--cv-warning); }
.cvm-w[data-cur="true"] { background: var(--cv-accent); color: var(--cv-on-accent); box-shadow: none; }
.cvm-tr-jump {
  display: flex; align-items: center; gap: var(--cv-space-1);
  font-size: var(--cv-text-2xs); color: var(--cv-text-muted);
}
.cvm-tr-jump b {
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-weight: var(--cv-weight-normal); color: var(--cv-accent);
}

/* ============================================== transcript ↔ waveform linkage
   The transcript sits directly under the waveform because they are two views of
   the same timeline. That adjacency earns its keep: a transcript search marks
   every hit ON the waveform, so "where in this hour does she say 'schedule'"
   becomes a glance instead of a scrub. */
.cvm-trmark {
  position: absolute; top: 0;
  width: 2px; height: 9px;
  background: var(--cv-warning);
  cursor: pointer;
  z-index: 2;
}
.cvm-trmark::after {                 /* widened hit area, no visual weight */
  content: ""; position: absolute; inset: -4px -5px;
}
.cvm-trmark[data-cur="true"] { background: var(--cv-accent); height: 17px; width: 2px; }
.cvm-trmark[data-cur="true"]::before {
  content: ""; position: absolute; left: -2px; top: 17px;
  border: 3px solid transparent; border-top-color: var(--cv-accent);
}

/* Inline transcript under the waveform — wider than the rail version was, so
   it reads as prose rather than a narrow column. */
.cvm-tr-inline { max-height: 108px; }

.cvm-kind-label {
  font-size: 9px; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
  color: var(--cv-text-disabled); margin-right: 2px;
}
.cvm-kind-sep { width: 1px; height: 14px; background: var(--cv-border); margin: 0 var(--cv-space-1); }
/* Modifier toggles read as constraints rather than inclusions, so they take the
   warning hue when active — "I am hiding things" not "I am showing things". */
.cvm-kind-tog[data-only="true"]:has(input:checked) {
  color: var(--cv-warning); border-color: var(--cv-warning-edge); background: var(--cv-warning-wash);
}

/* ============================================================================
   Sidebar — tightened "Console" restyle
   ----------------------------------------------------------------------------
   Re-skins the EXISTING `.sidebar` / `.nav-item` shell (styles.css) to the
   `cvm-side` aesthetic: tighter 34px rows, sprite SVG icons that tint with
   state (currentColor), an accent-wash active row with an inset accent bar,
   and `--cv-*` tokens. Router wiring is untouched — .nav-item[data-page],
   .active toggling, toggleSidebar()/collapse, applyMonitorNavVisibility(),
   the nav-agent/nav-tasks/nav-users ids and the mobile drawer all still work.
   Loaded after styles.css so equal-or-higher specificity wins.
   ============================================================================ */
.sidebar {
  background: var(--cv-bg-surface);
  border-right: 1px solid var(--cv-border);
}
.sidebar .sidebar-header {
  padding: 0 var(--cv-space-3);
  border-bottom: 1px solid var(--cv-border-subtle);
}
.sidebar .logo { gap: var(--cv-space-2); }
.sidebar .logo-icon {
  width: 28px; height: 28px;
  border-radius: var(--cv-radius-sm);
  box-shadow: none;
  overflow: hidden;
}
.sidebar .logo-icon img { width: 22px; height: 22px; filter: none; }
.sidebar .logo-text {
  font-size: var(--cv-text-md);
  font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-tight);
  color: var(--cv-text);
}
.sidebar .sidebar-toggle {
  width: 26px; height: 26px; padding: 0;
  background: none; border: 1px solid transparent;
  color: var(--cv-text-muted);
  border-radius: var(--cv-radius-sm);
}
.sidebar .sidebar-toggle:hover {
  background: var(--cv-bg-hover); color: var(--cv-text); border-color: var(--cv-border);
}
.sidebar .sidebar-header:hover .sidebar-toggle { border-color: transparent; color: var(--cv-text-muted); }

/* Nav list — tight rows, 1px gutter. */
.sidebar .sidebar-nav {
  flex: 1; overflow-y: auto;
  padding: var(--cv-space-2);
  display: grid; gap: 1px; align-content: start;
}
.sidebar .nav-item {
  height: var(--cv-row);
  min-height: var(--cv-row);
  padding: 0 var(--cv-space-2);
  gap: var(--cv-space-2);
  margin: 0;
  border-radius: var(--cv-radius-sm);
  color: var(--cv-text-secondary);
  font-size: var(--cv-text-sm);
  font-weight: var(--cv-weight-medium);
}
.sidebar .nav-item:hover { background: var(--cv-bg-hover); color: var(--cv-text); }
.sidebar .nav-item.active {
  background: var(--cv-accent-wash);
  color: var(--cv-accent);
  font-weight: var(--cv-weight-semibold);
  box-shadow: inset 2px 0 0 var(--cv-accent);
}
/* Icon holder — sized for the sprite SVG (which inherits currentColor, so the
   active row's icon turns accent along with its label). filter:none clears the
   old PNG contrast() hack. */
.sidebar .nav-icon {
  width: 20px; height: 20px; flex: none;
  display: grid; place-items: center;
  filter: none; font-size: 0;
}
.sidebar .nav-icon svg { width: 20px; height: 20px; }
.sidebar .nav-text { font-size: var(--cv-text-sm); }
.sidebar .nav-divider { background: var(--cv-border-subtle); margin: var(--cv-space-2) var(--cv-space-1); }

/* Small caps section label (e.g. "Admin") above the footer actions. */
.sidebar .sidebar-nav-label {
  padding: var(--cv-space-2) var(--cv-space-2) var(--cv-space-1);
  font-size: var(--cv-text-2xs); font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
  color: var(--cv-text-disabled);
}

/* Footer */
.sidebar .sidebar-footer {
  padding: var(--cv-space-2);
  border-top: 1px solid var(--cv-border-subtle);
}
.sidebar .sidebar-footer-actions {
  gap: var(--cv-space-1);
  margin-bottom: var(--cv-space-2);
  padding-bottom: var(--cv-space-2);
  border-bottom: 1px solid var(--cv-border-subtle);
}
.sidebar .sidebar-footer-link {
  height: var(--cv-row-dense); min-height: var(--cv-row-dense);
  padding: 0 var(--cv-space-2);
  gap: var(--cv-space-2);
  font-size: var(--cv-text-xs);
  border-radius: var(--cv-radius-sm);
}
.sidebar .sidebar-footer-link .nav-icon { width: 16px; height: 16px; }
.sidebar .sidebar-footer-link .nav-icon svg { width: 16px; height: 16px; }
.sidebar .user-profile { gap: var(--cv-space-2); margin-bottom: var(--cv-space-2); }
.sidebar .avatar {
  width: 28px; height: 28px;
  background: var(--cv-bg-active); color: var(--cv-text);
  font-size: var(--cv-text-xs); font-weight: var(--cv-weight-semibold);
  box-shadow: none;
}
.sidebar .user-name { font-size: var(--cv-text-sm); color: var(--cv-text); font-weight: var(--cv-weight-medium); }
.sidebar .user-role { font-size: var(--cv-text-2xs); color: var(--cv-text-muted); }
.sidebar .sidebar-footer-logout { width: 100%; justify-content: center; }

/* Collapsed rail — hide the section label (nav-text is already hidden by the
   existing collapsed rules; icons + tooltips carry the rail). */
.sidebar.collapsed .sidebar-nav-label { display: none; }
.sidebar.collapsed .nav-icon,
.sidebar.collapsed .sidebar-footer-link .nav-icon { width: 20px; height: 20px; }
.sidebar.collapsed .sidebar-footer-link .nav-icon svg { width: 20px; height: 20px; }

/* ============================================================================
   New File Transfer modal — cv- migrated internals (namespace cvm-)
   Built by batch-tools.js showCaptureTransferModal(), rendered in the shared
   openModal() frame. These classes modernize the machine checklist, the
   selection summary/metric chips, and the radio groups.
   ============================================================================ */
.cvm-checklist {
  max-height: 260px; overflow-y: auto;
  border: 1px solid var(--cv-border); border-radius: var(--cv-radius);
  background: var(--cv-bg-inset); padding: var(--cv-space-2);
}
.cvm-checklist .transfer-group { margin-bottom: var(--cv-space-3); }
.cvm-checklist .transfer-group:last-child { margin-bottom: 0; }
.cvm-checklist .transfer-machine-item {
  padding: 3px 4px; border-radius: var(--cv-radius-sm); cursor: pointer;
}
.cvm-checklist .transfer-machine-item:hover { background: var(--cv-bg-hover); }

.cvm-xfer-summary {
  font-size: var(--cv-text-sm); color: var(--cv-text-secondary);
  padding: 6px var(--cv-space-2); margin-bottom: var(--cv-space-2);
  border: 1px solid var(--cv-border); border-radius: var(--cv-radius);
  background: var(--cv-bg-inset);
}
.cvm-xfer-metric {
  font-family: var(--cv-font-mono); font-size: var(--cv-text-sm);
  color: var(--cv-text-secondary);
  padding: 6px var(--cv-space-2);
  border: 1px solid var(--cv-border); border-radius: var(--cv-radius);
  background: var(--cv-bg-inset);
}

/* Radio groups → segmented, tappable rows */
.cvm-xfer-radios { display: flex; flex-wrap: wrap; gap: var(--cv-space-1); }
.cvm-radio {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; min-height: 34px; cursor: pointer;
  font-size: var(--cv-text-sm); color: var(--cv-text-secondary);
  border: 1px solid var(--cv-border); border-radius: var(--cv-radius);
  background: var(--cv-bg-surface);
  transition: border-color var(--cv-dur-fast) var(--cv-ease), color var(--cv-dur-fast) var(--cv-ease);
}
.cvm-radio:hover { border-color: var(--cv-border-strong); }
.cvm-radio input { accent-color: var(--cv-accent); }
.cvm-radio:has(input:checked) { border-color: var(--cv-accent); color: var(--cv-text); background: var(--cv-accent-wash); }
