/* public/stream/sec-obs-live.css — Live OBS Studio sub-tab (§09 Phase 1).
   Tokens + component classes only; see docs/design/DESIGN-GUIDELINES.md. */
.obslive { display: flex; flex-direction: column; gap: var(--space-3); }
.obslive__scene { font-weight: 700; color: var(--text); }

/* ── Multi-region workspace (OBS-like): a central PREVIEW with four EDGE regions
   (left / right / top / bottom) around it, ALL visible at once. Each dock (Scenes /
   Sources / Audio mixer) is assigned to an edge (a per-dock picker in its header); edges
   are drag-resizable and empty edges collapse to 0. The workspace height is fitted to the
   viewport in JS. Assignment + edge sizes persist (localStorage). ── */
.obslive__ws {
  display: grid;
  width: 100%;
  min-height: 440px;
  grid-template-columns: var(--ed-left, 0px) minmax(0, 1fr) var(--ed-right, 0px);
  grid-template-rows: var(--ed-top, 0px) minmax(0, 1fr) var(--ed-bottom, 0px);
  grid-template-areas: "top top top" "left center right" "bottom bottom bottom";
  gap: var(--space-2);
}

/* Layout controls bar (Reset). */
.obslive__ws-bar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.obslive__ws-bar .spacer { flex: 1 1 auto; }
/* Fixed tools slot in the ws-bar — hosts the in-view layout editor's "✥ Edit layout"
   toggle. It cannot sit next to the stage: inside the center pane the stage is
   position:absolute inset:0 and paints OVER any in-flow sibling (the invisible-toggle
   bug). Empty (web / no layout editor), the slot takes no space. */
.obslive__ws-tools { display: flex; align-items: center; }
.obslive__ws-bar .obslive__layoutbar { margin-bottom: 0; }

/* Preview — the central, dominant region. A 16:9 box sized to the largest that FITS
   (letterbox bars in the region, never distortion; layout boxes stay aligned to 16:9).
   The stage fits inside the pane MINUS a gutter on every side, so an oversized source's
   edge handles have somewhere to live and be grabbed (the layout-editor overlay mounts
   on THIS pane and spans the gutter). The explicit 1px boundary keeps the canvas edge
   readable against the surround now that the stage is no longer flush. WORKSPACE scope
   only — the standalone .obslive__preview card keeps its full-width fit. */
.obslive__ws-preview { --obslive-canvas-gutter: 48px; grid-area: center; position: relative; min-width: 120px; min-height: 120px; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.obslive__ws-preview .obslive__preview { position: absolute; inset: 0; margin: auto; width: auto; height: auto; aspect-ratio: 16 / 9; max-width: calc(100% - 2 * var(--obslive-canvas-gutter)); max-height: calc(100% - 2 * var(--obslive-canvas-gutter)); border: 1px solid var(--border); }

/* Edge regions — hold the docks assigned to that side. Left/right stack vertically;
   top/bottom lay out in a row. position:relative anchors the resize handle. */
.obslive__edge { position: relative; display: flex; gap: var(--space-2); min-width: 0; min-height: 0; overflow: hidden; }
.obslive__edge--left { grid-area: left; flex-direction: column; }
.obslive__edge--right { grid-area: right; flex-direction: column; }
.obslive__edge--top { grid-area: top; flex-direction: row; }
.obslive__edge--bottom { grid-area: bottom; flex-direction: row; }
.obslive__edge > .obslive__dock { flex: 1 1 0; min-width: 0; min-height: 0; }

/* Edge resize handle — a thin bar on the INNER border of an edge region (toward center). */
.obslive__edgesplit { position: absolute; z-index: 2; touch-action: none; }
.obslive__edgesplit::before { content: ""; position: absolute; inset: 0; margin: auto; background: var(--border); transition: background var(--dur, 180ms) var(--ease); }
.obslive__edgesplit:hover::before, .obslive__edgesplit.is-dragging::before { background: var(--accent); }
.obslive__edgesplit--v { top: 0; bottom: 0; width: 9px; cursor: col-resize; }
.obslive__edgesplit--v::before { width: 2px; top: 6px; bottom: 6px; }
.obslive__edgesplit--h { left: 0; right: 0; height: 9px; cursor: row-resize; }
.obslive__edgesplit--h::before { height: 2px; left: 6px; right: 6px; }
.obslive__edge--left .obslive__edgesplit { right: -6px; }
.obslive__edge--right .obslive__edgesplit { left: -6px; }
.obslive__edge--top .obslive__edgesplit { bottom: -6px; }
.obslive__edge--bottom .obslive__edgesplit { top: -6px; }
/* GROW handle on the bottom of the bottom dock: drag DOWN to make the bottom dock taller AND
   grow the whole workspace (the page scrolls) — the preview/center keeps its size. Sits fully
   inside the edge's bottom (so overflow:hidden doesn't clip it). */
.obslive__edgegrow { position: absolute; left: 0; right: 0; bottom: 0; height: 11px; z-index: 3; cursor: ns-resize; touch-action: none; }
.obslive__edgegrow::before { content: ""; position: absolute; left: 16px; right: 16px; bottom: 3px; height: 3px; border-radius: 2px; background: var(--border); transition: background var(--dur, 180ms) var(--ease); }
.obslive__edgegrow:hover::before, .obslive__edgegrow.is-dragging::before { background: var(--accent); }
/* Per-dock edge picker in the header. */
.obslive__dock-pos { font-size: 10px; padding: 1px 4px; color: var(--text-dim); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm); cursor: pointer; }

