/**
 * The product-skin stylesheet, shared by capture.html (the filmstrip view,
 * 2026-08 redesign) and connect.html (the extension login prompt).
 *
 * This is no longer a skin over styles.css — both pages load ONLY
 * ember-tokens.css (the shared Ember tokens, first) and this file. The rest
 * of the cockpit (index, agent, captures list) keeps its ops look in
 * styles.css; nothing here leaks there and nothing there is needed here.
 *
 * The design language (rationale in the extension side panel's header,
 * browser-control/ember-extension/sidepanel.html — this page is its mirror):
 * warm neutrals (ink never pure black, paper never pure white), the EMBER
 * accent reserved for FILLS — the played timeline, the playhead, dots,
 * focus, selection rings — never as text (that is --ember-ink) and never as
 * a button fill; primaries are flat ink that INVERT on hover (white fill,
 * ink text); headings are quiet 11px sentence-case labels; cards with soft
 * borders and a whisper of shadow. The panel's brand-band aura is
 * deliberately NOT reproduced here (spec §4).
 */

/* ── supported widths (capture.html only) ─────────────────────────────────
   This band applies to capture.html, the ADMIN surface: an operator at a
   desk, reviewing a 1080p recording against a 54-step trace against
   per-step verdicts. That comparison is the page's reason to exist and it
   needs two columns, so the supported floor is 1280px — a laptop. It does
   NOT apply to the other two pages that load this same stylesheet
   (index.html, the customer home page, and connect.html, the extension
   approve page) — both stay usable at any width; see the `.wide-view`
   scoping note below the `.narrow-note` rules.

   Below the floor capture.html does not degrade — it STOPS:
     ≥1280  supported — the two-column comparison, hero and rail fluid;
     1080–1280  still two columns, tighter;
     <1080  nothing renders but `.narrow-note`, which explains why.
   A stacked half-version was the obvious middle ground and the wrong one:
   this page's whole job is holding two things side by side, so a single
   column is not a smaller version of it — it is a different, worse page
   that still looks authoritative. Saying so costs one media query and
   leaves a link out. */

/* ── base ────────────────────────────────────────────────────────────────── */

* {
  box-sizing: border-box;
}

/* Panes get explicit displays below — [hidden] must still win. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 13px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--ember-ink);
}

button {
  font-family: var(--font);
  cursor: pointer;
}

button:disabled {
  cursor: not-allowed;
}

:focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 2px;
}

/* ── scrollbars ───────────────────────────────────────────────────────────
   Quiet warm-ink pills on a transparent track, everywhere the page scrolls
   (the window, the transcript, the filmstrip dock, the SOP editor) — the
   OS default bar is grey-blue chrome and reads as someone else's app.

   Two engines, deliberately NOT both at once: Chrome honours the standard
   `scrollbar-color` but IGNORES the `::-webkit-*` pseudo-elements as soon
   as it is set, and the pseudo-elements are what give us the inset pill
   (a transparent border + `background-clip: padding-box`). So the standard
   properties are fenced behind `@supports not selector(::-webkit-scrollbar)`
   — Firefox takes that branch, Chrome/WebKit take the pseudo-elements. */
@supports not selector(::-webkit-scrollbar) {
  * {
    scrollbar-width: thin;
    scrollbar-color: rgba(28, 26, 23, 0.28) transparent;
  }
}

::-webkit-scrollbar {
  width: 12px;
  height: 12px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: rgba(28, 26, 23, 0.2);
  /* The transparent border insets the pill inside the gutter — the reason
     this needs the pseudo-elements rather than `scrollbar-color`. */
  border: 3px solid transparent;
  background-clip: padding-box;
  border-radius: 999px;
}

::-webkit-scrollbar-thumb:hover {
  background: rgba(28, 26, 23, 0.34);
  background-clip: padding-box;
}

/* The corner where two bars meet (never a light-grey notch). */
::-webkit-scrollbar-corner {
  background: transparent;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
}

/* ── header band ─────────────────────────────────────────────────────────── */

.capture-head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 15px var(--gutter) 13px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.back-link {
  font-size: 12.5px;
  font-weight: 640;
  color: var(--ember-ink);
  text-decoration: none;
  flex: none;
}

.back-link:hover {
  text-decoration: underline;
}

.head-titles {
  min-width: 0;
}

.head-title {
  margin: 0;
  font-size: 16.5px;
  font-weight: 740;
  letter-spacing: -0.015em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.head-meta {
  font-size: 12px;
  color: var(--ink-faint);
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}

.head-spacer {
  flex: 1;
}

/* Status pill: quiet border chip with a toned dot. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 620;
  color: var(--ink-faint);
  padding: 4px 10px 4px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  flex: none;
}

.pill i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
  background: var(--ink-ghost);
}

.pill-ok i {
  background: var(--ok);
}

.pill-warn i {
  background: var(--amber);
}

.pill-err i {
  background: var(--danger);
}

/* ── notices + errors (fail visible, never blank) ────────────────────────── */

.capture-notice {
  padding: 8px var(--gutter);
  color: var(--ink-faint);
  font-size: 12px;
}

.panel-error {
  margin: 10px var(--gutter);
  padding: 10px 13px;
  background: var(--danger-tint);
  border: 1px solid #f5d5d0;
  border-radius: var(--radius-sm);
  color: var(--danger);
  font-size: 12px;
}

/* Cards carry their own error strips — no page gutter inside a card. */
.side-card .panel-error,
.doc-card .panel-error {
  margin: 10px 0 0;
}

/* ── pipeline crumbs ─────────────────────────────────────────────────────── */

.crumbs {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px var(--gutter);
}

.crumb {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  font-weight: 660;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink-soft);
  transition: background 140ms var(--ease), color 140ms var(--ease);
}

.crumb:hover:not(:disabled) {
  background: var(--line-soft);
  color: var(--ink);
}

.crumb i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
  background: var(--ink-ghost);
}

.crumb-done i {
  background: var(--ok);
}

.crumb-now {
  border-color: #e8c9bb;
  background: #fcf0ea;
  color: var(--ink);
}

.crumb-now i {
  background: var(--ember);
}

.crumb-failed i {
  background: var(--danger);
}

/* Locked: dashed, quiet — and the reason is IN the chip (crumb-note),
   visible, never tooltip-only. */
.crumb-locked {
  color: var(--ink-ghost);
  border-style: dashed;
  background: transparent;
}

.crumb-locked i {
  opacity: 0.5;
}

/* The crumb whose view is on screen gets the ember underline-as-ring cue. */
.crumb-active:not(:disabled) {
  box-shadow: 0 0 0 1px var(--ember);
  border-color: var(--ember);
}

.crumb-note {
  font-size: 12px;
  font-weight: 480;
  color: var(--ink-ghost);
}

.crumb-now .crumb-note,
.crumb-failed .crumb-note {
  color: var(--ink-faint);
}

.crumb-sep {
  /* aria-hidden decoration, not a word — the only sanctioned use of the
     non-text colour. */
  color: var(--ink-decor);
  font-size: 12px;
}

/* ── filmstrip home view ─────────────────────────────────────────────────── */

