Files
LLeMbas-Desktop/ui/app.css
T
HomerandClaude Opus 5.5 d04bc4b6bd
ci / check (push) Failing after 3s
ci / start (push) Failing after 3s
The activity panel, and continuing an agent chat in a terminal
The activity panel beside a chat, from /api/v2's activity routes: Context
(used share, tokens against the window, a segmented bar and a row per
category), Outputs (the files written, lines added and removed, each opening
in the viewer), Background tasks (a team's members as a tree, helpers and
commands: state, elapsed time counting on between reads, model, latest step,
what it is writing, its whole conversation read-only, Stop -- a member after
a confirmation, as it takes those under it), the team's room (who to whom,
reports marked, the words through the page's own markdown) and the plan.

The chat follows GET /chats/{chat}/activity/events while it is open: the strip
above the message box sums the agents up and opens the panel; a new version
reads the panel again while it is open, the costly context part on opening,
after a reply and at most every 15 s. The stream is reopened when the server
closes it, and more slowly while it fails. Beside the chat on a wide window
(the viewer then goes over the chat), over it below --bp-wide, a full-height
sheet at --bp-narrow. The header's Activity button, the palette and Escape
open and close it.

An agent chat with a device session (device_session_id) can be continued in a
terminal: the chat's menu and a header button show 'cd <folder> && lembas -s
<id>' to copy, with lembas the CLI as it starts here (on PATH, else the app's
own copy by its path). Shown rather than run: no terminal opens the same way
on every desktop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 10:50:46 +00:00

1097 lines
52 KiB
CSS

