:root{
  /* Spacing scale — consistent form rhythm (4 / 8 / 12).
     NOTE (2026-07-06 token audit): a full font-size + spacing token sweep was deliberately NOT
     done — ~500 raw padding/gap/font-size sites, mostly one-offs, where a blind sweep risks
     visual regression the node harness can't catch for near-zero governance benefit. The
     high-value, byte-identical consolidations WERE done: the z-index scale (--z-*) and the
     semantic alpha tints (--warn-tint-*, --danger-tint-*), both guarded in ci-check. A real
     type/space scale, if wanted, should be an explicit pass with per-change eyes-on. */
  --space-1:4px; --space-2:8px; --space-3:12px;
  /* Dark theme (default) — Blender-style neutral grays */
  --bg:#0d0d0d; --panel:#2b2b2b; --panel-2:#232323; --panel-3:#2a2a2a; --border:#3a3a3a;
  /* Tunable surface tones (live dev color tuner — press ` to toggle). Grayscale defaults.
     The form column has no fill of its own — it shows the app --bg. --splitter-bg is the
     RESTING divider color (splitters brighten to --accent on hover). */
  --group-card-bg:#171717; --group-card-bd:#262626; --chip-bg:#222222; --chip-bd:#2c2c2c; --splitter-bg:#050505;
  --timeline-bg:#171717; --menu-bg:#101010; --menu-bd:#1b1b1b;
  --field-bg:#0c0c0c; --field-bd:#151515;
  --transport-bg:#070707; --transport-bd:#242424;
  /* Timeline lane area (right of the gutter) — one neutral gray for every lane; the gutter
     keeps its own per-type wash + active highlight. */
  --lane-bg:#1d1d1d; --lane-bd:#282828;
  /* Section-header surface (Overview/Acoustics/Comments + the Subjects panel head) and the
     view-tabs bar (Timeline/Outline/Spreadsheet/Side-by-side). Dedicated tones (tuned via the dev
     color tuner, then baked here) so they read distinctly from the --panel-2 surfaces and tuning
     them doesn't drag every other --panel-2 surface along. One pair per concept, multiple consumers. */
  --section-head-bg:#353535; --section-head-bd:#4d4d4d;
  --viewbar-bg:#191919;      --viewbar-bd:#454545;
  --text:#d4d4d4; --muted:#9a9a9a; --dim:#8a8a8a;   /* was #6e6e6e — failed WCAG on every panel surface */
  --accent:#4772b3; --accent-2:#5680c2;
  --accent-tint-soft:rgba(71,114,179,.18);
  --accent-tint-med:rgba(71,114,179,.28);
  --accent-tint-strong:rgba(71,114,179,.40);
  --accent-on:#b8cdf0;
  /* --<type> track-color vars (--speech, --music, …) are generated from TYPE_META.pal by
     _injectTypeColorCSS() — see index.html. Don't redeclare them here (one owner). */
  --danger:#ef4444; --warn:#f59e0b; --ok:#10b981;
  /* Semantic alpha tints — ONE owner per (color × strength), mirroring --accent-tint-*.
     Values equal the raw rgba() literals they replaced (byte-identical), so a future hue
     change to warn/danger propagates from here instead of ~16 scattered rgba() copies. */
  --warn-tint-soft:rgba(245,158,11,.07); --warn-tint:rgba(245,158,11,.14);
  --warn-tint-med:rgba(245,158,11,.18); --warn-tint-strong:rgba(245,158,11,.32);
  --danger-tint-soft:rgba(239,68,68,.12); --danger-tint:rgba(239,68,68,.18);
  /* Z-index scale — 5 named layers replacing a 20-value magic-number scatter. Local stacking
     contexts (0–8, within a component) stay as raw small ints; these own the cross-component order. */
  --z-sticky:10; --z-overlay:100; --z-confirm:200; --z-panel-float:230; --z-menu:1000; --z-ctx:1100; --z-top:9999;
  /* "Inherited" marker tint — a calm teal, deliberately NOT the blue accent (reserved for override) nor
     amber/green/red (warn/ok/danger). Lets the ↳ register as "this value comes from the speaker" without
     out-shouting an override. Single owner of the inherited-marker colour. */
  --inherit:#5fb0a6; --inherit-tint:rgba(95,176,166,.14);
  --gutter:240px;
  --lane-h:24px;
  --scroll-thumb:#4a4a4a;
  --rcol-w:420px;
  --tline-h:240px;
  --lcol-w:300px;
  --outliner-h:160px;
}
.app[data-theme="light"]{
  --bg:#eaeaea; --panel:#ffffff; --panel-2:#f0f0f0; --panel-3:#ffffff; --border:#d0d0d0;
  /* Light-theme form surfaces track the theme tokens (the dark tuned grays would be wrong here). */
  --group-card-bg:#f0f0f0; --group-card-bd:#d0d0d0; --chip-bg:#ffffff; --chip-bd:#d0d0d0; --splitter-bg:#d0d0d0;
  --timeline-bg:#f0f0f0; --menu-bg:#ffffff; --menu-bd:#d0d0d0;
  --field-bg:#f0f0f0; --field-bd:#d0d0d0;
  --transport-bg:#ffffff; --transport-bd:#d0d0d0;
  --lane-bg:#f0f0f0; --lane-bd:#d0d0d0;
  --section-head-bg:#f0f0f0; --section-head-bd:#d0d0d0;
  --viewbar-bg:#f0f0f0;      --viewbar-bd:#d0d0d0;
  --builtin-icon-invert:0;
  --text:#1a1a1a; --muted:#6b6b6b; --dim:#767676;   /* was #9a9a9a — same contrast failure, light side */
  --accent:#3b5ea6; --accent-2:#2d4d8e;
  --accent-tint-soft:rgba(59,94,166,.12);
  --accent-tint-med:rgba(59,94,166,.22);
  --accent-tint-strong:rgba(59,94,166,.36);
  --accent-on:#1a3565;
  --inherit:#2f8079; --inherit-tint:rgba(47,128,121,.12);
  --scroll-thumb:#b8b8b8;
}

/* Minimalist line icons */
.type-icon{
  width:16px;height:16px;display:inline-block;vertical-align:middle;
  flex-shrink:0;
}
/* Built-in PNG icons: black strokes on transparent — invert on dark theme */
img.type-icon[data-builtin]{filter:invert(var(--builtin-icon-invert,1))}
.tile-big .type-icon{width:13px;height:13px}
.seg-table .type-icon{width:12px;height:12px}
/* The ONE colored type-icon: a content type shown as its icon in its colour. Used by the outline
   rows, the spreadsheet Type cell, the completion card, and the triage head. Per-type colour rule
   .type-ic.<type>{color:var(--<type>)} is generated by _injectTypeColorCSS() — the single owner. */
.type-ic{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0}
.type-ic .type-icon{width:14px;height:14px}
.ic{
  width:18px;height:18px;display:inline-block;vertical-align:-2px;flex-shrink:0;
  stroke:currentColor;fill:none;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round;
}
.ic.sm{width:15px;height:15px}
.ic.lg{width:18px;height:18px}
.ic.xl{width:30px;height:30px;stroke-width:1.25}
*{box-sizing:border-box}
[hidden]{display:none!important}
html,body{height:100%;overscroll-behavior:none}   /* no rubber-band panning the whole app on touch */
body{
  margin:0; font:13px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,sans-serif;
  background:var(--bg); color:var(--text); overflow:hidden;
  touch-action:manipulation;   /* no double-tap page zoom — taps act immediately, page stays put */
}
button{font:inherit;color:inherit}

/* ============================== LAYOUT (After Effects-style) ============================== */
.app{
  display:none;
  color:var(--text);background:var(--bg);
  /* The center column splits into chrome (transport + tabs, auto height) and content
     (the active view, --tline-h). The three views are direct grid children sharing the
     `content` area; in two-col the spreadsheet is reassigned to the `sheet` area by CSS
     (no DOM teleport). See docs/slot-layout-design.md. */
  grid-template-columns: var(--lcol-w) 5px minmax(360px,1fr) 5px var(--rcol-w);
  grid-template-rows: auto minmax(160px,1fr) 5px auto var(--tline-h) auto;
  grid-template-areas:
    "topbar topbar topbar  topbar topbar"
    "form   lsplit video   csplit props"
    "form   lsplit rsplit  csplit props"
    "form   lsplit chrome  csplit props"
    "form   lsplit content csplit props"
    "keys   keys   keys    keys   keys";
  /* dvh after vh: on iOS Safari 100vh includes the retracted URL bar, so the bottom ~6% of the
     app (the timeline!) rendered under browser chrome. dvh tracks the REAL visible height. */
  height:100vh;height:100dvh;overflow:hidden;
}
.topbar{grid-area:topbar}
.col-video{
  grid-area:video;display:flex;flex-direction:column;
  min-width:0;overflow:hidden;position:relative;
}
.col-video-content{
  display:flex;flex-direction:column;
  /* No L/R padding: the video + waveform (.video-unit) span the full center column,
     matching the timeline width below them. Top padding is the standard gap below the
     header bar. */
  padding:8px 0 0;gap:0;
  min-height:0;min-width:0;overflow:hidden;flex:1 1 auto;
}
.col-props{grid-area:props;display:flex;flex-direction:column;min-width:0;overflow:hidden}
.timeline-panel{grid-area:chrome;display:flex;flex-direction:column;min-height:0;overflow:hidden}
.form-foot{grid-area:foot}
.keys{grid-area:keys}
/* Draggable splitter handles — Blender-style */
.splitter{
  position:relative;z-index:5;background:var(--splitter-bg,var(--border));
  transition:background .12s;
}
.splitter:hover,.splitter.dragging{background:var(--accent)}
.splitter::after{content:'';position:absolute;}
.splitter.col-split{grid-area:csplit;cursor:col-resize}
.splitter.col-split::after{top:0;bottom:0;left:-3px;right:-3px}
.splitter.row-split{grid-area:rsplit;cursor:row-resize}
.splitter.row-split::after{left:0;right:0;top:-3px;bottom:-3px}
.splitter.outliner-split{cursor:row-resize;height:5px;flex-shrink:0}
.splitter.outliner-split::after{left:0;right:0;top:-3px;bottom:-3px}
/* Gutter splitter — overlays the lane-gutter / lane-area boundary at x=var(--gutter).
   Background is transparent at rest (so the existing 1px column border shows through),
   accent on hover/drag. */
.splitter.gutter-split{
  position:absolute;top:0;bottom:0;width:5px;
  left:calc(var(--gutter) - 2px);
  cursor:col-resize;z-index:8;background:transparent;
}
.splitter.gutter-split:hover,.splitter.gutter-split.dragging{background:var(--accent)}
.splitter.gutter-split::after{top:0;bottom:0;left:-3px;right:-3px}
body.resizing{user-select:none}
body.resizing.col-resizing,body.resizing.col-resizing *{cursor:col-resize !important}
body.resizing.row-resizing,body.resizing.row-resizing *{cursor:row-resize !important}

/* ── Column collapse ──────────────────────────────────────────────────────── */
/* Toggle buttons sit at the top of each vertical splitter */
.col-collapse-btn{
  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:18px;height:28px;border-radius:5px;
  background:var(--panel-2);border:1px solid var(--border);
  color:var(--muted);font-size:9px;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  opacity:0;transition:opacity .12s,background .12s;
  z-index:var(--z-sticky);pointer-events:all;line-height:1;
}
.splitter:hover .col-collapse-btn,
.splitter.col-hidden-adj .col-collapse-btn{opacity:1}
.col-collapse-btn:hover{background:var(--accent-tint-soft);color:var(--accent-on);border-color:var(--accent)}
/* Collapsed left: form column hidden, lsplit becomes a thin expand-tab */
.app.lcol-hidden{
  grid-template-columns: 0px 20px minmax(360px,1fr) 5px var(--rcol-w);
}
.app.lcol-hidden .col-form{display:none}
.app.lcol-hidden .splitter.left-split{
  background:var(--panel-2);border-right:1px solid var(--border);
  cursor:default;
}
.app.lcol-hidden .splitter.left-split .col-collapse-btn{opacity:1}
/* Collapsed right: props column hidden, csplit becomes expand-tab */
.app.rcol-hidden{
  grid-template-columns: var(--lcol-w) 5px minmax(360px,1fr) 20px 0px;
}
.app.rcol-hidden .col-props{display:none}
.app.rcol-hidden .splitter.col-split{
  background:var(--panel-2);border-left:1px solid var(--border);
  cursor:default;
}
.app.rcol-hidden .splitter.col-split .col-collapse-btn{opacity:1}
/* BOTH collapsed: each single-class rule above rewrites the WHOLE grid-template-columns at equal
   specificity, so with both classes set the later one (rcol-hidden) wins and the form column springs
   back to var(--lcol-w) (a blank gap, since .col-form is display:none) — i.e. the left won't collapse
   while the right is collapsed. This combined rule (higher specificity) zeroes BOTH edge columns and
   turns both splitters into expand-tabs. */
.app.lcol-hidden.rcol-hidden{
  grid-template-columns: 0px 20px minmax(360px,1fr) 20px 0px;
}
.col-form{
  grid-area:form;display:flex;flex-direction:column;min-width:0;overflow:hidden;
}
/* Dev color tuner — live-edits the form-surface vars (toggle with the backtick key). */
.theme-tuner{position:fixed;top:58px;right:14px;z-index:var(--z-top);width:270px;
  background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:11px 13px;
  box-shadow:0 10px 34px rgba(0,0,0,.55);font-size:12px;color:var(--text)}
.theme-tuner[hidden]{display:none}
.theme-tuner h4{margin:0 0 8px;font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);font-weight:600}
.theme-tuner .tt-row{display:flex;align-items:center;gap:8px;margin:7px 0}
.theme-tuner .tt-row label{flex:0 0 78px;font-size:11px;color:var(--muted)}
.theme-tuner .tt-row input[type=range]{flex:1;min-width:0}
.theme-tuner .tt-hex{flex:0 0 56px;text-align:right;font-family:ui-monospace,Menlo,monospace;font-size:11px}
.theme-tuner .tt-foot{display:flex;gap:6px;margin-top:10px}
.theme-tuner .tt-foot button{flex:1;font:inherit;font-size:11px;padding:5px;border:1px solid var(--border);border-radius:5px;background:var(--panel-2);color:var(--text);cursor:pointer}
.theme-tuner .tt-foot button:hover{border-color:var(--accent)}
.theme-tuner .tt-hint{font-size:10px;color:var(--dim);margin-top:8px;line-height:1.4}
.form-col-head{
  background:var(--panel-2);border-bottom:1px solid var(--border);
  padding:3px 8px 3px 12px;font-size:10px;font-weight:600;color:var(--muted);
  display:flex;align-items:center;gap:6px;flex-shrink:0;
  text-transform:uppercase;letter-spacing:.6px;
}
.fch-title{flex-shrink:0}
.fch-progress{font-variant-numeric:tabular-nums;letter-spacing:0;text-transform:none;white-space:nowrap;flex-shrink:0}
.fch-spacer{flex:1 1 auto}
.form-col-body{flex:1 1 auto;overflow-y:auto;display:flex;flex-direction:column;gap:4px}
.form-col-body .form-scroll{flex:0 0 auto}
.splitter.left-split{grid-area:lsplit;cursor:col-resize}
.splitter.left-split::after{top:0;bottom:0;left:-3px;right:-3px}
/* col-video itself is now a grid; the rules panel and col-video-content handle their own overflow */
.col-form::-webkit-scrollbar,.col-video::-webkit-scrollbar,.col-props::-webkit-scrollbar,
.timeline-panel::-webkit-scrollbar,.form-scroll::-webkit-scrollbar{width:8px}
.col-form::-webkit-scrollbar-thumb,.col-video::-webkit-scrollbar-thumb,.col-props::-webkit-scrollbar-thumb,
.timeline-panel::-webkit-scrollbar-thumb,.form-scroll::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:4px}

/* Properties column header */
/* Persistent panel title — the always-present anchor, styled exactly like the Subjects panel header
   (.outliner-head / shared --section-head-* tokens) so the two right-column panels read as one set. */
/* Identity card — wraps the title + contextual row on an elevated surface so it reads as the panel's
   "what am I editing" block, lifted off the dark editor below. The left accent bar names the KIND:
   the span's type colour (the one colour axis), neutral for a subject, nothing when empty. */
.props-id{
  flex-shrink:0;background:var(--panel-2);
  border-left:3px solid var(--id-accent, transparent);
  transition:border-color .12s, background .12s;
}
.props-id.is-span{ background:color-mix(in srgb, var(--id-accent) 7%, var(--panel-2)); }
.props-id.is-subject{ --id-accent:var(--dim); }   /* subjects aren't colour-coded — a neutral bar */
.props-id.is-empty{ --id-accent:transparent; background:transparent; }
/* The persistent title row stays filled like its Subjects sibling (.outliner-head) at ANY width,
   empty or not — only the accent tint (border + content-row wash) recedes when nothing is
   selected. Was gated behind the ≤1100px stacked-layout media query, so on a normal desktop-width
   window the title band just vanished; that query is still needed for wider layout concerns but no
   longer owns this fill. */
.props-id.is-empty .props-title{ background:var(--section-head-bg); border-bottom-color:var(--section-head-bd); }
.props-id.is-triage{ background:color-mix(in srgb, var(--id-accent) 7%, var(--panel-2)); }  /* triage = the reviewed type's colour context */
.props-title{
  background:transparent;border-bottom:1px solid var(--section-head-bd);
  padding:5px 8px 5px 9px;font-size:10px;font-weight:600;color:var(--muted);
  text-transform:uppercase;letter-spacing:.6px;flex-shrink:0;
  display:flex;align-items:center;min-height:24px;
}
/* Context row beneath the title — what's currently selected (identity strip) or the empty hint. A
   quieter sub-bar so the persistent title above stays the panel's identity, not a competing header. */
.props-header{
  padding:6px 8px 6px 12px;color:var(--muted);
  display:flex;align-items:center;gap:6px;flex-shrink:0;min-height:30px;
}
.ph-type-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
/* Two-row identity header: row 1 = identity, row 2 = subject + actions. */
.ph-stack{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.ph-row{display:flex;align-items:center;gap:6px;min-width:0}
.ph-row-assign .ph-subject-sel{flex:1 1 auto;max-width:none}
/* Identity strip: [TYPE] derived-title · time · subject */
.ph-title{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  text-transform:none;letter-spacing:normal;font-size:12px;font-weight:600;color:var(--text)}
.ph-title-empty{color:var(--dim);font-style:italic;font-weight:500}
.ph-title-edit,.ph-title-auto{background:none;border:0;color:var(--dim);cursor:pointer;font-size:11px;padding:1px 3px;flex-shrink:0}
.ph-title-edit:hover,.ph-title-auto:hover{color:var(--text)}
/* ✎ (edit) is occasional — reveal on hover of the strip. ↺ (auto) stays visible
   when present because it also signals "this label was set by hand". */
.ph-title-edit{opacity:0;transition:opacity .12s}
.props-header:hover .ph-title-edit{opacity:1}
.ph-dur{font-size:10px;font-weight:500;font-variant-numeric:tabular-nums;color:var(--muted);
  letter-spacing:0;text-transform:none;white-space:nowrap;flex-shrink:0}
.ph-title-input{flex:1;min-width:60px;background:var(--panel);border:1px solid var(--accent);border-radius:4px;
  color:var(--text);font:inherit;font-size:12px;text-transform:none;letter-spacing:normal;padding:2px 6px}
.ph-time{
  font-size:10px;font-weight:500;font-variant-numeric:tabular-nums;
  color:var(--text);letter-spacing:0;text-transform:none;white-space:nowrap;flex-shrink:0;
}
.ph-spacer{flex:1}
.ph-act{background:none;border:0;color:var(--muted);cursor:pointer;flex-shrink:0;
  padding:3px;border-radius:4px;display:inline-flex;align-items:center;line-height:0;transition:color .12s,background .12s}
.ph-act .ic{width:14px;height:14px}
.ph-act:hover{color:var(--text);background:var(--panel-2)}
.ph-act-del:hover{color:var(--danger);background:var(--danger-tint-soft)}
.ph-act-text{gap:5px;padding:3px 8px;line-height:1;font-size:12px;font-weight:600;
  border:1px solid var(--border);color:var(--text)}
.ph-act-text:hover{border-color:var(--accent);color:var(--accent)}
.ph-subject-sel{
  font-size:11px;padding:2px 5px;border-radius:4px;
  border:1px solid var(--border);background:var(--panel);color:var(--text);
  flex:1 1 0;min-width:0;max-width:130px;letter-spacing:0;text-transform:none;
  font-weight:400;cursor:pointer;
}
.ph-split-btn{
  font-size:10px;font-weight:600;padding:2px 9px;border-radius:4px;
  background:var(--accent);color:var(--accent-on);border:none;cursor:pointer;
  white-space:nowrap;flex-shrink:0;letter-spacing:.3px;
}
.ph-split-btn:hover{opacity:.82}
.ph-hint{font-size:10px;color:var(--muted);text-transform:none;letter-spacing:0;font-weight:400}
.ph-subject-meta{font-size:11px;color:var(--muted);text-transform:none;letter-spacing:0;font-weight:400}

/* Outliner (Blender-style scene block) — subjects list above the Properties pane */
.outliner{
  height:var(--outliner-h,160px);min-height:80px;
  flex-shrink:0;display:flex;flex-direction:column;
  background:var(--bg);overflow:hidden;
}
.outliner-head{
  background:var(--section-head-bg);border-bottom:1px solid var(--section-head-bd);
  padding:5px 8px 5px 12px;font-size:10px;font-weight:600;color:var(--muted);
  display:flex;align-items:center;gap:6px;flex-shrink:0;
  text-transform:uppercase;letter-spacing:.6px;
}
.outliner-head .outliner-title{color:var(--muted)}
.outliner-head .outliner-count{
  background:var(--panel);border:1px solid var(--border);color:var(--muted);
  padding:0 5px;border-radius:7px;font-size:9px;letter-spacing:0;text-transform:none;font-weight:600;
}
.outliner-btn{
  background:transparent;border:0;color:var(--muted);
  width:18px;height:18px;border-radius:3px;cursor:pointer;font-size:13px;
  display:flex;align-items:center;justify-content:center;padding:0;line-height:1;
}
.outliner-btn:hover{color:var(--text);background:rgba(255,255,255,.06)}
.app[data-theme="light"] .outliner-btn:hover{background:rgba(0,0,0,.06)}
.outliner-body{flex:1 1 auto;overflow-y:auto;padding:2px 0}
.outliner-body::-webkit-scrollbar{width:8px}
.outliner-body::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:4px}
.outliner-row{
  display:flex;align-items:center;gap:7px;
  padding:4px 10px 4px 8px;cursor:pointer;font-size:12px;color:var(--text);
  border-left:2px solid transparent;line-height:1.2;
}
.outliner-row:hover{background:var(--panel-2)}
.outliner-row.selected{
  background:var(--accent-tint-soft);
  border-left-color:var(--accent);color:var(--accent-on);
}
.outliner-row.active-via-span{
  background:rgba(255,255,255,.04);
  border-left-color:var(--accent-tint-strong);
}
.app[data-theme="light"] .outliner-row.active-via-span{background:rgba(0,0,0,.04)}
/* Subject KIND icon (person today) — monochrome; colour stays on the content-type axis. */
.subject-icon{width:14px;height:14px;color:var(--muted);flex-shrink:0}
.outliner-row.selected .subject-icon,.outliner-row.active-via-span .subject-icon{color:var(--text)}
.ph-assign-icon{display:inline-flex;align-items:center;flex-shrink:0}
/* Drag-to-reorder grip — hidden until row hover so it doesn't compete with the name; grabbing it is
   the only way to start a reorder (click-select / dblclick-rename stay on the rest of the row). */
.outliner-row .drag-handle{opacity:0;cursor:grab;color:var(--dim);font-size:11px;line-height:1;flex-shrink:0;user-select:none;transition:opacity .12s}
.outliner-row:hover .drag-handle{opacity:.5}
.outliner-row .drag-handle:hover{opacity:1;color:var(--muted)}
.outliner-row .drag-handle:active{cursor:grabbing}
.outliner-row.dragging{opacity:.45}
.outliner-row.drag-over{box-shadow:inset 0 2px 0 var(--accent)}          /* insert-before indicator */
.outliner-row.drag-over-below{box-shadow:inset 0 -2px 0 var(--accent)}   /* insert-after  indicator (drop in lower half → land below, reaches the last slot) */
/* Neutral marker for the catch-all "(unassigned)" bucket — not an entity, so a plain dot, not a kind icon. */
.outliner-row .dot{
  width:8px;height:8px;border-radius:50%;background:var(--muted);flex-shrink:0;
  box-shadow:0 0 0 1px rgba(0,0,0,.25);
}
.outliner-row .name{
  flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:12px;  /* sans (inherited) — matches the timeline lane names + the rest of the app */
}
.outliner-row.unassigned .name{font-style:italic;color:var(--muted)}
.outliner-row .name-edit{
  flex:1;min-width:0;font-size:12px;font-family:inherit;color:var(--text);
  background:var(--panel-2);border:1px solid var(--accent);border-radius:3px;
  padding:1px 5px;margin:-2px 0;outline:none;
}
.outliner-row .count{
  font-size:10px;color:var(--muted);
  font-variant-numeric:tabular-nums;
}
.outliner-row.selected .count{color:var(--accent-on)}
.outliner-empty{
  padding:14px 12px;text-align:center;color:var(--muted);font-size:11px;
  display:flex;flex-direction:column;gap:8px;align-items:center;
}
.outliner-empty .btn{font-size:11px;padding:4px 10px}

/* Tabbed timeline panel */
.panel-tabs{
  display:flex;align-items:center;gap:0;
  background:var(--viewbar-bg);border-bottom:1px solid var(--viewbar-bd);
  /* No left padding: the first tab (Timeline) sits flush with the bar's left edge. */
  padding:0 8px 0 0;flex-shrink:0;min-height:34px;
}
.panel-tab{
  background:transparent;border:0;color:var(--muted);
  padding:8px 14px;cursor:pointer;font-size:12px;font-weight:500;
  border-bottom:2px solid transparent;display:inline-flex;align-items:center;gap:5px;
  white-space:nowrap;   /* never wrap the label (e.g. "Side-by-side" in a narrow left column) */
}
.panel-tab:hover{color:var(--text)}
.panel-tab.active{color:var(--text);border-bottom-color:var(--accent);background:var(--panel)}
.panel-tab .tab-count{
  background:var(--panel);border:1px solid var(--border);
  padding:1px 6px;border-radius:8px;font-size:10px;
}
/* The three content panes are direct .app grid children sharing the `content` slot;
   only the active one displays. (Two-col reassigns the spreadsheet's grid-area to `sheet`.) */