/* ── the control-room layout ──────────────────────────────────────────────
   Horizontal bands, not columns: command bar (crumbs + review, styled below
   with the crumbs/review rules) → stage row (transcript left, monitor +
   Now-showing right) → full-width timeline → full-width filmstrip dock. */

.cmdbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px var(--gutter);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  /* The stage strip IS this page's navigation, and the approve control sits
     in the same band. A SOP runs well past a screen, so without this the
     reader loses both exactly while deciding whether to approve. */
  position: sticky;
  top: 0;
  z-index: 20;
}

.cmd-spacer {
  flex: 1;
}

.cmd-notes {
  padding: 0 var(--gutter);
}

.cmd-notes > * {
  margin: 8px 0 0;
}

/* In-bar variants: the crumbs and the review widgets lose their card/strip
   spacing and read as one line of controls. */
.cmdbar .crumbs {
  padding: 0;
  flex-wrap: wrap;
}

.cmdbar .review-running {
  margin: 0;
}

.cmdbar .review-run-row {
  margin: 0;
}

.cmdbar .review-run-row .lb {
  flex: none;
  width: auto;
}

.stage-row {
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  gap: 13px;
  padding: 13px var(--gutter) 0;
  align-items: stretch;
}

#view-home {
  padding-bottom: 20px;
}

.monitor-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  padding: 12px;
  display: flex;
  /* The Now-showing pane wraps UNDER the fixed hero when the window can't
     fit both side by side — the hero never shrinks. */
  flex-wrap: wrap;
  gap: 14px;
  min-width: 0;
}

/* The page's three sizing ranges, page-local (nothing outside this layout
   has an opinion about them).

   A wide screen should give its space to the thing that has no natural
   maximum — here, the recording. Prose does have one, so the Now-showing
   pane keeps its 680px readability cap and the rail stops at 460px; past
   that, extra width becomes gutter rather than longer lines.

   `--hero-h` stays derived rather than measured, so the box is still a
   CONSTANT at any given viewport: the Screen/Page no-reflow guarantee only
   needs the box to be deterministic, not to be the same number forever. */
:root {
  --gutter: 22px;
  --rail-w: clamp(336px, 22vw, 460px);
  --hero-w: clamp(880px, 46vw, 1600px);
  --hero-h: calc(var(--hero-w) * 9 / 16);
}

/* Enough room that the space is worth spending rather than hoarding. */
@media (min-width: 1900px) {
  :root {
    --gutter: 34px;
  }
}

/* The hero is a FIXED BOX beside the Now-showing pane at any given
   viewport — `--hero-w` × its 16:9 —
   in BOTH modes, so a Screen↔Page switch never reflows the page around it.
   (the height is derived from the width, so the ratio is exact at every
   size — an off-ratio box would letterbox the video inside its own frame.)
   Screen: the video fills it exactly (16:9 — captures record at 1080p; the
   aspect-ratio fallback holds the box before metadata; an odd-ratio dev
   video is cropped by the box rather than breaking the halo's wrap-hugs-
   frame invariant). Page: page_view.js contain-fits the replay into the
   box and centers it, monitor-style. */
.monitor-card .hero {
  flex: none;
  width: var(--hero-w);
  height: var(--hero-h);
  align-self: flex-start;
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.now-pane {
  flex: 1 1 260px;
  min-width: 0;
  /* Readability cap — an ultrawide monitor must not stretch the verdict
     card into one unreadable line. */
  max-width: 680px;
}

/* A floor, not a knife-edge: both the rail and the hero are ranges now, so
   the exact width at which the pane stops wrapping is piecewise — worked
   through, it lands around 1590px — with ~13px of slack, which is not a
   floor, it is a coin toss. This sits above the arithmetic on purpose:
   capping a pane that has WRAPPED would make it scroll for no reason.
   Above this the pane reliably sits BESIDE
   the hero, and only there does it need a ceiling: the step inspector and
   the narration are long, and a pane that grew past the hero would stretch
   the monitor card — and with it the Steps rail tied to that card's height.
   So it matches the hero and scrolls. Narrower, it has wrapped onto its own
   line, where its natural height is the right one. */
@media (min-width: 1700px) {
  .now-pane {
    max-height: var(--hero-h);
    overflow-y: auto;
    /* Its own scroll region: a wheel over the inspector must not chain into
       the page once the pane bottoms out. */
    overscroll-behavior: contain;
  }
}

/* Narrower than even the hero's MINIMUM (880px) can sit beside the rail, so
   it gives up its own width and fills the column instead. `aspect-ratio`
   keeps it a deterministic box in both modes — the no-reflow-on-switch
   guarantee survives; it just stops being expressible as a number.
   The arithmetic: ~419px of chrome flanks the hero at the rail's own
   minimum (44 gutter + 336 rail + 13 gap + 24 card padding + 2 border —
   measured, not derived), so 880 needs ~1299px of LAYOUT width. The
   breakpoint sits above that because a media query matches the VIEWPORT,
   which is ~15px wider wherever scrollbars are not overlays. */
@media (max-width: 1360px) {
  .monitor-card .hero {
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
  }
}

.hero {
  position: relative;
  background: #211e1b;
  min-height: 120px;
}

.seg {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 2;
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  background: var(--surface);
}

.seg button {
  font-size: 12px;
  font-weight: 640;
  padding: 6px 13px;
  color: var(--ink-soft);
  background: var(--surface);
  border: 0;
  border-right: 1px solid var(--line);
}

.seg button:last-child {
  border-right: 0;
}

.seg button:hover:not(.active) {
  background: var(--line-soft);
  color: var(--ink);
}

.seg button.active {
  background: var(--ink);
  color: #fff;
}

/* The wrap hugs the video box exactly so the halo's frame-fraction
   percentages resolve against the frame, not a letterboxed container.
   Never `max-height` + `object-fit` on the video — object-fit letterboxes
   inside the element box and would shift the halo off the true frame. The
   hero's fixed width (`.monitor-card .hero`) is the one sizing input: the
   video fills this wrap (width 100%, height by intrinsic ratio), so the
   wrap still hugs the frame. */
.screen-wrap {
  position: relative;
  display: block;
  width: 100%;
}

/* width:100% + height:auto keeps the element box exactly the frame's aspect
   — no max-height clamp: a letterboxed box would shift the halo's
   frame-fraction percentages off the true frame (the marker bug class the
   v5 framePos work eliminated; don't reintroduce it in CSS).
   `aspect-ratio` is the PRE-metadata fallback only: for a replaced element
   it applies until the intrinsic ratio is known, so the hero holds a stable
   16:9 box (captures record at 1080p) instead of collapsing to a strip
   while — or if — the browser loads the video's metadata. Once metadata
   arrives the intrinsic ratio wins; halo math is unaffected (there is no
   frame to mark before then). */
.capture-video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  background: #211e1b;
}

/* The v5 framePos click halo — deliberately large and translucent (the
   frame-fraction is exact, but a big soft ring reads better over video). */
.step-marker {
  position: absolute;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(210, 69, 30, 0.22);
  border: 2px solid var(--ember);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.65);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

