Files
restic-manager/design/v1-operator-console.html
steve cca525a04d design: v1 polish — row accents, wider last-backup col, empty state
- Single .host-row CSS rule replaces 13 inline grid-template-columns
  copies; column widths bumped so "backup running…" doesn't wrap.
- Faint left-edge accent for degraded / failed / offline rows so
  problem hosts are scannable without reading.
- Empty-state hero added: top-bar + nav still present (Dashboard
  active, others dimmed) but body collapses to a calm "no hosts yet"
  prompt with the install command as the load-bearing affordance.
  Prerequisite note keeps the deliberate "restic must already be
  installed" decision visible to first-time operators.

This is the artefact P1-23/24/27 will template against.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 18:48:15 +01:00

699 lines
40 KiB
HTML
Raw Permalink 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.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>restic-manager · v1 Operator Console</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
/* Warm-dark slate, deliberately not pitch black — eyes prefer it on long sessions. */
--bg: oklch(0.17 0.006 250);
--panel: oklch(0.20 0.007 250);
--panel-hi: oklch(0.23 0.008 250);
--line: oklch(0.27 0.010 250);
--line-soft: oklch(0.23 0.008 250);
--ink: oklch(0.96 0.005 250);
--ink-mid: oklch(0.78 0.005 250);
--ink-mute: oklch(0.58 0.006 250);
--ink-fade: oklch(0.42 0.006 250);
--ok: oklch(0.78 0.14 155);
--warn: oklch(0.82 0.13 80);
--bad: oklch(0.70 0.20 25);
--off: oklch(0.50 0.005 250);
--accent: oklch(0.82 0.12 195);
--accent-dim:oklch(0.55 0.10 195);
}
html { background: var(--bg); }
body {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
color: var(--ink);
font-feature-settings: 'cv11', 'ss01', 'ss03';
}
.mono { font-family: 'JetBrains Mono', ui-monospace, monospace; font-variant-numeric: tabular-nums; }
.hairline { box-shadow: inset 0 -1px 0 var(--line-soft); }
.panel { background: var(--panel); border: 1px solid var(--line-soft); }
.row-hover:hover { background: var(--panel-hi); }
.dot { width: 7px; height: 7px; border-radius: 9999px; display: inline-block; }
.dot-online { background: var(--ok); box-shadow: 0 0 0 3px color-mix(in oklch, var(--ok), transparent 80%); }
.dot-degraded { background: var(--warn); box-shadow: 0 0 0 3px color-mix(in oklch, var(--warn), transparent 80%); }
.dot-offline { background: var(--off); }
.dot-failed { background: var(--bad); box-shadow: 0 0 0 3px color-mix(in oklch, var(--bad), transparent 80%); }
/* The 120% detail: status dots get a quiet pulse only when the host is currently running a job. */
.pulse {
animation: pulse 2.4s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent), transparent 80%); }
50% { box-shadow: 0 0 0 6px color-mix(in oklch, var(--accent), transparent 92%); }
}
.tag {
font-size: 11px; line-height: 1; padding: 4px 7px;
border: 1px solid var(--line); color: var(--ink-mid);
border-radius: 3px; letter-spacing: 0.01em;
}
.btn {
font-family: 'Inter'; font-size: 12px; font-weight: 500;
padding: 6px 11px; border-radius: 5px;
color: var(--ink-mid);
background: transparent; border: 1px solid var(--line);
transition: all 120ms ease;
}
.btn:hover { background: var(--panel-hi); color: var(--ink); border-color: var(--line); }
.btn-primary {
color: oklch(0.18 0.01 195); background: var(--accent); border-color: var(--accent);
}
.btn-primary:hover { background: var(--accent); filter: brightness(1.08); color: oklch(0.16 0.01 195);}
.btn-ghost { border-color: transparent; }
.btn-ghost:hover { border-color: var(--line); }
.nav-tab {
font-size: 13px; padding: 18px 0; color: var(--ink-mute);
border-bottom: 2px solid transparent; margin-right: 28px;
cursor: pointer;
}
.nav-tab.active { color: var(--ink); border-color: var(--accent); }
.nav-tab:hover { color: var(--ink); }
.text-balance { text-wrap: balance; }
::selection { background: color-mix(in oklch, var(--accent), transparent 70%); }
/* doc shell — separates the philosophy preamble from the hi-fi screen */
.doc {
max-width: 1280px; margin: 0 auto; padding: 0 32px;
}
.philosophy {
border-bottom: 1px solid var(--line-soft);
padding: 56px 0 40px;
}
.philosophy h1 { font-size: 22px; font-weight: 600; letter-spacing: -0.01em; }
.philosophy p {
color: var(--ink-mid); max-width: 680px;
margin-top: 14px; line-height: 1.65;
text-wrap: pretty;
}
.philosophy .meta { color: var(--ink-fade); font-size: 12px; margin-top: 14px; }
.stage-frame {
margin: 48px -32px;
border-top: 1px solid var(--line-soft);
border-bottom: 1px solid var(--line-soft);
}
.stage-frame::before, .stage-frame::after {
content: ''; display: block; height: 0;
}
.components-section { margin: 64px 0 96px; }
.components-section h2 { font-size: 14px; font-weight: 600; color: var(--ink-mute); text-transform: uppercase; letter-spacing: 0.08em; }
/* Single source of truth for the host-row grid. The "last backup"
column was previously 1.1fr — too narrow for "backup running…"
to fit on one line. Bumped to 1.5fr; trimmed name to compensate. */
.host-row {
display: grid; align-items: center;
grid-template-columns: 24px 1.4fr 0.95fr 1.5fr 0.75fr 0.7fr 0.7fr 1.1fr 92px;
padding: 11px 16px; font-size: 13px;
/* Reserve the 3px the row-accent will live in so degraded/failed
rows don't shift sideways relative to healthy ones. */
border-left: 3px solid transparent;
}
.host-row.head { padding-top: 10px; padding-bottom: 10px; font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em; border-left-width: 3px; }
.host-row.degraded { border-left-color: color-mix(in oklch, var(--warn), transparent 50%); }
.host-row.failed { border-left-color: color-mix(in oklch, var(--bad), transparent 50%); }
.host-row.offline { border-left-color: color-mix(in oklch, var(--off), transparent 70%); }
/* Empty state: full-bleed quiet panel inside the stage. */
.empty-state {
text-align: center; padding: 88px 32px 96px;
border: 1px dashed var(--line); border-radius: 8px;
background:
radial-gradient(ellipse at top, color-mix(in oklch, var(--accent), transparent 95%), transparent 60%),
var(--panel);
}
.empty-state h3 { font-size: 18px; font-weight: 500; letter-spacing: -0.005em; }
.empty-state p {
color: var(--ink-mid); max-width: 520px; margin: 12px auto 0;
line-height: 1.65; font-size: 13px; text-wrap: pretty;
}
.empty-state .install-card {
max-width: 640px; margin: 28px auto 0;
text-align: left; background: var(--bg);
border: 1px solid var(--line-soft); border-radius: 6px;
overflow: hidden;
}
.empty-state .install-card-head {
font-size: 11px; color: var(--ink-fade);
padding: 10px 14px; border-bottom: 1px solid var(--line-soft);
display: flex; justify-content: space-between; align-items: center;
text-transform: uppercase; letter-spacing: 0.1em;
}
.empty-state .install-card pre {
margin: 0; padding: 14px; font-family: 'JetBrains Mono', monospace;
font-size: 12px; color: var(--ink-mid); line-height: 1.7;
white-space: pre-wrap; word-break: break-all;
}
.empty-state .install-card pre .var { color: var(--accent); }
</style>
</head>
<body class="bg-[color:var(--bg)]">
<div class="doc">
<!-- Philosophy preamble -->
<header class="philosophy">
<div class="text-xs uppercase tracking-[0.18em] text-[color:var(--ink-fade)] mb-3">v1 · Direction A</div>
<h1>Operator console.</h1>
<p>
Optimised for the daily 30-second glance. Dense by default, never fussy. Every
pixel earns its place; colour is reserved for state, not decoration. Numerics
are monospaced and right-aligned so the eye can scan a column without
tracking labels. Hover reveals secondary actions; primary actions are always visible.
</p>
<p>
Reference: Linear, Datadog, Vercel observability. Built for someone who looks
at this every morning while their coffee brews.
</p>
<p class="meta">
Not for: a public-facing landing page, a marketing surface, a screen anyone
shows to non-technical stakeholders. This is a tool, not a product page.
</p>
</header>
<!-- Stage: full hi-fi dashboard -->
<div class="stage-frame">
<div style="background: var(--bg); padding: 0;">
<!-- Top bar -->
<div class="hairline" style="background: var(--bg);">
<div class="doc flex items-center justify-between" style="padding: 16px 0;">
<div class="flex items-center gap-3">
<div class="mono" style="font-size:13px; letter-spacing: 0.02em; color: var(--ink); font-weight:500;">restic-manager</div>
<div class="mono" style="font-size:11px; color: var(--ink-fade);">v0.1.0-alpha</div>
</div>
<div class="flex items-center gap-5">
<div class="mono" style="font-size:12px; color: var(--ink-mute);">steve@dcglab</div>
<button class="btn btn-ghost">Sign out</button>
</div>
</div>
</div>
<!-- Primary nav -->
<div class="hairline" style="background: var(--bg);">
<div class="doc flex items-end justify-between">
<nav class="flex items-end">
<div class="nav-tab active">Dashboard</div>
<div class="nav-tab">Repos</div>
<div class="nav-tab">Alerts <span class="mono ml-1.5" style="font-size:11px; color: var(--bad);">5</span></div>
<div class="nav-tab">Audit</div>
<div class="nav-tab">Settings</div>
</nav>
<div class="pb-3">
<button class="btn btn-primary">+ Add host</button>
</div>
</div>
</div>
<!-- Fleet summary strip -->
<div class="doc" style="padding: 28px 32px;">
<div class="grid grid-cols-12 gap-6">
<div class="col-span-3">
<div style="font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 8px;">Fleet</div>
<div class="mono" style="font-size: 28px; font-weight: 500; letter-spacing: -0.02em;">12 <span style="color: var(--ink-mute); font-size:13px; font-weight:400;">hosts</span></div>
<div class="flex items-center gap-3 mt-2.5 text-xs">
<span class="flex items-center gap-1.5"><span class="dot dot-online"></span><span class="mono" style="color: var(--ink-mid);">10</span><span style="color: var(--ink-mute);">online</span></span>
<span class="flex items-center gap-1.5"><span class="dot dot-degraded"></span><span class="mono" style="color: var(--ink-mid);">1</span><span style="color: var(--ink-mute);">degraded</span></span>
<span class="flex items-center gap-1.5"><span class="dot dot-offline"></span><span class="mono" style="color: var(--ink-mid);">1</span><span style="color: var(--ink-mute);">offline</span></span>
</div>
</div>
<div class="col-span-3">
<div style="font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 8px;">Backed up</div>
<div class="mono" style="font-size: 28px; font-weight: 500; letter-spacing: -0.02em;">4.9 <span style="color: var(--ink-mute); font-size:13px; font-weight:400;">TB across 12 repos</span></div>
<div class="text-xs mt-2.5" style="color: var(--ink-mute);"><span class="mono" style="color: var(--ink-mid);">23,649</span> snapshots total</div>
</div>
<div class="col-span-3">
<div style="font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 8px;">Last 24h</div>
<div class="mono" style="font-size: 28px; font-weight: 500; letter-spacing: -0.02em;">147 <span style="color: var(--ink-mute); font-size:13px; font-weight:400;">jobs</span></div>
<div class="text-xs mt-2.5"><span class="mono" style="color: var(--ok);">144</span> <span style="color: var(--ink-mute);">succeeded</span> · <span class="mono" style="color: var(--bad);">2</span> <span style="color: var(--ink-mute);">failed</span> · <span class="mono" style="color: var(--ink-mid);">1</span> <span style="color: var(--ink-mute);">cancelled</span></div>
</div>
<div class="col-span-3">
<div style="font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 8px;">Open alerts</div>
<div class="mono" style="font-size: 28px; font-weight: 500; letter-spacing: -0.02em; color: var(--bad);">5 <span style="color: var(--ink-mute); font-size:13px; font-weight:400;">unresolved</span></div>
<div class="text-xs mt-2.5" style="color: var(--ink-mute);">oldest <span class="mono" style="color: var(--ink-mid);">3h</span> · acknowledge in panel</div>
</div>
</div>
</div>
<!-- Hosts table -->
<div class="doc" style="padding: 8px 32px 24px;">
<!-- table head -->
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-3">
<h2 style="font-size: 13px; font-weight: 600; color: var(--ink); letter-spacing: 0.01em;">Hosts</h2>
<div style="font-size: 12px; color: var(--ink-fade);">12 of 12</div>
</div>
<div class="flex items-center gap-2">
<input type="text" placeholder="Filter by name, tag, status…"
class="mono px-3 py-1.5 text-xs"
style="background: var(--panel); border: 1px solid var(--line-soft); color: var(--ink); border-radius: 5px; width: 280px; outline: none;"
/>
<button class="btn">Group: tag ▾</button>
<button class="btn">Sort: status ▾</button>
</div>
</div>
<!-- table -->
<div class="panel" style="border-radius: 7px; overflow: hidden;">
<!-- header row -->
<div class="host-row head hairline">
<div></div>
<div>Host</div>
<div>OS · arch</div>
<div>Last backup</div>
<div class="text-right">Repo size</div>
<div class="text-right">Snapshots</div>
<div class="text-right">Alerts</div>
<div>Tags</div>
<div></div>
</div>
<!-- row: prod-db-01 — online + currently running -->
<div class="row-hover host-row hairline">
<div><span class="dot dot-online pulse"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">prod-db-01</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/amd64</div>
<div class="text-xs" style="color: var(--ink-mid);">
<span style="color: var(--accent);">backup running…</span><br>
<span class="mono" style="color: var(--ink-fade);">started 3m ago</span>
</div>
<div class="text-right mono" style="color: var(--ink);">412 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">1,847</div>
<div class="text-right mono" style="color: var(--ink-mute);"></div>
<div class="flex gap-1.5"><span class="tag">prod</span><span class="tag">db</span></div>
<div class="text-right"><button class="btn btn-ghost">View →</button></div>
</div>
<!-- row: prod-db-02 -->
<div class="row-hover host-row hairline">
<div><span class="dot dot-online"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">prod-db-02</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/amd64</div>
<div class="text-xs" style="color: var(--ink-mid);">
<span style="color: var(--ok);">succeeded</span> · <span class="mono">4m ago</span>
</div>
<div class="text-right mono" style="color: var(--ink);">389 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">1,802</div>
<div class="text-right mono" style="color: var(--ink-mute);"></div>
<div class="flex gap-1.5"><span class="tag">prod</span><span class="tag">db</span></div>
<div class="text-right"><button class="btn">Run now</button></div>
</div>
<!-- row: prod-web-01 -->
<div class="row-hover host-row hairline">
<div><span class="dot dot-online"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">prod-web-01</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/amd64</div>
<div class="text-xs" style="color: var(--ink-mid);">
<span style="color: var(--ok);">succeeded</span> · <span class="mono">11m ago</span>
</div>
<div class="text-right mono" style="color: var(--ink);">87 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">2,103</div>
<div class="text-right mono" style="color: var(--ink-mute);"></div>
<div class="flex gap-1.5"><span class="tag">prod</span><span class="tag">web</span></div>
<div class="text-right"><button class="btn">Run now</button></div>
</div>
<!-- row: prod-web-02 -->
<div class="row-hover host-row hairline">
<div><span class="dot dot-online"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">prod-web-02</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/amd64</div>
<div class="text-xs" style="color: var(--ink-mid);">
<span style="color: var(--ok);">succeeded</span> · <span class="mono">12m ago</span>
</div>
<div class="text-right mono" style="color: var(--ink);">87 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">2,098</div>
<div class="text-right mono" style="color: var(--ink-mute);"></div>
<div class="flex gap-1.5"><span class="tag">prod</span><span class="tag">web</span></div>
<div class="text-right"><button class="btn">Run now</button></div>
</div>
<!-- row: prod-cache-01 — degraded with alerts -->
<div class="row-hover host-row degraded hairline">
<div><span class="dot dot-degraded"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">prod-cache-01</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/amd64</div>
<div class="text-xs" style="color: var(--ink-mid);">
<span style="color: var(--ok);">succeeded</span> · <span class="mono">1h ago</span>
</div>
<div class="text-right mono" style="color: var(--ink);">128 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">1,402</div>
<div class="text-right mono" style="color: var(--bad); font-weight: 500;">3</div>
<div class="flex gap-1.5"><span class="tag">prod</span><span class="tag">cache</span></div>
<div class="text-right"><button class="btn">Run now</button></div>
</div>
<!-- row: homelab-nas -->
<div class="row-hover host-row hairline">
<div><span class="dot dot-online"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">homelab-nas</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/arm64</div>
<div class="text-xs" style="color: var(--ink-mid);">
<span style="color: var(--ok);">succeeded</span> · <span class="mono">2h ago</span>
</div>
<div class="text-right mono" style="color: var(--ink);">3.7 <span style="color: var(--ink-mute); font-size: 11px;">TB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">8,912</div>
<div class="text-right mono" style="color: var(--ink-mute);"></div>
<div class="flex gap-1.5"><span class="tag">homelab</span><span class="tag">storage</span></div>
<div class="text-right"><button class="btn">Run now</button></div>
</div>
<!-- row: homelab-pi -->
<div class="row-hover host-row hairline">
<div><span class="dot dot-online"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">homelab-pi</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/arm64</div>
<div class="text-xs" style="color: var(--ink-mid);">
<span style="color: var(--ok);">succeeded</span> · <span class="mono">6h ago</span>
</div>
<div class="text-right mono" style="color: var(--ink);">8.4 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">421</div>
<div class="text-right mono" style="color: var(--ink-mute);"></div>
<div class="flex gap-1.5"><span class="tag">homelab</span><span class="tag">iot</span></div>
<div class="text-right"><button class="btn">Run now</button></div>
</div>
<!-- row: dev-laptop — offline -->
<div class="row-hover host-row offline hairline">
<div><span class="dot dot-offline"></span></div>
<div class="mono" style="color: var(--ink-mid); font-weight: 500;">dev-laptop</div>
<div class="mono" style="color: var(--ink-mute); font-size:12px;">linux/amd64</div>
<div class="text-xs" style="color: var(--ink-mute);">
<span>last seen <span class="mono">2d ago</span></span>
</div>
<div class="text-right mono" style="color: var(--ink-mid);">64 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mute);">127</div>
<div class="text-right mono" style="color: var(--bad); font-weight: 500;">1</div>
<div class="flex gap-1.5"><span class="tag">dev</span><span class="tag">personal</span></div>
<div class="text-right"><span class="mono text-xs" style="color: var(--ink-fade);">offline</span></div>
</div>
<!-- row: windows-vm -->
<div class="row-hover host-row hairline">
<div><span class="dot dot-online"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">windows-vm</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">windows/amd64</div>
<div class="text-xs" style="color: var(--ink-mid);">
<span style="color: var(--ok);">succeeded</span> · <span class="mono">28m ago</span>
</div>
<div class="text-right mono" style="color: var(--ink);">44 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">156</div>
<div class="text-right mono" style="color: var(--ink-mute);"></div>
<div class="flex gap-1.5"><span class="tag">staging</span><span class="tag">vm</span></div>
<div class="text-right"><button class="btn">Run now</button></div>
</div>
<!-- row: build-runner — failed -->
<div class="row-hover host-row failed hairline">
<div><span class="dot dot-online"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">build-runner</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/amd64</div>
<div class="text-xs" style="color: var(--ink-mid);">
<span style="color: var(--bad); font-weight:500;">failed</span> · <span class="mono">47m ago</span>
</div>
<div class="text-right mono" style="color: var(--ink);">97 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">2,847</div>
<div class="text-right mono" style="color: var(--bad); font-weight: 500;">1</div>
<div class="flex gap-1.5"><span class="tag">ci</span><span class="tag">build</span></div>
<div class="text-right"><button class="btn">Retry</button></div>
</div>
<!-- row: backup-test — never -->
<div class="row-hover host-row hairline">
<div><span class="dot dot-online"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">backup-test</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/amd64</div>
<div class="text-xs" style="color: var(--ink-mute);">
<span style="color: var(--ink-fade); font-style: italic;">never run</span>
</div>
<div class="text-right mono" style="color: var(--ink-fade);"></div>
<div class="text-right mono" style="color: var(--ink-fade);"></div>
<div class="text-right mono" style="color: var(--ink-mute);"></div>
<div class="flex gap-1.5"><span class="tag">test</span></div>
<div class="text-right"><button class="btn">Run first</button></div>
</div>
<!-- row: edge-node-eu (last row, no hairline) -->
<div class="row-hover host-row">
<div><span class="dot dot-online"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">edge-node-eu</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/arm64</div>
<div class="text-xs" style="color: var(--ink-mid);">
<span style="color: var(--ok);">succeeded</span> · <span class="mono">7m ago</span>
</div>
<div class="text-right mono" style="color: var(--ink);">23 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">934</div>
<div class="text-right mono" style="color: var(--ink-mute);"></div>
<div class="flex gap-1.5"><span class="tag">edge</span><span class="tag">prod</span></div>
<div class="text-right"><button class="btn">Run now</button></div>
</div>
</div>
</div>
<!-- Recent jobs strip -->
<div class="doc" style="padding: 16px 32px 56px;">
<div class="flex items-center justify-between mb-3">
<h2 style="font-size: 13px; font-weight: 600; color: var(--ink); letter-spacing: 0.01em;">Recent activity</h2>
<a class="text-xs" style="color: var(--ink-mute);">View all jobs →</a>
</div>
<div class="panel" style="border-radius: 7px;">
<div class="grid hairline" style="grid-template-columns: 1fr 1.5fr 0.8fr 1fr 1fr 1fr; padding: 9px 16px; font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em;">
<div>Job</div><div>Host</div><div>Kind</div><div>Status</div><div>Started</div><div class="text-right">Bytes / Δ</div>
</div>
<div class="grid hairline mono" style="grid-template-columns: 1fr 1.5fr 0.8fr 1fr 1fr 1fr; padding: 10px 16px; font-size: 12px;">
<div style="color: var(--ink-mute);">01KQH…E59B</div>
<div style="color: var(--ink);">prod-db-01</div>
<div style="color: var(--ink-mid);">backup</div>
<div style="color: var(--accent);">running · 38%</div>
<div style="color: var(--ink-mid);">3m ago</div>
<div class="text-right" style="color: var(--ink-mid);">1.4 GB</div>
</div>
<div class="grid hairline mono" style="grid-template-columns: 1fr 1.5fr 0.8fr 1fr 1fr 1fr; padding: 10px 16px; font-size: 12px;">
<div style="color: var(--ink-mute);">01KQH…D9XK</div>
<div style="color: var(--ink);">prod-db-02</div>
<div style="color: var(--ink-mid);">backup</div>
<div style="color: var(--ok);">succeeded</div>
<div style="color: var(--ink-mid);">4m ago</div>
<div class="text-right" style="color: var(--ink-mid);">1.2 GB</div>
</div>
<div class="grid hairline mono" style="grid-template-columns: 1fr 1.5fr 0.8fr 1fr 1fr 1fr; padding: 10px 16px; font-size: 12px;">
<div style="color: var(--ink-mute);">01KQH…7P2R</div>
<div style="color: var(--ink);">edge-node-eu</div>
<div style="color: var(--ink-mid);">backup</div>
<div style="color: var(--ok);">succeeded</div>
<div style="color: var(--ink-mid);">7m ago</div>
<div class="text-right" style="color: var(--ink-mid);">18 MB</div>
</div>
<div class="grid hairline mono" style="grid-template-columns: 1fr 1.5fr 0.8fr 1fr 1fr 1fr; padding: 10px 16px; font-size: 12px;">
<div style="color: var(--ink-mute);">01KQH…M4QQ</div>
<div style="color: var(--ink);">prod-web-01</div>
<div style="color: var(--ink-mid);">backup</div>
<div style="color: var(--ok);">succeeded</div>
<div style="color: var(--ink-mid);">11m ago</div>
<div class="text-right" style="color: var(--ink-mid);">42 MB</div>
</div>
<div class="grid mono" style="grid-template-columns: 1fr 1.5fr 0.8fr 1fr 1fr 1fr; padding: 10px 16px; font-size: 12px;">
<div style="color: var(--ink-mute);">01KQH…9F8C</div>
<div style="color: var(--ink);">build-runner</div>
<div style="color: var(--ink-mid);">backup</div>
<div style="color: var(--bad);">failed · repo locked</div>
<div style="color: var(--ink-mid);">47m ago</div>
<div class="text-right" style="color: var(--ink-fade);"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Empty state — the very first screen a new user sees. The chrome,
fleet stat blocks, and table are all replaced with a single
calm prompt: enrol your first host. The install command is the
hero affordance. -->
<div style="margin-top: 64px;">
<div class="text-xs uppercase tracking-[0.18em]" style="color: var(--ink-fade); margin-bottom: 12px;">Empty state · first run</div>
</div>
<div class="stage-frame">
<div style="background: var(--bg); padding: 0;">
<!-- Top bar (still present, just with no host context) -->
<div class="hairline" style="background: var(--bg);">
<div class="doc flex items-center justify-between" style="padding: 16px 0;">
<div class="flex items-center gap-3">
<div class="mono" style="font-size:13px; letter-spacing: 0.02em; color: var(--ink); font-weight:500;">restic-manager</div>
<div class="mono" style="font-size:11px; color: var(--ink-fade);">v0.1.0-alpha</div>
</div>
<div class="flex items-center gap-5">
<div class="mono" style="font-size:12px; color: var(--ink-mute);">steve@dcglab</div>
<button class="btn btn-ghost">Sign out</button>
</div>
</div>
</div>
<!-- Nav (Dashboard active, but everything else dimmed since there's nothing to see yet) -->
<div class="hairline" style="background: var(--bg);">
<div class="doc flex items-end justify-between">
<nav class="flex items-end">
<div class="nav-tab active">Dashboard</div>
<div class="nav-tab" style="opacity: 0.45;">Repos</div>
<div class="nav-tab" style="opacity: 0.45;">Alerts</div>
<div class="nav-tab" style="opacity: 0.45;">Audit</div>
<div class="nav-tab">Settings</div>
</nav>
<div class="pb-3">
<button class="btn btn-primary">+ Add host</button>
</div>
</div>
</div>
<!-- The main empty body -->
<div class="doc" style="padding: 56px 32px 80px;">
<div class="empty-state">
<h3>No hosts yet.</h3>
<p>
<span class="mono" style="color: var(--ink);">restic-manager</span> tracks
backups across a fleet — but theres nothing to track until you enrol
your first host. Run the snippet below on a Linux box, paste the
one-time token, and itll appear here within a few seconds.
</p>
<div class="install-card">
<div class="install-card-head">
<span>Install command · expires in 1h</span>
<button class="btn btn-ghost mono" style="font-size: 11px;">Copy</button>
</div>
<pre>curl -fsSL <span class="var">https://restic.lab.example/install.sh</span> | sudo \
RM_SERVER=<span class="var">https://restic.lab.example</span> \
RM_TOKEN=<span class="var">HdqFbQh8U-I1fb52iP1M8qxvoYS5t9VZ-T-yghr_CzA</span> sh</pre>
</div>
<div style="margin-top: 28px; display: flex; justify-content: center; gap: 10px;">
<button class="btn btn-primary">+ Add host (mint another token)</button>
<button class="btn">Read the install guide →</button>
</div>
<div style="margin-top: 36px; font-size: 12px; color: var(--ink-fade);">
Prerequisites: <span class="mono" style="color: var(--ink-mute);">restic</span> ≥ 0.16 already installed on the target host. The agent will not install it for you — see <a class="underline underline-offset-4 decoration-1" style="color: var(--ink-mid); text-decoration-color: var(--line);">why</a>.
</div>
</div>
</div>
</div>
</div>
<!-- Component vocabulary -->
<section class="components-section">
<h2>Component vocabulary</h2>
<p style="color: var(--ink-mute); font-size: 13px; margin-top: 8px; max-width: 620px;">
Pulled out of the live screen so the system reads cleanly. Anything that doesnt appear
here doesnt exist in this direction — and the goal is for the list below to stay short.
</p>
<div class="grid grid-cols-2 gap-10 mt-10">
<!-- status states -->
<div>
<div style="font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 12px;">Status</div>
<div class="panel" style="border-radius: 7px; padding: 14px 18px;">
<div class="flex items-center justify-between text-sm py-2">
<span class="flex items-center gap-3"><span class="dot dot-online"></span><span>online</span></span>
<span class="mono text-xs" style="color: var(--ink-mute);">heartbeat &lt; 90s</span>
</div>
<div class="flex items-center justify-between text-sm py-2 hairline">
<span class="flex items-center gap-3"><span class="dot dot-online pulse"></span><span>online · running job</span></span>
<span class="mono text-xs" style="color: var(--ink-mute);">pulse only when active</span>
</div>
<div class="flex items-center justify-between text-sm py-2 hairline">
<span class="flex items-center gap-3"><span class="dot dot-degraded"></span><span>degraded</span></span>
<span class="mono text-xs" style="color: var(--ink-mute);">open alerts &gt; 0</span>
</div>
<div class="flex items-center justify-between text-sm py-2 hairline">
<span class="flex items-center gap-3"><span class="dot dot-offline"></span><span>offline</span></span>
<span class="mono text-xs" style="color: var(--ink-mute);">no heartbeat &gt; 90s</span>
</div>
<div class="flex items-center justify-between text-sm py-2 hairline">
<span class="flex items-center gap-3"><span class="dot dot-failed"></span><span>last job failed</span></span>
<span class="mono text-xs" style="color: var(--ink-mute);">distinct from offline</span>
</div>
</div>
</div>
<!-- buttons -->
<div>
<div style="font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 12px;">Buttons</div>
<div class="panel" style="border-radius: 7px; padding: 16px 18px;">
<div class="flex items-center gap-2 flex-wrap">
<button class="btn btn-primary">+ Add host</button>
<button class="btn">Run now</button>
<button class="btn btn-ghost">View →</button>
<button class="btn" style="color: var(--bad); border-color: color-mix(in oklch, var(--bad), transparent 70%);">Cancel job</button>
<button class="btn" disabled style="opacity: 0.4; cursor: not-allowed;">Run now</button>
</div>
<div class="text-xs mt-3" style="color: var(--ink-mute);">
One <span style="color:var(--ink);">primary</span> per page (Add host). Everything else is the
neutral <span style="color:var(--ink);">secondary</span> or <span style="color:var(--ink);">ghost</span>; danger only on destructive verbs.
</div>
</div>
</div>
</div>
<!-- host card states (the rows compressed into 3 distinct examples) -->
<div class="mt-10">
<div style="font-size: 11px; color: var(--ink-fade); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 12px;">Host row · 3 states</div>
<div class="panel" style="border-radius: 7px;">
<div class="row-hover host-row hairline">
<div><span class="dot dot-online"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">healthy-host</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/amd64</div>
<div class="text-xs" style="color: var(--ink-mid);"><span style="color: var(--ok);">succeeded</span> · <span class="mono">5m ago</span></div>
<div class="text-right mono" style="color: var(--ink);">87 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">2,103</div>
<div class="text-right mono" style="color: var(--ink-mute);"></div>
<div class="flex gap-1.5"><span class="tag">prod</span></div>
<div class="text-right"><button class="btn">Run now</button></div>
</div>
<div class="row-hover host-row degraded hairline">
<div><span class="dot dot-degraded"></span></div>
<div class="mono" style="color: var(--ink); font-weight: 500;">degraded-host</div>
<div class="mono" style="color: var(--ink-mid); font-size:12px;">linux/amd64</div>
<div class="text-xs" style="color: var(--ink-mid);"><span style="color: var(--ok);">succeeded</span> · <span class="mono">1h ago</span></div>
<div class="text-right mono" style="color: var(--ink);">128 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">1,402</div>
<div class="text-right mono" style="color: var(--bad); font-weight: 500;">3</div>
<div class="flex gap-1.5"><span class="tag">prod</span></div>
<div class="text-right"><button class="btn">Run now</button></div>
</div>
<div class="row-hover host-row offline">
<div><span class="dot dot-offline"></span></div>
<div class="mono" style="color: var(--ink-mid); font-weight: 500;">offline-host</div>
<div class="mono" style="color: var(--ink-mute); font-size:12px;">linux/amd64</div>
<div class="text-xs" style="color: var(--ink-mute);">last seen <span class="mono">2d ago</span></div>
<div class="text-right mono" style="color: var(--ink-mid);">64 <span style="color: var(--ink-mute); font-size: 11px;">GB</span></div>
<div class="text-right mono" style="color: var(--ink-mute);">127</div>
<div class="text-right mono" style="color: var(--bad); font-weight: 500;">1</div>
<div class="flex gap-1.5"><span class="tag">dev</span></div>
<div class="text-right"><span class="mono text-xs" style="color: var(--ink-fade);">offline</span></div>
</div>
</div>
</div>
</section>
</div>
</body>
</html>