/* LLeMbas Desktop's page. Every colour, size, space and font is a token from tokens.css (the
server's, copied by scripts/fetch-tokens.sh and pinned); the only values of this page's own are
the title bar's height, the setup card's width and the activity panel's number columns, named
below. Media queries cannot read a
custom property, so the two breakpoints are written out and named after the tokens they are:
48rem is --bp-narrow (the sidebar becomes a drawer), 64rem is --bp-wide (the viewer goes over
the chat instead of beside it).
Rows line up by construction: every row of controls sits on a grid with known tracks, a field
is always three parts (label, control, hint) on shared rows, and nothing is aligned by
`baseline` across text of unknown length. */
* { box-sizing: border-box; }
:root {
/* The title bar's height: a fact of this page, not a token the server has. */
--titlebar-h: 36px;
/* The setup card's widest. */
--card-max: 40rem;
/* The sidebar toggle's track in the title bar: none until the sidebar is a drawer. */
--side-btn-w: 0px;
/* The activity panel's number columns: a token count or a share ("180k", "63.0%"), and an
elapsed time ("2h 03m"), in the panel's monospaced figures. */
--num-col: 4.5em;
--time-col: 5.5em;
}
html, body { margin: 0; height: 100%; overflow: hidden; }
body {
background: var(--bg);
color: var(--ink);
font: var(--text-base) / var(--leading-normal) var(--font-body);
}
[hidden] { display: none !important; }
.app { display: grid; grid-template-rows: var(--titlebar-h) minmax(0, 1fr); height: 100vh; }
h1, h2, h3, p { margin: 0; }
.muted { color: var(--ink-muted); font-size: var(--text-sm); }
.is-error { color: var(--warning); }
bdi { unicode-bidi: isolate; }
.icon { width: 16px; height: 16px; fill: currentColor; flex: none; }
/* --- Controls ------------------------------------------------------------------------------ */
.btn {
height: var(--control-h);
padding: 0 var(--sp-4);
display: inline-grid;
grid-auto-flow: column;
gap: var(--sp-2);
align-items: center;
justify-content: center;
font: 600 var(--text-sm) var(--font-ui);
color: var(--ink);
background: var(--surface);
border: var(--border-w) solid var(--border-strong);
border-radius: var(--radius);
box-shadow: var(--shadow-block);
cursor: pointer;
white-space: nowrap;
}
.btn:hover { background: var(--surface-hover); }
.btn:active { transform: translate(var(--press), var(--press)); box-shadow: none; }
.btn:disabled { opacity: var(--opacity-dim); cursor: default; }
.btn--primary { color: var(--accent-ink); background: var(--accent); border-color: var(--accent); }
.btn--primary:hover { background: var(--accent-hover); }
.btn--sm { height: var(--control-h-sm); padding: 0 var(--sp-3); box-shadow: none; }
.btn--danger { color: var(--ink-inverse); background: var(--danger); border-color: var(--danger); }
.btn--danger:hover { background: var(--danger-hover); }
input[type="text"], input[type="number"], select, textarea {
min-width: 0;
font: var(--text-sm) var(--font-ui);
color: var(--ink);
background: var(--bg);
border: var(--border-w) solid var(--border-strong);
border-radius: var(--radius);
}
input[type="text"], input[type="number"], select { height: var(--control-h); padding: 0 var(--control-px-sm); }
textarea { padding: var(--sp-2) var(--control-px-sm); resize: none; line-height: var(--leading-normal); }
:focus-visible { outline: var(--outline-w) solid var(--accent); outline-offset: var(--outline-offset); }
label, .label { font: var(--text-sm) / var(--leading-tight) var(--font-ui); color: var(--ink-muted); }
.hint { font-size: var(--text-sm); color: var(--ink-muted); min-height: calc(var(--text-sm) * var(--leading-normal)); }
.status { font: var(--text-sm) / var(--leading-normal) var(--font-ui); }
.status.is-ok { color: var(--leaf); }
.check { display: grid; grid-template-columns: var(--check-size) minmax(0, 1fr); gap: var(--sp-2); align-items: center; color: var(--ink); }
.check input { width: var(--check-size); height: var(--check-size); margin: 0; accent-color: var(--accent); }
.chip {
display: inline-block;
padding: 0 var(--sp-2);
font: var(--text-xs) / 1.6 var(--font-ui);
color: var(--ink-muted);
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius);
white-space: nowrap;
}
.chip--leaf { color: var(--leaf); border-color: var(--leaf); }
.dot { width: var(--dot-size); height: var(--dot-size); background: var(--ink-faint); flex: none; }
.dot.is-ok { background: var(--leaf); }
.dot.is-busy { background: var(--accent); }
.dot.is-new { background: var(--leaf); }
.dot.is-error { background: var(--warning); }
/* A field is three stacked parts on fixed rows: label, control, hint -- always all three. */
.field { display: grid; grid-template-rows: auto var(--control-h) auto; gap: var(--sp-2); align-content: start; }
.fields {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
grid-auto-rows: auto var(--control-h) auto;
column-gap: var(--sp-4);
row-gap: var(--sp-2);
}
/* Each field spans three shared rows, so side-by-side controls sit on one line whatever their
labels and hints do. */
.fields .field { grid-row: span 3; grid-template-rows: subgrid; }
.row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--sp-2); }
.row--buttons { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr)); gap: var(--sp-2); }
/* A section's heading in brackets, as on the server's screens: `[ SERVER ]`. */
.card h2, .set h2 {
margin: 0 0 var(--sp-3);
font: 600 var(--text-sm) / var(--leading-tight) var(--font-ui);
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--accent);
}
.card h2::before, .set h2::before { content: "[ "; }
.card h2::after, .set h2::after { content: " ]"; }
.code {
font: 700 var(--text-xl) / var(--leading-tight) var(--font-mono);
letter-spacing: 0.15em;
color: var(--leaf);
margin: var(--sp-2) 0 var(--sp-3);
}
/* --- The title bar ------------------------------------------------------------------------- */
.bar {
display: grid;
/* side · leaf · name · host (the rest, and the drag area) · update · agent · menu · sep · 3 controls */
grid-template-columns: var(--side-btn-w) var(--titlebar-h) auto minmax(0, 1fr) auto auto var(--titlebar-h) var(--border-w) repeat(3, 2.875rem);
align-items: center;
height: var(--titlebar-h);
border-bottom: var(--border-w) solid var(--border);
background: var(--bg);
font: 600 var(--text-sm) / 1 var(--font-ui);
user-select: none;
-webkit-user-select: none;
cursor: default;
}
.bar button {
height: var(--titlebar-h);
margin: 0;
padding: 0;
font: inherit;
color: var(--ink-muted);
background: transparent;
border: 0;
border-radius: 0;
cursor: pointer;
}
.bar button:hover { color: var(--ink); background: var(--surface-hover); }
.bar button:focus-visible { outline-offset: calc(-1 * var(--outline-w)); }
.bar__leaf { justify-self: center; width: 18px; height: 18px; }
.bar__name b { color: var(--leaf); font-weight: 700; }
.bar__host {
padding: 0 var(--sp-3);
color: var(--ink-muted);
font-weight: 400;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
align-self: stretch;
display: flex;
align-items: center;
}
.bar__btn { display: grid; place-items: center; }
.bar__side { overflow: hidden; visibility: hidden; }
.bar__agent { display: grid; grid-template-columns: var(--dot-size) auto; gap: var(--sp-2); align-items: center; padding: 0 var(--sp-3) !important; font-weight: 400 !important; }
.bar__agent[data-state="running"] .dot, .bar__agent[data-state="elsewhere"] .dot { background: var(--leaf); }
.bar__agent[data-state="starting"] .dot, .bar__agent[data-state="connecting"] .dot { background: var(--accent); }
.bar__agent[data-state="failed"] .dot, .bar__agent[data-state="not_linked"] .dot, .bar__agent[data-state="no_folders"] .dot,
.bar__agent[data-state="waiting"] .dot, .bar__agent[data-state="refused"] .dot, .bar__agent[data-state="elsewhere_waiting"] .dot { background: var(--warning); }
.bar__update { padding: 0 var(--sp-3) !important; color: var(--accent-ink) !important; background: var(--accent) !important; }
.bar__update:hover { background: var(--accent-hover) !important; }
/* Hidden keeps its grid track (auto, so zero wide): the slots after it never move. */
.bar__update[hidden] { display: block !important; width: 0; padding: 0 !important; overflow: hidden; visibility: hidden; }
.bar__sep { width: var(--border-w); height: var(--sp-5); background: var(--border); }
.bar__btn--close:hover { color: var(--ink-inverse) !important; background: var(--danger) !important; }
/* --- Setup --------------------------------------------------------------------------------- */
.setup { overflow: auto; display: grid; place-items: center; padding: var(--sp-4); }
.card {
width: min(100%, var(--card-max));
display: grid;
gap: var(--sp-6);
padding: var(--sp-8);
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow-block-lg);
}
.setup__body { display: grid; gap: var(--sp-6); }
.setup__body section { display: grid; gap: var(--sp-3); }
.brand { display: grid; grid-template-columns: var(--sp-10) minmax(0, 1fr); gap: var(--sp-3); align-items: center; }
.brand img { width: var(--sp-10); height: var(--sp-10); }
.brand h1 { font: 600 var(--text-xl) / var(--leading-tight) var(--font-ui); }
.brand h1 b { color: var(--leaf); font-weight: 700; }
/* --- The shell ----------------------------------------------------------------------------- */
.shell {
position: relative;
min-height: 0;
display: grid;
grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
grid-template-areas: "side main";
}
.shell[data-viewer="true"] {
grid-template-columns: var(--sidebar-width) minmax(0, 1fr) minmax(var(--inspector-width-min), 42%);
grid-template-areas: "side main viewer";
}
/* The sidebar: the switch, the new-chat button, the chats, then the two explorers (each a row of
its own that takes room only while open), then settings. */
.side {
grid-area: side;
min-height: 0;
display: grid;
--local-row: auto;
--server-row: auto;
grid-template-rows: auto auto minmax(calc(3 * var(--row-h)), 2fr) var(--local-row) var(--server-row) auto;
border-right: var(--border-w) solid var(--border);
background: var(--bg-sunken);
}
.side[data-local="true"] { --local-row: minmax(calc(4 * var(--row-h)), 1.5fr); }
.side[data-server="true"] { --server-row: minmax(calc(4 * var(--row-h)), 1.5fr); }
.switch { display: grid; grid-template-columns: 1fr 1fr; border-bottom: var(--border-w) solid var(--border); }
.switch button {
height: var(--control-h);
font: 600 var(--text-sm) var(--font-ui);
color: var(--ink-muted);
background: transparent;
border: 0;
border-bottom: var(--border-w-accent) solid transparent;
border-radius: var(--radius-none);
cursor: pointer;
}
.switch button[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); background: var(--surface); }
.side__new { margin: var(--sp-3); }
.list { list-style: none; margin: 0; padding: 0 0 var(--sp-2); overflow: auto; min-height: 0; }
.list__head { padding: var(--sp-2) var(--sp-3) var(--sp-1); font: 600 var(--text-xs) var(--font-ui); text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-faint); }
.list__note { padding: var(--sp-3); font-size: var(--text-sm); color: var(--ink-muted); }
.list__more { margin: var(--sp-2) var(--sp-3); }
/* A chat's row: dot · title · when, and under them its folder -- fixed tracks, so rows with long
titles or no folder keep their dots and times on the same lines. */
.chatrow {
width: 100%;
display: grid;
grid-template-columns: var(--dot-size) minmax(0, 1fr) auto;
grid-template-rows: auto auto;
column-gap: var(--sp-2);
align-items: center;
min-height: var(--row-h);
padding: var(--sp-2) var(--sp-3);
text-align: left;
font: var(--text-sm) / var(--leading-tight) var(--font-ui);
color: var(--ink);
background: transparent;
border: 0;
border-left: var(--border-w-accent) solid transparent;
cursor: pointer;
}
.chatrow:hover { background: var(--surface-hover); }
.chatrow[aria-current="true"] { background: var(--surface-active); border-left-color: var(--accent); }
.chatrow.is-unread .row__title { font-weight: 700; }
.row__title, .row__sub { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.row__when { font-size: var(--text-xs); color: var(--ink-faint); }
.row__sub { grid-column: 2 / 4; font-size: var(--text-xs); color: var(--ink-muted); direction: rtl; text-align: left; }
/* The explorers. */
.pane { min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr); border-top: var(--border-w) solid var(--border); }
.pane[data-open="false"] .pane__body { display: none; }
.pane__head { display: grid; grid-template-columns: minmax(0, 1fr) var(--control-h-sm); align-items: center; }
.pane__toggle {
height: var(--control-h);
display: grid;
grid-template-columns: 1rem minmax(0, 1fr);
gap: var(--sp-1);
align-items: center;
padding: 0 var(--sp-3);
text-align: left;
font: 600 var(--text-xs) var(--font-ui);
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--ink-muted);
background: transparent;
border: 0;
cursor: pointer;
}
.pane__toggle[aria-expanded="true"] .tree__chevron .icon { transform: rotate(90deg); }
.pane__btn {
width: var(--control-h-sm);
height: var(--control-h-sm);
display: grid;
place-items: center;
padding: 0;
color: var(--ink-muted);
background: transparent;
border: 0;
border-radius: var(--radius);
cursor: pointer;
}
.pane__btn:hover { color: var(--ink); background: var(--surface-hover); }
.pane__body { min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr); }
.pane__where { padding: 0 var(--sp-3) var(--sp-2); }
.pane__where:empty { display: none; }
.pane__pick { width: 100%; height: var(--control-h-sm); }
.pane__path { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; direction: rtl; text-align: left; font: var(--text-xs) var(--font-ui); color: var(--ink-muted); }
.pane__tree { overflow: auto; min-height: 0; padding-bottom: var(--sp-2); }
.pane__note { padding: var(--sp-2) var(--sp-3); font-size: var(--text-sm); color: var(--ink-muted); }
.pane__tree > .btn { margin: 0 var(--sp-3); }
.tree, .tree ul { list-style: none; margin: 0; padding: 0; }
.tree__row {
width: 100%;
height: var(--row-h);
display: grid;
grid-template-columns: calc(var(--depth, 0) * var(--sp-3)) 1rem 1rem minmax(0, 1fr) auto;
gap: var(--sp-1);
align-items: center;
padding: 0 var(--sp-3);
text-align: left;
font: var(--text-xs) var(--font-ui);
color: var(--ink);
background: transparent;
border: 0;
cursor: pointer;
}
/* The indent's track needs an item of its own, or the chevron would sit in it. */
.tree__row::before { content: ""; }
.tree__row:hover { background: var(--surface-hover); }
.tree__row .icon { color: var(--ink-muted); }
.tree__chevron { display: grid; place-items: center; width: 1rem; }
.tree__chevron .icon { width: 12px; height: 12px; transition: transform var(--dur-1) var(--ease-out); }
.tree__row[aria-expanded="true"] .tree__chevron .icon { transform: rotate(90deg); }
.tree__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.tree__size { color: var(--ink-faint); }
.tree__note { padding: var(--sp-1) var(--sp-3); font-size: var(--text-xs); color: var(--ink-muted); }
.side__settings {
display: grid;
grid-template-columns: 16px minmax(0, 1fr);
gap: var(--sp-2);
align-items: center;
height: var(--control-h);
padding: 0 var(--sp-3);
text-align: left;
font: var(--text-sm) var(--font-ui);
color: var(--ink-muted);
background: transparent;
border: 0;
border-top: var(--border-w) solid var(--border);
cursor: pointer;
}
.side__settings:hover { color: var(--ink); background: var(--surface-hover); }
.scrim { display: none; }
/* --- The chat ------------------------------------------------------------------------------ */
.main, .settings { grid-area: main; min-width: 0; min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr) auto auto auto; }
.settings { grid-template-rows: auto minmax(0, 1fr); }
.head {
display: grid;
grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto;
gap: var(--sp-3);
align-items: center;
min-height: var(--header-height);
padding: 0 var(--sp-4);
border-bottom: var(--border-w) solid var(--border);
}
.head__title { font: 600 var(--text-md) / var(--leading-tight) var(--font-ui); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.head__place { display: flex; gap: var(--sp-2); align-items: center; min-width: 0; }
.head__dir { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; direction: rtl; text-align: left; font: var(--text-xs) var(--font-ui); color: var(--ink-muted); min-width: 0; }
.head__tools { display: flex; gap: var(--sp-2); align-items: center; }
.head__mode { height: var(--control-h-sm); }
.thread { overflow: auto; min-height: 0; padding: var(--sp-4); scroll-behavior: auto; }
.thread > * { max-width: var(--thread-max-width); margin-left: auto; margin-right: auto; }
.thread__note { padding: var(--sp-2) 0; font-size: var(--text-sm); }
.thread__loading { padding: var(--sp-4) 0; }
.thread__earlier { display: block; margin: 0 auto var(--sp-4); }
.messages { display: grid; gap: var(--sp-5); }
.new { display: grid; gap: var(--sp-4); padding: var(--sp-4) 0; }
.new__warn { color: var(--warning); font-size: var(--text-sm); }
.new select { width: 100%; }
.msg { display: grid; gap: var(--sp-3); min-width: 0; }
.msg--user {
justify-self: end;
max-width: min(100%, var(--w-prose));
padding: var(--sp-3) var(--sp-4);
background: var(--bubble-user);
border: var(--border-w) solid var(--border);
border-radius: var(--radius-lg);
}
.msg--user.is-queued { opacity: var(--opacity-dim); }
.msg--user.is-machine { justify-self: stretch; max-width: none; font-size: var(--text-sm); color: var(--ink-muted); background: transparent; }
.msg__text { white-space: pre-wrap; overflow-wrap: anywhere; }
.msg__files { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: var(--sp-1); font-size: var(--text-xs); }
.msg__note { font-size: var(--text-sm); color: var(--ink-muted); }
.msg__foot { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; }
.msg__files .chip { max-width: var(--w-chip); overflow: hidden; text-overflow: ellipsis; }
/* Under a message: small icon buttons, all one size. */
.msg__actions { display: grid; grid-auto-flow: column; grid-auto-columns: var(--control-h-sm); justify-content: start; }
.msg--user .msg__actions { justify-content: end; margin-top: calc(-1 * var(--sp-2)); }
.msg__act { display: grid; place-items: center; width: var(--control-h-sm); height: var(--control-h-sm); padding: 0; color: var(--ink-faint); background: none; border: 0; border-radius: var(--radius); cursor: pointer; }
.msg__act:hover, .msg__act:focus-visible { color: var(--ink); background: var(--surface-hover); }
/* Regenerate: on the last reply only, and not while it is being written. */
.msg__regen { display: none; }
.messages > .msg--assistant:last-child:not(.is-live) .msg__regen { display: grid; }
.msg__editor { display: grid; gap: var(--sp-2); }
.msg__edit { width: 100%; min-height: var(--control-h); font: var(--text-base) / var(--leading-normal) var(--font-body); resize: vertical; }
.msg__editbar { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(min(100%, 6rem), auto); gap: var(--sp-2); justify-content: end; }
.chip--model { display: inline-grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-1); align-items: center; max-width: var(--w-chip); padding-left: var(--sp-1); }
.chip--model > span:last-child { overflow: hidden; text-overflow: ellipsis; }
/* A model's avatar: its initial on its own colour. Only the hue is the model's (models.js, the
server's stable_hue); how strong it is comes from the theme, by mixing it into the surface. */
.avatar {
display: inline-grid;
place-items: center;
width: 1.25rem;
height: 1.25rem;
font: 700 var(--text-xs) / 1 var(--font-ui);
color: var(--ink);
background: color-mix(in oklab, hsl(var(--avatar-hue, 40) 100% 50%) 32%, var(--surface));
border-radius: var(--radius-sm);
flex: none;
}
.avatar--none { background: var(--surface-active); }
/* A model's own picture fills the same square, cut to it: the avatar's size does not depend on it. */
.avatar--image { overflow: hidden; background: var(--surface); }
.avatar--image img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* The Thinking switch: a checkbox drawn as a switch, then its words, on two known tracks. The
label is the tap target; the whole of it is a control's height. */
.toggle {
display: inline-grid;
grid-template-columns: calc(2 * var(--check-size)) auto;
gap: var(--sp-2);
align-items: center;
height: var(--control-h);
color: var(--ink);
font: var(--text-sm) var(--font-ui);
cursor: pointer;
white-space: nowrap;
}
.toggle--compact { height: var(--control-h-sm); }
.toggle input {
appearance: none;
position: relative;
width: calc(2 * var(--check-size));
height: var(--check-size);
margin: 0;
background: var(--surface-active);
border: var(--border-w) solid var(--border-strong);
border-radius: var(--radius-full);
cursor: pointer;
transition: background var(--dur-1) var(--ease-out);
}
.toggle input::after {
content: "";
position: absolute;
top: var(--border-w);
left: var(--border-w);
width: calc(var(--check-size) - 4 * var(--border-w));
height: calc(var(--check-size) - 4 * var(--border-w));
background: var(--ink-muted);
border-radius: var(--radius-full);
transition: transform var(--dur-1) var(--ease-out);
}
.toggle input:checked { background: var(--accent); border-color: var(--accent); }
.toggle input:checked::after { background: var(--accent-ink); transform: translateX(var(--check-size)); }
/* The team's size: two number fields of one width with a dash between them, and in the header
its word before them -- fixed tracks, so nothing moves when a number grows a digit. */
.agents {
display: inline-grid;
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
gap: var(--sp-2);
align-items: center;
width: 100%;
height: var(--control-h);
}
.agents__dash { color: var(--ink-muted); }
.agents__input { width: 100%; min-width: 0; height: var(--control-h); padding: 0 var(--control-px-sm); }
.agents--compact {
grid-template-columns: auto calc(var(--w-field-xs) / 2) auto calc(var(--w-field-xs) / 2);
width: auto;
height: var(--control-h-sm);
font: var(--text-sm) var(--font-ui);
}
.agents--compact .agents__input { height: var(--control-h-sm); }
.agents__label { color: var(--ink-muted); white-space: nowrap; }
/* The model picker: a button the height of a control, avatar · name · chevron. */
.picker {
display: inline-grid;
grid-template-columns: auto minmax(0, 1fr) auto;
gap: var(--sp-2);
align-items: center;
height: var(--control-h);
max-width: 100%;
padding: 0 var(--control-px-sm);
font: var(--text-sm) var(--font-ui);
color: var(--ink);
text-align: left;
background: var(--bg);
border: var(--border-w) solid var(--border-strong);
border-radius: var(--radius);
cursor: pointer;
}
.picker__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.head__model { display: flex; gap: var(--sp-2); align-items: center; min-width: 0; }
.head__model .picker { height: var(--control-h-sm); max-width: var(--w-chip); }
.new .picker { width: 100%; }
.msg__metrics { font: var(--text-xs) var(--font-ui); color: var(--ink-faint); }
.msg__steps { display: grid; gap: var(--sp-3); min-width: 0; }
.msg__status, .msg__thinking { font: var(--text-xs) var(--font-ui); }
.msg__status.is-error { color: var(--warning); }
.msg__jobs { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--sp-1); font-size: var(--text-sm); }
.job { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-2); align-items: center; }
.step--thinking summary, .call__head { cursor: pointer; font: var(--text-sm) var(--font-ui); color: var(--ink-muted); }
.step__pre { margin-top: var(--sp-2); padding-left: var(--sp-3); border-left: var(--border-w-thick) solid var(--border); white-space: pre-wrap; font-size: var(--text-sm); color: var(--ink-muted); }
.step--tools { display: grid; gap: var(--sp-1); }
.step--steer { padding-left: var(--sp-3); border-left: var(--border-w-thick) solid var(--accent-soft); }
.steer__text { white-space: pre-wrap; font-size: var(--text-sm); }
.call { border: var(--border-w) solid var(--border); border-radius: var(--radius); background: var(--surface); min-width: 0; }
.call.is-failed { border-color: var(--warning); }
/* The call's line: dot · title · chips, on fixed tracks. */
.call__head {
list-style: none;
display: grid;
grid-template-columns: var(--dot-size) minmax(0, 1fr) auto;
gap: var(--sp-2);
align-items: center;
min-height: var(--row-h);
padding: 0 var(--sp-3);
}
.call__head::-webkit-details-marker { display: none; }
.call__title { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--ink); font-family: var(--font-mono); }
.call__chips { display: flex; gap: var(--sp-1); }
.call__body { display: grid; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3) var(--sp-3); border-top: var(--border-w) solid var(--border); }
.call__pre, .md-code pre {
margin: 0;
padding: var(--sp-2) var(--sp-3);
max-height: var(--h-list);
overflow: auto;
font: var(--text-xs) / var(--leading-normal) var(--font-mono);
background: var(--code-bg);
border: var(--border-w) solid var(--code-border);
border-radius: var(--radius);
white-space: pre;
}
.call__open { justify-self: start; margin: 0 var(--sp-3) var(--sp-3); }
/* Markdown, drawn by markdown.js from model output (never as HTML). */
.md { overflow-wrap: anywhere; min-width: 0; }
.md > * + * { margin-top: var(--sp-3); }
.md h1, .md h2, .md h3, .md h4, .md h5, .md h6 { font-family: var(--font-ui); line-height: var(--leading-tight); }
.md h1 { font-size: var(--text-xl); }
.md h2 { font-size: var(--text-lg); }
.md h3 { font-size: var(--text-md); }
.md h4, .md h5, .md h6 { font-size: var(--text-base); }
.md ul, .md ol { margin: 0; padding-left: var(--sp-6); }
.md li + li { margin-top: var(--sp-1); }
.md-task { list-style: none; margin-left: calc(-1 * var(--sp-5)); }
.md-task input { margin: 0 var(--sp-2) 0 0; }
.md blockquote { margin: 0; padding-left: var(--sp-3); border-left: var(--border-w-thick) solid var(--border-strong); color: var(--ink-muted); }
.md hr { border: 0; border-top: var(--border-w) solid var(--border); }
.md code { font: 0.9em var(--font-mono); padding: 0 var(--sp-1); background: var(--code-bg); border-radius: var(--radius-sm); }
.md pre code { padding: 0; background: none; }
.md a { color: var(--accent); }
.md a.md-unsafe { color: var(--ink-muted); text-decoration: line-through dotted; }
.md-code { position: relative; display: grid; }
.md-lang { position: absolute; top: var(--sp-1); left: var(--sp-3); font: var(--text-xs) var(--font-ui); color: var(--ink-faint); }
.md-code:has(.md-lang) pre { padding-top: var(--sp-6); }
.md-copy {
position: absolute;
top: var(--sp-1);
right: var(--sp-1);
height: var(--control-h-sm);
padding: 0 var(--sp-2);
font: var(--text-xs) var(--font-ui);
color: var(--ink-muted);
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius);
cursor: pointer;
}
/* A wide table scrolls by itself; the page does not. */
.md-table { overflow-x: auto; }
.md table { border-collapse: collapse; font-size: var(--text-sm); }
.md th, .md td { padding: var(--sp-1) var(--sp-3); border: var(--border-w) solid var(--border); text-align: left; vertical-align: top; }
.md [data-align="center"] { text-align: center; }
.md [data-align="right"] { text-align: right; }
.md th { background: var(--surface); }
/* Cards: what waits for an answer, above the composer. */
.cards { overflow: auto; max-height: 50vh; padding: var(--sp-3) var(--sp-4) 0; display: grid; gap: var(--sp-3); }
.ask {
width: 100%;
max-width: var(--thread-max-width);
margin: 0 auto;
display: grid;
gap: var(--sp-3);
padding: var(--sp-3) var(--sp-4);
background: var(--surface-raised);
border: var(--border-w-accent) solid var(--accent);
border-radius: var(--radius);
box-shadow: var(--shadow-block-accent);
}
.ask__item { display: grid; gap: var(--sp-2); }
.ask__header { font: 600 var(--text-xs) var(--font-ui); text-transform: uppercase; letter-spacing: 0.08em; color: var(--accent); }
.ask__title { font-weight: 600; }
.ask__edit { width: 100%; font-family: var(--font-mono); }
.ask__text { width: 100%; }
.ask__options { display: grid; gap: var(--sp-2); }
.opt {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
grid-template-rows: auto auto;
column-gap: var(--sp-2);
align-items: center;
min-height: var(--control-h);
padding: var(--sp-2) var(--sp-3);
text-align: left;
font: var(--text-sm) var(--font-body);
color: var(--ink);
background: var(--bg);
border: var(--border-w) solid var(--border-strong);
border-radius: var(--radius);
cursor: pointer;
}
.opt[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); }
.opt__desc { grid-column: 1; font-size: var(--text-xs); color: var(--ink-muted); }
.opt .chip { grid-column: 2; grid-row: 1; }
.ask__actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 8rem), 1fr)); gap: var(--sp-2); }
.ask__said { font-size: var(--text-sm); color: var(--warning); }
.ask__said:empty { display: none; }
.plan { margin: 0; padding-left: var(--sp-5); display: grid; gap: var(--sp-2); font-size: var(--text-sm); }
.plan ul { margin: var(--sp-1) 0 0; padding-left: var(--sp-4); }
.plan__phase { font-weight: 600; }
.plan__task.is-done { text-decoration: line-through; color: var(--ink-muted); }
/* The composer: the files it carries on a row of their own, then three tracks -- the attach
button, the text, the send buttons. The buttons are one control high and sit at the foot of
the row the text sets the height of: their size is known, the text's is not. */
.composer {
position: relative;
display: grid;
grid-template-columns: var(--control-h) minmax(0, 1fr) auto;
grid-template-areas: "files files files" "attach text actions";
/* No gap between the rows: the files' row has its own margin, and none while it is empty. */
gap: 0 var(--sp-2);
padding: var(--sp-3) var(--sp-4) var(--sp-4);
}
.composer[data-attach="false"] { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "files files" "text actions"; }
.composer > * { min-width: 0; }
.composer textarea { grid-area: text; width: 100%; min-height: var(--control-h); max-height: 40vh; font: var(--text-base) / var(--leading-normal) var(--font-body); }
.composer__attach { grid-area: attach; align-self: end; width: var(--control-h); padding: 0; }
.composer__actions { grid-area: actions; align-self: end; display: grid; grid-auto-flow: column; gap: var(--sp-2); }
.composer__files { grid-area: files; margin: 0 0 var(--sp-2); padding: 0; list-style: none; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--w-chip)), 1fr)); gap: var(--sp-2); }
/* One file: name · size or state · remove, the same tracks on every row. */
.file {
display: grid;
grid-template-columns: minmax(0, 1fr) auto var(--control-h-sm);
gap: var(--sp-2);
align-items: center;
height: var(--control-h-sm);
padding-left: var(--sp-2);
font: var(--text-xs) var(--font-ui);
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius);
}
.file__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--ink); }
.file__state { color: var(--ink-muted); max-width: var(--w-chip); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.file.is-failed { border-color: var(--warning); }
.file.is-failed .file__state { color: var(--warning); }
.file__remove { display: grid; place-items: center; height: 100%; padding: 0; color: var(--ink-muted); background: none; border: 0; border-left: var(--border-w) solid var(--border); cursor: pointer; }
.file__remove:hover { color: var(--ink); background: var(--surface-hover); }
/* Files dragged over the window: where they will go, over the whole composer. */
.composer__drop { display: none; }
.composer[data-drag="true"] .composer__drop {
display: grid;
place-items: center;
position: absolute;
inset: var(--sp-2) var(--sp-4);
z-index: var(--z-raised);
font: 600 var(--text-sm) var(--font-ui);
color: var(--accent);
background: var(--accent-soft);
border: var(--border-w-thick) dashed var(--accent);
border-radius: var(--radius);
}
/* --- Settings ------------------------------------------------------------------------------ */
.settings__body { overflow: auto; padding: var(--sp-4); display: grid; gap: var(--sp-6); align-content: start; }
.settings__body > .set { width: 100%; max-width: var(--w-prose); margin: 0 auto; display: grid; gap: var(--sp-3); }
/* The folder list: every row the same two tracks, the path cut from the left (its end tells
folders apart), the full path in the title. */
.folders { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-2); }
.folders li {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: var(--sp-2);
align-items: center;
min-height: var(--control-h);
padding: 0 0 0 var(--control-px);
background: var(--bg);
border: var(--border-w) solid var(--border);
border-radius: var(--radius);
}
.folders .path { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; direction: rtl; text-align: left; font: var(--text-sm) var(--font-ui); }
.folders .btn { height: calc(var(--control-h) - 2 * var(--border-w)); box-shadow: none; border-width: 0 0 0 var(--border-w); }
.folders .empty { padding: var(--control-px); color: var(--warning); font-size: var(--text-sm); }
/* --- The viewer ---------------------------------------------------------------------------- */
.viewer {
grid-area: viewer;
min-width: 0;
min-height: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
border-left: var(--border-w) solid var(--border);
background: var(--bg);
}
.viewer__head {
display: grid;
grid-template-columns: minmax(0, 1fr) auto var(--control-h-sm);
gap: var(--sp-2);
align-items: center;
min-height: var(--header-height);
padding: 0 var(--sp-3);
border-bottom: var(--border-w) solid var(--border);
}
.viewer__id { min-width: 0; }
.viewer__name { font: 600 var(--text-sm) / var(--leading-tight) var(--font-ui); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.viewer__where { font: var(--text-xs) var(--font-ui); color: var(--ink-muted); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.viewer__modes { display: grid; grid-auto-flow: column; }
.seg {
height: var(--control-h-sm);
padding: 0 var(--sp-3);
font: var(--text-xs) var(--font-ui);
color: var(--ink-muted);
background: var(--surface);
border: var(--border-w) solid var(--border);
cursor: pointer;
}
.seg + .seg { border-left: 0; }
.seg[aria-pressed="true"] { color: var(--ink); background: var(--surface-active); }
.viewer__body { min-height: 0; display: grid; }
.viewer__code { min-height: 0; overflow: hidden; }
.viewer__code .cm-editor { height: 100%; }
.viewer__md { overflow: auto; padding: var(--sp-4); }
.viewer__frame { width: 100%; height: 100%; border: 0; background: var(--surface); }
.viewer__note { padding: var(--sp-4); font-size: var(--text-sm); color: var(--ink-muted); }
.viewer__note.is-error { color: var(--warning); }
/* --- The activity panel -------------------------------------------------------------------------
Beside the chat, the inspector's width; while the viewer is open too, the viewer goes over the
chat (it was opened from here, and three columns of text beside the sidebar do not fit). Over
the chat from --bp-wide down, as the viewer is; at --bp-narrow that is a full-height sheet. */
.shell[data-activity="true"],
.shell[data-activity="true"][data-viewer="true"] {
grid-template-columns: var(--sidebar-width) minmax(0, 1fr) var(--inspector-width);
grid-template-areas: "side main activity";
}
.shell[data-activity="true"] .viewer { grid-area: main; z-index: var(--z-panel); border-left: 0; }
.activity {
grid-area: activity;
min-width: 0;
min-height: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
border-left: var(--border-w) solid var(--border);
background: var(--bg);
}
/* Title and close on two known tracks; with an agent's conversation open, Back before them. */
.activity__head { grid-template-columns: minmax(0, 1fr) var(--control-h-sm); }
.activity[data-view="agent"] .activity__head { grid-template-columns: var(--control-h-sm) minmax(0, 1fr) var(--control-h-sm); }
.activity__body { overflow: auto; min-height: 0; display: grid; align-content: start; font-size: var(--text-sm); }
.activity__section { padding: var(--sp-3); border-bottom: var(--border-w) solid var(--border); min-width: 0; }
.activity__section:last-child { border-bottom: 0; }
.activity__heading, .activity__subheading {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: var(--sp-2);
align-items: center;
margin: 0;
font: 600 var(--text-sm) / var(--leading-tight) var(--font-ui);
color: var(--ink);
cursor: pointer;
}
.activity__heading::-webkit-details-marker { display: none; }
.activity__section[open] > .activity__heading { margin-bottom: var(--sp-3); }
.activity__subheading { margin: var(--sp-4) 0 var(--sp-2); cursor: default; }
.activity__count { font: var(--text-xs) var(--font-mono); color: var(--ink-muted); }
.activity__empty { margin: 0; padding: var(--sp-1) 0; color: var(--ink-faint); font-size: var(--text-sm); }
.activity__empty.is-error { color: var(--warning); padding: var(--sp-3); }
.activity__plan { min-width: 0; }
/* Context: the line, the bar, then one row per category on four fixed tracks -- swatch, the name
that gives, and two number columns -- so the numbers stand in columns in any language. The
categories' colours are tokens, one each. */
.ctx { display: grid; gap: var(--sp-2); }
.ctx__used { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--sp-2); margin: 0; color: var(--ink); }
.ctx__of { font-family: var(--font-mono); color: var(--ink-muted); }
.ctx__bar { display: flex; height: var(--sp-2); overflow: hidden; background: var(--surface-active); border-radius: var(--radius-full); }
.ctx__seg { flex: none; width: var(--share, 0); background: var(--ctx, var(--ink-faint)); }
.ctx__rows { display: grid; margin: 0; padding: 0; list-style: none; }
.ctx__row {
display: grid;
grid-template-columns: var(--sp-3) minmax(0, 1fr) var(--num-col) var(--num-col);
gap: var(--sp-2);
align-items: center;
min-height: var(--row-h);
}
.ctx__key { width: var(--sp-3); height: var(--sp-3); border-radius: var(--radius-sm); background: var(--ctx, var(--ink-faint)); }
.ctx--free.ctx__key { background: transparent; border: var(--border-w) solid var(--border-strong); }
.ctx__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--ink-muted); }
.ctx__num { font: var(--text-xs) var(--font-mono); text-align: right; color: var(--ink-muted); }
.ctx__note { margin: 0; font-size: var(--text-xs); color: var(--ink-faint); }
.ctx--messages { --ctx: var(--accent); }
.ctx--system { --ctx: var(--ink-muted); }
.ctx--memory { --ctx: var(--warning); }
.ctx--instructions { --ctx: var(--success); }
.ctx--tools { --ctx: var(--ink); }
.ctx--mcp_tools { --ctx: var(--danger); }
.ctx--since { --ctx: var(--ink-faint); }
.ctx--buffer { --ctx: var(--border-strong); }
.ctx--free { --ctx: transparent; }
/* Outputs: icon, the name (cut, never wrapped), the lines added and removed. */
.outs { display: grid; margin: 0; padding: 0; list-style: none; }
.outs__row { display: grid; grid-template-columns: var(--sp-4) minmax(0, 1fr) auto; gap: var(--sp-2); align-items: center; min-height: var(--row-h); color: var(--ink-muted); }
.outs__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 0; font: var(--text-sm) var(--font-ui); color: var(--ink); text-align: left; background: none; border: 0; cursor: pointer; }
.outs__name:hover { color: var(--accent); text-decoration: underline; }
.outs__diff { display: grid; grid-auto-flow: column; gap: var(--sp-2); font: var(--text-xs) var(--font-mono); }
.outs__add { color: var(--success); }
.outs__del { color: var(--danger); }
/* Background tasks. An agent's row is one grid of known tracks: state dot, what it is (the one
track that gives, cut), elapsed time in a column of its own width, the stop button at its size
(an empty box where there is none) -- so rows line up whatever each says. */
.tasks { display: grid; gap: var(--sp-2); min-width: 0; }
.tasks__summary, .tasks__team { margin: 0; color: var(--ink-muted); }
.tasks__goal { margin: 0; color: var(--ink); }
.tasks__list { display: grid; gap: var(--sp-1); }
.tasks__more > summary { cursor: pointer; padding: var(--sp-1) 0; color: var(--ink-muted); }
.tasks__more[open] > summary { margin-bottom: var(--sp-1); }
.agent { margin-left: calc(var(--depth, 0) * var(--sp-4)); background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius); min-width: 0; }
.agent__row {
display: grid;
grid-template-columns: var(--dot-size) minmax(0, 1fr) var(--time-col) var(--control-h-sm);
gap: var(--sp-2);
align-items: center;
min-height: var(--control-h);
padding: 0 var(--sp-1) 0 var(--sp-3);
cursor: pointer;
list-style: none;
}
.agent__row::-webkit-details-marker { display: none; }
.agent__dot { width: var(--dot-size); height: var(--dot-size); border-radius: var(--radius-full); background: var(--ink-faint); }
.agent.is-working .agent__dot { background: var(--accent); }
.agent.is-queued .agent__dot, .agent.is-waiting .agent__dot { background: var(--warning); }
.agent.is-done .agent__dot { background: var(--leaf); }
.agent.is-error .agent__dot { background: var(--danger); }
.agent__what { display: grid; min-width: 0; }
.agent__name, .agent__role { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.agent__name { color: var(--ink); }
.agent__role { font-size: var(--text-xs); color: var(--ink-muted); }
.agent__time { font: var(--text-xs) var(--font-mono); text-align: right; color: var(--ink-muted); }
.agent__stop { width: var(--control-h-sm); height: var(--control-h-sm); }
.agent__body { display: grid; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3) var(--sp-3); border-top: var(--border-w) solid var(--border); min-width: 0; }
.agent__body > .btn { justify-self: start; }
.agent__facts { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3); margin: 0; font-size: var(--text-xs); color: var(--ink-muted); }
.agent__state { color: var(--ink); }
.agent__model { font-family: var(--font-mono); overflow-wrap: anywhere; }
.agent__brief { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--ink); }
.agent__step { display: grid; grid-template-columns: var(--dot-size) auto minmax(0, 1fr); gap: var(--sp-2); align-items: center; margin: 0; }
.agent__verb { color: var(--ink); white-space: nowrap; }
.agent__title { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font: var(--text-xs) var(--font-mono); color: var(--ink-muted); }
.agent__text { margin: 0; max-height: calc(6 * var(--row-h)); overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--text-xs); color: var(--ink-muted); }
/* The team's room: who to whom, a report marked, the words as markdown. */
.room { display: grid; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.room__entry { display: grid; gap: var(--sp-1); padding: var(--sp-2) var(--sp-3); background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius); min-width: 0; }
.room__entry.is-report { border-left: var(--border-w-thick) solid var(--leaf); }
.room__meta { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--sp-2); align-items: center; font-size: var(--text-xs); color: var(--ink-muted); }
.room__n { font-family: var(--font-mono); }
.room__route { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.room__badge { color: var(--leaf); border-color: var(--leaf); }
/* One agent's conversation, read-only. */
.convo__head { display: grid; gap: var(--sp-2); padding: var(--sp-3); border-bottom: var(--border-w) solid var(--border); }
.convo__head > .btn { justify-self: start; }
.convo { display: grid; gap: var(--sp-3); padding: var(--sp-3); min-width: 0; }
.convo__turn { font-size: var(--text-sm); }
.convo__tools { display: grid; gap: var(--sp-1); margin: var(--sp-2) 0 0; padding: 0; list-style: none; }
.convo__tool { display: grid; grid-template-columns: var(--dot-size) auto minmax(0, 1fr); gap: var(--sp-2); align-items: center; font-size: var(--text-xs); }
/* The strip above the message box: what the agents are doing, one line, and the way into the panel. */
.strip { padding: var(--sp-2) var(--sp-4) 0; }
.strip__btn {
display: grid;
grid-template-columns: var(--dot-size) minmax(0, 1fr) auto;
gap: var(--sp-2);
align-items: center;
width: 100%;
max-width: var(--thread-max-width);
min-height: var(--control-h);
margin: 0 auto;
padding: 0 var(--control-px-sm);
font: var(--text-sm) var(--font-ui);
color: var(--ink-muted);
text-align: left;
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius);
cursor: pointer;
}
.strip__btn:hover { color: var(--ink); background: var(--surface-hover); }
.strip__text { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* --- Menus, dialogs and the palette ----------------------------------------------------------- */
.menu {
position: fixed;
z-index: var(--z-dropdown);
min-width: 12rem;
max-width: min(var(--w-menu), calc(100vw - 2 * var(--sp-2)));
max-height: min(var(--h-list), calc(100vh - 2 * var(--sp-2)));
overflow: auto;
padding: var(--sp-1) 0;
background: var(--surface-raised);
border: var(--border-w) solid var(--border-strong);
border-radius: var(--radius);
box-shadow: var(--shadow-lg);
}
/* Every item: lead · label · check, on the same tracks, so labels line up with or without a lead. */
.menu__item {
display: grid;
grid-template-columns: 1.25rem minmax(0, 1fr) 1rem;
gap: var(--sp-2);
align-items: center;
width: 100%;
min-height: var(--row-h);
padding: 0 var(--sp-3);
font: var(--text-sm) var(--font-ui);
color: var(--ink);
text-align: left;
background: none;
border: 0;
cursor: pointer;
}
/* A menu where no item has a lead: no lead track either. */
.menu--plain .menu__item { grid-template-columns: minmax(0, 1fr) 1rem; }
.menu--plain .menu__lead { display: none; }
.menu__item:hover, .menu__item:focus-visible { background: var(--surface-hover); outline: none; }
.menu__item:disabled { opacity: var(--opacity-dim); cursor: default; }
.menu__item.is-danger { color: var(--danger); }
.menu__label { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.menu__check { color: var(--accent); }
.menu__sep { height: var(--border-w); margin: var(--sp-1) 0; background: var(--border); }
.dialog {
width: min(var(--w-dialog), calc(100vw - 2 * var(--sp-4)));
padding: 0;
color: var(--ink);
background: var(--surface-raised);
border: var(--border-w) solid var(--border-strong);
border-radius: var(--radius);
box-shadow: var(--shadow-lg);
}
.dialog::backdrop { background: var(--scrim); }
.dialog__body { display: grid; gap: var(--sp-3); padding: var(--sp-4); }
.dialog__title { font: 600 var(--text-md) / var(--leading-tight) var(--font-ui); }
/* A line to copy: as it is, on one line that scrolls on its own rather than the dialog. */
.dialog__code {
margin: 0;
padding: var(--sp-2) var(--sp-3);
overflow-x: auto;
white-space: pre;
font: var(--text-sm) var(--font-mono);
color: var(--ink);
background: var(--code-bg);
border: var(--border-w) solid var(--code-border);
border-radius: var(--radius);
user-select: all;
}
.dialog__input { width: 100%; }
.dialog__actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-2); }
.palette { align-self: start; margin-top: 12vh; }
.palette__line { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-2); align-items: center; padding: 0 var(--sp-3); border-bottom: var(--border-w) solid var(--border); color: var(--ink-muted); }
.palette__input { height: var(--control-h-lg) !important; border: 0 !important; background: none !important; outline: none; font-size: var(--text-base); }
.palette__list { list-style: none; margin: 0; padding: var(--sp-1) 0; max-height: var(--h-list); overflow: auto; }
/* A result: icon · label · hint, the same tracks on every row. */
.palette__item {
display: grid;
grid-template-columns: 1rem minmax(0, 1fr) auto;
gap: var(--sp-3);
align-items: center;
min-height: var(--row-h);
padding: 0 var(--sp-3);
font: var(--text-sm) var(--font-ui);
cursor: pointer;
}
.palette__item[aria-selected="true"] { background: var(--surface-active); }
.palette__icon { display: grid; place-items: center; color: var(--ink-muted); }
.palette__label { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.palette__hint { font-size: var(--text-xs); color: var(--ink-faint); white-space: nowrap; }
.palette__none { padding: var(--sp-3); font-size: var(--text-sm); color: var(--ink-muted); }
/* --- Narrower windows ---------------------------------------------------------------------- */
/* 64rem (--bp-wide): the viewer goes over the chat instead of beside it. */
@media (max-width: 64rem) {
.shell[data-viewer="true"] { grid-template-columns: var(--sidebar-width) minmax(0, 1fr); grid-template-areas: "side main"; }
.viewer { grid-area: main; z-index: var(--z-panel); border-left: 0; }
.shell[data-activity="true"],
.shell[data-activity="true"][data-viewer="true"] { grid-template-columns: var(--sidebar-width) minmax(0, 1fr); grid-template-areas: "side main"; }
.activity { grid-area: main; z-index: var(--z-panel); border-left: 0; }
}
/* 48rem (--bp-narrow): the sidebar is a drawer, opened from the title bar. */
@media (max-width: 48rem) {
:root { --side-btn-w: var(--titlebar-h); }
.bar__side { visibility: visible; }
.bar__host { display: none; }
.shell, .shell[data-viewer="true"],
.shell[data-activity="true"], .shell[data-activity="true"][data-viewer="true"] { grid-template-columns: minmax(0, 1fr); grid-template-areas: "main"; }
.side {
position: absolute;
inset: 0 auto 0 0;
width: min(85vw, var(--sidebar-width));
z-index: var(--z-panel);
transform: translateX(-100%);
transition: transform var(--dur-2) var(--ease-out);
box-shadow: var(--shadow-lg);
}
.shell[data-side="true"] .side { transform: none; }
.shell[data-side="true"] .scrim { display: block; position: absolute; inset: 0; z-index: calc(var(--z-panel) - 1); background: var(--scrim); }
.viewer { z-index: var(--z-panel); }
.head { padding: var(--sp-2) var(--sp-3) 0; grid-template-columns: minmax(0, 1fr) auto; }
.head__place { grid-column: 1 / -1; grid-row: 2; padding-bottom: var(--sp-2); }
/* The chat's controls get a row of their own: beside the title they would squeeze it out. */
.head__tools { grid-column: 1 / -1; grid-row: 3; flex-wrap: wrap; padding-bottom: var(--sp-2); }
.head__tools:empty { display: none; }
.thread { padding: var(--sp-3); }
.composer { padding: var(--sp-2) var(--sp-3) var(--sp-3); }
.strip { padding: var(--sp-2) var(--sp-3) 0; }
.cards { padding: var(--sp-2) var(--sp-3) 0; }
.card { padding: var(--sp-4); gap: var(--sp-4); }
.msg--user { max-width: 100%; }
.palette__hint { display: none; }
.palette__item { grid-template-columns: 1rem minmax(0, 1fr); }
.head__model .picker { max-width: 8rem; }
}
/* Very narrow: the agent's words go, its dot stays; the row's button goes under its field. */
@media (max-width: 30rem) {
#agent-text { display: none; }
.setup .row { grid-template-columns: minmax(0, 1fr); }
.setup .field { grid-template-rows: auto auto auto; }
}