/* SPDX-License-Identifier: Apache-2.0 */
/* Maku's design tokens. Light is the default; the .dark block applies when theme.ts puts
   the class on <html> (the app-wizard's contract: a media query would ignore the user's
   explicit choice).
   The brand's supplied colours are Ink #0A0A0F, Surface #14141C, Paper #F3F2FA, Core #8B5CF6
   and Muted #9B9AAE. Core is for the mark, not text: it is 3.81:1 on Paper and 4.33:1 on
   Surface, and Paper on Core is 3.81:1. So --primary, --link and --ring are the derived
   #6D28D9 (light) and #A78BFA (dark), and Muted is secondary text on dark only (2.48:1 on
   Paper). web/test/contrast.test.ts pins every value and pair below. --input is a 3:1
   boundary in both schemes (WCAG 1.4.11); --surface-raised is the dock's surface. */
@font-face {
  font-family: "Space Grotesk"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("assets/fonts/space-grotesk-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Space Grotesk"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("assets/fonts/space-grotesk-latin-500-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Space Grotesk"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("assets/fonts/space-grotesk-latin-700-normal.woff2") format("woff2");
}
:root {
  /* The log's measure: every row, and on a wide screen the conversation's column and the composer. */
  --log-w: 72ch;
  /* One radius scale: a control, a row (a call, the table, the composer), a card. */
  --radius-control: 6px; --radius-row: 8px; --radius-card: 12px;
  color-scheme: light;

  --flow-writer: #0e7490; /* the run rail: a writer's lane */
  --flow-judge: #6d28d9; /* a judge's lane */
  --flow-plan: #b45309; /* a plan step no run has reached */

  --background: #ffffff;
  --foreground: #0a0a0f; /* Ink */
  --card: #ffffff;
  --card-foreground: #0a0a0f;

  --muted: #f3f2fa; /* Paper: the page and inset surfaces */
  --muted-foreground: #5e5a6e; /* secondary text: 5.97:1 on the page */
  --border: #d8d5e3;
  --input: #7e788c; /* an input's boundary: 4.24:1 on a card */
  --surface-raised: #fcfbff; /* the dock: a card, lifted by its shadow */

  --primary: #6d28d9; /* Core, darkened until Paper text on it reads */
  --primary-foreground: #f3f2fa;
  --ring: #6d28d9;
  --link: #6d28d9;

  --accent: #6ab825; /* actor green: decorative, use sparingly */
  --accent-foreground: #0a0a0f;

  --success: #4d8f14;
  --success-foreground: #0a0a0f;
  --success-text: #3a7410;

  --warning: #ed9d13;
  --warning-foreground: #4a2f00; /* warning text on a tint, the page or a card */
  --warning-solid-foreground: #4a2f00; /* the needs-you pill, on solid amber */
  --warning-border: #b36f00;

  --destructive: #d22323;
  --destructive-foreground: #ffffff;
  /* Destructive text where --destructive falls short: the deny button, the error status,
     notices, on the card, the page and the dock's raised surface. */
  --destructive-text: #d02323;

  /* The page header: white, so the supplied light-theme mark sits on Paper/white. */
  --brand-header: #ffffff;
  --brand-header-foreground: #0a0a0f;

  --placeholder: #6b6b6b;

  /* Elevation: a layered transparent shadow lifts a card off whatever surface
     ends up underneath; a border colour doesn't. Structural borders stay borders. */
  --shadow-border:
    0 0 0 1px rgb(0 0 0 / 0.06),
    0 1px 2px -1px rgb(0 0 0 / 0.06),
    0 2px 4px 0 rgb(0 0 0 / 0.04);
  --shadow-border-hover:
    0 0 0 1px rgb(0 0 0 / 0.08),
    0 1px 2px -1px rgb(0 0 0 / 0.08),
    0 2px 4px 0 rgb(0 0 0 / 0.06);
  /* The dock floats over the stream it scrolls past. */
  --shadow-dock: 0 -1px 0 rgb(0 0 0 / 0.06), 0 -6px 16px -4px rgb(0 0 0 / 0.10);

  /* The smallest target a control gets: 40 px on a dense desktop, 44 px on a phone (below).
     A control drawn smaller extends to it with a pseudo-element, never over a neighbour's. */
  --hit: 40px;
}

.dark {
  color-scheme: dark;

  --flow-writer: #22d3ee;
  --flow-judge: #a78bfa;
  --flow-plan: #f5a524;

  --background: #0a0a0f; /* Ink */
  --foreground: #f3f2fa; /* Paper */
  --card: #14141c; /* Surface */
  --card-foreground: #f3f2fa;

  --muted: #0a0a0f; /* the page: Ink, so cards and the header lift off it */
  --muted-foreground: #9b9aae; /* supplied Muted: 7.17:1 on Ink, 6.65:1 on Surface */
  --border: #3c3949;
  --input: #777185; /* an input's boundary: 3.63:1 on the dock */
  --surface-raised: #1c1b26; /* the dock */

  --primary: #a78bfa; /* Core, lightened for text on Ink and Surface */
  --primary-foreground: #0a0a0f;
  --ring: #a78bfa;
  --link: #a78bfa;

  --accent: #6ab825;
  --accent-foreground: #0a0a0f;

  --success: #6ab825;
  --success-foreground: #0a0a0f;
  --success-text: #7fcf3a;

  --warning: #ed9d13;
  /* Light amber: warning text renders on a faint warning tint. */
  --warning-foreground: #f5c97f;
  --warning-solid-foreground: #0a0a0f;
  --warning-border: #ed9d13;

  --destructive: #f37171;
  --destructive-foreground: #0a0a0f;
  --destructive-text: #f47e7e;

  /* The page header: Surface, so the supplied dark-theme mark sits on Ink/Surface. */
  --brand-header: #14141c;
  --brand-header-foreground: #f3f2fa;

  --placeholder: #9b9aae;

  /* Layered depth shadows are invisible against Ink: collapse to the ring. */
  --shadow-border: 0 0 0 1px rgb(255 255 255 / 0.08);
  --shadow-border-hover: 0 0 0 1px rgb(255 255 255 / 0.13);
  --shadow-dock: 0 -1px 0 rgb(255 255 255 / 0.10), 0 -6px 16px -4px rgb(0 0 0 / 0.45);
}

html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  font: 500 16px/1.55 "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  margin: 0;
  background: var(--muted);
  color: var(--foreground);
}

h1, h2, h3, h4 { text-wrap: balance; }
/* Section headings: 16 px bold; metadata labels stay labels, never headings. */
h2, h3, h4 { font-size: 16px; line-height: 1.4; font-weight: 700; }
strong, b { font-weight: 700; }
/* Long prose reads lighter at 400: message bodies, handoff summaries and the notes. The chrome
   (names, chips, buttons, the strip) keeps the body's 500. */
.bubble .md, .handoff > .md, .notes { font-weight: 400; }
/* Code, commands and tool I/O are monospace at 14 px; IDs, timestamps and counters at 13 px
   with tabular numerals. Prose, bubbles and cards stay in the sans. */
.t-cmd, .t-command, .approval-command, pre, code { font: 14px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
time, .t-time, .step-dur, .replay-progress, .t-prompt {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; line-height: 1.45;
  font-variant-numeric: tabular-nums;
}
p, li { text-wrap: pretty; }
.waiting, .closed, .mark-text, .sys, .hint, .approval-meta { text-wrap: pretty; }

a { color: var(--link); }
a:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: 2px; }

/* The page chrome: a brand header, a docked footer. Class-scoped: the chat's
   <header class="msg-head"> and the raw view's <header class="ev-head"> are headers too,
   and the bare selector made each of them a sticky navy bar (review 2026-10-15, U1). */
.page-header, .page-dock { position: sticky; }
.page-dock { padding: 8px 16px; }
.page-header {
  top: 0; z-index: 2;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 4px 16px; align-items: start;
  padding: 16px;
  background: var(--brand-header); color: var(--brand-header-foreground);
}
/* The Maku lockup (brand.ts): the 32 px SVG mark, 16 px clear of the wordmark, and 16 px of
   header padding around it: at least half the mark's width on every side. The two marks are
   both in the DOM; .dark (theme.ts), not the OS media query, picks one, so a manual choice
   and the system choice agree. */
.brand {
  display: flex; align-items: center; gap: 16px; align-self: start;
  color: var(--brand-header-foreground); text-decoration: none;
}
.page-header a.brand { text-decoration: none; }
/* No `display` here: this rule (0,1,1) would beat `.brand-mark-dark { display: none }` (0,1,0)
   and show both marks. Flex items are blockified already. */
.brand img { width: 32px; height: 32px; flex: none; }
.brand-mark-dark { display: none; }
.dark .brand-mark-dark { display: block; }
.dark .brand-mark-light { display: none; }
.brand-wordmark { font: 700 24px/1 "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; letter-spacing: -0.03em; }
.brand:focus-visible { outline: 2px solid var(--ring); outline-offset: 4px; border-radius: 4px; }
/* The dock: the composer on the left (the text over its row: deliver to, the notice, send),
   the act notice, the theme button and the connection on the right; one column on a phone. */
/* Sticky, not fixed: it sits in the flow at the page's end, so once scrolled there it
   covers no row. The shadow, not a border, lifts it over the rows it scrolls past. */
.page-dock {
  bottom: 0; z-index: 2;
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px 12px; align-items: end;
  padding-top: 10px; padding-bottom: 10px;
  font-variant-numeric: tabular-nums;
  background: var(--surface-raised); color: var(--card-foreground);
  box-shadow: var(--shadow-dock);
}
/* The session: Run, Driver, the queue and Room, behind a toggle across the dock (P7).
   Open, it scrolls inside a bound, so the dock never takes the whole screen. */
.page-dock > .session { grid-column: 1 / -1; min-width: 0; }
/* 32 px tall, the top 6 px taken back from the dock's padding; the ::after reaches 40 px,
   half the dock's 8 px gap below, so it never covers the composer. */
.session > summary { cursor: pointer; font-size: 13px; color: var(--muted-foreground); width: fit-content; position: relative; padding-block: 6px; margin-top: -6px; }
.session > summary::after { content: ""; position: absolute; inset: -4px -8px; }
.session[open] > section.controls { max-height: 40vh; overflow: auto; margin-top: 6px; }
.page-dock .controls { padding: 0; background: transparent; }
.page-dock .composer { display: grid; gap: 8px; min-width: 0; }
/* The composer (P4): the text and Send side by side, Send as tall as the field. */
.composer-write { display: flex; gap: 8px; align-items: stretch; min-width: 0; }
.composer-write textarea {
  flex: 1 1 auto; min-width: 0; box-sizing: border-box; font: inherit;
  padding: 8px 10px; border-radius: var(--radius-row); resize: vertical;
  /* Grows with the text from two lines to eight where the browser can; rows=2 elsewhere. */
  field-sizing: content; min-height: calc(2lh + 18px); max-height: calc(8lh + 18px);
}
button.send { display: inline-flex; gap: 6px; align-items: center; justify-content: center; padding: 0 14px 0 16px; font-weight: 700; border-radius: var(--radius-row); min-width: 88px; }
/* The arrow beside a 700 label carries its weight: 2.5, not the set's 1.5. The icon side's
   padding is the text side's less 2 px, so the pair reads centred. */
button.send .icon { stroke-width: 2.5; }
.composer-row, .dock-meta { display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: center; min-width: 0; }
.composer-row .hint { color: var(--muted-foreground); font-size: 13px; }
.composer-row .keys { margin-left: auto; }
/* Where a message goes (P5): a segmented radio group. The radios stay real inputs, for
   the keyboard and assistive tech; the checked one's label is the raised segment. */
fieldset.delivery { display: inline-flex; gap: 2px; margin: 0; padding: 2px; border: 0; border-radius: 8px; background: var(--muted); min-width: 0; }
fieldset.delivery > legend { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.seg { position: relative; display: flex; }
/* The radio is the target: 6 px taller than its pill on each side (8 px on a phone), inside
   the composer's 8 px gaps; the segments' 2 px gap stays theirs. */
.seg input { position: absolute; inset: -6px 0; margin: 0; opacity: 0; cursor: pointer; }
.seg span {
  padding: 4px 12px; border-radius: 6px; font-size: 13px; color: var(--muted-foreground);
  transition-property: background-color, color, box-shadow; transition-duration: 150ms; transition-timing-function: ease-out;
}
.seg input:not(:checked, :disabled):hover + span { color: var(--foreground); }
.seg input:checked + span { background: var(--card); color: var(--foreground); font-weight: 700; box-shadow: var(--shadow-border); }
.seg input:focus-visible + span { outline: 2px solid var(--ring); outline-offset: 1px; }
.seg input:disabled + span { opacity: .5; }
.dock-meta { justify-content: flex-end; }
.page-dock .status { color: var(--muted-foreground); font-size: 13px; font-variant-numeric: tabular-nums; }
/* The connection is a dot beside the words: green live, amber reconnecting, grey otherwise. */
.page-dock .status::before { content: ""; display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; vertical-align: 1px; background: var(--placeholder); }
.page-dock[data-status="live"] .status::before { background: var(--success); }
.page-dock[data-status="reconnecting"] .status::before { background: var(--warning); }
.page-dock .notice { color: var(--destructive-text); overflow-wrap: anywhere; }
@media (max-width: 640px) {
  :root { --hit: 44px; }
  /* Static, not sticky: pinned, the header and the dock took two thirds of an 844 px screen. */
  .page-header { position: static; padding: 14px; row-gap: 14px; }
  .brand { gap: 14px; }
  .brand img { width: 28px; height: 28px; }
  .brand-wordmark { font-size: 22px; }
  .page-header .strip { grid-column: 1 / -1; }
  .page-dock { grid-template-columns: minmax(0, 1fr); }
  .dock-meta { flex-wrap: nowrap; justify-content: space-between; }
  .dock-meta .theme-toggle { flex: none; white-space: nowrap; }
  .page-dock .status { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  .composer-row .keys { display: none; }
  .seg input { inset: -8px 0; }
  /* Touch: every form control is a full target, not only the dock's. */
  .controls button, .controls select, .controls input:not([type="checkbox"]), .needs button, .approval-decide input, .fork button, .fork input, .new-room button, .new-room input { min-height: var(--hit); box-sizing: border-box; }
}
/* Not live: under the strip, why and when the page retries. The unsandboxed banner sits above the strip. */
.banner, .banner-unsandboxed { background: color-mix(in srgb, var(--warning) 18%, var(--card)); color: var(--warning-foreground); padding: 6px 16px; }
/* The live demo (web/preview/demo.ts): its banner, and the replay bar as the dock's first row. */
.demo-banner a { color: inherit; text-decoration: underline; }
.replay-bar { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; font-size: 13px; }
.replay-bar button, .replay-bar select { min-height: 40px; }
.replay-bar select { font: inherit; background: var(--card); color: var(--card-foreground); border: 1px solid var(--input); border-radius: 6px; padding: 0 8px; }
.replay-progress { color: var(--muted-foreground); }
main { padding: 8px 16px 16px; max-width: 100%; }

button {
  font: inherit; cursor: pointer;
  background: var(--card); color: var(--card-foreground);
  border: 1px solid var(--border); border-radius: var(--radius-control); padding: 2px 10px;
  min-height: 28px;
  transition-property: box-shadow, background-color, scale; transition-duration: 150ms; transition-timing-function: ease-out;
}
button:hover { box-shadow: var(--shadow-border-hover); }
/* Press feedback: never below .95, which reads as exaggerated, and only where motion is
   welcome. The buttons drawn as a line of text underline instead: a shrinking sentence reads
   as a glitch. */
@media (prefers-reduced-motion: no-preference) {
  button:active:not(:disabled, .now-link, .clip-toggle, .t-more) { scale: .96; }
}
button:disabled { opacity: .55; cursor: not-allowed; }
/* The acts a reader comes to do get a full target: Send, approve, deny. */
.composer button, .needs button { min-height: var(--hit); }
.icon { width: 14px; height: 14px; flex: none; vertical-align: -2px; }
/* The small standalone controls reach --hit through a centred ::after: it only grows, never
   shrinks a control already larger. Each has 8 px or more of inert space around it. */
.theme-toggle, .fork-here, .clip-toggle, .t-more, .cli-setup > summary, .notes details > summary { position: relative; }
.theme-toggle::after, .fork-here::after, .clip-toggle::after, .t-more::after, .cli-setup > summary::after, .notes details > summary::after {
  content: ""; position: absolute; inset: min(0px, (100% - var(--hit)) / 2);
}
/* The muted fold toggles darken on hover: a colour, the cheapest cue for a frequent act. */
.session > summary, .cli-setup > summary, .notes details > summary, .fold > summary, .step-calls > summary, .controls .advanced > summary, .approval-json > summary {
  transition-property: color; transition-duration: 150ms; transition-timing-function: ease-out;
}
.session > summary:hover, .cli-setup > summary:hover, .notes details > summary:hover, .fold > summary:hover, .step-calls > summary:hover, .controls .advanced > summary:hover, .approval-json > summary:hover { color: var(--foreground); }
button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, summary:focus-visible {
  outline: 2px solid var(--ring); outline-offset: 1px;
}
.page-header button { background: transparent; color: var(--brand-header-foreground); }

/* The title strip (strip.ts): rows of the header, on the brand header, so every chip and link
   takes the header's text token rather than the card's. */
.strip { display: grid; gap: 4px; min-width: 0; overflow-wrap: anywhere; }
/* Size carries the title, not weight: 500 at 26 px, tracked in as display type is. */
.strip-title { font-size: 26px; line-height: 1.2; margin: 0; font-weight: 500; letter-spacing: -0.02em; }
/* The title links its issue: underlined on hover only, a heading first. */
.page-header .strip-title a { text-decoration: none; }
.page-header .strip-title a:hover, .page-header .strip-title a:focus-visible { text-decoration: underline; }
.strip-chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-variant-numeric: tabular-nums; }
/* A chip's link and the Needs-you pill grow to their half of the 6 px row gap: a full --hit
   would cover the next row's chips. */
.page-header .chip a { position: relative; }
.page-header .chip a::after { content: ""; position: absolute; inset: -5px -2px; }
/* On a touch screen the link's target reaches 44 px: the row gap grows to 24 px so the taller
   targets of two wrapped rows (19 px link + 2 x 12.5 px) do not overlap. */
@media (pointer: coarse) {
  .strip-chips { row-gap: 24px; }
  .page-header .chip a::after { inset: calc((100% - 44px) / 2) -4px; }
}
.page-header .needs-pill { position: relative; }
.page-header .needs-pill::after { content: ""; position: absolute; inset: -3px 0; }
/* The facts read as one line of text, not a row of buttons: no outline, a middle dot between
   them; the links among them stay underlined. */
/* Hierarchy by strength: the facts at 68% of the header's ink, the state chips at full. */
.page-header .chip { color: color-mix(in srgb, var(--brand-header-foreground) 68%, var(--brand-header)); background: transparent; border-color: transparent; padding: 0; }
.page-header .chip-phase, .page-header .chip-run, .page-header .chip-verdict { color: var(--brand-header-foreground); }
.page-header .chip + .chip::before { content: "·"; margin-right: 6px; opacity: .6; }
.page-header .chip[title] { cursor: help; }
/* The budget is quiet until it matters: amber from 80 %, red once spent. */
.page-header .chip-budget[data-level="warn"] { color: var(--warning); font-weight: 700; }
.page-header .chip-budget[data-level="warn"] .meter-bar::after { background: var(--warning); }
.page-header .chip-budget[data-level="over"] { color: var(--destructive-text); font-weight: 700; }
.page-header .chip-budget[data-level="over"] .meter-bar::after { background: var(--destructive); }
.page-header a { color: var(--brand-header-foreground); text-decoration: underline; }
.meter-bar {
  display: inline-block; width: 64px; height: 6px; border-radius: 3px; vertical-align: middle;
  background: color-mix(in srgb, var(--brand-header-foreground) 12%, transparent); position: relative;
}
.meter-bar::after { content: ""; position: absolute; inset: 0; width: var(--fill, 0%); background: var(--primary); border-radius: 3px; }
.waiting, .closed { font-size: 13px; color: var(--brand-header-foreground); opacity: .9; }
.strip-head { display: grid; gap: 4px; min-width: 0; }
.strip-tail:empty { display: none; }
/* What comes next: one box under the title, the most actionable lines on the page. */
.strip-next {
  display: grid; gap: 2px; padding: 8px 12px; border-radius: 8px; width: fit-content; max-width: 100%;
  background: color-mix(in srgb, var(--brand-header-foreground) 6%, transparent);
  border-left: 3px solid var(--warning);
}
.strip-next .waiting { font-size: 14px; opacity: 1; }
.next-label { font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--brand-header-foreground); opacity: .7; }
.page-header .waiting code {
  font-size: 13px; padding: .1em .35em; border-radius: 4px;
  background: color-mix(in srgb, var(--brand-header-foreground) 10%, transparent);
}
.page-header .copy-code { font-size: 13px; padding: 0 6px; margin-left: 4px; border: 1px solid color-mix(in srgb, var(--brand-header-foreground) 30%, transparent); border-radius: 4px; }
/* The Now line: a button that reads as a line of text, so it takes the reader to the row. */
.strip-now { display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; font-size: 13px; color: var(--brand-header-foreground); font-variant-numeric: tabular-nums; }
.page-header .now-link {
  border: 0; padding: 0; text-align: left; font-size: 13px; overflow-wrap: anywhere;
  color: var(--brand-header-foreground); background: transparent;
}
.page-header .now-link:hover { box-shadow: none; text-decoration: underline; }
.page-header .now-link { position: relative; }
.page-header .now-link::after { content: ""; position: absolute; inset: min(0px, (100% - var(--hit)) / 2) 0; }
.now-hint { font-style: italic; }
.page-header .needs-pill {
  background: var(--warning); color: var(--warning-solid-foreground);
  border: 0; border-radius: 999px; padding: 2px 10px; font-weight: 700; font-size: 13px;
}
.theme-toggle { font-size: 13px; }
/* On a phone the strip stays short, so the conversation starts on the first screen: the chips
   keep one line that scrolls sideways (pure CSS: no count to keep right as chips come and go),
   Needs you leads it, and the title, the waiting lines and the Now line clamp at two lines.
   The desktop strip is untouched: nothing here applies above 640px. */
@media (max-width: 640px) {
  /* The padding, taken back by the margin, keeps the pill's focus ring out of the clip. */
  /* 8 px: the pill's ring and its 44 px target both fit in the scroll box, which clips them. */
  .page-header .strip-chips { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding-block: 8px; margin-block: -8px; }
  .page-header .needs-pill::after { inset: -8px 0; }
  .page-header .strip-chips::-webkit-scrollbar { display: none; }
  .page-header .strip-chips > * { flex: none; } /* never squeezed: a short fact would stack a letter a line */
  .page-header .needs-pill { order: -1; }
  .strip-title { font-size: 22px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .page-header .waiting { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  /* The clamp's overflow clips an ::after: the button's own box grows instead, the margin
     taking the 24 px back: 20 px of line and 24 px of padding is the 44. The clamp hides lines
     past the second only by clipping at the padding box, so a third line used to show in the
     bottom padding: a mask (it never changes hit-testing, unlike clip-path) paints everything
     but that padding. */
  .page-header .now-link {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; padding-block: 12px; margin-block: -12px;
    -webkit-mask: linear-gradient(#000, #000) top / 100% calc(100% - 12px) no-repeat;
    mask: linear-gradient(#000, #000) top / 100% calc(100% - 12px) no-repeat;
  }
}

/* Chat view */
.msg { margin: 14px 0; max-width: var(--log-w); }
.msg-head { color: var(--muted-foreground); font-size: 13px; display: flex; gap: 8px; align-items: baseline; margin-bottom: 3px; }
.msg-head time, .t-time { margin-left: auto; color: var(--placeholder); white-space: nowrap; }
/* --bubble-bg is the bubble's own surface: a long message's fade (below) ends in it. */
.bubble {
  --bubble-bg: var(--card);
  background: var(--bubble-bg); color: var(--card-foreground);
  border-radius: 4px var(--radius-card) var(--radius-card) var(--radius-card); padding: 8px 14px;
  box-shadow: var(--shadow-border);
}
.from-human .bubble { --bubble-bg: color-mix(in srgb, var(--primary) 10%, var(--card)); }
.from-system .bubble { --bubble-bg: var(--muted); }
/* A long message folds (clip.ts): one measured "closed" is clipped at twelve lines, fades
   into its own surface and gets Show more; a short or opened one is never clipped. The 4 px
   padding, taken back by the margin, keeps a focus ring inside the clip box whole. */
.bubble[data-clip="closed"] > .md {
  position: relative; max-height: calc(12 * 1.55em); overflow-y: hidden;
  padding: 4px; margin: -4px;
}
.bubble[data-clip="closed"] > .md::after {
  content: ""; position: absolute; inset: auto 0 0; height: 3em; pointer-events: none;
  background: linear-gradient(to bottom, transparent, var(--bubble-bg));
}
.clip-toggle {
  display: block; margin-top: 4px; padding: 2px 0; border: 0; background: transparent;
  color: var(--link); font-size: 13px; font-weight: 700;
}
.clip-toggle:hover { box-shadow: none; text-decoration: underline; }
.bubble .md > :first-child { margin-top: 0; } .bubble .md > :last-child { margin-bottom: 0; }
.bubble p { margin: .45em 0; }

/* A tool row: one line, the detail behind the toggle */
.tool {
  background: var(--card); color: var(--card-foreground);
  border-radius: var(--radius-row); margin: 6px 0; max-width: var(--log-w);
  box-shadow: var(--shadow-border);
}
.tool summary { display: flex; gap: 8px; align-items: center; padding: 6px 10px; cursor: pointer; list-style: none; flex-wrap: wrap; border-radius: var(--radius-row); }
.tool summary::before { content: "▸"; color: var(--placeholder); }
.tool[open] summary::before { content: "▾"; }
.tool summary::-webkit-details-marker { display: none; }
.tool[open] > summary { border-radius: var(--radius-row) var(--radius-row) 0 0; } /* its bottom meets the body's rule */
.tool.st-rejected, .tool.st-error { box-shadow: var(--shadow-border), inset 3px 0 0 color-mix(in srgb, var(--destructive) 65%, var(--card)); }
.tool.st-rejected { box-shadow: var(--shadow-border), inset 3px 0 0 color-mix(in srgb, var(--warning) 80%, var(--card)); }
.t-name { color: var(--link); font-weight: 700; }
.t-prompt { color: var(--placeholder); }
/* A call's status (P3): ok a muted check, running the link colour, the rest in words. */
.t-status { display: inline-flex; gap: 4px; align-items: center; font-size: 13px; }
.t-status.st-ok { color: var(--placeholder); }
.t-status.st-running { color: var(--link); }
.t-status.st-rejected { color: var(--warning-foreground); }
.t-status.st-error { color: var(--destructive-text); }
@media (prefers-reduced-motion: no-preference) {
  .icon-spinner { animation: spin 1s linear infinite; }
  @keyframes spin { to { rotate: 360deg; } }
}
.t-cmd { min-width: 0; flex: 1 1 12em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.t-body { border-top: 1px solid var(--border); padding: 6px 10px; }
.t-sec { color: var(--placeholder); font-size: 13px; text-transform: uppercase; letter-spacing: .04em; margin: 6px 0 2px; }
.t-meta { color: var(--placeholder); font-size: 13px; margin-top: 6px; overflow-wrap: anywhere; }
.t-body pre, .other pre { background: var(--muted); border-radius: 6px; padding: 6px 8px; }
.t-command { margin: 2px 0 6px; }
.t-out { max-height: none; }
.t-out.capped { max-height: 16em; }
.t-more { font-size: 13px; }
.t-more:hover { text-decoration: underline; }

/* Chips and badges: a tinted pill, the status token for its text. */
.chip, .pol {
  font-size: 13px; line-height: 1.45; padding: 0 8px; border-radius: 999px; white-space: nowrap;
  border: 1px solid var(--border); color: var(--muted-foreground);
}
.chip-delivery {
  color: var(--warning-foreground);
  background: color-mix(in srgb, var(--warning) 18%, var(--card));
  border-color: color-mix(in srgb, var(--warning) 55%, var(--border));
}
/* A message for the room prompts no agent: a neutral chip, the others amber (R09). */
.chip-delivery.dl-none { color: var(--muted-foreground); background: none; border-color: var(--border); }
@keyframes pulse { 50% { opacity: .45; } }
.pol-allow, .pol-approved, .verdict-approve {
  color: var(--success-text);
  border-color: color-mix(in srgb, var(--success) 45%, var(--border));
}
.pol-deny, .pol-denied, .pol-expired {
  color: var(--destructive-text);
  border-color: color-mix(in srgb, var(--destructive) 45%, var(--border));
}
.verdict-changes {
  color: var(--warning-foreground);
  border-color: color-mix(in srgb, var(--warning) 55%, var(--border));
}

/* Timeline markers and system lines */
.handoff > .md { margin: -2px 0 8px 14px; max-width: var(--log-w); }
.mark, .sys { color: var(--muted-foreground); font-size: 13px; margin: 6px 0; display: flex; gap: 8px; align-items: baseline; }
.mark .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--placeholder); align-self: center; flex: none; }
.mark-text { overflow-wrap: anywhere; }
.sys { font-style: italic; }
.sys::before { content: "·"; }
.mk-harness_error .mark-text, .mk-limit .mark-text { color: var(--destructive); }
.mark { max-width: var(--log-w); }
/* U7: a run of plumbing rows reads as one "· N changes" line, open on click. */
.fold { color: var(--muted-foreground); font-size: 13px; margin: 6px 0; max-width: var(--log-w); }
.fold > summary { cursor: pointer; list-style: none; }
.fold > summary::-webkit-details-marker { display: none; }
.fold-body { padding-left: 14px; }
.notes details > summary, .fold > summary, .step-calls > summary, .t-sec { font-variant-numeric: tabular-nums; }
/* A step (P1): an agent's message, then its calls behind one line, on a rail under it. */
.step { margin: 12px 0; max-width: var(--log-w); }
.step > .msg, .step > .row > .msg { margin: 0; }
.step-calls { margin: 6px 0 0 3px; padding-left: 12px; border-left: 2px solid var(--border); }
.step[data-state="failed"] > .step-calls { border-left-color: color-mix(in srgb, var(--destructive) 65%, var(--card)); }
.step-calls > summary {
  display: flex; gap: 8px; align-items: baseline; padding: 4px 0; cursor: pointer; list-style: none;
  font-size: 13px; font-weight: 500; color: var(--muted-foreground);
}
.step-calls > summary::-webkit-details-marker { display: none; }
.step-calls > summary::before { content: "▸"; color: var(--placeholder); }
.step-calls[open] > summary::before { content: "▾"; }
.step-exc { color: var(--warning-foreground); font-weight: 700; }
.step-sep { color: var(--placeholder); }
.step-dur { margin-left: auto; color: var(--placeholder); }
/* Inside a step a call is a line, not a card: the rail groups them. */
.step-body > .tool { background: transparent; box-shadow: none; margin: 0; }
.step-body > .tool > summary:hover { background: color-mix(in srgb, var(--card) 70%, transparent); }
.step-body > .tool[open] { background: var(--card); box-shadow: var(--shadow-border); margin: 4px 0; }
.step-body > .fold { margin: 2px 0 2px 10px; }
.step-body .mark { max-width: none; } /* its time aligns with the call lines' */
/* An actor's colour, keyed on its id: decoration beside the name, which carries the meaning. */
.swatch { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: -2px; flex: none; align-self: center; }
.swatch-0 { background: var(--primary); }
.swatch-1 { background: var(--accent); }
.swatch-2 { background: var(--warning); }
.swatch-3 { background: var(--destructive); }
.swatch-4 { background: var(--link); }
.swatch-5 { background: var(--muted-foreground); }
.other { max-width: var(--log-w); color: var(--muted-foreground); }
.other summary { cursor: pointer; }

/* Raw view */
.ev { border-bottom: 1px solid var(--border); padding: 6px 0; }
.ev-head { color: var(--muted-foreground); font-size: 13px; }
pre { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 24em; overflow: auto; }
.md { overflow-wrap: anywhere; } /* a 64-hex digest has no break point; pre and code keep their own wrapping */
.md a { overflow-wrap: anywhere; }
/* Markdown in a message stays in the conversation's scale: a heading is a bold line, never the
   browser's 2em poster, and inline code reads as code inside the prose. */
.md :is(h1, h2, h3, h4, h5, h6) { font-size: 16px; line-height: 1.4; font-weight: 700; margin: .9em 0 .3em; }
.md h1 { font-size: 18px; }
.md :not(pre) > code { font-size: .875em; padding: .1em .35em; border-radius: 4px; background: color-mix(in srgb, var(--foreground) 7%, transparent); }
.md pre { padding: 8px 10px; border-radius: 6px; background: color-mix(in srgb, var(--foreground) 5%, transparent); }
.status-error, .status-rejected { color: var(--destructive); }
/* An audit note, not an alarm: the warning foreground reads on both schemes. */
.redacted { color: var(--warning-foreground); font-size: 13px; }

/* Controls (unchanged behaviour, platform palette) */
/* hidden must win over a flex display: the controls toggle it, never re-mount (R10). */
[hidden] { display: none !important; }
.controls { padding: 0 16px 8px; display: grid; background: var(--background); }
.controls .driver, .controls form, .new-room { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.controls input, .new-room input { flex: 1 1 12em; min-width: 0; }
/* Named groups (U8): Run, Driver, the queue, Room behind a toggle. */
.controls .group, .controls .group-queue { border-top: 1px solid var(--border); padding: 8px 0; min-width: 0; }
.controls h2, .controls .group-room > summary, .controls .group-label {
  font-size: 13px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted-foreground); margin: 0 0 6px; font-weight: 700;
}
.controls .group-room > summary { cursor: pointer; margin: 0; }
.controls .group-room[open] > summary { margin-bottom: 6px; }
.controls .group-room > * + * { margin-top: 6px; }
.controls label.field { display: inline-flex; flex-wrap: wrap; gap: 4px 6px; align-items: center; flex: 1 1 12em; min-width: 0; }
.controls label.field > span { color: var(--muted-foreground); font-size: 13px; }
.composer-row label.field { display: inline-flex; gap: 6px; align-items: center; min-width: 0; }
.composer-row label.field > span { color: var(--muted-foreground); font-size: 13px; }
.composer-row select { max-width: 100%; }
.controls .advanced { flex: 1 1 100%; }
.controls .advanced > summary { cursor: pointer; color: var(--muted-foreground); font-size: 13px; }
.members { margin: 0; padding-left: 18px; } .members li { overflow-wrap: anywhere; }
.stop { margin: 0; }
.confirm { display: inline-flex; gap: 6px; }
button.destructive { color: var(--destructive-text); border-color: color-mix(in srgb, var(--destructive) 45%, var(--border)); }
input, select, textarea {
  font: inherit;
  background: var(--card); color: var(--card-foreground);
  border: 1px solid var(--input); border-radius: 6px; padding: 2px 6px;
}
input::placeholder, textarea::placeholder { color: var(--placeholder); }
.queue { margin: 0; padding-left: 18px; } .queue li span { overflow-wrap: anywhere; margin-right: 6px; }
/* The needs-you panel, under the strip: approval cards lead with their command, the JSON
   behind a toggle; approve is the primary button, deny the destructive outline. */
.needs { padding: 8px 16px; display: grid; gap: 6px; max-width: 100%; }
.needs h2 { margin: 0; font-size: 1em; }
.approvals, .needs-other { margin: 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.needs-other li { overflow-wrap: anywhere; }
/* A list that could not be refreshed, and a room whose state could not be read: information under the
   list, not needs themselves, so the pill does not count them. */
.needs-other li.needs-stale, .needs-other li.needs-unread { color: var(--muted-foreground); font-size: 13px; }
/* The 6 px reason field and buttons plus the 8 px of padding, concentric. The accent is a bar,
   as a rejected call's is, on the card's own edge; the left padding clears its 3 px. */
.approval { border-radius: calc(var(--radius-control) + 8px); padding: 8px 12px 8px 15px; display: grid; gap: 6px;
  background: var(--card); color: var(--card-foreground); min-width: 0;
  box-shadow: var(--shadow-border), inset 3px 0 0 var(--warning); }
.approval-command { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.approval-meta { color: var(--muted-foreground); font-size: 13px; overflow-wrap: anywhere; }
.approval-json > summary { cursor: pointer; color: var(--muted-foreground); font-size: 13px; }
.approval-json pre { margin: 4px 0 0; background: var(--muted); padding: 6px; overflow-x: auto; max-height: 16em; border-radius: 6px; }
.approval-decide { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.approval-decide input { flex: 1 1 12em; min-width: 0; }
button.primary { background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); }
/* Deny is a ghost: the destructive word, no outline; hover tints it. */
button.deny { color: var(--destructive-text); border-color: transparent; background: transparent; }
button.deny:hover { background: color-mix(in srgb, var(--destructive) 10%, transparent); box-shadow: none; }
.manifest pre { background: var(--muted); padding: 8px; border-radius: 6px; }
.new-room { padding: 8px 16px; } .new-room .notice { color: var(--destructive); }
.fork-here { font-size: 13px; padding: 0 6px; }
/* U7: fork here sits in a toolbar beside a message or a handoff, shown on hover and focus. */
.row { position: relative; max-width: var(--log-w); }
.row-tools { position: absolute; right: -4px; top: 0; opacity: 0; visibility: hidden; pointer-events: none; transition-property: opacity, visibility; transition-duration: 150ms; transition-timing-function: ease-out; }
.row:hover .row-tools, .row:focus-within .row-tools { opacity: 1; visibility: visible; pointer-events: auto; }
/* No hover, no reveal: on touch the control stays visible, in the flow under the row where it
   cannot cover the timestamp, and is a full 44 px target. */
@media (hover: none), (pointer: coarse) {
  .row-tools { position: static; opacity: 1; visibility: visible; pointer-events: auto; text-align: right; }
  .row-tools { margin-top: -2px; }
  .row-tools .fork-here { border: 0; background: none; box-shadow: none; color: var(--muted-foreground); font-size: 13px; line-height: 20px; height: 20px; min-height: 0; padding: 0 8px; }
  .row-tools .fork-here:hover { box-shadow: none; text-decoration: underline; }
  /* 20 px visible + 12 px each way = 44 px, over the row's own inert space. */
  .row-tools .fork-here::after { inset: -12px -8px; }
}
.fork { padding: 8px 16px; border-top: 1px solid var(--border); background: var(--background); }
.fork form { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.fork input { flex: 1 1 12em; min-width: 0; }
.fork pre { background: var(--muted); padding: 8px; border-radius: 6px; white-space: pre-wrap; overflow-wrap: anywhere; }
.cli-setup { margin-top: 16px; padding: 0 16px 16px; }
.cli-setup > summary { cursor: pointer; font-size: 13px; color: var(--muted-foreground); width: fit-content; }
.cli-setup[open] > summary { margin-bottom: 8px; }
/* The CLI setup card (view.ts cliSetup): numbered steps, each command in an inset block with
   its Copy button. Concentric radii: the card 14 px, a block 10 px, the button 6 px at 4 px
   in. A block wraps its lines (a word breaks only when nothing else fits), so the fold never
   scrolls sideways; the button floats, so the command's later lines run under it. */
.cli-card { background: var(--card); color: var(--card-foreground); box-shadow: var(--shadow-border); border-radius: var(--radius-card); padding: 16px; max-width: 80ch; min-width: 0; }
.cli-message { margin: 0; }
.cli-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 20px; counter-reset: cli-step; }
.cli-step { counter-increment: cli-step; display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 12px; row-gap: 6px; }
.cli-step > * { grid-column: 2; min-width: 0; }
.cli-step::before {
  content: counter(cli-step); grid-column: 1; grid-row: 1; width: 24px; height: 24px; border-radius: 999px;
  display: grid; place-items: center; font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
  background: color-mix(in srgb, var(--primary) 12%, var(--card)); color: var(--primary);
}
.cli-step-title { margin: 0; line-height: 24px; }
.cli-note { margin: 0; font-size: 13px; color: var(--muted-foreground); }
.cli-note code { font-size: 13px; color: var(--card-foreground); }
.cli-code { background: var(--muted); border-radius: 10px; padding: 4px; }
.cli-code pre { margin: 0; padding: 8px 8px 8px 10px; white-space: pre-wrap; overflow-wrap: break-word; max-height: none; overflow: visible; color: var(--foreground); }
.cli-copy { float: right; min-height: var(--hit); min-width: 84px; margin: 0 0 4px 8px; padding: 0 12px; font-size: 13px; border-radius: 6px; }
.cli-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
@media (max-width: 640px) {
  .cli-card { padding: 12px; }
  .cli-step { grid-template-columns: minmax(0, 1fr); }
  .cli-step::before { display: none; }
  .cli-step-title::before { content: counter(cli-step) ". "; color: var(--primary); }
  .cli-step > * { grid-column: 1; }
  .cli-code { border-radius: 8px; padding: 2px; }
  .cli-copy { margin: 0 0 2px 6px; }
}
.new-room .hint { flex-basis: 100%; color: var(--muted-foreground); font-size: 13px; }
.gone { padding: 16px; }

/* Room page lead: the notes. Wraps at phone width, never scrolls sideways. */
.summary { padding: 8px 16px; display: grid; gap: 8px; max-width: 100%; overflow-wrap: anywhere; }
.summary-error { color: var(--destructive); margin: 0; }
.summary .hint { color: var(--muted-foreground); font-size: 13px; }
.notes { overflow-wrap: anywhere; }
.notes .note-latest { margin: 0; }
.notes .note-latest .hint { margin-left: 8px; }
.notes details > summary { cursor: pointer; color: var(--muted-foreground); font-size: 13px; }
.notes ul { margin: 4px 0 0; padding-left: 18px; }
.raw-events { margin-top: 8px; }
.raw-events > summary { cursor: pointer; padding: 4px 0; }
/* Until the first frame (P9): three message shapes, pulsing where motion is welcome. */
.skeleton { padding: 8px 16px; display: grid; gap: 12px; max-width: var(--log-w); }
.sk-msg { height: 56px; border-radius: var(--radius-card); background: var(--card); box-shadow: var(--shadow-border); }
.sk-msg:nth-child(2) { width: 70%; }
.sk-msg:nth-child(3) { width: 85%; }
@media (prefers-reduced-motion: no-preference) {
  .sk-msg { animation: pulse 1.6s ease-in-out infinite; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* Room list */
/* The page title beside the brand: a 1px divider, then the title lighter and smaller than the
   wordmark, so the header reads "maku | Rooms", never "maku Rooms". The grid's 16px gap and the
   16px padding put the divider midway; 24px of line height makes it 24px tall. */
.page-header .page-title {
  margin: 0; align-self: center;
  font: 500 18px/24px "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color: var(--muted-foreground);
  border-left: 1px solid var(--border); padding-left: 16px;
}
.page-header .theme-toggle { align-self: center; }
.list-tools { display: flex; gap: 8px 12px; align-items: center; padding: 12px 16px; flex-wrap: wrap; }
.list-tools label { display: inline-flex; gap: 6px; align-items: center; }
.list-tools .notice { color: var(--destructive-text); }
.list-tools .notice:empty { display: none; }
.list-tools label, .list-tools select, .list-tools button { min-height: var(--hit); }
.list-tools [data-act="new-room"] { margin-left: auto; padding: 0 14px; font-weight: 700; }
/* The auto-refresh: when it last read the list, the interval and the pause, before New room. */
.list-tools .refresh { display: inline-flex; gap: 8px; align-items: center; margin-left: auto; }
.list-tools .refresh + [data-act="new-room"] { margin-left: 0; }
.refresh-status { color: var(--muted-foreground); font-size: 13px; font-variant-numeric: tabular-nums; }
.refresh-status[data-failed] { color: var(--destructive-text); }
.refresh-pause { padding: 0 10px; }
table.rooms { width: calc(100% - 32px); margin: 0 16px; border-collapse: collapse; background: var(--card); box-shadow: var(--shadow-border); border-radius: var(--radius-row); overflow: hidden; }
.rooms th, .rooms td { text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--border); vertical-align: baseline; font-variant-numeric: tabular-nums; }
.rooms tbody tr:last-child td { border-bottom: 0; }
.rooms th { font-size: 13px; color: var(--muted-foreground); text-transform: uppercase; letter-spacing: .04em; }
.rooms th:first-child, .rooms .room-row td:first-child { width: 1%; white-space: nowrap; }
.rooms .needs-pill { background: var(--warning); color: var(--warning-solid-foreground); border-radius: 999px; padding: 2px 10px; font-weight: 700; font-size: 13px; }
/* A row opens its room: the task link's ::after covers the row (the stretched-link pattern),
   so there is one link per row and no click handler; any other link or button sits above it. */
.rooms tr.room-row { position: relative; }
.rooms tr.room-row:hover, .rooms tr.room-row:focus-within { background: color-mix(in srgb, var(--primary) 6%, var(--card)); }
.rooms .task-link { text-decoration: none; }
.rooms .task-link::after { content: ""; position: absolute; inset: 0; }
.rooms .task-link:hover, .rooms .task-link:focus-visible { text-decoration: underline; }
.rooms .task-link:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: 2px; }
.rooms .room-row a:not(.task-link), .rooms .room-row button { position: relative; z-index: 1; }
/* The room id is copied into maku: above the link too, so it selects and a click stays put. */
.rooms td.room-id { position: relative; z-index: 1; cursor: text; }
/* The strip's phase chip (.page-header .chip), on the card's tokens; 50%, not the strip's 45%,
   so its border still holds 3:1 on a hovered row. */
.rooms .chip-phase {
  color: var(--card-foreground); background: transparent;
  border-color: color-mix(in srgb, var(--card-foreground) 50%, var(--card));
}
/* The room page's status line (strip.ts's .run-status), compact: the accent bar is the colour cue,
   never the only one, on tokens that hold 3:1 on a card and on a hovered row in both themes. */
.rooms .row-status { display: inline-flex; gap: 6px; align-items: baseline; padding-left: 8px; border-left: 3px solid var(--muted-foreground); color: var(--card-foreground); font-weight: 500; line-height: 1.35; }
.rooms .row-status[data-kind="running"] { border-left-color: var(--primary); }
.rooms .row-status[data-kind="waiting-you"], .rooms .row-status[data-kind="waiting-other"], .rooms .row-status[data-kind="waiting-pr"], .rooms .row-status[data-kind="waiting"] { border-left-color: var(--warning-border); }
.rooms .row-status[data-kind="finished"], .rooms .row-status[data-kind="done"] { border-left-color: var(--success); }
.rooms .row-status[data-kind="failed"] { border-left-color: var(--destructive); }
.rooms .row-status-icon { flex: none; }
.rooms .repo, .rooms .room-id { color: var(--muted-foreground); }
.rooms td.repo { white-space: nowrap; } /* an owner/name reads whole; the task and the status wrap */
.rooms .room-id code { font-size: 13px; }
@media (max-width: 600px) {
  /* A phone keeps what needs you, the task and its status; the repository and the rest go. */
  .rooms th:nth-child(3), .rooms td:nth-child(3), .rooms th:nth-child(n+5), .rooms td:nth-child(n+5) { display: none; }
  .rooms .row-status { font-size: 14px; }
  .rooms th:first-child { white-space: normal; } /* "Needs / you" on two lines, not a wide column */
  .rooms th, .rooms td { padding: 8px 10px; }
}

/* Approval card: the plain-words block leads, the raw command folds under details. */
.approval-intent { margin: 0; font-weight: 500; }
.approval-why, .approval-deadline, .approval-status { margin: 0; font-size: 14px; overflow-wrap: anywhere; }
.approval-outcomes { display: grid; gap: 2px; font-size: 14px; }
.approval-outcomes p { margin: 0; }
.approval-status[data-can="true"] { color: var(--foreground); font-weight: 500; }
.approval-status[data-can="false"] { color: var(--muted-foreground); }
.approval-details > summary { cursor: pointer; color: var(--muted-foreground); font-size: 13px; }
.approval-details > :not(summary) { margin-top: 6px; }
.approvals-banner { font-weight: 500; }

/* The run status (strip.ts): the first line of the strip. Icon and words say it; the accent bar
   is the colour cue, never the only one. Maku weights: 700 for the bold line. */
.run-status { display: flex; gap: 8px; align-items: baseline; font-size: 16px; line-height: 1.4; font-weight: 700; font-variant-numeric: tabular-nums; padding-left: 10px; border-left: 4px solid var(--brand-header-foreground); }
.run-status[hidden] { display: none; }
.run-status[data-kind="running"] { border-left-color: var(--primary); }
.run-status[data-kind="waiting-you"], .run-status[data-kind="waiting-other"], .run-status[data-kind="waiting-pr"], .run-status[data-kind="waiting"] { border-left-color: var(--warning); }
.run-status[data-kind="finished"], .run-status[data-kind="done"] { border-left-color: var(--success); }
.run-status[data-kind="failed"] { border-left-color: var(--destructive); }
.run-status-icon { flex: none; align-self: center; display: inline-flex; }
.run-status-icon .icon, .row-status-icon .icon { display: block; }
.icon-dot { fill: currentColor; }

/* The run rail (rail.ts): a node's lane colour comes from data-tone, a plan step's from
   data-state=ghost; role text never reaches a class. Past runs dim so the live one leads. */
.rail { font-size: 13px; min-width: 0; overflow-wrap: anywhere; }
.rail[hidden] { display: none; }
.rail-path, .rail-milestones { list-style: none; margin: 0; padding: 0; }
.rail-node { position: relative; --lane: var(--flow-writer); }
.rail-node[data-tone="judge"] { --lane: var(--flow-judge); }
.rail-node[data-state="ghost"] { --lane: var(--flow-plan); }
.rail-node[data-state="ghost"] > .rail-button { opacity: 0.6; }
.rail-node[data-state="done"] > .rail-button { opacity: 0.72; }
.rail-button {
  display: grid; gap: 2px; width: 100%; min-height: var(--hit); padding: 6px 8px; text-align: left;
  font: inherit; color: var(--foreground); background: var(--card); cursor: pointer;
  border: 1px solid var(--border); border-left: 3px solid var(--lane); border-radius: 6px;
}
.rail-button:hover { box-shadow: var(--shadow-border-hover); }
.rail-button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.rail-button[aria-current="true"] { border-color: var(--lane); }
.rail-button[aria-pressed="true"] { background: var(--accent); color: var(--accent-foreground); }
.rail-node[data-state="ghost"] .rail-button { background: transparent; border-style: dashed; cursor: default; }
.rail-line { display: flex; gap: 6px; align-items: center; min-width: 0; }
.rail-line .glyph { flex: none; width: 18px; height: 18px; color: var(--lane); }
.rail-persona { font-weight: 500; min-width: 0; }
.rail-chip { margin-left: auto; white-space: nowrap; color: var(--muted-foreground); }
.rail-chip[data-kind="ok"] { color: var(--success-text); }
.rail-chip[data-kind="changes"], .rail-chip[data-kind="fail"] { color: var(--destructive-text); }
.rail-chip[data-kind="live"] { color: var(--lane); }
.rail-judge { color: var(--flow-judge); white-space: nowrap; }
.rail-detail { color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
.rail-edge { display: flex; align-items: center; gap: 6px; padding-left: 4px; }
.rail-edge svg { flex: none; }
.rail-stroke { fill: none; stroke: var(--border); stroke-width: 2; }
.rail-edge[data-state="ghost"] .rail-stroke { stroke: var(--flow-plan); stroke-dasharray: 3 3; opacity: 0.6; }
.rail-edge-label { color: var(--muted-foreground); }
.rail-round > button {
  width: 100%; min-height: var(--hit); padding: 6px 8px; text-align: left; font: inherit;
  color: var(--muted-foreground); background: transparent; border: 1px dashed var(--border); border-radius: 6px; cursor: pointer;
}
.rail-parts { list-style: none; margin: 4px 0 0; padding: 0 0 0 12px; display: grid; gap: 2px; font-size: 13px; }
.rail-part, .rail-parent { color: var(--muted-foreground); }
.rail-part a, .rail-parent a { color: var(--muted-foreground); }
.rail-part[data-state="running"] { color: var(--foreground); }
.rail-part[data-state="queued"] { opacity: 0.6; }
.rail-parent { padding: 0 0 6px 4px; font-size: 13px; }
.rail-milestones { display: grid; gap: 4px; margin-top: 8px; }
.rail-milestone { display: flex; gap: 6px; align-items: center; color: var(--muted-foreground); }
.rail-milestone::before { content: ""; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--muted-foreground); }
.rail-milestone[data-state="ghost"] { color: var(--flow-plan); opacity: 0.6; }
.rail-milestone[data-state="ghost"]::before { background: transparent; border: 1px dashed var(--flow-plan); }
.rail-card {
  position: absolute; z-index: 5; top: calc(100% + 4px); left: 0; right: 0; padding: 8px; display: grid; gap: 2px;
  background: var(--surface-raised); color: var(--foreground); box-shadow: var(--shadow-border); border-radius: 6px;
}
.rail-card[hidden] { display: none; }
.rail-card p { margin: 0; }
.rail-card .hint { margin-left: 8px; color: var(--muted-foreground); }
@media (prefers-reduced-motion: no-preference) {
  .rail-node.enter { animation: rail-enter 150ms ease-out; }
  .rail-stroke.draw { stroke-dasharray: var(--len); animation: rail-draw 400ms ease-out; }
  .rail-judge.tick { display: inline-block; animation: rail-tick 200ms ease-out; }
  .rail-milestone.fill::before { animation: rail-fill 300ms ease-out; }
  .rail-node[data-state="live"] > .rail-button { animation: rail-breathe 2.4s ease-in-out infinite; }
  @keyframes rail-enter { from { opacity: 0; transform: scale(0.96); } }
  @keyframes rail-draw { from { stroke-dashoffset: var(--len); } to { stroke-dashoffset: 0; } }
  @keyframes rail-tick { 50% { transform: scale(1.4); } }
  @keyframes rail-fill { from { transform: scale(0); } }
  @keyframes rail-breathe { 50% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--lane) 35%, transparent); } }
}
.rail-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; margin: 0; }

/* The room body: the conversation, and the rail beside it from 1100 px up. Below that the rail is
   a drawer a row of dots opens. --header-h and --dock-h come from room.ts (CSSOM). */
.room-body { min-width: 0; }
.rail-dots, .rail-close { display: none; }
.room-body:not(.has-rail) > .rail { display: none; }
.rail-dots { gap: 6px; align-items: center; margin: 8px 16px 0; padding: 6px 8px; min-height: var(--hit); background: var(--card); border: 1px solid var(--border); border-radius: 6px; cursor: pointer; }
.rail-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--flow-writer); }
.rail-dot[data-tone="judge"] { background: var(--flow-judge); }
.rail-dot[data-state="ghost"] { background: transparent; border: 2px dashed var(--flow-plan); box-sizing: border-box; }
.rail-dot[data-state="done"] { opacity: 0.72; }
@media (max-width: 1099.98px) {
  .room-body.has-rail > .rail-dots:not([hidden]) { display: flex; }
  .room-body.has-rail > .rail.open {
    display: block; position: fixed; z-index: 3; top: 0; right: 0; bottom: 0; width: min(320px, 100vw);
    overflow-y: auto; padding: 16px; box-sizing: border-box; background: var(--surface-raised); box-shadow: var(--shadow-dock);
  }
  .rail.open > .rail-close { display: block; margin-left: auto; margin-bottom: 8px; min-height: var(--hit); }
  .room-body.has-rail > .rail:not(.open) { display: none; }
}
@media (min-width: 1100px) {
  /* The column is the log's measure plus the main's padding, so the rail sits beside the rows
     rather than across an empty band; the composer takes the same measure. */
  .page-dock { grid-template-columns: minmax(0, var(--log-w)) 300px; column-gap: 32px; }
  .room-body.has-rail { display: grid; grid-template-columns: minmax(0, calc(var(--log-w) + 32px)) 300px; column-gap: 16px; align-items: start; padding-right: 16px; }
  .room-body.has-rail > :not(.rail) { grid-column: 1; }
  .room-body.has-rail > .rail {
    grid-column: 2; grid-row: 1 / span 6; position: sticky; top: var(--header-h, 0px);
    max-height: calc(100vh - var(--header-h, 0px) - var(--dock-h, 0px)); overflow-y: auto; align-self: start;
    padding-top: 8px;
  }
}

/* A run's own view (runview.ts), in place of the room's conversation. */
.run-view-bar { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.run-view-title { font-weight: 500; min-width: 0; overflow-wrap: anywhere; }
.run-view-note { margin: 0 0 8px; color: var(--muted-foreground); }
.rail-replays { display: flex; gap: 6px; margin-bottom: 8px; }
.rail-replays[hidden], .rail-replay[hidden] { display: none; }
.rail-replay { font: inherit; padding: 2px 10px; border: 1px solid var(--border); border-radius: 6px; background: transparent; color: inherit; cursor: pointer; }
.rail-replay:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