/* Page replay (paused rrweb) as/inside the hero. */
#page-view {
  overflow: hidden;
  background: var(--surface);
  height: 100%;
}

#page-view .page-view-mount {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

#page-view .replayer-wrapper {
  position: relative;
}

#page-view iframe {
  border: 0;
  pointer-events: none; /* replay is evidence, not a live page */
}

/* The click marker IS rrweb's replayed cursor (design D5) — restyle its dot
   into a prominent ring, same palette as the Screen halo. */
#page-view .replayer-mouse::after {
  width: 26px;
  height: 26px;
  background: rgba(210, 69, 30, 0.22);
  border: 2px solid var(--ember);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.8);
  opacity: 1;
  transform: translate(-50%, -50%);
}

/* Hero placeholders: no video / replay refusal — words, not grey boxes. */
.hero-note {
  display: grid;
  place-items: center;
  /* Fill the hero's box — a note must hold the same frame as the
     video/replay, or the page reflows around the mode switch. */
  height: 100%;
  min-height: 220px;
  padding: 24px;
  color: var(--ink-faint);
  font-size: 12.5px;
  text-align: center;
  background: var(--line-soft);
}

/* ── timeline ────────────────────────────────────────────────────────────── */

.timeline {
  /* Full page width — the control-room layout's spine. */
  padding: 18px var(--gutter) 6px;
}

.timeline-track {
  position: relative;
  height: 4px;
  background: var(--line);
  border-radius: 3px;
}

/* Played portion + playhead: ember, fill-only — exactly its lane. */
.timeline-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0%;
  background: var(--ember);
  border-radius: 3px;
  pointer-events: none;
}

.timeline-playhead {
  position: absolute;
  left: 0%;
  top: 50%;
  width: 12px;
  height: 12px;
  margin-left: -6px;
  border-radius: 50%;
  background: var(--ember);
  box-shadow: 0 0 0 2.5px var(--surface);
  transform: translateY(-50%);
  pointer-events: none;
  z-index: 2;
}

/* One tick per step: quiet ink-ghost by default; verdict-toned when a
   processed trace exists — amber corrected/unclear, red (taller) dropped. */
.timeline-track .tick {
  position: absolute;
  top: -3px;
  width: 8px;
  height: 10px;
  margin-left: -4px;
  padding: 0;
  border: 0;
  background: transparent;
}

.timeline-track .tick::after {
  content: "";
  position: absolute;
  left: 3px;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--ink-ghost);
}

.timeline-track .tick:hover::after {
  background: var(--ink);
}

.timeline-track .tick-amber::after {
  background: var(--amber);
}

.timeline-track .tick-red::after {
  background: var(--danger);
}

.timeline-track .tick-tall {
  top: -6px;
  height: 16px;
}

.timeline-times {
  display: flex;
  justify-content: space-between;
  font: 12px var(--mono);
  color: var(--ink-ghost);
  margin-top: 8px;
}

/* ── filmstrip ───────────────────────────────────────────────────────────── */

.strip {
  /* Full-width dock under the timeline. The vertical padding is not
     decoration: the selected cell scales UP, and `overflow-x: auto` clips
     the cross axis too, so without this the lift would be shaved off. */
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 16px var(--gutter) 10px;
  overflow-x: auto;
}

.cell {
  flex: none;
  /* Transform, not width: growing the box would reflow every neighbour and
     shift the strip under the pointer. A lifted tile moves nothing. */
  transition: transform 180ms var(--ease), box-shadow 180ms var(--ease);
  transform-origin: center;
  /* Big enough to read the recorded screen at a glance — at ~104px a 1080p
     page was texture, not a picture. The shot below is this width's exact
     16:9, so the frame is never cropped, and the server's poster-frame width
     cap (web/captures/limits.py) is sized against this number: raising one
     without the other either softens the image or wastes bytes. */
  width: 192px;
  padding: 0;
  border-radius: 9px;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--line-soft);
  text-align: left;
}

/* The picked frame: bigger, ringed, and lifted off the dock. Scale is the
   loudest of the three because the strip is scanned peripherally — colour
   alone loses against 30 thumbnails of browser chrome. */
.cell.on {
  border-color: var(--ember);
  transform: scale(1.07);
  box-shadow:
    0 0 0 2px var(--ember),
    0 6px 16px -6px rgba(28, 26, 23, 0.35);
  position: relative;
  z-index: 1; /* over its neighbours, since the scale overlaps them */
}

.cell-shot {
  position: relative;
  display: block;
  height: 108px;
  background: var(--line-soft);
  overflow: hidden;
}

.cell-shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A cell whose frame failed twice (queue's one retry exhausted): hide just
   that image, not the strip — the button stays clickable, seeking the
   video, with no broken-image glyph. */
.cell-missing .cell-shot {
  background: var(--line-soft);
}

.cell-missing .cell-shot img {
  display: none;
}

/* Dropped overlay wash. */
.cell-dropped .cell-shot::after {
  content: "dropped";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 12px;
  font-weight: 730;
  color: var(--danger);
  background: rgba(253, 237, 235, 0.75);
}

.cell-label {
  display: flex;
  justify-content: space-between;
  padding: 5px 9px;
  font-size: 12px;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  background: var(--surface);
}

/* ── side column ─────────────────────────────────────────────────────────── */

.side-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  padding: 14px 16px;
}

/* Quiet labels, not prominent section titles — the single biggest cue this
   is a product, not a console. */
.card-label {
  font-size: 12px;
  font-weight: 660;
  color: var(--ink-faint);
  margin: 0 0 9px;
  letter-spacing: -0.005em;
  text-transform: none;
}

.now-title {
  font-size: 14px;
  font-weight: 720;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}

.chip-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 7px;
  flex-wrap: wrap;
}