.obslive__dock { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--surface); overflow: hidden; }
.obslive__dock-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); font-family: var(--font-display); font-weight: 700; font-size: var(--fs-small, 12px); text-transform: uppercase; letter-spacing: .04em; color: var(--text-dim); padding: var(--space-1) var(--space-2) var(--space-1) var(--space-3); border-bottom: 1px solid var(--border); background: var(--surface-inset); }
.obslive__dock-head .obslive__dock-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.obslive__dock-body { padding: var(--space-1); flex: 1 1 auto; min-height: 80px; overflow-y: auto; display: flex; flex-direction: column; }
.obslive__dock-toolbar { display: flex; align-items: center; gap: 2px; padding: var(--space-1) var(--space-2); border-top: 1px solid var(--border); background: var(--surface-inset); min-height: 38px; }
/* Collapse the toolbar when a dock doesn't use it (Audio never does; Scenes only when editing
   a non-program scene) — otherwise it renders as an awkward empty black strip at the dock's
   bottom. Sources always has its action buttons, so its toolbar stays. */
.obslive__dock-toolbar:empty { display: none; }
.obslive__dock-action { margin-left: auto; }
.obslive__toolbar-sep { width: 1px; align-self: stretch; background: var(--border); margin: var(--space-1) 4px; }
.obslive__toolbtn { padding: 4px; min-width: 30px; height: 28px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; font-size: 14px; }
.obslive__toolbtn:disabled { opacity: .3; cursor: not-allowed; }

/* Audio mixer dock — OBS-style: a row of vertical channel strips (name on top, a VERTICAL
   fader, mute at the bottom). Strips sit side by side and wrap. The mixer FILLS the dock body
   (flex:1 + min-height:0) so the strips/faders size to the dock's height instead of forcing a
   fixed tall height that overflows a short dock (esp. the bottom dock). */
.obslive__mixer { display: flex; flex-direction: row; flex-wrap: wrap; align-items: stretch; gap: var(--space-2); padding: var(--space-2); flex: 1 1 auto; min-height: 0; }
.obslive__mix { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2); padding: var(--space-2); border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--surface-inset); min-width: 72px; min-height: 0; }
.obslive__mix.is-muted { opacity: .6; }
.obslive__mix-name { flex: 0 0 auto; font-size: 11px; font-weight: 600; max-width: 88px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: center; }
/* The fader sizes to the strip: flex-basis 0 (NOT auto/px) so its height is purely the
   grow-allocated share of the strip's free space. A vertical <input type=range> has an
   intrinsic ~120px length that Chromium REFUSES to flex-SHRINK below — so basis:auto/px
   overflowed the short bottom dock (the bug in the screenshot: mute scrolled off, scrollbar).
   With basis:0 there's nothing to "shrink": the height grows from 0 to fill the strip, so it
   fits any dock height (down to a usable 44px floor, capped at 300px). */
