Files
hamlab/web/src/styles.css
T
Mitchell-JohnsonandClaude Opus 5 7d0ec5e018 ZFS gets its own tab, with scheduled snapshots and a retention period
Pools, datasets and snapshots move out of Disks — Disks is the hardware, this is the
filesystem on top of it. New: snapshot schedules, hourly to monthly, with a retention
period and a floor that keeps the newest few whatever their age.

The naming is zfSnap's, deliberately. grid has run zfSnap from root cron since before this
repo existed and it works, so rather than invent a second convention this writes and reads
exactly what zfSnap does: 2026-09-24_02.30.01--6w, with the retention in the name. Ham Lab
can therefore show and expire the snapshots zfSnap already took, the two cannot disagree
about what a snapshot is, and changing a schedule never re-dates old ones — each carries
the rule it was taken under. An existing cron job is detected and shown on the page, with a
warning if a Ham Lab schedule is running as well.

Nothing is destroyed unseen. Every schedule answers what it would delete, with the list,
before Run now touches anything, and only names that parse as a stamp and a TTL are ever
candidates. A snapshot taken by hand is invisible to it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 13:01:53 -07:00

736 lines
69 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.
:root {
--bg: #111418; --bg2: #171b21; --bg3: #1e242c; --border: #2a313b;
--fg: #d0d7de; --muted: #7d8590; --accent: #e3a33b; --accent2: #58a6ff;
--ok: #3fb950; --bad: #ff7b72;
--s1: #3987e5; --s2: #c98500; --track: rgba(57, 135, 229, .18);
--btn-fg: #111; --term-bg: var(--term-bg); --font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
--header-h: 44px; --footer-h: 28px; --rail-w: 220px; --tabs-h: 34px;
}
* { box-sizing: border-box; }
html, body, #root { height: 100%; margin: 0; }
body { background: var(--bg); color: var(--fg); font: 14px/1.4 var(--font); overscroll-behavior: none; transition: background .2s, color .2s; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
.spacer { flex: 1; }
.muted { color: var(--muted); }
.center { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; }
/* login */
.login { display: flex; flex-direction: column; gap: 10px; width: min(320px, 90vw); }
.login-logo { width: 176px; height: 176px; align-self: center; margin-bottom: 8px; } /* the artwork brings its own shape: no circular crop */
.login input { padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); font-size: 16px; }
.login button, .primary { padding: 10px 12px; background: var(--accent); color: var(--btn-fg); border-radius: 6px; font-weight: 600; }
.login button:disabled { opacity: .5; }
.error { color: var(--bad); font-size: 13px; }
/* layout */
.app { height: 100%; display: flex; flex-direction: column; padding-top: env(safe-area-inset-top); }
.header { height: var(--header-h); display: flex; align-items: center; gap: 12px; padding: 0 12px; background: var(--bg2); border-bottom: 1px solid var(--border); }
.brand { font-weight: 700; color: var(--accent); letter-spacing: .5px; }
.host { color: var(--muted); font-family: ui-monospace, monospace; font-size: 13px; }
.stat { font-family: ui-monospace, monospace; font-size: 12px; color: var(--muted); }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); }
.dot.ok { background: var(--ok); } .dot.bad { background: var(--bad); }
.link { color: var(--muted); } .link:hover { color: var(--fg); }
.icon-btn { padding: 2px 6px; color: var(--muted); border-radius: 4px; } .icon-btn:hover { color: var(--fg); background: var(--bg3); }
.menu { display: none; font-size: 18px; }
.body { flex: 1; display: flex; min-height: 0; position: relative; }
.footer { height: var(--footer-h); display: flex; align-items: center; gap: 16px; padding: 0 12px; background: var(--bg2); border-top: 1px solid var(--border); font-size: 12px; color: var(--muted); padding-bottom: env(safe-area-inset-bottom); box-sizing: content-box; }
/* rail */
.rail { width: var(--rail-w); background: var(--bg2); border-right: 1px solid var(--border); overflow-y: auto; padding: 6px 0; flex-shrink: 0; }
.group-head { width: 100%; display: flex; align-items: center; gap: 8px; padding: 8px 12px; text-align: left; }
.group-head:hover { background: var(--bg3); }
.group.soon .group-head { color: var(--muted); cursor: default; }
.group.soon .group-head:hover { background: none; }
.chev { width: 10px; color: var(--muted); font-size: 11px; }
.gicon { width: 18px; text-align: center; color: var(--accent2); font-family: ui-monospace, monospace; font-size: 12px; display: inline-flex; justify-content: center; align-items: center; }
.badge { margin-left: auto; font-size: 10px; text-transform: uppercase; border: 1px solid var(--border); border-radius: 3px; padding: 0 4px; }
.sdot { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); flex-shrink: 0; }
.sdot.run { background: var(--ok); }
/* main + terminal */
.main { flex: 1; min-width: 0; position: relative; background: var(--term-bg); }
.term { position: absolute; inset: var(--tabs-h) 0 0 0; padding: 6px 0 0 6px; }
.term-tabs { position: absolute; top: 0; left: 0; right: 0; height: var(--tabs-h); display: flex; align-items: stretch; background: var(--bg2); border-bottom: 1px solid var(--border); overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.term-tabs::-webkit-scrollbar { display: none; }
.ttab { flex: 0 1 200px; min-width: 96px; display: flex; align-items: center; gap: 6px; padding: 0 4px 0 10px; cursor: pointer; border-right: 1px solid var(--border); color: var(--muted); font-size: 13px; user-select: none; }
.ttab:hover { background: var(--bg3); color: var(--fg); }
.ttab.active { background: var(--term-bg); color: var(--fg); box-shadow: inset 0 2px 0 var(--accent); }
.ttab.exited .tname { text-decoration: line-through; }
.ttab .icon-btn { padding: 0 5px; font-size: 12px; }
.ttab:not(.active):not(:hover) .icon-btn { visibility: hidden; }
.ttab.new { flex: 0 0 auto; min-width: 40px; justify-content: center; padding: 0 12px; border-right: 0; color: var(--accent2); font-size: 16px; position: sticky; right: 0; background: var(--bg2); box-shadow: -1px 0 0 var(--border); }
.tname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.term-inner { height: 100%; }
.term .xterm { height: 100%; }
.xterm-viewport::-webkit-scrollbar { width: 8px; } .xterm-viewport::-webkit-scrollbar-thumb { background: var(--border); }
/* key bar (touch devices / narrow screens) */
.keybar { display: none; gap: 4px; padding: 4px 6px; background: var(--bg2); border-top: 1px solid var(--border); overflow-x: auto; white-space: nowrap; -webkit-overflow-scrolling: touch; }
.keybar button { flex: 0 0 auto; padding: 8px 10px; min-width: 40px; background: var(--bg3); border: 1px solid var(--border); border-radius: 5px; font-family: ui-monospace, monospace; font-size: 13px; }
.keybar button.on { background: var(--accent); color: var(--btn-fg); border-color: var(--accent); }
@media (pointer: coarse), (max-width: 800px) { .keybar { display: flex; } }
/* mobile: rail becomes a drawer */
.backdrop { display: none; }
@media (max-width: 800px) {
.menu { display: inline-block; }
.host, .stat { display: none; }
.rail { position: absolute; top: 0; bottom: 0; left: 0; z-index: 10; transform: translateX(-100%); transition: transform .15s ease; box-shadow: 4px 0 16px rgba(0,0,0,.5); }
.drawer-open .rail { transform: none; }
.drawer-open .backdrop { display: block; position: absolute; inset: 0; z-index: 9; background: rgba(0,0,0,.5); }
.footer { font-size: 11px; }
.ttab { flex: 0 0 auto; max-width: 70vw; }
}
/* dashboard */
.dash { position: absolute; inset: 0; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 12px; background: var(--bg); -webkit-overflow-scrolling: touch; }
.dash > * { flex-shrink: 0; }
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px; }
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 12px; align-items: start; }
.tile, .card { background: var(--bg2); border: 1px solid var(--border); border-radius: 8px; padding: 12px; min-width: 0; }
.tile .label, .card h2 { margin: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
.tile .value { font-size: 22px; font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.2; }
.tile .sub { font-size: 12px; color: var(--muted); margin-top: 2px; font-variant-numeric: tabular-nums; }
.tile .spark { margin-top: 8px; }
.card .empty { color: var(--muted); font-size: 13px; }
.row { display: grid; grid-template-columns: minmax(90px, 1fr) 2fr auto; gap: 10px; align-items: center; padding: 4px 0; font-size: 13px; min-width: 0; }
.row .k { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .row .k small { color: var(--muted); margin-left: 6px; }
.num { font-variant-numeric: tabular-nums; font-family: ui-monospace, monospace; font-size: 12px; white-space: nowrap; }
.meter { height: 6px; background: var(--track); border-radius: 3px; overflow: hidden; }
.meter .fill { height: 100%; background: var(--s1); border-radius: 3px; transition: width .3s; }
.meter .fill.warn { background: var(--s2); } .meter .fill.bad { background: var(--bad); }
.sub-card { padding: 8px 0; border-top: 1px solid var(--border); } .sub-card:first-of-type { border-top: 0; padding-top: 0; }
.sub-head { display: flex; align-items: center; gap: 8px; font-size: 13px; margin-bottom: 4px; } .sub-head .spacer { flex: 1; }
.sub-head .model { color: var(--muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.legend { display: flex; gap: 12px; font-size: 11px; color: var(--muted); margin-top: 4px; }
.sw { display: inline-block; width: 12px; height: 2px; vertical-align: middle; margin-right: 5px; border-radius: 1px; }
.spark { position: relative; width: 100%; height: 44px; touch-action: pan-y; }
.spark svg { display: block; width: 100%; height: 100%; }
.spark .tip { position: absolute; top: 0; transform: translateX(-50%); background: var(--bg3); border: 1px solid var(--border); border-radius: 4px; padding: 2px 6px; font-size: 11px; white-space: nowrap; pointer-events: none; font-variant-numeric: tabular-nums; }
.status { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; }
.rail .group-head.active { background: var(--bg3); box-shadow: inset 3px 0 0 var(--accent); }
@media (max-width: 800px) { .dash { padding: 8px; gap: 8px; } .kpis, .cards { gap: 8px; } .cards { grid-template-columns: 1fr; } }
/* dashboard: customize mode */
.dash-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dash-bar .hint { color: var(--muted); font-size: 12px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.dash-bar select.btn { padding: 3px 6px; }
.btn { padding: 5px 10px; background: var(--bg3); border: 1px solid var(--border); border-radius: 5px; font-size: 12px; color: var(--fg); }
.btn.on { background: var(--accent); color: var(--btn-fg); border-color: var(--accent); }
.mod-tools { display: flex; gap: 4px; margin: -4px -4px 6px auto; justify-content: flex-end; }
.mod-tools button { padding: 2px 7px; background: var(--bg3); border: 1px solid var(--border); border-radius: 4px; font-size: 12px; color: var(--muted); }
.mod-tools button:hover:not(:disabled) { color: var(--fg); } .mod-tools button:disabled { opacity: .35; cursor: default; }
.editing .tile, .editing .card { border-style: dashed; }
.hide-item { padding: 0 6px; color: var(--muted); font-size: 12px; border-radius: 4px; } .hide-item:hover { color: var(--bad); background: var(--bg3); }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip { padding: 3px 9px; background: var(--bg3); border: 1px solid var(--border); border-radius: 12px; font-size: 12px; color: var(--muted); }
.chip:hover { color: var(--fg); border-color: var(--accent2); }
.chips.inline { margin-top: 6px; }
/* services */
.svc { position: absolute; inset: 0; display: flex; background: var(--bg); }
.svc-list { width: 360px; flex-shrink: 0; display: flex; flex-direction: column; border-right: 1px solid var(--border); background: var(--bg2); min-height: 0; }
.svc-tools { padding: 10px; display: flex; flex-direction: column; gap: 8px; border-bottom: 1px solid var(--border); }
.svc-tools input { padding: 8px 10px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); font-size: 14px; }
.chip.on { color: var(--fg); border-color: var(--accent); } .chip.bad { color: var(--bad); }
.svc-rows { flex: 1; overflow-y: auto; }
.svc-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px; cursor: pointer; border-bottom: 1px solid var(--border); }
.svc-row:hover { background: var(--bg3); } .svc-row.active { background: var(--bg3); box-shadow: inset 3px 0 0 var(--accent); }
.svc-name { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.svc-name .n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.svc-name .d { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.svc-state { font-size: 11px; color: var(--muted); font-family: ui-monospace, monospace; white-space: nowrap; }
.sdot.ok { background: var(--ok); } .sdot.bad { background: var(--bad); } .sdot.warn { background: var(--s2); } .sdot.exited { background: var(--accent2); }
.svc-empty { flex: 1; display: flex; align-items: center; justify-content: center; }
.svc-detail { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 10px; padding: 12px; overflow-y: auto; }
/* The pane scrolls; its parts keep their height. Left to shrink, whatever clips its own overflow
(a task's output box) is squeezed to a line the moment the pane is full — on a phone, always.
What is meant to give way (.logs, .stack-editor) says so itself, further down. */
.svc-detail > *, .cron-detail > * { flex-shrink: 0; }
.svc-detail .back { display: none; align-self: flex-start; }
/* The × that closes a detail pane on a wide screen, where the list stays beside it and there is
no Back. Sits in the pane's top-right corner; the header keeps clear of it. */
.svc-detail, .cron-detail { position: relative; }
.detail-close { position: absolute; top: 10px; right: 10px; z-index: 1; width: 26px; height: 26px; padding: 0; display: inline-flex; align-items: center; justify-content: center; color: var(--muted); background: none; border: 0; border-radius: 50%; opacity: .7; }
.detail-close:hover { color: var(--fg); background: var(--bg3); opacity: 1; }
.svc-detail > .svc-head, .cron-detail > h2 { padding-right: 36px; }
/* Nothing selected: the list has the window to itself rather than a 360px column beside an
empty pane. The names, descriptions and commands that get an ellipsis are what it's for.
Picking something puts the list back at its own width, beside the detail. A phone shows one
pane at a time and is already full width — this is only for a window with room to waste. */
@media (min-width: 801px) {
.svc:not(.has-sel) .svc-list, .cron:not(.has-sel) .cron-list { width: auto; flex: 1 1 auto; border-right: 0; }
.svc:not(.has-sel) .svc-empty, .cron:not(.has-sel) .svc-empty { display: none; }
}
.svc-head { display: flex; align-items: center; gap: 10px; } .svc-head h2 { margin: 0; font-size: 18px; font-weight: 600; word-break: break-all; }
.svc-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.props { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 4px 16px; font-size: 13px; }
.props > div { display: flex; gap: 8px; min-width: 0; } .props .k { color: var(--muted); width: 80px; flex-shrink: 0; }
.props span:not(.k) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .mono { font-family: ui-monospace, monospace; font-size: 12px; }
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); }
.tabs button { padding: 6px 12px; color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -1px; }
.tabs button.on { color: var(--fg); border-bottom-color: var(--accent); }
.unit-file { margin: 0; padding: 10px; background: var(--term-bg); border: 1px solid var(--border); border-radius: 6px; font: 12px/1.5 ui-monospace, monospace; white-space: pre-wrap; word-break: break-all; }
.logs { flex: 1; min-height: 240px; display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: 6px; background: var(--term-bg); overflow: hidden; }
.logs-bar { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 12px; background: var(--bg2); }
.log-lines { flex: 1; overflow: auto; padding: 6px 8px; font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; }
.log { display: flex; gap: 8px; white-space: pre-wrap; word-break: break-word; }
.log .lt { color: var(--muted); flex-shrink: 0; } .log .li { color: var(--accent2); flex-shrink: 0; }
.log.err .lm { color: var(--bad); } .log.warn .lm { color: var(--s2); } .log.debug .lm { color: var(--muted); }
@media (max-width: 800px) {
.svc-list { width: 100%; border-right: 0; } .has-sel .svc-list { display: none; } .svc-empty { display: none; }
.svc-detail .back { display: inline-block; } .props { grid-template-columns: 1fr; } .detail-close { display: none; }
}
/* cron */
.cron { position: absolute; inset: 0; display: flex; background: var(--bg); }
.cron-list { width: 420px; flex-shrink: 0; display: flex; flex-direction: column; border-right: 1px solid var(--border); background: var(--bg2); min-height: 0; }
.cron-rows { flex: 1; overflow-y: auto; }
.cron-rows h3 { margin: 0; padding: 8px 12px 4px; font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; background: var(--bg2); position: sticky; top: 0; }
.cron-row { display: grid; grid-template-columns: 8px 1fr auto; gap: 4px 10px; align-items: center; padding: 8px 12px; cursor: pointer; border-bottom: 1px solid var(--border); }
.cron-row:hover { background: var(--bg3); } .cron-row.active { background: var(--bg3); box-shadow: inset 3px 0 0 var(--accent); }
.cron-row .cmd { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: ui-monospace, monospace; font-size: 12px; }
.cron-row .meta { grid-column: 2 / 4; font-size: 11px; color: var(--muted); display: flex; gap: 8px; flex-wrap: wrap; }
.cron-row .when { font-size: 11px; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.cron-row.off .cmd { color: var(--muted); text-decoration: line-through; }
.cron-detail { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 10px; padding: 12px; overflow-y: auto; }
.cron-detail .back { display: none; align-self: flex-start; }
.cron-detail h2 { margin: 0; font-size: 15px; font-weight: 600; font-family: ui-monospace, monospace; word-break: break-all; white-space: pre-wrap; }
.notice { padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 12px; color: var(--muted); background: var(--bg2); }
.editor { width: 100%; min-height: 260px; padding: 10px; background: var(--term-bg); color: var(--fg); border: 1px solid var(--border); border-radius: 6px; font: 12px/1.5 ui-monospace, monospace; resize: vertical; }
.run-out { margin: 0; padding: 10px; background: var(--term-bg); border: 1px solid var(--border); border-radius: 6px; font: 12px/1.5 ui-monospace, monospace; white-space: pre-wrap; word-break: break-word; max-height: 50vh; overflow: auto; }
@media (max-width: 800px) {
.cron-list { width: 100%; border-right: 0; } .has-sel .cron-list { display: none; }
.cron-detail .back { display: inline-block; }
}
/* updates */
.updates .pkgs { padding: 0; }
/* the package list is flush to the card's edge, but a one-line "nothing to do" in the same
card should sit in it like every other empty state, not as a 20px sliver */
.updates .pkgs:has(> .empty:only-child) { padding: 12px; }
.pkg { display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: center; padding: 8px 12px; border-bottom: 1px solid var(--border); }
.pkg.sel { background: var(--bg3); }
.pkg input { width: 16px; height: 16px; }
.pkg-name { min-width: 0; display: flex; flex-direction: column; cursor: pointer; }
.pkg-name .n { font-family: ui-monospace, monospace; font-size: 13px; }
.pkg-name .d { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pkg-ver { font-family: ui-monospace, monospace; font-size: 12px; white-space: nowrap; }
.badge.bad { color: var(--bad); border-color: var(--bad); margin-left: 8px; }
.changelog { margin: 0 12px 8px; max-height: 40vh; }
.notice.attn { border-color: var(--s2); color: var(--fg); }
.task { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.task-out { border: 0; border-radius: 0; max-height: 45vh; min-height: 120px; }
.task.failed { border-color: var(--bad); } .task .badge { margin-left: 0; flex-shrink: 0; } .badge.ok { color: var(--ok); border-color: var(--ok); }
@media (max-width: 800px) { .pkg { grid-template-columns: auto 1fr; } .pkg-ver { grid-column: 2; white-space: normal; } }
/* power + modal */
.power { font-size: 15px; } .power.attn { color: var(--s2); }
/* The backdrop keeps clear of the phone's status bar and home indicator; a dialog is never taller
than what is left, and scrolls inside itself. Its parts keep their height: in a column that
may shrink, anything with its own scrollbar (a preview, a list) is squeezed to a sliver
instead, and the dialog never scrolls at all. */
.modal-back { position: fixed; inset: 0; z-index: 50; background: rgba(0,0,0,.6); display: flex; align-items: center; justify-content: center; padding: max(16px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right)) max(16px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left)); }
.modal { --modal-bg: var(--bg2); width: min(420px, 100%); max-height: 100%; overflow-y: auto; overscroll-behavior: contain; background: var(--modal-bg); border: 1px solid var(--border); border-radius: 10px; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.modal > * { flex-shrink: 0; }
/* a long dialog's buttons (and what went wrong) stay in reach while the rest scrolls */
.modal:has(> .modal-foot) { padding-bottom: 0; }
.modal-foot { position: sticky; bottom: 0; z-index: 1; margin: 0 -16px; padding: 12px 16px 16px; background: var(--modal-bg); border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 10px; }
.modal-foot .error { max-height: 30vh; overflow-y: auto; }
.modal h3 { margin: 0; font-size: 15px; }
.modal label { display: flex; flex-direction: column; gap: 6px; font-size: 12px; }
.modal input { padding: 9px 10px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); font-size: 16px; }
.btn.danger { color: var(--bad); border-color: var(--bad); } .btn.danger:disabled { opacity: .4; }
/* feedback: spinners, buttons, toasts, activity bar */
.spin { display: inline-block; border: 2px solid rgba(128,128,128,.35); border-top-color: currentColor; border-radius: 50%; animation: spin .7s linear infinite; vertical-align: -2px; margin-right: 6px; flex-shrink: 0; box-sizing: border-box; }
@keyframes spin { to { transform: rotate(360deg); } }
.btn, .primary, .login button { display: inline-flex; align-items: center; justify-content: center; }
.btn.busy, .primary.busy { opacity: .85; cursor: progress; }
.loading { gap: 10px; padding: 24px; height: auto; min-height: 120px; }
.loading .spin { margin: 0; }
.tools-row { display: flex; gap: 6px; } .tools-row input { flex: 1; min-width: 0; }
.toasts { position: fixed; left: 50%; bottom: calc(var(--footer-h) + 16px + env(safe-area-inset-bottom)); transform: translateX(-50%); z-index: 60; display: flex; flex-direction: column; gap: 6px; align-items: center; pointer-events: none; }
.toast { pointer-events: auto; padding: 8px 14px; border-radius: 8px; background: var(--bg3); border: 1px solid var(--border); font-size: 13px; box-shadow: 0 4px 16px rgba(0,0,0,.45); max-width: 90vw; cursor: pointer; animation: toast-in .15s ease-out; }
.toast.ok { border-color: var(--ok); } .toast.err { border-color: var(--bad); color: var(--bad); }
@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.netbar { position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 70; background: var(--accent2); opacity: 0; transition: opacity .2s .3s; pointer-events: none; }
.netbar.on { opacity: 1; animation: netbar 1.1s ease-in-out infinite; }
@keyframes netbar { 0% { transform: scaleX(0); transform-origin: left; } 50% { transform: scaleX(1); transform-origin: left; } 51% { transform-origin: right; } 100% { transform: scaleX(0); transform-origin: right; } }
.term-overlay { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(13,17,23,.55); color: var(--muted); z-index: 2; gap: 4px; font-size: 13px; }
.saved { font-size: 11px; color: var(--muted); display: inline-flex; align-items: center; }
.task.running .logs-bar { background: rgba(57,135,229,.12); }
.task.failed .logs-bar { background: rgba(255,123,114,.12); }
.task.done .logs-bar { background: rgba(63,185,80,.10); }
/* disks */
h2.section { margin: 0; font-size: 13px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
.disk .dname, .card .dname { font-weight: 600; }
.badge { font-size: 10px; text-transform: uppercase; border: 1px solid var(--border); border-radius: 3px; padding: 0 4px; color: var(--muted); }
.good { color: var(--ok); } .warn-text { color: var(--s2); }
.smart { display: flex; flex-direction: column; gap: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
.attrs { width: 100%; border-collapse: collapse; font-size: 12px; }
.attrs th { text-align: left; color: var(--muted); font-weight: 500; padding: 4px 4px; border-bottom: 1px solid var(--border); }
.attrs td { padding: 3px 4px; border-bottom: 1px solid var(--border); } .attrs td.num, .attrs th:not(:nth-child(2)) { text-align: right; }
.attrs tr.warn td { color: var(--s2); }
/* A SMART raw value is not always a number — a temperature reads "36 (Min/Max 28/39)" — and a
table cannot be narrower than its widest unbreakable cell, so one of those pushed the whole
table out through the side of its card. The raw column wraps, but only where there is a
space: a number broken across two lines reads as two numbers. If some drive ever reports
one long enough to still not fit, the table scrolls inside its card rather than out of it. */
.attrs td.raw { white-space: normal; }
.attrs-wrap { overflow-x: auto; scrollbar-width: thin; }
/* Six columns of SMART want about 350px between them; a disk card is given room for that, so
the fallback above stays a fallback. */
.disk-cards { grid-template-columns: repeat(auto-fill, minmax(min(420px, 100%), 1fr)); }
.datasets { padding: 0; overflow-x: auto; }
.ds-row { display: grid; grid-template-columns: minmax(160px, 1fr) 80px 80px 80px 50px 50px minmax(120px, 1fr); gap: 8px; align-items: center; padding: 6px 12px; border-bottom: 1px solid var(--border); font-size: 13px; min-width: 700px; } /* the columns and gaps add up to 692: at 640 the row ended before its own cells did */
.ds-row.head { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .5px; }
.ds-row .num { text-align: right; }
.ds-name { cursor: pointer; display: flex; align-items: center; gap: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ds-mount { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: ui-monospace, monospace; font-size: 12px; }
.ds-snaps { padding: 8px 12px 10px 40px; border-bottom: 1px solid var(--border); background: var(--bg); display: flex; flex-direction: column; gap: 6px; }
.snap-list { display: flex; flex-direction: column; gap: 2px; max-height: 40vh; overflow-y: auto; }
.snap { display: flex; justify-content: space-between; gap: 12px; font-size: 12px; }
/* files */
.files { position: absolute; inset: 0; display: flex; flex-direction: column; background: var(--bg); }
.files.drag { outline: 2px dashed var(--accent2); outline-offset: -4px; }
.files-bar { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; background: var(--bg2); border-bottom: 1px solid var(--border); }
.crumbs { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; }
.crumb { padding: 3px 6px; border-radius: 4px; font-family: ui-monospace, monospace; font-size: 13px; color: var(--accent2); }
.crumb:hover { background: var(--bg3); } .crumb + .crumb::before { content: '/'; color: var(--muted); margin-right: 4px; }
.path-input { flex: 1; min-width: 160px; margin-left: 8px; padding: 4px 8px; background: var(--bg); border: 1px solid var(--border); border-radius: 5px; color: var(--muted); font: 12px ui-monospace, monospace; }
.path-input:focus { color: var(--fg); border-color: var(--accent2); outline: none; }
.files-tools { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.files-tools .filter { width: 110px; padding: 5px 8px; background: var(--bg); border: 1px solid var(--border); border-radius: 5px; color: var(--fg); font-size: 13px; }
.file-list { flex: 1; overflow-y: auto; }
.file-row { display: grid; grid-template-columns: 28px 1fr 80px 80px 130px 32px; gap: 8px; align-items: center; padding: 7px 10px; border-bottom: 1px solid var(--border); cursor: pointer; position: relative; }
.file-row:hover { background: var(--bg3); }
.file-row.up { color: var(--muted); }
.file-row.broken .fname { color: var(--bad); }
.ficon { text-align: center; font-size: 15px; }
.fname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fmeta { color: var(--muted); text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.more { justify-self: end; }
.ctx { position: absolute; right: 36px; top: 4px; z-index: 5; background: var(--bg2); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 6px 20px rgba(0,0,0,.5); display: flex; flex-direction: column; min-width: 180px; overflow: hidden; }
.ctx button, .ctx a { padding: 9px 14px; text-align: left; color: var(--fg); text-decoration: none; font-size: 13px; }
.ctx button:hover, .ctx a:hover { background: var(--bg3); } .ctx .danger { color: var(--bad); }
.upload-bar { padding: 6px 12px; font-size: 12px; color: var(--muted); border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px; }
.upload-bar .meter { flex: 1; }
.drop-hint { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(17,20,24,.7); color: var(--fg); font-size: 16px; pointer-events: none; }
.viewer { position: absolute; inset: 0; display: flex; flex-direction: column; }
.viewer-bar { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: var(--bg2); border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.vname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 50vw; } .dirty { color: var(--accent); }
.file-editor { flex: 1; min-height: 0; border: 0; border-radius: 0; resize: none; font-size: 13px; }
.preview { flex: 1; display: flex; align-items: center; justify-content: center; overflow: auto; padding: 12px; }
.preview img { max-width: 100%; max-height: 100%; object-fit: contain; }
@media (max-width: 800px) {
.file-row { grid-template-columns: 24px 1fr 70px 32px; } .file-row .fmeta:nth-child(4), .file-row .perm { display: none; }
.path-input { width: 100%; margin: 4px 0 0; }
.files-tools .filter { width: 80px; }
}
/* processes tab — btop-style boxes */
.procs-page { position: absolute; inset: 0; display: flex; flex-direction: column; background: var(--bg); overflow-y: auto; -webkit-overflow-scrolling: touch; }
.procs-page > * { flex-shrink: 0; }
.procs-top { padding: 14px 10px 8px; display: grid; grid-template-columns: 1.4fr 1fr; gap: 12px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
.procs-top .netbox { grid-column: 1 / -1; }
.box { position: relative; border: 1px solid var(--border); border-radius: 8px; padding: 12px 12px 8px; min-width: 0; }
.box-title { position: absolute; top: -8px; left: 10px; padding: 0 6px; background: var(--bg); color: var(--accent2); font-weight: 700; font-size: 12px; letter-spacing: .5px; }
.box-right { position: absolute; top: -8px; right: 10px; padding: 0 6px; background: var(--bg); color: var(--muted); font-size: 11px; max-width: 70%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.box-sub { margin: 8px 0 2px; color: var(--accent2); font-size: 11px; }
.box-foot { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 8px; font-size: 11px; } .box-foot b { color: var(--fg); font-weight: 600; }
.small { font-size: 11px; }
.brow { display: grid; grid-template-columns: 64px 1fr auto; gap: 8px; align-items: center; padding: 2px 0; }
.bl { color: var(--muted); } .bv { font-variant-numeric: tabular-nums; white-space: nowrap; } .bv.big { font-size: 20px; font-weight: 700; color: var(--fg); }
.gbar { position: relative; height: 10px; border-radius: 3px; overflow: hidden; background: linear-gradient(90deg, #3fb950 0%, #9ccc3f 40%, #d29922 65%, #f0883e 82%, #ff7b72 100%); }
.gcover { position: absolute; top: 0; right: 0; bottom: 0; background: var(--bg3); }
.graph { display: flex; line-height: 1; font-size: 22px; letter-spacing: -1px; overflow: hidden; white-space: nowrap; }
.graph span { flex: 1 1 0; min-width: 0; text-align: center; } .graph .gpad { flex: 0 0 auto; color: var(--bg3); }
.graph .g1 { color: var(--ok); } .graph .g2 { color: var(--s2); } .graph .g3 { color: var(--bad); }
.cpu-total { display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: end; margin-bottom: 8px; }
.cpu-total-v { display: flex; flex-direction: column; align-items: flex-end; }
.cores { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 0 16px; }
.cores .brow { grid-template-columns: 30px 1fr 42px; } .cores .bv { text-align: right; }
.netrow { display: grid; grid-template-columns: 90px auto 1fr; gap: 12px; align-items: center; padding: 2px 0; }
.netrow .dn { color: var(--accent2); } .netrow .upc { color: var(--s2); }
.procs-sticky { position: sticky; top: 0; z-index: 2; background: var(--bg); }
.procs-tools { display: flex; gap: 6px; align-items: center; padding: 8px 10px; flex-wrap: wrap; }
.procs-tools input { flex: 1; min-width: 140px; padding: 6px 8px; background: var(--bg2); border: 1px solid var(--border); border-radius: 5px; color: var(--fg); font-size: 13px; }
.procs-tools label { display: inline-flex; align-items: center; gap: 4px; } .procs-tools select.btn { padding: 3px 6px; }
.ptable { font-size: 13px; min-height: 50vh; }
.prow { display: grid; grid-template-columns: 60px 80px minmax(120px, 1fr) 60px 76px 70px 64px; gap: 8px; align-items: center; padding: 4px 10px; border-bottom: 1px solid var(--border); cursor: pointer; }
.prow:hover { background: var(--bg3); } .prow.active { background: var(--bg3); box-shadow: inset 3px 0 0 var(--accent); }
.prow.zombie { color: var(--muted); font-style: italic; } .prow.running .mono { color: var(--ok); }
.prow .num { text-align: right; font-variant-numeric: tabular-nums; font-family: ui-monospace, monospace; font-size: 12px; } .prow .hot { color: var(--s2); }
.prow .ell { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.phead { background: var(--bg2); cursor: default; padding: 0 10px; border-top: 1px solid var(--border); }
.th { padding: 6px 0; text-align: left; color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .5px; } .th.num { text-align: right; } .th.on { color: var(--fg); }
.pdetail { position: sticky; bottom: 0; z-index: 3; border-top: 1px solid var(--border); background: var(--bg2); padding: 10px; display: flex; flex-direction: column; gap: 8px; box-shadow: 0 -6px 16px rgba(0,0,0,.4); }
.cmdline { font-size: 12px; word-break: break-all; white-space: pre-wrap; color: var(--fg); background: var(--term-bg); padding: 8px; border-radius: 6px; border: 1px solid var(--border); max-height: 120px; overflow: auto; }
@media (max-width: 800px) {
.procs-top { grid-template-columns: 1fr; gap: 14px; padding-top: 16px; }
.cmdline { max-height: 72px; }
.prow { grid-template-columns: 52px minmax(100px, 1fr) 56px 70px; } .prow > :nth-child(2), .prow > :nth-child(6), .prow > :nth-child(7) { display: none; }
.cores { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
.graph { font-size: 16px; }
.netrow { grid-template-columns: 70px 1fr; } .netrow > :nth-child(3) { display: none; }
}
/* machine switcher + machines page */
.mswitch { position: relative; padding: 4px 8px 8px; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.mswitch-btn { width: 100%; display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; text-align: left; }
.mswitch-btn .mname { font-weight: 700; color: var(--accent); }
.mswitch-btn .chev { margin-left: auto; color: var(--muted); }
.mmenu { position: absolute; left: 8px; right: 8px; top: calc(100% - 4px); z-index: 20; background: var(--bg2); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.5); overflow: hidden; }
.mitem { width: 100%; display: flex; align-items: center; gap: 8px; padding: 9px 12px; text-align: left; }
.mitem:hover { background: var(--bg3); } .mitem.on { box-shadow: inset 3px 0 0 var(--accent); }
.mitem .mname { flex: 1; } .mitem.manage { color: var(--accent2); }
.addform { display: flex; flex-direction: column; gap: 10px; }
.addform label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.addform input { padding: 8px 10px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); font-size: 15px; }
.header .badge.bad { color: var(--bad); border-color: var(--bad); }
/* settings + themes */
.themes { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
.theme-card { text-align: left; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; padding: 10px; display: flex; flex-direction: column; gap: 6px; }
.theme-card:hover { border-color: var(--accent2); } .theme-card.on { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.theme-name { font-weight: 600; display: flex; align-items: center; gap: 8px; }
.tprev { border: 1px solid; border-radius: 6px; overflow: hidden; font-size: 9px; height: 96px; display: flex; flex-direction: column; }
.tprev-head { display: flex; align-items: center; justify-content: space-between; padding: 4px 8px; border-bottom: 1px solid; font-weight: 700; }
.tprev-dot { width: 6px; height: 6px; border-radius: 50%; }
.tprev-body { flex: 1; display: flex; }
.tprev-rail { width: 34px; border-right: 1px solid; display: flex; flex-direction: column; gap: 4px; padding: 6px 4px; }
.tprev-rail span { display: block; height: 7px; border-radius: 2px; background: transparent; }
.tprev-main { flex: 1; padding: 6px; display: flex; flex-direction: column; gap: 6px; }
.tprev-tile { border: 1px solid; border-radius: 4px; padding: 4px 6px; display: flex; flex-direction: column; gap: 2px; } .tprev-tile b { font-size: 12px; }
.tprev-bar { height: 4px; border-radius: 2px; overflow: hidden; } .tprev-bar div { height: 100%; }
.tprev-term { flex: 1; border-radius: 4px; padding: 4px 6px; font-family: var(--mono); }
/* per-theme flourishes */
[data-theme="cyberpunk"] .brand { text-shadow: 0 0 8px rgba(255, 43, 214, .7), 0 0 18px rgba(255, 43, 214, .35); }
[data-theme="cyberpunk"] .gicon, [data-theme="cyberpunk"] .box-title { text-shadow: 0 0 6px rgba(0, 229, 255, .6); }
[data-theme="cyberpunk"] .group-head.active { box-shadow: inset 3px 0 0 var(--accent), 0 0 12px rgba(255, 43, 214, .25); }
[data-theme="retro"] body, [data-theme="retro"] .btn, [data-theme="retro"] input, [data-theme="retro"] select { font-family: var(--mono); }
[data-theme="retro"] .brand { text-shadow: 0 0 6px rgba(255, 176, 0, .6); letter-spacing: 1px; }
[data-theme="retro"] .app::after { content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 100; background: repeating-linear-gradient(0deg, rgba(0,0,0,0) 0 2px, rgba(0,0,0,.12) 2px 3px); }
[data-theme="retro"] .tile, [data-theme="retro"] .card, [data-theme="retro"] .box { border-radius: 2px; }
[data-theme="light"] .term-overlay { background: rgba(255,255,255,.6); }
[data-theme="light"] .toast { box-shadow: 0 4px 16px rgba(0,0,0,.15); }
[data-theme="light"] .modal-back { background: rgba(0,0,0,.35); }
.disk-summary { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 2px 0 6px; }
/* touch: every control at least 36px tall (bug 22) */
@media (pointer: coarse) {
.btn, .chip, .icon-btn, .link, .th, .mod-tools button, .hide-item, .crumb, .files-tools select, .procs-tools select, .dash-bar select { min-height: 36px; padding-top: 6px; padding-bottom: 6px; }
.icon-btn { min-width: 36px; }
.path-input, .filter, .procs-tools input, .files-tools input { min-height: 36px; }
.crumb { min-width: 32px; }
:root { --tabs-h: 40px; }
.ttab .icon-btn { min-width: 30px; }
.ttab:not(.active) .icon-btn { display: none; }
.header .link, .header .icon-btn { min-height: 36px; display: inline-flex; align-items: center; }
.mod-tools { margin-top: 0; }
}
/* sensor widgets */
.gauge-svg { width: 100%; max-width: 180px; display: block; margin: 0 auto; }
.w-gauge-v { fill: var(--fg); font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
.tile .value.warn, .w-row .num.warn { color: var(--s2); } .tile .value.bad, .w-row .num.bad { color: var(--bad); }
.tile .meter { margin-top: 8px; }
.tile.w-list { grid-column: span 2; }
.w-list { display: flex; flex-direction: column; gap: 6px; }
.w-row { display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; font-size: 13px; align-items: baseline; }
.w-row .k { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .w-row .meter { grid-column: 1 / 3; margin-top: 0; }
.tile.add-widget { border-style: dashed; color: var(--accent2); min-height: 72px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.modal.picker { width: min(520px, 100%); }
.picker-list { overflow-y: auto; max-height: 34vh; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); }
.picker-row { display: flex; justify-content: space-between; gap: 10px; width: 100%; padding: 7px 10px; text-align: left; font-size: 12px; border-bottom: 1px solid var(--border); }
.picker-row:hover, .picker-row.active { background: var(--bg3); } .picker-row.active { box-shadow: inset 3px 0 0 var(--accent); }
@media (max-width: 800px) { .tile.w-list { grid-column: 1 / -1; } }
/* a widget alone in an iframe */
.embed { min-height: 100vh; padding: 10px; background: var(--bg2); box-sizing: border-box; display: flex; flex-direction: column; justify-content: center; }
.embed.bare { background: transparent; }
.embed .tile { border: 0; padding: 0; background: none; }
/* widget gallery */
.gallery { overflow-x: hidden; }
.gallery section { min-width: 0; display: flex; flex-direction: column; gap: 12px; }
.gallery h2.section { margin: 10px 0 0; font-size: 13px; }
.gallery-tile { min-width: 0; max-width: 100%; overflow: hidden; }
.gallery-tile .label, .kpis.widgets .tile .label { overflow-wrap: anywhere; } /* container names run long */
.gallery .kpis { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); }
.gallery-tile { text-align: left; cursor: pointer; display: flex; flex-direction: column; color: inherit; font: inherit; }
.gallery-tile:hover { border-color: var(--accent2); }
.gallery-tile .value { font-size: 18px; overflow-wrap: anywhere; }
.gallery-id { margin-top: auto; padding-top: 8px; font-size: 10.5px; color: var(--muted); overflow-wrap: anywhere; }
@media (max-width: 800px) { .gallery .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
a.btn { text-decoration: none; display: inline-flex; align-items: center; }
/* in-app dialogs, gallery search */
/* a question is asked by something, often another dialog: it goes on top of whatever asked */
.modal-back.ask-back { z-index: 70; }
.modal.ask { width: min(440px, 100%); } .ask-msg { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 14px; }
.gallery-find { flex: 1 1 200px; max-width: 340px; padding: 7px 10px; background: var(--bg2); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); font-size: 14px; }
.gallery .dash-bar { flex-wrap: wrap; gap: 8px; }
.ttab.drop { box-shadow: inset 3px 0 0 var(--accent2); }
.ttab-move { display: none; } .ttab-move .icon-btn:disabled { opacity: .3; }
@media (pointer: coarse) { .ttab-move { display: inline-flex; } } /* no drag and drop on a touch screen */
/* each machine's colour */
.header { box-shadow: inset 0 3px 0 var(--machine, var(--accent)); }
.mswitch-btn .mname, .header .host { color: var(--machine, var(--accent)); }
.swatches { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 8px; }
.swatch { width: 26px; height: 26px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; padding: 0; }
.swatch.on { border-color: var(--fg); box-shadow: 0 0 0 2px var(--bg2) inset; }
.swatches input[type=color] { width: 30px; height: 30px; padding: 0; border: 1px solid var(--border); border-radius: 50%; background: none; cursor: pointer; }
.themes { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
/* HUD: light drawn on glass — hairlines, no fills, thin type */
[data-theme="hud"] .tile, [data-theme="hud"] .card, [data-theme="hud"] .box { background: rgba(53, 214, 255, .03); border-color: rgba(53, 214, 255, .28); border-radius: 2px; box-shadow: 0 0 0 1px rgba(53, 214, 255, .05), inset 0 0 24px rgba(53, 214, 255, .04); }
/* anything that floats over the page is solid: glass on glass is two layers of text */
[data-theme="hud"] .modal { --modal-bg: #061019; border-color: rgba(53, 214, 255, .45); border-radius: 2px; box-shadow: 0 0 0 1px rgba(53, 214, 255, .08), 0 0 40px rgba(53, 214, 255, .12), inset 0 0 24px rgba(53, 214, 255, .04); }
[data-theme="hud"] .modal-back { background: rgba(0, 3, 6, .82); }
[data-theme="hud"] .tile::before, [data-theme="hud"] .card::before { content: ''; position: absolute; top: -1px; left: -1px; width: 12px; height: 12px; border-top: 2px solid var(--accent); border-left: 2px solid var(--accent); }
[data-theme="hud"] .tile, [data-theme="hud"] .card { position: relative; }
[data-theme="hud"] body { font-weight: 300; letter-spacing: .2px; }
[data-theme="hud"] .tile .value, [data-theme="hud"] .brand { font-weight: 300; letter-spacing: 1.5px; text-shadow: 0 0 10px rgba(53, 214, 255, .55); }
[data-theme="hud"] .tile .label, [data-theme="hud"] .card h2, [data-theme="hud"] h2.section { letter-spacing: 2px; color: var(--accent); opacity: .8; }
[data-theme="hud"] .meter { height: 3px; border-radius: 0; } [data-theme="hud"] .meter .fill { border-radius: 0; box-shadow: 0 0 8px var(--s1); }
[data-theme="hud"] .rail, [data-theme="hud"] .header, [data-theme="hud"] .footer { background: rgba(3, 7, 12, .92); border-color: rgba(53, 214, 255, .22); }
@media (max-width: 800px) { [data-theme="hud"] .rail { background: #03070c; } }
[data-theme="hud"] .group-head.active { background: linear-gradient(90deg, rgba(53, 214, 255, .16), transparent); box-shadow: inset 2px 0 0 var(--accent); }
[data-theme="hud"] .btn, [data-theme="hud"] .chip { background: transparent; border-color: rgba(53, 214, 255, .35); border-radius: 2px; } [data-theme="hud"] .btn.on, [data-theme="hud"] .chip.on { background: rgba(53, 214, 255, .16); color: var(--accent2); border-color: var(--accent); }
[data-theme="hud"] .app { background: radial-gradient(ellipse at 50% -20%, rgba(53, 214, 255, .10), transparent 60%), var(--bg); }
[data-theme="hud"] .gauge-svg path:first-child { stroke-width: 3; } [data-theme="hud"] .gauge-svg path:nth-child(2) { stroke-width: 5; filter: drop-shadow(0 0 4px var(--s1)); }
/* alerts */
.card.all-clear { padding: 16px; } .card.firing { border-color: var(--s2); } .card.firing.bad { border-color: var(--bad); box-shadow: 0 0 0 1px var(--bad) inset; }
.rule { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-top: 1px solid var(--border); } .rule:first-child { border-top: 0; padding-top: 0; } .rule.off .rule-body { opacity: .5; }
.rule-body { flex: 1; min-width: 0; cursor: pointer; } .rule-name { display: flex; align-items: center; gap: 8px; } .rule-body .mono { overflow-wrap: anywhere; margin-top: 2px; }
.toggle { width: 38px; height: 22px; border-radius: 11px; background: var(--bg3); border: 1px solid var(--border); position: relative; flex-shrink: 0; padding: 0; } .toggle i { position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--muted); transition: left .15s, background .15s; }
.toggle.on { background: color-mix(in srgb, var(--ok) 25%, var(--bg3)); border-color: var(--ok); } .toggle.on i { left: 18px; background: var(--ok); }
.rule-cond { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .rule-cond select, .rule-cond input, .chan-add select, .chan-add input { padding: 8px 9px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); font-size: 14px; } .rule-cond input.mono { flex: 1 1 120px; min-width: 0; }
.chan-add { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; } .chan-add input { flex: 1 1 220px; min-width: 0; }
.ev { display: flex; align-items: baseline; gap: 8px; padding: 6px 0; border-top: 1px solid var(--border); font-size: 13px; } .ev:first-child { border-top: 0; } .ev-main { flex: 1; min-width: 0; overflow-wrap: anywhere; } .bad-text { color: var(--bad) !important; }
/* Arranging the rail: a foot row toggles it; each page then carries a handle to drag by. */
.rail-foot { margin-top: 8px; padding: 6px 12px; display: flex; gap: 10px; align-items: center; font-size: 12px; color: var(--muted); border-top: 1px solid var(--border); }
.rail-foot button { color: var(--muted); } .rail-foot button:hover { color: var(--fg); } .rail-foot button.on { color: var(--accent); }
.rail.arranging .group-head { cursor: default; } .rail.arranging .group-head:hover { background: none; }
.rail .handle { display: none; width: 18px; margin: -4px 0 -4px -4px; align-self: stretch; align-items: center; justify-content: center; color: var(--muted); cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none; font-size: 14px; letter-spacing: -2px; }
.rail.arranging .handle { display: inline-flex; } .rail.arranging .handle:hover { color: var(--fg); }
.rail .group.dragging .group-head { background: var(--bg3); opacity: .75; box-shadow: inset 3px 0 0 var(--accent2); }
.rail.arranging .group { transition: transform .12s ease; }
.rail-badge { margin-left: auto; min-width: 18px; padding: 0 5px; border-radius: 9px; background: var(--bad); color: #fff; font-size: 11px; font-weight: 700; text-align: center; line-height: 18px; }
@media (pointer: coarse) { .toggle { width: 52px; height: 32px; border-radius: 16px; } .toggle i { width: 26px; height: 26px; } .toggle.on i { left: 22px; } .swatch { width: 34px; height: 34px; } .swatches input[type=color] { width: 36px; height: 36px; } }
/* docker */
.stack-head { margin: 0; padding: 8px 12px 4px; font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; background: var(--bg2); position: sticky; top: 0; z-index: 1; }
.docker .svc-state { text-align: right; line-height: 1.35; }
.dtable { width: 100%; border-collapse: collapse; } .dtable th { text-align: left; padding: 8px 12px; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; position: sticky; top: 0; background: var(--bg2); }
.dtable td { padding: 7px 12px; border-top: 1px solid var(--border); overflow-wrap: anywhere; }
.docker:not(.has-sel) .svc-list { flex: 1; width: auto; }
.docker .svc-tools .chips { flex-wrap: wrap; } .docker .svc-name { flex: 1; min-width: 0; } .docker .svc-name .n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.docker .svc-row { min-width: 0; } .docker .svc-state { flex-shrink: 0; }
.alerts .row { display: flex; align-items: center; gap: 8px; } .alerts .row .badge { flex: 0 0 auto; } .alerts .row .k { flex: 1; min-width: 0; overflow-wrap: anywhere; }
/* docker: the list must never size itself to its longest name (that was the sideways scroll on phones) */
.docker .svc-list { min-width: 0; max-width: 100%; } .docker .svc-rows { overflow-x: hidden; } .docker .svc-name .d { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.docker:not(.has-sel) .svc-list { flex: 1 1 0; }
.stack-editor { flex: 1; min-height: 46vh; font-size: 13px; tab-size: 2; white-space: pre; overflow: auto; }
@media (max-width: 800px) { .docker .svc-list { width: 100%; flex: 1 1 auto; } .stack-editor { min-height: 52vh; font-size: 12px; } }
/* stacks: diff, images, .env */
.stack-extra { display: flex; flex-direction: column; gap: 6px; border-top: 1px solid var(--border); padding-top: 10px; } .stack-extra .sub-head .k { font-size: 12px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
pre.diff { margin: 4px 0 8px; padding: 8px 10px; background: var(--term-bg); border: 1px solid var(--border); border-radius: 6px; font: 12px/1.5 var(--mono); overflow-x: auto; white-space: pre; }
pre.diff .add { color: var(--ok); background: color-mix(in srgb, var(--ok) 12%, transparent); } pre.diff .del { color: var(--bad); background: color-mix(in srgb, var(--bad) 12%, transparent); }
.img-row { display: flex; align-items: center; gap: 8px; min-width: 0; } .img-row .mono { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; } .badge.attn { color: var(--s2); border-color: var(--s2); }
.env-masked { position: relative; display: flex; flex-direction: column; flex: 1; } .env-masked pre { margin: 0; min-height: 30vh; opacity: .8; } .env-masked .btn { position: absolute; top: 10px; right: 10px; }
/* new stack / new container */
.modal.stack-new { width: min(680px, 100%); }
.stack-new textarea, .stack-new select, .stack-new input:not([type=checkbox]) { padding: 8px 10px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); font-size: 14px; width: 100%; box-sizing: border-box; }
.stack-new textarea.mono, .stack-new input.mono { font-family: var(--mono); font-size: 13px; } .stack-new textarea { resize: vertical; }
.stack-new .two { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; } .stack-new .stack-editor { min-height: 32vh; }
.stack-new .check { display: flex; flex-direction: row; align-items: flex-start; gap: 8px; font-size: 13px; } .stack-new .check input { margin-top: 3px; }
.stack-preview { max-height: 180px; overflow: auto; opacity: .85; }
.clash { color: var(--s2); font-size: 12px; overflow-wrap: anywhere; }
@media (max-width: 800px) { .stack-new .two { grid-template-columns: 1fr; } }
.dtable td.row-end { text-align: right; white-space: nowrap; width: 1%; } .dtable .badge { margin-left: 0; }
/* ---- Top: a machine drawn the way btop draws it ----
The layout and the dots are btop's; the colours are the skin's. The ground is the skin's
terminal background and everything on it comes from the skin's variables and its sixteen
terminal colours (Top.tsx hands the few it needs over as --t-*), so switching skin reskins
this page like any other. Fixed-width everywhere, because that is the look. */
.top { position: absolute; inset: 0; background: var(--term-bg); color: var(--fg); font-family: var(--mono); font-size: 13px; line-height: 1.35;
padding: 6px; display: grid; gap: 4px; grid-template-columns: 1fr 1fr 2.2fr; grid-template-rows: minmax(200px, 1fr) auto minmax(170px, 1fr); overflow-y: auto; }
/* every box says how little it can be: a window too short to hold them all scrolls, rather than
cutting the bottom off whichever box the grid ran out of room for */
.t-cpu-graph { min-height: 120px; } .t-net-graphs { min-height: 130px; } .top-proc { min-height: 300px; }
.top-mem, .top-disks { min-height: 165px; } /* a grid row set to auto will otherwise squeeze them to nothing */
.top-cpu { grid-column: 1 / -1; } .top-mem { grid-column: 1; grid-row: 2; } .top-disks { grid-column: 2; grid-row: 2; }
.top-net { grid-column: 1 / 3; grid-row: 3; } .top-proc { grid-column: 3; grid-row: 2 / 4; }
.tbox { position: relative; border: 1px solid var(--border); border-radius: 3px; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.tbox-in { flex: 1; min-height: 0; min-width: 0; padding: 9px 8px 6px; display: flex; flex-direction: column; overflow: hidden; }
.tbox-name, .tbox-right { position: absolute; top: -9px; background: var(--term-bg); padding: 0 4px; font-size: 12px; white-space: nowrap; z-index: 1; }
.tbox-name { left: 8px; color: var(--fg); } .tbox-name b { color: var(--accent); font-weight: 400; }
.tbox-right { right: 8px; color: var(--muted); display: flex; align-items: center; gap: 8px; }
.tbox-right button { background: none; border: 0; color: var(--muted); font-family: var(--mono); font-size: 12px; padding: 0 3px; cursor: pointer; }
.tbox-right button:hover:not(:disabled), .tbox-right button.on { color: var(--accent); } .tbox-right button:disabled { opacity: .35; cursor: default; }
.t-clock { color: var(--fg); } .t-rate { display: inline-flex; align-items: center; gap: 2px; }
.t-dim { color: var(--muted); } .top .spacer { flex: 1; }
/* cpu */
.t-cpu { display: flex; gap: 8px; min-width: 0; flex: 1; min-height: 0; }
.t-cpu-graph { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.t-up { color: var(--muted); font-size: 12px; padding-top: 2px; }
.t-cores { width: 310px; flex-shrink: 0; border: 1px solid var(--border); border-radius: 3px; padding: 4px 6px; display: flex; flex-direction: column; gap: 1px; overflow-y: auto; scrollbar-width: thin; }
.t-cores-head { display: flex; gap: 8px; color: var(--muted); font-size: 12px; white-space: nowrap; }
.t-cores-head b { color: var(--fg); font-weight: 400; overflow: hidden; text-overflow: ellipsis; }
.t-core { display: flex; align-items: center; gap: 6px; }
.t-core-n { width: 30px; } .t-core-p { width: 34px; text-align: right; }
.t-core-g { flex: 1; color: var(--ok); letter-spacing: 0; overflow: hidden; white-space: nowrap; }
.t-core .tm { flex: 1; } .t-core-t { flex: 0 0 62px; display: flex; } .t-core-c { width: 42px; text-align: right; color: var(--t-blue); }
.t-load { display: flex; margin-top: 2px; }
/* the dot graph and the dotted meter */
.tg { width: 100%; height: 100%; flex: 1; min-height: 0; overflow: hidden; }
.tm { position: relative; display: inline-block; height: 9px; flex: 1; min-width: 24px; vertical-align: middle; }
.tm-t, .tm-f { position: absolute; inset: 0; -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 5px); mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 5px); }
.tm-t { background: var(--border); }
/* mem and disks */
.t-row { display: flex; align-items: center; gap: 6px; } .t-row b { font-weight: 400; }
.t-sub { color: var(--muted); font-size: 12px; }
.t-mem { margin-bottom: 4px; } .t-mem .t-sub { margin-top: -2px; } .t-pct { width: 34px; color: var(--fg); }
.t-mem > .tm { display: block; width: 100%; }
.t-amt { width: 76px; text-align: right; color: var(--fg); white-space: nowrap; }
.t-disks { overflow-y: auto; display: flex; flex-direction: column; gap: 6px; scrollbar-width: thin; }
.t-disk .t-row + .t-row { margin-top: -1px; } .t-disk b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* net */
.t-net { display: flex; gap: 10px; min-width: 0; flex: 1; min-height: 0; }
.t-net-graphs { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 140px; }
.t-net-g { position: relative; flex: 1; min-height: 0; display: flex; } .t-scale { position: absolute; left: 2px; top: 0; font-size: 11px; color: var(--muted); z-index: 1; background: var(--term-bg); padding-right: 3px; }
.t-scale-b { top: auto; bottom: 0; }
.t-net-nums { width: 300px; flex-shrink: 0; border: 1px solid var(--border); border-radius: 3px; padding: 4px 8px; white-space: nowrap; overflow: hidden; }
.t-dl { color: var(--t-magenta); } .t-ul { color: var(--t-blue); } .t-gap { height: 6px; } .t-up-label { text-align: right; }
.t-if { color: var(--fg); }
/* proc — the columns that fit are the columns shown: the box measures itself (it is a third of
a wide window and all of a narrow one, so the window's width says nothing) and drops the
curve, then the threads, then the command, rather than crushing Command to one letter */
.top-proc { container: tproc / inline-size; }
.t-proc { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.t-filter { background: var(--bg); border: 1px solid var(--border); border-radius: 3px; color: var(--fg); font-family: var(--mono); font-size: 12px; padding: 3px 6px; margin-bottom: 4px; }
.t-thead, .t-prow { display: grid; grid-template-columns: 58px 116px minmax(0, 1fr) 62px 58px 50px 56px 42px; gap: 7px; align-items: center; white-space: nowrap; }
.t-thead > span, .t-prow > span { min-width: 0; overflow: hidden; } /* a heading never runs into its neighbour */
.t-thead { padding-right: 8px; color: var(--muted); font-size: 12px; border-bottom: 1px solid var(--border); padding-bottom: 2px; }
.t-rows { flex: 1; min-height: 0; overflow-y: auto; scrollbar-width: thin; }
.t-prow { cursor: pointer; } .t-prow:hover { background: var(--bg2); } .t-prow.on { background: var(--bg3); }
.t-prow .c-name { color: var(--ok); text-overflow: ellipsis; } /* --ok, not the terminal's green: it is the one each skin was measured readable with */
.t-prow .c-cmd { color: var(--muted); text-overflow: ellipsis; }
.t-prow .c-user { text-overflow: ellipsis; }
.c-pid, .c-thr, .c-mem, .c-cpu { text-align: right; } .t-thead .c-pid, .t-thead .c-name, .t-thead .c-cmd, .t-thead .c-user { text-align: left; }
.c-g { color: var(--muted); text-align: right; }
@container tproc (max-width: 640px) {
.t-thead, .t-prow { grid-template-columns: 58px 116px minmax(0, 1fr) 62px 58px 50px 42px; }
.t-thead .c-g, .t-prow .c-g { display: none; }
}
@container tproc (max-width: 560px) {
.t-thead, .t-prow { grid-template-columns: 58px 110px minmax(0, 1fr) 58px 50px 42px; }
.t-thead .c-thr, .t-prow .c-thr { display: none; }
}
@container tproc (max-width: 470px) {
.t-thead, .t-prow { grid-template-columns: 52px minmax(0, 1fr) 52px 48px 40px; gap: 6px; }
.t-thead .c-cmd, .t-prow .c-cmd { display: none; }
}
.t-foot { display: flex; align-items: center; gap: 8px; border-top: 1px solid var(--border); padding-top: 3px; margin-top: 2px; font-size: 12px; }
.t-foot button { background: none; border: 1px solid var(--border); border-radius: 3px; color: var(--fg); font-family: var(--mono); font-size: 12px; padding: 1px 6px; cursor: pointer; }
.t-foot button:hover { border-color: var(--bad); color: var(--bad); } .t-count { color: var(--muted); }
.t-sorts { display: inline-flex; gap: 2px; }
@media (max-width: 1280px) {
.top { grid-template-columns: 1fr 1fr; grid-template-rows: minmax(230px, auto) minmax(220px, auto) minmax(200px, auto) minmax(420px, 1fr); }
.top-mem, .top-disks { min-height: 220px; }
.top-cpu { grid-column: 1 / -1; grid-row: 1; } .top-mem { grid-column: 1; grid-row: 2; } .top-disks { grid-column: 2; grid-row: 2; }
.top-net { grid-column: 1 / -1; grid-row: 3; } .top-proc { grid-column: 1 / -1; grid-row: 4; }
}
@media (max-width: 800px) {
.top { display: flex; flex-direction: column; font-size: 12px; }
.top > .tbox { flex: 0 0 auto; }
.t-cpu { flex-direction: column; } .t-cpu-graph { height: 130px; flex: 0 0 130px; } .t-cores { width: auto; max-height: 40vh; }
.t-net { flex-direction: column; } .t-net-nums { width: auto; } .t-net-graphs { flex: 0 0 130px; }
.top-proc { height: 70vh; min-height: 420px; }
}
/* ---- Space: what is using a disk ---- */
.space { position: absolute; inset: 0; display: flex; flex-direction: column; background: var(--bg); min-height: 0; }
.space-bar { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--border); background: var(--bg2); }
.space-pick-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.space-status { display: flex; align-items: center; gap: 10px; min-height: 28px; flex-wrap: wrap; }
.space-about { display: flex; gap: 0; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.space-about .cur { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* the disk-or-pool picker */
.tpick { position: relative; min-width: 260px; max-width: 100%; }
.tpick-btn { display: flex; align-items: center; gap: 8px; padding: 6px 10px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; text-align: left; max-width: 100%; }
.tpick-btn b { color: var(--accent); } .tpick-btn .chev { margin-left: auto; color: var(--muted); padding-left: 8px; } .tpick-btn .spin { margin: 0 0 0 4px; }
.ticon { color: var(--accent2); font-family: ui-monospace, monospace; width: 16px; text-align: center; flex-shrink: 0; }
.tmenu { position: absolute; z-index: 20; top: calc(100% + 4px); left: 0; min-width: min(520px, 90vw); max-height: 60vh; overflow-y: auto; background: var(--bg2); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.4); padding: 4px; }
.titem { width: 100%; display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 6px; text-align: left; }
.titem:hover { background: var(--bg3); } .titem.on { box-shadow: inset 3px 0 0 var(--accent); background: var(--bg3); } .titem.off { opacity: .5; cursor: default; }
.titem .tmain { display: flex; flex-direction: column; min-width: 0; flex: 1; } .titem .tname b { color: var(--fg); }
.titem .tsub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: ui-monospace, monospace; }
.titem .tstate { flex-shrink: 0; white-space: nowrap; }
.space .warn { color: var(--s2); }
.space-tabs { padding: 8px 12px 0; }
.space-split { flex: 1; display: flex; flex-direction: column; min-height: 0; padding: 8px 12px 12px; gap: 8px; }
.space-tree { flex: 1 1 55%; min-height: 120px; display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: 6px; background: var(--bg2); overflow: hidden; }
.space-rows { flex: 1; overflow: auto; min-height: 0; }
.space-row { display: grid; grid-template-columns: minmax(160px, 1fr) 84px 120px 70px 64px 88px; gap: 8px; align-items: center; padding: 3px 8px; font-size: 13px; border-bottom: 1px solid var(--border); cursor: pointer; white-space: nowrap; }
.space-row.head { position: sticky; top: 0; z-index: 1; background: var(--bg3); color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; cursor: default; }
.space-row:hover:not(.head) { background: var(--bg3); } .space-row.active { background: var(--bg3); box-shadow: inset 3px 0 0 var(--accent); }
.space-row.agg { color: var(--muted); font-style: italic; }
.space-row .n { display: flex; align-items: center; gap: 4px; min-width: 0; } .space-row .nm { overflow: hidden; text-overflow: ellipsis; }
.space-row .tw { width: 16px; flex-shrink: 0; color: var(--muted); padding: 0; font-size: 11px; } .space-row .tw:hover { color: var(--fg); }
.space-row .ico { width: 10px; height: 10px; flex-shrink: 0; border-radius: 2px; font-size: 9px; line-height: 10px; color: var(--accent2); text-align: center; }
.space-row .r { text-align: right; } .space-row .pct { display: flex; align-items: center; gap: 6px; }
.space-row .bar { flex: 1; height: 8px; background: var(--track); border-radius: 4px; overflow: hidden; } .space-row .bar > span { display: block; height: 100%; background: var(--s1); border-radius: 4px; }
.space-row.file { grid-template-columns: minmax(160px, 1fr) 84px 120px 88px; }
.space-map { flex: 1 1 45%; min-height: 200px; display: flex; flex-direction: column; gap: 6px; }
.space-crumbs { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; font-size: 13px; }
.space-crumbs .crumb { padding: 2px 6px; border-radius: 4px; color: var(--muted); } .space-crumbs .crumb:hover { color: var(--fg); background: var(--bg3); } .space-crumbs .crumb.on { color: var(--fg); font-weight: 600; }
.space-crumbs .crumb + .crumb::before { content: '›'; color: var(--muted); margin-right: 4px; }
.space-canvas { flex: 1; position: relative; min-height: 160px; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; background: var(--bg2); }
.space-canvas canvas { display: block; cursor: pointer; }
.space-tip { position: absolute; z-index: 2; max-width: 260px; padding: 6px 8px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; font-size: 12px; pointer-events: none; box-shadow: 0 4px 16px rgba(0,0,0,.35); }
.space-legend { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 11px; color: var(--muted); }
.space-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 4px; vertical-align: -1px; }
.space-legend i.dir { background: var(--bg3); border: 1px solid var(--border); } .space-legend i.small { background: #3a414b; }
.space-files { flex: 1; display: flex; flex-direction: column; min-height: 0; padding: 8px 12px 12px; gap: 8px; }
.space-files .space-rows { border: 1px solid var(--border); border-radius: 6px; background: var(--bg2); }
.space-files input.filter { padding: 6px 10px; background: var(--bg2); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); font-size: 13px; width: min(320px, 100%); }
@media (max-width: 800px) {
.space-row { grid-template-columns: minmax(120px, 1fr) 72px 80px; } .space-row .c, .space-row .d { display: none; }
.space-row.file { grid-template-columns: minmax(120px, 1fr) 72px 80px; }
.space-split { padding: 8px; } .space-tree { flex: 1 1 45%; } .space-map { flex: 1 1 55%; }
}
/* the Processes tab's drive rows and NIC switcher */
.t-disk .t-model { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 14ch; margin-left: 6px; }
.t-disk .t-temp { margin-right: 8px; color: var(--t-blue, var(--accent2)); }
.t-disk .t-mounts { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.t-ifs { display: inline-flex; gap: 4px; flex-wrap: wrap; justify-content: flex-end; }
.t-ifs .t-if { padding: 0 4px; border-radius: 3px; color: var(--muted); }
.t-ifs .t-if:hover { color: var(--fg); background: var(--bg3); }
.t-ifs .t-if.on { color: var(--fg); box-shadow: inset 0 -2px 0 var(--accent); }
/* ZFS: the schedule dialog's controls match the rest of the modal forms */
.modal select, .modal input[type=time], .modal input[type=number] { padding: 9px 10px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); font-size: 16px; }
.modal .chips { margin-top: 2px; }