hyperhive/frontend/packages/dashboard/src/dashboard.css
iris d9c66de069 refactor(frontend): move S3TT1NGS to its own /settings.html page (#1464 step 2)
Extracts the operator-local preferences (browser-notification toggle)
out of the dashboard tab strip into a standalone /settings.html page,
reached from the H0M3 hub — same minimal-chrome pattern as /flow.html
and /logs.html. The dashboard tab strip is now purely operational.

- new settings.{html,css,js}; settings.js binds NOTIF + renders the
  shared server-warnings banner (matching the other standalone pages).
- drop the S3TT1NGS tab + pane from dashboard.html; the NOTIF.bind()
  toggle wiring moves to settings.js. The dashboard keeps NOTIF.show()
  (approvals / questions) — it reads the same browser permission +
  localStorage mute flag the settings page sets, so firing still works.
- move .notif-row / .btn-notif from dashboard.css to settings.css.
- add a Settings tile to the H0M3 hub; wire the new entries into
  build.mjs and update the nix/frontend.nix asset manifest comment.

First slice of #1464 step 2; the ST4TS page extraction follows separately.
2026-06-08 23:44:22 +02:00

1507 lines
39 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.

/* Dashboard-specific styles. common.css must be loaded first —
it defines the CSS custom properties, typography, and shared
component rules that this file depends on. */
/* ─── tabbed dashboard chrome ──────────────────────────────────────
Top-of-page sticky header with banner + tab strip. SSE stays
alive across tab switches so count pills update live on inactive
tabs. See docs/web-ui.md::Chrome header + Tab strip for the
routing model. */
body.dashboard-shell {
/* Full-width layout — no max-width cap so wide screens don't
waste real estate on empty side margins. `padding: 0 1.5em
1.5em` keeps a small gutter on the left/right so cards don't
kiss the viewport edge; `.dashboard-chrome { margin: 0 -1.5em
... }` still pulls the chrome bar edge-to-edge through that
gutter. */
margin: 0;
padding: 0 1.5em 1.5em;
}
.dashboard-chrome {
position: sticky;
top: 0;
z-index: 25;
background: color-mix(in srgb, var(--bg) 86%, transparent);
-webkit-backdrop-filter: blur(8px) saturate(120%);
backdrop-filter: blur(8px) saturate(120%);
border-bottom: 1px solid var(--purple-dim);
padding: 0.4em 0 0;
margin: 0 -1.5em 1em;
}
/* ← home back-link to the H0M3 hub. Small purple link top-left of the
chrome, mirroring the .logs-back treatment the sub-pages use so the
navigation reads consistently. Full chrome unification (a shared,
reusable tab/back-link component) is a later step. */
.dash-home-back {
display: inline-block;
color: var(--purple);
text-decoration: none;
font-size: 0.82em;
padding: 0.1em 1em 0;
white-space: nowrap;
}
.dash-home-back:hover { text-decoration: underline; }
.banner-thin {
text-align: center;
margin: 0;
padding: 0 1em;
font-size: 0.78em;
color: var(--purple-dim);
letter-spacing: 0.15em;
white-space: pre;
overflow: hidden;
text-overflow: ellipsis;
}
.tabbar {
display: flex;
flex-wrap: nowrap;
align-items: center;
gap: 0.2em;
padding: 0.5em 1em 0;
border-bottom: 1px solid var(--purple-dim);
}
.tabbar .tab {
display: inline-flex;
align-items: center;
gap: 0.5em;
flex-shrink: 0;
padding: 0.55em 1em 0.45em;
margin-bottom: -1px; /* overlap the tabbar bottom border */
color: var(--muted);
font-family: inherit;
font-size: 0.92em;
letter-spacing: 0.08em;
text-decoration: none;
border: 1px solid transparent;
border-bottom: 0;
border-radius: 4px 4px 0 0;
cursor: pointer;
transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}
.tabbar .tab:hover {
color: var(--purple);
background: color-mix(in srgb, var(--purple) 6%, transparent);
}
.tabbar .tab.hive-tab--active {
color: var(--purple);
border-color: var(--purple-dim);
background: var(--bg);
/* Lift the active tab visually — the bottom border of the tabbar
yields under it via the -1px margin above. */
box-shadow: 0 -2px 12px -4px color-mix(in srgb, var(--purple) 40%, transparent);
}
.tab-label { font-weight: bold; white-space: nowrap; }
.tab-count {
display: inline-block;
background: var(--purple-dim);
color: var(--purple);
border-radius: 999px;
padding: 0 0.55em;
min-width: 1.6em;
text-align: center;
font-size: 0.82em;
font-variant-numeric: tabular-nums;
font-weight: bold;
}
.tab-count-attn {
/* Y3R C4LL — operator-action-required pill colour, harder to miss. */
background: color-mix(in srgb, var(--red) 22%, transparent);
color: var(--red);
}
/* Tab pane visibility — createTabStrip (@hive/shared/tabs.js) sets the
`hidden` attribute on every inactive pane (resolved from the URL hash;
default — no hash — is SW4RM). Only the active pane stays visible. */
.tab-pane[hidden] { display: none; }
/* FL0W is a separate page (`/flow.html`) — its full-viewport
styling lives in flow.css. */
.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));
}
.banner.active {
animation: banner-shimmer 1.8s linear infinite;
}
@keyframes banner-shimmer {
from { background-position: 200% 0; }
to { background-position: -100% 0; }
}
.role {
display: inline-block;
margin-left: 0.4em;
padding: 0.05em 0.5em;
border: 1px solid;
border-radius: 2px;
font-size: 0.8em;
letter-spacing: 0.1em;
text-transform: uppercase;
}
/* Container rows: a full-height square agent icon on the left, the
identity / actions / drill-in lines stacked in the card body on the
right. Pending rows dim everything except the pending indicator. */
.containers { display: flex; flex-direction: column; gap: 0.4em; }
.container-row {
padding: 0.6em 0.8em;
border: 1px solid var(--border);
border-radius: 4px;
background: color-mix(in srgb, var(--bg-elev) 55%, transparent);
transition: opacity 200ms ease, border-color 200ms ease;
}
/* Topology indent ladder. See docs/web-ui.md::Topology tree (Indent
+ lane geometry paragraph) for the 1.8em-per-depth-level
rationale + CSS-attr()-not-yet-portable caveat. */
.container-row[data-depth] { position: relative; }
.container-row[data-depth="1"] { margin-left: 1.8em; }
.container-row[data-depth="2"] { margin-left: 3.6em; }
.container-row[data-depth="3"] { margin-left: 5.4em; }
.container-row[data-depth="4"] { margin-left: 7.2em; }
.container-row[data-depth="5"] { margin-left: 9em; }
.container-row[data-depth="6"] { margin-left: 10.8em; }
/* Tree prefix lanes — DOM-painted, not text-glyph-painted. */
.container-row .tree-prefix {
position: absolute;
top: 0;
bottom: -0.4em;
display: flex;
flex-direction: row;
pointer-events: none;
user-select: none;
color: var(--purple-dim);
}
.container-row[data-depth="1"] .tree-prefix { left: -1.8em; }
.container-row[data-depth="2"] .tree-prefix { left: -3.6em; }
.container-row[data-depth="3"] .tree-prefix { left: -5.4em; }
.container-row[data-depth="4"] .tree-prefix { left: -7.2em; }
.container-row[data-depth="5"] .tree-prefix { left: -9em; }
.container-row[data-depth="6"] .tree-prefix { left: -10.8em; }
.tree-prefix .tree-lane {
flex: 0 0 1.8em;
position: relative;
}
.tree-prefix .lane-line::before,
.tree-prefix .lane-joint::before {
content: '';
position: absolute;
left: 0.6em;
top: 0;
bottom: 0;
border-left: 1px solid currentColor;
}
.tree-prefix .lane-joint-last::before {
bottom: auto;
height: 3.1em;
}
.tree-prefix .lane-joint::after {
content: '';
position: absolute;
left: 0.6em;
top: 3.1em;
width: 2em;
border-top: 1px solid currentColor;
}
.container-row:not(.tombstone) {
display: flex;
align-items: flex-start;
gap: 0.7em;
}
.container-row:not(.tombstone) > .container-icon {
position: relative;
overflow: hidden;
flex: none;
width: 5em;
aspect-ratio: 1;
border-radius: 6px;
background-color: color-mix(in srgb, var(--crust) 60%, transparent);
cursor: pointer;
transition: box-shadow 120ms ease, transform 120ms ease;
}
.container-row:not(.tombstone) > .container-icon:hover {
box-shadow: 0 0 0 2px var(--purple);
}
.container-row:not(.tombstone) > .container-icon:focus-visible {
outline: 2px solid var(--purple);
outline-offset: 2px;
}
.container-row.selected {
border-color: var(--purple);
box-shadow: inset 0 0 0 1px var(--purple);
background: color-mix(in srgb, var(--purple) 6%, transparent);
}
.container-row.selected > .container-icon {
box-shadow: 0 0 0 2px var(--purple), 0 0 12px -4px var(--purple);
}
.container-row:not(.tombstone) > .container-icon > .container-icon-img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: contain;
}
.container-row:not(.tombstone) > .container-icon.icon-unreachable {
filter: grayscale(1);
opacity: 0.4;
}
.container-row .card-body {
flex: 1;
min-width: 0;
}
/* ── per-agent three-dot context menu ─────────────────────────────────────
Positioned after .card-body in the flex row. */
.agent-menu {
flex: none;
position: relative;
align-self: flex-start;
margin-top: 0.3em;
}
.agent-menu-btn {
display: block;
background: none;
border: none;
color: var(--subtext0);
font-size: 1.1em;
line-height: 1;
cursor: pointer;
padding: 0.1em 0.4em;
border-radius: 4px;
opacity: 0;
transition: opacity 120ms, background 120ms, color 120ms;
}
.container-row:hover .agent-menu-btn,
.agent-menu.open .agent-menu-btn {
opacity: 1;
}
.agent-menu-btn:hover,
.agent-menu-btn:focus-visible {
background: color-mix(in srgb, var(--purple) 10%, transparent);
color: var(--purple);
outline: none;
}
.agent-menu-btn:focus-visible {
outline: 1px solid var(--purple);
}
.agent-menu-dropdown {
position: absolute;
right: 0;
top: calc(100% + 2px);
z-index: 50;
background: var(--bg-elev);
border: 1px solid var(--purple-dim);
border-radius: 6px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
list-style: none;
margin: 0;
padding: 0.3em 0;
min-width: 10em;
white-space: nowrap;
}
.agent-menu-item {
display: block;
width: 100%;
background: none;
border: none;
color: var(--fg);
font-family: inherit;
font-size: 0.88em;
letter-spacing: 0.04em;
text-align: left;
padding: 0.4em 0.9em;
cursor: pointer;
text-decoration: none;
box-sizing: border-box;
}
.agent-menu-item:hover {
background: var(--border);
color: var(--purple);
}
.agent-menu-sep {
height: 1px;
background: var(--purple-dim);
margin: 0.3em 0;
padding: 0;
}
/* Pending state splits queued vs running. */
.container-row.pending .actions { opacity: 0.4; pointer-events: none; }
.container-row.pending-running {
border-color: var(--amber);
background: color-mix(in srgb, var(--amber) 5%, transparent);
}
@keyframes container-icon-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.container-row.pending-running > .container-icon {
overflow: visible;
}
.container-row.pending-running > .container-icon::after {
content: '';
position: absolute;
inset: -5px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: var(--amber);
animation: container-icon-spin 0.9s linear infinite;
pointer-events: none;
}
.container-row .head {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.5em;
margin-bottom: 0.4em;
}
.container-row .head .name {
font-size: 1.05em;
font-weight: bold;
}
.container-row .head .meta { margin-left: auto; }
.container-row .head .nav-strip {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
gap: 0.35em;
}
.nav-link {
color: var(--muted);
font-size: 0.95em;
line-height: 1;
padding: 0.15em 0.35em;
border-radius: 3px;
text-decoration: none;
transition: background 0.12s ease, color 0.12s ease;
}
.nav-link:hover {
background: color-mix(in srgb, var(--purple) 12%, transparent);
color: var(--cyan);
text-shadow: 0 0 6px color-mix(in srgb, var(--cyan) 50%, transparent);
}
.container-row .actions {
display: flex;
flex-wrap: wrap;
gap: 0.4em;
}
.container-row .actions form.inline { display: inline-block; margin: 0; }
.agent-status {
font-size: 0.82em;
color: var(--subtext0);
padding: 0.1em 0.3em 0.25em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.agent-status .status-icon { opacity: 0.65; }
.agent-status .status-age { opacity: 0.5; font-size: 0.9em; margin-left: 0.2em; }
.container-row.tombstone {
border-style: dashed;
background: color-mix(in srgb, var(--bg-elev) 35%, transparent);
opacity: 0.85;
}
.container-row.tombstone .name { color: var(--muted); }
/* Notification controls — sit between the banner and the containers. */
.port-conflict {
background: color-mix(in srgb, var(--red) 8%, transparent);
border: 1px solid var(--red);
color: var(--red);
padding: 0.5em 0.8em;
margin-bottom: 0.6em;
border-radius: 4px;
text-shadow: 0 0 6px color-mix(in srgb, var(--red) 40%, transparent);
animation: questions-pulse 2.4s ease-in-out infinite;
}
.port-conflict strong { color: var(--red); }
/* .notif-row / .btn-notif moved to settings.css with the S3TT1NGS
page. */
.pending-state {
color: var(--amber);
font-size: 0.85em;
letter-spacing: 0.08em;
text-transform: uppercase;
text-shadow: 0 0 6px color-mix(in srgb, var(--amber) 55%, transparent);
animation: badge-pulse 1.6s ease-in-out infinite;
}
@keyframes badge-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
@keyframes questions-pulse {
0%, 100% { box-shadow: 0 0 12px -4px color-mix(in srgb, var(--amber) 55%, transparent); }
50% { box-shadow: 0 0 22px -2px color-mix(in srgb, var(--amber) 95%, transparent); }
}
/* Pending approval: a card with three stacked sections — identity
header, what-changed body, decision actions. */
.approvals { list-style: none; padding: 0; margin: 0.4em 0 0; }
.approval-card {
background: var(--bg-elev);
border: 1px solid var(--border);
border-left: 3px solid var(--purple);
border-radius: 4px;
padding: 0.6em 0.8em;
margin-bottom: 0.6em;
}
.approval-head {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 0.3em;
}
.approval-ts {
margin-left: auto;
color: var(--muted);
font-size: 0.85em;
}
.approval-ts.stale {
color: var(--amber);
text-shadow: 0 0 6px color-mix(in srgb, var(--amber) 50%, transparent);
}
.approval-body {
margin: 0.45em 0;
padding-left: 1.3em;
}
.approval-description {
font-size: 0.9em;
color: var(--fg);
white-space: pre-wrap;
margin-bottom: 0.35em;
}
.approval-actions {
display: flex;
gap: 0.5em;
padding-top: 0.45em;
border-top: 1px solid var(--border);
}
.approval-actions form.inline { display: inline; }
/* Inline drill-in triggers (logs / config repo / view diff). */
.drill-ins {
display: flex;
flex-wrap: wrap;
gap: 0.15em 1.1em;
margin-top: 0.4em;
}
.drill-ins .panel-trigger { margin-top: 0; }
.approval-tabs {
display: flex;
gap: 0.4em;
margin: 0.6em 0 0.4em;
}
.approval-tab {
background: transparent;
border: 1px solid var(--border);
color: var(--muted);
font: inherit;
font-size: 0.85em;
letter-spacing: 0.08em;
padding: 0.25em 0.9em;
cursor: pointer;
transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.approval-tab:hover { color: var(--fg); }
.approval-tab.active {
color: var(--purple);
border-color: var(--purple);
background: color-mix(in srgb, var(--purple) 8%, transparent);
text-shadow: 0 0 4px currentColor;
}
.approvals-history .status { font-size: 0.85em; padding: 0 0.5em; }
.status-approved { color: var(--green); }
.status-denied { color: var(--red); }
.status-failed { color: var(--amber); }
.status-cancelled { color: var(--muted); }
.glyph-approved { color: var(--green); }
.glyph-denied { color: var(--red); }
.glyph-failed { color: var(--amber); }
.glyph-cancelled { color: var(--muted); }
.meta-inputs {
list-style: none;
padding: 0;
margin: 0 0 0.8em;
display: grid;
gap: 0.2em;
}
.meta-inputs li {
padding: 0.25em 0.6em;
border: 1px solid var(--border);
background: color-mix(in srgb, var(--bg-elev) 60%, transparent);
}
.meta-inputs label {
display: flex;
align-items: baseline;
gap: 0.5em;
cursor: pointer;
font-size: 0.9em;
}
.meta-input-name { color: var(--amber); font-weight: bold; }
.meta-input-rev { color: var(--muted); }
.meta-input-ts { color: var(--muted); font-size: 0.85em; }
.meta-input-url {
color: var(--muted);
font-size: 0.85em;
margin-left: auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.meta-inputs-bulk {
margin: 0 0 0.5em;
font-size: 0.8em;
color: var(--muted);
}
.meta-bulk-btn {
font: inherit;
font-size: 1em;
background: transparent;
border: 1px solid var(--purple-dim);
color: var(--cyan);
padding: 0.1em 0.6em;
margin-right: 0.2em;
cursor: pointer;
}
.meta-bulk-btn:hover {
border-color: var(--cyan);
text-shadow: 0 0 6px currentColor;
}
.meta-input-twig {
color: var(--purple-dim);
margin-right: 0.1em;
}
.btn-meta-update {
background: color-mix(in srgb, var(--purple) 12%, transparent);
border: 1px solid var(--purple);
color: var(--purple);
text-shadow: 0 0 4px currentColor;
padding: 0.3em 1em;
font: inherit;
font-size: 0.85em;
letter-spacing: 0.08em;
cursor: pointer;
transition: box-shadow 0.15s ease, background 0.15s ease;
}
.btn-meta-update:hover:not([disabled]) {
background: color-mix(in srgb, var(--purple) 22%, transparent);
box-shadow: 0 0 10px -2px currentColor;
}
.btn-meta-update[disabled] {
opacity: 0.35;
cursor: not-allowed;
}
.meta-update-running {
margin: 0 0 0.7em;
padding: 0.4em 0.7em;
border: 1px solid var(--purple);
background: color-mix(in srgb, var(--purple) 12%, transparent);
color: var(--purple);
font-size: 0.85em;
animation: badge-pulse 1.6s ease-in-out infinite;
}
/* ─── rebuild queue panel ──────────────────────────────────────────────── */
.rebuild-queue {
list-style: none;
padding: 0;
margin: 0;
display: grid;
gap: 0.2em;
}
.rebuild-queue-entry {
padding: 0.3em 0.6em;
border: 1px solid var(--border);
background: color-mix(in srgb, var(--bg-elev) 60%, transparent);
font-size: 0.9em;
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 0.4em;
}
.rebuild-queue-entry.rqe-child { margin-left: 1.6em; border-color: var(--purple-dim); }
.rebuild-queue-entry.rqe-running {
border-color: var(--purple);
background: color-mix(in srgb, var(--purple) 12%, transparent);
animation: badge-pulse 1.6s ease-in-out infinite;
}
.rebuild-queue-entry.rqe-failed { border-color: var(--red); color: var(--red); }
.rebuild-queue-entry.rqe-cancelled { opacity: 0.6; }
.rebuild-queue-entry.rqe-done { opacity: 0.7; color: var(--green); }
.rqe-state { font-weight: bold; min-width: 1.2em; text-align: center; }
.rqe-kind { color: var(--cyan); }
.rqe-agent { color: var(--amber); font-weight: bold; }
.rqe-source {
font-size: 0.75em;
padding: 0.05em 0.45em;
border-radius: 0.7em;
border: 1px solid var(--border);
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.rqe-source-manual { color: var(--cyan); border-color: var(--cyan); }
.rqe-source-meta_update { color: var(--purple); border-color: var(--purple); }
.rqe-source-auto_update { color: var(--muted); }
.rqe-source-startup_sweep { color: var(--muted); }
.rqe-source-crash_recover { color: var(--amber); border-color: var(--amber); }
.rqe-source-approval { color: var(--green); border-color: var(--green); }
.rqe-when { color: var(--muted); font-size: 0.85em; }
.rqe-reason { color: var(--muted); font-size: 0.85em; flex: 1 1 auto; }
.rqe-step {
flex-basis: 100%;
margin: 0.1em 0 0 1.8em;
color: var(--cyan);
font-size: 0.85em;
}
.rqe-error {
flex-basis: 100%;
margin: 0.3em 0 0;
padding: 0.3em 0.5em;
background: color-mix(in srgb, var(--red) 10%, transparent);
border-left: 2px solid var(--red);
color: var(--red);
font-size: 0.8em;
white-space: pre-wrap;
}
.rqe-cancel {
margin-left: auto;
}
.rqe-cancel-btn {
background: transparent;
border: 1px solid var(--purple-dim);
color: var(--muted);
border-radius: 999px;
width: 1.6em;
height: 1.6em;
font-size: 0.85em;
line-height: 1;
padding: 0;
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;
}
.rqe-cancel-btn:hover:not(:disabled),
.rqe-cancel-btn:focus-visible {
color: var(--red);
border-color: var(--red);
box-shadow: 0 0 8px -2px var(--red);
outline: none;
}
.rqe-cancel-btn:disabled {
opacity: 0.5;
cursor: default;
}
.history-note {
margin-left: 1.8em;
margin-top: 0.2em;
color: var(--muted);
font-size: 0.85em;
white-space: pre-wrap;
word-break: break-word;
}
ul form.inline { display: inline-block; }
.spawnform { display: flex; gap: 0.6em; align-items: stretch; margin: 0.5em 0; }
.spawnform input {
font-family: inherit;
font-size: 1em;
background: var(--bg-elev);
color: var(--fg);
border: 1px solid var(--border);
padding: 0.4em 0.6em;
flex: 1;
}
.spawnform input::placeholder { color: var(--muted); }
.spawnform input:focus { outline: 1px solid var(--purple); }
.role-pending { color: var(--amber); border-color: var(--amber); }
.btn-inline {
font-family: inherit;
background: transparent;
cursor: pointer;
margin-left: 0.4em;
}
/* Off-palette warm-amber hover tint: not an exact theme var (brighter than
--amber), left as a literal pending a dedicated named var. */
.btn-inline:hover { background: rgba(255, 184, 77, 0.1); }
.kind {
display: inline-block;
margin-left: 0.4em;
padding: 0.05em 0.5em;
border: 1px solid var(--purple-dim);
color: var(--purple-dim);
border-radius: 2px;
font-size: 0.75em;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.kind-spawn { color: var(--amber); border-color: var(--amber); }
.spinner {
display: inline-block;
animation: spin 1s linear infinite;
color: var(--amber);
}
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
details { margin-top: 0.5em; }
summary {
cursor: pointer;
color: var(--muted);
font-size: 0.85em;
text-transform: uppercase;
letter-spacing: 0.1em;
}
summary:hover { color: var(--purple); }
.diff {
background: var(--bg-elev);
border: 1px solid var(--border);
padding: 0.8em;
margin-top: 0.4em;
overflow-x: auto;
font-size: 0.85em;
line-height: 1.4;
color: var(--muted);
white-space: pre;
}
.diff span { display: block; }
.diff .diff-add { color: var(--green); }
.diff .diff-del { color: var(--red); }
.diff .diff-hunk { color: var(--cyan); }
.diff .diff-file { color: var(--purple); font-weight: bold; }
.diff .diff-ctx { color: var(--fg); }
.questions {
background: var(--bg-elev);
border: 1px solid var(--amber);
box-shadow: 0 0 12px -4px var(--amber);
padding: 0.6em 0.9em;
animation: questions-pulse 2.4s ease-in-out infinite;
}
.reminders {
list-style: none;
padding: 0;
margin: 0;
}
.reminder-row {
padding: 0.4em 0;
border-bottom: 1px solid var(--border);
}
.reminder-row:last-child { border-bottom: 0; }
.reminder-head { font-size: 0.9em; }
.reminder-body {
color: var(--fg);
white-space: pre-wrap;
word-break: break-word;
margin: 0.3em 0;
}
.reminder-row.reminder-failed {
border-left: 2px solid var(--red);
padding-left: 0.5em;
}
.reminder-error {
color: var(--red);
background: color-mix(in srgb, var(--red) 6%, transparent);
border: 1px solid color-mix(in srgb, var(--red) 25%, transparent);
padding: 0.3em 0.5em;
font-size: 0.85em;
white-space: pre-wrap;
word-break: break-word;
margin: 0.2em 0;
}
.reminder-actions {
display: flex;
gap: 0.4em;
margin-top: 0.3em;
}
.questions-filters {
display: flex;
flex-wrap: wrap;
gap: 0.3em;
margin-bottom: 0.5em;
}
.q-filter-chip {
background: var(--bg);
color: var(--muted);
border: 1px solid var(--border);
border-radius: 999px;
padding: 0.15em 0.7em;
font: inherit;
font-size: 0.85em;
cursor: pointer;
}
.q-filter-chip:hover { color: var(--fg); }
.q-filter-chip.active {
color: var(--amber);
border-color: var(--amber);
}
.questions li.question-peer {
border-left: 2px solid var(--purple);
padding-left: 0.6em;
}
.questions .msg-to-peer { color: var(--purple); }
.btn-override { background: var(--purple) !important; color: var(--bg) !important; }
.questions li.question {
padding: 0.4em 0;
border-bottom: 1px solid var(--border);
}
.questions li.question:last-child { border-bottom: 0; }
.questions .q-head { font-size: 0.9em; }
.questions .q-ttl {
color: var(--amber);
margin-left: 0.4em;
font-size: 0.95em;
letter-spacing: 0.05em;
}
.questions .q-body {
color: var(--fg);
margin: 0.3em 0;
white-space: pre-wrap;
word-break: break-word;
}
.qform {
display: flex;
flex-direction: column;
gap: 0.5em;
margin-top: 0.4em;
}
.qform .q-options {
display: flex;
flex-direction: column;
gap: 0.25em;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 4px;
padding: 0.4em 0.6em;
}
.qform .q-option label { cursor: pointer; user-select: none; }
.qform .q-option input { margin-right: 0.4em; accent-color: var(--amber); }
.qform .q-free { display: flex; }
.qform .q-free textarea {
flex: 1;
font-family: inherit;
font-size: 1em;
background: var(--bg);
color: var(--fg);
border: 1px solid var(--border);
padding: 0.4em 0.6em;
resize: vertical;
line-height: 1.4;
}
.qform .q-free textarea::placeholder { color: var(--muted); }
.qform .q-free textarea:focus { outline: 1px solid var(--amber); }
.qform button { align-self: flex-start; }
.qform-cancel { margin-top: 0.3em; }
.q-history {
margin-top: 0.8em;
border: 1px solid var(--border);
border-radius: 4px;
padding: 0.4em 0.7em;
}
.q-history summary { cursor: pointer; color: var(--muted); font-size: 0.9em; user-select: none; }
.questions-answered {
border: none;
box-shadow: none;
animation: none;
padding: 0;
margin-top: 0.5em;
}
.question-answered { opacity: 0.7; }
.question-answered .q-body { color: var(--muted); margin-bottom: 0.15em; }
.q-answer { font-size: 0.9em; color: var(--green); padding: 0.1em 0 0.4em 0; }
.q-answer-text { font-style: italic; }
footer {
margin-top: 4em;
text-align: center;
color: var(--muted);
font-size: 0.9em;
}
footer a { color: var(--purple); }
footer .banner-thin {
margin-bottom: 0.8em;
}
/* ─── capabilities (permissions) table ─────────────────────────────
Agents × capabilities matrix in the P3RM1SS10NS tab. Same layout
as the tool-groups table below. Horizontally scrollable on narrow
viewports. */
.cap-table-wrap { overflow-x: auto; margin-top: 0.5em; }
.cap-table { border-collapse: collapse; font-size: 0.82em; min-width: 100%; }
.cap-table th,
.cap-table td {
padding: 0.35em 0.6em;
border: 1px solid var(--border);
text-align: center;
vertical-align: middle;
}
.cap-table thead th {
background: var(--bg-elev);
color: var(--muted);
letter-spacing: 0.05em;
white-space: nowrap;
}
.cap-agent-col {
text-align: left !important;
min-width: 8em;
}
.cap-col {
min-width: 5em;
}
.cap-save-col {
min-width: 4em;
}
.cap-agent-name {
color: var(--fg);
font-weight: 600;
}
.cap-cb {
cursor: pointer;
width: 1em;
height: 1em;
accent-color: var(--purple);
}
.cap-save-btn {
font-size: 0.78em;
padding: 0.2em 0.6em;
white-space: nowrap;
}
.cap-row:hover td {
background: var(--bg-elev);
}
/* ─── tool-groups (permissions) table ──────────────────────────────
Agents × tool-groups matrix in the P3RM1SS10NS tab. Same layout
as the capabilities table above. Horizontally scrollable on narrow
viewports. */
.tg-table-wrap { overflow-x: auto; margin-top: 0.5em; }
.tg-table { border-collapse: collapse; font-size: 0.82em; min-width: 100%; }
.tg-table th,
.tg-table td { padding: 0.35em 0.6em; border: 1px solid var(--border); text-align: center; vertical-align: middle; }
.tg-table thead th { background: var(--bg-elev); color: var(--muted); letter-spacing: 0.05em; white-space: nowrap; }
.tg-agent-col { text-align: left !important; min-width: 8em; }
.tg-group-col { min-width: 5em; }
.tg-save-col { min-width: 4em; }
.tg-agent-name { color: var(--fg); font-weight: 600; }
.tg-cb { cursor: pointer; width: 1em; height: 1em; accent-color: var(--purple); }
.tg-save-btn { font-size: 0.78em; padding: 0.2em 0.6em; white-space: nowrap; }
.tg-row:hover td { background: var(--bg-elev); }
/* ─── scheduled prompts tab ────────────────────────────────────────
Creation form at the top, list of queued schedule cards below. */
.schedule-edit-form {
display: flex;
flex-direction: column;
gap: 0.6em;
background: var(--bg-elev);
border: 1px solid var(--border);
border-radius: 4px;
padding: 0.8em 1em;
margin-bottom: 0.5em;
}
.schedule-edit-form-wrapper {
margin-top: 0.5em;
padding-left: 0.5em;
border-left: 2px solid var(--yellow);
}
.schedule-edit-targets-note {
color: var(--muted);
font-style: italic;
font-size: 0.85em;
margin: 0.2em 0 0 0;
}
.schedule-field {
display: flex;
flex-direction: column;
gap: 0.2em;
}
.schedule-field-label {
color: var(--muted);
font-size: 0.85em;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.schedule-field input,
.schedule-field textarea {
font-family: inherit;
font-size: inherit;
background: var(--bg);
color: var(--fg);
border: 1px solid var(--border);
border-radius: 3px;
padding: 0.35em 0.5em;
}
.schedule-field textarea { resize: vertical; min-height: 4em; }
.schedule-targets {
display: flex;
flex-wrap: wrap;
gap: 0.4em;
}
.schedule-target-chip {
display: inline-flex;
align-items: center;
gap: 0.4em;
padding: 0.2em 0.6em;
border: 1px solid var(--border);
border-radius: 999px;
font-size: 0.85em;
cursor: pointer;
background: color-mix(in srgb, var(--bg-elev) 40%, transparent);
}
.schedule-target-chip:hover { border-color: var(--purple-dim); }
.schedule-target-chip:has(input:checked) {
border-color: var(--purple);
color: var(--purple);
background: color-mix(in srgb, var(--purple) 8%, transparent);
}
.schedule-target-chip input { margin: 0; }
.schedule-actions {
display: flex;
gap: 0.5em;
justify-content: flex-end;
}
.schedule-interval-presets {
display: flex;
flex-wrap: wrap;
gap: 0.3em;
margin-top: 0.1em;
}
.btn-interval-preset {
color: var(--cyan);
border-color: var(--cyan);
font-size: 0.75em;
padding: 0.15em 0.55em;
letter-spacing: 0.05em;
}
.btn-interval-oneshot {
color: var(--muted);
border-color: var(--muted);
}
.schedule-interval-parts {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.35em;
margin-top: 0.4em;
}
.schedule-interval-num {
width: 4em;
text-align: right;
font-variant-numeric: tabular-nums;
}
.schedule-interval-unit {
color: var(--muted);
font-size: 0.85em;
margin-right: 0.2em;
}
.schedule-interval-preview {
margin-top: 0.4em;
color: var(--cyan);
font-size: 0.9em;
font-variant-numeric: tabular-nums;
}
.schedule-interval-preview-oneshot { color: var(--muted); font-style: italic; }
.schedules-table-wrap { overflow-x: auto; margin-top: 0.5em; }
.schedules-table {
min-width: 100%;
border-collapse: collapse;
font-size: 0.82em;
}
.schedules-table th,
.schedules-table td {
padding: 0.35em 0.6em;
border: 1px solid var(--border);
vertical-align: middle;
}
.schedules-table thead th {
background: var(--bg-elev);
color: var(--muted);
font-weight: normal;
text-transform: uppercase;
letter-spacing: 0.05em;
white-space: nowrap;
}
.schedules-table-id { width: 3em; text-align: center; }
.schedules-table-next-col { width: 8em; white-space: nowrap; }
.schedules-table-every-col { width: 7em; white-space: nowrap; }
.schedules-table-body-th { min-width: 12em; }
.schedules-table-actions-th { width: 7em; }
.schedules-table-agent-th {
width: 28px;
min-width: 28px;
height: 95px;
padding: 0 !important;
vertical-align: bottom;
white-space: nowrap;
text-transform: none !important;
letter-spacing: 0 !important;
position: relative;
}
.schedules-table-agent-th > div {
position: absolute;
bottom: 4px;
left: 14px;
transform-origin: left bottom;
transform: rotate(-45deg);
font-family: ui-monospace, 'JetBrains Mono', monospace;
font-size: 0.9em;
color: var(--fg);
}
.schedules-table-agent-th > div > span {
display: inline-block;
padding: 0 4px 1px;
}
.schedules-table-row-cancelled td { opacity: 0.55; }
.schedules-table-body-cell {
max-width: 30em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: ui-monospace, 'JetBrains Mono', monospace;
font-size: 0.9em;
}
.schedules-table-check {
width: 28px;
text-align: center;
padding: 0.3em 0 !important;
}
.schedules-table-check-btn {
background: transparent;
border: 0;
color: var(--green);
cursor: pointer;
padding: 0.1em 0.3em;
font-family: inherit;
font-size: 1em;
}
.schedules-table-check-btn:hover:not(:disabled) {
color: var(--red);
}
.schedules-table-check-btn:disabled {
cursor: default;
}
.schedules-table-check-cancelled {
color: var(--muted);
font-size: 0.9em;
}
.schedules-table-actions {
text-align: right;
white-space: nowrap;
}
.schedules-table-actions .btn-inline-small {
margin-left: 0.3em;
}
.schedules-table-edit-row > td {
/* Off-palette dim-purple surface tint: not an exact theme var, left as a
literal pending a dedicated named var so a theme swap can recolour it. */
background: rgba(38, 32, 60, 0.35);
padding: 0.5em 0.6em;
}
.schedules-table-create-row td {
/* Off-palette dim-purple surface tint — see the edit-row note above. */
background: rgba(38, 32, 60, 0.25);
border-top: 2px solid var(--border);
vertical-align: top;
padding: 0.4em 0.5em;
}
.schedules-table-create-cell {
padding: 0.35em 0.4em;
}
.schedules-table-inline-input,
.schedules-table-inline-textarea,
.schedules-table-inline-num {
background: var(--bg);
border: 1px solid var(--border);
color: var(--fg);
font-family: ui-monospace, 'JetBrains Mono', monospace;
font-size: 0.85em;
padding: 0.2em 0.4em;
border-radius: 2px;
}
.schedules-table-inline-input:focus,
.schedules-table-inline-textarea:focus,
.schedules-table-inline-num:focus {
border-color: var(--purple);
outline: none;
}
.schedules-table-inline-textarea {
display: block;
width: 100%;
min-height: 1.6em;
resize: vertical;
}
.schedules-table-create-body:focus-within .schedules-table-inline-textarea {
min-height: 4em;
}
.schedules-table-inline-desc {
display: block;
width: 100%;
margin-top: 0.25em;
}
.schedules-table-inline-datetime {
width: 100%;
font-size: 0.78em;
}
.schedules-table-inline-interval {
display: flex;
align-items: center;
gap: 0.1em;
white-space: nowrap;
}
.schedules-table-inline-num {
width: 2.4em;
text-align: right;
font-variant-numeric: tabular-nums;
-moz-appearance: textfield;
appearance: textfield;
}
.schedules-table-inline-num::-webkit-outer-spin-button,
.schedules-table-inline-num::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.schedules-table-inline-unit {
color: var(--muted);
font-size: 0.7em;
margin-right: 0.15em;
}
.schedules-table-inline-check {
width: 16px;
height: 16px;
cursor: pointer;
margin: 0;
}
.schedules-table-inline-check-lbl {
display: inline-flex;
cursor: pointer;
padding: 0.4em 0.6em;
}
.schedules-table-create-submit {
font-size: 1em;
padding: 0.15em 0.55em;
color: var(--green);
border-color: var(--green);
}
.schedules-table-create-reset {
font-size: 0.85em;
padding: 0.15em 0.45em;
color: var(--muted);
border-color: var(--border);
}
.btn-inline-small {
font-size: 0.7em;
padding: 0.1em 0.45em;
}
/* Selection bar — sticky-bottom strip for bulk actions. */
.selection-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 40;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.6em;
padding: 0.55em 1em;
background: var(--flow-frost-bg, color-mix(in srgb, var(--bg) 74%, transparent));
-webkit-backdrop-filter: blur(12px) saturate(140%);
backdrop-filter: blur(12px) saturate(140%);
border-top: 1px solid var(--purple);
box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.4);
}
.selection-bar[hidden] { display: none; }
.selection-count {
color: var(--purple);
font-weight: bold;
letter-spacing: 0.05em;
}
.selection-names {
color: var(--muted);
font-size: 0.85em;
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.selection-actions {
display: inline-flex;
flex-wrap: wrap;
gap: 0.4em;
align-items: center;
}
.selection-actions .btn {
font-size: 0.75em;
padding: 0.2em 0.7em;
}
.selection-clear {
color: var(--muted);
border-color: var(--purple-dim);
font-size: 0.75em;
padding: 0.2em 0.7em;
}
/* Pad the dashboard body so the sticky bar doesn't cover the
bottom of the agent list. Only applies when bar is visible. */
body.dashboard-shell.has-selection { padding-bottom: 4.5em; }
.move-picker {
display: inline-flex;
align-items: center;
gap: 0.3em;
margin-left: 0.6em;
}
.move-picker-select {
font-family: inherit;
font-size: 0.75em;
background: var(--bg);
color: var(--fg);
border: 1px solid var(--purple);
border-radius: 2px;
padding: 0.1em 0.3em;
max-width: 16em;
}
.move-picker-select:disabled {
opacity: 0.5;
cursor: default;
}
/* ─── ST4TS tab: hive-wide turn-stats rollup ──────────────────────────────
Plain tables + CSS bars (no chart lib in the dashboard bundle). */
.hive-stats-windows {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin: 12px 0;
}
.hive-stats-windows .btn.active {
border-color: var(--amber);
color: var(--amber);
}
.hive-stats-chips {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 16px;
}
.hive-stats-chip {
display: inline-flex;
flex-direction: column;
padding: 6px 10px;
background: var(--bg-elev);
border: 1px solid var(--border);
border-radius: 4px;
min-width: 7em;
}
.hive-stats-chip .k {
font-size: 0.72rem;
color: var(--muted);
text-transform: lowercase;
}
.hive-stats-chip .v {
font-size: 1.05rem;
color: var(--fg);
}
.hive-stats-chip.est .v {
color: var(--amber);
}
.hive-stats-table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
margin-bottom: 8px;
}
.hive-stats-table th,
.hive-stats-table td {
border-bottom: 1px solid var(--border);
padding: 5px 8px;
text-align: right;
}
.hive-stats-table th:first-child,
.hive-stats-table td:first-child {
text-align: left;
}
.hive-stats-table th {
color: var(--muted);
font-weight: normal;
}
.hive-stats-table td.num {
font-variant-numeric: tabular-nums;
}
.hive-stats-bar {
display: grid;
grid-template-columns: 12em 1fr 4em;
align-items: center;
gap: 8px;
margin: 3px 0;
font-size: 0.85rem;
}
.hive-stats-bar .track {
background: var(--bg-elev);
border: 1px solid var(--border);
border-radius: 3px;
height: 14px;
overflow: hidden;
}
.hive-stats-bar .fill {
display: block;
height: 100%;
background: var(--purple);
}
.hive-stats-bar .cnt {
text-align: right;
color: var(--muted);
font-variant-numeric: tabular-nums;
}
/* ─── SYST3M C0NT41N3R L04D: live cgroup cpu/mem ─────────────────────────────
Reuses the `.hive-stats-table` styling from the ST4TS tab; only the
inline meter bar is new. */
.cload-meter {
display: inline-block;
width: 6em;
height: 10px;
vertical-align: middle;
margin-left: 6px;
background: var(--bg-elev);
border: 1px solid var(--border);
border-radius: 3px;
overflow: hidden;
}
.cload-meter .fill {
display: block;
height: 100%;
background: var(--green);
}
.cload-meter .fill.warn {
background: var(--amber);
}
.cload-meter .fill.hot {
background: var(--red);
}