.tab-pane{grid-area:content;display:none;min-height:0;overflow:hidden;flex-direction:column}
.tab-pane.active{display:flex}

/* Spreadsheet view */
.sheet-filter{display:flex;align-items:center;gap:var(--space-1);flex-wrap:wrap;padding:var(--space-2);border-bottom:1px solid var(--border);background:var(--panel)}
.sheet-type-pill{font:inherit;font-size:11px;padding:2px 9px;border-radius:999px;border:1px solid var(--border);background:var(--panel-2);color:var(--muted);cursor:pointer}
.sheet-type-pill:hover{color:var(--text)}
.sheet-type-pill.on{background:var(--accent-tint-soft);border-color:var(--accent);color:var(--accent-on)}
.sheet-filter-note{margin-left:var(--space-2);font-size:11px;color:var(--muted)}
.spreadsheet-wrap{flex:1;overflow:auto;background:var(--panel)}
.seg-table{width:100%;border-collapse:collapse;font-size:11px}
.seg-table th{
  text-align:left;padding:5px 8px;
  background:var(--panel-2);color:var(--muted);
  border-bottom:1px solid var(--border);font-weight:600;
  position:sticky;top:0;z-index:1;white-space:nowrap;
  user-select:none;
}
.seg-table th.sortable{cursor:pointer}
.seg-table th.sortable:hover{color:var(--text);background:var(--panel)}
.seg-table th.sorted{color:var(--accent-on);background:var(--accent-tint-soft)}
.seg-table th .sort-ind{
  margin-left:4px;font-size:9px;opacity:.85;
  display:inline-block;min-width:8px;
}
.seg-table td{
  padding:4px 8px;border-bottom:1px solid rgba(255,255,255,.04);
  vertical-align:top;
}
.seg-table tbody tr:hover{background:var(--panel-2);cursor:pointer}
.seg-table tbody tr.selected{background:var(--accent-tint-soft)}
.seg-table .tc{font-variant-numeric:tabular-nums;color:var(--muted);font-size:10px}
.seg-table .ttype{display:inline-flex;align-items:center;gap:4px}
.seg-table .ttext{max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.seg-table .dim-cell{color:var(--dim);font-style:italic}
.cell-edit{display:block;min-width:20px;padding:1px 2px;outline:none;border-radius:2px;white-space:nowrap;overflow:hidden;cursor:text}
.cell-edit:focus{background:rgba(255,255,255,.1);white-space:normal;overflow:visible;box-shadow:inset 0 0 0 1px var(--accent)}
.seg-table td:has(.cell-edit){cursor:text}
/* Menu-backed spreadsheet cells render as a dropdown instead of an editable text span. */
.cell-select{width:100%;max-width:100%;box-sizing:border-box;font:inherit;font-size:12px;color:var(--text);
  background:var(--panel-2);border:1px solid transparent;border-radius:3px;padding:1px 2px;cursor:pointer;
  appearance:auto}
.cell-select:hover{border-color:var(--border)}
.cell-select:focus{outline:none;box-shadow:inset 0 0 0 1px var(--accent)}
.seg-table td:has(.cell-select){cursor:pointer;padding:1px 3px}
.cell-select option{background:var(--panel);color:var(--text)}

/* Responsive: stack columns when too narrow */
@media (max-width:1100px){
  .app{
    /* minmax(0,1fr), NOT 1fr: 1fr's implied auto-minimum lets the widest row's min-content
       (the waveform-controls strip, ~574px) blow the single track past the viewport — the
       app then clips off-screen on phones (topbar buttons + third tab unreachable). */
    grid-template-columns: minmax(0,1fr);
    /* Use the REAL area names — chrome (transport + tabs) and content (the active view). The old
       single-column template referenced stale "tline"/"foot" areas that no .app child uses anymore;
       the timeline (grid-area:chrome) and the views (grid-area:content) then orphaned into implicit
       columns, shattering the layout below 1100px. */
    grid-template-rows: auto auto auto minmax(30vh,1fr) auto auto auto;
    grid-template-areas:
      "topbar"
      "video"
      "chrome"
      "content"
      "props"
      "form"
      "keys";
    /* Work-surface-first (2026-07-06): timeline (chrome+content) sits under the video, ABOVE the
       metadata panels (props/form) — the old order buried the primary annotation surface at the
       bottom of a long scroll. The full on-demand-drawer redesign stays deferred (see HANDOFF);
       this reorders the inline panels so the work surface is where the eye lands first. */
  }
  /* Chips get their OWN full-width row (they clipped/side-scrolled without affordance in the single
     topbar row). Mirrors the ≤700px rule; raised to the whole narrow band. */
  .topbar{flex-wrap:wrap;row-gap:2px}
  .topbar .topbar-chips{order:3;flex-basis:100%;justify-content:flex-start;padding:2px 0 0}
  /* The rail-collapse rules (.app.lcol-hidden / .rcol-hidden / both) set a 5-track
     grid-template-columns at HIGHER specificity (0,2,0)–(0,3,0) than .app (0,1,0).
     Media queries add no specificity, so without this they'd override the stacked
     1fr above while the single-column grid-template-areas stays — tracks and areas
     mismatch and the layout shatters. Re-collapse every hidden state to the single
     column here (each comma-selector keeps its own specificity, so all three are
     covered). */
  .app.lcol-hidden,
  .app.rcol-hidden,
  .app.lcol-hidden.rcol-hidden{
    grid-template-columns: minmax(0,1fr);
  }
  .col-form{border-right:none;border-bottom:1px solid var(--border);max-height:40vh}
  /* The center column's `video` row is `auto` here; the video region flexes (min-height:0) and would
     collapse to 0 once a clip loads (the empty-state placeholder no longer holds the row open). Give
     it a definite height so the loaded <video> has room to fill. */
  .col-video{border-bottom:1px solid var(--border);min-height:42vh}
  .col-props{border-bottom:1px solid var(--border);max-height:30vh}
  /* Splitters don't apply in stacked layout */
  .splitter{display:none}
  /* The outliner-split splitter bar (hidden above) was ALSO the only visible boundary between
     the Subjects outliner and the Properties panel below it — on desktop the draggable bar
     itself (a solid var(--border)-colored strip) reads as a clear divider; stacked layouts lost
     it entirely, leaving no line between the two at all (iPhone report: "not clearly separated
     from Subjects"). A plain static border stands in; no drag affordance needed once hidden. */
  .outliner{border-bottom:1px solid var(--border)}
  /* The title-row fill for .props-id.is-empty is unconditional now (see the base rule near
     .props-id) — no narrow-layout-only override needed here anymore. */
}

/* Mobile tab bar (base: hidden on desktop/tablet; the ≤700px block below turns it on —
   the base rules MUST precede that block, same specificity, order is load-bearing). */
.m-tabs{
  display:none;grid-area:mtabs;
  border-top:1px solid var(--border);background:var(--panel-2);
  padding-bottom:env(safe-area-inset-bottom);
}
.m-tab{
  flex:1;display:flex;align-items:center;justify-content:center;gap:6px;
  font:inherit;font-size:12px;padding:11px 4px;min-height:44px;
  background:none;border:none;color:var(--muted);cursor:pointer;
}
.m-tab .ic{width:15px;height:15px}
.m-tab.active{color:var(--accent-on,var(--text));box-shadow:inset 0 2px 0 var(--accent)}
/* The phone-only video toggle stays out of the desktop transport. (.transport prefix: the
   generic .tcontrol rule is later in the file — specificity, not order, must win here.) */
.transport .m-video-toggle{display:none}

/* ============================== MOBILE (phone, ≤700px) ==============================
   A tabbed SINGLE SCREEN, not a stack: topbar → media strip (compact video + waveform +
   transport, always present) → ONE region (1fr) → bottom tab bar. Which region shows is
   owned by _setMobileView (index.html) via .app[data-mview]:
     annotate — the timeline chrome + panes (Timeline/Outline/Spreadsheet tabs inside)
     props    — the right rail (subjects + properties editor + triage)
     form     — the left rail (Overview form)
   The pre-existing ≤1100px stack above still applies first; everything here overrides it
   for phones (this block is later in the file, same specificity — order is load-bearing). */
@media (max-width:700px){
  .app{
    grid-template-rows:auto auto auto minmax(0,1fr) auto;
    grid-template-areas:
      "topbar"
      "video"
      "chrome"
      "content"
      "mtabs";
  }
  /* Media strip: the video picture gets a fixed modest height (it letterboxes inside);
     waveform + controls + transport keep their natural heights below it. m-video-off
     (the transport toggle) drops the picture entirely — audio keeps playing. */
  .col-video{min-height:0;border-bottom:1px solid var(--border)}
  /* .app/.transport prefixes throughout: the base .video-fit/.tcontrol/.keys rules sit LATER in
     the file, so these must win on specificity, not order. */
  .app .video-fit{flex:0 0 auto;height:24dvh}
  .app.m-video-off .video-fit{display:none}
  .transport .m-video-toggle{display:inline-flex}
  /* One region at a time: rails hide unless their view is active; when a rail view is up,
     the timeline chrome + panes hide (their auto rows collapse) and the rail takes `content`. */
  .col-form,.col-props{display:none}
  .app[data-mview="props"] .col-props{display:flex;grid-area:content;max-height:none;border-bottom:none}
  .app[data-mview="props"] .timeline-panel,
  .app[data-mview="props"] .tab-pane{display:none}
  .app[data-mview="form"] .col-form{display:flex;grid-area:content;max-height:none;border-right:none;border-bottom:none}
  .app[data-mview="form"] .timeline-panel,
  .app[data-mview="form"] .tab-pane{display:none}
  /* Keyboard-hint footer is meaningless on touch; Side-by-side needs two columns. */
  .app .keys{display:none}
  #reviewSplitBtn{display:none}
  /* Topbar: two rows, NOT overflow-x (an overflow topbar clips the File/View dropdowns —
     shipped once). Row 1 = brand + menus/actions; row 2 = the type-chip strip, which owns
     its own horizontal scroll (.topbar-chips base rule). Non-essentials hide: version,
     saved text (auto-save is silent anyway), theme toggle. */
  .topbar{flex-wrap:wrap;row-gap:2px}
  .topbar .topbar-chips{order:3;flex-basis:100%;justify-content:flex-start;padding:0}
  /* Logo-only brand: with the wordmark hidden, brand (~40px) + the actions group (~300px) share
     ONE row, so the topbar is two rows (actions + chip strip), not three — height matters here. */
  #appVersionLabel,.topbar .saved-indicator,#themeToggle,.topbar .brand-text{display:none}
  /* Dropdowns anchor to the VIEWPORT, not the button: a right:0-in-wrap menu hangs off-screen
     left once its button sits near the left edge (264px min-width vs a ~50px-from-left button). */
  .topbar-menu-wrap .file-menu{
    position:fixed;left:8px;right:8px;top:calc(env(safe-area-inset-top) + 48px);
    min-width:0;max-height:72dvh;overflow-y:auto;
  }
  /* Compact media strip in the rail views: the picture belongs to Annotate — hide it (audio,
     waveform and transport stay) so Properties/Overview get real editing height. */
  .app[data-mview="props"] .video-fit,
  .app[data-mview="form"] .video-fit{display:none}
  /* Subjects outliner: smaller fixed share on phones (still scrolls internally) so the
     range-properties editor below it gets the bulk of the region. */
  .app .outliner{height:120px}
  /* Timeline gutter: 240px is desktop-sized — 130px still fits type headers and truncates
     subject names with the existing ellipsis. Override the VAR, not the grid template: the
     playhead overlay (left:var(--gutter)) and the splitter position derive from it and must
     stay in sync. (Gutter cells are position:sticky;left:0 already; the "unpinned gutter" on
     phones was page-level focus-zoom, fixed by the viewport maximum-scale=1.) */
  :root{--gutter:130px}
  /* Phone zoom affordance: visible − / + buttons beside the zoom pill (no Ctrl+scroll on touch).
     .timeline-area prefix: the base .zoom-btns{display:none} sits later in the file — specificity,
     not order, must carry this. */
  .timeline-area .zoom-btns{display:flex}
  /* Bottom tab bar — thumb-reachable, above the home indicator. */
  .m-tabs{display:flex}
}
/* Touch: bump the smallest interactive targets to a tappable size without disturbing
   mouse/trackpad layouts (pointer:coarse only matches touch-primary devices). */
@media (pointer:coarse){
  .tcontrol{min-width:36px;min-height:36px}
  .panel-tab{padding:8px 12px}
  .outliner-btn{min-width:32px;min-height:32px}
}

/* ============================== TOPBAR ============================== */
.topbar{
  display:flex;align-items:center;justify-content:space-between;
  /* env() is 0 outside notched/standalone contexts, so desktop sees plain 12px. */
  padding:8px calc(12px + env(safe-area-inset-right)) 8px calc(12px + env(safe-area-inset-left));
  border-bottom:1px solid var(--border);background:var(--panel-2);
  flex-shrink:0;
}
.topbar h1{font-size:13px;margin:0;font-weight:600;letter-spacing:.2px}
/* Brand wordmark — WarpDiff style: icon mark + italic "Warp". (Placeholder logo for now.) */
.topbar h1.brand{display:inline-flex;align-items:center;gap:6px;font-size:16px;opacity:.5}
.brand-logo{width:26px;height:26px;display:block;flex-shrink:0}
.brand-text i{font-style:italic}
.topbar .meta{font-size:11px;color:var(--muted)}
.topbar-sep{width:1px;height:16px;background:var(--border);flex-shrink:0;margin:0 2px}
/* File menu (import / export / snapshot / clear) */
.file-menu-wrap,.topbar-menu-wrap{position:relative;display:inline-flex}
.file-menu{position:absolute;top:calc(100% + 6px);right:0;z-index:var(--z-menu);min-width:264px;
  background:var(--menu-bg,var(--panel));border:1px solid var(--menu-bd,var(--border));border-radius:8px;padding:5px;
  box-shadow:0 8px 28px rgba(0,0,0,.4);display:flex;flex-direction:column;gap:1px}
.fm-section{font-size:9px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);padding:7px 9px 3px}
.fm-item{display:flex;flex-direction:column;align-items:flex-start;gap:1px;text-align:left;
  background:transparent;border:0;border-radius:5px;padding:6px 9px;cursor:pointer;color:var(--text);font:inherit}
.fm-item:hover{background:var(--accent-tint-soft)}
.fm-item b{font-size:12px;font-weight:600}
.fm-item span{font-size:10px;color:var(--muted)}
.fm-item.danger:hover{background:color-mix(in srgb,var(--danger) 15%,transparent)}
.fm-item.danger b{color:var(--danger)}
.fm-sep{height:1px;background:var(--border);margin:4px 0}
/* Reusable right-click context menu (e.g. span split) */
.ctx-menu{position:fixed;z-index:var(--z-ctx);min-width:180px;background:var(--menu-bg,var(--panel));
  border:1px solid var(--menu-bd,var(--border));border-radius:7px;padding:4px;
  box-shadow:0 8px 28px rgba(0,0,0,.45);display:flex;flex-direction:column;gap:1px}
.ctx-item{text-align:left;background:transparent;border:0;border-radius:5px;
  padding:6px 9px;cursor:pointer;color:var(--text);font:inherit;font-size:12px;white-space:nowrap}
.ctx-item:hover:not(:disabled){background:var(--accent-tint-soft)}
.ctx-item:disabled{color:var(--dim);cursor:default}
.ctx-item.danger{color:var(--danger)}
.topbar-chips{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;min-width:0;overflow-x:auto;scrollbar-width:none;padding:0 10px}
.topbar-chips::-webkit-scrollbar{display:none}
.sec-chip{display:inline-flex;align-items:center;gap:5px;padding:2px 9px;border-radius:999px;border:1px solid var(--border);
  background:transparent;color:var(--muted);font:inherit;font-size:10.5px;font-weight:600;line-height:1.6;
  cursor:pointer;white-space:nowrap;user-select:none;flex-shrink:0}
.sec-chip:hover{border-color:var(--accent);color:var(--text);background:var(--accent-tint-soft)}
.sec-chip .cnt{font-variant-numeric:tabular-nums;font-weight:700;color:var(--warn)}
.sec-chip .ck{color:var(--ok);font-weight:700}
.sec-chip-dot{width:7px;height:7px;border-radius:50%;display:inline-block;margin-right:2px;flex-shrink:0}
.sec-chip.focused{outline:2px solid var(--accent);outline-offset:1px;background:var(--accent-tint-soft);color:var(--text)}
.sec-chip.triage-open{background:var(--accent-tint-soft);border-color:var(--accent);color:var(--accent-on)}
.sec-chip-x{margin-left:5px;color:var(--muted);font-size:9px;line-height:1}
.sec-chip-x:hover{color:var(--text)}
.sec-chip.done{border-color:rgba(16,185,129,.35)}
.sec-chip.done .cnt{color:var(--ok)}
/* Vocal events: insert-at-cursor chips + parsed onset list */
.ve-chips{display:flex;flex-wrap:wrap;gap:4px;margin:4px 0 2px}
.ve-ins{font:inherit;font-size:10px;padding:2px 7px;border-radius:999px;border:1px solid var(--border);background:transparent;color:var(--muted);cursor:pointer;line-height:1.5}
.ve-ins:hover{border-color:var(--accent);color:var(--text);background:var(--accent-tint-soft)}
.ve-list{display:flex;flex-direction:column;gap:3px;margin-top:6px}
.ve-row{display:flex;align-items:center;gap:8px;font-size:11.5px}
.ve-name{color:var(--text);font-weight:600}
.ve-time{color:var(--warn);font-variant-numeric:tabular-nums}
.ve-row.pinned .ve-time{color:var(--ok)}
.ve-pin{background:none;border:1px solid var(--border);border-radius:4px;color:var(--muted);cursor:pointer;font-size:10px;padding:1px 5px}
.ve-pin:hover{border-color:var(--accent);color:var(--text)}
.ve-unknown{margin-top:5px;font-size:10.5px;color:var(--warn)}
#timelineHelp:empty{display:none}
/* Field comments — faint 💬 in each label that you hover to add; a SAVED note makes the whole field
   stand out so you can scan for where you left notes. (The old marker — a 2px bar + grayscale 10px
   emoji — was too subtle to notice after Save.) No added height: the field cue is background + an
   inset bar, not a border. */
.cmt-btn{margin-left:auto;border:0;background:transparent;cursor:pointer;font-size:11px;line-height:1;
  padding:0 3px;opacity:0;filter:grayscale(1);transition:opacity .12s}
.field:hover .cmt-btn{opacity:.45}
.cmt-btn:hover{opacity:1!important;filter:none}
.cmt-btn.on{opacity:1;filter:none}                              /* commented: full-colour 💬, always visible */
/* Size-INDEPENDENT field cue (a thin inset ring + tint), so it reads the same on a one-line field and
   on a tall container field like Tracks — a left bar that scaled with height became a giant blue line. */
.field.has-comment{background:var(--accent-tint-soft);box-shadow:inset 0 0 0 1px var(--accent-tint-strong);border-radius:6px}
.field.has-comment > label{color:var(--accent-on)}
/* The always-on 💬 should also read as a small filled chip so it's obvious next to the label text. */
.cmt-btn.on{background:var(--accent-tint-soft);border-radius:5px;padding:1px 4px}
/* Triage popover — per-field gap breakdown off a completion chip */
/* Triage breakdown — rendered in the right-rail properties panel (was a floating popover). */
.triage-panel{padding:var(--space-2);font-size:12px;color:var(--text)}
.triage-panel .tp-head{display:flex;align-items:center;gap:6px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);padding:2px 4px 8px;margin-bottom:var(--space-1);border-bottom:1px solid var(--border)}
.triage-panel .tp-close{margin-left:auto;font:inherit;font-size:12px;line-height:1;padding:2px 6px;border:1px solid var(--border);border-radius:4px;background:transparent;color:var(--muted);cursor:pointer}
.triage-panel .tp-close:hover{border-color:var(--accent);color:var(--text)}
.triage-panel .tp-rows{display:flex;flex-direction:column;gap:2px}
.triage-panel .tp-row{display:flex;align-items:center;gap:8px;padding:5px 4px;border-radius:5px}
.triage-panel .tp-row:hover{background:var(--panel-2)}
.triage-panel .tp-field{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.triage-panel .tp-count{font-variant-numeric:tabular-nums;color:var(--muted);min-width:16px;text-align:right}
.triage-panel .tp-acts{display:flex;gap:4px;flex-shrink:0}
.triage-panel .tp-act{font:inherit;font-size:11px;padding:2px 8px;border:1px solid var(--border);border-radius:4px;background:transparent;color:var(--muted);cursor:pointer}
.triage-panel .tp-act:hover{border-color:var(--accent);color:var(--accent-on);background:var(--accent-tint-soft)}
.triage-panel .tp-act.tp-bulk{color:var(--accent-on);border-color:var(--accent)}
.triage-panel .tp-allok{padding:6px 4px;color:var(--ok)}
.triage-panel .tp-sub{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--dim);padding:8px 4px 2px}
.triage-panel .tp-rec .tp-field{color:var(--muted)}
.triage-panel .tp-selectall{width:100%;margin-top:8px;font:inherit;font-size:11px;padding:6px;border:1px solid var(--border);border-radius:5px;background:var(--panel);color:var(--muted);cursor:pointer}
.triage-panel .tp-selectall:hover{border-color:var(--accent);color:var(--text)}
#cmtPopover{position:fixed;z-index:var(--z-menu);background:var(--panel);border:1px solid var(--accent);
  border-radius:8px;padding:8px;box-shadow:0 6px 24px rgba(0,0,0,.4);display:flex;flex-direction:column;gap:7px}
#cmtPopover .cmt-text{width:100%;min-height:64px;resize:vertical;background:var(--panel-2);
  border:1px solid var(--border);border-radius:5px;color:var(--text);font:inherit;font-size:12px;padding:6px}
#cmtPopover .cmt-actions{display:flex;align-items:center;gap:6px}
/* Empty-state editor: status card (counts + next action) or cold-start paths */
/* Guide → Analysis: automatic-detection toggles */
.analysis-switches{display:flex;flex-direction:column;gap:2px;margin-top:10px}
.analysis-switch{display:flex;align-items:flex-start;gap:10px;padding:9px 10px;border-radius:8px;cursor:pointer}
.analysis-switch:hover{background:var(--panel-2)}
.analysis-switch input{margin-top:2px;flex:0 0 auto;width:16px;height:16px;cursor:pointer}
.analysis-switch .as-text{display:flex;flex-direction:column;gap:2px}
.analysis-switch .as-label{font-weight:600;color:var(--text);font-size:13px}
.analysis-switch .as-help{font-size:11.5px;color:var(--muted);line-height:1.4}
/* Overview "Primitives" picker — an intro line + a stack of selectable cards. Each card carries
   its lane colour via --pc; selection is shown by the card itself (coloured border + tint + ✓),
   no checkbox. */
.primitives-intro{font-size:11.5px;color:var(--muted);margin:8px 0 2px;line-height:1.4}
.primitives-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:5px}
/* Chips sit one tone LIGHTER than their group card (--panel on the card's --panel-2) so they
   stay distinct in the three-tier nest: column --bg < group card --panel-2 < chip --panel. */
.primitive-card{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:6px;width:100%;text-align:center;cursor:pointer;
  font:inherit;color:var(--text);background:var(--chip-bg);border:1px solid var(--chip-bd);
  border-radius:10px;padding:11px 6px 9px;transition:border-color .12s,background .12s}
.primitive-card:hover{border-color:var(--muted)}
/* Selected: a neutral, desaturated border (a hint of the lane hue blended into the neutral chip
   border) + a very faint tint OVER the chip's own fill — not the full bright lane color. The
   colored icon + ✓ carry identity; brightness/opacity carries the selected state. */
.primitive-card.on{border-color:color-mix(in srgb,var(--pc) 30%,var(--chip-bd));background:color-mix(in srgb,var(--pc) 9%,var(--chip-bg))}
.primitive-card .pc-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:25px;height:25px;color:var(--pc);opacity:.5}
.primitive-card.on .pc-icon{opacity:1}
.primitive-card .pc-icon .type-icon{width:25px;height:25px}
.primitive-card .pc-text{display:flex;flex-direction:column;align-items:center;gap:1px;min-width:0}
.primitive-card .pc-name{font-weight:600;font-size:12px;line-height:1.2}
/* Unselected tracks read as secondary — dim the name (the icon already dims via .pc-icon). */
.primitive-card:not(.on) .pc-name{opacity:.7}
/* Hidden-span count: a circle for a single digit, growing into a capsule when wider numbers need it. */
.primitive-card .pc-hidden{position:absolute;top:4px;left:5px;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:16px;height:16px;padding:0 4px;font-size:9px;font-weight:700;line-height:1;color:var(--warn);background:var(--warn-tint);border-radius:999px}
.primitive-card .pc-check{position:absolute;top:5px;right:6px;width:14px;text-align:center;color:var(--ok);font-weight:700;opacity:0;transition:opacity .12s}  /* status mark = one semantic color; the card body owns identity color */
.primitive-card.on .pc-check{opacity:1}
/* Default-track marker: the ✓ gains a ring → "auto-loads on new videos" (the 3rd state in the
   card's off → on → on+default click cycle). A default track is always on, so the check is shown. */
.primitive-card.is-default .pc-check{width:16px;height:16px;top:4px;right:5px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;font-size:10px;box-shadow:inset 0 0 0 1.5px var(--ok)}
/* ★ dominant toggle — its own click target in the top-left corner of a selected chip. */
.primitive-card .pc-star{position:absolute;top:3px;left:4px;display:inline-flex;align-items:center;justify-content:center;
  width:18px;height:18px;border-radius:50%;font-size:13px;line-height:1;cursor:pointer;
  color:var(--dim);transition:color .12s,background .12s}
