/* Yarness admin console (ADR-0031) — unified with the dashboard's "Reclaim" design system.
   The design language (colors · accent · fonts · radius) is the shared SINGLE SOURCE OF TRUTH in
   tokens.css (= the dashboard baseline; kept byte-identical, guarded by uiTokensSync.test.ts) and
   is linked BEFORE this file. Only this UI's layout var + shell/login/table styling live here. */
/* --ui-zoom is the far-view scale (a wall/shelf display read from ~1m). It lives HERE, not in tokens.css:
   that file is kept byte-identical with the dashboard's and its own header sends layout vars to each UI.
   ⚠ `zoom`, not a root font-size: this UI carries no `rem`, so scaling the root would cascade to almost
   nothing. zoom scales computed lengths for the whole subtree — the 162 px font-sizes, the layout, and
   the inline styles app.js emits — without editing any of them, and it reflows (text stays sharp). */
:root { --rail-w: 220px; --ui-zoom: 1; }

* { box-sizing: border-box; }
html, body { height: 100%; }
body { margin: 0; font: 14px/1.5 var(--ui); background: var(--bg); color: var(--text); zoom: var(--ui-zoom); }
[hidden] { display: none !important; }
.muted { color: var(--dim); }
.mono { font-family: var(--mono); }
.ok { color: var(--ok); } .err { color: var(--err); }
a { color: inherit; text-decoration: none; }

/* Scrollbars — thin, theme-aware (matches the dashboard). */
* { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-thumb { background-color: var(--line); border: 3px solid transparent; background-clip: padding-box; border-radius: 8px; }
::-webkit-scrollbar-thumb:hover { background-color: var(--muted); }

/* ---------- controls ---------- */
input, button, textarea, select {
  font: inherit; background: var(--panel-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r); padding: 8px 10px;
}
input::placeholder { color: var(--faint); }
input:focus, textarea:focus, select:focus { outline: none; border-color: var(--accent); }
button { cursor: pointer; }
button.primary {
  background: var(--accent); border-color: var(--accent); color: #14120d;
  font-weight: 700; letter-spacing: .08em; text-transform: uppercase; font-size: 12px;
  box-shadow: 0 0 16px -4px var(--accent);
}
button.primary:hover { filter: brightness(1.06); }
button.secondary, button.ghost { background: transparent; border-color: var(--border); color: var(--text); }
button.secondary:hover, button.ghost:hover { border-color: var(--accent); }
button:disabled { opacity: .5; cursor: default; filter: none; }
.iconbtn { background: transparent; border: 0; padding: 6px 8px; color: var(--muted); border-radius: var(--r); font-size: 15px; }
.iconbtn:hover { background: var(--raised); color: var(--text); }
/* The far-view scale is hidden only on PHONE-SIZED TOUCH screens, where the topbar has no room and the
   browser's own pinch-zoom is the control. It stays on a large touch wall/kiosk, which is itself a
   plausible read-from-a-metre target. ⚠ The width clause is gated on `pointer: coarse` and must stay
   that way: a bare width query is engine-contingent under `zoom` (Chromium 149/151 does not re-evaluate
   media queries on CSS zoom, but that is unspecified and the Tauri shells are WebKit) and could hide the
   only decrement on a zoomed desktop, stranding it at its stored scale. A coarse pointer is a touch
   device with pinch-zoom as a recovery, and `pointer` cannot move with zoom. */
@media (pointer: coarse) and (max-width: 760px) { #ui-scale-dec, #ui-scale-inc { display: none; } }

/* ---------- login ---------- */
/* ⚠ viewport units are resolved against the VIEWPORT and then multiplied by `zoom`, so each one is
   divided by it: without this, `height: 100vh` at 150% is 1.5 viewports tall and the shell overflows. */
#login { height: calc(100vh / var(--ui-zoom)); display: grid; place-items: center; }
#login .card {
  position: relative; width: 340px; padding: 26px 24px;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--r);
}
#login .card::before, #login .card::after { content: ""; position: absolute; width: 11px; height: 11px; }
#login .card::before { top: -1px; left: -1px; border-top: 1px solid var(--accent); border-left: 1px solid var(--accent); }
#login .card::after { bottom: -1px; right: -1px; border-bottom: 1px solid var(--accent); border-right: 1px solid var(--accent); }
.brand { font-family: var(--ui); font-weight: 700; font-size: 20px; letter-spacing: .06em; }
.brand b { color: var(--accent-ink); }
.brand .tag { font-family: var(--mono); font-size: 10px; letter-spacing: .18em; text-transform: uppercase; color: var(--dim); }
#login .sub { color: var(--dim); font-family: var(--mono); font-size: 12px; margin: 8px 0 18px; }
label { display: block; font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: .12em; color: var(--dim); margin: 14px 0 5px; }
input, select, textarea { width: 100%; }
button.wide { width: 100%; margin-top: 22px; padding: 11px; }

/* ---------- app shell ---------- */
/* ⚠⚠ THE CONTENT ROW IS minmax(0, 1fr), NOT 1fr. `1fr` means `minmax(auto, 1fr)`, and that automatic
   minimum is the row's MIN-CONTENT height — the row cannot be shorter than its children demand. Measured
   live at 1512×945 (operator report 2026-09-21, "the right column exceeds the browser"): `#app` was the
   correct 945px, but the row stretched to 1182.5px and took `#main` with it, putting the composer at
   `bottom: 1195.5` — the Send button 250px BELOW the fold. This is the grid twin of flex's
   `min-height: auto`, and it is why every link BELOW the row (#main:has(.sc-col), #view, .sc-col,
   .sc-card) already sets `min-height: 0` and still could not shrink it: the row was never
   over-constrained, so `#main:has(.sc-col) { overflow: hidden }` never engaged. `minmax(0, 1fr)` lets the
   row be exactly the remainder of the viewport. */
#app { height: calc(100vh / var(--ui-zoom)); display: grid; grid-template-rows: 54px minmax(0, 1fr); grid-template-columns: var(--rail-w) minmax(0, 1fr);
  grid-template-areas: "top top" "side main"; }
/* c3 gap ruling 3: ONE zoom-aware notion of a short window. A `@media (max-height)` query measures the PHYSICAL window and never
   sees `--ui-zoom` (a 700px window at zoom 1.25 is 560 CSS px, yet never counted as short). `#app`'s height IS the effective
   height — `100vh / --ui-zoom` in CSS px — so a size container on it answers "short" (≤ 620) and "tiny" (≤ 460) for the zoomed
   page, and follows a resize and a UI-scale change with no script. Scoped to the Sessions view, the only reader. */
#app:has(.sc-col) { container: yarness-app / size; }
/* ⚠ `100vh` is the LARGE viewport on a real mobile browser — it excludes the dynamic toolbars — so the
   bottom of the shell can sit behind browser chrome even when the layout is correct. `dvh` follows the
   VISIBLE viewport. ⚠ UNVERIFIED: chromium device emulation has no toolbar, so `innerHeight ===
   visualViewport` and the committed height check cannot tell `vh` from `dvh`; this needs one real phone or
   it should not be called done. Strictly ADDITIVE — without `dvh` support the rule above stands. Divided
   by `--ui-zoom` for the same reason as the fallback: `zoom` multiplies a viewport unit. */
@supports (height: 100dvh) {
  #app, #login { height: calc(100dvh / var(--ui-zoom)); }
}
#topbar { grid-area: top; display: flex; align-items: center; gap: 14px; padding: 0 20px;
  background: var(--panel); border-bottom: 1px solid var(--border); }
#topbar .brand { font-size: 16px; }
.spacer { flex: 1; }
#whoami { font-family: var(--mono); font-size: 12px; color: var(--dim); }
#whoami b { color: var(--text); font-weight: 600; }

/* ⚠⚠ THE RAIL IS ITS OWN SCROLL CONTAINER. With the row fixed, 282px of page scroll remained and it was
   ALL the sidebar's: `#nav` measured 1162.5px inside `#side`'s 891px area with `overflow-y: visible`, so
   eight agent groups spilled past the fold and handed the scrollbar to the PAGE — which reads as "the
   right column exceeds the browser", because the column is what you are looking at. `min-height: 0` so
   the grid item may be shorter than its content and scroll instead of stretching the shell.
   ⚠ `overflow-y: auto` also forces the COMPUTED `overflow-x` from `visible` to `auto` (CSS overflow
   coupling), so on a phone the rail becomes a horizontal scroll container in addition to `.nav`'s own.
   Measured: both phone viewports behave and pass — recorded because it is the axis nobody was thinking
   about, not because it needed a fix. */
#side { grid-area: side; background: var(--panel); border-right: 1px solid var(--border); padding: 10px;
  overflow-y: auto; min-height: 0; }
.nav { display: flex; flex-direction: column; gap: 1px; }
.navitem { position: relative; text-align: left; background: transparent; border: 0; color: var(--muted);
  padding: 10px 12px; border-radius: var(--r); font-family: var(--ui); font-weight: 600; font-size: 12.5px;
  letter-spacing: .06em; text-transform: uppercase; cursor: pointer; }
.navitem:hover { background: var(--raised); color: var(--text); }
.navitem.active { background: var(--accent-dim); color: var(--accent-ink); }
.navitem.active::before { content: ""; position: absolute; left: -10px; top: 50%; transform: translateY(-50%);
  width: 3px; height: 16px; background: var(--accent); }

/* In-page tabs (EntropyHub: a project's Tickets / Agents). Shares .navitem's type treatment so a tab reads
   as navigation, but runs horizontally with the active marker UNDER the label — the sidebar's marker is a
   left-edge bar, which would sit in the middle of a row here. */
.subnav { display: flex; flex-direction: row; gap: 4px; border-bottom: 1px solid var(--border); }
.subnav .navitem { border-radius: var(--r) var(--r) 0 0; }
.subnav .navitem.active::before { left: 0; right: 0; top: auto; bottom: -1px; transform: none;
  width: auto; height: 2px; }

/* Main view — faint technical grid canvas (matches the dashboard's #view). */
#main { grid-area: main; overflow: auto; padding: 24px 26px;
  background-image: linear-gradient(var(--border-2) 1px, transparent 1px), linear-gradient(90deg, var(--border-2) 1px, transparent 1px);
  background-size: 34px 34px; background-position: -1px -1px; }

/* ---------- content ---------- */
.pagehead { display: flex; align-items: baseline; gap: 12px; margin-bottom: 18px; }
.pagehead h1 { font-family: var(--ui); font-size: 22px; font-weight: 700; letter-spacing: .01em; margin: 0; }
.pagehead .muted { font-family: var(--mono); font-size: 12.5px; }

.card { position: relative; background: var(--panel); border: 1px solid var(--border); border-radius: var(--r);
  padding: 16px 18px; margin-bottom: 16px; }
.card::before, .card::after { content: ""; position: absolute; width: 11px; height: 11px; }
.card::before { top: -1px; left: -1px; border-top: 1px solid var(--accent); border-left: 1px solid var(--accent); }
.card::after { bottom: -1px; right: -1px; border-bottom: 1px solid var(--accent); border-right: 1px solid var(--accent); }
.card h2 { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: .18em; color: var(--dim); margin: 0 0 12px; }

.formrow { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
.formrow > div { flex: 1; min-width: 140px; }
.formrow label { margin-top: 0; }
.check { display: inline-flex; align-items: center; gap: 6px; font-family: var(--ui); font-size: 13px; color: var(--text); text-transform: none; letter-spacing: 0; }
.check input { width: auto; }

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border-2); font-size: 13px; vertical-align: middle; }
th { color: var(--faint); font-family: var(--mono); font-weight: 600; font-size: 10px; letter-spacing: .14em; text-transform: uppercase; }
tr:hover td { background: var(--raised); }
td .rowbtn { padding: 4px 9px; font-size: 11px; margin-right: 6px; }