.meta-chip {
  font-size: 12px;
  font-weight: 640;
  padding: 2.5px 8px;
  border-radius: 999px;
  background: var(--line-soft);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.now-url {
  font: 12px var(--mono);
  color: var(--ink-faint);
  margin-top: 8px;
  overflow-wrap: anywhere;
}

.now-url:empty {
  display: none;
}

/* Verdict card: tinted argument — danger for dropped, amber for
   corrected/unclear — model name + confidence, note as a quote. */
.verdict-card {
  margin-top: 12px;
  padding: 12px 13px;
  border-radius: var(--radius-sm);
}

.verdict-danger {
  background: var(--danger-tint);
  border: 1px solid #f5d5d0;
}

.verdict-amber {
  background: var(--amber-tint);
  border: 1px solid #f0dcbb;
}

.verdict-head {
  font-size: 12px;
  font-weight: 720;
}

.verdict-conf {
  font-weight: 620;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.verdict-quote {
  margin: 3px 0 0;
  font-size: 12px;
  color: var(--ink-soft);
  line-height: 1.5;
}

.verdict-quote::before {
  content: "“";
}

.verdict-quote::after {
  content: "”";
}

.verdict-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}

.verdict-actions .action-btn {
  background: rgba(255, 255, 255, 0.7);
}

/* ── step inspector (the Now-showing pane's lower half) ──────────────────
   Every field the recorder captured for the selected step. Reference
   material, so it reads as a quiet spec sheet — hairline-separated groups,
   a label column in the muted ink, values in mono because most of them ARE
   code (selectors, markup, coordinates). It never competes with the step's
   headline above it. */

.detail-pane {
  margin-top: 14px;
  border-top: 1px solid var(--line);
  padding-top: 2px;
}

.detail-group {
  padding: 10px 0 2px;
  border-bottom: 1px solid var(--line-soft);
}

.detail-group:last-of-type {
  border-bottom: 0;
}

.detail-title {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 660;
  color: var(--ink-ghost);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

/* Label / value as a two-column grid: the labels are short and repeat down
   the pane, so a fixed rail keeps every value starting on the same line. */
.detail-list {
  display: grid;
  grid-template-columns: 104px minmax(0, 1fr);
  gap: 4px 10px;
  margin: 0;
}

.detail-label {
  font: 12px var(--mono);
  color: var(--ink-ghost);
  overflow-wrap: anywhere;
}

.detail-value {
  margin: 0;
  font: 12px var(--mono);
  color: var(--ink-soft);
  /* A CSS selector or a URL has no spaces to break at — break anywhere
     rather than push the pane wider than its column. */
  overflow-wrap: anywhere;
}

/* Markup and JSON: preserved whitespace, capped so one enormous
   domNeighborhood can't own the pane, scrolling inside its own box. */
.detail-code {
  white-space: pre-wrap;
  max-height: 168px;
  overflow: auto;
  overscroll-behavior: contain;
  padding: 7px 8px;
  border-radius: var(--radius-sm);
  background: var(--line-soft);
  color: var(--ink-soft);
  font: 12px/1.5 var(--mono);
}

.detail-raw {
  margin: 10px 0 2px;
}

.detail-raw > summary {
  font-size: 12px;
  color: var(--ink-faint);
  cursor: pointer;
  padding: 3px 0;
}

.detail-raw > summary:hover {
  color: var(--ink);
}

.detail-raw > .detail-code {
  margin-top: 6px;
  max-height: 320px;
}

/* ── review summary card ─────────────────────────────────────────────────── */

.review-running {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--ink-faint);
  font-size: 12px;
  margin: 4px 0 8px;
}

.spinner {
  width: 12px;
  height: 12px;
  flex: none;
  border-radius: 50%;
  border: 2px solid var(--line);
  border-top-color: var(--ember);
  animation: spin 0.9s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.side-note {
  font-size: 12px;
  color: var(--ink-faint);
  line-height: 1.55;
  margin-top: 9px;
}

.review-run-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}

.review-run-row .lb {
  flex: 1;
  min-width: 0;
}

/* ── buttons + selects (shared control language) ─────────────────────────── */

/* Selects wear the same shell as the ghost buttons beside them: surface
   fill, soft border, ink text — plus our OWN chevron. `appearance: none`
   drops the OS widget (the bare native control was the one place this page
   still looked like a browser default); the chevron is an inline SVG data
   URI, and its stroke is the one value here that cannot be a token — a data
   URI can't read `var()`. It is `--ink-faint` (#7a726b); if that token
   moves, move the `%237a726b` below with it.

   `color-scheme: light` is deliberate: the option POPUP is drawn by the OS
   and cannot be styled, so this at least pins it to a light menu on a
   dark-themed desktop instead of a black list under a paper-white page. */
select {
  appearance: none;
  border: 1px solid var(--line);
  background-color: var(--surface);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237a726b' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  color: var(--ink);
  color-scheme: light;
  border-radius: var(--radius-sm);
  font: 500 12.5px var(--font);
  padding: 7px 30px 7px 11px;
  transition: background-color 140ms var(--ease), border-color 140ms var(--ease),
    box-shadow 140ms var(--ease);
}

select:hover:not(:disabled) {
  background-color: var(--line-soft);
  border-color: #e2dad1;
}

/* The house focus treatment for typed/chosen controls (mirrors the side
   panel's inputs): ember border + a soft ember halo, not the generic
   2px outline the page gives everything else. */
select:focus-visible {
  outline: none;
  border-color: var(--ember);
  box-shadow: 0 0 0 3px rgba(210, 69, 30, 0.13);
}

select:disabled {
  opacity: 0.45;
  background-color: var(--line-soft);
}

/* ── the custom listbox (lib/listbox.js) ──────────────────────────────────
   The rules above now only dress the FALLBACK: every select on this page is
   enhanced at boot, and `.lb-native` hides it. They still earn their place —
   if capture.js throws before the enhancement runs, the native controls are
   what's left on screen, and they should not look like raw browser chrome.

   The trigger repeats that shell as a real button (so the swap is invisible)
   with one upgrade: its chevron is a MASK rather than a background image, so
   the arrow takes `--ink-faint` from the token instead of a copied hex, and
   can rotate when the popup opens. */

.lb {
  display: inline-flex;
  min-width: 0;
}

.lb-native {
  display: none;
}

.lb-trigger {
  flex: 1;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  appearance: none;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius-sm);
  font: 500 12.5px var(--font);
  padding: 7px 11px;
  text-align: left;
  transition: background 140ms var(--ease), border-color 140ms var(--ease),
    box-shadow 140ms var(--ease);
}

.lb-value {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lb-trigger::after {
  content: "";
  flex: none;
  width: 10px;
  height: 6px;
  background-color: var(--ink-faint);
  /* Alpha-only mask: the stroke colour inside the SVG is irrelevant, the
     colour above is what paints. */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    no-repeat center / contain;
  transition: transform 160ms var(--ease);
}

.lb-trigger:hover:not(:disabled) {
  background: var(--line-soft);
  border-color: #e2dad1;
}

.lb-trigger:focus-visible,
.lb-trigger[aria-expanded="true"] {
  outline: none;
  border-color: var(--ember);
  box-shadow: 0 0 0 3px rgba(210, 69, 30, 0.13);
}

.lb-trigger[aria-expanded="true"]::after {
  transform: rotate(180deg);
}

.lb-trigger:disabled {
  opacity: 0.45;
  background: var(--line-soft);
  cursor: not-allowed;
}

/* The popup renders into <body> as `position: fixed` — the one placement
   that escapes every ancestor overflow and stacking context. `listbox.js`
   sets top/left/max-height from `popupPlacement`. */
.lb-popup {
  position: fixed;
  z-index: 60;
  padding: 5px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 14px 34px -14px rgba(28, 26, 23, 0.3),
    0 2px 6px -2px rgba(28, 26, 23, 0.12);
  overflow-y: auto;
  overscroll-behavior: contain;
  animation: lb-in 120ms var(--ease);
}

@keyframes lb-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

.lb-popup[data-placement="above"] {
  animation-name: lb-in-above;
}

@keyframes lb-in-above {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
}

.lb-opt {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  font-size: 12.5px;
  color: var(--ink);
  cursor: pointer;
  white-space: nowrap;
}

/* Pointer hover and keyboard-active are the same affordance — the cursor
   and the arrow keys must never disagree about "this one". */
.lb-opt:hover,
.lb-opt.is-active {
  background: var(--line-soft);
}

/* The current choice: ember tint, same as a selected transcript row, AND a
   tick — so the state survives a tinted row being hovered. */
.lb-opt[aria-selected="true"] {
  background: #fcf0ea;
  font-weight: 620;
}

.lb-opt[aria-selected="true"]:hover,
.lb-opt[aria-selected="true"].is-active {
  background: #f8e2d6;
}

.lb-check {
  margin-left: auto;
  flex: none;
  font-size: 12px;
  color: var(--ember-ink);
  opacity: 0;
}

.lb-opt[aria-selected="true"] .lb-check {
  opacity: 1;
}

.action-btn {
  appearance: none;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius-sm);
  font: 600 12px/1 var(--font);
  padding: 8px 12px;
  transition: background 140ms var(--ease);
}