.obslive__mix-fader { writing-mode: vertical-lr; direction: rtl; width: 22px; flex: 1 1 0; min-height: 44px; max-height: 300px; accent-color: var(--accent); cursor: pointer; }
.obslive__mix-mute { flex: 0 0 auto; padding: 2px 7px; font-size: 13px; }
/* Audio dock body floor — the CSS backstop twin of AUDIO_DOCK_MIN_H (sec-obs-live.js):
   even if a bug elsewhere sizes the hosting edge below the floor, the fader cards keep
   their room (the dock clips at the edge instead of crushing the mixer behind its own
   scrollbar). 144 = channel strip ≈120 + mixer padding 16 + dock-body padding 8. */
.obslive__dock--audio .obslive__dock-body { min-height: 144px; }
.obslive__mix-mute.is-muted { color: var(--danger); border-color: var(--danger); }

/* Generic selectable list row (used by both docks). */
.obslive__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; }
.obslive__listitem { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2); border-radius: var(--r-sm); cursor: pointer; min-width: 0; }
.obslive__listitem:hover { background: var(--surface-inset); }
.obslive__listitem.is-sel { background: var(--accent-wash); box-shadow: inset 0 0 0 1px var(--border-accent); }
.obslive__listitem-name { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1 1 auto; min-width: 0; }
/* Inline rename (✎) — subtle, revealed on row hover; swaps the name span for an input. */
.obslive__rename { flex: 0 0 auto; margin-left: 4px; padding: 0 5px; font-size: 11px; line-height: 1.5; background: none; border: 1px solid transparent; border-radius: var(--r-sm); color: var(--text-dim); cursor: pointer; opacity: 0; transition: opacity var(--dur, 180ms) var(--ease), color var(--dur, 180ms) var(--ease); }
.obslive__listitem:hover .obslive__rename, .obslive__source:hover .obslive__rename { opacity: 1; }
.obslive__rename:hover, .obslive__rename:focus-visible { color: var(--accent); border-color: var(--border); }
.obslive__rename-input { font-size: 12px; padding: 1px 5px; max-width: 170px; flex: 1 1 auto; min-width: 0; }
.obslive__livetag { flex: 0 0 auto; margin-left: auto; font-family: var(--font-mono); font-size: 9px; font-weight: 700; letter-spacing: .06em; color: var(--danger); border: 1px solid var(--danger); border-radius: var(--r-pill); padding: 1px 6px; }
/* Scene properties (⋯) — hover-revealed like ✎; the hotkey capture UI lives in its modal. */
.obslive__sceneprops { flex: 0 0 auto; padding: 0 5px; font-size: 12px; line-height: 1.5; background: none; border: 1px solid transparent; border-radius: var(--r-sm); color: var(--text-dim); cursor: pointer; opacity: 0; transition: opacity var(--dur, 180ms) var(--ease), color var(--dur, 180ms) var(--ease); }
.obslive__listitem:hover .obslive__sceneprops, .obslive__sceneprops:focus-visible { opacity: 1; }
.obslive__sceneprops:hover, .obslive__sceneprops:focus-visible { color: var(--accent); border-color: var(--border); }
/* A hotkey registration failure surfaces on the ⋯ button itself: always visible + danger-colored. */
.obslive__sceneprops.danger { opacity: 1; color: var(--danger); border-color: var(--danger); }
/* Scene-properties modal: read-only name value + the bind/clear hotkey row. */
.obslive__propsval { font-weight: 600; }
.obslive__hotkeyrow { display: flex; align-items: center; gap: var(--space-2); }

/* Source row = a list row with an eye toggle + name/badge (no per-row buttons; the
   dock toolbar acts on the selection). */
