Files
LLeMbas-Desktop/ui/app.css
T
HomerandClaude Opus 5.5 67ce6076c5
ci / ui (push) Waiting to run
ci / check (push) Failing after 4s
ci / start (push) Failing after 4s
Library upload, new folder, rename and delete; a chat's tools and skills (/scope)
Files -> Server writes through /api/v2/files/{upload,mkdir,rename} and
DELETE /files (the trash); upload is a Rust command (library_upload) that
picks files in the system dialog and sends them multipart in the field
`files`. A chat's menu opens Tools and skills..., the web's chat-settings
switches over GET/POST /chats/{id}/scope and /allow/clear. A popover can
open at a context menu's point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 13:53:38 +00:00

1806 lines
91 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 few values of this page's own are
named in :root below, each derived from a token where one exists. 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 panels go over the
chat instead of beside it).
The shape is the one people know from AI desktop apps: a slim sidebar (New chat, search, the
pages, the chats by day, Settings at the foot), and a chat page with the conversation in a
centred column and the composer under it, the chat's settings as chips in the composer.
Reading text is set in --font-body; the chrome's small print (chips, metadata, code) in the
brand's monospace.
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. Spaces are the 4 px scale (--sp-*). */
* { 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's width, the server's. */
--side-w: var(--sidebar-width);
/* The softer corner of the composer and the message bubbles: twice the theme's largest. */
--radius-soft: calc(2 * var(--radius-xl));
/* The conversation's column and the composer under it: the server's thread width. */
--column: var(--thread-max-width);
/* A pill in the title bar (the agent's chip, the update button): inside the bar's height, which
the touch sizes of the controls would otherwise outgrow. */
--bar-chip-h: calc(var(--titlebar-h) - 2 * var(--sp-1));
/* A chip under the message box: a small control's height. */
--chip-h: var(--control-h-sm);
/* The round send and stop buttons. */
--round: var(--control-h);
/* A thumbnail of an attached picture. */
--thumb: calc(4 * var(--sp-12) / 3);
/* 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);
-webkit-font-smoothing: antialiased;
}
[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-body);
color: var(--ink);
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius-lg);
cursor: pointer;
white-space: nowrap;
transition: background var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.btn:hover { background: var(--surface-hover); border-color: var(--border-strong); }
.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); border-color: var(--accent-hover); }
.btn--sm { height: var(--control-h-sm); padding: 0 var(--sp-3); font-size: var(--text-xs); }
.btn--ghost { background: transparent; border-color: transparent; color: var(--ink-muted); }
.btn--ghost:hover { color: var(--ink); background: var(--surface-hover); border-color: transparent; }
.btn--danger { color: var(--ink-inverse); background: var(--danger); border-color: var(--danger); }
.btn--danger:hover { background: var(--danger-hover); }
/* A square icon button: a header's tools, a pane's refresh, a panel's close. */
.tool, .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-lg);
cursor: pointer;
}
.tool:hover, .pane__btn:hover { color: var(--ink); background: var(--surface-hover); }
.tool:disabled { opacity: var(--opacity-dim); cursor: default; }
input[type="text"], input[type="search"], input[type="number"], input[type="time"], select, textarea {
min-width: 0;
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-lg);
}
input[type="text"], input[type="search"], input[type="number"], input[type="time"], 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-body); color: var(--ink-muted); }
.hint { font-size: var(--text-sm); color: var(--ink-muted); min-height: calc(var(--text-sm) * var(--leading-normal)); }
.hint.is-warn { color: var(--warning); }
.status { font: var(--text-sm) / var(--leading-normal) var(--font-body); }
.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); border-radius: var(--radius-full); 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); }
.kbd { font: var(--text-xs) / 1 var(--font-ui); color: var(--ink-faint); white-space: nowrap; }
/* 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); }
/* The setup card's headings in brackets, as on the server's screens: `[ SERVER ]`. */
.card 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 { content: "[ "; }
.card 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(--titlebar-h) 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-sunken);
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-faint);
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; }
/* The agent's chip: a dot and a short word, a pill inside the bar's height. */
.bar > .bar__agent {
display: grid;
grid-template-columns: var(--dot-size) auto;
gap: var(--sp-2);
align-items: center;
height: var(--bar-chip-h);
margin: 0 var(--sp-2);
padding: 0 var(--sp-3);
font: var(--text-xs) / 1 var(--font-ui);
color: var(--ink-muted);
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius-full);
}
.bar > .bar__agent:hover, .bar > .bar__agent[aria-expanded="true"] { color: var(--ink); background: var(--surface-hover); }
.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__agent[data-state="refused"], .bar__agent[data-state="not_linked"] { color: var(--warning); border-color: var(--warning); }
.bar > .bar__update { height: var(--bar-chip-h); margin: 0 var(--sp-1); padding: 0 var(--sp-3); color: var(--accent-ink); background: var(--accent); border-radius: var(--radius-full); font-size: var(--text-xs); }
.bar > .bar__update:hover { background: var(--accent-hover); color: var(--accent-ink); }
/* Hidden keeps its grid track (auto, so zero wide): the slots after it never move. */
.bar > .bar__update[hidden] { display: block !important; width: 0; margin: 0; padding: 0; overflow: hidden; visibility: hidden; }
.bar__sep { width: var(--border-w); height: var(--sp-5); background: var(--border); }
.bar > .bar__btn--close:hover { color: var(--ink-inverse); background: var(--danger); }
/* --- Popovers: the agent's panel, the team's size ------------------------------------------- */
.pop {
position: fixed;
z-index: var(--z-dropdown);
width: min(var(--w-menu-sm), calc(100vw - 2 * var(--sp-2)));
background: var(--surface-raised);
border: var(--border-w) solid var(--border-strong);
border-radius: var(--radius-soft);
box-shadow: var(--shadow-lg);
}
.pop__body { display: grid; gap: var(--sp-3); padding: var(--sp-4); }
.pop__title { font: 600 var(--text-xs) / var(--leading-tight) var(--font-ui); letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint); }
.pop__actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: flex-end; }
/* The agent's state as a block: a dot beside its sentence, a line more, its one button. */
.agentbox {
display: grid;
grid-template-columns: var(--dot-size) minmax(0, 1fr);
column-gap: var(--sp-3);
row-gap: var(--sp-2);
align-items: start;
padding: var(--sp-3) var(--sp-4);
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius-soft);
}
.agentbox > :not(.agentbox__dot) { grid-column: 2; }
.agentbox__dot { width: var(--dot-size); height: var(--dot-size); margin-top: calc((var(--text-sm) * var(--leading-normal) - var(--dot-size)) / 2); border-radius: var(--radius-full); background: var(--ink-faint); }
.agentbox.is-ok .agentbox__dot { background: var(--leaf); }
.agentbox.is-busy .agentbox__dot { background: var(--accent); }
.agentbox.is-warn { border-color: var(--warning); background: var(--warning-soft); }
.agentbox.is-warn .agentbox__dot { background: var(--warning); }
.agentbox__text { font-size: var(--text-sm); font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.agentbox__more { font-size: var(--text-sm); color: var(--ink-muted); }
.agentbox__code { margin: 0; }
.agentbox__actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.agentbox__actions .btn { height: var(--control-h-sm); }
.agentbox__error { font-size: var(--text-sm); color: var(--warning); }
/* --- 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-soft);
box-shadow: var(--shadow-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 ----------------------------------------------------------------------------- */
/* Three tracks: the sidebar (zero wide when folded away), the page, and a panel beside it (the
activity panel or the viewer) while one is open. */
.shell {
position: relative;
min-height: 0;
display: grid;
--side-col: var(--side-w);
--panel-col: 0px;
grid-template-columns: var(--side-col) minmax(0, 1fr) var(--panel-col);
grid-template-areas: "side main panel";
}
.shell[data-side="false"] { --side-col: 0px; }
.shell[data-viewer="true"] { --panel-col: minmax(var(--inspector-width-min), 42%); }
.shell[data-activity="true"] { --panel-col: var(--inspector-width); }
/* The terminal: the server's width for it, giving way down to its least before the chat does. */
.shell[data-terminal="true"] { --panel-col: minmax(var(--terminal-width-min), var(--terminal-width)); }
/* --- The sidebar --------------------------------------------------------------------------- */
.side {
grid-area: side;
min-width: 0;
min-height: 0;
overflow: hidden;
display: grid;
grid-template-rows: auto auto minmax(0, 1fr) auto;
background: var(--bg-sunken);
border-right: var(--border-w) solid var(--border);
}
.shell[data-side="false"] .side { border-right: 0; visibility: hidden; }
.side__top { display: grid; gap: var(--sp-2); padding: var(--sp-3) var(--sp-3) var(--sp-2); width: var(--side-w); }
/* New chat: icon · words · its key, on known tracks. */
.side__new {
display: grid;
grid-template-columns: 16px minmax(0, 1fr) auto;
gap: var(--sp-2);
align-items: center;
height: var(--control-h);
padding: 0 var(--sp-3);
font: 600 var(--text-sm) var(--font-body);
color: var(--ink);
text-align: left;
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius-lg);
cursor: pointer;
}
.side__new:hover { background: var(--surface-hover); border-color: var(--border-strong); }
.side__new .icon { color: var(--leaf); }
.side__new-label { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.search { position: relative; display: block; }
.search__icon { position: absolute; left: var(--sp-3); top: 50%; transform: translateY(-50%); display: grid; color: var(--ink-faint); pointer-events: none; }
.search input {
width: 100%;
height: var(--control-h-sm);
padding-left: calc(var(--sp-3) + 16px + var(--sp-2));
font-size: var(--text-sm);
background: var(--bg);
border-color: var(--border);
}
.search input::-webkit-search-cancel-button { display: none; }
.side__nav { display: grid; padding: 0 var(--sp-2) var(--sp-2); width: var(--side-w); border-bottom: var(--border-w) solid var(--border); }
/* A row of the sidebar's nav and of Settings' groups: icon · words, a control's height. */
.navrow {
display: grid;
grid-template-columns: 16px minmax(0, 1fr);
gap: var(--sp-3);
align-items: center;
width: 100%;
min-height: var(--control-h-sm);
padding: var(--sp-1) var(--sp-2);
font: var(--text-sm) / var(--leading-tight) var(--font-body);
color: var(--ink-muted);
text-align: left;
background: transparent;
border: 0;
border-radius: var(--radius-lg);
cursor: pointer;
}
.navrow:hover { color: var(--ink); background: var(--surface-hover); }
.navrow[aria-current="true"] { color: var(--ink); background: var(--surface-active); }
.navrow__icon { display: grid; place-items: center; }
/* The chats: by day, each row icon · title · folder · state, on known tracks. */
.list { list-style: none; margin: 0; padding: var(--sp-2) var(--sp-2) var(--sp-3); overflow: auto; min-height: 0; width: var(--side-w); }
.list__head { padding: var(--sp-3) var(--sp-2) var(--sp-1); font: 600 var(--text-xs) var(--font-ui); letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); }
.list__head:first-child { padding-top: var(--sp-1); }
.list__note { padding: var(--sp-2); font-size: var(--text-sm); color: var(--ink-muted); }
.list__more { margin: var(--sp-2); }
.chatrow {
width: 100%;
display: grid;
grid-template-columns: 16px minmax(0, 1fr) auto var(--dot-size);
gap: var(--sp-2);
align-items: center;
height: var(--control-h-sm);
padding: 0 var(--sp-2);
text-align: left;
font: var(--text-sm) / var(--leading-tight) var(--font-body);
color: var(--ink-muted);
background: transparent;
border: 0;
border-radius: var(--radius-lg);
cursor: pointer;
}
.chatrow:hover { color: var(--ink); background: var(--surface-hover); }
.chatrow[aria-current="true"] { color: var(--ink); background: var(--surface-active); }
.chatrow.is-unread { color: var(--ink); }
.chatrow.is-unread .chatrow__title { font-weight: 600; }
.chatrow__icon { display: grid; place-items: center; color: var(--ink-faint); }
.chatrow__icon.is-agent { color: var(--leaf); }
.chatrow__icon .icon { width: 14px; height: 14px; }
.chatrow__title { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.chatrow__where { max-width: calc(var(--w-field-xs) - var(--sp-4)); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font: var(--text-xs) var(--font-ui); color: var(--ink-faint); }
.chatrow__state { width: var(--dot-size); height: var(--dot-size); border-radius: var(--radius-full); }
.chatrow__state.is-busy { background: var(--accent); animation: pulse 1.4s var(--ease-in-out) infinite; }
.chatrow__state.is-new { background: var(--leaf); }
/* A chat folder's head: chevron · folder · name · count, and its own ⋯ beside it on one track. */
.list__folder { display: grid; grid-template-columns: minmax(0, 1fr) var(--control-h-sm); align-items: center; }
.folderrow {
display: grid;
grid-template-columns: 12px 16px minmax(0, 1fr) auto;
gap: var(--sp-2);
align-items: center;
height: var(--control-h-sm);
padding: 0 var(--sp-2) 0 calc(var(--sp-2) + var(--depth, 0) * var(--sp-4));
text-align: left;
font: 600 var(--text-sm) / var(--leading-tight) var(--font-ui);
color: var(--ink-muted);
background: transparent;
border: 0;
border-radius: var(--radius-lg);
cursor: pointer;
}
.folderrow:hover { color: var(--ink); background: var(--surface-hover); }
.folderrow__chev, .folderrow__icon { display: grid; place-items: center; color: var(--ink-faint); }
.folderrow__chev .icon { width: 12px; height: 12px; }
.folderrow__icon .icon { width: 14px; height: 14px; }
.folderrow__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.folderrow__count { font: var(--text-xs) var(--font-ui); color: var(--ink-faint); }
.folderrow__more { display: grid; place-items: center; width: var(--control-h-sm); height: var(--control-h-sm); color: var(--ink-faint); background: transparent; border: 0; border-radius: var(--radius-lg); cursor: pointer; opacity: 0; }
.list__folder:hover .folderrow__more, .folderrow__more:focus-visible { opacity: 1; }
.folderrow__more:hover { color: var(--ink); background: var(--surface-hover); }
@media (hover: none) { .folderrow__more { opacity: 1; } }
.chatrow.is-filed { padding-left: calc(var(--sp-2) + var(--depth, 1) * var(--sp-4)); }
@keyframes pulse { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .chatrow__state.is-busy { animation: none; } }
.side__foot { padding: var(--sp-2); border-top: var(--border-w) solid var(--border); width: var(--side-w); }
.navrow--account { grid-template-columns: var(--control-h-sm) minmax(0, 1fr) 16px; min-height: var(--control-h); }
.navrow__avatar {
display: grid;
place-items: center;
width: var(--control-h-sm);
height: var(--control-h-sm);
font: 700 var(--text-sm) / 1 var(--font-ui);
color: var(--accent-ink);
background: var(--accent);
border-radius: var(--radius-full);
}
.navrow__two { display: grid; min-width: 0; }
.navrow__label { color: var(--ink); }
.navrow__sub { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--text-xs); color: var(--ink-faint); }
.scrim { display: none; }
/* --- The chat ------------------------------------------------------------------------------ */
.main, .page { grid-area: main; min-width: 0; min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr) auto; background: var(--bg); }
.page { 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: var(--sp-2) var(--sp-4);
border-bottom: var(--border-w) solid var(--border);
}
.head__title { font: 600 var(--text-base) / var(--leading-tight) var(--font-body); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.head__place { display: flex; gap: var(--sp-2); align-items: center; min-width: 0; }
.head__tools { display: flex; gap: var(--sp-1); align-items: center; justify-content: end; }
.main[data-new="true"] .head { display: none; }
.main > .head { grid-row: 1; }
.main > .thread { grid-row: 2; }
.main > .dock { grid-row: 3; }
/* Where an agent chat works: folder icon · its name · the device, one chip. */
.place {
display: inline-grid;
grid-template-columns: 16px minmax(0, auto) auto;
gap: var(--sp-2);
align-items: center;
min-width: 0;
height: var(--chip-h);
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);
border-radius: var(--radius-full);
}
.place.is-here .icon { color: var(--leaf); }
.place__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--ink); }
.place__where { color: var(--ink-faint); white-space: nowrap; }
.thread { overflow: auto; min-height: 0; padding: var(--sp-6) var(--sp-4) var(--sp-4); scroll-behavior: auto; }
.thread > * { max-width: var(--column); margin-left: auto; margin-right: auto; }
.thread__note { 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-6); }
/* A new chat is the same page, empty: the greeting low in the space above the composer, which
sits in the middle of the window rather than at its foot. */
.main[data-new="true"] { grid-template-rows: 0 minmax(0, 1fr) auto minmax(0, 0.8fr); }
.main[data-new="true"] .thread { display: grid; align-content: end; padding-bottom: var(--sp-6); }
.hello { display: grid; justify-items: center; gap: var(--sp-3); text-align: center; }
.hello__mark { width: var(--sp-10); height: var(--sp-10); }
@media (max-height: 40rem) { .hello__mark { display: none; } }
.hello__title { font: 600 var(--text-2xl) / var(--leading-tight) var(--font-body); letter-spacing: -0.01em; }
.hello__sub { max-width: var(--w-prose-sm); color: var(--ink-muted); font-size: var(--text-sm); }
/* Agent or Chat: two tabs of one width, one control. */
.kind {
display: inline-grid;
grid-template-columns: 1fr 1fr;
margin-top: var(--sp-2);
padding: var(--sp-1);
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius-full);
}
.kind__tab {
display: inline-grid;
grid-template-columns: 16px auto;
gap: var(--sp-2);
align-items: center;
justify-content: center;
height: var(--chip-h);
padding: 0 var(--sp-4);
font: 600 var(--text-sm) var(--font-body);
color: var(--ink-muted);
background: transparent;
border: 0;
border-radius: var(--radius-full);
cursor: pointer;
}
.kind__tab:hover { color: var(--ink); }
.kind__tab[aria-selected="true"] { color: var(--ink); background: var(--surface-active); box-shadow: var(--shadow-sm); }
.kind__tab[aria-selected="true"] .icon { color: var(--leaf); }
/* Messages: the person's in a bubble on the right, a reply full width as the page's text. */
.msg { display: grid; gap: var(--sp-2); min-width: 0; }
.msg--user { justify-items: end; gap: var(--sp-1); }
.msg__bubble {
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-soft);
}
.msg--user.is-queued .msg__bubble { opacity: var(--opacity-dim); }
.msg--user.is-machine { justify-items: stretch; }
.msg--user.is-machine .msg__bubble { max-width: none; font-size: var(--text-sm); color: var(--ink-muted); background: transparent; border-style: dashed; }
.msg__text { white-space: pre-wrap; overflow-wrap: anywhere; }
.msg__note { font-size: var(--text-sm); color: var(--ink-muted); }
.msg--assistant { font-size: var(--text-base); line-height: var(--leading-relaxed); }
.msg__foot { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3); align-items: center; min-height: var(--control-h-sm); }
/* Under a message: small icon buttons, all one size, quiet until the message is pointed at. */
.msg__actions { display: grid; grid-auto-flow: column; grid-auto-columns: var(--control-h-sm); justify-content: start; }
/* The person's: shown on pointing, in the gap under the bubble rather than a row of their own. */
.msg--user .msg__actions { height: var(--sp-1); overflow: visible; align-items: start; opacity: 0; transition: opacity var(--dur-1) var(--ease-out); }
.msg--user:hover .msg__actions, .msg--user:focus-within .msg__actions { opacity: 1; }
.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-lg); 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); width: min(100%, var(--w-prose)); }
.msg__edit { width: 100%; min-height: var(--control-h); font: var(--text-base) / var(--leading-normal) var(--font-body); resize: vertical; border-radius: var(--radius-soft); }
.msg__editbar { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(min(100%, 6rem), auto); gap: var(--sp-2); justify-content: end; }
.msg__model { display: inline-grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-2); align-items: center; max-width: var(--w-chip); font: var(--text-xs) var(--font-ui); color: var(--ink-muted); }
.msg__model > span:last-child { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.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); color: var(--ink-muted); }
.msg__status.is-error { color: var(--warning); }
.is-live .msg__status::before { content: ""; display: inline-block; width: var(--dot-size); height: var(--dot-size); margin-right: var(--sp-2); border-radius: var(--radius-full); background: var(--accent); animation: pulse 1.4s var(--ease-in-out) infinite; vertical-align: middle; }
.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; }
/* Attached files, as tiles: a picture's thumbnail, else its icon, name and size. */
.tiles { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: end; }
.tile {
position: relative;
display: grid;
grid-template-columns: 16px minmax(0, 1fr);
grid-template-rows: auto auto;
column-gap: var(--sp-2);
align-items: center;
width: var(--w-field-sm);
height: var(--thumb);
padding: var(--sp-2) var(--sp-3);
align-content: center;
font: var(--text-xs) / var(--leading-tight) var(--font-body);
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius-soft);
overflow: hidden;
}
.tile__icon { grid-row: 1 / 3; display: grid; color: var(--ink-faint); }
.tile__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--ink); }
.tile__size { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--ink-faint); }
.tile.has-thumb { width: var(--thumb); padding: 0; }
.tile.has-thumb > :not(.tile__thumb):not(.file__remove) { display: none; }
.tile__thumb { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* A reply's steps: thinking and each tool call as one compact row that unfolds. */
.step__row, .call__head {
list-style: none;
display: grid;
gap: var(--sp-2);
align-items: center;
min-height: var(--control-h-sm);
padding: 0 var(--sp-2);
font: var(--text-sm) / var(--leading-tight) var(--font-body);
color: var(--ink-muted);
border-radius: var(--radius-lg);
cursor: pointer;
}
.step__row::-webkit-details-marker, .call__head::-webkit-details-marker { display: none; }
.step__row:hover, .call__head:hover { color: var(--ink); background: var(--surface-hover); }
.step__row { grid-template-columns: 16px minmax(0, auto) 16px; justify-content: start; }
.step__icon { display: grid; }
.step__meta { color: var(--ink-faint); }
.step__chev { display: grid; place-items: center; color: var(--ink-faint); }
.step__chev .icon { width: 12px; height: 12px; transition: transform var(--dur-1) var(--ease-out); }
details[open] > summary .step__chev .icon { transform: rotate(90deg); }
/* A row's hover box reaches out by its padding, so its icon stands on the text's left edge. */
.step--thinking { justify-self: start; max-width: calc(100% + var(--sp-2)); margin-left: calc(-1 * var(--sp-2)); }
.step--tools { margin-left: calc(-1 * var(--sp-2)); margin-right: calc(-1 * var(--sp-2)); }
.step__pre { margin: var(--sp-2) 0 0 var(--sp-4); padding-left: var(--sp-3); border-left: var(--border-w-thick) solid var(--border); white-space: pre-wrap; font-size: var(--text-sm); line-height: var(--leading-normal); 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 { min-width: 0; border: var(--border-w) solid transparent; border-radius: var(--radius-lg); }
.call[open] { border-color: var(--border); background: var(--surface); }
.call.is-failed .call__name { color: var(--warning); }
/* The call's line: dot · tool · what it did · chips · chevron, on fixed tracks. */
.call__head { grid-template-columns: var(--dot-size) auto minmax(0, 1fr) auto 16px; }
.call__name { font: 600 var(--text-xs) var(--font-ui); color: var(--ink); white-space: nowrap; }
.call__title { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font: var(--text-xs) var(--font-mono); color: var(--ink-muted); }
.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); font-size: var(--text-sm); line-height: var(--leading-normal); }
.call__pre, .md-code pre {
margin: 0;
padding: var(--sp-3) var(--sp-4);
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-lg);
white-space: pre;
}
.call__open { justify-self: start; margin: 0 var(--sp-3) var(--sp-3); }
/* 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 subagents, Min and Max: one range with two handles. Three rows of known height -- the
values said on one line, the track box (one control high), the two ends -- and in the box two
range inputs laid over one track, the span between the handles filled. The inputs let only
their handles take the pointer, so either can be grabbed wherever the other is. */
.range2 { display: grid; grid-template-rows: auto var(--control-h) auto; gap: var(--sp-1); width: 100%; }
.range2__said { font: 600 var(--text-sm) / var(--leading-tight) var(--font-ui); color: var(--ink); font-variant-numeric: tabular-nums; }
.range2__box { position: relative; display: block; height: var(--control-h); --lo: 0%; --hi: 100%; }
.range2__track {
position: absolute;
top: 50%;
left: calc(var(--check-size) / 2);
right: calc(var(--check-size) / 2);
height: var(--sp-1);
transform: translateY(-50%);
background: var(--border);
border-radius: var(--radius-full);
}
.range2__fill {
position: absolute;
top: 0;
bottom: 0;
left: var(--lo);
right: calc(100% - var(--hi));
background: var(--accent);
border-radius: var(--radius-full);
}
.range2__input {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
margin: 0;
background: none;
pointer-events: none;
-webkit-appearance: none;
appearance: none;
}
.range2__input.is-top { z-index: 1; }
.range2__input::-webkit-slider-runnable-track { height: 100%; background: none; border: 0; }
.range2__input::-moz-range-track { height: 100%; background: none; border: 0; }
.range2__input::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: var(--check-size);
height: var(--check-size);
margin-top: calc((var(--control-h) - var(--check-size)) / 2);
background: var(--surface-raised);
border: var(--border-w-thick) solid var(--accent);
border-radius: var(--radius-full);
box-shadow: var(--shadow-sm);
cursor: grab;
pointer-events: auto;
}
.range2__input::-moz-range-thumb {
width: var(--check-size);
height: var(--check-size);
background: var(--surface-raised);
border: var(--border-w-thick) solid var(--accent);
border-radius: var(--radius-full);
box-shadow: var(--shadow-sm);
cursor: grab;
pointer-events: auto;
}
.range2__input:focus-visible { outline: none; }
.range2__input:focus-visible::-webkit-slider-thumb { box-shadow: var(--ring); }
.range2__input:focus-visible::-moz-range-thumb { box-shadow: var(--ring); }
/* The two ends, each one handle wide, so each number sits centred under its handle at that end. */
.range2__ends { display: flex; justify-content: space-between; font-size: var(--text-xs); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.range2__ends > span { width: var(--check-size); text-align: center; white-space: nowrap; }
/* 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-lg);
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-lg);
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-lg);
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 viewer ---------------------------------------------------------------------------- */
.viewer {
grid-area: panel;
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"] .viewer { grid-area: main; z-index: var(--z-panel); border-left: 0; }
.activity {
grid-area: panel;
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); }
/* "41k until auto-compact" and the Compact button: the words give, the button keeps its size. */
.ctx__compact { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--sp-1) var(--sp-2); align-items: center; padding-top: var(--sp-2); border-top: var(--border-w) solid var(--border); }
.ctx__until { color: var(--ink-muted); }
.ctx__error { grid-column: 1 / -1; margin: 0; font-size: var(--text-xs); color: var(--warning); }
/* Environment: an agent chat's folder and where it is. */
.env { display: grid; grid-template-columns: var(--sp-4) minmax(0, auto) auto; justify-content: start; gap: var(--sp-2); align-items: center; margin: 0; }
.env__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--ink); }
.env__where { font: var(--text-xs) var(--font-mono); color: var(--ink-muted); }
.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__team { margin: 0; color: var(--ink-muted); }
.tasks__goal { margin: 0; color: var(--ink); }
.tasks__group { display: grid; grid-template-columns: auto auto; justify-content: start; gap: var(--sp-2); align-items: baseline; margin: var(--sp-1) 0 0; color: var(--ink-muted); }
.tasks__list { display: grid; gap: var(--sp-2); }
/* A fold ("Show 3 more", "Finished 12", a task's own "2 tasks"): a chevron that turns, then the
words; nested ones step in by the depth of what is under them. */
.tasks__more { margin-left: calc(var(--depth, 0) * var(--sp-4)); min-width: 0; }
.tasks__more > summary {
display: grid;
grid-template-columns: 12px minmax(0, 1fr);
gap: var(--sp-2);
align-items: center;
min-height: var(--row-h);
padding: 0 var(--sp-3);
color: var(--ink-muted);
cursor: pointer;
list-style: none;
}
.tasks__more > summary::-webkit-details-marker { display: none; }
.tasks__more > summary:hover { color: var(--ink); }
.tasks__more[open] > summary { margin-bottom: var(--sp-2); }
.tasks__nested { margin-top: calc(-1 * var(--sp-1)); }
.tasks__finished { padding-top: var(--sp-1); border-top: var(--border-w) solid var(--border); }
.tasks__chev, .agent__chev { display: grid; place-items: center; width: 12px; height: 12px; color: var(--ink-faint); transition: transform var(--dur-1) var(--ease-out); }
.tasks__chev .icon, .agent__chev .icon { width: 12px; height: 12px; }
.tasks__more[open] > summary .tasks__chev, .agent[open] > .agent__row .agent__chev { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .tasks__chev, .agent__chev { transition: none; } }
/* A task: one card, one row of known tracks -- the chevron, what it does (the one track that
gives, cut), the elapsed time in a column of its own width, the stop square at its size (an
empty box where it cannot be stopped) -- so the rows line up whatever each says. */
.agent { margin-left: calc(var(--depth, 0) * var(--sp-4)); background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius-lg); min-width: 0; }
.agent__row {
display: grid;
grid-template-columns: 12px 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__row:hover { background: var(--surface-hover); border-radius: var(--radius-lg); }
.agent__what { display: grid; min-width: 0; }
.agent__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--ink); }
.agent.is-done .agent__name, .agent.is-stopped .agent__name { color: var(--ink-muted); }
.agent.is-error .agent__name { color: var(--danger); }
.agent__role { margin: 0; color: var(--ink-muted); overflow-wrap: anywhere; }
.agent__time { font: var(--text-xs) var(--font-mono); text-align: right; color: var(--ink-muted); }
.agent.is-queued .agent__time, .agent.is-waiting .agent__time { color: var(--warning); }
/* The stop square: a small filled square in a button the size of the others. */
.agent__stop { display: grid; place-items: center; width: var(--control-h-sm); height: var(--control-h-sm); padding: 0; background: var(--surface-active); border: 0; border-radius: var(--radius-md); cursor: pointer; }
.agent__stop:hover { background: var(--surface-hover); }
.agent__stop.is-none { background: none; cursor: default; }
.agent__square { width: var(--sp-3); height: var(--sp-3); background: var(--ink); border-radius: var(--radius-sm); }
.agent__stop:hover .agent__square { background: var(--danger); }
.agent__id { font: var(--text-xs) var(--font-mono); color: var(--ink-faint); overflow-wrap: anywhere; }
.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-lg); 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); }
/* --- 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-lg);
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-soft);
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-lg);
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; }
/* A hit found inside: its title over the snippet, the words that matched marked. */
.palette__label--two { display: grid; padding: var(--sp-1) 0; white-space: normal; }
.palette__title, .palette__snippet { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.palette__snippet { font-size: var(--text-xs); color: var(--ink-muted); }
.palette__snippet mark { color: var(--ink); background: var(--surface-active); border-radius: var(--radius-sm); }
.palette__none { padding: var(--sp-3); font-size: var(--text-sm); color: var(--ink-muted); }
/* --- Under the conversation: notes, cards, the agent's notice, the activity chip, the composer --
One column the conversation's width, so all of it lines up with the messages above. */
.dock { display: grid; gap: var(--sp-2); align-content: start; padding: 0 var(--sp-4) var(--sp-4); }
.dock > * { width: 100%; max-width: var(--column); margin: 0 auto; }
.dock .cards { padding: 0; max-height: 50vh; overflow: auto; }
.thread__note { padding: var(--sp-2) var(--sp-3); background: var(--warning-soft); border-radius: var(--radius-lg); }
/* The activity chip: what the agents are doing (or only "Activity"), and the way into the panel. */
.strip { display: flex; justify-content: start; }
.strip__btn {
display: inline-grid;
grid-template-columns: var(--dot-size) minmax(0, auto) 12px;
gap: var(--sp-2);
align-items: center;
max-width: 100%;
height: var(--chip-h);
padding: 0 var(--sp-3);
font: var(--text-xs) var(--font-ui);
color: var(--ink-muted);
text-align: left;
background: var(--surface);
border: var(--border-w) solid var(--border);
border-radius: var(--radius-full);
cursor: pointer;
}
.strip__btn:hover, .strip__btn[aria-expanded="true"] { color: var(--ink); background: var(--surface-hover); }
.strip__btn .dot { background: var(--ink-faint); }
.strip__btn.is-active .dot { background: var(--accent); animation: pulse 1.4s var(--ease-in-out) infinite; }
.strip__btn .icon { width: 12px; height: 12px; }
.strip__text { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* The composer: a card with the files it carries, the text, and under them one row -- attach,
the chat's chips (the one track that gives, wrapping onto a second line if it must), then Stop
and Send at their own size. */
.composer {
position: relative;
display: grid;
grid-template-rows: auto auto auto;
gap: var(--sp-2);
padding: var(--sp-3) var(--sp-3) var(--sp-2);
background: var(--surface);
border: var(--border-w) solid var(--border-strong);
border-radius: var(--radius-soft);
box-shadow: var(--shadow);
transition: border-color var(--dur-1) var(--ease-out);
}
.composer:focus-within { border-color: var(--accent); }
.composer > * { min-width: 0; }
.composer textarea {
width: 100%;
min-height: calc(var(--text-base) * var(--leading-normal) + 2 * var(--sp-1));
max-height: 40vh;
padding: var(--sp-1) var(--sp-1);
font: var(--text-base) / var(--leading-normal) var(--font-body);
background: transparent;
border: 0;
outline: none;
}
.composer textarea::placeholder { color: var(--ink-faint); }
.composer__files { justify-content: start; }
.composer__bar {
display: grid;
grid-template-columns: var(--chip-h) minmax(0, 1fr) auto auto;
gap: var(--sp-2);
align-items: center;
}
.composer[data-attach="false"] .composer__bar { grid-template-columns: minmax(0, 1fr) auto auto; }
.composer__attach { width: var(--chip-h); height: var(--chip-h); border: var(--border-w) solid var(--border); border-radius: var(--radius-full); }
/* The chips stay on one line; where they do not fit, the line scrolls by itself. */
.composer__chips { display: flex; flex-wrap: nowrap; gap: var(--sp-1); min-width: 0; align-items: center; overflow-x: auto; scrollbar-width: none; }
.composer__chips::-webkit-scrollbar { display: none; }
.composer__chips > * { flex: none; }
/* Send and Stop: round, one size, side by side when both show (a message typed while a reply runs). */
.round {
display: grid;
place-items: center;
width: var(--round);
height: var(--round);
padding: 0;
color: var(--accent-ink);
background: var(--accent);
border: 0;
border-radius: var(--radius-full);
cursor: pointer;
transition: background var(--dur-1) var(--ease-out), opacity var(--dur-1) var(--ease-out);
}
.round:hover { background: var(--accent-hover); }
.round:disabled { opacity: var(--opacity-dim); cursor: default; }
.round--stop { color: var(--ink); background: var(--surface-active); border: var(--border-w) solid var(--border-strong); }
.round--stop:hover { background: var(--surface-hover); }
.composer:has(textarea:placeholder-shown) .round:not(.round--stop) { opacity: var(--opacity-dim); }
/* A chip under the text: icon or avatar · words · chevron, a small control's height. */
.cchip {
display: inline-grid;
grid-auto-flow: column;
grid-auto-columns: auto;
gap: var(--sp-1);
align-items: center;
max-width: var(--w-field);
height: var(--chip-h);
padding: 0 var(--sp-2);
font: var(--text-xs) / 1 var(--font-ui);
color: var(--ink-muted);
background: transparent;
border: var(--border-w) solid transparent;
border-radius: var(--radius-full);
cursor: pointer;
white-space: nowrap;
}
.cchip:hover { color: var(--ink); background: var(--surface-hover); border-color: var(--border); }
.cchip:disabled { cursor: default; }
.cchip[aria-pressed="true"], .tool[aria-pressed="true"] { color: var(--ink); background: var(--surface-active); }
.cchip:disabled:hover { background: transparent; border-color: transparent; }
.cchip__label { overflow: hidden; text-overflow: ellipsis; }
.cchip__chev { display: grid; color: var(--ink-faint); }
.cchip__chev .icon { width: 12px; height: 12px; }
.cchip .avatar { width: 1rem; height: 1rem; font-size: 0.625rem; }
.cchip.is-dim { color: var(--ink-faint); }
.cchip.is-warn { color: var(--warning); border-color: var(--warning); }
/* 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: 0;
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-soft);
}
/* A pending file's tile carries its remove button in its corner. */
.file .file__remove {
position: absolute;
top: var(--sp-1);
right: var(--sp-1);
display: grid;
place-items: center;
width: var(--sp-5);
height: var(--sp-5);
padding: 0;
color: var(--ink);
background: var(--surface-raised);
border: var(--border-w) solid var(--border);
border-radius: var(--radius-full);
cursor: pointer;
}
.file .file__remove .icon { width: 12px; height: 12px; }
.file.is-failed { border-color: var(--warning); }
.file.is-failed .tile__size { color: var(--warning); }
.file.is-sending { opacity: var(--opacity-dim); }
/* The agent's notice above the composer: the block, the column's width. */
.notice .agentbox { box-shadow: var(--shadow-sm); }
/* --- Pages: Files, Scheduled ---------------------------------------------------------------- */
.page__body { overflow: auto; min-height: 0; padding: var(--sp-6) var(--sp-4); }
.page__body > * { width: 100%; max-width: var(--w-prose); margin-left: auto; margin-right: auto; }
.page__loading { padding: var(--sp-4) 0; }
.tabs { display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr; padding: var(--sp-half); background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius-full); }
.tabs__tab {
display: inline-grid;
grid-template-columns: 16px auto;
gap: var(--sp-2);
align-items: center;
justify-content: center;
height: var(--chip-h);
padding: 0 var(--sp-3);
font: var(--text-xs) var(--font-ui);
color: var(--ink-muted);
background: transparent;
border: 0;
border-radius: var(--radius-full);
cursor: pointer;
}
.tabs__tab[aria-selected="true"] { color: var(--ink); background: var(--surface-active); }
.files { display: grid; align-content: start; }
.files__pane { display: grid; gap: var(--sp-2); background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius-soft); padding: var(--sp-3); min-width: 0; }
.files__head { display: grid; grid-template-columns: minmax(0, 1fr) var(--control-h-sm); gap: var(--sp-2); align-items: center; }
.files__where { min-width: 0; }
/* The server's pane: Upload and New folder before Read again. */
#pane-server .files__head { grid-template-columns: minmax(0, 1fr) repeat(3, var(--control-h-sm)); }
#server-status { white-space: pre-line; }
.files__where select { width: 100%; height: var(--control-h-sm); }
.files__tree { min-height: 0; }
.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__pick { width: 100%; }
.pane__note { padding: var(--sp-2) 0; font-size: var(--text-sm); color: var(--ink-muted); }
.files__tree > .btn { justify-self: start; }
.tree, .tree ul { list-style: none; margin: 0; padding: 0; }
.tree__row {
width: 100%;
height: var(--control-h-sm);
display: grid;
grid-template-columns: calc(var(--depth, 0) * var(--sp-4)) 1rem 1rem minmax(0, 1fr) auto;
gap: var(--sp-2);
align-items: center;
padding: 0 var(--sp-2);
text-align: left;
font: var(--text-sm) var(--font-body);
color: var(--ink);
background: transparent;
border: 0;
border-radius: var(--radius-lg);
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 { font: var(--text-xs) var(--font-ui); color: var(--ink-faint); }
/* A row with a more button beside it: the button on the row's own line, its folder below both. */
.tree__item { display: grid; grid-template-columns: minmax(0, 1fr) var(--control-h-sm); align-items: center; }
.tree__item > ul { grid-column: 1 / -1; }
.tree__more { opacity: 0; }
.tree__item:hover > .tree__more, .tree__more:focus-visible, .tree__more[aria-expanded="true"] { opacity: 1; }
@media (hover: none) { .tree__more { opacity: 1; } }
.tree__note { padding: var(--sp-1) var(--sp-3); font-size: var(--text-xs); color: var(--ink-muted); }
/* An empty page: an icon, a heading, a sentence, centred. */
.empty { display: grid; justify-items: center; gap: var(--sp-3); padding: var(--sp-12) var(--sp-4) var(--sp-4); text-align: center; }
.empty__icon { display: grid; place-items: center; width: var(--sp-12); height: var(--sp-12); color: var(--leaf); background: var(--leaf-soft); border-radius: var(--radius-full); }
.empty__icon .icon { width: 24px; height: 24px; }
.empty__title { font: 600 var(--text-lg) / var(--leading-tight) var(--font-body); }
.empty__text { max-width: var(--w-prose-sm); color: var(--ink-muted); font-size: var(--text-sm); }
.empty__actions { display: flex; justify-content: center; }
/* Scheduled: the list. One row per task on fixed tracks -- icon, title over its rule, the next
run, the last run's status, and the pause switch outside the row's button. */
.tasks-list { list-style: none; margin: 0 auto; padding: 0; display: grid; gap: var(--sp-2); }
.task { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius-soft); }
.task:hover { border-color: var(--border-strong); }
.task.is-paused .task__title, .task.is-paused .task__icon { color: var(--ink-faint); }
.task__open {
display: grid;
grid-template-columns: var(--sp-8) minmax(0, 1fr) var(--w-field-sm) var(--w-field-sm);
gap: var(--sp-3);
align-items: center;
min-height: calc(2 * var(--control-h));
padding: var(--sp-2) var(--sp-3);
text-align: left;
font: inherit;
color: inherit;
background: transparent;
border: 0;
border-radius: var(--radius-soft) 0 0 var(--radius-soft);
cursor: pointer;
}
.task__icon { display: grid; place-items: center; width: var(--sp-8); height: var(--sp-8); color: var(--leaf); background: var(--leaf-soft); border-radius: var(--radius-full); }
.task__what, .task__next { display: grid; min-width: 0; }
.task__title { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-weight: 600; }
.task__rule, .task__value { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--text-sm); color: var(--ink-muted); }
.task__label { font: var(--text-xs) var(--font-ui); color: var(--ink-faint); }
.task__toggle { padding: 0 var(--sp-4); height: auto; }
/* A run's status: a small chip coloured by what happened. */
.schip { display: inline-block; max-width: 100%; padding: 0 var(--sp-2); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font: var(--text-xs) / 1.8 var(--font-ui); color: var(--ink-muted); background: var(--surface-active); border-radius: var(--radius-full); }
.schip.is-ok { color: var(--success); background: var(--success-soft); }
.schip.is-failed, .schip.is-missed, .schip.is-skipped { color: var(--warning); background: var(--warning-soft); }
.schip.is-running, .schip.is-waiting { color: var(--accent); background: var(--accent-soft); }
/* A task's page: the facts on a grid of label and value, what it does, the next runs, history. */
.sched { display: grid; gap: var(--sp-6); }
.sched__h { font: 600 var(--text-xs) / var(--leading-tight) var(--font-ui); letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: var(--sp-2); }
.sched__facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--w-field)), 1fr)); gap: var(--sp-3) var(--sp-4); }
.fact { display: grid; gap: var(--sp-1); align-content: start; min-width: 0; }
.fact__label { font: var(--text-xs) var(--font-ui); color: var(--ink-faint); }
.fact__value { min-width: 0; overflow-wrap: anywhere; font-size: var(--text-sm); }
.fact__value select { width: 100%; }
.sched__model { display: inline-grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-2); align-items: center; }
.sched__instruction { white-space: pre-wrap; overflow-wrap: anywhere; padding: var(--sp-3) var(--sp-4); background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius-soft); }
.sched__asked { margin-top: var(--sp-2); font-size: var(--text-sm); color: var(--ink-muted); }
.runs-next { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--sp-1); counter-reset: next; }
.runs-next li { display: grid; grid-template-columns: var(--sp-6) minmax(0, 1fr) auto; gap: var(--sp-2); align-items: center; min-height: var(--control-h-sm); font-size: var(--text-sm); counter-increment: next; }
.runs-next li::before { content: counter(next); font: var(--text-xs) var(--font-ui); color: var(--ink-faint); text-align: right; }
.sched__history { display: grid; gap: var(--sp-2); }
.runs { list-style: none; margin: 0; padding: 0; display: grid; }
.run {
display: grid;
grid-template-columns: var(--w-field-sm) minmax(0, 1fr) minmax(0, var(--w-field-sm)) auto;
gap: var(--sp-3);
align-items: center;
min-height: var(--control-h);
padding: var(--sp-1) 0;
font-size: var(--text-sm);
border-bottom: var(--border-w) solid var(--border);
}
.run__when { display: flex; gap: var(--sp-2); align-items: center; min-width: 0; white-space: nowrap; overflow: hidden; }
.run__late { font: var(--text-xs) var(--font-ui); color: var(--warning); }
.run__model { display: inline-grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-2); align-items: center; min-width: 0; color: var(--ink-muted); }
.run__model > span:last-child { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.run__error { max-width: var(--w-field-sm); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--warning); font-size: var(--text-xs); }
.sched--new { gap: var(--sp-3); }
.sched__words { width: 100%; font-size: var(--text-base); border-radius: var(--radius-soft); padding: var(--sp-3) var(--sp-4); }
.sched__preview { margin-top: var(--sp-3); }
.sched__card { display: grid; gap: var(--sp-4); padding: var(--sp-4); background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius-soft); }
.sched__title { font: 600 var(--text-md) / var(--leading-tight) var(--font-body); }
/* A run's row opens its result under it, across every track. */
.run__open { justify-self: end; }
.run__switched { font: var(--text-xs) var(--font-ui); color: var(--ink-faint); }
.run__detail { grid-column: 1 / -1; display: grid; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-2); background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius-soft); }
.run__detail[hidden] { display: none; }
.run__report { font: var(--text-xs) var(--font-ui); color: var(--ink-muted); overflow-wrap: anywhere; }
.sched__more { justify-self: start; }
.sched + .sched__history { margin-top: var(--sp-6); }
.sched__trouble { padding: var(--sp-3) var(--sp-4); color: var(--warning); background: var(--warning-soft); border-radius: var(--radius-soft); overflow-wrap: anywhere; }
.fact__hint { font: var(--text-xs) var(--font-ui); color: var(--ink-faint); }
/* The form: the When field's words on the control's track, one line, the time zone as its hint. */
.sched__desc { align-self: center; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--text-sm); }
.sched__desc.is-error { color: var(--warning); }
/* The rule by hand: seven day toggles on equal tracks, and a time. */
.sched__manual[hidden] { display: none; }
.sdays { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--sp-1); }
.sday { position: relative; display: grid; place-items: center; min-width: 0; height: var(--control-h); border: var(--border-w) solid var(--border); border-radius: var(--radius-lg); cursor: pointer; font: var(--text-xs) var(--font-ui); color: var(--ink-muted); overflow: hidden; }
.sday input { position: absolute; opacity: 0; pointer-events: none; }
.sday:has(input:checked) { color: var(--accent); background: var(--accent-soft); border-color: var(--accent); }
.sday:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 1px; }
.sched__manual-open { justify-self: start; }
.sched__manual-open[hidden] { display: none; }
.sched__settings { padding-top: var(--sp-3); border-top: var(--border-w) solid var(--border); }
/* The scheduling models: a tick, the avatar, the name, free or busy -- on fixed tracks. */
.smodels { width: 100%; }
.smodels__hint { font-size: var(--text-xs); color: var(--ink-muted); }
.smodels__list { list-style: none; margin: 0; padding: 0; display: grid; }
.smodel { display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto; gap: var(--sp-2); align-items: center; min-height: var(--control-h); color: var(--ink); cursor: pointer; }
.smodel__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--text-sm); }
.smodel__state { font: var(--text-xs) var(--font-ui); color: var(--warning); }
.smodel__state.is-free { color: var(--success); }
/* A page's way back, before its title: "Scheduled ›". */
.crumb { display: inline-flex; gap: var(--sp-1); align-items: center; padding: 0; font: inherit; color: var(--ink-muted); background: none; border: 0; cursor: pointer; }
.crumb:hover { color: var(--ink); }
.crumb__sep { margin: 0 var(--sp-2); color: var(--ink-faint); font-weight: 400; }
/* --- Settings -------------------------------------------------------------------------------
The groups listed on the left, the groups themselves on the right, each a card of lines: words
(a label over a hint) on one track, the control on the other. */
.settings__layout { min-height: 0; display: grid; grid-template-columns: minmax(var(--w-field-sm), max-content) minmax(0, 1fr); }
.settings__nav { display: grid; align-content: start; gap: var(--sp-1); padding: var(--sp-6) var(--sp-2) var(--sp-4) var(--sp-4); }
.settings__body { overflow: auto; min-height: 0; padding: var(--sp-6) var(--sp-4) var(--sp-12); display: grid; gap: var(--sp-8); align-content: start; }
.set { width: 100%; max-width: var(--w-prose); display: grid; gap: var(--sp-3); scroll-margin-top: var(--sp-6); }
.set__title { font: 600 var(--text-lg) / var(--leading-tight) var(--font-body); }
.set__card { display: grid; gap: var(--sp-3); padding: var(--sp-4); background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius-soft); }
.set__intro { font-size: var(--text-sm); color: var(--ink-muted); }
.set__line { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--sp-4); align-items: center; padding-top: var(--sp-3); border-top: var(--border-w) solid var(--border); }
.set__card > .set__line:first-child { padding-top: 0; border-top: 0; }
.set__words { display: grid; gap: var(--sp-1); min-width: 0; }
.set__label { font-size: var(--text-sm); font-weight: 600; overflow-wrap: anywhere; }
.set__hint { font-size: var(--text-xs); color: var(--ink-muted); }
.set__hint:empty { display: none; }
.set__control { display: grid; justify-items: end; min-width: 0; max-width: var(--w-chip); }
.set__control select { max-width: 100%; }
.set__value { font: var(--text-sm) var(--font-ui); color: var(--ink-muted); overflow-wrap: anywhere; }
.set__buttons { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.set .agentbox { background: var(--bg); }
.set .agentbox.is-warn { background: var(--warning-soft); }
/* A switch: a checkbox drawn as one, the label its tap target. */
.toggle:not(:has(.toggle__label)) { grid-template-columns: calc(2 * var(--check-size)); height: var(--control-h-sm); }
/* The folder list: every row the same three 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: 16px minmax(0, 1fr) auto;
gap: var(--sp-3);
align-items: center;
min-height: var(--control-h);
padding: var(--sp-1) var(--sp-1) var(--sp-1) var(--sp-3);
background: var(--bg);
border: var(--border-w) solid var(--border);
border-radius: var(--radius-lg);
}
.folders__icon { display: grid; color: var(--leaf); }
.folders .path { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; direction: rtl; text-align: left; font: var(--text-sm) var(--font-ui); }
.folders .empty { display: block; padding: var(--sp-3); color: var(--warning); font-size: var(--text-sm); text-align: left; }
/* --- The terminal panel -------------------------------------------------------------------------
The panel column, as the activity panel has it, and never beside it (opening one closes the
other; the shell lives on). The viewer goes over the chat while it is open, as with the
activity panel. Not `.terminal`: xterm gives its own element that class. The head is the
viewer's: an icon, the device's name over the folder, then the buttons on known tracks (the
command ones only while the shell marks its commands). */
.shell[data-terminal="true"] .viewer { grid-area: main; z-index: var(--z-panel); border-left: 0; }
.termpanel {
grid-area: panel;
min-width: 0;
min-height: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr) auto;
border-left: var(--border-w) solid var(--border);
background: var(--bg-sunken);
}
.termpanel__head {
grid-template-columns: 16px minmax(0, 1fr);
grid-auto-flow: column;
grid-auto-columns: var(--control-h-sm);
}
.termpanel__icon { display: grid; place-items: center; color: var(--ink-muted); }
.termpanel__where { font-family: var(--font-mono); }
/* xterm measures this box: it has a size of its own, and the terminal scrolls inside it. */
.termpanel__screen { min-height: 0; min-width: 0; overflow: hidden; padding: var(--sp-2); background: var(--code-bg); }
.termpanel__screen .xterm { height: 100%; }
/* What happened, the last command, and Send: the words take what is left and wrap; the last
command is one clipped line; the button keeps its size. */
.termpanel__foot {
display: grid;
grid-template-columns: minmax(0, 1fr);
grid-auto-flow: column;
grid-auto-columns: auto;
gap: var(--sp-2);
align-items: center;
padding: var(--sp-2) var(--sp-3);
border-top: var(--border-w) solid var(--border);
font-size: var(--text-xs);
color: var(--ink-faint);
}
.termpanel__message { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.termpanel__message.is-error { color: var(--danger); }
.termpanel__last {
max-width: var(--w-chip);
font-family: var(--font-mono);
color: var(--ink-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.termpanel__last:empty { display: none; }
/* --- The subagent tree (spec 3.4.0) ------------------------------------------------------------
Rows are the tasks' rows (.agent): name and id on one line, the position, then the phase (or how
it ended) clipped to one line -- its clock is in the opened row. Ended ones are dimmed. */
.subagents { display: grid; gap: var(--sp-2); min-width: 0; }
.subagents .tasks__team, .subagents .tasks__goal { margin: 0; }
.subagent__who { display: flex; gap: var(--sp-2); align-items: baseline; min-width: 0; }
.subagent__who .agent__id { flex: none; }
.subagent__phase { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--text-xs); color: var(--ink-muted); }
.agent.is-ended { opacity: var(--opacity-dim); }
.subagent__buttons { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: 0; }
.findings { margin: 0; padding: var(--sp-3) var(--sp-3) var(--sp-3) calc(var(--sp-3) + var(--sp-5)); display: grid; gap: var(--sp-3); }
.findings__item { min-width: 0; overflow-wrap: anywhere; }
.ask__subagent { color: var(--ink); }
/* --- Archived chats: icon · title over its folder and date · Unarchive · more, on known tracks. */
.arch { list-style: none; margin: 0; padding: 0; display: grid; }
.arow {
display: grid;
grid-template-columns: 16px minmax(0, 1fr) auto var(--control-h-sm);
gap: var(--sp-3);
align-items: center;
min-height: var(--control-h);
padding: var(--sp-1) 0;
border-bottom: var(--border-w) solid var(--border);
}
.arow__icon { display: grid; place-items: center; color: var(--ink-faint); }
.arow__icon.is-agent { color: var(--leaf); }
.arow__icon .icon { width: 14px; height: 14px; }
.arow__open {
display: grid;
min-width: 0;
min-height: var(--control-h);
padding: var(--sp-1) 0;
text-align: left;
color: var(--ink);
background: transparent;
border: 0;
cursor: pointer;
}
.arow__open:hover .arow__title { text-decoration: underline; }
.arow__title, .arow__sub { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.arow__title { font: var(--text-sm) / var(--leading-tight) var(--font-body); }
.arow__sub { font: var(--text-xs) var(--font-ui); color: var(--ink-faint); }
.arch__more { margin-top: var(--sp-3); }
.arch__page { display: grid; gap: var(--sp-3); }
.head__unarchive { white-space: nowrap; }
/* --- A chat's tools and skills (scope.js): switches in a popover, the allowed commands below. */
.scope { max-height: min(70vh, 32rem); overflow-y: auto; }
.scope .pop__actions { justify-content: flex-start; }
.scope .pop__actions .btn { max-width: 100%; white-space: normal; height: auto; min-height: var(--control-h-sm); }
.scope__list { display: grid; gap: var(--sp-2); }
.scope__row span { overflow-wrap: anywhere; }
.scope__hint { margin: 0; font-size: var(--text-xs); }
.scope__allow { display: grid; gap: var(--sp-1); margin: 0; padding: 0; list-style: none; }
.scope__allow code { font: var(--text-xs) var(--font-mono); overflow-wrap: anywhere; }
/* --- Memory: a new one on top, then each record over its group and date · Edit · more. */
.mnew { display: grid; gap: var(--sp-2); }
.mnew textarea { width: 100%; }
.mnew__foot { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--sp-2); align-items: center; }
.mnew__foot select { justify-self: start; max-width: 100%; }
.mrow {
display: grid;
grid-template-columns: minmax(0, 1fr) auto var(--control-h-sm);
gap: var(--sp-3);
align-items: center;
min-height: var(--control-h);
padding: var(--sp-2) 0;
border-bottom: var(--border-w) solid var(--border);
}
.mrow.is-editing { grid-template-columns: minmax(0, 1fr); }
.mrow.is-editing textarea { width: 100%; }
.mrow.is-marked { background: var(--surface-active); border-radius: var(--radius-lg); padding-inline: var(--sp-2); }
.mrow__words { display: grid; gap: var(--sp-1); min-width: 0; }
.mrow__text { margin: 0; font: var(--text-sm) / var(--leading-normal) var(--font-body); color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
.mrow__sub { margin: 0; font: var(--text-xs) var(--font-ui); color: var(--ink-faint); }
/* --- Narrower windows ---------------------------------------------------------------------- */
/* 64rem (--bp-wide): a panel goes over the chat instead of beside it. */
@media (max-width: 64rem) {
.shell[data-viewer="true"], .shell[data-activity="true"], .shell[data-terminal="true"] { --panel-col: 0px; }
.viewer, .activity, .termpanel, .shell[data-activity="true"] .viewer, .shell[data-terminal="true"] .viewer { grid-area: main; z-index: var(--z-panel); border-left: 0; }
.task__open { grid-template-columns: var(--sp-8) minmax(0, 1fr) var(--w-field-sm); }
.task__next { display: none; }
}
/* The chat's header and chips by the chat's own width, not the window's. */
.main { container: chat / inline-size; }
@container chat (max-width: 40rem) {
.head { grid-template-columns: minmax(0, 1fr) auto; }
.head__place { grid-column: 1 / -1; grid-row: 2; }
.head__place:empty { display: none; }
.cchip { max-width: var(--w-field-xs); }
}
/* 48rem (--bp-narrow): the sidebar is a drawer over the page, opened from the title bar. */
@media (max-width: 48rem) {
.shell, .shell[data-side="false"] { --side-col: 0px; }
.bar__host { display: none; }
.side {
position: absolute;
inset: 0 auto 0 0;
width: min(85vw, var(--side-w));
z-index: var(--z-panel);
transform: translateX(-100%);
transition: transform var(--dur-2) var(--ease-out);
box-shadow: var(--shadow-lg);
visibility: visible;
}
.shell[data-side="false"] .side { visibility: hidden; }
.shell[data-side="true"] .side { transform: none; visibility: visible; }
.side__top, .side__nav, .list, .side__foot { width: auto; }
.shell[data-side="true"] .scrim { display: block; position: absolute; inset: 0; z-index: calc(var(--z-panel) - 1); background: var(--scrim); }
.thread { padding: var(--sp-4) var(--sp-3) var(--sp-3); }
.dock { padding: 0 var(--sp-3) var(--sp-3); }
.page__body { padding: var(--sp-4) var(--sp-3); }
.card { padding: var(--sp-4); gap: var(--sp-4); }
.hello__title { font-size: var(--text-xl); }
.settings__layout { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
.settings__nav { grid-auto-flow: column; grid-auto-columns: max-content; overflow-x: auto; padding: var(--sp-2) var(--sp-3); border-bottom: var(--border-w) solid var(--border); }
.settings__body { padding: var(--sp-4) var(--sp-3) var(--sp-8); }
.set__line { grid-template-columns: minmax(0, 1fr); }
.set__control { justify-items: start; }
.run { grid-template-columns: minmax(0, 1fr) auto; }
.run__model { display: none; }
.run__status { grid-row: 1; }
.palette__hint { display: none; }
.palette__item { grid-template-columns: 1rem minmax(0, 1fr); }
.kbd { display: none; }
.side__new { grid-template-columns: 16px minmax(0, 1fr); }
}
/* Very narrow: the agent's words go, its dot stays; the row's button goes under its field. */
@media (max-width: 30rem) {
.bar__agent-text { display: none; }
.bar > .bar__agent { grid-template-columns: var(--dot-size); }
.setup .row { grid-template-columns: minmax(0, 1fr); }
.setup .field { grid-template-rows: auto auto auto; }
.task__open { grid-template-columns: var(--sp-8) minmax(0, 1fr); }
.task__last { display: none; }
}