/* The room's work status (DESIGN "Room work status"; web/modules/room_work.js). The status control is a
   status sentence right after the room's title in its bar; in every room its list is one floating sheet of its own
   material, hung under the bar's capsules. The control's tone (its word's ink) comes with its word
   and mark from one producer (work_status.roomStatusView over the server's room: live, queued, wait for the
   owner's question, held for a pause or a wait, unknown, and the connection's own). A row's tone comes from
   its work's facts, never from text, always beside a word and one mark:
     live       working: the accent ink
     wait       a wait on the owner's own question: the warn ink
     held       a pause or a wait nothing needs the owner for: neutral
     queued     neutral;   unknown: neutral
     done / stopped / failed   a known outcome in its status foreground
   The mark is the shared one (status_mark.js, drawn by the recipe at the end of ui.css): the work's shape in
   its word's ink, a known outcome's dot in that outcome's hue, and the amber dot in its place while something
   waits for the owner (a question or an action only the owner takes), beside any word. This file only places it.
   Names stay --text-primary at 400; local dimensions are variables on .rw-surface, never a type size. */

/* ---- the status control ---------------------------------------------------------------------------
   The short word in its status ink, then one mark, as on the sidebar's rows; nothing is filled. Pressing is
   the only fill: a neutral tint on hover and while open.
   It follows the title and takes only room the title leaves; on a short row it shows its mark alone, and the
   control's name says the whole status either way. */
.rw-trigger {
    gap: var(--space-2);
    min-width: 0;
    max-width: 100%;
    border: 1px solid transparent;
    background: transparent;
    color: var(--text-meta);
    font: inherit;
    font-size: var(--type-meta);
    cursor: pointer;
    transition: background 120ms ease;
}
.rw-trigger[hidden] { display: none; }
.rw-trigger:disabled { cursor: default; opacity: 1; }
.rw-trigger:focus-visible { outline: 2px solid var(--focus-accent-border); outline-offset: 2px; }
/* The status follows the room's title in its bar. The place's name wins (DESIGN "Shell"): when the row is short
   the status word yields first (its dot and caret stay, and the control's name keeps the whole status), and only
   then does the title ellipsize; the control never goes under the touch minimum. Neither moves the bar's other
   controls: each bar is one row whose commands keep their width while its title block yields to them. */
:is(.chat-page-header, .project-panel-bar) .app-page-title-row > .rw-trigger {
    flex: 0 1000 auto; min-width: var(--touch-target-min); max-width: 100%; justify-content: center;
    margin-inline-start: 6px; padding-inline: 12px;
}
:is(.chat-page-header, .project-panel-bar) .app-page-title-row > .rw-trigger:not([data-word="off"]):has(> .rw-caret:not([hidden])) {
    min-width: max(var(--touch-target-min), 60px);
}
/* A word that went keeps no room of its own (U117): the control closes round its mark and caret, so the name it gave
   way to takes the freed width; the whole title capsule stays its target. */