.primitive-card .pc-star:hover{color:var(--warn);background:var(--warn-tint)}
.primitive-card .pc-star.on{color:var(--warn)}
/* Dominant chip: a warm hairline so it reads at a glance alongside the lane-colored selection. */
.primitive-card.dominant{border-color:color-mix(in srgb,var(--warn) 45%,var(--chip-bd))}
/* Legend keys in the intro line. */
.primitives-intro .pi-key{display:inline-block;font-weight:700;color:var(--muted)}
.primitives-intro .pi-key.pi-star{color:var(--warn)}
.primitives-intro .pi-key.pi-ring{border-radius:50%;box-shadow:inset 0 0 0 1.3px currentColor;width:15px;height:15px;line-height:13px;text-align:center;font-size:9px}
.seg-editor.empty .es-card{display:flex;flex-direction:column;gap:10px;align-items:flex-start;padding:4px 2px;text-align:left}
.es-title{font-weight:600;color:var(--muted);font-size:11px}
.es-sub{font-size:11px;color:var(--muted);line-height:1.5}
.es-paths{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:6px;font-size:11.5px;color:var(--muted)}
.es-counts{display:flex;flex-wrap:wrap;gap:12px;font-size:11.5px;color:var(--text);font-weight:600}
.es-count{display:inline-flex;align-items:center;gap:5px}
.es-left{margin-left:5px;color:var(--warn);font-weight:700}
.es-ok{margin-left:5px;color:var(--ok);font-weight:700}
.es-gaps{font-size:11.5px;color:var(--warn)}
/* Gap highlight — a longer multi-pulse so it's easy to catch (and re-catch) without re-jumping. */
.q-flash{animation:qflash 2.8s ease-out;border-radius:6px}
@keyframes qflash{
  0%{background:var(--accent-tint-strong)}
  20%{background:var(--accent-tint-soft)}
  40%{background:var(--accent-tint-strong)}
  60%{background:var(--accent-tint-soft)}
  80%{background:var(--accent-tint-strong)}
  100%{background:transparent}
}
.topbar .saved-indicator{font-size:10px;color:var(--muted);font-style:italic;letter-spacing:.2px;font-variant-numeric:tabular-nums}
/* Failed-write state — save truthfulness is load-bearing, so a failed persist reads as alarming, not muted. */
.saved-indicator.save-failed{color:var(--danger);font-style:normal;font-weight:600}
.btn.icon-btn{padding:4px 7px;display:inline-flex;align-items:center;justify-content:center}
.btn{
  background:var(--accent);border:0;color:white;padding:6px 12px;border-radius:6px;
  cursor:pointer;font-weight:600;font-size:12px;
}
.btn:hover{background:var(--accent-2)}
.btn.ghost{background:transparent;border:1px solid var(--border);color:var(--text)}
.btn.ghost:hover{background:var(--panel)}
.btn.sm{padding:3px 8px;font-size:11px;font-weight:500}
.btn.xs{padding:2px 6px;font-size:10px;font-weight:500}
.btn:disabled{opacity:.4;cursor:not-allowed}
.btn:disabled:hover{background:transparent}

/* ============================== RULES PANEL (left sidebar of col-video) ============================== */
/* Rules section — now lives in the left column as a collapsible <details> block */
.rules-section{border-bottom:1px solid var(--border);flex-shrink:0}
.rules-section > summary{
  list-style:none;cursor:pointer;
  display:flex;align-items:center;gap:6px;
  padding:5px 8px 5px 12px;background:var(--panel-2);
  font-size:10px;font-weight:600;color:var(--muted);
  text-transform:uppercase;letter-spacing:.6px;user-select:none;
}
.rules-section > summary::-webkit-details-marker{display:none}
.rules-section > summary .rules-chev{
  margin-left:auto;transition:transform .15s;flex-shrink:0;
}
.rules-section[open] > summary .rules-chev{transform:rotate(90deg)}
.rules-section .rules-body{
  padding:10px 12px;font-size:11px;color:var(--text);line-height:1.5;
}
.rules-section .rules-body b{color:var(--text)}
.rules-section .rules-body ul{margin:6px 0 0;padding-left:18px}
.rules-section .rules-body li{margin-bottom:5px}
.rules-section .rules-body li b{color:var(--accent-on)}

/* ============================== EMPTY STATE / IN-VIDEO LOADER ============================== */
/* WarpDiff-style framed CTA: rounded box with a slowly spinning conic-gradient
   border + a gently pulsing logo. (Starfield deliberately omitted.) */
@property --grad-angle{syntax:'<angle>';initial-value:0deg;inherits:false}
.empty-state .es-frame{
  position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;
  width:360px;aspect-ratio:16/9;margin-bottom:14px;cursor:pointer;
}
@keyframes es-logo-pulse{0%,100%{transform:scale(1);filter:brightness(.82)}50%{transform:scale(1.06);filter:brightness(1.12)}}
.empty-state .es-logo{width:77px;height:77px;display:block;animation:es-logo-pulse 3.2s ease-in-out infinite;opacity:.75}
.empty-state .es-wordmark{font-size:22px;font-weight:700;letter-spacing:.2px;color:var(--text);line-height:1;opacity:.75}
.empty-state .es-wordmark i{font-style:italic}
.empty-state .es-tagline{font-size:11px;color:var(--muted);margin:0;opacity:.75}
.empty-state .es-hints{font-size:12px;color:var(--dim);margin-top:14px}
.empty-state .es-hints kbd{background:rgba(0,0,0,.4);border:1px solid var(--border);border-radius:3px;
  padding:1px 5px;font-family:ui-monospace,Menlo,monospace;font-size:11px;color:var(--muted)}
.empty-state .es-actions{display:flex;flex-direction:column;align-items:center;gap:6px}
.empty-state .es-or{font-size:9px;color:var(--muted);text-transform:uppercase;letter-spacing:.6px}
.empty-state .es-caps{
  font-size:9px;color:var(--dim);margin-top:12px;letter-spacing:.4px;text-transform:uppercase;
}
.empty-state .es-hint{font-size:10px;color:var(--muted);margin-top:8px}
.empty-state .es-hint kbd{
  background:rgba(0,0,0,.4);border:1px solid var(--border);border-radius:3px;
  padding:0 4px;font-family:ui-monospace,Menlo,monospace;font-size:10px;color:white;
}
.video-load-btn{
  position:absolute;top:6px;right:6px;z-index:4;
  width:26px;height:26px;border-radius:5px;
  background:rgba(0,0,0,.55);color:white;border:1px solid rgba(255,255,255,.2);
  font-size:13px;cursor:pointer;
  display:none;align-items:center;justify-content:center;padding:0;line-height:1;
}
.video-load-btn:hover{background:rgba(0,0,0,.85);border-color:white}
.video.has-video .video-load-btn{display:flex}

/* ============================== VIDEO ============================== */
.video-wrap{
  flex:1 1 auto;min-height:0;
  display:flex;flex-direction:column;
  overflow:hidden;
}
/* Media block: the video region flexes; the waveform + controls + transport are fixed (flex:0 0 auto
   below) so they ALWAYS claim their height and can never be clipped by an oversized video. */
.video-unit{
  display:flex;flex-direction:column;
  flex:1 1 auto;min-width:0;min-height:0;
}
.video-unit > .waveform-wrap,
.video-unit > .waveform-controls,
.video-unit > .transport{flex:0 0 auto}
/* The flexible video region — takes whatever height is left after the fixed rows. The video box is
   JS-sized to fit HERE, so it can't push the transport into the divider. Bottom-aligned so a short
   (width-bound) clip sits just above the waveform with the slack above it. */
.video-fit{
  flex:1 1 auto;min-height:0;min-width:0;
  display:flex;overflow:hidden;
}
.video{
  position:relative;background:#000;overflow:hidden;
  /* Full-bleed media region: no border, no fixed aspect — the video fills the whole space above the
     waveform and letterboxes within it (object-fit:contain on #vid), so it's edge-to-edge for any
     aspect (a portrait clip simply uses the full height). The empty-state logo/wordmark/text centre
     in this same space. */
  flex:1 1 auto;min-width:0;min-height:0;
}
.video.has-video::before{display:none}
.video:not(.has-video)::before{
  content:"";position:absolute;inset:0;
  background:radial-gradient(ellipse at 30% 20%,#2a2a2a 0%,#0a0a0a 70%);
}
.video .label{
  position:absolute;left:10px;top:8px;font-size:11px;color:var(--muted);
  background:rgba(0,0,0,.5);padding:2px 6px;border-radius:4px;z-index:3;pointer-events:none;
}
.video.has-video .label{display:none}
.video #vid{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:contain;background:#000;z-index:1;
}
.video .empty-state{
  position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;text-align:center;z-index:2;
}
.video.has-video .empty-state{display:none}

/* ============================== TRANSPORT ============================== */
.transport{
  display:flex;align-items:center;gap:8px;padding:6px 12px;border-top:1px solid var(--border);
  font-variant-numeric:tabular-nums;flex-shrink:0;
  /* Wrap instead of clipping: the bar packs ~800px of fixed-width controls (playback + loudness
     cluster + monitoring toggles + speed). On a narrow window the monitoring cluster flows onto a
     second row rather than spilling off the right edge. Wide windows have room and never wrap.
     row+column gap is the existing 8px. */
  flex-wrap:wrap;
  /* justify-content:center is INERT on a wide bar: .tr-left/.tr-right's flex:1 (below) already
     grow to consume every free pixel, leaving nothing left for justify-content to distribute —
     it only takes visible effect once the narrow-bar rule (below) turns that growth off and a
     wrapped row has real leftover space. On an iPhone-width transport that wraps to 2-3 rows,
     each row then centers itself instead of left-hugging with a big gap on the right (2026-07
     iPhone report: "icons wrap to two lines" with no centering). */
  justify-content:center;
}
.tcontrol{
  background:var(--transport-bg,var(--panel));border:1px solid var(--transport-bd,var(--border));border-radius:6px;
  width:34px;height:34px;display:flex;align-items:center;justify-content:center;cursor:pointer;
  font-size:22px;line-height:1;
  flex-shrink:0;   /* fixed icon buttons: when the bar is over-full they WRAP to a new row (see
                      .transport flex-wrap), they don't compress below 34px and cram/clip. */
}
/* Transport zones: timecode (left) · playback group (mid) · monitoring cluster (right). The two
   flex:1 spacers center the playback group on a wide bar. They're classes (not inline) so the
   narrow-bar container query below can neutralize them. */
.tr-zone{display:flex;align-items:center}
.tr-left{flex:1}
.tr-mid{gap:8px}
.tr-right{flex:1;min-width:0;justify-content:flex-end;gap:8px}
/* Narrow bar: drop the flex:1 grow so the timecode no longer hogs row 1 and the monitoring
   cluster no longer gets crushed — each zone shrinks to its own content width, so whichever rows
   it wraps onto, .transport's justify-content:center (above) centers that row instead of packing
   it against one edge. (No margin-left:auto on .tr-right anymore — that fought centering by
   always shoving it to the far right regardless of how much room the row actually had.) The
   transport is container-type:inline-size (below), so this tracks the BAR width, not the window
   — correct when a wide rail squeezes the center column. */
@container (max-width:920px){
  .tr-left{flex:0 0 auto}
  .tr-right{flex:0 0 auto}
}
.tcontrol .ic{display:block;vertical-align:0}
.tcontrol[data-seek]{padding-bottom:4px}
.tcontrol:hover{background:var(--panel-2)}
.tcontrol.on{background:var(--accent-tint-soft);border-color:var(--accent);color:var(--accent-on)}
.tcontrol.on:hover{background:var(--accent-tint-med)}
/* Audition loop = auto-armed by focusing a Transcription field (NOT a manual Shift+L loop). Distinct
   look so its glow is never mistaken for "I turned looping on": a dashed border + a slow pulse reads
   "temporary · listening" vs the steady fill of a manual loop. Form-based — no new colour axis. */
.tcontrol.on.auditioning{border-style:dashed;animation:audition-pulse 1.5s ease-in-out infinite}
@keyframes audition-pulse{0%,100%{box-shadow:0 0 0 0 transparent}50%{box-shadow:0 0 7px 0 var(--accent)}}
@media (prefers-reduced-motion:reduce){
  .tcontrol.on.auditioning{animation:none;box-shadow:inset 0 0 0 2px var(--accent)}
}

/* Clarity tuning panel (PROTOTYPE) — floating A/B-each-link card. */
.clarity-panel{
  position:fixed;top:74px;right:18px;width:300px;max-height:78vh;overflow-y:auto;z-index:var(--z-panel-float);
  background:var(--panel);border:1px solid var(--border);border-radius:8px;
  box-shadow:0 8px 28px rgba(0,0,0,.45);padding:10px 12px;font-size:11px;color:var(--text);
}
.clarity-panel[hidden]{display:none}
.cp-title{display:flex;align-items:center;justify-content:space-between;font-weight:700;font-size:12px;margin-bottom:8px}
.cp-title span{font-weight:400;color:var(--muted);font-size:10px}
.cp-x{border:0;background:transparent;color:var(--muted);font-size:15px;cursor:pointer;line-height:1}
.cp-x:hover{color:var(--text)}
.cp-row{border:1px solid var(--border);border-radius:6px;padding:6px 8px;margin-bottom:6px;background:var(--panel-2)}
.cp-row.off{opacity:.5}
.cp-head{display:flex;align-items:center;justify-content:space-between}
.cp-en{display:flex;align-items:center;gap:6px;cursor:pointer}
.cp-en b{font-weight:600}
.cp-solo{border:1px solid var(--border);background:var(--panel);color:var(--muted);font-size:9px;text-transform:uppercase;letter-spacing:.05em;border-radius:3px;padding:1px 6px;cursor:pointer}
.cp-solo:hover{border-color:var(--accent);color:var(--accent-on)}
.cp-hint{color:var(--muted);font-size:10px;margin:3px 0 5px}
.cp-params{display:flex;flex-direction:column;gap:4px}
.cp-param{display:grid;grid-template-columns:34px 1fr 40px;align-items:center;gap:6px;color:var(--muted)}
.cp-param input[type=range]{width:100%}
.cp-val{font-variant-numeric:tabular-nums;color:var(--text);text-align:right}
.cp-foot{display:flex;gap:6px;margin-top:8px}
.cp-foot button{flex:1;border:1px solid var(--border);background:var(--panel-2);color:var(--text);border-radius:4px;padding:3px 0;cursor:pointer;font-size:10px}
.cp-foot button:hover{border-color:var(--accent)}
/* Deep-clean tune panel — reuses .clarity-panel + .cp-* ; these are its method-picker + extras. */
.cln-methods{display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-bottom:8px}
.cln-m{border:1px solid var(--border);background:var(--panel-2);color:var(--text);border-radius:5px;padding:5px 6px;cursor:pointer;font-size:11px;text-align:left}
.cln-m:hover:not(:disabled){border-color:var(--accent)}
.cln-m.on{background:var(--accent-tint-soft);border-color:var(--accent);color:var(--accent-on);font-weight:600}
.cln-m:disabled{opacity:.45;cursor:not-allowed}
.cln-toggles{display:flex;flex-wrap:wrap;gap:10px;margin-top:6px}
.cln-model{margin-top:8px;font-size:11px;color:var(--muted);display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.cln-load{cursor:pointer;color:var(--accent-on);border:1px solid var(--accent);border-radius:4px;padding:3px 8px;background:var(--accent-tint-soft)}
.cln-load input[type=file]{display:none}
.cln-filter{margin-top:8px;font-size:10px;color:var(--muted);overflow-x:auto}
.cln-filter code{font-family:ui-monospace,Menlo,monospace;white-space:nowrap}
.cln-go{background:var(--accent)!important;border-color:var(--accent)!important;color:var(--accent-on)!important;font-weight:600;flex:2!important}

/* Loop range marker — sits inside the waveform area to show the loop scope */
.waveform-loop{
  position:absolute;top:0;bottom:0;
  background:var(--accent-tint-soft);
  border-left:1px dashed var(--accent);border-right:1px dashed var(--accent);
  pointer-events:none;display:none;z-index:1;
}
.waveform-loop.active{display:block}
.waveform-loop .loop-flag{
  position:absolute;top:2px;font-size:9px;color:var(--accent-on);
  background:var(--accent-tint-strong);padding:1px 5px;border-radius:3px;
  font-weight:600;letter-spacing:.4px;text-transform:uppercase;
}
.waveform-loop .loop-flag.left{left:-1px}
/* Unsure sub-range bands (Thread B) — amber wash over the stretches the annotator flagged while
   transcribing. Container is inert; the bands themselves are clickable (clear-on-click). Sits under
   the selection/playhead layers so those stay legible. */
.waveform-unsure{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:none;z-index:1}
.wf-unsure-band{
  position:absolute;top:0;bottom:0;pointer-events:auto;cursor:pointer;
  background:repeating-linear-gradient(45deg,var(--warn-tint,color-mix(in srgb,var(--warn) 20%,transparent)) 0 6px,color-mix(in srgb,var(--warn) 8%,transparent) 6px 12px);
  border-left:1px solid var(--warn);border-right:1px solid var(--warn);
}
.wf-unsure-band:hover{background:color-mix(in srgb,var(--warn) 34%,transparent)}
.timecode{color:var(--muted);font-size:12px;white-space:nowrap}
.timecode b{color:var(--text);font-weight:600}
.active-type-pill{
  display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border-radius:11px;
  background:var(--panel-2);border:1px solid var(--border);font-size:11px;color:var(--muted);
}
.active-type-pill .dot{width:8px;height:8px;border-radius:50%}
.speed-wrap{position:relative;display:inline-flex;flex-shrink:0}
.speed-pill{
  display:inline-flex;align-items:center;padding:3px 8px;border-radius:4px;
  background:var(--panel-2);border:1px solid var(--border);font-size:11px;color:var(--muted);
  font-variant-numeric:tabular-nums;cursor:pointer;
  white-space:nowrap;flex-shrink:0;   /* never wrap "1×" or get squeezed on a narrow transport */
}
.speed-pill:hover{border-color:var(--accent);color:var(--text)}
.speed-pill[aria-expanded="true"]{border-color:var(--accent);color:var(--accent-on);background:var(--accent-tint-soft)}
/* Speed picker menu — opens upward (the transport bar sits low), right-aligned to the pill. */
.speed-menu{
  position:absolute;bottom:calc(100% + 6px);right:0;z-index:var(--z-panel-float);min-width:96px;
  background:var(--panel);border:1px solid var(--border);border-radius:6px;
  box-shadow:0 6px 22px rgba(0,0,0,.4);padding:4px;display:flex;flex-direction:column;gap:1px;
}
.speed-menu[hidden]{display:none}
.speed-opt{
  display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;
  padding:4px 8px;border:0;border-radius:4px;background:transparent;color:var(--text);
  font-size:11px;font-variant-numeric:tabular-nums;cursor:pointer;text-align:left;
}
.speed-opt:hover{background:var(--panel-2)}
.speed-opt.on{background:var(--accent-tint-soft);color:var(--accent-on);font-weight:600}
/* Checked state for menu radio items (the Workspace section) — same treatment as .speed-opt.on,
   the app's one existing pick-one-of-N menu. aria-checked is the state owner; this makes it VISIBLE
   (it was ARIA-only: no way to see which workspace was active from the menu). */
.file-menu .fm-item[role="menuitemradio"][aria-checked="true"]{
  background:var(--accent-tint-soft);
}
.file-menu .fm-item[role="menuitemradio"][aria-checked="true"] b{color:var(--accent-on)}
.speed-opt-note{color:var(--muted);font-size:9px;text-transform:uppercase;letter-spacing:.05em}
.speed-opt.on .speed-opt-note{color:var(--accent-on)}

/* Loudness-normalized monitoring control (transport bar). On a narrow (laptop) bar the gain readout
   is the secondary bit — it shrinks/clips first (and hides entirely when very tight) so the LUFS
   target can never overlap it. The toggle + target never shrink. */
/* NO min-width:0 here: that let the transport's flex crush the whole wrap below its protected
   toggle+target, so they overflowed and spilled into the buttons to the right (norm-wrap clientW 49
   vs scrollW 106 at a ~760px transport — the "right side clips" report). min-width:auto floors the
   wrap at toggle+target; the readout still shrinks/ellipsizes via its OWN min-width:0 + overflow. */
.norm-wrap{display:inline-flex;align-items:center;gap:6px}
.norm-readout{
  font-size:10px;color:var(--muted);font-variant-numeric:tabular-nums;
  min-width:0;white-space:nowrap;flex:0 1 auto;overflow:hidden;text-overflow:ellipsis;
}
.norm-target{
  display:inline-flex;align-items:center;padding:2px 7px;border-radius:4px;
  background:var(--panel-2);border:1px solid var(--border);font-size:10px;color:var(--muted);
  font-variant-numeric:tabular-nums;cursor:pointer;transition:opacity .15s;
  white-space:nowrap;flex-shrink:0;   /* keep "−18 LUFS" on one line, don't let it shrink/overlap */
}
/* The transport is a size container so the verbose readout can drop out before it crowds. */
.transport{container-type:inline-size}
@container (max-width:600px){ .transport .norm-readout{display:none} }
.norm-target:hover{color:var(--text);border-color:var(--accent)}

/* HUD flash for quick feedback (mute, speed change, etc.) */
/* Keyboard-focus floor — the ONE :focus-visible rule (there were zero): every interactive
   element gets a visible ring when reached by keyboard; mouse focus stays clean. */
:is(button, select, input, textarea, summary, [tabindex]):focus-visible{
  outline:2px solid var(--accent);outline-offset:1px;
}

#hudFlash{
  /* Below the topbar, NOT dead-center — centered toasts sat exactly on the transport
     controls (the most-clicked strip in the app) and blocked them while visible. */
  position:fixed;top:64px;left:50%;transform:translateX(-50%);
  background:rgba(0,0,0,.75);color:white;padding:10px 18px;border-radius:8px;
  font-size:13px;font-weight:600;letter-spacing:.5px;
  pointer-events:none;opacity:0;transition:opacity .2s;z-index:300;
}
#hudFlash.on{opacity:1}
/* HUD kinds — severity is OWNED by flashHud's kind param (style + duration in one place). */
#hudFlash.hud-error{background:rgba(62,14,14,.94);border:1px solid var(--danger);color:#ffd9d9}
#hudFlash.hud-warning{background:rgba(64,46,6,.94);border:1px solid var(--warn);color:#ffe6b3}
#hudFlash.hud-ok{border:1px solid var(--ok)}

/* Transcode toast — persistent progress banner during ffmpeg.wasm operation */
#transcodeToast{
  position:fixed;bottom:20px;left:50%;transform:translateX(-50%);
  background:var(--panel-3);border:1px solid var(--border);
  border-radius:10px;padding:10px 16px;min-width:260px;max-width:420px;
  box-shadow:0 4px 20px rgba(0,0,0,.5);z-index:500;
  display:none;flex-direction:column;gap:6px;
}
#transcodeToast.visible{display:flex}
.tt-row{display:flex;align-items:center;justify-content:space-between;gap:12px}
.tt-label{font-size:12px;font-weight:600;color:var(--text)}
.tt-sub{font-size:11px;color:var(--muted)}
.tt-bar-wrap{height:3px;background:var(--border);border-radius:2px;overflow:hidden}
.tt-bar{height:100%;background:var(--accent);border-radius:2px;transition:width .3s}

/* Drag-and-drop overlay — appears when files are dragged over the window */
.dnd-overlay{
  position:fixed;inset:0;z-index:var(--z-top);pointer-events:none;
  background:rgba(0,0,0,.55);
  display:flex;align-items:center;justify-content:center;
  animation:dnd-in .15s ease-out;
}
@keyframes dnd-in{from{opacity:0}to{opacity:1}}
.dnd-card{
  background:var(--panel);border:2px dashed var(--accent);border-radius:10px;
  padding:32px 48px;text-align:center;
  box-shadow:0 0 60px rgba(0,0,0,.6);
  color:var(--text);
}
.dnd-icon{color:var(--accent);margin-bottom:12px;display:flex;justify-content:center}
.dnd-title{font-size:18px;font-weight:600;margin-bottom:6px}
.dnd-sub{font-size:12px;color:var(--muted);font-family:ui-monospace,Menlo,monospace}


/* Help modal — WarpDiff-style chunky key chips */
#helpModal .modal{background:var(--panel);display:flex;flex-direction:column;max-height:88vh}
#helpModal .modal-head{padding:14px 18px;background:transparent;flex-shrink:0}
#helpModal .modal-head h2{font-size:15px;font-weight:600}
#helpModal .modal-body{padding:0;overflow-y:auto;flex:1;display:flex;flex-direction:column}
/* Tab nav */
.help-tabs{
  display:flex;gap:0;border-bottom:1px solid var(--border);
  padding:0 18px;flex-shrink:0;
}
.help-tab{
  background:transparent;border:none;border-bottom:2px solid transparent;
  color:var(--muted);font-size:12px;font-weight:500;padding:8px 14px;
  cursor:pointer;margin-bottom:-1px;transition:color .12s,border-color .12s;
}
.help-tab.active{color:var(--text);border-bottom-color:var(--accent)}
.help-tab:hover:not(.active){color:var(--text)}
/* Panes */
.help-pane{padding:18px 22px 24px;flex:1;overflow-y:auto}
.help-pane[hidden]{display:none}
/* Prose pane */
.help-prose h3{
  margin:22px 0 8px;font-size:13px;font-weight:700;color:var(--text);
  padding-bottom:5px;border-bottom:1px solid var(--border);
}
.help-prose h3:first-child{margin-top:0}
.help-prose p{margin:0 0 10px;font-size:12px;line-height:1.65;color:var(--muted)}
.help-prose p strong{color:var(--text)}
.help-prose ul{margin:0 0 10px 16px;padding:0;font-size:12px;line-height:1.65;color:var(--muted)}
.help-prose li{margin-bottom:5px}
.help-prose li strong{color:var(--text)}
.help-prose .fw-ref{font-size:10px;color:var(--dim);font-style:italic;margin-top:-6px;margin-bottom:12px}
/* Span type table */
.span-type-table{width:100%;border-collapse:collapse;font-size:12px;margin-bottom:16px}
.span-type-table th{
  text-align:left;font-size:10px;font-weight:700;color:var(--muted);
  text-transform:uppercase;letter-spacing:.8px;
  padding:0 10px 8px 0;border-bottom:1px solid var(--border);
}
.span-type-table td{
  padding:8px 10px 8px 0;vertical-align:top;
  border-bottom:1px solid rgba(255,255,255,.05);color:var(--text);line-height:1.4;
}
.span-type-table td:first-child{white-space:nowrap;padding-right:16px}
.type-swatch{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:middle}
/* Quick-start steps */
.qs-steps{display:flex;flex-direction:column;gap:14px;margin-bottom:20px}
.qs-step{display:grid;grid-template-columns:24px 1fr;gap:10px;align-items:start}
.qs-num{
  width:24px;height:24px;border-radius:50%;background:var(--accent-tint-soft);
  border:1px solid var(--accent);color:var(--accent-on);font-size:11px;font-weight:700;
  display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:1px;
}
.qs-step p{margin:0;font-size:12px;line-height:1.6;color:var(--muted)}
.qs-step p strong{color:var(--text)}
/* Shortcuts (existing) */
.help-content{display:grid;grid-template-columns:1fr 1fr;gap:30px 36px}
.help-section{margin-bottom:18px}
.help-section h4{
  margin:0 0 10px;font-size:10px;font-weight:600;color:var(--muted);
  text-transform:uppercase;letter-spacing:1.4px;
}
.help-section ul{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:8px}
.help-section li{
  display:grid;grid-template-columns:110px 1fr;gap:14px;
  font-size:13px;align-items:start;line-height:1.3;color:var(--text);
}
.help-section li .keys-col{display:flex;gap:5px;flex-wrap:wrap;align-items:center;margin-top:1px}
/* The description MUST be one element (this wrapper) so the li grid has exactly two children:
   keys-col → col 1, help-desc → col 2. Without it, inline <kbd>/<em> in the description become
   their own grid items and scatter into the 110px key column. */