.obslive__source { /* extends .obslive__listitem */ }
.obslive__source-main { display: flex; align-items: center; gap: var(--space-2); min-width: 0; flex: 1 1 auto; }
.obslive__source-name { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.obslive__source-main .badge { flex: 0 0 auto; }
.obslive__source-eye { flex: 0 0 auto; }
/* Drag-to-reorder grip. */
.obslive__source-grip { flex: 0 0 auto; cursor: grab; color: var(--text-faint); user-select: none; line-height: 1; padding: 0 2px; }
.obslive__listitem:hover .obslive__source-grip { color: var(--text-dim); }
.obslive__source--dragging { opacity: .5; }
.obslive__source--dropbefore { box-shadow: inset 0 2px 0 var(--accent); }
.obslive__source--dropafter { box-shadow: inset 0 -2px 0 var(--accent); }
.obslive__addmenu, .obslive__props { margin-top: var(--space-2); padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--r-sm); display: flex; flex-direction: column; gap: var(--space-2); }
/* Optional custom-name field above the Add-source kind picker. */
.obslive__addname { display: flex; flex-direction: column; gap: var(--space-1); }
.obslive__addname > .lbl { margin: 0; color: var(--text-dim); font-size: 12px; }
.obslive__addname > .input { width: 100%; }
/* Name (display label) field at the top of the source Properties modal. */
.obslive__propname { padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); }
.obslive__propname > .help { color: var(--text-dim); font-size: 11px; }
/* Deferred "Edit source settings" section (opening it reads the live source). */
.obslive__props-settings > summary { cursor: pointer; padding: var(--space-2) 0; font-weight: 600; }
.obslive__props-settings-body { display: flex; flex-direction: column; gap: var(--space-2); }
/* ── OBS-style properties modal (Add wizard + Edit pop-up) ──────────────────── */
.obslive__modal-overlay { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; padding: var(--space-3); background: color-mix(in srgb, var(--bg) 62%, transparent); }
.obslive__modal { display: flex; flex-direction: column; width: 100%; max-width: min(560px, 92vw); max-height: 85vh; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm); }
.obslive__modal-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); padding: var(--space-3); border-bottom: 1px solid var(--border); }
.obslive__modal-x { flex: 0 0 auto; }
.obslive__modal-body { overflow-y: auto; padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); }
.obslive__modal-foot { display: flex; justify-content: flex-end; gap: var(--space-2); padding: var(--space-3); border-top: 1px solid var(--border); }
.obslive__group { font-size: 12px; color: var(--text-dim); margin-top: var(--space-2); }
/* Add-source picker buttons: a clear task label + a one-line description (stacked,
   left-aligned), so "ffmpeg" → "Media file / stream" and the webcam is obvious. */
.obslive__addkind { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; text-align: left; width: 100%; height: auto; padding: var(--space-2); }
.obslive__addkind-label { font-weight: 600; }
.obslive__addkind-desc { font-size: 11px; color: var(--text-dim); }
/* Image add-step: file picker → cloud upload → /o/image browser source. */
.obslive__imgadd { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-1) 0; }
.obslive__imgadd .muted { font-size: 12px; color: var(--text-dim); margin: 0; }
.obslive__or { font-size: 11px; color: var(--text-dim); text-align: center; margin-top: var(--space-1); }
.obslive__imgadd-urlrow { display: flex; gap: var(--space-2); align-items: center; }
.obslive__imgadd-urlrow .input { flex: 1; }
.obslive__imgadd-status { font-size: 12px; color: var(--text-dim); }
.obslive__imgadd-status.danger { color: var(--danger); }
/* Alert/widget add-step: pick the alert box / a widget → tokenized /o/render
   browser source. Same layout idiom as the image add-step above. */
.obslive__awadd { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-1) 0; }
.obslive__awadd .muted { font-size: 12px; color: var(--text-dim); margin: 0; }
.obslive__awadd-list { display: flex; flex-direction: column; gap: var(--space-1); }
/* Cloud library picker (media + slideshow add steps): thumbnail grid. */
.obslive__lib { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: var(--space-2); max-height: 220px; overflow-y: auto; padding: var(--space-1); border: 1px solid var(--border); border-radius: var(--r-sm); }
.obslive__lib-item { padding: 0; border: 2px solid transparent; border-radius: var(--r-sm); overflow: hidden; cursor: pointer; background: var(--surface-2); aspect-ratio: 16 / 9; display: flex; align-items: center; justify-content: center; }
.obslive__lib-item.is-sel { border-color: var(--accent); }
.obslive__lib-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.obslive__lib-chip { font-size: 11px; color: var(--text-dim); padding: var(--space-1); text-align: center; word-break: break-all; }
.obslive__lib-empty { padding: var(--space-2); font-size: 12px; }
.obslive__props-title { font-weight: 700; }
/* Property editor rows: clean two-column label | control grid (overrides the shared
   .field column layout that left checkboxes/inputs floating centered). Label sits
   left at a fixed width; the control fills the rest, left-aligned + consistent. */