.pill { display: inline-block; padding: 2px 9px; border-radius: 20px; background: var(--panel-2); border: 1px solid var(--border); color: var(--muted); font-family: var(--mono); font-size: 11px; font-weight: 600; }
.pill.admin { background: var(--accent-dim); border-color: var(--accent); color: var(--accent-ink); }
.pill.on { color: var(--det-ink); border-color: var(--det); }
code { background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--r); padding: 0 5px; font-family: var(--mono); font-size: 12px; }

.empty { color: var(--dim); font-family: var(--mono); font-size: 12.5px; padding: 8px 2px; }
.tree { font-family: var(--mono); font-size: 12.5px; }
.tree .p { margin-top: 10px; color: var(--accent-ink); }
.tree .t { margin-left: 18px; color: var(--text); }
.tree .t .s { color: var(--dim); }

/* Org accordions + inline link buttons + the staged-access save bar (ADR-0032). */
.linkbtn { color: var(--accent-ink); font-family: var(--mono); font-size: 11.5px; margin-right: 12px; }
.linkbtn:hover { text-decoration: underline; }
.acc { position: relative; border: 1px solid var(--border); border-radius: var(--r); background: var(--panel); margin-bottom: 10px; }
.acc > summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 12px; padding: 12px 16px; font-family: var(--ui); font-weight: 600; }
.acc > summary::-webkit-details-marker { display: none; }
.acc > summary::before { content: "▸"; color: var(--dim); display: inline-block; transition: transform .12s ease; }
.acc[open] > summary::before { transform: rotate(90deg); }
.acc-t { font-size: 14px; }
.acc-net { margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--muted); }
.acc .acc-body { padding: 2px 16px 14px; }
.acc.proj { background: var(--panel-2); margin: 8px 0; }
.acc.proj .acc-body { padding: 2px 14px 10px; }
#savebar { position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%); z-index: 30; display: flex; align-items: center; gap: 14px; background: var(--panel); border: 1px solid var(--accent); border-radius: var(--r); padding: 10px 16px; box-shadow: 0 12px 40px -10px rgba(0,0,0,.5); font-size: 13px; }
#savebar b { color: var(--accent-ink); }
#savebar button { padding: 7px 14px; font-size: 12px; }

#toast { position: fixed; right: 20px; bottom: 20px; background: var(--panel); border: 1px solid var(--accent);
  border-radius: var(--r); padding: 10px 14px; font-size: 13px; box-shadow: 0 8px 30px -12px rgba(0,0,0,.5); }
#toast.err { border-color: var(--err); color: var(--err); }
#toast.ok { border-color: var(--ok); }

/* Schedule timeline (v28) — layout only; every colour comes from tokens.css.
   Structure: a horizontally scrollable board where each ROW is one task — a sticky left rail plus a
   7-column day track holding the task's bar. Rows are grouped into assignee swimlanes. */
.sched-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* The org/region pickers are <label class="check">, which otherwise inherits the global
   `label { margin: 14px 0 5px }` — that dropped them below the buttons, so the agent-token button next
   to them looked misaligned. Reset the margin and stop the global `select { width:100% }` stretching. */
.sched-bar .check { margin: 0; }
.sched-bar .check select { width: auto; }
.tc-btn { background: none; border: 0; padding: 0; color: var(--accent-ink); font-family: var(--mono); font-size: 11px; cursor: pointer; }
.tc-btn:hover { text-decoration: underline; }

/* A CONTINUOUS, horizontally-scrolling timeline (Jira-roadmap style): a sticky left rail + a
   fixed-width day strip (--tl-day × --tl-n columns) that all rows share, so bars line up as you scroll.
   --tl-n (the column count) is set inline per render; --tl-day MUST match SCHED_DAYW in app.js. */
.tl { --tl-rail: 190px; --tl-day: 46px; margin-top: 14px; overflow: auto; min-height: 480px; max-height: calc(80vh / var(--ui-zoom));
  border: 1px solid var(--border); border-radius: var(--r); background: var(--panel);
  display: flex; flex-direction: column; align-items: stretch; }
/* Fill the min-height when there are only a few rows: the last lane grows and its final row stretches,
   so the day gridlines run to the bottom (empty timeline space, Jira-style) instead of a blank slab.
   Content in that stretched row aligns to the TOP so a lone task isn't floated in the middle. */
.tl-lane:last-child { flex: 1 1 auto; display: flex; flex-direction: column; }
.tl-lane:last-child .tl-row:last-child { flex: 1 1 auto; }
.tl-lane:last-child .tl-row:last-child .tl-rail { align-items: flex-start; }
.tl-lane:last-child .tl-row:last-child .tl-bar,
.tl-lane:last-child .tl-row:last-child .tl-over { align-self: start; }
.tl-head, .tl-row { display: grid; grid-template-columns: var(--tl-rail) calc(var(--tl-day) * var(--tl-n)); align-items: stretch; }
.tl-track { display: grid; grid-template-columns: repeat(var(--tl-n), var(--tl-day)); position: relative; }

/* date axis — sticky on top; two rows (week band + day letters/dates) */
.tl-head { position: sticky; top: 0; z-index: 4; background: var(--panel-2); border-bottom: 1px solid var(--line); }
.tl-head-rail { position: sticky; left: 0; z-index: 5; background: var(--panel-2);
  font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: .18em; color: var(--dim);
  display: flex; align-items: flex-end; padding: 6px 10px; }
.tl-weeks { border-bottom: 1px solid var(--border-2); }
.tl-wk { grid-row: 1; padding: 5px 8px; font-family: var(--mono); font-size: 11px; color: var(--muted);
  border-left: 1px solid var(--line); white-space: nowrap; }
.tl-col { grid-row: 1; padding: 4px 0; text-align: center; font-family: var(--mono); font-size: 10px;
  color: var(--dim); border-left: 1px solid var(--border-2); line-height: 1.35; }
.tl-col b { display: block; font-size: 11px; color: var(--muted); font-weight: 600; }
.tl-col.wk-start { border-left-color: var(--line); }
.tl-col.is-today { color: var(--accent-ink); background: var(--accent-dim); box-shadow: inset 0 2px 0 var(--accent); }
.tl-col.is-today b { color: var(--accent-ink); }
.tl-col.is-wknd { background: var(--panel-2); }

/* swimlanes + rows */
.tl-lane { border-bottom: 1px solid var(--line); }
.tl-lane:last-child { border-bottom: 0; }
.tl-lane-head { position: sticky; left: 0; z-index: 3; display: flex; align-items: center; gap: 8px; padding: 6px 10px;
  background: var(--panel-2); border-bottom: 1px solid var(--border-2); font-size: 12.5px; width: var(--tl-rail); }
.tl-av { display: inline-flex; align-items: center; justify-content: center; width: 19px; height: 19px; border-radius: 50%;
  background: var(--accent-dim); color: var(--accent-ink); border: 1px solid var(--accent-line); font-family: var(--mono); font-size: 10px; }
.tl-lane-head .muted { font-family: var(--mono); font-size: 11px; }
/* project group band — one level ABOVE the assignee swimlanes (a flat sibling of .tl-lane so the
   last-lane height stretch and the drag hit-testing stay untouched) */
.tl-group-head { position: sticky; left: 0; z-index: 3; display: flex; align-items: center; gap: 8px; padding: 7px 10px;
  background: var(--accent-dim); border-top: 1px solid var(--accent-line); border-bottom: 1px solid var(--border-2);
  width: var(--tl-rail); font-size: 12.5px; }
.tl-group-head b { color: var(--accent-ink); }
.tl-group-head .muted { font-family: var(--mono); font-size: 11px; }
.tl-proj { color: var(--accent-ink); font-size: 11px; }
.tl-row { border-top: 1px solid var(--border-2); }
.tl-row:first-of-type { border-top: 0; }
.tl-rail { position: sticky; left: 0; z-index: 2; display: flex; align-items: center; gap: 6px; padding: 5px 10px;
  background: var(--panel); border-right: 1px solid var(--line); overflow: hidden; width: var(--tl-rail); }
.tl-row:hover .tl-rail { background: var(--raised); }
.tl-name { font-size: 12.5px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; }
.tl-name.is-done { color: var(--dim); text-decoration: line-through; }
.tl-flow { color: var(--det-ink); font-size: 11px; }
.tl-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: var(--dim); }

/* day cells (gridlines + today / weekend tint + a stronger divider each Monday) */
.tl-cell { grid-row: 1; border-left: 1px solid var(--border-2); min-height: 36px; }
.tl-cell.wk-start { border-left-color: var(--line); }
/* today reads as a continuous "now" band: an accent rule on both edges of the column, on every row */
.tl-cell.is-today { background: var(--accent-dim); box-shadow: inset 2px 0 0 var(--accent), inset -2px 0 0 var(--accent); }
.tl-cell.is-wknd { background: var(--panel-2); }
.tl-track.drop .tl-cell { background: var(--accent-dim); }

/* the bar: planned segment (draggable) + hatched overrun. Columns are narrow, so labels are allowed
   to overflow the bar to the right (a common roadmap treatment) rather than truncate to nothing. */
.tl-bar { grid-row: 1; z-index: 1; margin: 5px 2px; padding: 0 7px; border-radius: 9px; height: 26px; display: flex; align-items: center; gap: 5px;
  min-width: 0; cursor: grab; border: 1px solid var(--border); background: var(--raised); color: var(--text); font-size: 11.5px; overflow: visible; white-space: nowrap; }