.help-section li .help-desc{line-height:1.5}
.help-section li .help-desc kbd{margin:0 1px}
.help-section li kbd{
  background:var(--panel-2);border:1px solid var(--border);border-radius:5px;
  padding:3px 8px;
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  font-size:11px;font-weight:500;line-height:1;
  min-width:26px;min-height:22px;text-align:center;
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--text);box-shadow:0 1px 0 rgba(0,0,0,.25);
}
.app[data-theme="light"] .help-section li kbd{
  background:#e8e8e8;border-color:#a0a0a0;
  box-shadow:0 1px 0 #b8b8b8, 0 2px 0 -1px #aaaaaa;
}
/* Light-mode type-tile accents (--tc) generated by _injectTypeColorCSS() from pal.tileTcLight */
/* Light-mode: fix elements that use hardcoded rgba-white borders/fills */
.app[data-theme="light"] .seg{border-color:rgba(0,0,0,.18)}
.app[data-theme="light"] .seg.point{border-color:rgba(0,0,0,.3)}
.app[data-theme="light"] .axw-stop{border-color:rgba(0,0,0,.2)}
.app[data-theme="light"] .axw-stop:hover:not(.active){border-color:rgba(0,0,0,.45)}
.app[data-theme="light"] .intent-type-btn:hover:not(.active){border-color:rgba(0,0,0,.25)}
.app[data-theme="light"] .cell-edit:focus{background:rgba(0,0,0,.05)}
.app[data-theme="light"] .waveform-vzoom button{color:rgba(0,0,0,.5)}
.app[data-theme="light"] .waveform-vzoom button:hover{background:rgba(0,0,0,.08);color:var(--text)}
.app[data-theme="light"] .waveform-vzoom button.active{background:rgba(0,0,0,.12);color:var(--text)}

/* ============================== TIMELINE / TRACK STACK ============================== */
.timeline-area{
  display:flex;flex-direction:column;flex:1 1 auto;min-height:200px;overflow:hidden;
  border-bottom:1px solid var(--border);
}
.timeline-toolbar{
  display:flex;align-items:center;gap:8px;padding:5px 10px;
  background:var(--panel-2);border-bottom:1px solid var(--border);font-size:11px;
}
.timeline-toolbar .help{color:var(--muted);flex:1}
/* touch-action:pan-y, NOT pan-x: every scrubbable row (.ruler-area/.track-header-area/
   .lane-area — startTimelineScrub's targets) lives inside this scroller, so pan-x let the
   browser's native horizontal pan fight the JS scrub drag on every touchmove — competing
   with the rAF-scheduled seekTo/audio-grain updates (the reported choppiness), and rubber-
   banding at the clip start made the whole scrolled content (ruler ticks, lanes) visibly
   shift under the sticky gutter — worst when scrubbing backward, since that's exactly where
   backward drags run into the t=0 boundary. pan-y keeps native (smooth, compositor-driven)
   vertical scrolling for the track list; horizontal touch drags are now scrub-only, with NO
   native pan fallback since pan-x is gone — the two-finger pan in ui/timeline.js's pinch
   handler is the replacement gesture for navigating a zoomed-in timeline on touch. No
   effect on mouse/trackpad (touch-action only governs touch input). */
.timeline-scroll{flex:1;overflow:auto;background:var(--timeline-bg,var(--panel-2));touch-action:pan-y}
.timeline-scroll::-webkit-scrollbar{width:8px;height:8px}
.timeline-scroll::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:4px}

.timeline-grid{
  display:grid;grid-template-columns:var(--gutter) 1fr;
  grid-auto-rows:min-content;align-content:start;
  position:relative;min-width:100%;
}
/* Empty state — shown when no tracks are selected (the prompt to choose tracks). */
.timeline-empty{grid-column:1 / -1;display:flex;align-items:center;justify-content:center;min-height:200px;padding:24px 16px}
.timeline-empty-cta{
  display:flex;flex-direction:column;align-items:center;gap:6px;text-align:center;max-width:380px;
  cursor:pointer;font:inherit;color:var(--text);
  background:var(--group-card-bg);border:1px dashed var(--border);border-radius:12px;padding:20px 28px;
  transition:border-color .15s,background .15s;
}
.timeline-empty-cta:hover{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 9%,var(--group-card-bg))}
.timeline-empty-cta .te-title{font-weight:700;font-size:14px}
.timeline-empty-cta .te-sub{font-size:12px;color:var(--muted);line-height:1.45}
.zoom-pill{
  position:absolute;bottom:6px;right:10px;z-index:var(--z-sticky);
  font-size:10px;font-weight:600;color:var(--muted);
  background:var(--panel-2);border:1px solid var(--border);
  border-radius:4px;padding:2px 6px;cursor:pointer;user-select:none;
  transition:color .15s,border-color .15s;
}
.zoom-pill:hover{color:var(--fg);border-color:var(--accent)}
.zoom-pill.active{color:var(--accent-on);border-color:var(--accent);background:var(--accent-tint-soft)}
/* Phone zoom buttons — hidden by default; the ≤700px block shows them (touch has no
   Ctrl+scroll / = / − keys, so the pill alone left zoom undiscoverable — user report). */
.zoom-btns{
  display:none;position:absolute;bottom:34px;right:10px;z-index:var(--z-sticky);
  flex-direction:column;gap:6px;
}
.zoom-btns button{
  width:34px;height:34px;border-radius:6px;font-size:17px;line-height:1;
  background:var(--panel-2);border:1px solid var(--border);color:var(--text);
  cursor:pointer;padding:0;
}
.zoom-btns button:active{border-color:var(--accent);color:var(--accent-on)}

.ruler-gutter{
  background:var(--panel-2);border-bottom:1px solid var(--border);
  border-right:1px solid var(--border);height:20px;
  position:sticky;top:0;left:0;z-index:6;
}
/* touch-action:none on the THREE scrub-drag targets (.ruler-area/.track-header-area/
   .lane-area — startTimelineScrub's hitEl), not just .timeline-scroll: pan-y at the
   scroller level still let the browser's gesture recognizer treat an imperfectly-straight
   drag (any real finger has some vertical wobble) as an ambiguous/vertical gesture and hand
   it to native scrolling early — pointer-cancelling the scrub after only a few pixels (2026-07
   report: "can scrub only very short distances", and backward scrubs never got far enough
   for the video's slower backward-seek decode to visibly catch up, reading as "doesn't scrub
   backward" at all). none here makes these rows pure scrub surfaces with NO native gesture
   competing, at either axis — .timeline-scroll keeps pan-y as the fallback so the (non-scrub)
   sticky gutter strip stays touch-scrollable for the track list. */
.ruler-area{
  position:sticky;top:0;height:20px;background:var(--panel-2);
  border-bottom:1px solid var(--border);font-size:11px;color:var(--muted);z-index:5;
  cursor:pointer;touch-action:none;
}
.ruler-area .tick{position:absolute;top:12px;width:1px;height:6px;background:var(--border)}
.ruler-area span{position:absolute;transform:translateX(-50%);padding-top:0}
.ruler-area span.ruler-lbl-first{transform:translateX(0)}      /* left-align the 0:00 label at the start line */
.ruler-area span.ruler-lbl-last{transform:translateX(-100%)}   /* right-align the final label so it stays visible */

/* track structure: header row + lane rows */
.track-header-gutter{
  background:var(--panel);border-bottom:1px solid var(--border);
  border-right:1px solid var(--border);
  padding:0 6px;display:flex;align-items:center;gap:4px;
  position:sticky;left:0;z-index:4;height:26px;
}
.track-header-area{height:26px}
.track-header-area{
  background:var(--lane-bg,var(--panel-2));border-bottom:1px solid var(--lane-bd,var(--border));
  position:relative;overflow:hidden;z-index:0;touch-action:none;   /* see .ruler-area's comment */
}
.track-header-area.dim{background:var(--panel-2);opacity:.6}
.track-header-area.lane-area-inline{cursor:col-resize}
/* Hover feedback on EVERY type's header-row timeline area — not just the inline-lane types
   (sfx/ambience/artifacts/subtitles). Speech & music render a bare header (their lanes sit
   below with subject gutters), but the row should still light up on hover for consistency.
   Strips have their own hover (var(--panel)), so exclude them. */
.track-header-area:not(.lane-area-strip):hover{background:var(--panel)}

/* Section separator — visual break between type stacks */
.track-header-gutter.section-start,.track-header-area.section-start{
  border-top:none;
}
/* Per-type color wash on track headers — dim when inactive, bright when active.
   Gutters: background-color sets the opaque base; background-image overlays the tint.
   This prevents spans from showing through the sticky gutter when scrolled. */
/* .track-header-gutter / .track-header-area per-type washes (base .08, active pal.activeAlpha)
   generated by _injectTypeColorCSS() */
/* Active type left bar */
.track-header-gutter.active{box-shadow:inset 3px 0 0 var(--active-type-color,var(--accent))}
.lane-gutter.active{box-shadow:inset 3px 0 0 var(--active-type-color,var(--accent))}

/* Collapse chevron in header */
.track-chevron{
  background:transparent;border:0;color:var(--muted);
  width:16px;height:16px;display:flex;align-items:center;justify-content:center;
  cursor:pointer;padding:0;border-radius:3px;flex-shrink:0;
}
.track-chevron:hover{color:var(--text);background:rgba(255,255,255,.06)}
.app[data-theme="light"] .track-chevron:hover{background:rgba(0,0,0,.06)}

/* Strip view — single condensed row showing positions only */
.lane-area-strip{
  cursor:pointer;
  background:var(--lane-bg,var(--panel-2));
}
.lane-area-strip:hover{background:var(--panel)}
.strip-mark{
  position:absolute;top:4px;bottom:4px;border-radius:1px;
  min-width:2px;pointer-events:auto;
  opacity:.85;cursor:pointer;
}
.strip-mark:hover{opacity:1;transform:scaleY(1.2)}
/* .strip-mark.<type> backgrounds generated by _injectTypeColorCSS() */
.strip-mark.point{width:2px;background:var(--effects)}
.strip-mark.selected{box-shadow:0 0 0 1px #fff,0 0 4px var(--accent-tint-strong);opacity:1}

/* lane rows */
.lane-gutter{
  background:var(--panel-2);border-bottom:1px solid var(--border);
  border-right:1px solid var(--border);
  padding:0 8px;display:flex;align-items:center;
  font-size:13px;color:var(--muted);
  height:var(--lane-h);
  position:sticky;left:0;z-index:3;
}
.lane-gutter .subject-name{
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  color:var(--text);font-weight:500;font-size:13px;
  padding:1px 5px;border-radius:3px;
}
.lane-gutter.subject-lane{padding-left:28px}
.lane-gutter.subject-lane::before{
  content:'└';font-size:10px;color:var(--muted);margin-right:4px;flex-shrink:0;
}
.lane-gutter.subject-lane .subject-name{font-size:11px}
.lane-gutter .subject-name.clickable{cursor:pointer;transition:background .1s}
.lane-gutter .subject-name.clickable:hover{background:var(--accent-tint-soft);color:var(--accent-on)}
.lane-gutter .subject-name.selected{background:var(--accent-tint-med);color:#fff;box-shadow:inset 0 0 0 1px var(--accent)}
.lane-gutter .subject-name.multi-sel{background:var(--accent-tint-soft);color:var(--accent-on);box-shadow:inset 0 0 0 1px var(--accent)}
.lane-gutter .subject-name.unassigned{color:var(--muted);font-style:italic;font-weight:400;cursor:default}
.lane-area{
  background:var(--lane-bg,var(--panel-2));border-bottom:1px solid var(--lane-bd,var(--border));
  position:relative;height:var(--lane-h);
  overflow:hidden;z-index:0;touch-action:none;   /* see .ruler-area's comment, styles.css */
}
.lane-area{cursor:col-resize}
/* The most-clicked element gets real hover feedback (was cursor-only) — a subtle lift
   that reads on every track color without competing with the .selected outline. */
.seg{transition:filter .1s, box-shadow .1s}
.seg:hover{filter:brightness(1.15)}
.seg.selected:hover{filter:none}
.lane-area:hover{background:var(--panel)}
/* Speech join-gap lint (Thread C) — a dotted amber connector across the <3s gap between two speech
   spans our rule says to string together. Sits in the empty gap (between the tiles, never over them);
   inert so lane scrubbing still works. End caps bracket the exact span edges. */
.lane-joingap{
  position:absolute;top:50%;transform:translateY(-50%);height:0;min-width:3px;
  border-top:2px dotted var(--warn);pointer-events:none;z-index:1;
}
.lane-joingap::before,.lane-joingap::after{content:"";position:absolute;top:-3px;width:2px;height:6px;background:var(--warn)}
.lane-joingap::before{left:0}
.lane-joingap::after{right:0}
.lane-area.dim{opacity:.4}
.lane-area.drop-target{background:rgba(99,179,237,.15);outline:1px solid rgba(99,179,237,.5);outline-offset:-1px;z-index:1}

/* type tile (lit/dim) */
.type-tile{
  display:inline-flex;align-items:center;gap:5px;padding:0 4px;
  font-size:12px;font-weight:600;cursor:pointer;border:none;background:transparent;flex:1;min-width:0;
  line-height:1;text-align:left;
}
.type-tile .tname{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.type-tile .tcount{font-size:10px;opacity:.7;margin-left:2px}
/* type tile color — --tc per type generated by _injectTypeColorCSS() (pal.tileTc);
   modulate with color-mix per state */
.type-tile         {color:color-mix(in srgb,var(--tc) 35%,transparent)}
.type-tile.lit     {color:color-mix(in srgb,var(--tc) 65%,transparent)}
.type-tile.active  {color:var(--tc)}

.track-controls{display:flex;gap:2px}
.track-controls button{
  background:transparent;border:1px solid var(--border);color:var(--muted);
  width:22px;height:22px;border-radius:3px;cursor:pointer;font-size:14px;
  display:flex;align-items:center;justify-content:center;padding:0;
}
.track-controls button:hover{color:var(--accent);border-color:var(--accent)}
/* Hidden-track strip — shown for a track that's off but still has spans, so an accidental hide can't
   silently drop it from the timeline. One click on Show restores it. */
.track-hidden-strip{
  display:flex;align-items:center;gap:8px;padding:4px 12px;
  font-size:11px;color:var(--muted);background:var(--panel-2);border-bottom:1px solid var(--border);
}
.track-hidden-strip .type-ic{opacity:.7;display:inline-flex;align-items:center;flex-shrink:0}
.track-hidden-strip .type-ic .type-icon{width:14px;height:14px}
.track-hidden-strip .ths-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.track-hidden-strip .ths-show{
  background:transparent;border:1px solid var(--border);color:var(--muted);
  border-radius:4px;padding:1px 9px;font-size:11px;cursor:pointer;flex-shrink:0;
}
.track-hidden-strip .ths-show:hover{border-color:var(--accent);color:var(--accent)}

.hidden-data-badge{
  font-size:9px;color:var(--warn);margin-left:4px;
}

/* spans */
.seg{
  position:absolute;top:1px;bottom:1px;border-radius:2px;
  cursor:pointer;display:flex;align-items:center;padding:0 4px;
  font-size:9px;color:white;font-weight:600;overflow:hidden;
  border:1px solid rgba(255,255,255,.2);
  user-select:none;
  container-type:inline-size;   /* the .stext label queries the BAR's own width (narrow-bar hide below) */
}
/* .seg.<type> backgrounds generated by _injectTypeColorCSS() (pal.seg; bright warm hues
   are darkened there so white seg text stays legible) */
/* Selection is signaled by BRIGHTNESS: a span is full color ONLY when it's the
   selected one. So full-bright ⟺ selected in both directions — when nothing is
   selected every span sits at the dim resting level (selecting one "lights it up"),
   which keeps "all bright" from reading as "all selected". */
.seg.selected{z-index:5}
/* Dim the BAR with an inset wash (paints under the text), and dim the TEXT only
   slightly — so unselected spans recede but their labels stay readable. Applies to
   the non-selected spans when something IS selected, and to ALL spans when nothing is. */
.timeline-grid.has-sel .seg:not(.selected),
.timeline-grid:not(.has-sel) .seg{box-shadow:inset 0 0 0 9999px rgba(0,0,0,.4);color:rgba(255,255,255,.82)}
/* (Removed) .ambience-prompt — the empty-Ambience "Add full-clip / No ambience" prompt is gone;
   the Ambience primitive (Overview → Primitives) declares its presence now. */
.seg.overlap{outline:2px solid var(--danger);outline-offset:-2px;z-index:4}
.seg.overlap.selected{z-index:5}
.overlap-badge{
  display:inline-flex;align-items:center;gap:2px;
  font-size:9px;font-weight:700;color:var(--danger);
  background:color-mix(in srgb,var(--danger) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--danger) 30%,transparent);
  border-radius:8px;padding:1px 5px;cursor:default;flex-shrink:0;
}
/* Speaker-ID outliers — spans the speaker analysis is least sure about
   (single-region / minority speaker, or a weak match to its assigned voice).
   Amber + dashed reads as "review me", distinct from the solid-red overlap. */
.seg.speaker-outlier{outline:2px dashed var(--warn);outline-offset:-2px;z-index:4}
.seg.speaker-outlier.selected{z-index:5}
.seg .outlier-badge{
  flex-shrink:0;margin-right:2px;font-size:9px;line-height:1;
  pointer-events:none;text-shadow:0 0 2px rgba(0,0,0,.7);
}
.strip-mark.speaker-outlier{box-shadow:0 0 0 1px var(--warn);opacity:1}
.outlier-badge-hdr{
  display:inline-flex;align-items:center;gap:2px;
  font-size:9px;font-weight:700;color:var(--warn);
  background:color-mix(in srgb,var(--warn) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--warn) 30%,transparent);
  border-radius:8px;padding:1px 5px;cursor:pointer;flex-shrink:0;
}
.outlier-badge-hdr:hover{background:color-mix(in srgb,var(--warn) 24%,transparent)}
/* Opt-in "flag possible multi-speaker spans" toggle next to Identify speakers */
.aap-msflag{display:inline-flex;align-items:center;gap:4px;font-size:10px;color:var(--muted);cursor:pointer;user-select:none;white-space:nowrap}
.aap-msflag input{margin:0;cursor:pointer}
.aap-msflag:hover{color:var(--text)}
.seg.dragging{opacity:.95;cursor:grabbing;z-index:6}
.seg.dragging .grab{cursor:inherit}
.seg.drag-source{opacity:.25}
.seg.drag-proxy{
  position:fixed;pointer-events:none;z-index:var(--z-top);
  top:auto;bottom:auto;
  opacity:.85;box-shadow:0 4px 16px rgba(0,0,0,.5);
  cursor:grabbing;
}
/* Wider (8px) and extended ~3px OUTWARD so the edge of a span flush against the
   timeline boundary is still grabbable — the old 5px inset handle left a dead
   strip of lane (which scrubs) between it and the boundary. The lane's
   overflow:hidden harmlessly clips the outward sliver at the very edge. */
.seg .grab{position:absolute;top:0;bottom:0;width:8px;cursor:ew-resize;z-index:3}
.seg .grab.l{left:-3px}
.seg .grab.r{right:-3px}
.seg .stext{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;pointer-events:none}
/* Unnamed span: the dim type placeholder ("(line)", "(lyric)"…) signals "complete me in Properties". */
.seg .stext.is-empty{opacity:.6;font-style:italic;font-weight:500}
/* A label only earns its space when the bar can show a few characters — below that, show just the
   colored bar + tooltip (like ticks do), never a clipped "doo…" fragment. Tracks the seg's own width. */
@container (max-width:44px){ .seg .stext{display:none} }

/* Tick rendering for very short event spans (duration < TICK_DURATION_S).
   Class applied by renderSegmentEl / renderStripMark based on duration alone — no data flag. */
.seg.point{
  width:6px !important;border-radius:2px;padding:0;
  background:var(--effects);
  border-color:rgba(255,255,255,.6);
}

/* timeline-area is the positioning context for the playhead overlay */
.timeline-area{position:relative}
/* playhead overlay — anchored to the visible viewport, NOT the scrolling grid */
.playhead-overlay{
  position:absolute;top:0;bottom:0;left:var(--gutter);right:0;
  pointer-events:none;z-index:7;overflow:hidden;
}
/* Lane viewport band — the time slice the (independently zoomed) waveform is magnifying, drawn on
   the full-clip lane overview so the relationship is clear. Derived read of waveformView (see
   _updateWaveformBand); sits behind the playhead line (DOM order). */
.wf-zoom-band{
  position:absolute;top:0;bottom:0;display:none;pointer-events:none;
  background:var(--accent-tint-soft);
  border-left:1.5px solid var(--accent);border-right:1.5px solid var(--accent);
}
.playhead{
  position:absolute;top:0;bottom:0;width:1px;background:var(--text);
  opacity:1;
  pointer-events:auto;cursor:ew-resize;
}
/* Widen the hit zone around the 2px line so it's easy to grab */
.playhead::after{
  content:"";position:absolute;top:0;bottom:0;left:-5px;right:-5px;
}
.playhead::before{
  content:"";position:absolute;top:0;left:-5px;width:12px;height:8px;background:var(--text);
  clip-path:polygon(0 0,100% 0,50% 100%);
}
.playhead:hover,.playhead.dragging{opacity:1}

/* pending in-point indicator (when I has been pressed) */
.pending-in{
  position:absolute;top:0;bottom:0;width:2px;background:var(--warn);opacity:.8;
}
.pending-in[hidden]{display:none}
.pending-in::before{
  content:"I";position:absolute;top:-2px;left:-4px;width:10px;height:11px;
  background:var(--warn);color:#000;font-size:9px;font-weight:700;
  display:flex;align-items:center;justify-content:center;border-radius:2px;
}
/* Pending out-point marker — mirror of pending-in, so a range can be marked in either order. The
   "O" label (vs "I") distinguishes the two; the chip sits on the right edge of the 2px line. */
.pending-out{
  position:absolute;top:0;bottom:0;width:2px;background:var(--warn);opacity:.8;
}
.pending-out[hidden]{display:none}
.pending-out::before{
  content:"O";position:absolute;top:-2px;right:-4px;width:10px;height:11px;
  background:var(--warn);color:#000;font-size:9px;font-weight:700;
  display:flex;align-items:center;justify-content:center;border-radius:2px;
}

/* ============================== SEGMENT EDITOR ============================== */
.seg-editor{
  background:var(--bg);
  padding:8px 12px;flex:1 1 auto;overflow-y:auto;
  position:relative;
}
/* The editor's own padding-top owns the gap under the header — the first block must not add its
   margin on top (that stacked --space-2 padding + --space-2 margin into a doubled gap). */
.seg-editor > :first-child{margin-top:0}
/* ── Diarization workspace: the transcript rail (ui/transcript-rail.js) ──────
   One node per speech utterance in the Properties column. Quiet cards; the selected node
   carries the accent left-bar (mirrors the props-id accent convention). Timecodes are the
   app font + tabular-nums (the 2026-07-08 timecode-font rule — no monospace). */
/* Workspace marker class (toggled by setWorkspace). The rail is a full-bleed card list, so the
   workspace tightens the editor gutter; future diarization-only styling scopes under this class. */
.app.ws-diarization .seg-editor{padding:8px}
.transcript-rail{display:flex;flex-direction:column;gap:6px}
/* Rail header bar: the audition-loop length control (shared .audition-len owner) + the
   persistent keyboard hint. Sticky so the listening controls stay at hand down a long list. */
.tr-bar{
  position:sticky;top:-8px;z-index:2;   /* -8px cancels the .seg-editor padding so it pins flush */
  display:flex;align-items:center;gap:8px;
  background:var(--bg);padding:4px 2px 5px;margin:-4px 0 0;
  border-bottom:1px solid var(--border);
}
.tr-bar-hint{font-size:10px;color:var(--dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tr-node{
  background:var(--panel-2);border:1px solid var(--border);border-radius:7px;
  padding:5px 8px 7px;border-left:3px solid transparent;
}
.tr-node.selected{border-left-color:var(--accent);background:var(--accent-tint-soft)}
.tr-head{display:flex;align-items:center;gap:6px;margin-bottom:3px}
.tr-time{
  background:none;border:none;padding:1px 3px;cursor:pointer;
  font-size:10px;font-variant-numeric:tabular-nums;color:var(--muted);border-radius:4px;
}
.tr-time:hover{color:var(--text);background:var(--chip-bg)}
/* Playhead node marker: the node whose range contains the playhead — the utterance you're
   HEARING during playback ("you are here" when paused). Deliberately on the time chip (the
   temporal element), not the card: selection keeps the left accent bar as its own channel. */
.tr-node.playing .tr-time{color:var(--accent);font-weight:700}
.tr-node.playing .tr-time::before{content:'▶ ';font-size:8px;vertical-align:1px}
.tr-speaker{
  flex:0 1 auto;min-width:0;max-width:46%;
  font-size:11px;background:var(--chip-bg);color:var(--text);
  border:1px solid var(--chip-bd);border-radius:5px;padding:1px 4px;
}
.tr-spacer{flex:1 1 auto}
.tr-act{
  background:none;border:1px solid transparent;border-radius:5px;padding:1px 6px;
  font-size:10px;color:var(--muted);cursor:pointer;
}
.tr-act:hover{color:var(--text);border-color:var(--chip-bd);background:var(--chip-bg)}
.tr-tx{
  width:100%;box-sizing:border-box;display:block;
  background:transparent;border:none;resize:none;overflow:hidden;
  font-size:12px;line-height:1.45;color:var(--text);font-family:inherit;
  padding:1px 3px;border-radius:4px;
}
.tr-tx:focus{outline:none;background:color-mix(in srgb,var(--accent) 6%,transparent)}
.seg-editor.collapsed{max-height:34px;overflow:hidden;padding:6px 12px;cursor:pointer}
.seg-editor.collapsed .seg-fields,
.seg-editor.collapsed .subject-pick,
.seg-editor.collapsed .conveyor-strip,
.seg-editor.collapsed .seg-actions{display:none}
/* Conveyor strip (reviewer R2 #5) — sticky commit bar at the bottom of the editor scroll pane:
   position in the type ("Speech 4 of 10"), remaining essentials (derived from the gap owner),
   Prev/Next-gap/Done&next routed to the same nav the G key uses. Negative margins cancel the
   pane's padding so the bar sits flush across the pane's full width. */
.conveyor-strip{
  position:sticky;bottom:-8px;
  display:flex;align-items:center;gap:6px;
  margin:10px -12px -8px;padding:6px 10px;
  background:var(--panel-2);border-top:1px solid var(--border);
  font-size:11px;color:var(--muted);z-index:2;
}
.conveyor-strip .cv-pos{font-weight:600;color:var(--text);white-space:nowrap}
.conveyor-strip .cv-gaps{color:var(--warn);white-space:nowrap}
.conveyor-strip .cv-ok{color:var(--ok);white-space:nowrap}
/* nowrap + tight buttons: the strip must hold ONE row in the ~370px rail (it used to stack) */
.conveyor-strip .cv-acts{margin-left:auto;display:flex;gap:4px;flex-wrap:nowrap}
.conveyor-strip .cv-acts .btn{padding:3px 8px;white-space:nowrap}
.conveyor-strip .cv-ico{padding:3px 6px}
.conveyor-strip .cv-done{border-color:var(--accent);color:var(--accent-on);background:var(--accent-tint-soft)}
.seg-editor.empty{color:var(--muted);text-align:center;padding:14px}
/* (.seg-conveyor removed in the 2026-07-02 merge — the two parallel conveyor builds were
   unified onto .conveyor-strip above; .ecw-map from the same local commit stays, used by the VA map.) */
/* Multi-select banner — slim strip at top of span/subject editor */
.multi-sel-banner{
  display:flex;flex-direction:column;gap:5px;
  padding:7px 10px 8px;
  background:var(--accent-tint-soft);
  border-bottom:1px solid var(--border);
  font-size:12px;
  flex-shrink:0;
}
.msb-header{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.msb-types{font-size:11px;color:var(--muted)}
.msb-header .btn{margin-left:auto;font-size:10px;padding:2px 7px}
.msb-shift{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--muted)}
.msb-shift input[type=number]{width:56px;padding:3px 5px;background:var(--panel-2);border:1px solid var(--border);border-radius:4px;color:var(--text);font-size:11px}
.msb-note{font-size:10px;color:var(--muted);opacity:.8}
/* Outliner row multi-select state */
.outliner-row.multi-sel{background:var(--accent-tint-soft);}
.seg-editor h3{margin:0 0 6px;font-size:12px;display:flex;align-items:center;gap:8px}
.seg-editor .editor-close{
  position:absolute;top:6px;right:8px;background:var(--panel-2);border:1px solid var(--border);
  color:var(--muted);padding:2px 8px;border-radius:4px;cursor:pointer;font-size:11px;z-index:5;
}
.seg-editor .editor-close:hover{color:var(--danger);border-color:var(--danger)}
.chip{padding:1px 8px;border-radius:4px;font-size:10px;font-weight:600}
/* .chip.<type> tints generated by _injectTypeColorCSS() (speech uses --accent tokens) */
.chip.subject{background:var(--accent-tint-med);color:var(--accent-on)}

.seg-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));column-gap:var(--space-2);row-gap:0;margin-top:var(--space-2)}
.seg-fields .full{grid-column:1/-1}
/* The subject-defaults block and each field group always span both columns of the parent grid,
   so they fill the whole row instead of taking only column 1 / column 2 */