.obslive__props .field {
  display: grid;
  grid-template-columns: minmax(7rem, 13rem) 1fr;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
}
.obslive__props .field > .lbl { margin: 0; text-align: left; align-self: center; }
.obslive__props .field > .select,
.obslive__props .field > .input { width: 100%; max-width: none; }
.obslive__props .field > input[type="checkbox"] { width: 1.05rem; height: 1.05rem; justify-self: start; margin: 0; }
.obslive__props-json { width: 100%; min-height: 10rem; font-family: var(--font-mono); resize: vertical; }
/* Generic property editor: the raw-JSON escape hatch, collapsed by default. */
.obslive__props-advanced { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-2); }
.obslive__props-advanced > summary { cursor: pointer; font-size: 12px; color: var(--text-dim); }
/* §09 Phase 2 — filter/effect UI (scoped inside the props panel). */
.obslive__filters { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: var(--space-2); }
.obslive__filters-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.obslive__filter { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2); border: 1px solid var(--border); border-radius: var(--r-sm); }
.obslive__filter-name { font-weight: 600; flex: 1 1 auto; }
/* §09 Phase 3 — scene-item transform / lock form (scoped inside the props panel). */
.obslive__source-eye { flex: 0 0 auto; }
.obslive__transform { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: var(--space-2); }
.obslive__tx-field { display: flex; align-items: center; gap: var(--space-2); }
.obslive__tx-field .input { max-width: 10rem; }
/* §09 Phase 4 — live frame preview (GetSourceScreenshot poll). 16:9 stage at the
   top of the panel; the placeholder shows until the first frame arrives. */
/* NO border-radius: this box clips the live <video>, and a ROUNDED clip forces the
   playing video to re-rasterize on the main thread every frame → app-wide input lag.
   A square overflow:hidden clip is free. (Same fix as .preview-stage in sections.css.) */
.obslive__preview { position: relative; width: 100%; aspect-ratio: 16 / 9; background: var(--surface-inset); border: 1px solid var(--border); overflow: hidden; display: flex; align-items: center; justify-content: center; }
.obslive__preview-img { width: 100%; height: 100%; object-fit: contain; display: block; }
.obslive__preview-video { width: 100%; height: 100%; object-fit: contain; display: block; background: var(--bg); }
/* display:block above overrides the `hidden` attribute the JS toggles, so an
   un-fetched frame showed a broken-image glyph / black video box. Let [hidden] win
   → the placeholder shows until a real frame arrives. */
.obslive__preview-img[hidden], .obslive__preview-video[hidden] { display: none; }
.obslive__preview-placeholder { text-align: center; padding: var(--space-3); }
/* ── In-view layout editor — drag/resize sources on the preview ── */
.obslive__layoutbar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-2); }
.obslive__layout-toggle.is-on { background: var(--accent); color: var(--on-strong); }
.obslive__layout-hint { font-size: 12px; }
/* The interactive overlay sits ON TOP of the preview; only the boxes capture
   pointer events so the rest of the preview stays inert. In the workspace it mounts
   on the CENTER PANE (not the stage), so boxes/handles render into the canvas gutter
   instead of being clipped at the stage edge — a full-canvas source's handles stay
   grabbable. z-index keeps it above the absolutely-positioned stage sibling. */
.obslive__layout-layer { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.obslive__layout-box {
  position: absolute;
  border: 1.5px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  box-sizing: border-box;
  cursor: move;
  pointer-events: auto;
  touch-action: none;
}
.obslive__layout-box.is-dragging { background: color-mix(in srgb, var(--accent) 22%, transparent); }
/* Click (no drag) SELECTS a box — visibly distinct; it never opens properties. */
.obslive__layout-box.is-sel { box-shadow: 0 0 0 1.5px var(--accent); background: color-mix(in srgb, var(--accent) 22%, transparent); }
.obslive__layout-box-label {
  position: absolute; top: 0; left: 0; max-width: 100%;
  font-size: 10px; font-weight: 700; line-height: 1.3;
  padding: 0 4px; background: var(--accent); color: var(--on-strong);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Snap guide lines — thin accent lines drawn while a drag is engaged on a snap
   target (canvas edge / center / another item's edge); removed on release. Inert:
   they must never intercept the pointer mid-drag. */
.obslive__layout-guide { position: absolute; background: var(--accent); pointer-events: none; z-index: 3; }
.obslive__layout-guide--v { width: 1px; }
.obslive__layout-guide--h { height: 1px; }
.obslive__layout-guide[hidden] { display: none; }
.obslive__layout-handle {
  position: absolute; right: -6px; bottom: -6px; width: 12px; height: 12px;
  background: var(--accent); border: 2px solid var(--text); border-radius: 2px;
  cursor: nwse-resize; pointer-events: auto; touch-action: none;
}