.tl-bar:active { cursor: grabbing; }
.tl-bar.cut-l { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: 0; }
.tl-bar.cut-r { border-top-right-radius: 0; border-bottom-right-radius: 0; margin-right: 0; }
.tl-lbl { white-space: nowrap; overflow: visible; } /* labels may spill past a short bar to stay readable */
.tl-bar.is-planned { background: var(--accent-dim); border-color: var(--accent-line); color: var(--accent-ink); }
.tl-bar.is-active { background: var(--accent); border-color: var(--accent); color: #1b1812; }
.tl-bar.is-blocked { background: transparent; border-color: var(--warn); color: var(--warn); }
.tl-bar.is-done { background: var(--panel-2); border-color: var(--det); color: var(--det-ink); }
.tl-bar.is-cancelled { background: var(--panel-2); border-style: dashed; color: var(--dim); }
.tl-dot.is-planned { background: var(--accent); } .tl-dot.is-active { background: var(--accent); }
.tl-dot.is-blocked { background: var(--warn); } .tl-dot.is-done { background: var(--det); }
.tl-dot.is-cancelled { background: var(--faint); }
.tl-pair { font-family: var(--mono); font-size: 11px; color: var(--det-ink); }
/* progress: a thin fill along the bottom of the planned bar (width = %) + a small % label */
.tl-fill { position: absolute; left: 0; bottom: 0; height: 3px; background: var(--accent); opacity: .85;
  border-bottom-left-radius: 9px; pointer-events: none; }
.tl-bar.is-active .tl-fill { background: #1b1812; opacity: .3; } /* on the filled active bar a dark underline reads */
.tl-pct { font-family: var(--mono); font-size: 10px; opacity: .7; }
.tl-bar:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* resize handles at each end; reveal on hover/focus so bars stay clean at rest */
.tl-handle { position: absolute; top: 0; bottom: 0; width: 8px; cursor: col-resize; touch-action: none; }
.tl-handle-l { left: -1px; } .tl-handle-r { right: -1px; }
.tl-bar { position: relative; touch-action: none; }
.tl-bar:hover .tl-handle, .tl-bar:focus-visible .tl-handle { background: var(--accent); border-radius: 3px; }
.tl-empty { grid-column: 1 / -1; align-self: center; padding-left: 12px; color: var(--dim); font-family: var(--mono); font-size: 12px; }
.tl-over { grid-row: 1; z-index: 1; height: 26px; margin: 5px 3px 5px 0; border-radius: 0 9px 9px 0; border: 1px dashed var(--err);
  background: repeating-linear-gradient(45deg, transparent, transparent 4px, var(--accent-dim) 4px, var(--accent-dim) 8px); }

/* live drag feedback: a snapping ghost bar in the target track, a highlighted target lane, a floating date label */
.tl-ghost { grid-row: 1; z-index: 5; height: 26px; margin: 5px 0; border-radius: 9px; pointer-events: none;
  border: 1px dashed var(--accent); background: var(--accent-dim); box-shadow: 0 2px 10px -4px var(--accent); }
.tl-track.lane-hot { background: color-mix(in srgb, var(--accent) 6%, transparent); }
.tl-datelabel { position: fixed; z-index: 50; transform: translateX(-50%); pointer-events: none; white-space: nowrap;
  background: var(--text); color: var(--panel); font-family: var(--mono); font-size: 11px; padding: 3px 8px; border-radius: var(--r); box-shadow: 0 6px 20px -8px rgba(0,0,0,.6); }

/* backlog strip */
.tl-backlog { margin-top: 14px; }
.tl-chip { display: inline-flex; align-items: center; gap: 6px; margin: 0 7px 7px 0; padding: 4px 10px; border-radius: 12px;
  background: var(--panel-2); border: 1px solid var(--border); font-size: 12px; cursor: grab; touch-action: none; }
.tl-chip:active { cursor: grabbing; }
.tl-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* toast "Undo" action */
.toast-act { background: none; border: 1px solid currentColor; color: inherit; border-radius: var(--r); padding: 1px 8px; margin-left: 4px; font-size: 12px; cursor: pointer; }
.toast-act:hover { background: var(--accent-dim); }

/* task detail / edit panel (right-side sheet) */
.sc-panel { position: fixed; top: 0; right: 0; bottom: 0; width: 360px; max-width: calc(92vw / var(--ui-zoom)); z-index: 40; overflow: auto;
  background: var(--panel); border-left: 1px solid var(--accent); box-shadow: -18px 0 50px -20px rgba(0,0,0,.55); padding: 16px 18px; }
.sc-panel-hd { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.sc-panel-hd b { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: .16em; color: var(--dim); }
.sc-panel label { margin: 12px 0 4px; }
.sc-panel input, .sc-panel select, .sc-panel textarea { width: 100%; }
.sc-panel textarea { resize: vertical; font: inherit; }
/* Rendered markdown, wherever it appears (app.js `md`). Shared by the to-do detail view and hub threads.
   ⚠ Wide content — code and tables — scrolls INSIDE its own box, so the page body never scrolls sideways. */
.md > :first-child { margin-top: 0; } .md > :last-child { margin-bottom: 0; }
.md p { margin: 6px 0; }
.md h4, .md h5, .md h6 { margin: 12px 0 4px; font-size: 13px; font-weight: 600; }
.md ul, .md ol { margin: 6px 0; padding-left: 20px; }
.md li { margin: 2px 0; }
.md code { font-family: var(--mono); font-size: 12px; background: var(--panel); border: 1px solid var(--line); border-radius: 4px; padding: 0 4px; }
.md a { color: var(--accent); text-decoration: underline; }
.md pre { margin: 8px 0; padding: 9px 11px; background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r); overflow-x: auto; position: relative; }
.md pre code { background: none; border: 0; padding: 0; font-size: 12px; line-height: 1.5; white-space: pre; }
/* The language, if the fence named one — a label on the block, not a badge competing with the code. */
.md pre[data-lang]::before { content: attr(data-lang); position: absolute; top: 3px; right: 8px;
  font-family: var(--mono); font-size: 10px; letter-spacing: .08em; color: var(--faint); }
.md blockquote { margin: 8px 0; padding: 2px 0 2px 10px; border-left: 3px solid var(--line); color: var(--muted); }
.md hr { margin: 12px 0; border: 0; border-top: 1px solid var(--line); }
.md table { margin: 8px 0; border-collapse: collapse; font-size: 12.5px; display: block; overflow-x: auto; }
.md th, .md td { border: 1px solid var(--line); padding: 4px 8px; text-align: left; }
.md th { background: var(--panel); font-weight: 600; }
.md td { font-variant-numeric: tabular-nums; }

/* Detail read view: rendered markdown styled like the textarea box; click swaps to raw edit. */
.sp-md { min-height: 300px; max-height: calc(55vh / var(--ui-zoom)); overflow: auto; background: var(--panel-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r); padding: 8px 10px; cursor: text; }
.sp-md:hover { border-color: var(--line); }
.sp-md:focus { outline: none; border-color: var(--accent); }
.sp-md > :first-child { margin-top: 0; } .sp-md > :last-child { margin-bottom: 0; }
.sp-md p { margin: 6px 0; }
.sp-md h4, .sp-md h5, .sp-md h6 { margin: 10px 0 4px; font-size: 13px; font-weight: 600; }
.sp-md ul, .sp-md ol { margin: 6px 0; padding-left: 20px; }
.sp-md li { margin: 2px 0; }
.sp-md code { font-family: var(--mono); font-size: 12px; background: var(--panel); border: 1px solid var(--line); border-radius: 4px; padding: 0 4px; }
.sp-md a { color: var(--accent); text-decoration: underline; }
.sp-md-empty { color: var(--faint); }
.sc-flow { margin-top: 12px; font-family: var(--mono); font-size: 11px; color: var(--det-ink); }
.sc-panel-ft { display: flex; align-items: center; gap: 8px; margin-top: 18px; }

/* schedule ops chat drawer — a wider .sc-panel that flex-fills; the log scrolls internally */
.sc-panel.sc-chat { width: 560px; padding: 0; overflow: hidden; display: flex; flex-direction: column; }
.sc-panel.sc-chat .sc-panel-hd { margin: 0; padding: 11px 14px; border-bottom: 1px solid var(--line); }
.sc-chat-log { flex: 1 1 auto; overflow-y: auto; padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; }
.sc-msg { padding: 7px 10px; border-radius: 10px; font-size: 13px; white-space: pre-wrap; word-break: break-word; max-width: 90%; }
.sc-user { align-self: flex-end; background: var(--accent-dim); border: 1px solid var(--accent-line); color: var(--accent-ink); }
.sc-assistant { align-self: flex-start; background: var(--panel-2); border: 1px solid var(--line); }
.sc-tool { align-self: flex-start; background: transparent; color: var(--muted); font-family: var(--mono); font-size: 11px; padding: 2px; }
.sc-error { align-self: flex-start; color: var(--err); font-size: 12px; }
.sc-proposal { align-self: stretch; border: 1px solid var(--accent-line); border-radius: 10px; padding: 8px 10px; background: var(--accent-dim); }
.sc-prop-hd { font-size: 12px; color: var(--accent-ink); margin-bottom: 5px; }
.sc-prop-note { white-space: pre-wrap; font-size: 12.5px; margin: 0 0 8px; max-height: 220px; overflow: auto; font-family: var(--mono); }
.sc-proposal.is-applied { opacity: .55; }
.sc-chat-in { display: flex; gap: 8px; padding: 11px 14px; border-top: 1px solid var(--line); }
.sc-chat-in textarea { flex: 1; resize: none; font: inherit; }
.sc-del { color: var(--err); border-color: var(--err); }
.sc-del:hover { background: var(--err); color: var(--panel); }
@media (max-width: 900px) { .tl { --tl-rail: 140px; } }

/* ---- hub ticket page (ADR-0061): a reading column of conversation, a sidebar of state ---- */
.tk { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 18px; align-items: start; }
.tk-main { min-width: 0; }                       /* min-width:0 or a wide code block stretches the grid */
.tk-side { display: flex; flex-direction: column; gap: 12px; }
.tk-side .card { margin: 0; }
.tk-side label { display: block; margin: 10px 0 4px; }
.tk-side label:first-of-type { margin-top: 0; }
.tk-side select, .tk-side input, .tk-side button { width: 100%; }

.tk-crumbs { font-size: 12px; color: var(--muted); margin-bottom: 4px; }
.tk-crumbs a { color: var(--muted); text-decoration: none; }
.tk-crumbs a:hover { color: var(--text); text-decoration: underline; }
.tk-title { margin: 0 0 8px; font-size: 21px; line-height: 1.25; text-wrap: balance; }
.tk-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-bottom: 16px; font-size: 12px; }
.tk-id { font-size: 11px; }
.tk-banner { margin-bottom: 12px; text-align: left; }

/* The timeline. Comments are boxes; events are quiet lines between them, never competing for attention. */
.tk-msg { border: 1px solid var(--border); border-radius: var(--r); background: var(--panel); margin-bottom: 12px; }
.tk-msg-hd { display: flex; align-items: center; gap: 7px; padding: 7px 11px; font-size: 12px;
  background: var(--panel-2); border-bottom: 1px solid var(--border); border-radius: var(--r) var(--r) 0 0; }
.tk-body { padding: 10px 12px; overflow-wrap: anywhere; }
.tk-act { color: var(--muted); text-decoration: none; font-size: 11px; }
.tk-act:hover { color: var(--accent-ink); text-decoration: underline; }
.tk-evt { margin: 0 0 12px; padding-left: 8px; border-left: 2px solid var(--border);
  font-family: var(--mono); font-size: 11px; color: var(--muted); }
/* What a reply answers: one collapsed line, and a link that jumps to it. */
.tk-quote { display: block; margin: 8px 12px 0; padding: 4px 8px; border-left: 2px solid var(--line);
  background: var(--panel-2); color: var(--muted); font-size: 11.5px; text-decoration: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tk-quote:hover { color: var(--text); }
/* The jump has to land somewhere visible. ⚠ Not `:target` — that needs the URL fragment, and on this
   console the hash IS the route, so a quote link can never set one without navigating away. */
.tk-msg.is-found { animation: tk-found 1.6s ease-out; }
@keyframes tk-found { from { border-color: var(--accent); background: var(--accent-dim); } to { border-color: var(--border); background: var(--panel); } }
@media (prefers-reduced-motion: reduce) { .tk-msg.is-found { animation: none; border-color: var(--accent); } }

.tk-compose { padding: 12px; }
.tk-compose textarea { width: 100%; resize: vertical; font: inherit; }
.tk-modes { margin-bottom: 8px; }
.tk-preview { min-height: 96px; padding: 8px 10px; background: var(--panel-2);
  border: 1px solid var(--border); border-radius: var(--r); }
.tk-compose-ft { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 10px; }
.tk-replying { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; padding: 4px 8px;
  background: var(--accent-dim); border: 1px solid var(--accent-line); border-radius: var(--r);
  color: var(--accent-ink); font-size: 12px; }
.tk-replying .iconbtn { padding: 0 4px; font-size: 12px; color: var(--accent-ink); }

/* One column when there is no room for two — the sidebar goes under the conversation, not beside it. */
@media (max-width: 860px) { .tk { grid-template-columns: minmax(0, 1fr); } }

/* ---- agent chat drawer (ADR-0062). Reuses the ops-chat vocabulary above; only what differs is here. ---- */
#chat-panel { width: 620px; }
.chat-sessions { display: flex; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--line); overflow-x: auto; }
.chat-sessions .navitem { flex: 0 0 auto; display: flex; align-items: center; gap: 5px; padding: 4px 8px; font-size: 11px; }
.chat-unread { background: var(--accent-dim); color: var(--accent-ink); border-color: var(--accent-line); }
.sc-chat-log .md { font-size: 13px; }
.sc-chat-log .md pre { max-height: 340px; }
/* Queued, not delivered — dashed because it has not happened yet. */
.chat-pending { border-style: dashed; opacity: .85; }
.chat-queued { margin-top: 4px; font-size: 10.5px; color: var(--accent-ink); font-family: var(--mono); }
.chat-trunc { margin-top: 4px; font-size: 10.5px; color: var(--faint); font-family: var(--mono); }
/* Who sent it — only ever present on a turn the hub DELIVERED, never on a mirrored transcript line. */
.chat-from { font-size: 10.5px; color: var(--accent-ink); font-family: var(--mono); margin-bottom: 3px; }
/* The hidden-activity line. Clickable, and it says the count so a busy session never reads as a quiet one. */
.chat-activity { align-self: center; cursor: pointer; text-decoration: underline dotted var(--line); text-underline-offset: 3px; }
.chat-activity:hover { color: var(--accent-ink); }
a.chat-open { color: inherit; text-decoration: underline dotted var(--line); text-underline-offset: 3px; }
a.chat-open:hover { color: var(--accent-ink); text-decoration-color: var(--accent); }
@media (max-width: 700px) { #chat-panel { width: calc(100vw / var(--ui-zoom)); } }

/* ================================================================================================
   PHONE. Measured before writing any of this: at 390px the shell gave the content column
   118 PIXELS — the fixed sidebar rail took the rest — and the page scrolled sideways at 427px
   against a 390px viewport. Every rule below is aimed at a number from that measurement, not at a
   guess about what "mobile" needs.
   ================================================================================================ */
@media (max-width: 760px) {
  /* ⚠ The rail becomes a ROW, not a drawer. A drawer needs a button, a state, an overlay and a way
     out of it; a horizontal strip of the same buttons needs none of those and keeps every
     destination one tap away — on a console whose whole job is getting to one session quickly. */
  /* ⚠ The same trap as the desktop shell: a bare `1fr` content row can outgrow the viewport (see #app
     above). The phone measures correct today, so this is about not INTRODUCING the regression at an
     intermediate width where the content is taller relative to the screen. */
  #app { grid-template-columns: minmax(0, 1fr); grid-template-areas: "top" "side" "main"; grid-template-rows: auto auto minmax(0, 1fr); }
  /* ⚠⚠ min-width: 0 OR THE SCROLLER DOES NOT SCROLL. A grid item defaults to min-width:auto, so
     #side grew to fit the whole nav row (875px) and stretched the page to 783px instead of clipping
     it — the overflow-x:auto below was inert. The same note already sits on .tk-main; it caught me
     anyway, because there the wide child was a code block and here it is the navigation itself. */
  #app > * { min-width: 0; }

  #side { border-right: 0; border-bottom: 1px solid var(--border); padding: 6px 8px; }
  .nav { flex-direction: row; gap: 4px; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; }
  .nav::-webkit-scrollbar { display: none; }
  /* ⚠ flex: 0 0 auto or the items compress to unreadable slivers instead of scrolling. */
  .nav .navitem { flex: 0 0 auto; padding: 9px 12px; min-height: 40px; }
  /* The active marker is a LEFT BAR in the sidebar, which lands mid-row here — move it under. */
  .navitem.active::before { left: 6px; right: 6px; top: auto; bottom: 2px; transform: none;
    width: auto; height: 2px; }

  #topbar { padding: 0 12px; gap: 8px; }
  /* ⚠ The identity is the first thing to go. It is reassurance, not navigation, and at 390px it was
     pushing "Sign out" off the edge — a control you cannot reach is worse than one you cannot see. */
  #whoami { display: none; }

  #main { padding: 14px 12px; }
  .pagehead { flex-wrap: wrap; gap: 4px 10px; }
  .pagehead h1 { font-size: 19px; }
  .card { padding: 13px 12px; }

  /* ⚠ 140px was two-per-row at 390 and left each field ~170px. One field per row is the honest
     layout on a phone; the desktop rule is untouched. */
  .formrow > div { min-width: 100%; }

  /* ⚠ Tables scroll INSIDE themselves rather than widening the page. A body that scrolls sideways
     makes every vertical swipe feel like it is fighting something. */
  .card > table, .card > div > table { display: block; overflow-x: auto; white-space: nowrap; }

  /* Touch targets. Measured at 25x35 (theme) and 199x39 (nav) — under the 44px anyone can hit. */
  /* Measured at 25x35 (theme), 199x39 (nav) and 38-39px (fields) — all under the 44px anyone can
     reliably hit with a thumb. */
  button, .navitem, input, select, textarea { min-height: 44px; }
  #theme, #logout { min-width: 44px; }
}

/* ⚠ Short viewports, which a phone in landscape and a phone with the keyboard up both are. A 48vh
   transcript on a 400px-tall window is 190px of terminal with the composer pushed off screen. */
/* ⚠ The short-window transcript floor USED TO BE HERE, and it never applied (c3 gap ruling 2, measured in Chromium 2026-09-29):
   `.sc-transcript { min-height: 110px }` had the same specificity as the base `.sc-transcript` rule and sat BEFORE it, so the
   base floor `min(320px, 52vh/zoom)` won at every height and a short window could not show the composer even with no dialog.
   It now sits after the base rule (search "c3 gap ruling 2: the short-window floor"), scaled with the zoom. */


/* ================================================================================================
   SESSION CONTROL — refined in Claude Design ("Yarness Reclaim", session-control-v2).

   The shape it settled on: SETUP LIVES IN THE SIDEBAR under the Sessions tab, and the main column is
   the conversation alone. That deleted the in-page two-pane rail entirely — one less layout, one less
   toggle, one less thing to keep responsive — and removed a real duplication, since the old
   "sessions on the device" chip row listed exactly what the sidebar sub-tabs already list.
   ================================================================================================ */

/* ⚠ The sidebar is wider now because it carries the sessions and their states, not just eight words. */
:root { --rail-w: 320px; }

/* ⚠ RESTORED after the two-pane block was cut: removing that block took the DESKTOP side-hidden rules
   with it, so hiding the sidebar left a 320px empty track where the pane had been — which reads as a
   rendering bug rather than a hidden pane. Caught by the test that pinned it, not by looking. */
#app.side-hidden { grid-template-columns: 0 minmax(0, 1fr); }
#app.side-hidden #side { display: none; }

/* ⚠ A READING WIDTH. A terminal transcript stretched across a 2560px monitor is unreadable, and the
   cap belongs on the column rather than on the pane inside it. */
/* ⚠ A reading-width cap so a transcript is not stretched edge-to-edge on a 2560px monitor — widened
   2026-08-27 (operator ask) now that the body is a clean conversation, not raw terminal columns.
   `overflow-wrap: anywhere` on the pane still breaks the odd unbroken path. Raise or drop the cap to
   go wider still. */
.sc-col { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; width: 100%; max-width: 1600px; }
/* ⚠ #view SITS BETWEEN #main AND THE COLUMN, and that broke the height chain: making #main a flex
   column did nothing because its only child is #view, an ordinary block with auto height — so
   .sc-col's flex:1 resolved against content and the transcript sat at its 220px floor on a 900px
   screen. Measured, not spotted by reading.

   Scoped with :has() rather than styling #view globally: every other tab shares that element, and a
   flex column would quietly change all of them. */
/* ⚠ overflow HIDDEN, not auto (operator ask 2026-08-26: the whole chat must fit the browser window
   like chatgpt/claude — composer pinned at the bottom, the LOG the thing that scrolls). With #main
   scrollable, any content growth handed the PAGE the scrollbar and the composer slid off-screen;
   hiding it here is what forces every scroll to happen inside the transcript and the drawers, which
   both carry their own overflow. Scoped: every other tab keeps its page scroll. */
#main:has(.sc-col) { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
#main:has(.sc-col) > #view { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }

/* ⚠ min-height: 0 — THE ONE LINK THAT BROKE THE WHOLE CHAIN (measured in chromium 2026-08-26: col
   bounded at 547px, card at 2763px inside it). A flex child's default min-height is min-CONTENT, so
   the card refused to shrink below the transcript's full backlog and the composer was clipped by
   #main's overflow:hidden with nothing scrollable anywhere. */
.sc-card { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; margin-bottom: 0; position: relative; }
/* ONE shared vertical center for the whole header (operator ask 2026-08-28): the session title, the
   state + transport pills, and the verb cluster all align on the row's centre. Was `baseline` with the
   pills force-centered on top of it — three different anchors, so the pills read off from the title and
   the buttons. Centering the row makes them one line and drops the per-pill override. */
.sc-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
/* The title keeps a readable floor and ellipsizes; the pills ride as one cluster that drops to a second
   line at narrow widths rather than crushing the title (design refine 2026-08-30). */
.sc-title { min-width: 180px; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-head-pills { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; flex-wrap: wrap; }
/* A working title button: label swapped by JS, spinner drawn here — one visual for close/restart/
   any future long verb. Honest progress beats a fire-and-forget toast. */
.sc-busy { opacity: .75; pointer-events: none; }
.sc-busy::after { content: ""; display: inline-block; width: 10px; height: 10px; margin-left: 7px;
  border: 2px solid var(--accent); border-top-color: transparent; border-radius: 50%;
  vertical-align: -1px; animation: sc-spin .7s linear infinite; }
@keyframes sc-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .sc-busy::after { animation: none; border-top-color: var(--accent); } }
.sc-danger { padding: 3px 9px; font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  white-space: nowrap; color: var(--err); border-color: var(--err); }
/* Session verbs as ONE segmented control (SessionControlV2 sync 2026-08-27): a bordered group with
   hairline dividers on the header's right. sc-verb-lead drops the left divider on whichever verb is
   first-VISIBLE (Restart hides on an ended session) so a hidden button never leaves a floating rule.
   No min-height here on purpose: the mobile 44px touch floor still applies for tappability. */
.sc-verbs { display: flex; align-items: center; align-self: center; flex: 0 0 auto;
  border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; }
.sc-verbs > button { border: 0; border-left: 1px solid var(--border); border-radius: 0;
  padding: 3px 10px; font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  white-space: nowrap; background: transparent; color: var(--muted); box-shadow: none; }
.sc-verbs > button:hover { background: var(--panel-2); color: var(--text); }
.sc-verbs > button.sc-verb-lead { border-left: 0; }
.sc-verbs > .sc-tools { font-size: 15px; line-height: 1; padding: 2px 10px; letter-spacing: 0; text-transform: none; }
/* Close: red text, filled red on HOVER — was a permanently red-outlined button that shouted on every visit. */
.sc-verbs > .sc-danger { color: var(--err); }
.sc-verbs > .sc-danger:hover { background: var(--err); color: var(--panel); }

/* ⚠ flex:1 with a min-height floor, NOT a fixed 48vh. A viewport-height guess wasted a tall screen and
   overflowed a short one; the floor is what stops the pane collapsing to nothing on a small window.
   `overflow-wrap:anywhere` because a pane line can be one unbroken path with no space to break at. */
/* ⚠ flex-basis 0, not auto: with basis auto the pane's CONTENT is the starting size, so a long
   backlog inflated the card before shrink could act. Basis 0 means the transcript takes exactly the
   leftover space — content never sizes anything, it only scrolls. */
.sc-transcript { overflow-wrap: anywhere; overflow-y: scroll; overflow-x: hidden;
  flex: 1 1 0; min-height: min(320px, calc(52vh / var(--ui-zoom))); margin: 0; padding: 12px 14px;
  border: 1px solid var(--border); border-radius: var(--r); background: var(--panel-2);
  font-family: var(--ui); font-size: 13.5px; line-height: 1.5; color: var(--text); }
/* c3 gap ruling 2: the short-window floor, AFTER the base rule so it wins, with or without a dialog: when the window is SHORT
   (ruling 3: 620 CSS px or less of EFFECTIVE height, see `container: yarness-app` below) the transcript keeps at most 110px, so
   the composer fits under it. Above that nothing changes. */
@container yarness-app (max-height: 620px) {
  .sc-transcript { min-height: min(110px, calc(20vh / var(--ui-zoom))); }
}
/* ── the MARKDOWN chat (ADR-0073). The conversation renders from the transcript, so it reads as a
   message thread — prose in the UI face, code/tables in mono. mdToHtml emits only the tags below. */
.sc-md > :first-child { margin-top: 0; }
.sc-md > :last-child { margin-bottom: 0; }
.sc-md .md-p { margin: 0 0 10px; }
.sc-md .md-p.muted { color: var(--dim); }
.sc-md .md-h { margin: 14px 0 6px; font-weight: 700; line-height: 1.3; }
.sc-md h1.md-h { font-size: 1.35em; } .sc-md h2.md-h { font-size: 1.2em; } .sc-md h3.md-h { font-size: 1.08em; }
.sc-md code { font-family: var(--mono); font-size: .9em; background: var(--panel); border: 1px solid var(--border); border-radius: 3px; padding: 0 4px; }
.sc-md .md-code { font-family: var(--mono); font-size: 12px; background: var(--panel); border: 1px solid var(--border); border-radius: var(--r); padding: 9px 11px; margin: 0 0 10px; white-space: pre-wrap; overflow-wrap: anywhere; }
.sc-md .md-code code { background: none; border: 0; padding: 0; white-space: inherit; }
/* Fallback: the raw terminal capture shown when no transcript stream is reaching the console (see
   liveIsScrape / renderCombined). Monospace <pre>, but it WRAPS (`pre-wrap`) rather than scrolling
   sideways — no horizontal scroll anywhere in the transcript (operator ask 2026-08-28). Its internal
   spacing is kept, only over-wide lines fold. */
.sc-md .sc-scrape { margin: 0; font-family: var(--mono); font-size: 12px; line-height: 1.45;
  color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; }
/* A tool call is a machine ACTION, not prose: its own mono row — the tool name tinted the design's
   "deterministic" green, the target muted, led by a ⚙ — so a run of calls scans apart from the agent's
   words (operator ask 2026-08-28). Emitted by mdToHtml when a line is `⚙ \`name\` <what>`. */
/* s2-4: structured messages as typed cards. Colours are the :root tokens; a kind's modifier is a fixed class. */
.sc-md .md-struct { display: block; margin: 4px 0; padding: 6px 10px; border: 1px solid var(--line); border-left: 3px solid var(--border);
  border-radius: 6px; background: var(--panel); color: var(--text); font-style: normal; }
.sc-md .md-struct-head { font-family: var(--mono); font-size: 12px; color: var(--muted); margin-bottom: 4px; display: flex; gap: 6px; align-items: baseline; flex-wrap: wrap; }
.sc-md .md-struct-body { margin: 0; white-space: pre-wrap; word-break: break-word; font-family: var(--mono); font-size: 12px; }
.sc-md .md-struct-meta { font-family: var(--mono); font-size: 11px; color: var(--dim); word-break: break-all; }
.sc-md .md-struct-summary { margin: 2px 0 4px; }
.sc-md .md-struct-id { color: var(--accent-ink); }
.sc-md .md-struct-task-notification { border-left-color: var(--accent); }
.sc-md .md-struct-pasted_content { border-left-color: var(--accent-line); }
.sc-md .md-struct-system-reminder { border-left-color: var(--dim); }
.sc-md .md-struct-system-reminder > summary { cursor: pointer; font-family: var(--mono); font-size: 12px; color: var(--muted); }
.sc-md .md-struct-bash-input, .sc-md .md-struct-bash-stdout { border-left-color: var(--ok); }
.sc-md .md-struct-err { border-left-color: var(--err); }
.sc-md .md-struct-err > .md-struct-head { color: var(--err); }
.sc-md .md-struct-other { border-left-color: var(--border); }
.sc-md .md-pill { font-size: 11px; padding: 0 6px; border-radius: 999px; border: 1px solid currentColor; }
.sc-md .md-pill.ok { color: var(--ok); }
.sc-md .md-pill.err { color: var(--err); }
.sc-md .md-pill.muted { color: var(--muted); }
.sc-md .md-cue { color: var(--muted); margin-right: 6px; }
.sc-md .md-from { margin-left: 6px; font-family: var(--mono); font-size: 11px; font-style: normal; color: var(--accent-ink);
  background: var(--accent-dim); border: 1px solid var(--accent-line); border-radius: 999px; padding: 0 6px; }
.sc-md .md-tool { display: flex; gap: 8px; align-items: baseline; margin: 3px 0; font-family: var(--mono);
  font-size: 12px; line-height: 1.5; }
.sc-md .md-tool-name { flex: 0 0 auto; white-space: nowrap; color: var(--det-ink); font-weight: 600; }
.sc-md .md-tool-name::before { content: "⚙"; margin-right: 6px; opacity: .6; font-weight: 400; }
.sc-md .md-tool-what { color: var(--muted); overflow-wrap: anywhere; }
/* the USER turn — our `> ❯ …` blockquote marker rendered as an accent-edged block, so whose words are whose reads at a glance. */
.sc-md .md-quote { margin: 0 0 10px; padding: 4px 12px; border-left: 3px solid var(--accent); background: var(--accent-dim); color: var(--accent-ink); font-weight: 600; }
.sc-md .md-list { margin: 0 0 10px; padding-left: 22px; }
.sc-md .md-list li { margin: 2px 0; }
.sc-md .md-hr { border: 0; border-top: 1px solid var(--border); margin: 12px 0; }
.sc-md .md-table { border-collapse: collapse; margin: 0 0 10px; width: 100%; table-layout: fixed; }
.sc-md .md-table th, .sc-md .md-table td { border: 1px solid var(--border); padding: 4px 9px; text-align: left; font-size: .95em; overflow-wrap: anywhere; word-break: break-word; vertical-align: top; }
.sc-md .md-table th { background: var(--panel); font-weight: 700; }
.sc-md a { color: var(--accent-ink); text-decoration: underline; }

/* ⚠ ONE row (operator ask 2026-08-27): the session's own status on the LEFT, what your last message is
   doing on the RIGHT. Both single-line so neither ever grows the layout and steals the transcript. */
.sc-statusbar { display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  min-height: 18px; padding: 3px 2px 0; }
.sc-status { font-family: var(--mono); font-size: 12px; color: var(--dim);
  flex: 0 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sc-activity { font-family: var(--mono); font-size: 12px; color: var(--muted);
  flex: 0 0 auto; white-space: nowrap; }
/* Pushes the ⋯ toggle to the right end of the header row. */
.sc-head-gap { flex: 1 1 auto; }
.sc-tools { font-size: 15px; line-height: 1; padding: 2px 9px; }
/* ⚠ OVERLAY, not a flow child (operator ask 2026-08-27): anchored to the card's bottom-right so it
   costs the transcript ZERO height, scrolls internally, and the ⋯ / ✕ toggle its [hidden] attribute. */
.sc-tools-panel { position: absolute; right: 10px; bottom: 10px; z-index: 20;
  width: min(560px, calc(100% - 20px)); max-height: 72%; overflow: auto;
  background: var(--panel); border: 1px solid var(--accent); border-radius: var(--r);
  box-shadow: 0 14px 44px -12px rgba(0,0,0,.55); padding: 12px 14px; }
.sc-tools-panel[hidden] { display: none; }
.sc-tools-hd { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;
  font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: .14em; color: var(--dim); }
/* The mode pill is a READ-ONLY indicator (2026-08-28) — the session's permission mode, beside the state
   and transport pills. Accent-tinted so the mode reads at a glance; not pressable. */
.sc-mode { background: var(--accent-dim); border-color: var(--accent-line);
  color: var(--accent-ink); text-transform: none; letter-spacing: 0; }

/* s2-1: the seat's context-window fill — a thin gauge in the header pills; amber from 70 %, when compaction is due. */
.ctx-bar { position: relative; display: inline-flex; align-items: center; min-width: 76px; height: 20px; padding: 0 8px;
  border: 1px solid var(--accent-line); border-radius: 10px; overflow: hidden; font-size: 11px; }
.ctx-bar .ctx-fill { position: absolute; inset: 0 auto 0 0; background: var(--accent-dim); }
.ctx-bar .ctx-label { position: relative; color: var(--accent-ink); }
.ctx-bar.attention { border-color: var(--warn); }
.ctx-bar.attention .ctx-fill { background: color-mix(in srgb, var(--warn) 30%, transparent); }
.ctx-bar.attention .ctx-label { color: var(--warn); }
.sc-compact { padding: 2px 8px; font-size: 11px; }

/* A dialog (/model, a permission prompt) is the session WAITING ON YOU — so it wears the same amber
   "waiting" language as its sidebar dot (§nav-dot-wait): an amber-framed panel with a soft glow, the
   menu kept mono in a recessed well (its column alignment carries meaning), and the answer keys as
   themed keycaps that light to accent-yellow — the console's action colour — on hover. */
.sc-dialog { margin: 0 0 8px; padding: 10px 12px 12px; background: var(--panel);
  border: 1px solid var(--warn); border-radius: var(--r); box-shadow: 0 0 16px -6px var(--warn); }
.sc-dialog-text { white-space: pre-wrap; margin: 0 0 10px; padding: 9px 11px; font-size: 12.5px;
  line-height: 1.5; color: var(--text); background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--r); max-height: calc(26vh / var(--ui-zoom)); overflow: auto; }
.sc-keys { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
/* The hint is the "waiting on you" cue — its own line above the keys, amber, led by the same pulsing
   amber dot as the sidebar row, so the dialog and its session read as one waiting state. */
.sc-keys-hint { flex: 1 1 100%; display: flex; align-items: center; gap: 7px; margin: 0 0 3px;
  font-family: var(--mono); font-size: 11px; color: var(--warn); }
.sc-keys-hint::before { content: ""; width: 7px; height: 7px; flex: 0 0 auto; border-radius: 1px;
  background: var(--warn); box-shadow: 0 0 6px var(--warn); animation: nav-pulse 1.6s ease-in-out infinite; }
.sc-keys .sc-key { padding: 4px 11px; min-width: 30px; text-align: center; font-size: 12px;
  background: var(--panel-2); border: 1px solid var(--border); color: var(--text); border-radius: var(--r); }
.sc-keys .sc-key:hover { background: var(--accent); border-color: var(--accent); color: #14120d; }

/* Messages sent but not yet taken by the session (operator ask 2026-09-01).
   ⚠ Sits directly above the composer on purpose: it answers "did that send?" in the place the
   operator is already looking, which the Recent drawer — collapsed, inside an overlay — did not. */
.sc-pending { display: flex; flex-direction: column; gap: 4px; margin: 0 0 8px; }
/* c3 gap ruling 2 (C): the strip is ALWAYS bounded and scrolls itself — a debug surface, and unbounded it pushed the composer out. */
.sc-pending { max-height: calc(15vh / var(--ui-zoom)); overflow-y: auto; }
/* c3 rule 1 (operator report 2026-09-29: the `/model` dialog went off-screen on a short window). Measured first
   (`scripts/verify-console-dialog.mjs`, the control at main 613c1c85): at 700px the column stacked 320 (the
   transcript's floor) + 338–424 (the panel) + the pending strip + the composer, overflowed the capped row by up to
   ~560px, and 0 of 16 cases kept the dialog's last key and the composer visible. So:
   - the WHOLE panel is bounded and scrolls itself, its cap scaling with the window (45vh does not fit at 420px);
   - while a dialog is shown (`sc-card-dialog`, set by the view) the transcript's floor drops, since the operator's
     eyes belong to the dialog; the dialog's TEXT scrolls first inside the panel, so the keys stay in reach;
   - under the mark the pending strip (capped for every card, gap ruling 2) yields first.
   A PEEK (an unknown pane's tail in the same box) is bounded too, but carries no mark: it never shrinks the
   transcript (rule 2). */
/* The panel and the strip are SHRINKABLE flex items that scroll themselves: the card's height is fixed by the capped row, so
   the flex column — not a guessed vh sum — decides what each gets (T-0: at 700px / zoom 1.25 the vh caps alone summed to
   734px against a 528px card). The strip yields first (shrink 4); the panel keeps room for one whole row of keys (52px). */
.sc-dialog { flex: 0 1 auto; min-height: 0; max-height: calc(45vh / var(--ui-zoom)); overflow-y: auto; }
.sc-card-dialog .sc-dialog { min-height: 52px; }
.sc-card-dialog .sc-transcript { min-height: min(110px, calc(18vh / var(--ui-zoom))); }
.sc-card-dialog .sc-dialog-text { max-height: calc(14vh / var(--ui-zoom)); }
.sc-card-dialog .sc-pending { flex: 0 4 auto; min-height: 0; }
/* Gap ruling 1 (plan 51fd53fa): on short windows the fixed parts of the card (header, status bar, one key row, composer) nearly
   fill it — 223 CSS px against 206 at 420px / zoom 1.25 — so under the mark the transcript's floor drops to 0 at 620px and
   below; the keys are the hard requirement, the composer yields only where the arithmetic forbids it. The mark clears when the
   dialog does, and every floor comes back with it. */
@container yarness-app (max-height: 620px) {
  .sc-card-dialog .sc-transcript { min-height: 0; }
}
/* The TINY windows (460 CSS px or less of effective height: a phone in landscape, a zoomed short window). Gap ruling 3 part B:
   with or without a dialog, the status bar is hidden and the card header keeps to ONE line (the title ellipsizes; the pills and
   verbs no longer wrap below it). Under the mark the transcript also collapses to nothing (ruling 1, part A). */
@container yarness-app (max-height: 460px) {
  .sc-card-dialog .sc-transcript { padding-top: 0; padding-bottom: 0; border-width: 0; }
  .sc-statusbar { display: none; }
  .sc-head, .sc-head-pills { flex-wrap: nowrap; }
  .sc-head { overflow: hidden; }
  .sc-title { min-width: 0; }
}
.sc-pending-row { display: flex; align-items: baseline; gap: 8px; font-size: 12px; min-width: 0; }
/* The message itself may be long; it wraps rather than pushing the state pill off the row. */
/* The MESSAGE keeps a floor and yields the rest: it is the thing the operator sent and must stay
   readable, so it never shrinks below a few characters however long the reason beside it runs. */
.sc-pending-text { flex: 0 1 auto; min-width: 10ch; max-width: 50%; overflow-wrap: anywhere; color: var(--text); }
/* ⚠ `min-width: 0` + `overflow-wrap: anywhere` are LOAD-BEARING. A flex item's default `min-width: auto`
   will not shrink below its content's minimum, and a reason that quotes a pane can carry a 200-char
   unbreakable run of `─` — which blew the row out sideways, crushed the message to one letter per line,
   and pushed the Clear button clean off-screen (operator: "a failed msg which can not be cleared",
   2026-09-02). Capped to a few lines with the full text on hover, so a long quote informs, not floods. */
.sc-pending-why {
  flex: 1 1 0; min-width: 0; overflow-wrap: anywhere; font-size: 11px;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.sc-pending-row > .sc-pending-clear { flex: 0 0 auto; }

.sc-compose { display: flex; gap: 8px; align-items: stretch; }
.sc-text { flex: 1 1 200px; min-width: 0; font-family: inherit; resize: none; }
/* The send column holds only the Send button now (the mode moved to the top bar, read-only) — one
   full-height button beside the composer. */
.sc-send { display: flex; flex-direction: column; align-items: stretch; gap: 4px; }
.sc-send .sc-send-btn { min-width: 96px; flex: 1 1 auto; }
/* The picker sits UNDER Send, small: pasting is the path this feature is for and must stay the obvious
   one, while the button is the fallback for a file already on disk. */
.sc-send .sc-attach-btn { flex: 0 0 auto; font-size: 11px; padding: 0; }
/* The always-on Esc sits under Send, small like Picture: it is a secondary affordance, not the send path. */
.sc-send .sc-esc { flex: 0 0 auto; font-size: 11px; padding: 0; }

/* ADR-0079: the pictures waiting to go, BELOW the box.
   ⚠ The token rides beside each thumbnail because the token is what the operator's own sentence refers
   to — a strip of anonymous thumbnails leaves them guessing which [Img..] was which. Wraps rather than
   scrolls: eight is the ceiling, and a second row is easier to read than a horizontal scroller. */
.sc-attach { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 0; }
.sc-attach-item {
  display: flex; align-items: center; gap: 6px; padding: 3px 6px 3px 3px;
  border: 1px solid var(--line, #ddd); border-radius: 6px; background: var(--panel, transparent);
  font-size: 11px; max-width: 100%; min-width: 0;
}
/* A fixed box with `object-fit: cover`, so a panorama and a portrait screenshot make the same-shaped
   chip and the strip does not reflow as pictures arrive. */
.sc-attach-thumb { width: 34px; height: 34px; object-fit: cover; border-radius: 4px; display: block; flex: 0 0 auto; }
.sc-attach-tok { flex: 0 0 auto; }
.sc-attach-size { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-attach-x { flex: 0 0 auto; padding: 0 2px; }
.sc-note { margin: 8px 0 0; font-size: 12px; }

/* Consulted when something looks wrong, not while working — but the relay's failures are quiet by
   nature, so they stay on the page rather than behind a route. */
.sc-drawers { display: flex; gap: 16px; margin-top: 10px; flex-wrap: wrap; flex: 0 0 auto; }
.sc-drawer { flex: 1 1 320px; }
/* ⚠ AN OPEN DRAWER SCROLLS ITSELF, BOUNDED. A long Recent history inside the card squeezed the
   transcript down to its min-height floor and handed the PAGE the scrollbar — the terminal stopped
   filling the window and the thing that scrolled was everything except the history (found live).
   The drawer is the consult-when-wrong region; it gets a bounded window, and the transcript keeps
   the room the flex column gives it. */
.sc-drawer > div { overflow-y: auto; max-height: calc(32vh / var(--ui-zoom)); }
.sc-drawer > summary { cursor: pointer; font-family: var(--mono); font-size: 10px;
  text-transform: uppercase; letter-spacing: .12em; color: var(--dim); padding: 4px 0; }

/* ⚠ Modals, because creating and closing a session are decisions — one with three fields and a
   warning, the other irreversible for a warm session. Both were previously competing for space with
   the conversation on every single visit. */
/* c3 rule 3: the overlay SCROLLS ITSELF and the card is centred with `margin: auto`, not `align-items: center`. A flex item
   centred that way and taller than its container overflows BOTH ends, and the top half sits above the scroll origin where no
   scroll can reach it; auto margins centre a card that fits and start an oversized one at the top. (Measured: today's card is
   capped at 88vh and never outgrows the overlay; G4 lifts the cap to prove the guard.) */
.sc-modal { position: fixed; inset: 0; z-index: 60; background: rgba(20, 18, 13, .45);
  display: flex; justify-content: center; padding: 16px; overflow-y: auto; }
.sc-modal-card { width: 420px; max-width: calc(92vw / var(--ui-zoom)); margin: auto; max-height: calc(88vh / var(--ui-zoom)); overflow-y: auto; }
.sc-modal-danger { border-color: var(--err); }
.sc-confirm-q { margin: 0; font-size: 13.5px; }
.sc-hint { margin: 8px 0 0; font-size: 11px; }
.sc-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; }

/* ⚠ "＋ New session" opens the modal and sits with the sessions it creates, at the top of the sub-list
   — the one place an operator is already looking. It reads as an empty SLOT, not a competing control
   (SessionControlV2 sync 2026-08-27): dashed + transparent, quiet until hover lights it with the accent. */
.nav-sub .sc-new-btn { margin: 2px 4px 8px; font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; background: transparent; border: 1px dashed var(--border); color: var(--muted); }
.nav-sub .sc-new-btn:hover { background: var(--accent-dim); border-color: var(--accent); color: var(--accent-ink); }
/* Session rows lead with a state DOT + glow (SessionControlV2 sync): working = green, idle = faint,
   "waiting on you" (prompting/trust) = amber + pulse with an amber-tinted word. The word alone read the
   same at a glance; the dot is what a scan catches. */
/* The session list reads as NESTED under the Sessions tab (SessionControlV2 sync): indented, with a
   faint left rail. The active row's accent bar sits ON that rail instead of floating 10px out. */
.nav-sub { margin-left: 14px; padding-left: 8px; border-left: 1px solid var(--border-2); }
.nav-sub .navitem.active::before { left: -9px; width: 2px; height: 14px; }
.nav-sub .navitem { display: flex; align-items: center; gap: 8px; }
.nav-name { flex: 1; min-width: 0; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The Sessions tab shows a live count of its sessions (SessionControlV2 sync) — pushed to the right
   edge, quiet mono so it reads as a tally, not a second label. */
.navitem.nav-parent { display: flex; align-items: center; gap: 6px; }
.nav-parent .nav-label { flex: 1; text-align: left; }
.nav-count { flex: 0 0 auto; font-family: var(--mono); font-size: 10px; letter-spacing: 0;
  text-transform: none; color: var(--dim); }
.nav-dot { width: 7px; height: 7px; flex: 0 0 auto; border-radius: 1px; background: var(--faint); }
.nav-dot-ok { background: var(--ok); box-shadow: 0 0 6px var(--ok); }
.nav-dot-wait { background: var(--warn); box-shadow: 0 0 6px var(--warn); animation: nav-pulse 1.6s ease-in-out infinite; }
.nav-state { flex: 0 0 auto; font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--dim); }
.nav-state-wait { color: var(--warn); }
.nav-shared { color: var(--err); margin-left: 4px; } /* c8: another OpenCode client works in this seat's folder */
/* ADR-0082: ORG › PROJECT wrap the agent groups, one level each. They are the outermost context and so
   read the QUIETEST thing on the rail — the org in small caps, the project under it — because a scan is
   still meant to land on a session row, not on a heading. Drawn only when the level distinguishes
   something (see the paint), so most viewers never see them. */
.nav-org { display: flex; align-items: baseline; gap: 6px; padding: 10px 8px 2px; cursor: pointer; }
.nav-org:first-of-type { padding-top: 2px; }
.nav-oname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .12em; color: var(--muted); }
.nav-ocount { flex: 0 0 auto; font-family: var(--mono); font-size: 9px; color: var(--faint); }
.nav-org-wait .nav-oname { color: var(--warn); }
.nav-proj { display: flex; align-items: baseline; gap: 6px; padding: 3px 8px 2px 10px; cursor: pointer;
  border-left: 1px solid var(--border-2); margin-left: 2px; }
.nav-pname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 10px; letter-spacing: .04em; color: var(--muted); }
.nav-pcount { flex: 0 0 auto; font-family: var(--mono); font-size: 9px; color: var(--faint); }
.nav-proj-wait .nav-pname { color: var(--warn); }

/* Sessions are GROUPED BY AGENT (operator ask 2026-08-30): every reachable agent is a header — presence
   dot + label + engine badge + tally — and its sessions are the rows beneath it. The header reads quieter
   than the rows it owns, so a scan lands on the sessions; the group is context, not a target. Its "＋"
   starts a session on THAT agent (shown only when the device offered roots). */
.nav-group { display: flex; align-items: center; gap: 7px; padding: 8px 8px 3px; margin-top: 2px; }
.nav-group:first-of-type { margin-top: 0; }
.nav-gdot { width: 6px; height: 6px; flex: 0 0 auto; border-radius: 50%; background: var(--faint); }
.nav-gdot-on { background: var(--ok); box-shadow: 0 0 6px var(--ok); }
.nav-gname { flex: 1; min-width: 0; text-align: left; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-size: 11px; font-weight: 600; letter-spacing: .04em; color: var(--text); }
.nav-engine { flex: 0 0 auto; font-family: var(--mono); font-size: 9px; text-transform: uppercase;
  letter-spacing: .04em; color: var(--dim); border: 1px solid var(--border-2); border-radius: var(--r); padding: 0 4px; }
/* ADR-0075: a messaging-capable agent (a coordinator/"PM"). Accent-coloured so it stands apart from the
   dim engine pill — this agent may relay text into OTHER agents' sessions. */
.nav-msg { flex: 0 0 auto; font-family: var(--mono); font-size: 9px; text-transform: uppercase;
  letter-spacing: .04em; color: var(--accent-ink); border: 1px solid var(--accent-ink); border-radius: var(--r);
  padding: 0 4px; cursor: default; }
.nav-gcount { flex: 0 0 auto; font-family: var(--mono); font-size: 10px; color: var(--dim); }
.nav-gnew { flex: 0 0 auto; background: transparent; border: 0; color: var(--muted); cursor: pointer;
  font-size: 13px; line-height: 1; padding: 0 2px; min-height: 0; }
.nav-gnew:hover { color: var(--accent-ink); }
/* Each agent's sessions sit in their OWN indented block under a second-level rail, so tab > agent >
   session reads as three nested steps, not one flat list (design refine 2026-08-30). The block's bottom
   margin is the air between groups. */
.nav-gitems { display: flex; flex-direction: column; margin: 0 0 6px 10px; padding-left: 8px; border-left: 1px solid var(--border-2); }
/* The active row's accent bar sits ON the second-level rail rather than floating out past it. */
.nav-gitems .navitem.active::before { left: -9px; }
/* ADR-0074: a NODE sub-header appears ONLY when an agent spans more than one machine — agent → node →
   session. Quieter and smaller than the agent header; its sessions indent under it via .nav-gitems. */
.nav-node { display: flex; align-items: center; gap: 6px; padding: 3px 8px 2px 16px; }
.nav-ndot { width: 5px; height: 5px; flex: 0 0 auto; border-radius: 50%; background: var(--faint); }
.nav-ndot-on { background: var(--ok); box-shadow: 0 0 5px var(--ok); }
.nav-nname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 10px; letter-spacing: .02em; color: var(--dim); }
.nav-nram { flex: 0 0 auto; font-family: var(--mono); font-size: 9px; color: var(--dim); letter-spacing: 0; }
/* The Claude account's weekly limit on a node (operator ask 2026-09-02): the sidebar's compact mark beside RAM,
   and the header pill. Quiet until it matters — amber from 80%, red from 95% — so a healthy fleet stays grey. */
.nav-nusage { flex: 0 0 auto; font-family: var(--mono); font-size: 9px; color: var(--dim); letter-spacing: 0; }
/* ONE row per machine above the agent groups (operator feedback 2026-09-02): the machine's name, then its weekly
   and 5-hour marks. Reads like a node sub-header, set off from the groups below by a hairline. */
.nav-usage-row { display: flex; align-items: center; gap: 8px; padding: 4px 8px 5px 10px; margin: 2px 0 3px; border-bottom: 1px solid var(--line); }
.nav-usage-row .nav-nname { font-size: 10px; color: var(--muted); }
.usage-pill { font-family: var(--mono); }
.nav-nusage.usage-hi, .usage-pill.usage-hi { color: var(--warn); border-color: var(--warn); }
.nav-nusage.usage-crit, .usage-pill.usage-crit { color: var(--err); border-color: var(--err); font-weight: 600; }
/* ADR-0074 device(node): a node that is ALSO a registered device wears its duties as a small tag, so the
   machine reads as one thing — its name plus what yarness has it do (capture/parse/serve). */
.nav-nduty { flex: 0 0 auto; max-width: 45%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 8.5px; letter-spacing: .02em; color: var(--dim); padding: 0 5px; border: 1px solid var(--border-2);
  border-radius: var(--r); }
.nav-ncount { flex: 0 0 auto; font-family: var(--mono); font-size: 9px; color: var(--dim); }
/* No-device gates: a bare identity with no node running as it. Muted, at the bottom, so it is visible
   without being mistaken for something you can start a session on. */
@keyframes nav-pulse { 50% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) { .nav-dot-wait, .sc-keys-hint::before { animation: none; } }

@media (max-width: 760px) {
  /* The sidebar is a horizontal strip on a phone, so its width token must not apply. */
  :root { --rail-w: 220px; }
  /* ⚠ flex:0 0 auto once STACKED. The desktop rule is flex:1 1 320px so two drawers share a row;
     in a column that same rule makes them GROW to fill the height, which flung "Recent" to the bottom
     of a very tall page with 600px of nothing above it. */
  .sc-drawers { flex-direction: column; gap: 8px; }
  .sc-drawer { flex: 0 0 auto; }
}

/* ================================================================================================
   PHONE — the drawer, from the mobile design ("session-control-mobile").

   ⚠ This REPLACES the horizontal nav strip. A strip kept every destination one tap away but had
   nowhere to put the sessions and their states, which is now most of what the sidebar carries — the
   strip would have scrolled a dozen items sideways past the thing you were looking for.
   ================================================================================================ */
#side-scrim { display: none; }

@media (max-width: 760px) {
  /* The sidebar leaves the grid entirely and becomes an overlay. */
  #app { grid-template-columns: minmax(0, 1fr); grid-template-areas: "top" "main"; grid-template-rows: 54px minmax(0, 1fr); }
  #app.side-hidden { grid-template-columns: minmax(0, 1fr); }

  #side {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 71;
    width: 290px; max-width: calc(85vw / var(--ui-zoom));
    border-right: 1px solid var(--accent);
    box-shadow: 18px 0 50px -20px rgba(0, 0, 0, .55);
    overflow-y: auto;
    /* ⚠ transform, not `display`, so it can animate and so its scroll position survives a close. */
    transform: translateX(0);
    transition: transform .18s ease;
  }
  /* `display:none` on the desktop rule would defeat the transform; override it here. */
  #app.side-hidden #side { display: block; transform: translateX(-100%); }

  #side-scrim { display: block; position: fixed; inset: 0; z-index: 70; background: rgba(20, 18, 13, .45); }
  #app.side-hidden #side-scrim { display: none; }

  /* The nav is a column again inside the drawer — the strip layout was the thing being replaced. */
  .nav { flex-direction: row; }
  #side .nav { flex-direction: column; overflow-x: visible; }
  #side .nav .navitem { flex: 0 0 auto; }
  .navitem.active::before { left: -10px; right: auto; top: 50%; bottom: auto;
    transform: translateY(-50%); width: 3px; height: 16px; }
  .nav-sub .navitem.active::before { left: -7px; width: 2px; height: 14px; }

  /* ⚠ ONE ROW for the ticking line and both pills. The stacked pill column beside Send was wider than
     390px allows, and it pushed the composer into a sliver. */
  .sc-compose { flex-wrap: wrap; }
  /* ⚠ Side by side on a phone, not stacked: two full-width buttons in a column would push the transcript
     up by a whole row for a control that is used once in a while. */
  .sc-send { flex: 1 1 100%; flex-direction: row; align-items: center; }
  .sc-send .sc-send-btn { flex: 1 1 auto; }
  .sc-send .sc-attach-btn { flex: 0 0 auto; padding: 0 8px; }
  .sc-send .sc-send-btn { flex: 1; min-width: 0; }
  .sc-text { flex: 1 1 100%; }
  .sc-transcript { min-height: 180px; font-size: 12px; }
  .sc-keys .sc-key { flex: 1; font-size: 13px; }
  .sc-keys-hint { flex: 1 1 100%; }
  /* No page heading on a phone: the card header already names the session, and the strap line cost
     three lines of a screen that has few. */
  .pagehead { display: none; }
}

/* ⚠ Staleness stays VISIBLE rather than inside a drawer. "reported 4h ago" is how an operator finds
   out the worker died rather than that the session is quiet, and it is the one fact that explains an
   unresponsive console. */
.sc-age { font-size: 11px; margin-top: 8px; font-family: var(--mono); }

/* ⚠ The standing warnings are worth having and not worth six lines of every visit. Collapsed, but
   present — they explain that this types into a LIVE shell and that the transcript is not history,
   which are the two things a newcomer gets wrong. */
.sc-note > summary { cursor: pointer; font-family: var(--mono); font-size: 10px;
  text-transform: uppercase; letter-spacing: .12em; color: var(--dim); padding: 6px 0 0; }
.sc-note-body { margin: 6px 0 0; font-size: 12px; }

/* ---- model / effort badge (operator ask 2026-09-02) --------------------------------------------
   ⚠ The QUIETEST thing on a session row: it sits on every one, so it whispers. No fill and a faint
   hairline, on-palette (the first version used an off-language cold-grey `--chip` that does not
   exist, so it fell back to a blue-grey chip on a warm UI). The effort reads dimmer still. */
.model-pill {
  padding: 1px 7px; border-radius: 20px;
  background: transparent; border: 1px solid var(--border-2);
  color: var(--dim); font-family: var(--mono); font-weight: 500; font-size: 10px;
  letter-spacing: .01em; vertical-align: middle;
}
.navitem .model-pill, .chat-sessions .model-pill { margin-left: 6px; }
/* On an ACTIVE (accent) row the hairline would fight the fill — lift it to an accent tint instead. */
.navitem.active .model-pill { border-color: var(--accent-line); color: var(--accent-ink); }

/* The "clear" on a settled pending row: a dismiss must not shout, so it is muted-to-text on hover,
   not the accent `.linkbtn` yellow. Pushed to the row's end. */
.sc-pending-clear {
  margin-left: auto; color: var(--dim); font-family: var(--mono); font-size: 10px;
  text-transform: uppercase; letter-spacing: .1em;
}
.sc-pending-clear:hover { color: var(--text); text-decoration: none; }

/* ---- debug log panel (operator ask 2026-09-02) -------------------------------------------------
   ⚠ Fixed and on top of everything: it must be usable while the view underneath is the broken thing.
   Constrained height with its own scroll so it never pushes page content around. On-palette and in the
   Reclaim language — sharp `--r` radius, warm panel/border, the soft drop shadow the toasts use. */
.dbg-panel {
  position: fixed; right: 14px; bottom: 14px; z-index: 9999;
  width: min(720px, calc(100vw / var(--ui-zoom) - 28px)); max-height: min(calc(46vh / var(--ui-zoom)), 420px);
  display: flex; flex-direction: column;
  background: var(--panel); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r);
  box-shadow: 0 12px 40px -10px rgba(0, 0, 0, .5);
  font-size: 11.5px; overflow: hidden;
}
.dbg-hd {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  background: var(--panel-2); border-bottom: 1px solid var(--border);
  font-family: var(--ui); font-weight: 600;
}
.dbg-hd b { font-size: 11px; text-transform: uppercase; letter-spacing: .12em; color: var(--muted); }
.dbg-hd .muted { color: var(--dim); font-family: var(--mono); font-size: 10px; }
.dbg-hd .spacer { flex: 1; }
.dbg-body { overflow: auto; padding: 6px 10px 10px; font-family: var(--mono); line-height: 1.7; }
.dbg-row { display: grid; grid-template-columns: 58px 46px minmax(0, auto) minmax(0, 1fr); gap: 10px; padding: 1px 0; }
.dbg-t { color: var(--dim); }
.dbg-c { color: var(--faint); text-transform: uppercase; font-size: 9.5px; letter-spacing: .06em; align-self: center; }
.dbg-e { white-space: nowrap; color: var(--text); }
.dbg-d { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ⚠ The render channel is the one worth spotting at a glance — it is the branch that decides what the
   operator is looking at, and the reason this panel exists at all. Accent, not a cold hex. */
.dbg-render .dbg-e { color: var(--accent-ink); }
.dbg-net .dbg-e { color: var(--muted); }
.dbg-relay .dbg-e { color: var(--det-ink); }

/* A safeguard fallback on the newest turn: shown INSIDE the model badge as an arrow to what answered,
   so the configured model stays the headline and the fallback is visibly a per-turn detail. */
.model-pill-fb { border-color: var(--warn); }
.model-fb { margin-left: 5px; color: var(--warn); }

/* ADR-0078 v2 shell: the tab strip above the column, the unread dot, the sidebar filter. */
.sc-tabs { display: flex; align-items: center; gap: 4px; overflow-x: auto; padding: 0 0 6px; margin: 0 0 6px; border-bottom: 1px solid var(--border-2); scrollbar-width: thin; }
.sc-tab { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; max-width: 220px; padding: 4px 8px; border: 1px solid var(--border-2);
  border-radius: 6px; background: transparent; color: var(--dim); font-size: 12px; cursor: pointer; }
.sc-tab.active { color: var(--fg); border-color: var(--border); background: var(--bg-2, rgba(127,127,127,.08)); }
.sc-tab-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-tab-dot { width: 7px; height: 7px; border-radius: 1px; background: var(--faint); flex: 0 0 auto; }
.sc-tab-working .sc-tab-dot { background: var(--ok); box-shadow: 0 0 6px var(--ok); }
.sc-tab-wait .sc-tab-dot { background: var(--warn); box-shadow: 0 0 6px var(--warn); animation: nav-pulse 1.6s ease-in-out infinite; }
.sc-tab-unread .sc-tab-dot { background: var(--accent, #3b82f6); }
.sc-tab-gone { text-decoration: line-through; opacity: .6; }
.sc-tab-x { opacity: .5; padding: 0 2px; } .sc-tab-x:hover { opacity: 1; color: var(--err); }
.sc-tab-bell { margin-left: auto; flex: 0 0 auto; border: 0; background: transparent; cursor: pointer; opacity: .6; font-size: 13px; } .sc-tab-bell.on { opacity: 1; }
.sc-tab-split { flex: 0 0 auto; border: 1px solid var(--border-2); border-radius: 6px; background: transparent; color: var(--dim); cursor: pointer; padding: 2px 7px; font-size: 13px; }
.sc-tab-split.on { color: var(--fg); border-color: var(--border); }
/* Two columns side by side on a wide screen (ADR-0078 §8, split view): the tab strip spans both. */
/* ⚠ minmax(0, 1fr) on BOTH — the vertical row trap's horizontal twin, and it was live: the two-up layout
   reported 337px of page overflow on production (found by `verify-console-height.mjs` on its first run).
   A bare `1fr` column's automatic minimum is min-content, so a wide card can push the grid past the
   viewport with no scrollable child to absorb it. */
.sc-col-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; max-width: none; }
.sc-col-split > .sc-tabs { grid-column: 1 / -1; }
.sc-card-split { min-width: 0; }
.nav-dot-unread { background: var(--accent, #3b82f6); }
.nav-group-wait .nav-gname { color: var(--warn); }
.nav-filter { width: 100%; box-sizing: border-box; margin: 4px 0 6px; padding: 3px 6px; font-size: 11px; border: 1px solid var(--border-2); border-radius: 4px; background: transparent; color: var(--fg); }

/* ADR-0078 v2 column: one node per transcript BLOCK (rendered once, reconciled by key). */
.sc-block { margin: 0 0 10px; }
.sc-block-pending { opacity: 0.55; }
.sc-block-failed { opacity: 0.85; border-left: 3px solid var(--err, #c0392b); padding-left: 8px; }
.sc-block-retry { display: block; margin-top: 4px; font-size: 12px; }
.sc-block-marker { text-align: center; color: var(--muted, #888); font-size: 12px; margin: 14px 0; }
/* A message from ANOTHER session (ADR-0075 sibling; see peerMessage.ts). ⚠ Must NOT look like the
   operator's own prompt — rendered as a `user` block it carried their `❯` marker and read as something
   they had said. The rule is the same one the relay marker follows: whoever spoke is never guessed from
   context. The sender's name is the block's first line, emitted bold by the renderer. */
.sc-newbelow { position: sticky; bottom: 8px; margin: -34px auto 0; display: block; width: max-content;
  padding: 4px 12px; border-radius: 999px; border: 1px solid var(--ok, #2e7d32); background: var(--bg, #fff);
  color: var(--ok, #2e7d32); font-size: 12px; cursor: pointer; z-index: 2; }

/* v2 scrape block (ADR-0078 §3): the pane screenshot that stands in while a session has no markdown
   blocks — same mono, wrapping treatment as v1's fallback so the two builds read alike. */
.sc-block-scrape .sc-scrape { margin: 0; font-family: var(--mono); font-size: 12px; line-height: 1.45;
  color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; }
/* §4 / finding B: WHY structured history is unavailable (an unbound OpenCode seat, a file not on disk) — a
   quiet note above the terminal fallback, so it never looks like rich history that is still loading. */
.sc-block-scrape .sc-scrape-unavailable { font-size: 12px; line-height: 1.4; color: var(--dim);
  margin: 0 0 8px; padding: 6px 8px; border-left: 2px solid var(--dim); overflow-wrap: anywhere; }

/* ADR-0078 §12 turn metrics: a turn's cost as a quiet mono row after its final answer, and the column's
   running totals as a chip beside the session's own status (the activity line keeps the right edge). */
.sc-block-turn { font-family: var(--mono); font-size: 11.5px; color: var(--dim); margin: 2px 0 14px; padding-left: 2px;
  overflow-wrap: anywhere; }
.sc-metrics { font-family: var(--mono); font-size: 12px; color: var(--dim); white-space: nowrap; }
.sc-statusbar .sc-activity { margin-left: auto; }

/* ADR-0077 §8 (S2): a node's home hub in the sidebar, and the Devices tab's home panel. */
.nav-nhome { font-size: 10px; color: var(--dim); margin-left: 6px; white-space: nowrap; }
.sc-home-row { display: flex; flex-wrap: wrap; gap: 14px; align-items: baseline; margin: 6px 0 10px; }
.sc-home-row select { font-family: var(--mono); font-size: 12px; }
.sc-probe { font-size: 12px; margin-top: 4px; }
.sc-probe th { font-weight: 500; color: var(--muted); text-align: left; padding-right: 12px; }
.sc-probe td { padding: 2px 12px 2px 0; }

/* ADR-0080 — the "make a worktree first" fold in the New Session dialog. Collapsed by default because
   isolation is opt-in: most sessions read or plan and want the project directory itself, so the common
   path must cost nothing. Indented when open so the extra fields read as belonging to the summary
   rather than as three more top-level questions. */
.sc-wt { margin: 6px 0 2px; }
.sc-wt > summary { cursor: pointer; font-size: 12px; color: var(--muted, #888); user-select: none; }
.sc-wt > summary:hover { color: var(--fg, inherit); }
.sc-wt[open] > summary { margin-bottom: 6px; }
.sc-wt[open] { padding-left: 10px; border-left: 2px solid var(--line, #ddd); }

/* ADR-0081 — Run buttons under a one-line shell block, and the "run in…" chooser. Quiet by default (a
   link-styled pair under the code), because most blocks are read, not run. The chooser is the confirm:
   the exact line and the exact target sit on screen before anything is sent. */
.md-run, .md-send { display: flex; gap: 10px; margin: -4px 0 8px; font-size: 12px; }
.md-run .linkbtn, .md-send .linkbtn { padding: 0; }
.sc-runin { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 6px 0 2px; padding: 8px 10px;
  border: 1px solid var(--line, #ddd); border-radius: 6px; background: var(--panel, transparent); font-size: 12px; }
.sc-runin-cmd { flex: 1 1 100%; white-space: pre-wrap; word-break: break-all; }
.sc-runin select { max-width: 100%; }
.sc-runin label { display: inline-flex; align-items: center; gap: 4px; }
/* ADR-0081 §2b — the "send to…" chooser under every OTHER fenced block. The preview IS the payload, note
   and reply-back request included, scrolling past a dozen lines rather than pushing the buttons off screen. */
.sc-sendto-text { max-height: 14em; overflow: auto; margin: 0; }
.sc-sendto-note { flex: 1 1 220px; min-width: 160px; font-size: 12px; }
/* Inline `! cmd` spans get the same two buttons, compact, riding after the code rather than under it. */
.md-cmd { font-weight: 600; }
.md-run-inline { display: inline-flex; gap: 6px; margin: 0 0 0 6px; vertical-align: baseline; }
.md-run-inline .linkbtn { padding: 0; font-size: 11px; }

/* The "you are running an older console" nudge. Deliberately unmissable and deliberately dismissible by acting
   on it: it appears only when the origin is serving something this tab did not boot with. */
#console-stale {
  position: fixed; z-index: 9999; left: 50%; transform: translateX(-50%); top: 8px;
  padding: 6px 14px; border-radius: 999px; border: 1px solid var(--warn, #b8860b);
  background: var(--panel, #1b1b1b); color: var(--fg, #eee); font: inherit; font-size: 12px; cursor: pointer;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .35);
}
#console-stale:hover { border-color: var(--fg, #eee); }

/* c1 rule 1: a copy button on every code block — top-right, shown on hover, ALWAYS shown on touch widths. It is never part of what a
   selection or the copy picks up (user-select: none; it sits outside the <code>). */
pre:has(> .code-copy) { position: relative; }
.code-copy {
  position: absolute; top: 4px; right: 4px; padding: 1px 7px; border-radius: 4px; border: 1px solid var(--border);
  background: var(--panel); color: var(--muted); font-family: var(--mono); font-size: 11px; line-height: 1.4; cursor: pointer;
  opacity: 0; transition: opacity .12s; user-select: none; -webkit-user-select: none;
}
pre:hover > .code-copy, .code-copy:focus-visible { opacity: 1; }
.code-copy:hover { color: var(--text); }
@media (hover: none), (max-width: 720px) { .code-copy { opacity: 1; } }
/* c1 rule 2: the copy-on-select switch reads dimmed when off. */
#copy-select.off { opacity: .45; }
/* c1 rule 3: the ghost's dismiss — a quiet link beside its retry offer. */
.sc-block-dismiss { margin-left: 10px; color: var(--muted); font-size: 11px; }

/* c12: a turn's natural phrasing — a hover/focus card on the user block (Sessions) and the instruction (Weaver). */
.sc-block-user, .phr-host { position: relative; }
.phr-card { display: none; position: absolute; left: 0; top: 100%; z-index: 20; max-width: 480px; padding: 6px 8px; background: var(--raised); border: 1px solid var(--border); border-radius: 6px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18); white-space: normal; font-weight: normal; }
.sc-block-user:hover > .phr-card, .sc-block-user:focus-within > .phr-card, .phr-host:hover > .phr-card, .phr-host:focus-within > .phr-card { display: block; }
/* c13: a phrased null — the prompt was already natural; muted so it reads apart from a rewrite. */
.phr-card.phr-muted { color: var(--muted); font-style: italic; }

/* c24: the Logs page (ADR-0094). Severity pills; error and critical take the error style. */
.log-filters { display: flex; gap: 8px; margin: 8px 0 12px; }
.log-row { padding: 6px 8px; border-bottom: 1px solid var(--border); cursor: pointer; display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.log-row:hover { background: var(--panel-2); }
.log-title { flex: 1 1 240px; }
.pill.sev-debug { opacity: .7; }
.pill.sev-warn { color: #b7791f; border-color: #b7791f; }
.pill.err { color: var(--err, #c53030); border-color: var(--err, #c53030); }
.pill.sev-critical { font-weight: 800; }
.log-body { margin: 0 8px 8px; padding: 8px; background: var(--panel-2); white-space: pre-wrap; word-break: break-word; }