/* Right-rail carded blocks span the full grid width; their card look + spacing is owned by
   the shared group-card rule (see "ONE owner of the group-card look" below). */
.seg-fields > .seg-fields-group,
.seg-fields > .field.full{grid-column:1/-1}
/* Group membership is conveyed by the header + the margin-top gap — NOT a left bar with content
   padding. The bar indented every grouped row ~10px right of ungrouped rows (e.g. Age vs Typical
   Values), breaking column alignment, and stacked another vertical line against the per-row label
   dividers. Dropping it aligns every row to one left edge and removes the competing line. */
.seg-fields-group{margin-top:var(--space-3);padding:0}
.seg-fields-group.no-header{padding-top:0;margin-top:0}
.seg-fields-group-head{
  font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.7px;
  color:var(--text);margin-bottom:var(--space-2);
  display:flex;align-items:center;gap:6px;
}
/* No underline rule — groups are separated by space (proximity), not full-width lines.
   A folded head has nothing below it, so drop its bottom margin. */
details.seg-fields-group.collapsible:not([open]) > .seg-fields-group-head{
  margin-bottom:0;
}
.seg-fields-group-head .group-name{flex:0 0 auto}
.seg-fields-group-head .group-sub{
  color:var(--dim);font-weight:500;letter-spacing:0;text-transform:none;font-size:10px;
}
.seg-fields-group-head .group-count,
.seg-fields-group-head .group-inherited-chip{
  font-size:9px;font-weight:600;letter-spacing:.4px;
  padding:1px 5px;border-radius:4px;line-height:1.3;
}
.seg-fields-group-head .group-count{
  background:var(--panel-2);border:none;color:var(--muted);
  text-transform:none;font-variant-numeric:tabular-nums;
}
/* Missing-first status chip: amber "N left" = required work remains; green "N set ✓" = complete. */
.seg-fields-group-head .group-count.gap{color:var(--warn);background:color-mix(in srgb,var(--warn) 12%,transparent)}
.seg-fields-group-head .group-count.done{color:var(--ok);background:transparent}
.seg-fields-group-head .group-inherited-chip{
  /* Quiet by design: reads as metadata, not a button — accent blue stays reserved for the
     primary action (Submit) and selection states. Sentence case ("usual") on purpose: the group
     head uppercases its children, and a shouted "USUAL" would defeat the word's own tone.
     (The old "Override" counterpart chip is RETIRED — mark the exception, not the default.) */
  background:transparent;border:1px solid var(--muted);color:var(--muted);
  text-transform:none;opacity:.7;
}
/* The group-level state chip summarizes what's HIDDEN — show it only while the group is
   collapsed. When it's open the fields inside already show their own ↳ / revert markers,
   so repeating "usual" on the header is double-signaling. */
.seg-fields-group.collapsible[open] .group-inherited-chip{display:none}
/* Field help text — clear guidance below fields */
.field-help{
  display:block;font-size:11px;color:var(--muted);margin-top:4px;font-style:italic;line-height:1.4;
}
.field-help.fc-warn{color:var(--warn);font-style:normal}
/* Collapsible group (Voice/Lang/Recording/Visual) — uses native <details> */
details.seg-fields-group{padding-top:0;margin-top:var(--space-2)}
details.seg-fields-group > summary{
  list-style:none;cursor:pointer;padding:var(--space-1) 0;border-radius:3px;
  margin-bottom:0;outline:none;user-select:none;
}
details.seg-fields-group > summary::-webkit-details-marker{display:none}
details.seg-fields-group > summary:hover{color:var(--text)}
details.seg-fields-group > summary .group-chev{
  transition:transform .12s;color:var(--muted);
}
details.seg-fields-group[open] > summary .group-chev{transform:rotate(90deg)}
details.seg-fields-group > .seg-fields-grid{margin-top:var(--space-1)}
/* The Content (What) group gets slight emphasis — it's the primary substance of the annotation */
.what-group{
  background:var(--panel-2);border:1px solid var(--border);border-radius:4px;
  padding:var(--space-2);margin-top:var(--space-3);
}
.what-group .seg-fields-group-head{margin-bottom:var(--space-1)}
.what-group textarea{min-height:60px;width:100%;resize:vertical}
/* min 130 (was 150) so short fields (Name | Source Type, Quality | Mic …) pair up at a narrower rail
   instead of stacking — fewer rows on a narrow editor without cramping the controls. */
.seg-fields-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:var(--space-2) var(--space-3)}
.seg-fields-grid .field.full{grid-column:1/-1}

/* Compact fields are now STACKED like every other field — label above the control, no colon —
   per the style guide (bringing the right column in line with the left). They live in a grid
   (.seg-fields-grid is auto-fit; .seg-fields is two-up) so they still pair up; only the
   inline-colon layout is dropped. */
.field.compact{
  display:flex;flex-direction:column;gap:var(--space-1);
  padding:1px 0;
}
.field.compact > input,
.field.compact > select,
.field.compact > textarea,
.field.compact > div{width:100%;min-width:0}   /* div = lang-detect-row, .field-val */
/* Inherited fields: the ↳ marker rides the value line (see _valueWithInheritGlyph).
   Default (chip/scale controls): glyph precedes the control inline. */
.field-val{display:flex;align-items:center;gap:6px;min-width:0}
.field-val > .default-badge{margin-left:0;flex:0 0 auto}
.field-val > :not(.default-badge){flex:1 1 auto;min-width:0;width:auto}
/* Boxed controls (select/input/textarea): overlay the glyph INSIDE the control's left padding so
   it reads as a prefix on the value text. pointer-events:none keeps the control fully clickable. */
.field-val.inside{position:relative;display:block;min-width:0}
.field-val.inside > .default-badge{
  position:absolute;left:8px;top:0;bottom:0;display:flex;align-items:center;
  margin:0;pointer-events:none;z-index:1;
  background:none;padding:0;   /* no pill inside a box — the teal arrow alone reads as the prefix */
}
.field-val.inside > :not(.default-badge){width:100%}
.field-val.inside > select,
.field-val.inside > input,
.field-val.inside > textarea,
.field-val.inside > .lang-detect-row > select{padding-left:27px}
/* textarea is multi-line — pin the glyph to the first line, not the vertical centre. */
.field-val.inside:has(> textarea) > .default-badge{top:7px;bottom:auto}
/* A muted inline sub-caption on a field label (e.g. Content "what was said") — Help/meta tier. */

/* Collapsible subject defaults panel */

/* State badges next to per-span field labels */
.state-badge{
  display:inline-flex;align-items:center;gap:3px;
  padding:0 5px;border-radius:8px;font-size:8px;font-weight:600;
  text-transform:uppercase;letter-spacing:.4px;line-height:1.4;margin-left:4px;
}
/* "default" = the quiet, inherited state (the value already renders muted/italic), so it's a
   subtle dim marker beside the label, not a bordered pill. The override badge stays a pill —
   that one earns attention. */
.default-badge{
  background:var(--inherit-tint);border:none;color:var(--inherit);
  padding:1px;border-radius:5px;line-height:1;
}
/* The inherited marker is drawn as an SVG corner-arrow (see _inheritStateBadge) so its weight is
   ours to set via stroke-width — the OS symbol-font ↳ was hairline and ignored font-weight.
   Sized + coloured (--muted, not --dim) to stay legible at 1× on a dark field. */
.inh-arrow{width:15px;height:15px;display:block}
/* Round container — it holds a single reset icon, so a circle reads better than an oblong pill. */
.override-badge{
  background:var(--accent-tint-soft);border:1px solid var(--accent);color:var(--accent-on);
  padding:0;width:18px;height:18px;border-radius:50%;justify-content:center;flex-shrink:0;
}
.override-badge .badge-revert{margin-left:0}
.app[data-theme="light"] .override-badge{color:var(--accent-2)}
.badge-revert{
  background:transparent;border:0;color:inherit;cursor:pointer;
  padding:0;margin-left:2px;line-height:0;display:inline-flex;align-items:center;
}
.badge-revert svg{width:11px;height:11px;display:block}
.badge-revert:hover{color:white}
.app[data-theme="light"] .badge-revert:hover{color:black}

/* Field input styling per state */
.field.from-default input,
.field.from-default textarea,
.field.from-default select{
  color:var(--muted);font-style:italic;
  border-color:rgba(255,255,255,.08);
}
.app[data-theme="light"] .field.from-default input,
.app[data-theme="light"] .field.from-default textarea,
.app[data-theme="light"] .field.from-default select{
  border-color:rgba(0,0,0,.08);
}
.field.is-override input,
.field.is-override textarea,
.field.is-override select{
  border-color:var(--accent);
  box-shadow:inset 2px 0 0 var(--accent);
}
.field{display:flex;flex-direction:column;gap:var(--space-1)}
/* Field-label token (global) — the title of a control: 12px · 600 · --text, sentence case.
   Was 10px muted and overridden per-panel (#metadataForm, .col-props); now one source of truth. */
/* Labels are wayfinding, values are content — labels sit one level below values. */
.field label{font-size:11px;font-weight:600;color:var(--muted);letter-spacing:normal;display:flex;align-items:center;gap:6px}
/* Multi-select chip fields (Genre, Mood, Instrument families) are big, important blocks — give their
   label the SAME section-header treatment as .seg-fields-group-head (Intent/Emotion section heads) so
   they read as sections. That head deliberately uses NO underline ("separated by space, not full-width
   lines"); this matches it — the border-bottom here was a stranded divergence that drew lines only on
   these fields. ONE section-header style across the span editor. */
#segEditor .field-chips{margin-top:var(--space-2)}
#segEditor .field-chips > label{
  font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.7px;color:var(--text);
  margin-bottom:var(--space-2);
}
/* Start/End share one row in the span inspector — utility fields shouldn't push
   the judgment fields (role, genre, emotion…) below the fold. */