.action-btn:hover:not(:disabled) {
  background: var(--line-soft);
}

.action-btn:disabled {
  opacity: 0.45;
}

/* The Page view's size-gate button sits inside the refusal note. */
.page-load-confirm { margin-left: 8px; vertical-align: baseline; }

/* Two-step inline confirm (lib/confirm_click.js) — the install-as-skill
   button's overwrite step, replacing window.confirm() (Chrome's
   dialog-suppression heuristic can silently no-op it). First click arms a
   button into this state; a second click within the window fires the
   guarded action. */
.confirm-armed {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}

.confirm-armed:hover:not(:disabled) {
  background: var(--ember-ink);
  border-color: var(--ember-ink);
}

/* The page's primaries: flat ink, INVERTING on hover (white fill, ink text)
   — the panel's Hire/Create register. Never an ember fill. */
.btn-primary {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  border: 1px solid var(--ink);
  background: var(--ink);
  color: #fff;
  border-radius: var(--radius-sm);
  font: 640 12.5px/1 var(--font);
  padding: 9px 14px;
  transition: background 140ms var(--ease), color 140ms var(--ease);
}

.btn-primary:hover:not(:disabled) {
  background: var(--surface);
  color: var(--ink);
}

.btn-primary:disabled {
  opacity: 0.4;
}

.btn-wide {
  width: 100%;
}

/* ── transcript card ─────────────────────────────────────────────────────── */

/* The Steps rail's height is TIED to the monitor card beside it, with no
   number of its own to drift: `contain: size` makes this card contribute
   nothing to the grid row's height, so the row is sized by the monitor
   alone, and `align-items: stretch` (the grid default) then hands that exact
   height back. The tie holds in every mode — fixed hero, fluid hero, a
   wrapped Now-showing pane — because it never mentions the hero at all.
   The list scrolls inside. */
.steps-card {
  padding-bottom: 8px;
  display: flex;
  flex-direction: column;
  min-height: 0;
  contain: size;
}

.steps-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.step-list {
  list-style: none;
  margin: 4px -8px 0;
  padding: 0 8px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.step-list:focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 0;
}

.step-list-item {
  display: flex;
  align-items: baseline;
  /* Chips (tab, frame, verdict) wrap under the text rather than crushing it
     into per-character breaking — the text keeps a readable minimum. */
  flex-wrap: wrap;
  gap: 4px 7px;
  padding: 7px 8px 7px 22px;
  border-radius: var(--radius-sm);
  position: relative;
  cursor: pointer;
  transition: background 140ms var(--ease);
}

/* The trail rail: a hairline with a dot per step, ember when selected. */
.step-list-item::before {
  content: "";
  position: absolute;
  left: 7px;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line);
}

.step-list-item:first-child::before {
  top: 16px;
}

.step-list-item:last-child::before {
  bottom: calc(100% - 16px);
}

.step-list-item::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 13px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink-ghost);
  box-shadow: 0 0 0 2.5px var(--surface);
}

.step-list-item:hover {
  background: var(--line-soft);
}

/* The picked step, in the register the tile uses: the same ember tint, plus
   a spine on the trail rail so the row is findable while scrolling without
   reading a word of it. Weight, not size — a row that grew would reflow the
   list under the pointer. */
.step-list-item.selected {
  background: #fcf0ea;
  box-shadow: inset 2px 0 0 var(--ember);
}

.step-list-item.selected .step-text {
  color: var(--ink);
  font-weight: 620;
}

.step-list-item.selected .step-seq {
  color: var(--ember-ink);
}

/* The trail dot swells into the selected state rather than only recolouring. */
.step-list-item.selected::after {
  background: var(--ember);
  width: 9px;
  height: 9px;
  left: 3px;
  top: 12px;
  box-shadow: 0 0 0 2.5px var(--surface), 0 0 0 4px rgba(210, 69, 30, 0.18);
}

.step-list-item::after {
  transition: width 160ms var(--ease), height 160ms var(--ease),
    box-shadow 160ms var(--ease);
}

.step-seq {
  color: var(--ink-ghost);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  flex: none;
  min-width: 16px;
  text-align: right;
}

.step-text {
  flex: 1;
  min-width: 140px;
  color: var(--ink);
  font-size: 12.5px;
  line-height: 1.45;
  /* A recorded URL is one unbreakable token; two lines of it is plenty. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}

.step-time {
  color: var(--ink-ghost);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  flex: none;
}

.step-tab-chip,
.step-frame-chip {
  background: var(--line-soft);
  color: var(--ink-faint);
  border-radius: 999px;
  font-size: 12px;
  font-weight: 650;
  padding: 1.5px 7px;
  flex: none;
}

.step-idle-divider {
  text-align: center;
  font-size: 12px;
  color: var(--ink-ghost);
  font-style: italic;
  padding: 4px 0;
  list-style: none;
}

/* Stage-2 verdict chips (lib/step_review.js's VERDICT_CLASS). */
.step-review-chip {
  border-radius: 999px;
  font-size: 12px;
  font-weight: 730;
  padding: 1.5px 7px;
  flex: none;
}

.review-confirmed {
  background: var(--ok-tint);
  color: var(--ok);
}

.review-corrected {
  background: var(--amber-tint);
  color: #8a5a17;
}

.review-dropped {
  background: var(--danger-tint);
  color: var(--danger);
}

.review-unclear {
  background: var(--line-soft);
  color: var(--ink-faint);
}

/* Dropped steps stay listed, struck through — nothing silently vanishes. */
.step-list-item.step-dropped .step-text {
  color: var(--ink-ghost);
  text-decoration: line-through;
}

/* ── SOP + Refine views (doc register) ───────────────────────────────────── */

.view-doc {
  padding: 2px var(--gutter) var(--gutter);
}

.doc-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  padding: 16px 18px 18px;
  max-width: 980px;
  /* Centered: the SOP/refine views have no side column, and a left-pinned
     980px card reads lopsided on wide monitors. */
  margin: 0 auto;
}

/* ── connect page (lib/connect_page.js) ───────────────────────────────────
   One yes/no decision, not a document — narrower than the SOP/refine
   980px default, which would read as an empty room around one sentence
   and a button. */
.connect-card {
  max-width: 460px;
  margin: 40px auto 0;
}

/* ── Pro home (lib/home/) ─────────────────────────────────────────────────
   Wider than the connect card: this page holds two forms side by side and
   a numbered install list, not one sentence and a button. */
