diff --git a/docs/superpowers/specs/2026-06-02-void-v2-plan6-sacred-valley-design.md b/docs/superpowers/specs/2026-06-02-void-v2-plan6-sacred-valley-design.md
new file mode 100644
index 0000000..310ea51
--- /dev/null
+++ b/docs/superpowers/specs/2026-06-02-void-v2-plan6-sacred-valley-design.md
@@ -0,0 +1,203 @@
+# Void 2.0 — Plan 6: Sacred Valley Widgets — Design Spec
+
+> Status: APPROVED 2026-06-02. Design-led phase; brainstormed with the
+> `frontend-design` skill and the visual companion. Next step: `writing-plans`.
+> Supersedes the prep brief `docs/plan-6-brainstorm-brief.md`.
+
+## 1. Purpose
+
+Bring the Sacred Valley homelab dashboard to Void 2.0, on v2's real backend.
+The `#/sacred-valley` route currently holds a stub (`public/views/sacred_valley.js`);
+this plan replaces it with a working dashboard composed of **two distinct bands**:
+
+1. **Data cards** — a draggable, reorderable set of widget cards.
+2. **Little Blue's Health band** — a separate, fixed zone owned by the Little Blue
+ agent persona, showing homelab service health & uptime.
+
+The two bands are kept visually and structurally separate, by explicit request.
+
+## 2. Scope
+
+### In scope (ships as `2.0.0-alpha.8`)
+- The two-band Sacred Valley view + grid framework + drag-to-reorder.
+- Seven data cards: clock, weather, host-perf, speedtest, jobs, inbox, search.
+- Little Blue **read-only** Health band: config-file service registry, a pg-boss
+ health-check engine, grouped status tiles with auto-pulled icons, links, and a
+ placeholder avatar/identity.
+- Server-side, owner-scoped layout persistence.
+
+### Explicit non-goals (deferred)
+- Little Blue as a **fix-it agent** — chat + repair/Proxmox tools. Belongs with the
+ later Yerin/Orthos agent-wiring phase.
+- DB-backed and Proxmox-auto-discovery service registries (future upgrade path).
+- Multi-host host-perf (Plan 6 monitors CT 311 only; "other hosts up?" is the
+ health band's job).
+- Per-agent hue theming, `--rail-accent`, final Little Blue avatar art.
+- Agent-output cards (Dross briefing / Yerin pulse / Orthos council).
+
+## 3. Decisions (from the brainstorm)
+
+| Decision | Choice |
+|---|---|
+| Grid engine | Custom CSS-grid auto-flow + hand-rolled drag-to-reorder. **No resize, zero deps.** Sizes are S/M/L presets (column spans 1/2/3). |
+| Scope of dashboard | One **global** homelab dashboard (not per-Space). |
+| Layout persistence | **Server-side**, owner-scoped, so desktop ↔ phone sync. localStorage mirrors for instant paint. |
+| Card chrome | **"Refined B"** — dark panel, faint engraved texture, accent-underlined Cinzel title, low blackflame glow that **intensifies on hover**. |
+| Real-time | **Per-card polling**, no new SSE. |
+| Service registry (LB) | **Config file** now; DB-backed + Proxmox auto-discovery are noted future upgrades. |
+| Little Blue fix-it tools | **Deferred** to a later agent phase. |
+| Service icons | Auto-pulled from the walkxcode/dashboard-icons CDN, first-letter fallback (parity with v1). |
+
+## 4. Architecture
+
+### 4.1 View structure
+`public/views/sacred_valley.js` (replaces stub) renders into `#main`:
+- `#sv-cards` — the data-card band (draggable).
+- `#sv-health` — Little Blue's health band (fixed).
+
+A small scheduler starts each card's refresh timer on view-mount and clears all
+timers on unmount (route change), so polling never leaks across views.
+
+### 4.2 Card component contract
+`public/components/sv_card.js` is a factory producing the refined-B chrome
+(`.sv-card` + theme tokens). Each data card is a self-contained module in
+`public/views/cards/` exporting a uniform interface:
+
+```js
+export default {
+ id: 'host-perf',
+ title: 'Host Perf',
+ size: 'm', // 's' | 'm' | 'l' → grid column span 1 | 2 | 3
+ mount(bodyEl) { /* build DOM via safe el()/mount() — never innerHTML from data */ },
+ start() { /* begin refresh interval */ },
+ stop() { /* clear interval */ }
+};
+```
+
+One module per card — each independently understandable and testable. All DOM is
+built with the existing safe `el()/mount()`/`safeHref()` helpers (Plan 2 safe-DOM
+invariant: no `innerHTML` from API data).
+
+### 4.3 Grid + reorder
+CSS-grid auto-flow; S/M/L = column spans. A hand-rolled pointer-drag reorder on
+`#sv-cards` (no resize). Reordering updates the in-memory order and persists it
+(§4.4). The health band is not draggable.
+
+### 4.4 Layout persistence
+- Migration: `dashboard_layout` table, owner-scoped, single logical row:
+ `{ order: text[]/jsonb, hidden: jsonb, sizes: jsonb }` + `updated_at`.
+- API: `GET /api/dashboard/layout`, `PUT /api/dashboard/layout` (owner bearer).
+- Client: localStorage mirror for first paint; server is source of truth.
+
+## 5. Data cards
+
+| Card | Size | Source | Backend |
+|---|---|---|---|
+| clock | S | Client only. Melbourne primary; optional secondary TZ. | none |
+| weather | S | Open-Meteo, Melbourne, no key. | **new** `GET /api/weather` — server proxy, 15-min cache |
+| host-perf | M | CT 311 `/proc` CPU/RAM/disk/net, 30s. | **new** `GET /api/host` — port v1 `lib/resources.js` → `lib/host/resources.js` |
+| speedtest | M | Latest + ~30-bar history. | **new** `speedtest_results` migration + pg-boss recurring `speedtest` job (hourly `speedtest-cli`) + `GET /api/speedtest/history` + `POST /api/speedtest/run` (enqueue) |
+| jobs | M | pg-boss states — counts by state + recent. | **reuse** Jobs API |
+| inbox | S | Pending changes awaiting approval — count + recent; links `#/inbox`. | **reuse** pending_changes API |
+| search | L | Spotlight: type → results → navigate. | **reuse** `GET /api/search` (FTS+vector RRF) |
+
+Notes:
+- `speedtest-cli` (or `speedtest` Ookla CLI) must be present on the worker box;
+ `push-workers.sh` / deploy notes updated accordingly.
+- `/api/weather` caches server-side (15 min) to avoid hammering Open-Meteo.
+
+## 6. Little Blue Health band
+
+### 6.1 Identity
+- `public/components/littleblue_avatar.js` — **placeholder** blue humanoid
+ water-creature (inline SVG, cyan `--lb` glow). Marked placeholder; final art later.
+- Header subtitle: "Health & Uptime of the lab".
+
+### 6.2 Service registry (config file)
+`config/services.yaml`, authored fresh — **v1 tile titles are NOT inherited**
+(they were mislabeled); every entry gets a correct title here.
+
+```yaml
+- id: gitea
+ name: Gitea
+ category: infrastructure # agents | infrastructure | media
+ host: ct105 # display label on the tile
+ url: http://192.168.1.223:3000 # link target — "how to get there"
+ icon: gitea # optional; defaults to slugified name
+ check:
+ type: http # http | tcp
+ # url defaults to `url`; expect 2xx–3xx; short timeout
+```
+
+Group render order: **Agents → Infrastructure → Media → other**. Each group header
+shows a "X/Y healthy" count. The build seeds the file with the real homelab
+services for the user to correct.
+
+### 6.3 Health-check engine
+- A **Node pg-boss recurring job** `health.check` (every ~60s) pings each service
+ (HTTP/TCP, short timeout) from void-server and writes a cache row to a
+ `service_status` table: `{ service_id, status, latency_ms, detail, checked_at }`.
+- Status semantics: `ok` = check passed · `warn` = reachable but slow/degraded ·
+ `down` = unreachable/error.
+- Decoupled from page load → the band renders instantly from cache.
+- The checker only fetches URLs from the operator-authored registry (no user
+ input) — pinned to the configured list (SSRF surface is operator-controlled).
+
+### 6.4 API
+- `GET /api/health/services` → services grouped by category, each with cached
+ status + per-group healthy counts.
+- `POST /api/health/check` → enqueue an immediate pass (owner-only). Backs the
+ "Run checks" button.
+
+### 6.5 Rendering
+- `public/views/health_band.js` + `public/components/service_tile.js`.
+- Tile: auto-icon (`
`,
+ slug = `icon` or slugified `name`, first-letter `