Four takes on the orb — all violet, all his. Which feels right?
+
+
+
+
A · Soft Eye
+
The eye, softened — rounder, a glint, a calmer gaze. Still true to character, less stare.
+
+
+
+
B · Wisp Core
+
A swirling violet madra core. No eye — a contained spirit. Abstract & mystical.
+
+
+
+
C · Rune Sigil
+
A glowing arcane glyph that slowly turns. Reads as "a power", not a face.
+
+
+
+
D · Orbiting Motes
+
A bright core with motes circling it. Lively, restless — feels alive & busy.
+
+
+
+
The chat (revised)
+
New mic icon · bigger mic + send below the input · opens anchored to the orb. The live orb is bottom-right — drag it, tap to open.
+
+
+
2
+
+
+
+
+
Dross always here, regrettably
+
+
+
+
Dross
Back already? Your CPU graphs and I were just getting acquainted. Thrilling curves. What do you need?
+
how's the farm backup
+
Dross
Two days ago — 2.5 gigs, landed on Won, didn't fall over. I'd have woken you otherwise. Per-guest breakdown, or shall we keep trusting the universe?
+
+
+
+
+
+
+
+
+
+ ⌄ collapse
+
+
+
+
+
+
diff --git a/docs/superpowers/specs/2026-06-09-floating-dross-chat-design.md b/docs/superpowers/specs/2026-06-09-floating-dross-chat-design.md
new file mode 100644
index 0000000..4346d3f
--- /dev/null
+++ b/docs/superpowers/specs/2026-06-09-floating-dross-chat-design.md
@@ -0,0 +1,94 @@
+# Floating Dross Chat — Design
+
+**Date:** 2026-06-09
+**Status:** Approved (pending final spec sign-off)
+**Goal:** Replace the docked, per-Space "Cradle Chat" with a global, movable floating-bubble Dross companion — mobile-first, with voice-clip input transcribed locally into instructions.
+
+---
+
+## Background / problem
+
+Today the companion lives in the right rail (`public/components/rightrail.js`). It is **per-Space**: it binds to the active Space's companion conversation (`/api/spaces/:space_id/companion`) and shows "Open a Space to chat with its companion." everywhere else — Sacred Valley, the apps, etc. Because the user mostly lives on non-Space views, the chat is empty/collapsed most of the time, which is why it "feels closed and not very Dross." The right rail is also cramped on mobile.
+
+The chat mechanics are already factored into a reusable engine (`public/components/agent_chat.js`, `wireAgentChat({logEl, inputEl, historyUrl, turnUrl, …})`). Turns stream over SSE via `lib/ai/agent/run_turn.js`. Dross is the agent with slug `companion`.
+
+## Locked decisions (from brainstorming)
+
+1. **Global Dross** — one always-available companion, summonable on every view; not tied to a Space. He is told what the user is currently looking at (view context) but isn't locked to it.
+2. **Floating bubble** — a draggable violet orb that opens a draggable chat panel anchored to the orb. Replaces the right-rail companion. Position + open/closed state persist. Mobile = near-full-width panel.
+3. **Collapse / close** — keep the **close (✕) top-right**, and add a thumb-friendly **"⌄ collapse" bar at the bottom** of the panel. Both minimise back to the orb.
+4. **Avatar** — default **Soft Eye**; selectable in Settings between **Soft Eye**, **Wisp Core**, **Orbiting Motes** (all violet).
+5. **Colour** — Dross is **violet** by default, but his accent is **tunable in Settings** (his own vars, independent of the UI theme).
+6. **Persona** — give him the real Cradle-Dross voice (dry, sardonic, impatient, brilliant, secretly loyal) via an **editable system prompt in Settings** (tunable).
+7. **Voice** — record a clip → transcribe with **local faster-whisper on the Ollama box (CT 102, GPU, CPU-fallback)** → transcript lands in the input for **review-and-send first (mode 1)**. A *voice-mode* setting allows graduating to **hands-free auto-send (mode 2)**, then **interpret-into-confirmable-action (mode 3)** later.
+
+## Non-goals (this iteration)
+
+- Voice modes 2 and 3 are designed-for but not built now (mode setting ships; only mode 1 wired).
+- Multi-conversation history browser, per-Space companions in the bubble, and wake-word/always-listening are out of scope.
+
+---
+
+## Architecture
+
+### Components
+
+| Unit | Responsibility |
+|---|---|
+| `public/components/dross_bubble.js` (new) | The floating orb + panel: render, drag (orb & panel header), anchored open, collapse/close, avatar switch, voice record UI. Drives chat via `wireAgentChat`. Replaces the `renderRightrail` mount in `app.js`. |
+| `public/components/dross_avatar.js` (new) | Pure render of the chosen avatar (soft-eye / wisp / motes) at a given size — reused by orb + panel header + settings preview. |
+| `lib/api/routes/dross.js` (new) | Global (space-less) Dross: `GET /api/dross` (history + conversation id) and `POST /api/dross/turn` (SSE). Mirrors `companion.js` but resolves a **global** conversation for the `companion` agent and injects the persona + view context. |
+| `lib/api/routes/voice.js` (new) | `POST /api/voice/transcribe` — accepts an audio blob, proxies to the faster-whisper service, returns `{ text }`. Owner-only. |
+| `public/views/settings.js` (extend) | New **Dross** section: avatar picker, accent colour, persona textarea, voice-mode select. Persists to `app_settings` key `dross`. |
+| faster-whisper service on **CT 102** (infra) | OpenAI-compatible `/v1/audio/transcriptions` (e.g. `faster-whisper-server`/`speaches`), GPU with CPU fallback, small/base model. Shares the Ollama LXC. |
+
+### Settings shape (`app_settings` key `dross`)
+
+```json
+{
+ "avatar": "soft-eye", // soft-eye | wisp | motes
+ "accent": "#a86adf", // Dross's violet (independent of UI theme)
+ "persona": "",
+ "voiceMode": "review" // review | handsfree | action(later)
+}
+```
+
+Reuses the generic `app_settings` store (added in 2.9.0) and the `/api/theme`-style read-on-boot pattern. The bubble fetches `dross` settings on mount; the Settings panel writes them.
+
+---
+
+## Data flow
+
+**Text turn:** input → `wireAgentChat` → `POST /api/dross/turn` (body `{ text, view }`) → SSE stream of Dross's reply (+ tool labels) into the panel log. History via `GET /api/dross`.
+
+**Voice turn (mode 1):** tap mic → `MediaRecorder` captures a clip → on stop, `POST /api/voice/transcribe` (audio blob) → void-app proxies to CT 102 faster-whisper → `{ text }` → text dropped into the input for the user to review/edit → user sends as a normal turn. (Mode 2 would auto-send; mode 3 would route the transcript through an interpret step.)
+
+**Persona:** the `dross.persona` setting is injected as/with the agent's system prompt in `run_turn` for the global conversation, so his voice is consistent and user-tunable.
+
+**Context:** `view` (current route/entity) is passed in the turn body so Dross can answer "what am I looking at" questions.
+
+---
+
+## Error handling
+
+- **STT unavailable / GPU absent:** transcribe endpoint returns a clear error; the bubble shows "couldn't transcribe — type instead" and never blocks text input. faster-whisper falls back to CPU on a GPU-less node (per the GPU/CPU-fallback HA rule) — slower but functional.
+- **Mic permission denied:** show a one-line hint; hide the recording UI, keep typing.
+- **Turn/stream failure:** existing `agent_chat` error path (surfaces an error bubble); retain the typed/transcribed text so it isn't lost.
+- **No token / 401:** bubble stays collapsed; opening prompts the normal owner-token flow.
+
+## Testing
+
+- **Headless UI:** bubble renders; orb → open (anchored) → drag → collapse (bottom bar) → close (✕); each avatar variant renders; mobile width = near-full panel.
+- **Settings:** changing avatar/accent/persona/voiceMode persists (`app_settings`) and re-applies on reload.
+- **API:** `GET /api/dross` returns a global conversation; `POST /api/dross/turn` streams; `POST /api/voice/transcribe` returns `{text}` for a sample WAV (mock the whisper service in the unit test; one live smoke test against CT 102).
+- **Persona:** a turn reflects the configured system prompt.
+
+## Build phases
+
+- **P1 — Floating bubble + global Dross + settings.** New `dross_bubble.js` + `dross_avatar.js`, `dross.js` route (global conversation), Settings → Dross section (avatar/accent/persona/voice-mode). Retire the right-rail companion. *No voice yet.* Ship-able on its own.
+- **P2 — Voice (review-and-send).** faster-whisper on CT 102, `voice.js` transcribe proxy, record UI + waveform, transcript → input → review → send.
+- **P3 — Later.** Voice mode 2 (hands-free auto-send), then mode 3 (interpret transcript into a confirmable action via the existing Little Blue action framework).
+
+## Documentation
+
+Per the standing rule, ship docs to the Void wiki + Gitea (`Hynes/Void-Homelab`) with each phase; spec + plan under `docs/superpowers/`. Mockup at `docs/mockups/dross-chat.html`.