/* 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 { /* Full-height square identity anchor (#394 — mara's spec). The `align-items: stretch` on .agent-header stretches the icon's `` box; `aspect-ratio: 1` keeps it square; `height: 100%` makes it follow the header's actual height through resizes / wrap. width:auto + aspect-ratio derives the width from height. */ height: 100%; width: auto; aspect-ratio: 1; flex-shrink: 0; 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. */ .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; display: flex; flex-direction: column; gap: 0.15em; 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 { color: var(--amber); border: 1px solid var(--amber); padding: 0.15em 0.6em; font-size: 0.55em; font-family: inherit; text-decoration: none; letter-spacing: 0.1em; margin-left: 0.6em; vertical-align: middle; cursor: pointer; } .btn-rebuild:hover { background: rgba(250, 179, 135, 0.1); } .btn-send { color: var(--green); border-color: var(--green); } .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; } .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); } 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; } /* `#state-row` layout is provided by `.agent-state-row` / `.agent-header-row` above (#394) — kept as a no-op selector anchor in case any future rule needs to scope by id. */ /* 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; } .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); } .btn-dashlink { color: var(--cyan); border: 1px solid var(--cyan); padding: 0.15em 0.6em; font-size: 0.55em; font-family: inherit; text-decoration: none; letter-spacing: 0.1em; margin-left: 0.6em; vertical-align: middle; } .btn-dashlink:hover { background: rgba(137, 220, 235, 0.1); box-shadow: 0 0 10px -2px currentColor; } .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; } .btn-new-session { font-family: inherit; font-size: 0.8em; letter-spacing: 0.08em; background: transparent; color: var(--amber); border: 1px solid var(--amber); 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-new-session:hover { background: rgba(250, 179, 135, 0.1); box-shadow: 0 0 10px -2px currentColor; } .btn-new-session:disabled { opacity: 0.4; cursor: progress; } .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
-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; }