.home-card {
  max-width: 640px;
  margin: 40px auto 0;
}

.connect-prompt {
  margin: 0 0 10px;
  font-size: 15px;
  font-weight: 720;
  letter-spacing: -0.01em;
  line-height: 1.45;
  overflow-wrap: anywhere;
}

.connect-actions {
  margin-top: 16px;
}

/* ── Pro home page (lib/home/home_page.js, install_panel.js) ─────────────
   The invitee's first screen — restrained, not decorated: the same card
   register as the connect page, plain form controls dressed in the token
   ramp, one error style reused everywhere a request can fail. */

/* Generic inline field/request error — .action-error is used for the
   /api/me failure, the create/join form errors, the build-missing notice,
   and the clipboard-copy failure. Scoped to #home-root (index.html's one
   content container): dom.js's showInlineError appends the same class name
   inside capture.html's pipeline tabs, and that page must keep its
   existing (unstyled) look — an unscoped rule here would restyle it too.
   --danger clears WCAG AA (4.8:1) against --line-soft, the darkest fill
   error text ever sits on here. */
#home-root .action-error {
  margin-top: 8px;
  font-size: 12px;
  color: var(--danger);
  line-height: 1.5;
}

#home-root .action-error:empty {
  display: none;
}

.install-panel {
  margin-top: 4px;
}

.install-panel p {
  margin: 10px 0;
  font-size: 13px;
  color: var(--ink-soft);
  line-height: 1.55;
}

.install-steps {
  margin: 12px 0 0;
  padding-left: 20px;
  font-size: 13px;
  color: var(--ink-soft);
  line-height: 1.7;
}

.install-steps li {
  margin-bottom: 4px;
}

.install-panel .back-link {
  display: inline-block;
  margin: 14px 14px 0 0;
  font-size: 12.5px;
  color: var(--ember-ink);
}

.org-onboarding {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin-top: 4px;
}

/* One form per row once two of them stop fitting side by side. */
@media (max-width: 620px) {
  .org-onboarding {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 640px) {
  .org-onboarding {
    grid-template-columns: 1fr;
  }
}

.org-form-section {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  padding: 14px 16px 16px;
  /* A grid item's default `min-width: auto` floors it at its content's
     intrinsic width. The inputs below carry the browser's own default
     width, so without this the two sections refuse to shrink into their
     columns and overhang the card. */
  min-width: 0;
}

.org-form-section h3 {
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 660;
  color: var(--ink-faint);
  letter-spacing: -0.005em;
}

.org-form-section form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.org-form-section label {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12px;
  color: var(--ink-faint);
}

/* Scoped to the org form fields only — a bare `input` selector would
   also restyle capture.html's Refine-tab "skip this question" checkbox
   (lib/capture/pipeline/refine_tab.js), an operator control this page
   must not touch. */
.org-form-section input {
  font: 500 13px/1.3 var(--font);
  /* Without a width an input keeps its intrinsic default (~224px), which
     is what pushed the sections out of the card. */
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--ink);
}

.org-form-section button[type="submit"] {
  align-self: flex-start;
  appearance: none;
  border: 1px solid var(--ink);
  background: var(--ink);
  color: #fff;
  border-radius: var(--radius-sm);
  font: 640 12.5px/1 var(--font);
  padding: 9px 14px;
  transition: background 140ms var(--ease), color 140ms var(--ease);
}

.org-form-section button[type="submit"]:hover:not(:disabled) {
  background: var(--surface);
  color: var(--ink);
}