.seg-fields .time-pair{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.seg-fields .time-pair .field{min-width:0}
.seg-fields .time-pair .row{gap:4px}
.seg-fields .time-pair .btn{padding:4px 6px;flex-shrink:0}
.field input,.field select,.field textarea{
  background-color:var(--panel-2);border:1px solid var(--border);color:var(--text);
  padding:5px 7px;border-radius:5px;font:inherit;width:100%;
}
/* Enum fields (selects) use the tunable single-owner vars; overrides the combined rule
   above so only selects pick them up — text inputs/textareas keep --panel-2/--border. */
.field select{background-color:var(--field-bg,var(--panel-2));border-color:var(--field-bd,var(--border))}
/* Properties editor is denser than the form column — values at 12px (labels are
   11px) tighten it without hurting readability. Scoped to the span editor only. */
#segEditor .field input,#segEditor .field select,#segEditor .field textarea{font-size:12px}
/* Consistent dark-themed look for every <select> in the app, with a custom chevron.
   Enum/select fill+border are tunable single-owner vars (dev color tuner). */
select{
  background:var(--field-bg,var(--panel-2));border:1px solid var(--field-bd,var(--border));color:var(--text);
  padding:5px 22px 5px 8px;border-radius:5px;font:inherit;
  appearance:none;-webkit-appearance:none;-moz-appearance:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%239a9a9a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><path d='M3 5l3 3 3-3'/></svg>");
  background-repeat:no-repeat;background-position:right 6px center;background-size:11px;
  cursor:pointer;
}
select:focus{outline:1px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
/* Option text — browsers vary in their support for styling <option>, but we set a baseline */
select option{background:var(--panel);color:var(--text)}
/* In light theme, swap the chevron stroke so it's visible */
.app[data-theme="light"] select{
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%23555555' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><path d='M3 5l3 3 3-3'/></svg>");
}
.field textarea{min-height:48px;resize:vertical}
.field .row{display:flex;gap:6px}
.field .row input{flex:1}

.seg-actions{display:flex;gap:6px;margin-top:8px;justify-content:flex-end}

.subject-pick{
  display:flex;gap:6px;align-items:center;
  background:var(--accent-tint-soft);border:1px solid var(--accent);
  border-radius:6px;padding:6px 8px;margin-bottom:8px;
}
.subject-pick label{font-size:10px;color:var(--accent);text-transform:uppercase;letter-spacing:.4px}
.subject-pick select{flex:1}
.subject-pick button{
  background:transparent;border:1px solid var(--border);color:var(--muted);
  padding:3px 8px;border-radius:4px;font-size:10px;cursor:pointer;
}
.subject-pick button:hover{color:var(--accent);border-color:var(--accent)}

/* ============================== SEGMENT LIST ============================== */
.seg-list-panel{
  background:var(--panel-2);   /* subtle raised surface — darker than the old --panel, but not the
                                  flat near-black of --bg (which read as a void next to the timeline) */
  display:flex;flex-direction:column;min-height:0;flex:1 1 auto;
}
.seg-list-panel .seg-list-body{flex:1 1 auto;overflow-y:auto}
.seg-list-panel.collapsed .seg-list-body,
.seg-list-panel.collapsed .seg-list-foot{display:none}
.seg-list-panel .collapse-toggle{
  background:transparent;border:1px solid var(--border);color:var(--muted);
  padding:2px 6px;border-radius:3px;cursor:pointer;font-size:10px;
}
.seg-list-panel .collapse-toggle:hover{color:var(--accent);border-color:var(--accent)}
.seg-list-head{
  display:flex;align-items:center;gap:6px;padding:6px 10px;
  border-bottom:1px solid var(--border);background:var(--panel-2);flex-shrink:0;
}
.seg-list-head .ttl{font-size:12px;font-weight:600;flex:0 0 auto}
.seg-list-head .ttl .count{
  font-size:10px;color:var(--muted);font-weight:500;margin-left:4px;
  padding:1px 6px;background:var(--panel);border-radius:8px;
}
.seg-list-head input,.seg-list-head select{
  background:var(--panel-2);border:1px solid var(--border);color:var(--text);
  padding:4px 8px;border-radius:5px;font:inherit;font-size:11px;
}
.seg-list-head input{flex:1;min-width:60px}
.seg-list-body{overflow-y:auto;flex:1}
.seg-list-body::-webkit-scrollbar{width:6px}
.seg-list-body::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:4px}
.seg-row{
  display:grid;grid-template-columns:auto auto auto 1fr auto;   /* icon · time-range · duration · label · actions */
  gap:8px;align-items:center;padding:5px 10px;cursor:pointer;
  border-bottom:1px solid rgba(255,255,255,.04);font-size:12px;
}
.seg-row:hover{background:var(--panel-2)}
.seg-row.selected{background:var(--accent-tint-soft)}
/* Type marker: the type's icon in the type's colour (replaces the old plain dot). The per-type
   colour rule .type-ic.<type>{color:var(--<type>)} is generated by _injectTypeColorCSS(). */
.seg-row .type-ic .type-icon{width:15px;height:15px}
.seg-row .tc{font-variant-numeric:tabular-nums;color:var(--muted);font-size:11px}
/* Duration (the Outline sort key) — muted + right-aligned tabular so it scans as a column without
   competing with the label, which is the one emphasised element in the row. */
.seg-row .seg-dur{font-variant-numeric:tabular-nums;color:var(--muted);font-size:11px;text-align:right;min-width:48px}
.seg-row .lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.seg-row .lbl small{color:var(--muted);margin-left:4px}
/* Unnamed span: dim italic placeholder so the eye slides past it to the timecode column. */
.seg-row .lbl .lbl-empty{color:var(--muted);font-style:italic;font-weight:400}
.seg-row .row-actions{display:flex;gap:3px;opacity:0;transition:opacity .1s}
.seg-row:hover .row-actions{opacity:1}
.seg-row .row-actions button{
  background:transparent;border:1px solid var(--border);border-radius:3px;
  width:20px;height:20px;cursor:pointer;color:var(--muted);font-size:10px;padding:0;
}
.seg-row .row-actions button:hover{color:var(--text);border-color:var(--accent)}
.seg-list-empty{padding:18px;text-align:center;color:var(--muted);font-size:11px}
.seg-list-foot{padding:5px 10px;border-top:1px solid var(--border);display:flex;gap:4px;flex-shrink:0}
.seg-list-foot button{
  background:var(--panel-2);border:1px solid var(--border);color:var(--text);
  padding:3px 8px;border-radius:4px;font-size:10px;cursor:pointer;
}
.seg-list-foot button:hover{border-color:var(--accent);color:var(--accent)}

/* ============================== RIGHT PANE ============================== */
/* Active-type banner inside form column */
.active-type-banner{
  padding:6px 10px;background:var(--panel-2);border-bottom:1px solid var(--border);
  font-size:11px;color:var(--muted);display:flex;align-items:center;gap:6px;flex-shrink:0;
}
.active-type-banner .swatch{width:8px;height:8px;border-radius:50%}
.active-type-banner b{color:var(--text)}

/* Properties column tabs — vertical icon rail on the left of the column */
.col-tabs{padding:0;min-height:32px;display:none} /* legacy horizontal tabs, hidden */

/* Waveform — sits below the video, fixed height (it's a 1D visualization, doesn't need much) */
/* Not ever-present: the region collapses to 0 when idle (no clip) and expands ONLY while a waveform
   is being generated (is-generating) or a clip is loaded (has-time — kept even if peaks fail, so it
   stays a scrub surface). So the area appearing = activity or a loaded clip, never dead placeholder. */
.waveform-wrap{
  position:relative;flex:0 0 auto;height:0;
  overflow:hidden;
}
.waveform-wrap.is-generating,
.waveform-wrap.has-time{height:85px}
/* Spectrogram stacked under the waveform: the wrap grows, the waveform keeps its 85px on top, the
   spectrogram fills the rest. The time overlays (playhead/span/selection) span the full height via
   top:0/bottom:0, so they cross both layers and stay aligned (shared time axis). */
.waveform-wrap.has-spectrogram{height:200px}
/* touch-action:none — this canvas is 100% a drag-gesture surface (scrub, Shift+drag range
   select); it owns every touch/pointer event itself (_beginWaveformScrub, index.html), so no
   native pan/pinch/double-tap should ever compete with the drag it's mid-way through. */
.waveform-canvas{
  position:absolute;inset:0;width:100%;height:100%;
  cursor:crosshair;touch-action:none;
}
.waveform-wrap.has-spectrogram .waveform-canvas{height:85px;bottom:auto}
.spectrogram-canvas{
  /* Explicit height (not top+bottom): a <canvas> is a replaced element, so with height:auto it would
     fall back to its intrinsic attribute height and ignore bottom. */
  position:absolute;left:0;right:0;top:85px;width:100%;height:calc(100% - 85px);display:none;
  cursor:crosshair;touch-action:none;
}
.waveform-wrap.has-spectrogram .spectrogram-canvas{display:block}
/* Layer toggles — top-left, mirrors the amplitude (Fit/Ref) group top-right. */
.waveform-layers{
  position:absolute;top:4px;left:4px;z-index:4;display:none;gap:2px;
  background:rgba(0,0,0,.45);border-radius:4px;padding:2px;
}
.waveform-wrap.has-time .waveform-layers{display:flex}
.waveform-layers button{
  background:transparent;border:0;color:rgba(255,255,255,.55);cursor:pointer;
  height:18px;padding:0 7px;font-size:9px;font-weight:600;letter-spacing:.4px;
  text-transform:uppercase;line-height:1;border-radius:3px;font-family:inherit;
}
.waveform-layers button:hover{background:rgba(255,255,255,.12);color:#fff}
.waveform-layers button.on{background:rgba(255,255,255,.18);color:#fff}
/* Frequency-band focus presets — shown over the spectrogram (top-left) only when it's on. */
.spec-freq{
  position:absolute;top:89px;left:4px;z-index:4;display:none;gap:2px;
  background:rgba(0,0,0,.5);border-radius:4px;padding:2px;
}
.waveform-wrap.has-spectrogram .spec-freq{display:flex}
.spec-freq button{
  background:transparent;border:0;color:rgba(255,255,255,.6);cursor:pointer;
  height:16px;padding:0 6px;font-size:8.5px;font-weight:600;letter-spacing:.3px;
  text-transform:uppercase;line-height:1;border-radius:3px;font-family:inherit;
}
.spec-freq button:hover{background:rgba(255,255,255,.14);color:#fff}
.spec-freq button.on{background:var(--accent);color:#fff}

/* ── Controls bar BELOW the waveform/spectrogram (the control groups were moved off the overlay) ──
   Visibility still driven by the waveformWrap state classes, now via sibling selectors so the single
   owner of has-time/has-spectrogram is unchanged. The in-bar overrides drop the absolute positioning
   the groups used as overlays and lay them out inline. */
.waveform-controls{
  display:none;align-items:center;gap:10px;flex-wrap:wrap;
  background:var(--transport-bg);border-top:1px solid var(--border);
  padding:5px 8px;flex:0 0 auto;
}
.waveform-wrap.has-time ~ .waveform-controls{display:flex}
.waveform-controls .wfc-spacer{flex:1 1 auto}
.waveform-controls .waveform-layers,
.waveform-controls .waveform-vzoom,
.waveform-controls .spec-freq,
.waveform-controls .waveform-hzoom{
  position:static;inset:auto;background:none;padding:0;display:flex;align-items:center;gap:4px;
}
/* freq-band presets only when the spectrogram is on (sibling-driven, same as before) */
.waveform-controls .spec-freq{display:none}
.waveform-wrap.has-spectrogram ~ .waveform-controls .spec-freq{display:flex}
.wf-transcode-btn{
  display:inline;margin-left:6px;padding:2px 8px;border-radius:10px;
  border:1px solid var(--accent);background:transparent;
  color:var(--accent);font-size:11px;cursor:pointer;
}
.wf-transcode-btn:hover{background:var(--accent-tint-soft)}
.waveform-status{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-size:13px;color:var(--muted);text-align:center;padding:0 10px;
  pointer-events:none;
}
.waveform-status:has(.wf-transcode-btn){pointer-events:auto}
.waveform-wrap.has-data .waveform-status{display:none}
/* Generating spinner — shown inside the status message while the waveform (+ spectrogram) build. */
.wf-spinner{
  display:inline-block;width:13px;height:13px;margin-right:8px;vertical-align:-2px;
  border:2px solid var(--muted);border-top-color:transparent;border-radius:50%;
  animation:spin .7s linear infinite;
}
.waveform-playhead{
  position:absolute;top:0;bottom:0;width:1px;background:var(--text);
  box-shadow:0 0 4px var(--text);pointer-events:none;
  display:none;z-index:3;opacity:.85;
}
/* Playhead is visible whenever the video has a known duration, even if waveform decode failed */
.waveform-wrap.has-time .waveform-playhead{display:block}

/* Selection overlay — color matches active type */
.waveform-selection{
  position:absolute;top:0;bottom:0;
  pointer-events:none;display:none;z-index:2;
  border-left:2px solid;border-right:2px solid;
}
.waveform-selection.active{display:block}
.app[data-active-type="speech"]  {--active-type-color:var(--speech)}
.app[data-active-type="music"]   {--active-type-color:var(--music)}
.app[data-active-type="effects"]  {--active-type-color:var(--effects)}
.app[data-active-type="ambience"]{--active-type-color:var(--ambience)}
.app[data-active-type="artifacts"] {--active-type-color:var(--artifacts)}
.app[data-active-type="speech"]  .waveform-selection{background:var(--accent-tint-med);border-color:var(--speech)}
.app[data-active-type="music"]   .waveform-selection{background:rgba(167,139,250,.28);border-color:var(--music)}
.app[data-active-type="effects"]  .waveform-selection{background:color-mix(in srgb,var(--warn) 28%,transparent);border-color:var(--effects)}
.app[data-active-type="ambience"] .waveform-selection{background:rgba(16,185,129,.28);border-color:var(--ambience)}
.app[data-active-type="artifacts"] .waveform-selection{background:var(--danger-tint);border-color:var(--artifacts)}

/* Selected-span overlay in waveform — colored band + drag handles */
.waveform-span-overlay{
  position:absolute;top:0;bottom:0;
  pointer-events:none;display:none;z-index:2;
  border-left:2px solid;border-right:2px solid;
}
.waveform-span-overlay.visible{display:block}
/* .waveform-span-overlay.<type> fills generated by _injectTypeColorCSS() (pal.overlayAlpha) */
.waveform-span-handle{
  position:absolute;top:0;bottom:0;width:12px;
  pointer-events:auto;cursor:ew-resize;z-index:5;touch-action:none;
  display:flex;align-items:center;justify-content:center;
}
.waveform-span-handle.wsh-left {left:-6px}
.waveform-span-handle.wsh-right{right:-6px}
.waveform-span-handle::after{
  content:'';display:block;width:3px;height:40%;border-radius:1.5px;
  background:rgba(255,255,255,.8);box-shadow:0 0 3px rgba(0,0,0,.6);
}
.waveform-hint{
  position:absolute;left:8px;bottom:2px;font-size:9px;color:rgba(255,255,255,.75);
  pointer-events:none;background:rgba(0,0,0,.4);padding:0 4px;border-radius:2px;
  display:none;
}
.app[data-theme="light"] .waveform-hint{background:rgba(0,0,0,.55);color:rgba(255,255,255,.85)}
.wh-kbd{background:rgba(255,255,255,.2);padding:0 3px;border-radius:2px;font-family:inherit}
.waveform-wrap.has-time .waveform-hint{display:block}

/* Horizontal (time) zoom — slider + level readout, bottom-right of the waveform. The slider OWNS
   zoom; scrolling over the strip pans. Both feed the one setWaveformView owner. */
.waveform-hzoom{
  position:absolute;bottom:4px;right:6px;z-index:4;display:none;align-items:center;gap:6px;
  background:rgba(0,0,0,.45);border-radius:4px;padding:2px 7px;
}
.waveform-wrap.has-time .waveform-hzoom{display:flex}
/* Zoom slider: dim the bright white track to a subtle gray (the rest of the controls stay full
   brightness); the thumb keeps the accent colour. */
.waveform-hzoom input[type=range]{
  -webkit-appearance:none;appearance:none;width:92px;height:12px;cursor:pointer;background:transparent;
}
.waveform-hzoom input[type=range]::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:rgba(255,255,255,.15)}
.waveform-hzoom input[type=range]::-moz-range-track{height:4px;border-radius:2px;background:rgba(255,255,255,.15)}
.waveform-hzoom input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;appearance:none;width:11px;height:11px;margin-top:-3.5px;
  border:none;border-radius:50%;background:var(--accent);cursor:pointer;
}
.waveform-hzoom input[type=range]::-moz-range-thumb{
  width:11px;height:11px;border:none;border-radius:50%;background:var(--accent);cursor:pointer;
}
.wf-follow-btn{
  background:transparent;border:0;color:rgba(255,255,255,.45);cursor:pointer;padding:0;
  width:16px;height:16px;display:flex;align-items:center;justify-content:center;border-radius:3px;
}
.wf-follow-btn svg{width:13px;height:13px}
.wf-follow-btn:hover{color:#fff}
/* ON = the app's standard toggle look (tint background is the real on/off signal). Was a bare
   pale-blue icon colour (var(--accent-on)) with no background → imperceptible on/off in dark theme. */
.wf-follow-btn.on{color:var(--accent-on);background:var(--accent-tint-med)}
.app[data-theme="light"] .wf-follow-btn{color:rgba(0,0,0,.4)}
.app[data-theme="light"] .wf-follow-btn.on{color:var(--accent);background:var(--accent-tint-soft)}
.wf-zoom-label{
  font-size:9px;font-weight:600;color:rgba(255,255,255,.7);
  font-family:ui-monospace,Menlo,monospace;min-width:44px;text-align:right;
}
.app[data-theme="light"] .wf-zoom-label{color:rgba(0,0,0,.6)}

/* Vertical zoom buttons — top-right corner of the waveform */
.waveform-vzoom{
  position:absolute;top:4px;right:4px;z-index:4;
  display:none;flex-direction:row;gap:2px;
  background:rgba(0,0,0,.45);border-radius:4px;padding:2px;
}
.waveform-wrap.has-time .waveform-vzoom{display:flex}
.waveform-vzoom button{
  background:transparent;border:0;color:rgba(255,255,255,.55);cursor:pointer;
  height:18px;padding:0 5px;font-size:9px;font-weight:600;letter-spacing:.4px;
  text-transform:uppercase;line-height:1;
  display:flex;align-items:center;justify-content:center;border-radius:3px;
  font-family:inherit;
}
.waveform-vzoom button:hover{background:rgba(255,255,255,.12);color:#fff}
.waveform-vzoom button.active{background:rgba(255,255,255,.18);color:#fff}

/* (The at-a-glance .waveform-metrics strip below the waveform was removed — its stats live in the
   EBU R128 Analysis panel and Acoustics. Controls now occupy the space; see .waveform-controls.) */

/* Brief flash on the Acoustics field when a user clicks a metric chip */
@keyframes wm-flash{
  0%   { box-shadow:0 0 0 2px var(--accent); background:var(--accent-tint-med); }
  100% { box-shadow:0 0 0 0 transparent;   background:transparent; }
}
.flash-highlight{animation:wm-flash 1.6s ease-out}

/* Spinner for loading states */
@keyframes spin{
  from { transform:rotate(0deg); }
  to   { transform:rotate(360deg); }
}

.type-tiles-row{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:5px;
  padding:6px 10px;border-bottom:1px solid var(--border);background:var(--panel-2);flex-shrink:0;
}
.tile-big{
  padding:5px 7px;border-radius:5px;border:1px solid var(--border);
  background:var(--panel);cursor:pointer;display:flex;flex-direction:column;gap:1px;
  position:relative;transition:all .15s;
}
.tile-big .tile-row{display:flex;align-items:center;gap:5px}
.tile-big .ticon{font-size:12px}
.ticon-wrap{position:relative;display:inline-flex;align-items:center;border-radius:2px}
.ticon-wrap:hover::after{content:'✎';position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:9px;background:rgba(0,0,0,.55);border-radius:2px;color:#fff;pointer-events:none}
.ticon-wrap img.type-icon{object-fit:contain}
.tile-big .tname{font-size:11px;font-weight:600;flex:1}
.tile-big .tcount{font-size:9px;color:var(--muted)}
/* .tile-big.lit.<type> generated by _injectTypeColorCSS() (speech uses --accent tokens) */
.tile-big.dim{color:var(--muted);background:var(--panel-2)}
/* .tile-big.dim.<type> left-bars generated by _injectTypeColorCSS() */
.tile-big .hidden-stripe{
  position:absolute;left:0;right:0;bottom:0;height:3px;background:var(--warn);
  border-radius:0 0 5px 5px;
}
.tile-big.active{box-shadow:inset 0 0 0 2px var(--accent),0 0 6px var(--accent-tint-med)}
.tile-off{
  margin-left:auto;color:var(--muted);
  width:14px;height:14px;display:inline-flex;
  align-items:center;justify-content:center;font-size:11px;line-height:1;
  background:rgba(0,0,0,.2);border-radius:50%;cursor:pointer;
}
.tile-off:hover{color:var(--danger);background:color-mix(in srgb,var(--danger) 20%,transparent)}

.form-scroll{padding:0;flex:0 0 auto}
.form-scroll:first-child{padding-top:10px}
.form-scroll:last-child{padding-bottom:10px}

.section{overflow:hidden}

/* EBU R128 analysis panel — sits at top of Acoustics tab */
.audio-analysis-panel{
  /* Same card surface as the form/right-rail group cards, full column width. */
  background:var(--group-card-bg);border:1px solid var(--group-card-bd);border-radius:10px;
  padding:10px 12px;margin-bottom:10px;font-size:11px;
  container-type:inline-size;  /* metrics grid responds to the PANEL's own width, not the viewport */
}
/* Inside a form section-body, break out of the 12px padding so the panel is the same
   full width as the group cards (which use the same -12px breakout). */
.section-body > .audio-analysis-panel{margin-left:-12px;margin-right:-12px}
.audio-analysis-panel.idle{color:var(--muted)}
.audio-analysis-panel .aap-head{
  font-size:10px;font-weight:700;color:var(--muted);
  text-transform:uppercase;letter-spacing:.6px;margin-bottom:8px;
  cursor:pointer;user-select:none;
  display:flex;align-items:center;justify-content:space-between;
}
.audio-analysis-panel.collapsed .aap-head{margin-bottom:0}
.audio-analysis-panel .aap-chevron{
  width:12px;height:12px;transition:transform .15s;flex-shrink:0;opacity:.5;
}
.audio-analysis-panel.collapsed .aap-chevron{transform:rotate(-90deg)}
.audio-analysis-panel.collapsed .aap-collapsible{display:none}
.audio-analysis-panel .aap-body{font-size:11px;color:var(--muted)}
.aap-metrics{
  /* 6 readouts. Lock to a divisor of 6 so the last row is never ragged: 2 cols
     when the panel is narrow (clean 2×3), 3 cols when it has room (clean 3×2).
     Never the 1-col ladder or 4-col ragged row that auto-fit produced. */
  display:grid;grid-template-columns:repeat(2,1fr);
  gap:5px 10px;margin-bottom:10px;
}
@container (min-width:360px){ .aap-metrics{grid-template-columns:repeat(3,1fr)} }
.aap-metric{display:flex;flex-direction:column;gap:1px;font-variant-numeric:tabular-nums}
.aap-metric label{font-size:9px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted)}
.aap-metric b{font-size:13px;color:var(--text);font-weight:600}
.aap-warn{font-size:9px;color:var(--warn);margin-left:4px}
.aap-suggest{margin:8px 0;padding-top:8px;border-top:1px solid var(--border)}
.aap-suggest-head{font-size:9px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin-bottom:2px}
.aap-suggest-sub{font-size:9.5px;color:var(--dim);margin-bottom:6px;line-height:1.35}
.aap-suggest-sub b{color:var(--muted);font-weight:600}
/* Unified derived-suggestion rows (renderSuggestionRows) — one row per field: label · value ·
   confidence dots · your value · accept ✓. Shared by Acoustics + (soon) Detected Voice. */
/* Columns: label (hugs content, truncates under pressure) · value · conf dots · current
   (absorbs the slack, right-aligned next to accept) · accept. Label is NOT 1fr — that wasted
   width after short labels. */
.sugg-rows{display:grid;grid-template-columns:minmax(0,auto) minmax(0,auto) auto minmax(0,1fr) 20px;gap:4px 8px;align-items:center;font-size:11px;margin-top:4px}
.sugg-row{display:contents}
.sugg-label{color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sugg-val{color:var(--text);font-weight:600;white-space:nowrap}
.sugg-conf{font-size:7px;letter-spacing:1px;color:var(--dim);white-space:nowrap}
.sugg-row.conf-low .sugg-conf{color:var(--warn)}
.sugg-cur{font-size:9.5px;color:var(--dim);white-space:nowrap;text-align:right}
.sugg-cur.match{color:var(--ok)}
.sugg-cur.diff{color:var(--warn)}
button.sugg-accept{width:18px;height:18px;display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--border);background:transparent;color:var(--muted);border-radius:4px;cursor:pointer;font-size:10px;padding:0;line-height:1}
button.sugg-accept:hover{border-color:var(--accent);color:var(--accent-on);background:var(--accent-tint-soft)}
.sugg-row.disabled{opacity:.45}
/* Aligned columns: label · check · value · confidence · conflict. Each row is display:contents
   so its five cells land in the shared grid. The match-check sits just LEFT of the value (close
   to it, not pinned to the far edge); confidence and the amber conflict are right-aligned columns. */
.aap-rows{display:grid;grid-template-columns:minmax(90px,132px) auto minmax(360px,1fr) auto auto;gap:3px 8px;align-items:center;font-size:11px}
.aap-rows .aap-row{display:contents}
.aap-rows .aap-rowhead{display:contents}
.aap-rows .aap-h{font-size:8.5px;text-transform:uppercase;letter-spacing:.5px;color:var(--dim);font-weight:600;padding-bottom:3px;border-bottom:1px solid var(--border);white-space:nowrap}
.aap-rows .aap-k{color:var(--muted)}
.aap-rows .aap-v{color:var(--text);min-width:0;font-weight:500}
.aap-rows .conf-low .aap-v{opacity:.6;font-style:italic}   /* low confidence: muted value carries the signal */
.aap-rows .conf-low .aap-k{color:var(--dim)}
/* Confidence column — a small neutral marker, right-aligned so MEDIUM/LOW form a clean column;
   not a colored pill, so amber stays reserved for a real conflict. */
.aap-confmark{font-size:8px;text-transform:uppercase;letter-spacing:.4px;color:var(--dim);cursor:help;justify-self:end}
/* The match-check hugs the value's left edge; the conflict pops on the right. (Own class — the
   .aap-st voice cards stay untouched.) */
.aap-status.match{color:var(--dim);font-weight:700;justify-self:end}
.aap-status.diff{font-size:9.5px;color:var(--warn);background:var(--warn-tint);border-radius:8px;padding:1px 7px;font-weight:500;justify-self:start}
/* Speaker actions grouped under Room & speakers */
.aap-spk-actions{margin-top:5px;gap:6px 10px}
/* Shared status pills used by the span-editor voice cards (.svs-card) — kept as-is. */
.aap-st{font-size:9px;padding:1px 6px;border-radius:8px;font-weight:600;text-transform:uppercase;letter-spacing:.4px}
.aap-st.empty{background:var(--accent-tint-soft);color:var(--accent)}
.aap-st.match{background:rgba(16,185,129,.15);color:var(--ok)}
.aap-st.diff{background:color-mix(in srgb,var(--warn) 15%,transparent);color:var(--warn);text-transform:none;letter-spacing:0;font-weight:500}
.aap-actions{display:flex;gap:6px;margin-top:6px;flex-wrap:wrap}
.aap-detected{margin-top:8px;padding-top:8px;border-top:1px solid var(--border)}
.aap-detected-head{font-size:9px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin-bottom:4px}
/* Speech (VAD) carries a lot — label, count, status, an action button and a toggle.
   Let the row wrap so the count stays intact ("12 regions found · 0 new" on one line)
   and the button/toggle flow to the next line instead of crushing word-by-word. */
.aap-det-row{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;padding:3px 0;font-size:11px}
.aap-det-row .aap-det-label{color:var(--muted);flex:0 0 110px}
.aap-det-row .aap-det-count{color:var(--text);font-weight:500;flex:0 1 auto;white-space:nowrap}
.aap-det-row .aap-det-info{font-size:9px;color:var(--dim);flex:0 1 auto}
.aap-det-pending{font-size:10px;color:var(--dim);font-style:italic;padding:2px 0}
/* Per-span voice suggestion strip */
.span-voice-strip{grid-column:1/-1;margin:0 0 8px;padding:6px 10px;background:var(--panel-2);border:1px solid var(--border);border-radius:4px;font-size:11px}
.svs-head{font-size:9px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin-bottom:6px;display:flex;justify-content:space-between;align-items:center}
/* 4-card grid — each metric stacks label / value / sub+chip */
.svs-cards{display:grid;grid-template-columns:repeat(4,1fr);gap:4px;margin-bottom:6px}
.svs-card{display:flex;flex-direction:column;gap:2px;padding:5px 7px;background:rgba(255,255,255,.04);border-radius:4px;min-width:0}
.app[data-theme="light"] .svs-card{background:rgba(0,0,0,.04)}
.svs-card-lbl{font-size:9px;text-transform:uppercase;letter-spacing:.5px;color:var(--dim)}
.svs-card-val{font-size:11px;font-weight:600;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.svs-card-foot{display:flex;align-items:center;gap:4px;flex-wrap:wrap;margin-top:1px}
.svs-card-sub{font-size:9px;color:var(--dim)}
/* Disabled metric (e.g. Effort while its estimate is unreliable) — greyed, non-actionable. */
.svs-card.disabled{opacity:.45}
.svs-card.disabled .svs-card-val{color:var(--dim);font-weight:500}
.svs-actions{display:flex;gap:5px}
.svs-pending{color:var(--dim);font-style:italic;font-size:10px}
.section-head{
  display:flex;align-items:center;gap:8px;padding:8px 12px;cursor:pointer;
  background:var(--section-head-bg);border-bottom:1px solid transparent;
}
.section.open .section-head{border-bottom-color:var(--section-head-bd)}
.section-head .num{
  width:20px;height:20px;border-radius:50%;background:var(--panel);
  display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;color:var(--warn);
}
.section.complete .section-head .num{background:var(--ok);color:#022}
.section-head .ttl{font-size:13px;font-weight:600;flex:1}
.section-head .progress{font-size:10px;color:var(--muted)}
.section-head .caret{transition:transform .15s}
.section.open .section-head .caret{transform:rotate(90deg)}
.section.gated{opacity:.5}
.section.gated .section-head{cursor:default}

.section-body{padding:4px 12px 8px;display:none}
.section.open .section-body{display:block}
.field-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px;align-items:start}
.field-grid .full{grid-column:1/-1}
/* Form sub-groups (Scene type, Language, Capture…) reuse the right-rail span-editor's
   collapsible-group classes (.seg-fields-group / -head / .group-chev) — ONE styling system, so
   the two rails can't drift. Only left-form tweak: drop the right-rail's left-border indent
   (these sit flush in the form section, not nested in a span editor) and the body grid spacing. */
/* Form sub-groups are soft cards (--panel-2 + hairline border) sitting directly on the deep
   column (--bg) — no extra layer behind them — separated by a small gap. */
/* Group cards. A section whose questions have no groupHead (e.g. Acoustics) renders its
   fields as a bare .field-grid directly under .section-body — give it the same card as the
   headed .seg-fields-group groups so every section reads consistently. */
/* ONE owner of the group-card look + box for BOTH rails — left-rail form sections
   (.section-body) and right-rail span editor (.seg-fields). Identical fill, border,
   radius, padding, full-width (the -12px cancels the container's 12px horizontal
   padding), and 5px vertical rhythm. The right-rail grid contributes no row gap
   (see .seg-fields) so this margin is the sole owner of vertical spacing on both. */
.section-body > .seg-fields-group,
.section-body > .field-grid,
.seg-fields > .seg-fields-group,
.seg-fields > .field.full{
  background:var(--group-card-bg);border:1px solid var(--group-card-bd);border-radius:10px;
  padding:10px 12px;margin:5px -12px 0;
}
/* Transcription + its vocal-event helpers are ONE concept (what was vocalised: words + non-word
   events), so they share ONE card instead of stacking as two. The wrapper takes the card above
   (direct .seg-fields child); the nested fields don't, and just stack with a small gap. */
.content-panel{display:flex;flex-direction:column;gap:8px}
.content-panel > .field{margin:0;background:none;border:0;padding:0}
/* Transcription label row: the field label on the left, the audition-loop-length stepper on the right. */
.content-label-row{display:flex;align-items:center;justify-content:space-between;gap:8px}
.split-speaker-btn{align-self:flex-start;margin-top:2px}
.content-label-row > label{margin:0}
.audition-len{display:inline-flex;align-items:center;gap:4px;flex:0 0 auto}
.audition-len-cap{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}
.audition-len-val{font-size:11px;font-variant-numeric:tabular-nums;color:var(--text);min-width:30px;text-align:center}
.aud-len-btn{
  width:18px;height:18px;line-height:1;padding:0;border:1px solid var(--border);border-radius:4px;
  background:var(--panel-2);color:var(--text);font-size:13px;cursor:pointer;display:flex;align-items:center;justify-content:center;
}
.aud-len-btn:hover{background:var(--panel);border-color:var(--accent)}
/* One-time teaching hint — a faint, self-retiring strip under a content field (the shared
   _oneTimeHint mechanism). Rendered hidden; revealed only while the mode is active. */
.field-hint{
  display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:4px;
  padding:3px 8px;font-size:10px;color:var(--muted);background:var(--panel-2);
  border:1px solid var(--border);border-radius:5px;
}
.field-hint[hidden]{display:none}
.field-hint-x{
  flex:0 0 auto;width:15px;height:15px;line-height:1;padding:0;border:0;border-radius:3px;
  background:transparent;color:var(--muted);font-size:13px;cursor:pointer;
}
.field-hint-x:hover{background:var(--panel);color:var(--text)}
.section-body > .seg-fields-group:first-child,
.section-body > .field-grid:first-child,
.seg-fields > .seg-fields-group:first-child,
.seg-fields > .field.full:first-child{margin-top:0}
.section-body > .seg-fields-group > .field-grid,
.seg-fields > .seg-fields-group > .field-grid{margin-top:8px}
/* Selects in form sections size to content rather than stretching */
.section .field select{width:auto;min-width:100%}
.section .field input[type="text"]{width:100%}
.field .help{
  font-size:10px;color:var(--muted);margin-top:3px;
  background:var(--accent-tint-soft);border-left:2px solid var(--accent);
  padding:5px 7px;border-radius:0 4px 4px 0;display:none;
}
.field .help.on{display:block}
/* (.req star rule removed 2026-07-02 — required is the silent norm everywhere now; the
   spreadsheet's .gap-dot.req compound below is a different concept and stays.) */
/* "(optional)" tag on an upstream field whose value propagates downstream — muted, hover for the why. */
.opt-note{color:var(--muted);font-size:.85em;font-weight:400;cursor:help;white-space:nowrap}
.rec{color:var(--muted);opacity:.7;font-size:.85em;margin-left:1px;vertical-align:middle}

.info-btn{
  width:13px;height:13px;border-radius:50%;border:1px solid var(--border);
  background:var(--panel-2);color:var(--muted);font-size:9px;font-weight:700;
  cursor:pointer;display:inline-flex;align-items:center;justify-content:center;
  line-height:1;padding:0;text-transform:none;letter-spacing:0;
}
.info-btn:hover{color:var(--accent);border-color:var(--accent)}
.info-btn.on{background:var(--accent);color:white;border-color:var(--accent)}

/* checkbox question */
.checkq{
  display:flex;align-items:center;gap:8px;padding:6px 0;cursor:pointer;
}
.checkq input[type="checkbox"]{
  appearance:none;-webkit-appearance:none;
  width:14px;height:14px;flex-shrink:0;
  background:var(--panel-2);border:1.5px solid var(--border);border-radius:3px;
  position:relative;cursor:pointer;margin:0;
  transition:background .1s,border-color .1s;
}
.checkq input[type="checkbox"]:checked{background:var(--accent);border-color:var(--accent)}
.checkq input[type="checkbox"]:checked::after{
  content:'';position:absolute;left:3px;top:0;width:5px;height:8px;
  border:1.5px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);
}
.checkq .ck-label{font-size:12px;flex:1}
.checkq .ck-help{font-size:10px;color:var(--muted)}

.chips{display:flex;flex-direction:column;gap:var(--space-2);margin-top:var(--space-1)}
/* Binary yes/no: lay the two picks on one row, sized to content (not the full-width stack
   the option lists use). Neither chip is pre-selected — "No" stays a recorded answer, not a
   default — so this only changes layout, not the unanswered-vs-No distinction (see isAnswered). */
.chips.yn-chips{flex-direction:row;gap:var(--space-2)}
.yn-chips .pick{flex:0 0 auto;min-width:64px;text-align:center}
/* ── Category-column widgets — ONE shared geometry ──────────────────────────────────────────────
   Every "labelled category → options" widget shares a single two-column GRID owned in this block.
   Two structural families opt in via the selector lists below:
     • .chips    > .chips-group (Scene type, Language, Capture, Intent, Emotion)
     • .tv-widget > .tv-group   (Timbre, Speech Dynamics — the _renderVocabWidget panels)
   The grid is:
     col 1  min-content → a RIGHT-justified category column sized to the widest category WORD, so
                          multi-word categories line-wrap and the column stays as narrow as possible;
     col 2  1fr         → the LEFT-justified options column.
   The grid (not any per-widget rule) owns the column width — so widgets drop their hand-set label
   widths (88px / 84px) and contribute only colour/affordance. display:contents promotes each row's
   label+items into the shared grid; align-items:stretch keeps each label's border-right divider as
   tall as its own options and re-fits on reflow. The non-row children (hidden inputs, the tv-head
   ⓘ-definitions bar, the tv-summary) span both columns. Applied via :has so a container with only
   bare (ungrouped) items keeps its plain flow. To add a third family, add its hooks to these lists. */
/* The tv grid lives on [data-tv-palette] (the collapsible body that directly holds the .tv-group rows),
   NOT .tv-widget — the collapse-on-leave wrapper sits between them, so the rows' label+chips must be
   promoted into THIS element's grid. */
/* Span-editor multi-chip fields (Genre / Mood / Instrument families) join this grid too, so they read
   like Scene Type — category label on the left, chips on the right — instead of stacked. */
.chips:has(.chips-group),
.field-chips:has(.chips-group),
[data-tv-palette]:has(.tv-group){
  display:grid;grid-template-columns:min-content 1fr;
  column-gap:var(--space-3);row-gap:var(--space-2);align-items:stretch;
}
.chips:has(.chips-group) > .chips-group,
.field-chips:has(.chips-group) > .chips-group,
[data-tv-palette] > .tv-group{display:contents}                  /* promote label + items into the shared grid */
.chips:has(.chips-group) > :not(.chips-group),
.field-chips:has(.chips-group) > :not(.chips-group),
[data-tv-palette] > :not(.tv-group){grid-column:1 / -1}          /* full-width extras (the field's own header) */
/* Shared label GEOMETRY (right-justify + divider). Each family keeps its own typography below.
   The grouping divider is drawn as a ::after layer, NOT the label's border-right — so a per-widget
   chrome reset (e.g. the Intent label is a <button> that needs `border:none`) can't clobber it. This
   is the single owner of the divider; align-items:stretch makes ::after span each category's full
   (multi-row) chip block. */
/* SINGLE OWNER of category-label typography + geometry for BOTH families — .chips-group-label (Scene
   type / Language / Capture / Intent / Emotion) and .tv-glabel (Timbre / Speech Dynamics). One look:
   right-justified, muted, 11px/600, listener-facing case (NO text-transform — the old base UPPERCASED
   and each container un-did it; a missed container, #acousticsForm, showed ALL CAPS). Per-class rules
   add only their own specifics (e.g. tv-glabel's help cursor) — never re-set the shared typography. */
.chips-group-label,
.tv-glabel{
  position:relative;text-align:right;padding-right:12px;padding-top:3px;
  font-size:11px;font-weight:600;color:var(--muted);letter-spacing:normal;
}
.chips-group-label::after,
.tv-glabel::after{content:"";position:absolute;top:0;right:0;bottom:0;width:2px;background:var(--border)}
.chips-group-items{display:flex;flex-wrap:wrap;row-gap:var(--space-1);column-gap:var(--space-1);align-content:start}
.chips .pick{
  padding:3px 9px;border:1px solid var(--border);border-radius:6px;
  cursor:pointer;font-size:11px;color:var(--muted);background:transparent;
  transition:all 0.15s ease;
}
.chips .pick:hover{border-color:var(--accent-muted);color:var(--text);background:var(--panel-2)}
/* CANONICAL selected-chip style — one rule for every chip picker so the selected look
   can't drift per-widget. A soft accent "highlight" (tint bg + accent border + accent-on
   text), NOT a solid fill. Covers Scene Type (.pick), span multis (.axw-tag: genre / mood /
   instruments), and the vocab widgets (.tv-chip: timbre / speech dynamics). The chip-group
   primitive unifies the selected class onto .on; families still carrying .sel migrate in turn. */
.chips .pick.on,
.axw-tag.on,
.tv-chip.on,
.ecw-int.sel{background:color-mix(in srgb, var(--accent) 24%, var(--panel-2));border-color:var(--accent);color:var(--accent-on);font-weight:600}
.chips .pick[data-tip]{position:relative;}
#dataTip{
  position:fixed;display:none;
  max-width:220px;width:max-content;padding:6px 9px;border-radius:6px;
  background:var(--panel-3);border:1px solid var(--border);
  color:var(--text);font-size:11px;line-height:1.45;
  white-space:normal;pointer-events:none;z-index:var(--z-top);
  box-shadow:0 4px 12px rgba(0,0,0,.35);
}

.timeline-link{
  margin-top:6px;display:inline-flex;align-items:center;gap:6px;
  background:var(--accent-tint-soft);border:1px dashed var(--accent);color:var(--accent);
  padding:6px 10px;border-radius:6px;font-size:11px;cursor:pointer;
}
.timeline-link:hover{background:var(--accent-tint-med)}

.skipped-summary{
  background:var(--panel-2);border:1px solid var(--border);border-radius:6px;
  padding:8px 10px;font-size:11px;color:var(--muted);margin-bottom:8px;
}
.skipped-summary summary{cursor:pointer;color:var(--text);font-weight:600;font-size:12px}
.skipped-summary[open]{padding-bottom:10px}
.skipped-summary ul{margin:6px 0 0;padding-left:18px}
.skipped-summary li{margin-bottom:3px}

.form-foot{
  padding:8px 12px;border-top:1px solid var(--border);background:var(--panel-2);
  display:flex;align-items:center;gap:8px;flex-shrink:0;
}
.subject-bulk{
  margin-top:10px;padding-top:8px;border-top:1px solid var(--border);
}
.subject-bulk-head{
  font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.6px;
  color:var(--muted);margin-bottom:6px;
}
.subject-bulk-actions{
  display:flex;flex-direction:column;gap:6px;align-items:flex-start;
  flex-wrap:wrap;
}
.subject-bulk-actions select{
  background:var(--panel-2);border:1px solid var(--border);color:var(--text);
  padding:3px 6px;border-radius:4px;font-size:11px;
  max-width:160px;
}
.form-foot .saved-indicator{
  font-size:10px;color:var(--muted);font-style:italic;
  letter-spacing:.2px;margin-left:4px;
  font-variant-numeric:tabular-nums;
}
.form-foot .grow{flex:1}
.review-check{
  display:inline-flex;align-items:center;gap:6px;
  font-size:11px;color:var(--muted);cursor:pointer;
  padding:3px 8px;border-radius:5px;
}
.review-check input{margin:0;cursor:pointer}
.review-check:hover{background:var(--panel)}

.keys{
  display:flex;gap:10px;font-size:11px;color:var(--muted);padding:5px 12px;
  border-top:1px solid var(--border);background:var(--panel-2);flex-shrink:0;
  flex-wrap:wrap;
}
.keys kbd{
  background:var(--panel);border:1px solid var(--border);border-radius:3px;
  padding:0 5px;font-family:ui-monospace,Menlo,monospace;font-size:11px;
}

/* ============================== MODAL ============================== */
.modal-bg{
  position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:var(--z-overlay);
  display:none;align-items:center;justify-content:center;
}
.modal-bg.on{display:flex}
/* Compare transcription models */
.mc-scope{color:var(--muted);font-size:13px;margin:0 0 10px}
.mc-models{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}
.mc-model{display:flex;align-items:center;gap:6px;padding:6px 10px;border:1px solid var(--border);
  border-radius:6px;cursor:pointer;font-size:13px;background:var(--panel-2)}
.mc-model b{font-weight:600}
.mc-model .mc-size{color:var(--muted);font-size:11px}
.mc-controls{display:flex;align-items:center;gap:12px;margin-bottom:14px}
.mc-hint{color:var(--dim);font-size:11px}
.mc-results{display:flex;flex-direction:column;gap:10px;max-height:48vh;overflow-y:auto}
.mc-card{border:1px solid var(--border);border-radius:8px;padding:10px 12px;background:var(--panel-2)}
.mc-card-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:6px}
.mc-card-head b{font-size:14px}
.mc-stat{color:var(--muted);font-size:12px;font-variant-numeric:tabular-nums}
.mc-text{font-size:13px;line-height:1.5;color:var(--text);white-space:pre-wrap}
.mc-text.mc-pending{color:var(--dim)}
.mc-text.mc-err,.mc-empty.mc-err{color:var(--danger)}
.mc-empty{color:var(--muted);font-size:13px}
.mc-active{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--muted)}
.mc-active select{background:var(--panel-2);color:var(--text);border:1px solid var(--border);
  border-radius:5px;padding:3px 6px;font-size:13px}
.modal{
  background:var(--panel);border:1px solid var(--border);border-radius:10px;
  width:min(680px,92vw);max-height:86vh;display:flex;flex-direction:column;
  box-shadow:0 18px 60px rgba(0,0,0,.5);
}
.modal-head{
  padding:12px 16px;border-bottom:1px solid var(--border);
  display:flex;align-items:center;justify-content:space-between;
}
.modal-head h2{margin:0;font-size:14px}
.modal-head .x{background:transparent;border:0;color:var(--muted);font-size:18px;cursor:pointer}
.modal-body{padding:14px 16px;overflow-y:auto;flex:1}
.modal-foot{padding:10px 16px;border-top:1px solid var(--border);display:flex;justify-content:flex-end;gap:6px}
/* Merge preview */
.mg-sub{color:var(--muted);font-size:12px;margin:0 0 6px;font-weight:600}
.mg-list{display:flex;flex-direction:column;gap:4px;margin-bottom:14px}
.mg-row{display:flex;align-items:baseline;gap:10px;padding:5px 8px;border:1px solid var(--border);
  border-radius:6px;background:var(--panel-2);font-size:13px}
.mg-tc{color:var(--muted);font-variant-numeric:tabular-nums;font-size:11px;flex:0 0 auto;white-space:nowrap}
.mg-txt{flex:1 1 auto;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mg-spk{flex:0 0 auto;color:var(--accent);font-size:11px}
.mg-result{border:1px solid var(--accent);border-radius:8px;padding:10px 12px;background:color-mix(in srgb,var(--accent) 7%,var(--panel-2))}
.mg-range{font-weight:600;font-variant-numeric:tabular-nums}
.mg-dur{color:var(--muted);font-size:12px}
.mg-warn{color:var(--warn);font-size:12px;margin-top:8px;line-height:1.4}
.mg-content{margin-top:8px;font-size:13px;line-height:1.5;color:var(--text);white-space:pre-wrap}
.mg-types{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px}
.mg-pill{padding:4px 10px;border:1px solid var(--border);border-radius:99px;background:var(--panel-2);font-size:12px}
.mg-note{color:var(--muted);font-size:12px;margin:0}

.subject-card{
  background:var(--panel-2);border:1px solid var(--border);border-radius:8px;
  padding:10px 12px;margin-bottom:8px;
}
.subject-card .subject-head{display:flex;align-items:center;gap:8px;margin-bottom:8px}
.subject-card .avatar{
  width:28px;height:28px;border-radius:50%;background:var(--accent);
  color:white;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12px;
}
.subject-card .subject-head input{
  flex:1;background:var(--panel);border:1px solid var(--border);color:var(--text);
  padding:5px 8px;border-radius:5px;font:inherit;font-weight:600;
}
.subject-card .subject-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:6px}
.subject-card .subject-grid .field.full-width{grid-column:1/-1}
.subject-card .field label{font-size:10px}
.subject-card .field input{background-color:var(--panel)}
.subject-card .actions{display:flex;justify-content:flex-end;margin-top:6px}
.subject-card .actions button{
  background:transparent;border:1px solid var(--border);color:var(--muted);
  padding:3px 8px;border-radius:4px;font-size:11px;cursor:pointer;
}
.subject-card .actions button:hover{color:var(--danger);border-color:var(--danger)}

/* (The inline speaker/performer section was removed from the span editor — subject identity is edited
   in the Subjects panel. Its .sp-inline-* rules are gone with it.) */
/* Accent picker */
.accent-picker{display:flex;flex-direction:column;gap:4px;margin-top:1px}
.accent-chips{display:flex;flex-wrap:wrap;gap:var(--space-1)}
.accent-chip{font-size:10px;padding:2px 7px;border-radius:6px;border:1px solid var(--border);background:transparent;color:var(--muted);cursor:pointer;transition:background .1s,border-color .1s,color .1s;line-height:1.5}
.accent-chip.on{background:var(--accent-tint-soft);border-color:var(--accent);color:var(--accent-on)}
.accent-chip:hover:not(.on){border-color:var(--accent-tint-strong);color:var(--text);background:var(--panel-2)}
.accent-custom{margin-top:2px;font-size:10px;padding:3px 6px;width:100%;background:var(--panel);border:1px solid var(--border);border-radius:4px;color:var(--text)}
/* When a preset chip is selected, the custom box would just echo it — hide it (re-shown when the
   chip is cleared). Replaces the old JS show/hide now that the picker is binding-agnostic. */
.accent-picker:has(.accent-chip.on) .accent-custom{display:none}
.accent-no-chips{font-size:10px;color:var(--muted);font-style:italic}
/* Language detect row */
.lang-detect-row{display:flex;gap:4px;align-items:center}
.lang-detect-row select{flex:1}

/* Axis widget — shared by timbre, emotion, prosody */
.axw-widget{display:flex;flex-direction:column;gap:7px;width:100%}
/* Single shared grid across all axis rows so columns align vertically.
   display:contents on .axw-row lets its 4 children (label, lo, stops, hi)
   sit directly in this grid — all rows share the same column tracks. */
.axw-dims{display:grid;grid-template-columns:88px max-content 120px max-content;column-gap:6px;row-gap:5px;align-items:center}
.axw-row{display:contents}
.axw-label{font-size:9px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;text-align:right;white-space:nowrap}
.axw-lo{font-size:9px;color:var(--dim);white-space:nowrap;text-align:right}
.axw-hi{font-size:9px;color:var(--dim);white-space:nowrap}
/* 5-stop segmented control — fixed 120px so circles are compact and aligned */
.axw-stops{
  position:relative;display:flex;align-items:center;
  justify-content:space-between;padding:0 7px;height:22px;
  width:120px;
}
.axw-stops::before{
  content:'';position:absolute;left:7px;right:7px;
  top:50%;height:2px;transform:translateY(-50%);
  background:rgba(255,255,255,.1);pointer-events:none;border-radius:1px;
}
.axw-stop{
  width:14px;height:14px;border-radius:50%;padding:0;
  background:var(--panel-2);border:2px solid rgba(255,255,255,.18);
  cursor:pointer;flex-shrink:0;position:relative;z-index:1;
  transition:background .12s,border-color .12s,transform .1s;
}
.axw-stop.active{
  background:var(--accent);border-color:var(--accent);
  transform:scale(1.35);box-shadow:0 0 0 3px var(--accent-tint-soft);
}
.axw-stop:hover:not(.active){border-color:rgba(255,255,255,.45);transform:scale(1.15)}
/* tags + text */
.axw-tags{display:flex;flex-wrap:wrap;gap:var(--space-1);min-height:24px;padding:2px 0}
.axw-hint{font-size:10px;color:var(--muted);font-style:italic;line-height:24px}
.axw-tag{
  font-size:11px;padding:2px 8px;border-radius:6px;cursor:pointer;
  background:transparent;border:1px solid var(--border);color:var(--muted);user-select:none;
  transition:background .12s,color .12s,border-color .12s;line-height:1.5;
}
.axw-tag.sug{color:var(--text)}
/* .axw-tag.on selected style is the canonical highlight (see CANONICAL chip rule above) */
.axw-tag:hover:not(.on){border-color:var(--accent-tint-strong);color:var(--text);background:var(--panel-2)}
.axw-text{width:100%;font-size:10px;padding:3px 6px;color:var(--muted);margin-bottom:7px}   /* result-first: input leads, chips below */
/* Intent result row: the value field + a stash toggle for the act palette below it. */
.intent-result-row{display:flex;align-items:center;gap:6px;margin-bottom:7px}
.intent-result-row .axw-text{flex:1;margin-bottom:0}   /* the row owns the gap to the chips now */
.chips-stash-btn{flex:0 0 auto;font-size:10px;color:var(--muted);background:none;border:none;cursor:pointer;
  padding:2px 4px;white-space:nowrap;border-radius:4px;transition:color .12s}
.chips-stash-btn:hover{color:var(--text)}
/* ── Animated disclosure (reusable) ─────────────────────────────────────────────────────────────
   Collapsing/expanding a block should be a FOLLOWABLE motion, not a teleport — so the eye tracks
   what opened/closed and the annotator stays oriented. The trigger always sits ABOVE the body, so it
   stays pinned as the body grows/shrinks below it — no scroll jump. height:auto can't be transitioned
   in CSS, so _animateCollapser() tweens the px height for open/close and clears it to auto when open;
   .collapsed (height:0) is the static collapsed state (initial render + the rest position). Honours
   reduced motion. Reuse for the section accordions + form sub-groups next. */
/* overflow is VISIBLE at rest (so an open section's field tooltips aren't clipped); _animateCollapser
   sets overflow:hidden inline only while a tween runs, and the collapsed state stays clipped. */
.collapser{transition:height .18s ease}
.collapser.collapsed{height:0;overflow:hidden}
@media (prefers-reduced-motion: reduce){ .collapser{transition:none} }
/* Segmented "scale" picker for ordinal fields (descriptors with scale:true). The .scale-bar is one
   connected bar of ordered segments left→right = the continuum; one selected at a time (click the
   selected segment to clear). A hidden <select> (data-meta / data-spf) carries the value for the
   existing wiring. fc.scaleEscape renders an off-scale value (e.g. "ambiguous") as a separate pill
   beside the bar. Scale fields are full-width so the bar gets the whole column. */
/* Single-line label + scale bar, responsive: the bar sits to the right of the label and wraps
   underneath it (full width) when the column is too narrow to fit both. */
.field.field-scale{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:2px 10px}
/* Fixed label width (shared --cat-col-w) so every scale row's bar starts at the same x and (the bar
   being flex:1) ends at the same x — related scale chips (Pitch / Volume / Rate …) get the same
   overall length — and the labels still share the column with the category-column widgets above/below.
   NO border-right divider here: that vertical bar is a GROUPING bracket (a label heading a set of
   chips); a scale row is ONE control (a segmented continuum), so it has nothing to group and the bar
   would falsely imply it. Right-justified label + the column gap pair it with its bar unambiguously. */
.field.field-scale > label{flex:0 0 var(--cat-col-w,72px);margin:0;text-align:right;padding-right:12px;position:relative}
/* The hover-only comment button is taken OUT of flow here so it can't push the fixed-width label past
   its column (it widened "Volume" → the +6px divider drift). It sits in the label's empty left zone
   (text is right-aligned), still appearing on hover. */
.field.field-scale > label > .cmt-btn{position:absolute;left:0;top:3px;margin-left:0;padding:0}
/* Editor cards mix scale rows with category-column widgets (Typical Values = Pitch/Volume/Rate +
   Timbre; Speech Dynamics = Volume + prosody). One shared label-column width (--cat-col-w, owned on
   .seg-fields) so every divider lines up within a card: the scale's fixed bar-width need sets it, and
   the otherwise min-content category widgets adopt it here instead of sitting at their own narrower x. */
.seg-fields{--cat-col-w:72px}
.seg-fields [data-tv-palette]:has(.tv-group){grid-template-columns:var(--cat-col-w) 1fr}
.field.field-scale > .scale-chips{flex:1 1 200px;min-width:0;margin-top:0}
/* Binary scale flagged compact (lip_sync no/yes): pairs in a half-cell, so it stacks label-above like
   the other compact fields and the two-chip bar fills the cell instead of holding a 200px basis. */
.field.compact.field-scale{flex-direction:column;align-items:stretch;gap:2px}
/* Reset the horizontal-layout label sizing: the base scale rule sets `flex:0 0 72px` to make the label a
   fixed 72px-WIDE left column (row layout). Here the field is flex-direction:column, so that flex-basis
   becomes a fixed 72px HEIGHT — a giant gap under the label. Size the label to its content instead. The
   comment button also comes back into flow (no right-aligned empty zone to tuck into when left-aligned). */
.field.compact.field-scale > label{flex:0 0 auto;width:auto;text-align:left}
.field.compact.field-scale > label > .cmt-btn{position:static;top:auto}
.field.compact.field-scale > .scale-chips{flex:1 1 auto;width:100%;margin-top:2px}
.scale-chips{display:flex;align-items:stretch;gap:6px;margin-top:4px}
/* The value-carrier <select> is JS-read only (aria-hidden, tabindex=-1) — fully removed from layout.
   Was visually-hidden (position:absolute;width:1px), but the later broad rule
   `.section .field select{min-width:100%}` (specificity 0,2,1) overrode the 1px width, leaving a
   full-column-width absolutely-positioned select that expanded the editor's horizontal scroll region
   (the right column scrolled sideways). display:none is immune to any width rule. */
.scale-hidden{display:none}
.scale-bar{display:flex;flex:1 1 auto;min-width:0;border:1px solid var(--chip-bd);border-radius:7px;overflow:hidden}
.scale-bar .scale-chip{flex:1 1 0;min-width:0;border:0;border-left:1px solid var(--chip-bd);border-radius:0}
.scale-bar .scale-chip:first-child{border-left:0}
/* Keyboard focus ring, INSET. The global :focus-visible ring (outline-offset:1px) is drawn OUTSIDE the
   chip and gets clipped by .scale-bar{overflow:hidden} — leaving only a thin sliver at the chip edge that
   barely moves as arrows rove focus (looked like "the cursor doesn't move"). Drawing it inside the chip's
   own box keeps the whole ring visible and it jumps chip-to-chip. Escape pill is outside the bar → keeps
   the default ring. Above .on's tint so the focused chip is always legible. */
.scale-bar .scale-chip:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;position:relative;z-index:1}
/* Off-scale escape value (e.g. Age "ambiguous") — a standalone rounded pill, not part of the bar. */
.scale-chip.scale-escape{flex:0 0 auto;border:1px solid var(--chip-bd);border-radius:7px}
.scale-chip{font-size:11px;padding:4px 8px;cursor:pointer;text-align:center;
  background:var(--chip-bg);color:var(--muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.5;
  transition:background .12s,color .12s}
.scale-chip:hover:not(.on){color:var(--text);background:color-mix(in srgb,var(--accent) 12%,var(--chip-bg))}
.scale-chip.on{background:var(--accent-tint-med);color:var(--accent-on)}
/* Inherited (from-default) scale field: the chip bar can't go italic like a <select>, so the
   selected chip gets a muted treatment instead — so an inherited value reads as inherited, not set
   (the "default" badge in the label is the primary cue; this matches the muted/italic select). */
.field.from-default .scale-chip.on{background:color-mix(in srgb,var(--muted) 16%,var(--chip-bg));color:var(--muted);font-style:italic}
/* Radar chart — rendered above axis rows in timbre / prosody widgets */
.axr-wrap{display:flex;justify-content:center;padding:2px 0 4px}
.axr-chart{display:block;overflow:visible}
.axr-ring{fill:none;stroke:rgba(255,255,255,.09);stroke-width:.6}
.axr-axis{stroke:rgba(255,255,255,.12);stroke-width:.6}
.axr-poly{fill:var(--accent-tint-soft);stroke:var(--accent);stroke-width:1.2;stroke-linejoin:round;transition:points .15s}
.axr-label{font-size:7.5px;fill:var(--muted);pointer-events:none}
.axr-center{fill:rgba(255,255,255,.15)}
:root.light .axr-ring{stroke:rgba(0,0,0,.1)}
:root.light .axr-axis{stroke:rgba(0,0,0,.12)}
:root.light .axr-label{fill:var(--muted)}
/* Intent widget — reuses .chips layout; category labels are clickable type selectors */
.intent-widget .chips-group-label{
  /* Reset <button> defaults so it looks identical to a plain group label. Safe to reset all borders:
     the grouping divider lives on the shared ::after layer, not the label's border. */
  appearance:none;border:none;background:none;padding-top:3px;
  cursor:pointer;transition:color .12s;   /* width owned by the shared .chips grid (min-content column) */
  /* A <button> vertically-centres its text; the span-based labels top-align. Flex it to top-right so
     a multi-row category's label heads its chips like every other widget (not floating mid-block). */
  display:flex;justify-content:flex-end;align-items:flex-start;
  /* Sub-group label: subordinate to the section header above it. */
  color:var(--muted);font-weight:600;
}
.intent-widget .chips-group-label:hover{color:var(--text)}
.intent-widget .chips-group-label.active{color:var(--accent-on)}
.app[data-theme="light"] .intent-widget .chips-group-label:hover{color:var(--text)}
/* Emotion categorical chip widget */
/* Emotion widget — reuses .chips layout; group labels keep valence colour */
/* SINGLE OWNER of the emotion-group palette: the chip group labels, the selected chips, AND the
   wheel map's dots/spokes (which carry the same positive/neutral/negative classes) all derive from
   these three vars — retune here, everything agrees. */
.ecw-widget{--emo-pos:#5da86e;--emo-neg:#c96060;--emo-neu:#8a887f}
.ecw-widget .chips-group-label{color:var(--muted)}   /* width owned by the shared .chips grid */
.ecw-widget .chips-group-label.positive{color:var(--emo-pos)}
.ecw-widget .chips-group-label.negative{color:var(--emo-neg)}
.ecw-widget .pick.positive.on{background:rgba(93,168,110,.15);border-color:var(--emo-pos);color:var(--emo-pos)}
.ecw-widget .pick.negative.on{background:rgba(201,96,96,.12);border-color:var(--emo-neg);color:var(--emo-neg)}
/* Multi-select emotion chips: selected chips carry an inline intensity mark + a × remove; the FOCUSED
   chip (whose intensity the row edits) gets a brighter ring so it's clear which one the row targets. */
.ecw-widget .pick{display:inline-flex;align-items:center;gap:4px}
.ecw-widget .pick.focused{box-shadow:0 0 0 2px var(--accent) inset}
/* Intensity mark + × are present in the DOM always; shown only when the chip is selected (so a
   selection flip via class-toggle reveals a × that already exists — no re-render needed). */
.ecw-widget .pick .ecw-cint{font-style:normal;font-size:9px;opacity:.8;padding:0 3px;border-radius:3px;background:color-mix(in srgb,currentColor 16%,transparent)}
.ecw-widget .pick:not(.on) .ecw-cint,.ecw-widget .pick .ecw-cint:empty{display:none}
.ecw-widget .pick .ecw-rm{border:none;background:none;color:inherit;cursor:pointer;font-size:13px;line-height:1;padding:0 0 0 1px;opacity:.6}
.ecw-widget .pick:not(.on) .ecw-rm{display:none}
.ecw-widget .pick .ecw-rm:hover{opacity:1}
/* Intensity row sits in the chips column (grid col 2) instead of a hand-tuned left indent. */
.ecw-widget.chips:has(.chips-group) > .ecw-intensity{grid-column:2;justify-self:start}
/* The "Intensity" label sits in the label column (col 1) — override the full-width rule that catches
   non-.chips-group children — so it lines up under positive/neutral/negative. */
.ecw-widget.chips:has(.chips-group) > .ecw-int-label{grid-column:1}
.ecw-intensity{display:flex;gap:3px;margin-top:1px}
/* VA orientation map inside the Emotion widget — plots the terms + lights the current pick (read-only) */
.ecw-map-wrap{margin:0 0 10px;border:1px solid var(--border);border-radius:8px;background:#ffffff05}
.ecw-map-cap{cursor:pointer;font-size:11px;color:var(--muted);padding:6px 9px;list-style:none;user-select:none}
.ecw-map-cap::-webkit-details-marker{display:none}
.ecw-map-cap::before{content:'▾ ';font-size:9px;color:var(--dim)}
.ecw-map-wrap:not([open]) .ecw-map-cap::before{content:'▸ '}
.ecw-map{padding:0 8px 8px}
.ecm-svg{width:100%;height:auto;display:block;max-height:240px}
.ecm-dot{opacity:.5;transition:opacity .12s;fill:var(--emo-neu)}
.ecm-dot.positive{fill:var(--emo-pos)}
.ecm-dot.negative{fill:var(--emo-neg)}
.ecm-dot.on{opacity:1;stroke:#fff;stroke-width:1.5}
.ecm-spoke{stroke-width:1.2;opacity:.16;transition:opacity .12s;stroke:var(--emo-neu)}
.ecm-spoke.positive{stroke:var(--emo-pos)}
.ecm-spoke.negative{stroke:var(--emo-neg)}
.ecm-spoke.on{opacity:.95;stroke-width:2}
.ecm-anti{stroke:var(--muted);stroke-width:1.2;stroke-dasharray:3 3;opacity:.5}
.ecm-lab{font-size:11px;font-weight:700;fill:var(--text);text-anchor:middle;paint-order:stroke;stroke:var(--bg);stroke-width:3px}
.ecm-ax{font-size:8.5px;fill:var(--dim)}
.ecw-int{
  font-size:9px;padding:2px 8px;border-radius:6px;cursor:pointer;
  border:1px solid var(--border);color:var(--muted);background:transparent;
  font-weight:600;letter-spacing:.3px;
  transition:background .12s,border-color .12s,color .12s;
}
/* .ecw-int.sel (emotion intensity) selected style = the canonical highlight (see above) */
/* Extra breathing room above Prosody */
.field[data-fc-key="prosody"]{margin-top:6px}
/* Framework info icon */
.widget-info{
  display:inline-flex;align-items:center;justify-content:center;
  width:13px;height:13px;border-radius:50%;
  border:1px solid var(--muted);color:var(--muted);
  font-size:8px;font-weight:700;font-style:normal;
  cursor:default;vertical-align:middle;margin-left:4px;user-select:none;
  transition:border-color .12s,color .12s;flex-shrink:0;
}
.widget-info:hover{border-color:var(--accent);color:var(--accent)}

/* ============================== PHASE 2: TASK QUEUE CONTROLS ============================== */
.task-queue-controls{
  display:flex;gap:8px;align-items:center;margin-left:16px;padding-left:16px;
  border-left:1px solid var(--border);
}
.project-dropdown{
  padding:4px 8px;border:1px solid var(--border);border-radius:4px;
  background:var(--panel);color:var(--text);font-size:11px;
  cursor:pointer;
}
.project-dropdown:hover{border-color:var(--accent)}
.task-progress{font-size:11px;color:var(--muted);white-space:nowrap}

/* ============================== HIDDEN-DATA CONFIRM ============================== */
.confirm-overlay{
  position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:var(--z-confirm);
  display:none;align-items:center;justify-content:center;
}
.confirm-overlay.on{display:flex}
.confirm-box{
  background:var(--panel);border:1px solid var(--border);border-radius:8px;
  width:min(460px,92vw);
  display:flex;flex-direction:column;overflow:hidden;
  box-shadow:0 18px 60px rgba(0,0,0,.5);
}
.confirm-head{
  padding:9px 14px;border-bottom:1px solid var(--border);background:var(--panel-2);
}
.confirm-head h3{
  margin:0;font-size:11px;font-weight:600;
  text-transform:uppercase;letter-spacing:.6px;color:var(--muted);
}
.confirm-body{
  padding:14px 16px;font-size:12px;color:var(--text);line-height:1.55;
  white-space:pre-line;   /* multi-line msgs (What's New bullets, task-queue prompts) keep their \n */
  max-height:60vh;overflow-y:auto;
}
.confirm-foot{
  padding:8px 12px;border-top:1px solid var(--border);background:var(--panel-2);
  display:flex;justify-content:flex-end;gap:6px;
}

/* Shared modal body (help / model-compare modals in index.html). The music-subject assignment
   modal that used to live here was removed — music spans now assign their performer inline, the
   same way speech spans assign their speaker (no popup). */
.modal-body {
  padding: 16px;
  max-height: 60vh;
  overflow-y: auto;
}



/* ── Utility classes (de-duped from repeated inline styles) ───────────────── */
.kbd{background:var(--panel-2);border:1px solid var(--border);border-radius:4px;padding:1px 6px;font-size:11px}
.kbd-sm{background:var(--panel-2);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-size:10px}
.auth-label{display:block;color:var(--muted);font-size:12px;margin-bottom:6px}

/* ── Loudness appropriateness panel (mock) ───────────────────────────────── */
.appr-panel{margin:8px 0;padding:8px 0 4px;border-top:1px dashed var(--border)}
/* Subheads (Appropriateness, Suggested values) share one style — uppercase muted,
   consistent with the panel head and the Overview group banners. flex-wrap keeps the
   "not saved" marker from forcing an ugly break at the minimum column width. */
.appr-head{font-size:9px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;display:flex;align-items:center;flex-wrap:wrap;gap:4px 6px;margin-bottom:6px}
.appr-mock{font-size:9px;font-weight:500;color:var(--dim);text-transform:none;letter-spacing:0;white-space:nowrap;cursor:help}
.appr-row{display:flex;align-items:center;gap:8px;margin:3px 0;font-size:10px}
.appr-lbl{width:58px;color:var(--muted);flex-shrink:0}
.appr-val{width:74px;flex-shrink:0;font-family:ui-monospace,Menlo,monospace;color:var(--text)}
.appr-bar{position:relative;flex:1;height:10px;background:var(--panel-2);border-radius:3px;overflow:hidden}
.appr-band{position:absolute;top:0;bottom:0;background:color-mix(in srgb,var(--accent) 30%,transparent);border-left:1px solid var(--accent);border-right:1px solid var(--accent)}
.appr-mark{position:absolute;top:-1px;bottom:-1px;width:2px;background:var(--text);transform:translateX(-1px)}
.appr-flag{width:120px;flex-shrink:0;text-align:right;font-weight:600}
.appr-flag.ok{color:#10b981}
.appr-flag.hot{color:var(--danger)}
.appr-flag.low{color:#60a5fa}
.appr-flag.warn{color:var(--warn)}
.appr-strip{display:block;width:100%;height:56px;margin:6px 0 2px;background:var(--panel-2);border-radius:4px;cursor:crosshair}
.appr-controls{display:flex;flex-wrap:wrap;align-items:center;gap:10px;font-size:10px;color:var(--muted);margin-top:4px}
/* Compact, intentionally chevron-less Reference dropdown. Suppress the chevron in
   BOTH themes: use background-color (NOT the `background` shorthand — that reset
   background-repeat, so the light-theme chevron tiled across the text), and kill the
   background-image in light mode at a specificity that beats `.app[data-theme="light"] select`. */
.appr-ctx select{font-size:10px;background-color:var(--panel);background-image:none;color:var(--text);border:1px solid var(--border);border-radius:4px;padding:1px 4px}
.app[data-theme="light"] .appr-ctx select{background-image:none}

/* appropriateness meters — labeled scales (override the bare-bar mock styles) */
.appr-meter{margin:5px 0}
.appr-mtop{display:flex;align-items:center;gap:8px;font-size:10px}
.appr-mtop .appr-lbl{width:58px;flex-shrink:0}
.appr-mtop .appr-val{width:auto;flex-shrink:0}
/* Keep the flag next to its value instead of pinned to the panel edge, so it doesn't
   float far away when the column is wide. A small left margin separates it from the value. */
.appr-mtop .appr-flag{margin-left:6px;width:auto;text-align:left}
.appr-mscale{display:flex;align-items:center;gap:5px;margin-top:2px}
.appr-mscale .appr-bar{height:8px}
.appr-end{font-size:8px;color:var(--dim);font-family:ui-monospace,Menlo,monospace;flex-shrink:0;min-width:16px;text-align:center}
.appr-cap{font-size:8px;color:var(--muted);flex-shrink:0;white-space:nowrap}
.appr-band.danger{background:color-mix(in srgb,var(--warn) 28%,transparent);border-color:var(--warn)}
.appr-strip-wrap{position:relative;margin-top:6px}
.appr-yax{position:absolute;left:3px;font-size:8px;color:var(--dim);font-family:ui-monospace,Menlo,monospace;pointer-events:none;background:color-mix(in srgb,var(--panel-2) 75%,transparent);padding:0 2px;border-radius:2px}
.appr-ytop{top:3px}
.appr-ybot{bottom:3px}
.appr-strip-cap{display:block;text-align:center;margin:2px 0 4px}
/* Legend: a short line segment in each series' actual color, not the color name. */
.appr-leg{display:inline-flex;align-items:center;gap:4px;margin:0 8px 0 0;white-space:nowrap;vertical-align:middle}
.appr-leg-seek{color:var(--dim);font-style:italic}
.appr-lg-line{display:inline-block;width:16px;height:0;border-top:2px solid currentColor}
.appr-lg-line.dashed{border-top-style:dashed}
.appr-lg-band{display:inline-block;width:16px;height:9px;border-radius:1px;background:rgba(96,165,250,0.30)}

/* Larger, more legible appropriateness panel (overrides the compact mock sizes) */
.appr-head{font-size:9.5px}
.appr-mock{font-size:9px}
.appr-meter{margin:9px 0}
.appr-mtop{font-size:12.5px;gap:8px}
.appr-mtop .appr-lbl{width:66px;font-size:12.5px}
.appr-mtop .appr-val{font-size:12.5px}
.appr-mtop .appr-flag{font-size:12px}
.appr-mscale{gap:7px;margin-top:4px}
.appr-mscale .appr-bar{height:16px;border-radius:3px}
.appr-mark{width:3px}
.appr-end{font-size:11px;min-width:24px}
.appr-cap{font-size:11px;white-space:normal;margin:2px 0 0 70px;color:var(--muted)}
.appr-strip-wrap{margin-top:10px}
.appr-strip{height:168px}
.appr-yax{font-size:10px}
.appr-strip-cap{font-size:11px;margin:3px 0 6px}
.appr-controls{font-size:12px;gap:14px;margin-top:10px}
.appr-ctx select{font-size:12px;padding:3px 6px}

/* appropriateness meters v2 — axis line + range segment + labeled ticks + value dot */
.appr-mscale{align-items:flex-start;gap:8px}
.appr-end{align-self:flex-start;margin-top:4px;min-width:26px}
.appr-axis{position:relative;flex:1;height:30px}
.appr-axis-line{position:absolute;left:0;right:0;top:9px;height:2px;background:var(--border);border-radius:1px}
.appr-range{position:absolute;top:7px;height:6px;border-radius:3px;background:color-mix(in srgb,var(--accent) 60%,transparent)}
.appr-range.danger{background:color-mix(in srgb,var(--warn) 60%,transparent)}
.appr-tick{position:absolute;top:5px;width:1px;height:11px;background:var(--muted)}
.appr-ticklbl{position:absolute;top:17px;font-size:10px;color:var(--muted);transform:translateX(-50%);white-space:nowrap;font-family:ui-monospace,Menlo,monospace}
.appr-dot{position:absolute;top:4px;width:11px;height:11px;border-radius:50%;background:#fff;border:2px solid #0b1220;transform:translateX(-50%);box-shadow:0 0 0 1px rgba(0,0,0,.35)}

/* Separate the over-time strip from the Peak meter above it with a recessed fill
   (no outline — just a darker "well" so it reads as its own graph) + more gap. */
.appr-strip-wrap{margin-top:16px}
.appr-strip{background:color-mix(in srgb,var(--panel-2) 52%,#000);border-radius:5px}

/* ── Style overhaul (see docs/style-guide.md) ────────────────────────────────
   The field-label token is now GLOBAL (base `.field label`). Category-label typography (incl. casing)
   is owned ONCE by the base `.chips-group-label` rule above — the old per-container `text-transform`
   overrides here are gone (they were the whack-a-mole: every container had to re-undo the base). */
/* Inline field action (Detect, Re-analyze…) — visually distinct from selectable chips (global, reusable) */
.field-head{display:flex;align-items:center;justify-content:space-between;gap:8px}
.field-action{display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:600;color:var(--accent-on);background:var(--accent-tint-soft);border:1px solid transparent;border-radius:5px;padding:2px 8px;cursor:pointer;flex-shrink:0;line-height:1.5}
.field-action:hover{background:var(--accent-tint-med)}
.field-action svg{flex-shrink:0;opacity:.95}
/* Small icon-only action in a field head (e.g. re-detect language) */
.field-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;
  color:var(--muted);background:transparent;border:1px solid var(--border);border-radius:5px;cursor:pointer;flex-shrink:0;padding:0}
.field-icon-btn:hover{color:var(--accent-on);background:var(--accent-tint-soft);border-color:transparent}
.field-icon-btn:disabled{opacity:.5;cursor:default}
.field-icon-btn.busy svg{animation:spin 0.9s linear infinite}

/* Machine-suggested, not-yet-confirmed field: provisional (dashed) selection + a confirm row */
.field.suggested .pick.on{
  border-style:dashed;border-color:var(--accent);
  background:var(--accent-tint-soft);color:var(--accent-on);
}
.suggest-row{display:flex;align-items:center;gap:8px;margin-top:6px}
.suggest-tag{font-size:11px;color:var(--muted);font-style:italic}
.suggest-confirm{font-size:11px;font-weight:600;color:var(--accent-on);background:var(--accent-tint-soft);
  border:1px solid transparent;border-radius:5px;padding:2px 10px;cursor:pointer;line-height:1.6}
.suggest-confirm:hover{background:var(--accent-tint-med)}

/* ============================================================================
   PROTOTYPE — Two-column "review" layout (proto branch):
   LEFT = video (top) + timeline (below);  RIGHT = grid (top) + properties/inspector.
   ============================================================================ */
.app[data-review="twocol"]{
  /* Slot layout, 3 columns (no DOM teleport):
       LEFT   = video + timeline (chrome/content) — own video/timeline divider (rsplit, --split-vh)
       MIDDLE = the spreadsheet (sheet slot), full height
       RIGHT  = editor + Subjects inspector (.col-props) — own divider (#osplit, --outliner-h)
     Two vertical dividers set the three widths: csplit (left|middle, --split-lw) and csplit2
     (middle|right, --split-rw). All four dividers are independent — each side owns its own
     horizontal split because they live in separate containers, not shared grid rows. */
  grid-template-columns:
    minmax(260px, var(--split-lw, 30%)) 5px minmax(320px, 1fr) 5px minmax(200px, var(--split-rw, 320px));
  grid-template-rows: auto minmax(140px, var(--split-vh, 44%)) 5px auto minmax(140px,1fr) auto;
  grid-template-areas:
    "topbar  topbar  topbar  topbar   topbar"
    "video   csplit  sheet   csplit2  props"
    "rsplit  csplit  sheet   csplit2  props"
    "chrome  csplit  sheet   csplit2  props"
    "content csplit  sheet   csplit2  props"
    "keys    keys    keys    keys     keys";
}
.app[data-review="twocol"] .col-form,
.app[data-review="twocol"] .splitter.left-split{display:none}   /* Overview hidden for now (needs a collapsible home) */
/* Second vertical divider (middle|right) exists only in Split. */
.splitter.col-split2{display:none}
.app[data-review="twocol"] .splitter.col-split2{display:block;grid-area:csplit2;cursor:col-resize}
.app[data-review="twocol"] .splitter.col-split2::after{top:0;bottom:0;left:-3px;right:-3px}
/* The grid occupies the MIDDLE `sheet` slot (placed by grid-area, not moved in the DOM); the
   editor + Subjects inspector (.col-props) is the RIGHT `props` slot, laid out by its own
   internal #osplit exactly as in normal mode — just a narrower column. */
.app[data-review="twocol"] .tab-pane[data-pane="spreadsheet"]{
  grid-area:sheet;display:flex;min-height:0;overflow:hidden}
/* column collapse is unavailable in Split — hide its button on the divider */
.app[data-review="twocol"] .splitter .col-collapse-btn{display:none}
/* Triage flag (both Spreadsheet tab + Split): a required/recommended field still empty AND live
   for THIS span's type — set in JS via _fieldGapTier (the single gap owner), NOT every blank cell,
   so optional / inapplicable fields don't read as "missing". Two tiers: required = strong amber
   ("you still owe this"); recommended = a muted dotted underline ("worth filling, not blocking"). */
.cell-edit.cell-gap-req{background:var(--warn-tint-med);border-radius:3px;min-width:16px;display:inline-block;min-height:12px}
.cell-select.cell-gap-req{background:var(--warn-tint-med);border-radius:3px}
.cell-edit.cell-gap-rec{background:var(--warn-tint-soft);border-radius:3px;min-width:16px;display:inline-block;min-height:12px;box-shadow:inset 0 -2px 0 var(--warn-tint-strong)}
.cell-select.cell-gap-rec{background:var(--warn-tint-soft);border-radius:3px;box-shadow:inset 0 -2px 0 var(--warn-tint-strong)}
/* Widget/read-only fields (emotion, intent, genre…) render as a dim placeholder; when empty AND a
   tracked gap, the placeholder carries the same tier tint so the count and the cells agree. */
.dim-cell.cell-gap-req{background:var(--warn-tint-med);border-radius:3px;padding:0 4px;color:#fbcb78}
.dim-cell.cell-gap-rec{background:var(--warn-tint-soft);border-radius:3px;padding:0 4px;box-shadow:inset 0 -2px 0 var(--warn-tint-strong)}
/* Spreadsheet header: "Gaps only" toggle + the required/recommended gap tally. */
.sheet-gaps-toggle{font:inherit;font-size:11px;padding:2px 9px;border-radius:999px;border:1px solid var(--border);background:transparent;color:var(--muted);cursor:pointer;margin-left:6px}
.sheet-gaps-toggle:hover{color:var(--fg);border-color:var(--muted)}
.sheet-gaps-toggle.on{background:color-mix(in srgb,var(--warn) 16%,transparent);border-color:color-mix(in srgb,var(--warn) 55%,transparent);color:#fbcb78}
.sheet-gap-count{font-size:11px;color:var(--muted);display:inline-flex;align-items:center;gap:5px;margin-left:8px}
.sheet-gap-count .gap-dot{width:8px;height:8px;border-radius:50%;display:inline-block}
.sheet-gap-count .gap-dot.req{background:color-mix(in srgb,var(--warn) 85%,transparent)}
.sheet-gap-count .gap-dot.rec{background:color-mix(in srgb,var(--warn) 40%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--warn) 50%,transparent) inset}
.sheet-gap-count .gap-spans{opacity:.7}
.sheet-gap-count.all-clear{color:#7fd6a3}
/* Spreadsheet row actions (duplicate / delete) — parity with Outline; hover/selected-revealed. */
#segTableBody .row-actions{display:flex;gap:3px;justify-content:flex-end;opacity:0;transition:opacity .1s}
#segTableBody tr:hover .row-actions,
#segTableBody tr.selected .row-actions{opacity:1}
#segTableBody .row-actions button{background:transparent;border:1px solid var(--border);color:var(--muted);border-radius:4px;cursor:pointer;font-size:11px;line-height:1;padding:1px 5px}
#segTableBody .row-actions button:hover{color:var(--text);border-color:var(--accent)}
/* Selected-row highlight is the SAME in every view: the accent tint from `.seg-table tbody tr.selected`
   / `.seg-row.selected` / `.outliner-row.selected`. (Was a Split-only 2px outline here, which made the
   same multi-selection look different in side-by-side than in the normal views — removed for consistency.) */

/* ============================================================================
   PROTOTYPE (proto/timbre-vocab): term-first Timbre widget.
   Defined, pickable vocabulary chips replacing the slider→term derivation.
   ============================================================================ */
/* Timbre is a SIBLING field to Pitch / Volume / Rate (all four in "Typical Values"), so its label reads
   as a peer field-label — same weight/case as the scale rows — NOT a section header. (It was styled like
   a group head, which made the fourth field look like its own section.) The widget is full-width, so the
   label sits as a block above it rather than inline-right like the scale rows, but the typography matches
   the global .field label token. */
.field-vocab > label{
  margin-bottom:6px;   /* block above the full-width widget; typography inherits .field label (sibling weight) */
}
/* Inherited (not yet overridden) span timbre: the widget is seeded with the speaker's baseline terms, so
   mute the SELECTED box to read as inherited — mirroring the scalar fields' muted/italic inherited value.
   The ↳ on the label already names it; this stops the seeded terms from looking like an own override. */
.field-vocab.from-default .tv-summary:not(:empty){opacity:.6;font-style:italic}
.tv-widget{display:flex;flex-direction:column;gap:7px}
/* The summary (the answer) and the definitions toggle share ONE row — the toggle no longer eats a
   whole line between the title and the values. The dashed separator sits under the whole row. */
.tv-head{display:flex;align-items:center;justify-content:flex-end;gap:8px}
/* Definitions toggle: a subtle link BELOW the chips (not a pill in the head eating the SELECTED box's
   width). Reveals each chip's inline meaning — sits right under the terms it explains. */
.tv-defs-toggle{align-self:flex-start;font-size:11px;color:var(--muted);background:none;border:0;
  padding:1px 0;margin-top:1px;cursor:pointer}
.tv-defs-toggle:hover{color:var(--text)}
.tv-defs-toggle.on{color:var(--accent)}
/* "edit" affordance — replaces the tiny ambiguous chevron. Shows ONLY when collapsed (a filled field
   folded to its answer); clicking it (or the summary box) re-opens the palette. */
.tv-edit{flex:0 0 auto;font-size:11px;color:var(--muted);background:var(--panel-2);border:1px solid var(--border);
  border-radius:5px;padding:2px 9px;cursor:pointer}
.tv-edit:hover{color:var(--text)}
.tv-widget:not(.tv-collapsed) .tv-edit{display:none}   /* nothing to edit-toggle until there's an answer to fold */
/* Folded: palette hidden, the head (its summary box) is the click-to-edit target; the definitions
   toggle hides (nothing to define while folded). */
.tv-collapsed .tv-head{cursor:pointer}
.tv-collapsed .tv-defs-toggle{display:none}
.tv-group{display:contents}   /* promoted into the shared .chips/.tv category-column grid above */
.tv-glabel{cursor:help}   /* typography + geometry from the shared category-label rule; only the help cursor is tv-specific */
.tv-chips{display:flex;flex-wrap:wrap;gap:5px}
.tv-chip{display:inline-flex;flex-direction:column;align-items:flex-start;gap:2px;
  background:var(--panel-2);color:var(--text);border:1px solid var(--border);border-radius:6px;
  padding:3px 9px;font-size:11px;cursor:pointer;line-height:1.3;text-align:left}
.tv-chip:hover{border-color:var(--accent)}
/* .tv-chip.on selected style is the canonical highlight (see CANONICAL chip rule above);
   the definition text stays muted (base .tv-def) since the chip is now tinted, not solid. */
.tv-def{display:none;font-size:10px;color:var(--muted);font-weight:400;max-width:220px}
.tv-def em{display:block;color:var(--dim);font-style:normal;margin-top:1px}
.tv-widget.defs-on .tv-chip{padding:5px 10px}
.tv-widget.defs-on .tv-def{display:block}
/* Result-first: summary leads the widget (left of the definitions toggle in .tv-head). Separator now
   lives on .tv-head so summary + toggle share the line. */
/* The selected-terms area: a DEFINED box, but only once it holds terms — empty collapses to nothing
   (no well, no reserved space). Present in both collapsed (the answer) and open (the accumulation tray). */
.tv-summary{flex:1;display:flex;flex-wrap:wrap;align-items:center;gap:6px;min-height:0}
.tv-summary:not(:empty){background:var(--panel);border:1px solid var(--border);border-radius:8px;padding:6px 9px}
.tv-sumlabel{font-size:10px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}
/* Selected-term summary pill = the canonical highlight (tint), matching the grid chips.
   Its children (term, intensity meter, ×) inherit the normal meter/label colours that
   already read on the tint — no more white-on-solid-accent. */
/* Selected-term pill: a plain highlight chip (term + remove). No per-term intensity. */
.tv-sel{display:inline-flex;align-items:center;gap:6px;background:var(--accent-tint-soft);
  border:1px solid var(--accent);border-radius:6px;padding:3px 8px}
.tv-sel b{font-size:11px;font-weight:500;color:var(--accent-on)}
.tv-sel .tv-x{background:none;border:0;cursor:pointer;color:var(--muted);font-size:13px;line-height:1;padding:0}
.tv-sel .tv-x:hover{color:var(--text)}
.tv-x{border:0;background:transparent;color:var(--muted);cursor:pointer;font-size:14px;line-height:1;padding:0 2px}
.tv-x:hover{color:var(--danger)}
