/* 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"; /* Shared page-header chrome (page-header / page-back / page-title) — gives the agent SUB-pages (stats, screen) the same back-link nav the dashboard's standalone pages use. The live terminal page keeps its own header. */ @import "@hive/shared/chrome.css"; /* Shared pill/chip/badge shape (the "extract pill component" issue, resolved as shared CSS rather than a JS component) — see pill.css itself for the classnames it covers. */ @import "@hive/shared/pill.css"; /* Themed dialog/toast component CSS (modal.js) now lives in each component's own shadow root (adoptedStyleSheets), not a global stylesheet — nothing to @import here any more. */ /* ─── full-screen layout overrides ───────────────────────────────── The agent page mounts a full-viewport terminal under a fixed frosted-glass header + composer pair. See docs/web-ui.md::Per-agent page for the layer / pill / side-panel structure; rules below override the in-page defaults from @hive/shared. */ :root { /* 6em min-height accommodates the full-height agent icon (5em + padding) alongside 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: color-mix(in srgb, var(--bg) 72%, transparent); --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. screen-shell is also excluded — it manages its own full-viewport layout below. */ body:not(.agent-shell):not(.screen-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 color-mix(in srgb, var(--purple) 45%, transparent)); } 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%, color-mix(in srgb, var(--purple) 6%, transparent) 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. 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 color-mix(in srgb, var(--purple) 40%, transparent); } .agent-icon { /* Square identity anchor — explicit 5em sizing + align-self. See docs/web-ui.md::Per-agent page (Agent icon) for the intrinsic-dim-pushes-parent-flex-open + sticks-to-top rationale. */ width: 5em; height: 5em; flex-shrink: 0; align-self: flex-start; border-radius: 8px; box-shadow: 0 0 18px -2px color-mix(in srgb, var(--purple) 40%, transparent); object-fit: cover; } /* Meta-nav links (stats / screen / forge / dashboard / extras) — no underline; 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 color-mix(in srgb, var(--cyan) 40%, transparent); transition: color 0.15s ease, text-shadow 0.15s ease, background 0.15s ease; } .agent-nav-link:hover { color: var(--fg); background: color-mix(in srgb, var(--cyan) 8%, transparent); text-shadow: 0 0 10px color-mix(in srgb, var(--cyan) 85%, transparent); } /* Overflow menu trigger — `⋯` round button on the right of the pills row. Quiet by default, lights on hover / open. */ .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 + logout (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. See docs/web-ui.md::Per-agent page (Overflow button) for the `:not([hidden])` scoping rationale. */ .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: color-mix(in srgb, var(--purple) 8%, transparent); 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: color-mix(in srgb, var(--amber) 10%, transparent); border-color: var(--amber); } .overflow-item-dashboard { color: var(--cyan); } .overflow-item-dashboard:hover { background: color-mix(in srgb, var(--cyan) 10%, transparent); border-color: var(--cyan); } .overflow-item:disabled { opacity: 0.4; cursor: progress; } .overflow-sep { height: 1px; background: var(--border); margin: 0.3em 0.4em; } .overflow-section-label { font-size: 0.72em; letter-spacing: 0.1em; color: var(--muted); padding: 0 0.7em 0.1em; text-transform: uppercase; } .overflow-item-model, .overflow-item-effort { color: var(--muted); font-variant-numeric: tabular-nums; } .overflow-item-model:hover, .overflow-item-effort:hover { color: var(--fg); background: color-mix(in srgb, var(--purple) 8%, transparent); border-color: var(--purple-dim); } .overflow-item-model.active, .overflow-item-effort.active { color: var(--purple); border-color: var(--purple-dim); background: color-mix(in srgb, var(--purple) 6%, transparent); } /* Header pill — inbox / loose-ends triggers. Compact, count-prominent. Markup carries `hive-pill` alongside `header-pill` (shared pill.css supplies border/radius/padding/font-size/letter-spacing; `display` is the one thing overridden here, to `inline-flex`, for the icon+ label+count row layout — see pill.css's own note on that). */ .header-pill { background: transparent; border-color: var(--purple-dim); color: var(--fg); font-family: inherit; 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: color-mix(in srgb, var(--amber) 18%, transparent); color: var(--amber); } .header-pill-todos .header-pill-count { background: color-mix(in srgb, var(--green) 18%, transparent); color: var(--green); } .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 color-mix(in srgb, var(--green) 55%, transparent); } .status-needs-login { color: var(--amber); text-shadow: 0 0 6px color-mix(in srgb, var(--amber) 55%, transparent); } code { background: color-mix(in srgb, var(--purple) 12%, transparent); padding: 0.05em 0.3em; border-radius: 2px; } a { color: var(--cyan); text-shadow: 0 0 4px color-mix(in srgb, var(--cyan) 50%, transparent); } a:hover { color: var(--fg); text-shadow: 0 0 12px color-mix(in srgb, var(--cyan) 90%, transparent); } .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: color-mix(in srgb, var(--fg) 6%, transparent); 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; } /* Orphaned rules — left here as a tombstone so a search for the class name finds them. Live consumers gone: - `.btn-rebuild` was a per-agent header chip (covered now by `.overflow-item-rebuild` in the overflow menu). - `.btn-send` was a green send-button variant — the dashboard's compose form that used it is retired. */ .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); } /* 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. */ .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; } /* Todos flyout per-row checkbox (bulk mark-done) — sits inline before the existing `.inbox-from` label, same row. */ .agent-inbox .todo-cb { vertical-align: middle; margin-right: 0.2em; } .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); } /* Running bash-tasks flyout rows (buildBashTasksList). */ .agent-inbox .bash-task-status { font-weight: bold; font-size: 0.9em; } .agent-inbox .bash-task-running { color: var(--green); } .agent-inbox .bash-task-pending { color: var(--blue); } .agent-inbox .bash-task-cmd { font-family: monospace; font-size: 0.92em; } .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; } /* Bulk-action header row: "mark all read" above the recent-messages list in the inbox flyout, and "select all / select none / mark done" above the list in the todos flyout — same classes, shared look (mauve hover, bg-elev background) so both 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; } /* Markup carries `hive-pill-sm` alongside each of these (shared pill.css supplies the shape); rules below are colour only. */ .model-chip, .effort-chip { border-color: var(--purple-dim); } .model-chip { color: var(--cyan); } .effort-chip { color: var(--amber); } /* 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 { border-color: var(--purple-dim); color: var(--green); cursor: default; white-space: pre-line; } /* Harness reachability badge. Markup carries `hive-pill` alongside `status-badge` (shared pill.css supplies the shape); colour communicates the actual reachability state. */ .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 color-mix(in srgb, var(--green) 55%, transparent); } .status-badge.status-rate-limited { color: var(--red); border-color: var(--red); text-shadow: 0 0 6px color-mix(in srgb, var(--red) 55%, transparent); } .status-badge.status-needs-login { color: var(--amber); border-color: var(--amber); } .status-badge.status-offline { color: var(--muted); border-color: var(--muted); } /* Orphaned tombstone — `.btn-dashlink` chip that lived beside the title was moved into the overflow menu (`.overflow-item-dashboard` covers it now). */ .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: color-mix(in srgb, var(--red) 10%, transparent); box-shadow: 0 0 10px -2px currentColor; } /* Orphaned tombstone — `.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). */ /* Markup carries `hive-pill` alongside `state-badge` (shared pill.css supplies the shape); this rule is only the transition + state- specific colour below. */ .state-badge { 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 color-mix(in srgb, var(--cyan) 55%, transparent); } .state-badge.state-thinking { color: var(--amber); border-color: var(--amber); text-shadow: 0 0 6px color-mix(in srgb, var(--amber) 65%, transparent); 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 color-mix(in srgb, var(--purple) 65%, transparent); 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. (Pill is anchored in `.agent-main` rather than `.terminal-wrap` — see docs/web-ui.md::Per-agent page Terminal-wrap paragraph for the backdrop-filter stacking-context rationale.) */ .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 color-mix(in srgb, var(--green) 60%, transparent); 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. The drawer chrome itself (backdrop, drawer, resize handle, header, title, close button) is now the shared `` element (@hive/shared/side-panel.js — this also picks up drag-to-resize, which this page didn't have before); what's left here is the drag cursor-override escape hatch (has to be a global, light-DOM rule — it reaches every element on the page during a drag, not just the shared element's own shadow tree) and the styling for the panel's own content types, which the shared element's shadow tree can never reach (see @hive/shared/side-panel/hive-side-panel.css's header comment). The `hive-side-panel .agent-inbox …` rules below use the element's own tag name as the selector root — no compatibility class needed, the tag name already uniquely identifies the light-DOM instance app.js's `sidePanel` creates. */ body.side-panel-resizing { user-select: none; } body.side-panel-resizing * { cursor: ew-resize !important; } /* Inbox / loose-ends lists rendered into the side-panel body. The legacy
-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. */ hive-side-panel .agent-inbox { margin: 0; font-size: inherit; color: var(--fg); } hive-side-panel .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; } /* Hive identity row — shows "swarm / hive" label beneath the title. Hidden until JS populates from /api/state (hive_name + swarm_name). Mirrors the `.banner-thin` style in dashboard.css but kept tight (no letter-spacing, smaller text) so it reads as a sub-label. */ .agent-hive-row { /* Omit the default row gap when the row is hidden to avoid a blank gap between the title row and the state row. */ gap: 0; } .agent-hive-label { font-size: 0.72em; letter-spacing: 0.1em; color: var(--purple-dim); text-transform: uppercase; } /* ─── /stats page ────────────────────────────────────────────────────────── */ /* The stats page header (back link + title) uses the shared `.page-header` chrome (chrome.css), same as /screen. */ .window-tabs { display: flex; gap: 0.4rem; margin: 0.5rem 0 1rem; } .window-tabs button { background: var(--bg-elev); color: var(--fg); border: 1px solid var(--border); padding: 0.3rem 0.8rem; font-family: inherit; cursor: pointer; } /* Active window: createTabStrip (@hive/shared/tabs.js) toggles `.hive-tab--active`. The window selector keeps its own pill look rather than the shared `.hive-tab` text-tab base. */ .window-tabs button.hive-tab--active { background: var(--purple-dim); border-color: var(--purple); color: var(--purple); } .summary { display: flex; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 1rem; } .summary .chip { display: flex; flex-direction: column; justify-content: center; align-items: stretch; background: var(--bg-elev); border: 1px solid var(--border); padding: 0.5rem 0.9rem; border-radius: 4px; box-sizing: border-box; min-width: 9rem; height: 3.4rem; line-height: 1.2; } .summary .chip .label { color: var(--muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; } .summary .chip .value { color: var(--cyan); font-weight: bold; font-size: 1.05rem; } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); gap: 1rem; } .stats-card { background: var(--bg-elev); border: 1px solid var(--border); padding: 0.75rem 1rem 1rem; border-radius: 4px; } .stats-card h3 { margin: 0 0 0.5rem; color: var(--purple); font-size: 0.95rem; font-weight: normal; } .stats-card .chart-wrap { position: relative; height: 220px; } .stats-card.wide { grid-column: 1 / -1; } .stats-card.wide .chart-wrap { height: 260px; } /* ─── /screen page ─── */ /* Full-screen VNC canvas viewer (screen.html). All rules scoped to body.screen-shell so they can coexist with the agent/stats rules. */ body.screen-shell { font-size: 14px; overflow: hidden; } /* The screen page header (back link + title + controls) uses the shared `.page-header` chrome (chrome.css). The fit/match/debug controls + status chip below sit inside that header. */ /* Toolbar toggle buttons (fit / match-size / debug). */ .tbtn { padding: 0.15rem 0.5rem; font-size: 0.72rem; font-family: inherit; background: var(--border); color: var(--subtext0); border: 1px solid var(--purple-dim); border-radius: 4px; cursor: pointer; } .tbtn.active { color: var(--green); border-color: var(--green); } .tbtn:disabled { opacity: 0.4; cursor: default; } /* Connection status chip at the right end of the screen page header. */ #status { margin-left: auto; font-size: 0.75rem; color: var(--subtext0); } #status.connected { color: var(--green); } #status.error { color: var(--red); } /* RFB debug log — fixed panel at the bottom, hidden by default. */ #debug-log { position: fixed; bottom: 0; left: 0; right: 0; max-height: 40vh; overflow-y: auto; background: color-mix(in srgb, var(--crust) 95%, transparent); border-top: 1px solid var(--purple-dim); font-size: 0.72rem; font-family: ui-monospace, monospace; padding: 0.4rem 0.6rem; z-index: 100; display: none; /* toggled by the debug button */ } #debug-log .dbg-line { color: var(--subtext0); margin: 1px 0; white-space: pre; } #debug-log .dbg-line.err { color: var(--red); } #debug-log .dbg-line.ok { color: var(--green); } #debug-log .dbg-line.send { color: var(--blue); } /* Canvas container — centres the VNC framebuffer. */ #canvas-wrap { display: flex; justify-content: center; align-items: flex-start; width: 100%; height: calc(100% - 36px); overflow: auto; background: var(--crust); } /* Fit mode: canvas scaled in JS to fill the container. */ #canvas-wrap.fit { align-items: center; overflow: hidden; } #canvas-wrap canvas { display: block; cursor: default; } /* Pin the canvas to the JS-calculated size; prevents flex-item min-width:auto from silently expanding beyond the scaled size. */ #canvas-wrap.fit canvas { flex: none; min-width: 0; min-height: 0; } /* Transient flash message (copy-text, disconnect, etc.). */ #msg { position: fixed; bottom: 1rem; left: 50%; transform: translateX(-50%); background: var(--border); color: var(--yellow); border-radius: 6px; padding: 0.4rem 0.9rem; font-size: 0.8rem; opacity: 0; transition: opacity 0.3s; pointer-events: none; }