.org-form-section button[type="submit"]:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.join-token-display {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.join-token-display p {
  margin: 0;
  font-size: 12.5px;
  color: var(--ink-soft);
  line-height: 1.5;
}

/* The address/code pair: a term above each value, since a teammate needs
   both and must not confuse one for the other. */
.join-token-display dl {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.join-token-display dt {
  font-size: 12px;
  color: var(--ink-faint);
  margin-bottom: 3px;
}

.join-token-display dd {
  margin: 0;
}

/* The workspace address on the ready screen — half of an invitation, so it
   reads as content, not as a caption. */
.workspace-address {
  margin: 0 0 4px;
  font-size: 12.5px;
  color: var(--ink-soft);
  line-height: 1.5;
}

.workspace-address code {
  padding: 2px 6px;
  background: var(--line-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: 500 12.5px/1.4 var(--mono);
  color: var(--ink);
}

.join-token-display code {
  display: block;
  padding: 9px 11px;
  background: var(--line-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: 500 13px/1.4 var(--mono);
  color: var(--ink);
  overflow-wrap: anywhere;
}

.join-token-display button {
  align-self: flex-start;
  appearance: none;
  border: 1px solid var(--ink);
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius-sm);
  font: 600 12px/1 var(--font);
  padding: 7px 12px;
  margin-right: 8px;
  transition: background 140ms var(--ease), color 140ms var(--ease);
}

.join-token-display button:hover:not(:disabled) {
  background: var(--ink);
  color: #fff;
}

.sop-toolbar,
.refine-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.busy-note {
  color: var(--ink-faint);
  font-size: 12px;
}

/* ── narration rail ───────────────────────────────────────────────────────
   What was SAID, in the column beside the frame it was said over — the
   second reading of the recording the Steps rail gives on the left. Its
   timecodes are BUTTONS (click a line, go to that moment), so they wear the
   ember-ink the page reserves for links, and the line at the playhead takes
   the same soft tint a selected step does. */

.narration-rail {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.narration-head {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 4px;
}

.narration-head .grow {
  flex: 1;
}

.narration-head .action-btn {
  padding: 3px 9px;
  font-size: 12px;
}

/* Bounded so a long recording cannot push the step inspector below it out
   of reach — the failure the footer disclosure had, at pane scale. */
.narration-rail .transcript-segments {
  max-height: 190px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.transcript-segments {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: grid;
  gap: 2px;
}

.transcript-segments li {
  display: flex;
  gap: 10px;
  align-items: baseline;
  font-size: 12.5px;
  line-height: 1.55;
  padding: 3px 5px;
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
}

/* The line being spoken at the playhead — the tint a selected step wears,
   so "where am I?" reads the same on both rails. */
.transcript-segments li.is-said {
  background: #fcf0ea;
  color: var(--ink);
}

.transcript-seg-time {
  border: 0;
  background: none;
  padding: 0;
  color: var(--ember-ink);
  font: 12px var(--mono);
  font-variant-numeric: tabular-nums;
  min-width: 3.2em;
  text-align: right;
  flex: none;
  cursor: pointer;
}

.transcript-seg-time:hover {
  text-decoration: underline;
}

/* ── the synthesis prompt (a disclosure inside the SOP view) ────────────── */

.prompt-pane {
  margin-top: 16px;
  border-top: 1px solid var(--line);
  padding-top: 4px;
}

.prompt-pane > summary {
  font-size: 12px;
  font-weight: 660;
  color: var(--ink-faint);
  cursor: pointer;
  padding: 8px 0;
  letter-spacing: -0.005em;
}

.prompt-pane > summary:hover {
  color: var(--ink);
}

.prompt-pane .sop-toolbar {
  margin: 4px 0 10px;
}

#prompt-blocks section {
  margin-bottom: 14px;
}

/* Each block is one message the model was sent — verbatim, so preserved
   whitespace and its own scroll box rather than a wall down the page. */
.prompt-content {
  margin: 6px 0 0;
  padding: 12px 13px;
  max-height: 340px;
  overflow: auto;
  overscroll-behavior: contain;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  background: var(--line-soft);
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  font: 12px/1.55 var(--mono);
}

/* ── the rendered SOP ─────────────────────────────────────────────────────
   MIRROR of the side panel's `.sop-doc` block
   (browser-control/ember-extension/sidepanel.html) — same document, same
   reading, whether a Pro sees it in the panel or an operator sees it here.
   Kept in sync by hand, like the rest of this stylesheet; the sizes are the
   panel's, scaled up for a full-width page instead of a 380px rail.

   Styled by TAG inside this scope, because markdown-it emits semantic tags
   and the scope is what stops them leaking into the shell. The h2 rule is
   the one with an opinion: a SOP's own section headings (Verification,
   Report) become quiet uppercase rules rather than shouting, which is what
   makes a synthesized document read as reference material. */
.sop-doc {
  margin: 14px 0 0;
  padding: 18px var(--gutter);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-size: 13.5px;
  line-height: 1.65;
  overflow-wrap: anywhere;
}

.sop-doc > :first-child {
  margin-top: 0;
}

.sop-doc > :last-child {
  margin-bottom: 0;
}

.sop-doc h1,
.sop-doc h2,
.sop-doc h3,
.sop-doc h4,
.sop-doc h5,
.sop-doc h6 {
  margin: 20px 0 7px;
  line-height: 1.3;
  font-weight: 700;
  color: var(--ink);
}

.sop-doc h1 {
  font-size: 19px;
  letter-spacing: -0.015em;
}

.sop-doc h2 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.7px;
  color: var(--ink-faint);
  font-weight: 800;
  border-top: 1px solid var(--line-soft);
  padding-top: 16px;
}

.sop-doc h1 + h2,
.sop-doc h2:first-child {
  border-top: 0;
  padding-top: 0;
}

.sop-doc h3,
.sop-doc h4,
.sop-doc h5,
.sop-doc h6 {
  font-size: 13.5px;
}

.sop-doc p {
  margin: 9px 0;
}

.sop-doc ul,
.sop-doc ol {
  margin: 9px 0;
  padding-left: 22px;
}

.sop-doc li {
  margin: 6px 0;
}

/* The one place ember appears in the document: numbering a Pro's steps. */
.sop-doc li::marker {
  color: var(--ember);
  font-weight: 700;
}

.sop-doc li ul,
.sop-doc li ol {
  margin: 6px 0;
}

.sop-doc li > p {
  margin: 0;
}

.sop-doc code {
  font: 12px var(--mono);
  background: var(--line-soft);
  padding: 1px 5px;
  border-radius: 4px;
}

.sop-doc pre {
  margin: 12px 0;
  padding: 12px 13px;
  overflow-x: auto;
  background: var(--line-soft);
  border-radius: var(--radius-sm);
}

.sop-doc pre code {
  background: none;
  padding: 0;
  line-height: 1.5;
}

.sop-doc blockquote {
  margin: 12px 0;
  padding: 2px 0 2px 13px;
  border-left: 3px solid var(--ember);
  color: var(--ink-soft);
}

.sop-doc hr {
  margin: 16px 0;
  border: 0;
  border-top: 1px solid var(--line);
}

.sop-doc a {
  color: var(--ember-ink);
}

.sop-doc strong {
  font-weight: 700;
}

/* Wide tables scroll inside their own box rather than stretching the page
   (the wrapper is markdown.js's one presentational post-pass). */
.md-table-wrap {
  overflow-x: auto;
  margin: 10px 0;
  max-width: 100%;
}

.md-table-wrap table {
  border-collapse: collapse;
  font-size: 12.5px;
}

.md-table-wrap th,
.md-table-wrap td {
  border: 1px solid var(--line);
  padding: 6px 10px;
  text-align: left;
}

.md-table-wrap th {
  background: var(--line-soft);
  font-weight: 600;
}

.sop-editor {
  display: block;
  width: 100%;
  min-height: 420px;
  margin-top: 14px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.6;
}

.sop-editor:focus {
  outline: none;
  border-color: var(--ember);
  box-shadow: 0 0 0 3px rgba(210, 69, 30, 0.13);
}

/* The install group rides the SOP toolbar (it is an action on the document,
   and it belongs beside the ones that make it), so it is an inline-flex
   span rather than a row of its own. `[hidden]` still wins — the page's
   base rule is `display: none !important`. */
.sop-install-row {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

/* Hairline between authoring the document and shipping it. */
.tool-div {
  width: 1px;
  align-self: stretch;
  min-height: 20px;
  background: var(--line);
  margin: 0 2px;
}

.refine-questions {
  margin-top: 14px;
}

.refine-question {
  padding: 10px 0;
  border-bottom: 1px solid var(--line-soft);
}

.refine-question-text {
  margin: 6px 0;
  font-size: 13px;
}

.refine-answer {
  display: block;
  width: 100%;
  min-height: 64px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--ink);
  font: 12.5px var(--font);
}

.refine-answer:focus {
  outline: none;
  border-color: var(--ember);
  box-shadow: 0 0 0 3px rgba(210, 69, 30, 0.13);
}

.refine-skip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  font-size: 12px;
  color: var(--ink-faint);
}

#refine-submit {
  margin-top: 12px;
}

.refine-round {
  margin-top: 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line-soft);
}

.refine-round-head {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 700;
}

.refine-transcript-q {
  margin: 6px 0 2px;
  font-size: 12.5px;
  font-weight: 640;
}

.refine-transcript-a {
  margin: 0 0 6px;
  font-size: 12.5px;
  color: var(--ink-soft);
}

.refine-versions {
  margin: 6px 0 0;
  padding-left: 18px;
  font-size: 12.5px;
}

/* ── under the floor ──────────────────────────────────────────────────────
   See the width bands at the top of this file. This rule is for
   capture.html only: capture.html's <body> carries the `wide-view` class,
   and only that body is replaced under the floor. connect.html and
   index.html load this same stylesheet but do not carry the class, so they
   are never affected — they have no `.narrow-note` element to fall back
   to, so hiding their content here would leave a blank page instead of a
   readable one. */

.narrow-note {
  display: none;
}

@media (max-width: 1079px) {
  body.wide-view > *:not(.narrow-note) {
    display: none !important;
  }

  .narrow-note {
    display: grid;
    place-items: center;
    min-height: 100vh;
    /* dvh where it exists — a phone's URL bar must not clip the message. */
    min-height: 100dvh;
    padding: 32px var(--gutter);
    text-align: center;
  }

  .narrow-note > div {
    max-width: 34ch;
    display: grid;
    gap: 12px;
  }

  .narrow-note p {
    margin: 0;
    color: var(--ink-soft);
    font-size: 13.5px;
    line-height: 1.6;
  }

  .narrow-note .narrow-title {
    color: var(--ink);
    font-size: 17px;
    font-weight: 720;
    letter-spacing: -0.015em;
  }

  .narrow-note a {
    justify-self: center;
    font-size: 12.5px;
    font-weight: 640;
    text-decoration: none;
  }

  .narrow-note a:hover {
    text-decoration: underline;
  }
}

