Loose-ends endpoint only carries pending state — a question disappearing from the list can mean answered, cancelled by asker, or TTL expired. Previous [answered ✓] glyph implied successful operator response across all three paths. Rename .ask-answered-tag → .ask-resolved-tag and use the neutral [resolved] label. Full resolution detail (who answered with what) remains visible via the question's history in the side panel. Addresses argus review note on #668.
937 lines
28 KiB
CSS
937 lines
28 KiB
CSS
/* Shared Catppuccin palette + body typography + terminal pane styles.
|
||
Bundled in front of the agent-only rules below via esbuild. */
|
||
@import "@hive/shared/base.css";
|
||
@import "@hive/shared/terminal.css";
|
||
|
||
/* ─── full-screen vibec0re overhaul (issue #360) ──────────────────
|
||
Layout shape: fixed-position frosted-glass header at top, fixed-
|
||
position composer at bottom, full-viewport terminal in between.
|
||
The terminal scrolls — its text passes BENEATH the floating
|
||
header/composer with backdrop-filter blur for the frosted look.
|
||
Inbox + loose-ends move into the side-panel flyout; header pills
|
||
surface their counts as the only chrome they get. */
|
||
|
||
:root {
|
||
/* Bumped to 6em (#394) so the agent icon can be a full-height
|
||
square identity anchor without crowding the two-row main column
|
||
(title + nav-links on top, state strip below). */
|
||
--agent-header-h: 6em;
|
||
--agent-composer-h: 3.6em;
|
||
--agent-frost-bg: rgba(30, 30, 46, 0.72);
|
||
--agent-frost-blur: blur(12px) saturate(140%);
|
||
}
|
||
|
||
html, body { height: 100%; margin: 0; }
|
||
|
||
/* Legacy in-page layout retained for the sibling stats page
|
||
(`stats.html`) which doesn't apply `body.agent-shell` and stays
|
||
on a normal-document scroll. */
|
||
body:not(.agent-shell) {
|
||
max-width: 110em;
|
||
margin: 1.5em auto;
|
||
padding: 0 1.5em;
|
||
height: auto;
|
||
}
|
||
.banner {
|
||
text-align: center;
|
||
margin: 0 0 1em 0;
|
||
font-size: 0.95em;
|
||
overflow-x: auto;
|
||
background: linear-gradient(
|
||
90deg,
|
||
var(--purple-dim) 0%,
|
||
var(--purple) 50%,
|
||
var(--purple-dim) 100%
|
||
);
|
||
background-size: 200% 100%;
|
||
background-position: 50% 0;
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
filter: drop-shadow(0 0 6px rgba(203, 166, 247, 0.45));
|
||
}
|
||
|
||
body.agent-shell {
|
||
background: var(--bg);
|
||
color: var(--fg);
|
||
/* Body itself doesn't scroll; the terminal does inside .agent-main. */
|
||
overflow: hidden;
|
||
/* Subtle radial accent to give the otherwise-flat full-screen
|
||
surface some depth and reinforce the vibec0re mood. */
|
||
background:
|
||
radial-gradient(ellipse 80% 60% at 50% 0%,
|
||
rgba(203, 166, 247, 0.06) 0%,
|
||
transparent 60%),
|
||
var(--bg);
|
||
}
|
||
|
||
.agent-header {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 30;
|
||
min-height: var(--agent-header-h);
|
||
display: flex;
|
||
/* align-items: stretch lets the icon take the full header height
|
||
(it sizes itself via aspect-ratio off the stretched height). The
|
||
main column + pills column self-centre via inner layout. */
|
||
align-items: stretch;
|
||
gap: 0.9em;
|
||
padding: 0.5em 1em;
|
||
background: var(--agent-frost-bg);
|
||
-webkit-backdrop-filter: var(--agent-frost-blur);
|
||
backdrop-filter: var(--agent-frost-blur);
|
||
border-bottom: 1px solid var(--purple-dim);
|
||
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
|
||
}
|
||
|
||
/* Main column: title row on top, state strip below (#394). Centred
|
||
vertically against the full-height icon on the left. */
|
||
.agent-header-main {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
gap: 0.45em;
|
||
min-width: 0;
|
||
flex: 1 1 auto;
|
||
}
|
||
.agent-header-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.8em;
|
||
flex-wrap: wrap;
|
||
min-width: 0;
|
||
}
|
||
.agent-header-title-row h2 {
|
||
margin: 0;
|
||
line-height: 1;
|
||
}
|
||
.agent-nav {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.4em 0.8em;
|
||
margin: 0;
|
||
}
|
||
|
||
.agent-state-row {
|
||
margin: 0;
|
||
gap: 0.5em;
|
||
}
|
||
|
||
/* Right cluster — flyout pills stacked / inline with the overflow
|
||
trigger. Vertically centred against the full-height icon, no
|
||
wrap; pills can drop to a row of their own under crowding via
|
||
the flex-wrap of `.agent-header-pills` itself. */
|
||
.agent-header-pills {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5em;
|
||
flex-shrink: 0;
|
||
flex-wrap: wrap;
|
||
justify-content: flex-end;
|
||
align-self: center;
|
||
}
|
||
|
||
h2, h3 {
|
||
color: var(--purple);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.15em;
|
||
text-shadow: 0 0 8px rgba(203, 166, 247, 0.4);
|
||
}
|
||
.agent-icon {
|
||
/* Square identity anchor (#394 — mara's spec). Explicit em sizing
|
||
so the <img>'s intrinsic (large) dimensions don't push the
|
||
parent flex container open via `align-items: stretch`-driven
|
||
height feedback. Width = header content area (header min-h 6em
|
||
- 2 × 0.5em padding ≈ 5em). Sticks to the top so a state-row
|
||
wrap doesn't drag the icon down with it. (#411) */
|
||
width: 5em;
|
||
height: 5em;
|
||
flex-shrink: 0;
|
||
align-self: flex-start;
|
||
border-radius: 8px;
|
||
box-shadow: 0 0 18px -2px rgba(203, 166, 247, 0.4);
|
||
object-fit: cover;
|
||
}
|
||
|
||
/* Meta-nav links (stats / screen / forge / dashboard / extras) —
|
||
no underline (#394 mara's spec); hover lights with cyan glow +
|
||
subtle background tint. Reads as a row of soft tabs rather than
|
||
default-styled inline anchors. */
|
||
.agent-nav-link {
|
||
color: var(--cyan);
|
||
text-decoration: none;
|
||
font-size: 0.85em;
|
||
letter-spacing: 0.04em;
|
||
padding: 0.1em 0.35em;
|
||
border-radius: 3px;
|
||
text-shadow: 0 0 4px rgba(137, 220, 235, 0.4);
|
||
transition: color 0.15s ease, text-shadow 0.15s ease, background 0.15s ease;
|
||
}
|
||
.agent-nav-link:hover {
|
||
color: var(--fg);
|
||
background: rgba(137, 220, 235, 0.08);
|
||
text-shadow: 0 0 10px rgba(137, 220, 235, 0.85);
|
||
}
|
||
|
||
/* Overflow menu trigger — `⋯` round button on the right of the
|
||
pills row. Quiet by default, lights on hover / open (#394). */
|
||
.overflow-btn {
|
||
background: transparent;
|
||
border: 1px solid var(--purple-dim);
|
||
color: var(--muted);
|
||
border-radius: 999px;
|
||
width: 2em;
|
||
height: 1.8em;
|
||
font-size: 1em;
|
||
line-height: 1;
|
||
cursor: pointer;
|
||
padding: 0;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
|
||
}
|
||
.overflow-btn:hover,
|
||
.overflow-btn[aria-expanded="true"] {
|
||
color: var(--purple);
|
||
border-color: var(--purple);
|
||
box-shadow: 0 0 10px -2px var(--purple);
|
||
}
|
||
|
||
/* Overflow popover — rebuild + new-session (and the dashboard
|
||
back-link, prepended in app.js setHeader). Positioned in JS so
|
||
the menu's top-right corner anchors under the trigger button.
|
||
`:not([hidden])` scoping (#411): the `[hidden]` HTML attribute
|
||
sets `display: none` via the UA stylesheet, but author CSS's
|
||
`display: flex` would override that. Scope display rules so
|
||
they apply only when the menu is unhidden. */
|
||
.overflow-menu:not([hidden]) {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.15em;
|
||
}
|
||
.overflow-menu {
|
||
position: fixed;
|
||
background: var(--agent-frost-bg);
|
||
-webkit-backdrop-filter: var(--agent-frost-blur);
|
||
backdrop-filter: var(--agent-frost-blur);
|
||
border: 1px solid var(--purple-dim);
|
||
border-radius: 6px;
|
||
padding: 0.35em;
|
||
z-index: 40;
|
||
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.45);
|
||
min-width: 14em;
|
||
}
|
||
.overflow-item {
|
||
background: transparent;
|
||
border: 1px solid transparent;
|
||
color: var(--fg);
|
||
font-family: inherit;
|
||
font-size: 0.9em;
|
||
text-align: left;
|
||
padding: 0.4em 0.7em;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.6em;
|
||
letter-spacing: 0.06em;
|
||
text-decoration: none;
|
||
text-shadow: 0 0 4px currentColor;
|
||
transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
||
}
|
||
.overflow-item:hover {
|
||
background: rgba(203, 166, 247, 0.08);
|
||
border-color: var(--purple-dim);
|
||
}
|
||
.overflow-item-icon {
|
||
font-size: 1.05em;
|
||
width: 1.4em;
|
||
text-align: center;
|
||
flex-shrink: 0;
|
||
}
|
||
.overflow-item-rebuild { color: var(--amber); }
|
||
.overflow-item-new-session { color: var(--amber); }
|
||
.overflow-item-rebuild:hover,
|
||
.overflow-item-new-session:hover {
|
||
background: rgba(250, 179, 135, 0.1);
|
||
border-color: var(--amber);
|
||
}
|
||
.overflow-item-dashboard { color: var(--cyan); }
|
||
.overflow-item-dashboard:hover {
|
||
background: rgba(137, 220, 235, 0.1);
|
||
border-color: var(--cyan);
|
||
}
|
||
.overflow-item:disabled {
|
||
opacity: 0.4;
|
||
cursor: progress;
|
||
}
|
||
|
||
/* Header pill — inbox / loose-ends triggers. Compact, count-prominent. */
|
||
.header-pill {
|
||
background: transparent;
|
||
border: 1px solid var(--purple-dim);
|
||
color: var(--fg);
|
||
font-family: inherit;
|
||
font-size: 0.85em;
|
||
letter-spacing: 0.04em;
|
||
border-radius: 999px;
|
||
padding: 0.25em 0.7em;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.4em;
|
||
cursor: pointer;
|
||
transition: border-color 0.15s ease, box-shadow 0.15s ease, color 0.15s ease;
|
||
}
|
||
.header-pill:hover {
|
||
border-color: var(--purple);
|
||
color: var(--purple);
|
||
box-shadow: 0 0 10px -2px var(--purple);
|
||
}
|
||
.header-pill-icon { font-size: 1.05em; line-height: 1; }
|
||
.header-pill-label { color: var(--muted); }
|
||
.header-pill-count {
|
||
background: var(--purple-dim);
|
||
color: var(--purple);
|
||
border-radius: 999px;
|
||
padding: 0 0.5em;
|
||
min-width: 1.6em;
|
||
text-align: center;
|
||
font-weight: bold;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.header-pill-inbox .header-pill-count {
|
||
background: rgba(250, 179, 135, 0.18);
|
||
color: var(--amber);
|
||
}
|
||
.header-pill-loose .header-pill-count {
|
||
background: rgba(243, 139, 168, 0.18);
|
||
color: var(--red);
|
||
}
|
||
|
||
.agent-main {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Login flow overlay: only rendered when status != online. Sits
|
||
centred over the (likely-empty) terminal area; doesn't take chrome
|
||
space in the normal online flow. */
|
||
.agent-status-overlay {
|
||
position: absolute;
|
||
top: calc(var(--agent-header-h) + 1.5em);
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
max-width: 44em;
|
||
width: calc(100% - 3em);
|
||
z-index: 10;
|
||
}
|
||
.agent-status-overlay:empty { display: none; }
|
||
.agent-status-overlay > * {
|
||
background: var(--bg-elev);
|
||
border: 1px solid var(--purple-dim);
|
||
border-radius: 6px;
|
||
padding: 1em 1.2em;
|
||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
|
||
}
|
||
|
||
.agent-composer {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 30;
|
||
min-height: var(--agent-composer-h);
|
||
background: var(--agent-frost-bg);
|
||
-webkit-backdrop-filter: var(--agent-frost-blur);
|
||
backdrop-filter: var(--agent-frost-blur);
|
||
border-top: 1px solid var(--purple-dim);
|
||
box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.35);
|
||
}
|
||
.agent-composer .term-input {
|
||
/* The composer is its own chrome now — drop the in-terminal-wrap
|
||
padding the legacy layout assumed. */
|
||
padding: 0.45em 1em;
|
||
}
|
||
.agent-composer .term-input .sendform-term {
|
||
/* No dashed top-border in the floating composer — the box-shadow
|
||
and frosted border already separate it from the terminal. */
|
||
border-top: 0;
|
||
padding-top: 0;
|
||
}
|
||
.meta { color: var(--muted); font-size: 0.85em; }
|
||
.status-online { color: var(--green); text-shadow: 0 0 6px rgba(166, 227, 161, 0.55); }
|
||
.status-needs-login { color: var(--amber); text-shadow: 0 0 6px rgba(250, 179, 135, 0.55); }
|
||
code { background: rgba(203, 166, 247, 0.12); padding: 0.05em 0.3em; border-radius: 2px; }
|
||
a {
|
||
color: var(--cyan);
|
||
text-shadow: 0 0 4px rgba(137, 220, 235, 0.5);
|
||
}
|
||
a:hover { color: var(--fg); text-shadow: 0 0 12px rgba(137, 220, 235, 0.9); }
|
||
.btn {
|
||
font-family: inherit;
|
||
font-size: 1em;
|
||
background: var(--bg);
|
||
border: 1px solid var(--purple);
|
||
color: var(--purple);
|
||
padding: 0.25em 0.8em;
|
||
cursor: pointer;
|
||
letter-spacing: 0.1em;
|
||
}
|
||
.btn {
|
||
text-shadow: 0 0 4px currentColor;
|
||
transition: box-shadow 0.15s ease, text-shadow 0.15s ease;
|
||
}
|
||
.btn:hover {
|
||
background: rgba(205, 214, 244, 0.06);
|
||
text-shadow: 0 0 10px currentColor;
|
||
box-shadow: 0 0 10px -2px currentColor;
|
||
}
|
||
.btn-login { color: var(--amber); border-color: var(--amber); }
|
||
.btn-cancel { color: var(--red); border-color: var(--red); font-size: 0.85em; padding: 0.15em 0.6em; }
|
||
/* `.btn-rebuild` was the per-agent header chip — moved into the
|
||
overflow menu in #394 (`.overflow-item-rebuild` covers it now).
|
||
The dashboard has its own `.btn-rebuild` rule for the per-row
|
||
R3BU1LD form on the SW4RM tab; this one was specific to the
|
||
per-agent header.
|
||
`.btn-send` was a green send-button variant — orphaned since
|
||
the dashboard's compose form was retired; no live consumer left
|
||
in either the agent or dashboard tree. */
|
||
.sendform { display: flex; gap: 0.6em; margin-top: 0.5em; }
|
||
.sendform input {
|
||
font-family: inherit; font-size: 1em;
|
||
background: rgba(255, 255, 255, 0.04);
|
||
color: var(--fg);
|
||
border: 1px solid var(--purple-dim);
|
||
padding: 0.4em 0.6em;
|
||
flex: 1;
|
||
}
|
||
.sendform input:focus { outline: 1px solid var(--purple); }
|
||
.loginform { display: flex; gap: 0.6em; margin-top: 0.5em; align-items: stretch; }
|
||
.loginform input {
|
||
font-family: inherit; font-size: 1em;
|
||
background: rgba(255, 255, 255, 0.04);
|
||
color: var(--fg);
|
||
border: 1px solid var(--purple-dim);
|
||
padding: 0.4em 0.6em;
|
||
flex: 1;
|
||
}
|
||
.loginform input:focus { outline: 1px solid var(--purple); }
|
||
|
||
/* #568: show / hide toggle for the masked OAuth-code input. Quiet
|
||
by default (muted border + transparent bg), lights amber on
|
||
hover / when pressed (aria-pressed="true") so the operator
|
||
sees at a glance whether the code is currently visible. */
|
||
.loginform-reveal {
|
||
font-family: inherit;
|
||
font-size: 1em;
|
||
background: transparent;
|
||
color: var(--muted);
|
||
border: 1px solid var(--purple-dim);
|
||
border-radius: 3px;
|
||
padding: 0 0.6em;
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
|
||
}
|
||
.loginform-reveal:hover,
|
||
.loginform-reveal:focus-visible {
|
||
color: var(--amber);
|
||
border-color: var(--amber);
|
||
outline: none;
|
||
}
|
||
.loginform-reveal[aria-pressed="true"] {
|
||
color: var(--amber);
|
||
border-color: var(--amber);
|
||
box-shadow: 0 0 8px -2px var(--amber);
|
||
}
|
||
pre.diff {
|
||
background: rgba(255, 255, 255, 0.03);
|
||
border: 1px solid var(--purple-dim);
|
||
padding: 0.6em 0.8em;
|
||
overflow-x: auto;
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
max-height: 30em;
|
||
}
|
||
/* Per-agent inbox section — collapsible, dim, lives between the
|
||
state row and the terminal so the operator can peek at what
|
||
landed without scrolling through the live tail. */
|
||
.agent-inbox {
|
||
margin: 0.4em 0;
|
||
font-size: 0.85em;
|
||
color: var(--muted);
|
||
}
|
||
.agent-inbox > summary {
|
||
cursor: pointer;
|
||
letter-spacing: 0.05em;
|
||
list-style: none;
|
||
}
|
||
.agent-inbox > summary::marker { content: ''; }
|
||
.agent-inbox[open] > summary > span::before { content: ''; }
|
||
.agent-inbox ul {
|
||
list-style: none;
|
||
padding: 0.4em 0.8em;
|
||
margin: 0.3em 0 0;
|
||
background: rgba(255, 255, 255, 0.02);
|
||
border-left: 2px solid var(--purple-dim);
|
||
max-height: 16em;
|
||
overflow-y: auto;
|
||
}
|
||
/* Inbox / loose-ends rows: header (from / sep / ts) on one line,
|
||
body on its own line below — gives the body the full panel width
|
||
instead of squeezing it into a fourth grid column that wrapped
|
||
long messages over many narrow lines (issue #376). */
|
||
.agent-inbox li {
|
||
padding: 0.4em 0;
|
||
display: block;
|
||
}
|
||
.agent-inbox .inbox-ts { color: var(--muted); font-size: 0.9em; margin-left: 0.5em; }
|
||
.agent-inbox .inbox-from { color: var(--amber); }
|
||
.agent-inbox .inbox-sep { color: var(--muted); margin-left: 0.4em; }
|
||
.agent-inbox .inbox-body {
|
||
display: block;
|
||
color: var(--fg);
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
margin-top: 0.3em;
|
||
padding-left: 0.8em;
|
||
border-left: 2px solid var(--purple-dim);
|
||
}
|
||
.agent-inbox li.inbox-reply {
|
||
padding-left: 1em;
|
||
border-left: 2px solid var(--border);
|
||
margin-left: 0.4em;
|
||
}
|
||
.agent-inbox .inbox-reply-tag { color: var(--muted); font-size: 0.85em; }
|
||
|
||
.agent-inbox .answer-form {
|
||
/* Block-level under the new layout — `grid-column: 1 / -1` was
|
||
for the legacy grid; under block layout the form naturally
|
||
starts on its own row. */
|
||
display: flex;
|
||
gap: 0.4em;
|
||
align-items: flex-start;
|
||
margin-top: 0.5em;
|
||
padding-left: 0.8em;
|
||
}
|
||
.agent-inbox .answer-form textarea {
|
||
flex: 1;
|
||
font-family: inherit;
|
||
font-size: inherit;
|
||
background: var(--bg);
|
||
color: var(--fg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 3px;
|
||
padding: 0.3em;
|
||
resize: vertical;
|
||
}
|
||
.agent-inbox .answer-form button {
|
||
font-family: inherit;
|
||
font-size: inherit;
|
||
background: var(--bg-elev);
|
||
color: var(--fg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 3px;
|
||
padding: 0.3em 0.7em;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
.agent-inbox .answer-form button:hover:not(:disabled) {
|
||
border-color: var(--purple);
|
||
color: var(--purple);
|
||
}
|
||
.agent-inbox .answer-form button:disabled { opacity: 0.5; cursor: default; }
|
||
.agent-inbox .answer-status { color: var(--muted); align-self: center; }
|
||
|
||
/* #666: inline answer slot mounted under each `ask → operator` row
|
||
in the live terminal stream. Mirrors the side-panel `.answer-form`
|
||
look-and-feel — same textarea, same send button — so the operator
|
||
doesn't have to context-switch between "answering in the panel" and
|
||
"answering inline". Empty slot collapses to nothing (no margin) so
|
||
pre-bind rows stay tidy; populated slot gets a thin top divider to
|
||
separate the question body from the form. Resolved tag (the
|
||
struck-through `[answered ✓]`) replaces the form once the
|
||
question's been answered. */
|
||
.live .ask-answer-inline-slot:empty { display: none; }
|
||
.live .ask-answer-inline-slot {
|
||
margin-top: 0.5em;
|
||
padding-top: 0.5em;
|
||
border-top: 1px dashed var(--border);
|
||
}
|
||
.live .ask-answer-inline-slot .answer-form {
|
||
display: flex;
|
||
gap: 0.4em;
|
||
align-items: flex-start;
|
||
}
|
||
.live .ask-answer-inline-slot .answer-form textarea {
|
||
flex: 1;
|
||
font-family: inherit;
|
||
font-size: inherit;
|
||
background: var(--bg);
|
||
color: var(--fg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 3px;
|
||
padding: 0.3em;
|
||
resize: vertical;
|
||
}
|
||
.live .ask-answer-inline-slot .answer-form button {
|
||
font-family: inherit;
|
||
font-size: inherit;
|
||
background: var(--bg-elev);
|
||
color: var(--fg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 3px;
|
||
padding: 0.3em 0.7em;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
.live .ask-answer-inline-slot .answer-form button:hover:not(:disabled) {
|
||
border-color: var(--purple);
|
||
color: var(--purple);
|
||
}
|
||
.live .ask-answer-inline-slot .answer-form button:disabled {
|
||
opacity: 0.5;
|
||
cursor: default;
|
||
}
|
||
.live .ask-answer-inline-slot .answer-status {
|
||
color: var(--muted);
|
||
align-self: center;
|
||
}
|
||
.live .ask-answer-inline-slot .ask-resolved-tag {
|
||
color: var(--muted);
|
||
font-style: italic;
|
||
text-decoration: line-through;
|
||
text-decoration-color: var(--muted);
|
||
}
|
||
|
||
/* #559: "mark all read" header row sits above the recent-messages
|
||
list in the inbox side-panel flyout. Same look as the answer-form
|
||
button (mauve hover, bg-elev background) so they read as part of
|
||
the same affordance family. */
|
||
.agent-inbox .inbox-mark-all-row {
|
||
display: flex;
|
||
gap: 0.6em;
|
||
align-items: center;
|
||
padding: 0.4em 0.2em 0.6em;
|
||
margin-bottom: 0.5em;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.agent-inbox .inbox-mark-all-btn {
|
||
font-family: inherit;
|
||
font-size: inherit;
|
||
background: var(--bg-elev);
|
||
color: var(--fg);
|
||
border: 1px solid var(--border);
|
||
border-radius: 3px;
|
||
padding: 0.3em 0.7em;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
.agent-inbox .inbox-mark-all-btn:hover:not(:disabled) {
|
||
border-color: var(--purple);
|
||
color: var(--purple);
|
||
}
|
||
.agent-inbox .inbox-mark-all-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: default;
|
||
}
|
||
.agent-inbox .inbox-mark-status {
|
||
color: var(--muted);
|
||
font-size: 0.9em;
|
||
}
|
||
|
||
.last-turn {
|
||
color: var(--muted);
|
||
font-size: 0.8em;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
.model-chip {
|
||
display: inline-block;
|
||
padding: 0.1em 0.6em;
|
||
border: 1px solid var(--purple-dim);
|
||
border-radius: 999px;
|
||
color: var(--cyan);
|
||
font-size: 0.78em;
|
||
letter-spacing: 0.04em;
|
||
}
|
||
/* Context-window badge. Mirrors Claude Code's bottom-right "N tokens"
|
||
chip — single primary number (total prompt tokens in use), full
|
||
breakdown on hover. Sized/coloured like a peer of model-chip so
|
||
the state row reads as one row of chrome. */
|
||
.ctx-badge {
|
||
display: inline-block;
|
||
padding: 0.1em 0.6em;
|
||
border: 1px solid var(--purple-dim);
|
||
border-radius: 999px;
|
||
color: var(--green);
|
||
font-size: 0.78em;
|
||
letter-spacing: 0.04em;
|
||
cursor: default;
|
||
white-space: pre-line;
|
||
}
|
||
|
||
/* Harness reachability badge. Same chip shape + sizing as
|
||
`.state-badge` / `.model-chip` so the state row stays visually
|
||
uniform; colour communicates the actual reachability state. */
|
||
.status-badge {
|
||
display: inline-block;
|
||
padding: 0.25em 0.8em;
|
||
border: 1px solid;
|
||
border-radius: 999px;
|
||
font-size: 0.85em;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
.status-badge.status-loading { color: var(--muted); border-color: var(--purple-dim); }
|
||
.status-badge.status-online { color: var(--green); border-color: var(--green);
|
||
text-shadow: 0 0 6px rgba(166, 227, 161, 0.55); }
|
||
.status-badge.status-rate-limited { color: var(--red); border-color: var(--red);
|
||
text-shadow: 0 0 6px rgba(243, 139, 168, 0.55); }
|
||
.status-badge.status-needs-login { color: var(--amber); border-color: var(--amber); }
|
||
.status-badge.status-offline { color: var(--muted); border-color: var(--muted); }
|
||
/* Orphaned in #394 — `.btn-dashlink` chip beside the title moved
|
||
into the overflow menu (`.overflow-item-dashboard` covers it). */
|
||
.btn-cancel-turn {
|
||
font-family: inherit;
|
||
font-size: 0.8em;
|
||
letter-spacing: 0.08em;
|
||
background: transparent;
|
||
color: var(--red);
|
||
border: 1px solid var(--red);
|
||
border-radius: 999px;
|
||
padding: 0.2em 0.8em;
|
||
cursor: pointer;
|
||
text-shadow: 0 0 4px currentColor;
|
||
transition: box-shadow 0.15s ease, background 0.15s ease;
|
||
}
|
||
.btn-cancel-turn:hover {
|
||
background: rgba(243, 139, 168, 0.1);
|
||
box-shadow: 0 0 10px -2px currentColor;
|
||
}
|
||
/* Orphaned in #394 — `.btn-new-session` round-pill moved into the
|
||
overflow menu (`.overflow-item-new-session` covers it; the
|
||
`:disabled` opacity treatment lives on the shared
|
||
`.overflow-item:disabled` rule). */
|
||
.state-badge {
|
||
display: inline-block;
|
||
padding: 0.25em 0.8em;
|
||
border: 1px solid;
|
||
border-radius: 999px;
|
||
font-size: 0.85em;
|
||
letter-spacing: 0.05em;
|
||
transition: color 280ms ease, border-color 280ms ease,
|
||
box-shadow 280ms ease, background 280ms ease;
|
||
}
|
||
.state-badge.state-loading {
|
||
color: var(--muted); border-color: var(--purple-dim);
|
||
}
|
||
.state-badge.state-offline {
|
||
color: var(--muted); border-color: var(--muted);
|
||
}
|
||
.state-badge.state-idle {
|
||
color: var(--cyan); border-color: var(--cyan);
|
||
text-shadow: 0 0 6px rgba(137, 220, 235, 0.55);
|
||
}
|
||
.state-badge.state-thinking {
|
||
color: var(--amber); border-color: var(--amber);
|
||
text-shadow: 0 0 6px rgba(250, 179, 135, 0.65);
|
||
animation: badge-pulse 1.8s ease-in-out infinite;
|
||
}
|
||
.state-badge.state-compacting {
|
||
color: var(--purple); border-color: var(--purple);
|
||
text-shadow: 0 0 6px rgba(203, 166, 247, 0.65);
|
||
animation: badge-pulse 1.8s ease-in-out infinite;
|
||
}
|
||
.state-badge.state-just-changed {
|
||
animation: state-flash 600ms ease-out;
|
||
}
|
||
@keyframes state-flash {
|
||
0% { box-shadow: 0 0 0 0 currentColor, 0 0 0 0 currentColor; }
|
||
60% { box-shadow: 0 0 18px -4px currentColor, 0 0 4px 0 currentColor; }
|
||
100% { box-shadow: 0 0 0 0 currentColor, 0 0 0 0 currentColor; }
|
||
}
|
||
/* Full-screen overrides for the shared terminal rules. The base
|
||
`.terminal-wrap` (in shared/src/terminal.css) ships a crust-on-
|
||
black frame for the in-page case; the agent page now owns the
|
||
whole viewport so the frame chrome would be redundant noise.
|
||
`.live.terminal` similarly drops the in-page max-height cap so
|
||
it can fill the main area top-to-bottom; the floating header +
|
||
composer overlay it via fixed positioning. */
|
||
.agent-main .terminal-wrap {
|
||
position: absolute;
|
||
inset: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
box-shadow: none;
|
||
border-radius: 0;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
.agent-main .live.terminal {
|
||
position: absolute;
|
||
inset: 0;
|
||
height: auto;
|
||
max-height: none;
|
||
/* Scroll behind the floating header/composer, but keep the first
|
||
and last rows reachable with extra padding inside the scroll
|
||
area. scroll-padding-* keeps anchor-jumps (the `↓ N new` pill,
|
||
focus restore) clear of the floats too. */
|
||
padding-top: calc(var(--agent-header-h) + 0.8em);
|
||
padding-bottom: calc(var(--agent-composer-h) + 0.8em);
|
||
scroll-padding-top: calc(var(--agent-header-h) + 0.8em);
|
||
scroll-padding-bottom: calc(var(--agent-composer-h) + 0.8em);
|
||
overflow: auto;
|
||
}
|
||
/* Tail pill (↓ N new): nudged up so it floats clear of the composer
|
||
rather than colliding with the frosted bar. z-index bumped above
|
||
the composer (z-30) so the pill sits on the top layer instead of
|
||
being clipped by the floating chrome (issue #375). */
|
||
.agent-main .tail-pill {
|
||
bottom: calc(var(--agent-composer-h) + 0.6em);
|
||
z-index: 35;
|
||
}
|
||
|
||
/* Composer chrome — used to live inside `.terminal-wrap`; now lives
|
||
inside the fixed `.agent-composer` defined further up. The base
|
||
rules below stay scoped to whichever ancestor owns it. */
|
||
.term-input .sendform-term {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 0.5em;
|
||
/* The dashed in-frame separator is dropped — see the
|
||
.agent-composer .term-input override above for the floating-bar
|
||
variant. */
|
||
border-top: 1px dashed var(--purple-dim);
|
||
padding-top: 0.5em;
|
||
}
|
||
.term-input .prompt, .term-input .submit-hint {
|
||
padding-top: 0.25em;
|
||
}
|
||
.term-input .prompt {
|
||
color: var(--green);
|
||
text-shadow: 0 0 6px rgba(166, 227, 161, 0.6);
|
||
user-select: none;
|
||
flex: 0 0 auto;
|
||
}
|
||
.term-input textarea {
|
||
flex: 1;
|
||
background: transparent;
|
||
border: 0;
|
||
outline: 0;
|
||
color: var(--fg);
|
||
font-family: inherit;
|
||
font-size: 1em;
|
||
padding: 0.2em 0;
|
||
caret-color: var(--green);
|
||
resize: none;
|
||
overflow-y: auto;
|
||
line-height: 1.4;
|
||
min-height: 1.4em;
|
||
}
|
||
.term-input textarea::placeholder { color: var(--muted); }
|
||
.term-input .submit-hint { color: var(--muted); font-size: 0.8em; flex: 0 0 auto; }
|
||
.term-input.disabled .prompt { color: var(--muted); text-shadow: none; }
|
||
.term-input.disabled textarea { color: var(--muted); }
|
||
/* Row + pill + details styling moved to hive-fr0nt::TERMINAL_CSS. */
|
||
|
||
/* ─── side panel (singleton drawer) ────────────────────────────────
|
||
Inbox + loose-ends details open here instead of expanding inline
|
||
(issue #360). Copy of the dashboard's side-panel pattern —
|
||
candidate for extraction into @hive/shared once both surfaces
|
||
stabilize. */
|
||
.side-panel {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 50;
|
||
/* Closed: ignore pointer events so the agent page underneath stays
|
||
interactive; `.open` flips it back on. */
|
||
pointer-events: none;
|
||
}
|
||
.side-panel-backdrop {
|
||
position: absolute;
|
||
inset: 0;
|
||
background: rgba(0, 0, 0, 0.55);
|
||
opacity: 0;
|
||
transition: opacity 0.2s ease;
|
||
}
|
||
.side-panel-drawer {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
width: min(640px, 92vw);
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: var(--bg-elev);
|
||
border-left: 2px solid var(--purple);
|
||
box-shadow: -10px 0 30px rgba(0, 0, 0, 0.45);
|
||
transform: translateX(100%);
|
||
transition: transform 0.25s ease;
|
||
}
|
||
.side-panel.open { pointer-events: auto; }
|
||
.side-panel.open .side-panel-backdrop { opacity: 1; }
|
||
.side-panel.open .side-panel-drawer { transform: translateX(0); }
|
||
.side-panel-head {
|
||
flex: 0 0 auto;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 1em;
|
||
padding: 0.7em 1em;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.side-panel-title {
|
||
color: var(--purple);
|
||
font-weight: bold;
|
||
letter-spacing: 0.05em;
|
||
word-break: break-all;
|
||
}
|
||
.side-panel-close {
|
||
flex: 0 0 auto;
|
||
background: var(--bg);
|
||
color: var(--fg);
|
||
border: 1px solid var(--border);
|
||
font-family: inherit;
|
||
font-size: 1em;
|
||
line-height: 1;
|
||
padding: 0.25em 0.55em;
|
||
cursor: pointer;
|
||
}
|
||
.side-panel-close:hover { border-color: var(--red); color: var(--red); }
|
||
.side-panel-body {
|
||
flex: 1 1 auto;
|
||
overflow: auto;
|
||
padding: 0.8em 1em;
|
||
}
|
||
/* Inbox / loose-ends lists rendered into the side-panel body. The
|
||
legacy <details>-collapsible variant of .agent-inbox is gone, so
|
||
here we strip the inbox-only chrome (background, border-left) and
|
||
let the panel body's own padding own the framing. */
|
||
.side-panel-body .agent-inbox {
|
||
margin: 0;
|
||
font-size: inherit;
|
||
color: var(--fg);
|
||
}
|
||
.side-panel-body .agent-inbox ul {
|
||
background: transparent;
|
||
border-left: 0;
|
||
padding: 0;
|
||
max-height: none;
|
||
overflow: visible;
|
||
}
|
||
|
||
/* Empty-state placeholders for the side panel (when count drops to 0
|
||
between the click and the render — rare, but possible). */
|
||
.side-panel-empty {
|
||
color: var(--muted);
|
||
font-style: italic;
|
||
padding: 1em 0;
|
||
}
|