:is(.chat-page-header, .project-panel-bar) .app-page-title-row > .rw-trigger[data-word="off"] {
    flex-shrink: 0;
    min-width: 0;
    padding-inline: var(--space-2);
}
html[data-layout="docked"] .chat-page-header .app-page-title-row > .rw-trigger {
    margin-inline-start: 0;
}
:is(.chat-page-header, .project-panel-bar) .app-page-title-row > .app-page-title {
    flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* A Project's name keeps one line at the controls' size and weight and ends in an ellipsis, so its capsule keeps the
   bar's height (owner 2026-11-01, A·14). The whole name is the title's hover text and accessible name, and heads the
   room's work list the title opens (.rw-title); on a phone a name still cut leaves the consciousness chip its brain
   and mark (room_work.js fitWord). */
.project-panel-bar .app-page-title-row > .app-page-title { font-size: var(--type-body); font-weight: 600; }
:is(.chat-page-header, .project-panel-bar) > .app-page-title-block { flex: 0 1 auto; min-width: 0; max-width: 100%; }
/* The consciousness chip follows the title block and keeps its width (consciousness.css); the commands take the bar's
   far end, so the title yields first. */
:is(.chat-page-header, .project-panel-bar) > :is(.app-page-actions, .app-page-trailing) { flex: 0 0 auto; margin-inline-start: auto; }
.project-panel-bar { column-gap: var(--space-2); }
:is(.chat-page-header, .project-panel-bar) .app-page-title-row {
    box-sizing: border-box;
    display: inline-flex; align-items: center; gap: 0; max-width: 100%; min-width: 0;
    height: var(--control-h-float); padding-inline: 14px 4px; border-radius: var(--pill-radius);
    background: var(--chip-float); box-shadow: var(--elev-rest);
    -webkit-backdrop-filter: var(--glass); backdrop-filter: var(--glass);
}
html[data-layout="docked"] .chat-page-header .app-page-title-row {
    padding-inline: 2px;
}
/* The real badge (setStatus writes it) is bare text inside the control. */
.rw-trigger .status-badge {
    display: block;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    animation: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 500;
    /* The clip box reaches a little past the line so descenders (g, p, y) are not cut; the margin gives the room back. */
    padding-block: 0.15em;
    margin-block: -0.15em;
}
/* A row too short for the name, the word and the mark keeps the name and the mark: the word goes whole instead of
   showing a cut fragment ("Wor…"), after a preparing turn's start step beside it (setStatus's data-step);
   room_work.js measures them (fitWord). */
.status-badge[data-step]:not([data-step=""])::after { content: " · " attr(data-step); }
.rw-trigger[data-step="off"] .status-badge::after { content: none; }
.rw-trigger[data-word="off"] .status-badge { display: none; }
/* (0,5,0) outranks the light theme's connection colours (0,4,0): the tone below owns the word's ink. */
:root .rw-trigger[data-tone] .status-badge.status-badge { color: inherit; }
/* Tones (one producer, work_status.roomStatusView): crimson is live work, amber only the owner's own question, green
   the room at rest; a pause, an access wait, a hold, starting and reconnecting are neutral facts. The mark after the
   word takes the word's ink. */
.rw-trigger[data-tone="live"] { color: var(--accent-light); }
.rw-trigger[data-tone="wait"] { color: var(--status-warn-fg); }
.rw-trigger:is([data-tone="queued"], [data-tone="held"], [data-tone="starting"]) { color: var(--text-meta); }
.rw-trigger[data-tone="unknown"] { color: var(--status-neutral-fg); }
.rw-trigger[data-tone="online"] { color: var(--status-ok-fg); }
/* A start that failed is the failure's word beside its red dot (host_starting_status.js), and the control stays
   pressable: its list says why, with Restart beside it. */
.rw-trigger[data-tone="failed"] { color: var(--status-error-fg); }
.rw-trigger[data-tone="offline"] { color: var(--text-secondary); }
/* The whole title capsule opens the work list; its state layer covers that same target. */
.app-page-title-row:has(> .rw-trigger:not(:disabled):not([hidden])) { cursor: pointer; transition: var(--motion-tone); }
@media (hover: hover) {
    .app-page-title-row:has(> .rw-trigger:not(:disabled):not([hidden])):hover { background: var(--chip-float-hover); }
}
.app-page-title-row:has(> .rw-trigger[data-open="1"]) { background: var(--chip-float-hover); }
.app-page-title-row:has(> .rw-trigger:not(:disabled):not([hidden])):active { filter: var(--press); }

/* ---- carets and hidden parts, shared by the control and the rows --------------------------------------- */
.rw-caret[hidden], .rw-secondary[hidden], .rw-where[hidden], .rw-chev[hidden] { display: none; }
/* The mark keeps the moving dots' width in every shape, so a room going between Online and Working never moves
   its caret (a state change never moves a neighbour). */
.rw-trigger .status-mark { min-width: calc(3 * var(--mark-dot) + 2 * var(--mark-gap)); }
.rw-caret, .rw-chev { flex: none; display: inline-flex; width: 10px; height: 10px; align-items: center; justify-content: center; }
.rw-caret::before, .rw-chev::before {
    content: '';
    width: 5px;
    height: 5px;
    margin-top: -3px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(45deg);
    transition: transform 140ms ease;
}
.rw-trigger[data-open="1"] .rw-caret::before,
.rw-row[data-open="1"] .rw-chev::before { margin-top: 2px; transform: rotate(-135deg); }
.rw-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.rw-sr[hidden] { display: none; }

/* ---- surfaces ---------------------------------------------------------------------------------------- */
.rw-surface {
    --rw-more: 28px;
    --rw-pill-h: 26px;
    --rw-gap: var(--space-2);
    --rw-hover: var(--menu-item-hover);
    color: var(--text-primary);
}
.rw-surface[hidden] { display: none; }
/* A pointer open lands on the list itself: a landing point, not a control, so no ring is drawn there. */
.rw-surface:focus { outline: none; }
@keyframes rw-unfold { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
/* The list is a floating sheet with its own material (DESIGN principle 2): the opaque neutral sheet, the float
   shadow and the sheet radius, never the room's ink or a bar's backing, so it stands the same under any header. A
   zero-height anchor under the bar lets it follow the bar's real height; room_work.js measures where the capsules
   are and writes --rw-left, --rw-right and --rw-top (the capsule's bottom edge) on the anchor. */
.rw-shade-anchor { position: relative; flex: 0 0 0; height: 0; z-index: 30; }
.rw-shade {
    --rw-hover: var(--menu-item-hover);
    position: absolute;
    top: calc(var(--rw-top, 0px) + var(--space-2));
    left: var(--rw-left, 0px);
    right: var(--rw-right, 0px);
    box-sizing: border-box;
    max-height: min(60svh, 28rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    border: 1px solid transparent;
    border-radius: var(--radius-sheet);
    background: var(--sheet-float);
    box-shadow: var(--elev-float);
    animation: rw-unfold var(--motion-fade) var(--ease-sheet);
}
/* Main and Project: the header floats over the transcript, so its anchor hangs from the header's bottom edge. */
:is(.chat-page-header, .project-panel-bar) > .rw-shade-anchor { position: absolute; left: 0; right: 0; top: 100%; height: 0; z-index: 30; }
/* Main's history note hangs on the place an open list covers, so it waits behind the list (Project's note is in
   flow): the room's work list, or the consciousness chip's (consciousness_status.js). */
.chat-page-header:is([data-rw-open], [data-cx-shade]) ~ .chat-page-note { visibility: hidden; }
/* A narrow bar's commands are the list's last rows (Main's Restart and budget, page_header.js): the same two nodes
   lie flat here, the budget saying its name and its amount, and an empty slot takes no room. */
.rw-extras { display: flex; flex-direction: column; gap: 2px; padding: 0 var(--space-2) var(--space-3); }
.rw-extras:empty { display: none; }
.rw-extras > :is(.chat-header-btn, .chat-budget-pill).ui-chrome-control {
    display: flex; flex-direction: row; align-items: center; justify-content: space-between; gap: var(--space-3);
    width: 100%; height: auto; min-height: var(--touch-target-min); padding: 0 var(--space-3);
    border-radius: var(--radius-inner); background: transparent; box-shadow: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
    color: var(--text-primary); font-size: var(--type-body); font-weight: 400; text-align: start;
}
@media (hover: hover) {
    .rw-extras > :is(.chat-header-btn, .chat-budget-pill).ui-chrome-control:hover { background: var(--rw-hover); }
}
.rw-extras .chat-budget-word { display: inline; }
.rw-extras .chat-budget-text { font-size: var(--type-body); color: var(--text-meta); }
.rw-extras .chat-budget-bar { display: none; }
/* The Project status bar keeps the room's history note; without one it takes no room. */
.chat-panel-statusbar:not(:has(> :not([hidden]))) { display: none; }

/* ---- the list and its rows ------------------------------------------------------------------------- */
/* A Project's list starts with the room's whole name, which its one-line title may cut: a section title, wrapping as
   far as it needs, its start edge on the rows' text edge (the list's 8px and a row's 8px). With nothing listed the
   name is followed by the note's one quiet sentence, without the empty list's padding between them. */
.rw-title {
    margin: 0;
    padding: var(--space-4) var(--space-4) var(--space-1);
    font-size: var(--type-section);
    font-weight: 500;
    line-height: var(--line-title);
    color: var(--text-primary);
    overflow-wrap: anywhere;
}
.rw-title[hidden] { display: none; }
.rw-shade[data-scope="project"] .rw-list:empty { display: none; }
.rw-list { display: flex; flex-direction: column; gap: 2px; box-sizing: border-box; width: 100%; min-width: 0; padding: 6px var(--space-2) 10px; }
.rw-shade .rw-list { padding: var(--space-2) var(--space-2) var(--space-3); }
.rw-group { padding: var(--space-3) 6px var(--space-1); font-size: var(--type-meta); line-height: var(--line-meta); color: var(--text-meta); }
.rw-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto var(--rw-more);
    grid-template-areas: "main verb more" "detail detail detail";
    column-gap: var(--rw-gap);
    align-items: center;
    padding-right: var(--space-1);
    border-radius: var(--radius-inner);
    /* A calm arrival each time the list opens: rows settle 24ms apart (none under reduced motion). */
    animation: rw-row-in 200ms cubic-bezier(0.2, 0.7, 0.2, 1) backwards;
    animation-delay: calc(var(--rw-i, 0) * 24ms);
}
.rw-row:hover { background: var(--rw-hover); }
@keyframes rw-row-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.rw-main {
    grid-area: main;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    row-gap: 2px;
    align-items: center;
    min-width: 0;
    padding: 9px var(--space-1) 9px var(--space-2);
    border-radius: var(--radius-inner);
    cursor: default;
    -webkit-user-select: text;
    user-select: text;
}
.rw-main:is([data-target="card"], [data-target="history"], [data-target="details"]) { cursor: pointer; }
/* The one standard ring, flush inside; where :has() exists it frames the whole row. */
.rw-main:focus-visible { outline: 2px solid var(--focus-accent-border); outline-offset: -2px; }
@supports selector(:has(*)) {
    .rw-main:focus-visible { outline: none; }
    .rw-row:has(> .rw-main:focus-visible) { outline: 2px solid var(--focus-accent-border); outline-offset: -2px; }
}
.rw-name {
    grid-column: 1;
    grid-row: 1;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
    font-size: var(--type-body);
    font-weight: 400;
    line-height: var(--line-title);
    color: var(--text-primary);
}
/* Keyboard focus on the row (room_work.js marks it) or open details show the whole name; only that row grows. */
.rw-row:is([data-reading="1"], [data-open="1"]) .rw-name { -webkit-line-clamp: 99; line-clamp: none; }
.rw-row[data-settled="1"] .rw-name { color: var(--text-secondary); }
.rw-state {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    column-gap: 6px;
    min-width: 0;
    font-size: var(--type-meta);
    line-height: var(--line-meta);
    color: var(--text-meta);
}
/* One flow: every row says its state under its name, so a long phase never starves the name. */
.rw-row .rw-state { grid-column: 1 / -1; grid-row: 2; }
.rw-word { font-weight: 400; }
/* The word's quieter line (its reason and what still runs, Finalizing… beside an outcome), one accessible status with
   it. The separator ends the word, so a line that wraps under it on a narrow row never starts with a stray dot. */
.rw-secondary { color: var(--text-meta); }
.rw-secondary::before { content: '· '; }
@supports selector(:has(*)) {
    .rw-secondary::before { content: none; }
    .rw-word:has(+ .rw-secondary:not([hidden]))::after { content: '\00a0·'; color: var(--text-meta); font-weight: 400; }
}
/* Where a press found its message is said in the row's details; only the chevron shows, beside the state. */
.rw-where { display: inline-flex; align-items: center; gap: 6px; }
.rw-chev { color: var(--text-primary); }
/* A row's tone is its state's ink: the word takes it and so does the mark before it, while the reason beside it stays
   quieter. Queued, held and unknown keep the state's neutral ink. */
.rw-row[data-tone="live"] .rw-state { color: var(--accent-light); }
.rw-row[data-tone="wait"] .rw-state { color: var(--status-warn-fg); }
.rw-row[data-tone="done"] .rw-state { color: var(--status-ok-fg); }
.rw-row[data-tone="stopped"] .rw-state { color: var(--status-neutral-fg); }
.rw-row[data-tone="failed"] .rw-state { color: var(--status-error-fg); }
.rw-row[data-tone="ended"] .rw-state { color: var(--text-secondary); }
.rw-row:is([data-tone="live"], [data-tone="wait"], [data-tone="done"], [data-tone="stopped"], [data-tone="failed"]) .rw-word {
    font-weight: 500;
}
/* The episode's own focus line (Ouroboros writes it, update_focus): quieter, under the state, at most two lines. */
.rw-row .rw-focus {
    grid-column: 1 / -1;
    grid-row: 3;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
    margin-top: 1px;
    font-size: var(--type-meta);
    line-height: var(--line-meta);
    color: var(--text-secondary);
}
.rw-row .rw-focus[hidden] { display: none; }

/* ---- verbs and the bare ⋯ ---------------------------------------------------------------------------- */
.rw-verb { grid-area: verb; display: inline-flex; align-items: center; gap: var(--space-1); }
.rw-more-slot { grid-area: more; display: inline-flex; align-items: center; justify-content: center; width: var(--rw-more); }
.rw-detail { grid-area: detail; }
.rw-row .rw-pill {
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: var(--rw-pill-h);
    min-height: 0;
    padding: 0 var(--space-3);
    border-radius: var(--pill-radius);
    border: 1px solid transparent;
    font: inherit;
    font-size: var(--button-font-size);
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    transition: var(--motion-tone);
}
.rw-row .rw-pill:not(:disabled):active { filter: var(--press); }
.rw-row .rw-pill:focus-visible { outline: 2px solid var(--focus-accent-border); outline-offset: 2px; }
.rw-row .rw-pill:disabled { opacity: 0.55; cursor: default; box-shadow: none; }
/* The list's controls are the shared capsule tones. View question is warm: it is the owner's turn (the amber tone).
   It goes to the question and never answers it. */
.rw-row .rw-pill-question { background: var(--tone-amber); color: var(--status-warn-fg); font-weight: 600; box-shadow: var(--elev-rest); }
.rw-row .rw-pill-question:not(:disabled):hover { background: var(--tone-amber-hover); }
/* Resume is neutral: safe, and not urgent. */
.rw-row .rw-pill-resume { background: var(--surface-capsule); color: var(--text-primary); box-shadow: var(--elev-rest); }
.rw-row .rw-pill-resume:not(:disabled):hover { background: var(--surface-capsule-hover); }
.rw-more {
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--rw-more);
    height: var(--rw-more);
    padding: 0;
    border: 0;
    border-radius: var(--pill-radius);
    background: transparent;
    color: var(--text-primary);
    cursor: pointer;
}
.rw-more-glyph { fill: currentColor; }
.rw-more:hover { background: rgba(var(--neutral-rgb), 0.1); }
.rw-more[aria-expanded="true"] { background: rgba(var(--neutral-rgb), 0.14); }
.rw-more:focus-visible { outline: 2px solid var(--focus-accent-border); outline-offset: 2px; }

/* ---- details (an explicit press, Enter or Space; never hover) ------------------------------------------ */
.rw-detail {
    margin: 0 var(--space-1) var(--space-2) var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-inner);
    background: rgba(var(--neutral-rgb), 0.05);
    font-size: var(--type-meta);
    line-height: var(--line-meta);
    color: var(--text-meta);
}
.rw-detail[hidden] { display: none; }
.rw-detail-line { margin: 0; }
.rw-detail-line + .rw-detail-line { margin-top: var(--space-1); }
.rw-detail-label { margin-right: 6px; color: var(--text-primary); font-weight: 600; }
.rw-note { margin: 0; padding: var(--space-1) var(--space-4) 10px; font-size: var(--type-meta); line-height: var(--line-meta); color: var(--text-meta); }
.rw-note[hidden] { display: none; }
/* A failed start heads the list: its line and Restart in one row, the boot document's cause behind Details. The
   narrow bar's own Restart row waits meanwhile, so the list offers Restart once. */