/* ── the review's finding, in the Steps rail header ───────────────────────
   Moved out of the command bar: what a review FOUND belongs above the
   document it found it in, not beside the buttons that start one. Only the
   verdicts that happened are rendered (review_summary.js's verdictSummary),
   so the common case reads "53 confirmed · 1 dropped" instead of a
   four-cell table with two zeros in it. */

.verdict-summary {
  display: inline-flex;
  align-items: baseline;
  gap: 9px;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-ghost);
}

.verdict-count b {
  font-weight: 720;
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}

/* Only the verdicts that need a human eye carry a tone; a confirmation is
   the expected outcome and stays quiet. */
.verdict-count-corrected b {
  color: var(--amber);
}

.verdict-count-dropped b {
  color: var(--danger);
}

/* ── Session chip (shared header affordance; markup in capture.html,
 * logic in lib/session_chip.js). This page doesn't load styles.css, so
 * the chip gets its own token-based rendition of the same look. ── */

.session-chip {
  display: flex;
  align-items: center;
  gap: 8px;
}

.session-name {
  font-size: 12px;
  color: var(--ink-faint);
  white-space: nowrap;
}

.session-admin {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  /* --ember is a fill (4.4:1); this is small text, so it takes the ink. */
  color: var(--ember-ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 1px 5px;
}

.session-chip .header-link {
  font-size: 12px;
  color: var(--ink-faint);
  text-decoration: none;
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: none;
  font-family: inherit;
  cursor: pointer;
  white-space: nowrap;
}

.session-chip .header-link:hover {
  color: var(--ink);
  border-color: var(--ink-faint);
}

.session-chip .header-link:disabled {
  opacity: 0.5;
  cursor: default;
}

.session-error {
  font-size: 12px;
  color: var(--danger);
}

/* ── plain-language notes and labels ──────────────────────────────────────
   The panels this page grew to say what it is doing, rather than leaving a
   reader to infer it from the pixels. All of them share one register: the
   readable muted ink, never the decorative one, and never below the 12px
   floor the rest of this sheet keeps. */

/* Under the stage strip: what the open stage shows, plus (on Review) what
   the review actually did to the recording. */
.stage-hint {
  font-size: 12.5px;
  color: var(--ink-soft);
  line-height: 1.55;
  margin: 8px 0 0;
  max-width: 92ch;
}

/* The narration rail sits inside the current-step pane, so it states its own
   scope or a reader takes it for that step's narration. */
.scope-note {
  font-size: 12px;
  color: var(--ink-faint);
  line-height: 1.5;
  margin: 2px 0 6px;
}

/* Why the filmstrip's step numbers can skip. */
.strip-note {
  font-size: 12px;
  color: var(--ink-faint);
  line-height: 1.5;
  margin: 8px var(--gutter) 0;
}

/* What refinement does, on a tab that is otherwise one button over a void. */
.intro-note {
  font-size: 13px;
  color: var(--ink-soft);
  line-height: 1.65;
  margin: 12px 0 0;
  max-width: 72ch;
}

/* Visible labels for the three model pickers — each page has more than one,
   and an aria-label alone leaves sighted readers guessing. */
.field-label {
  font-size: 12px;
  font-weight: 560;
  color: var(--ink-soft);
  white-space: nowrap;
}

/* The one line inside the step inspector's technical disclosure that says
   who the contents are for. */
.detail-hint {
  font-size: 12px;
  color: var(--ink-faint);
  line-height: 1.5;
  margin: 4px 0 10px;
}

/* The SOP's YAML frontmatter, kept verbatim but out of the prose. */
.sop-frontmatter {
  margin: 22px 0 0;
  border-top: 1px solid var(--line);
  padding-top: 12px;
}

.sop-frontmatter > summary {
  font-size: 12px;
  color: var(--ink-faint);
  cursor: pointer;
  padding: 3px 0;
}

.sop-frontmatter > summary:hover {
  color: var(--ink);
}

.sop-frontmatter > pre {
  margin: 8px 0 0;
  font: 12px var(--mono);
  color: var(--ink-soft);
  background: var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  overflow-x: auto;
  white-space: pre;
}

/* `overflow-wrap: anywhere` on .sop-doc is what long recorded URLs need —
   without it a parameter table's URL column cannot shrink and pushes the
   last column off the card. But it also let the browser size the PARAMETER
   column at one character, which is what split `{{reddit_url}}` and
   `{{max_scroll_attempts}}` mid-token. Only that first column opts out, so
   a parameter name always reads as one token and every other cell still
   shrinks as before. */
.sop-doc td:first-child,
.sop-doc th:first-child,
.sop-doc td:first-child code {
  overflow-wrap: normal;
  word-break: keep-all;
  white-space: nowrap;
}

/* ---- Understand view ---------------------------------------------------- */
.understand-toggle[aria-pressed="true"] { background: var(--line-soft); border-color: var(--ember); color: var(--ember-ink); }
.understanding-grid { display: grid; grid-template-columns: 280px minmax(0, 1fr); gap: 16px; margin-top: 12px; }
.understanding-rows { display: flex; flex-direction: column; gap: 8px; }
/* .understanding-row is the grid container (two SIBLING buttons, never one
   nested in the other — a nested <button> is invalid HTML and an axe
   "nested-interactive" violation); .row-open carries the row's old click
   surface and button-reset styling. */
.understanding-row { display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; align-items: start; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); }
.understanding-row[aria-current="true"] { border-color: var(--ember); background: var(--line-soft); }
.row-open { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; padding: 0; font: inherit; color: inherit; text-align: left; background: none; border: 0; cursor: pointer; }
.row-open:disabled { cursor: default; }
.understanding-row .name { font-weight: 600; font-size: 14px; }
.understanding-row .state { font-family: var(--mono); font-size: 12px; color: var(--ink-soft); }
.understanding-row .reason { flex-basis: 100%; font-size: 12px; color: var(--ink-faint); }
.understanding-row.state-done .state { color: var(--ok); }
.understanding-row.state-failed .state { color: var(--danger); }
.understanding-row .run { align-self: start; }
.understanding-artifact h3 { margin: 0 0 4px; font-size: 16px; }
.understanding-artifact .summary { font-size: 13px; color: var(--ink-soft); margin: 0 0 12px; }
.understanding-artifact table { width: 100%; border-collapse: collapse; font-size: 13px; }
.understanding-artifact th, .understanding-artifact td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
.understanding-artifact th { font-family: var(--mono); font-size: 12px; color: var(--ink-faint); font-weight: 500; }
.understanding-artifact details { margin-top: 12px; }
.understanding-artifact pre { max-height: 420px; overflow: auto; font-size: 12px; background: var(--line-soft); padding: 10px; border-radius: var(--radius-sm); }