.rw-failure { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-3) var(--space-4); }
.rw-failure[hidden], .rw-failure details[hidden] { display: none; }
.rw-failure-lead { margin: 0; color: var(--status-error-fg); font-size: var(--type-body); font-weight: 500; }
.rw-failure:not([hidden]) ~ .rw-extras > [data-chat-command="restart"] { display: none; }

/* ---- Back to reading: the same baseline as ↓ "Scroll to latest message", both quiet navigation -------- */
.rw-back {
    position: absolute;
    z-index: 6;
    left: 0;
    bottom: calc(100% + var(--space-2));
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px var(--space-3);
    border: 1px solid transparent;
    border-radius: var(--pill-radius);
    background: var(--surface-capsule);
    color: var(--text-primary);
    box-shadow: var(--elev-float);
    -webkit-backdrop-filter: var(--glass);
    backdrop-filter: var(--glass);
    font: inherit;
    font-size: var(--type-meta);
    cursor: pointer;
}
.rw-back[hidden] { display: none; }
.rw-back:not(:disabled):hover { background: var(--surface-capsule-hover); }
.rw-back:not(:disabled):active { filter: var(--press); }
.rw-back:disabled { box-shadow: none; }
.rw-back:focus-visible { outline: 2px solid var(--focus-accent-border); outline-offset: 2px; }
.rw-back-glyph { display: inline-flex; }

/* A coarse pointer (a phone) raises the list's own targets with the header family (ui.css). */
@media (pointer: coarse) {
    .rw-surface { --rw-more: var(--touch-target-min); --rw-pill-h: var(--control-h-float); }
    .rw-back { min-height: var(--touch-target-min); }
}
@media (max-width: 900px) {
    .rw-shade { max-height: min(62svh, 28rem); }
}
@media (prefers-reduced-motion: reduce) {
    .rw-shade, .rw-row { animation: none; }
    .rw-caret::before, .rw-chev::before { transition: none; }
}
