diff --git a/.fallowrc.json b/.fallowrc.json
index 785ae90..58c70ac 100644
--- a/.fallowrc.json
+++ b/.fallowrc.json
@@ -9,7 +9,8 @@
"**/__snapshots__/**",
"**/turbo/generators/templates/**",
"**/*.generated.ts",
- "**/*.d.ts"
+ "**/*.d.ts",
+ "docs/product/reference/**"
],
"dynamicallyLoaded": [
"packages/**/__factories__/**",
diff --git a/.prettierignore b/.prettierignore
index d230ed6..d42c1c8 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,2 +1,5 @@
# Generated compliance artifacts — do not reformat
compliance/*.yml
+
+# Design reference material (ADR-029) — committed byte-for-byte, never reformat
+docs/product/reference/
diff --git a/docs/product/reference/README.md b/docs/product/reference/README.md
new file mode 100644
index 0000000..8f8bce6
--- /dev/null
+++ b/docs/product/reference/README.md
@@ -0,0 +1,22 @@
+# CODING AGENTS: READ THIS FIRST
+
+This is a **handoff bundle** from Claude Design (claude.ai/design).
+
+A user mocked up designs in HTML/CSS/JS using an AI design tool, then exported this bundle so a coding agent can implement the designs for real.
+
+## What you should do — IMPORTANT
+
+**Read `app-prototype-development/project/Veect.dc.html` in full.** The user had this file open when they triggered the handoff, so it's almost certainly the primary design they want built. Read it top to bottom — don't skim. Then **follow its imports**: open every file it pulls in (shared components, CSS, scripts) so you understand how the pieces fit together before you start implementing.
+
+**If anything is ambiguous, ask the user to confirm before you start implementing.** It's much cheaper to clarify scope up front than to build the wrong thing.
+
+## About the design files
+
+The design medium is **HTML/CSS/JS** — these are prototypes, not production code. Your job is to **recreate them pixel-perfectly** in whatever technology makes sense for the target codebase (React, Vue, native, whatever fits). Match the visual output; don't copy the prototype's internal structure unless it happens to fit.
+
+**Don't render these files in a browser or take screenshots unless the user asks you to.** Everything you need — dimensions, colors, layout rules — is spelled out in the source. Read the HTML and CSS directly; a screenshot won't tell you anything they don't.
+
+## Bundle contents
+
+- `app-prototype-development/README.md` — this file
+- `app-prototype-development/project/` — the `App prototype development` project files (HTML prototypes, assets, components)
diff --git a/docs/product/reference/project/.thumbnail b/docs/product/reference/project/.thumbnail
new file mode 100644
index 0000000..d9ac15f
Binary files /dev/null and b/docs/product/reference/project/.thumbnail differ
diff --git a/docs/product/reference/project/VEECT-SPEC.md b/docs/product/reference/project/VEECT-SPEC.md
new file mode 100644
index 0000000..0091698
--- /dev/null
+++ b/docs/product/reference/project/VEECT-SPEC.md
@@ -0,0 +1,251 @@
+# Veect — Product & Design Specification
+
+_v1.0 · July 2026 · covers `Veect.dc.html` (interactive prototype), `Veect Landing.dc.html`, and `veect-codebase/` (React 18 · TypeScript strict · Tailwind · shadcn-style)_
+
+---
+
+## 1. Product
+
+**Veect is a design-system-native canvas.** A designer brings their own tokens and
+components; Veect composes screens from *only* that system and exports production React
+that imports their real library. The three invariants everything hangs on:
+
+1. **One runtime** — canvas, preview, isolation and export render the same tree through
+ the same renderer. No "roughly how it'll look."
+2. **0 unregistered elements** — AI output is whitelist-sanitized against the component
+ registry; anything outside it is refused, never faked.
+3. **Deterministic code** — same tree → same code, every time. Export imports the
+ customer's library (`@solstice/ui` in the demo), never inlined clones.
+
+Demo fiction: the customer is **Solstice** (warm team-scheduling brand), with designer
+Maya (owner) and design engineer Devon (reviewer, owns the merge veto).
+
+---
+
+## 2. Brand & design language
+
+### 2.1 Logo
+Typographic wordmark: lowercase **`veect`**, Instrument Sans 600, letter-spacing −0.035em,
+line-height 1, closed by a **square ink terminal** (the "vector point") sized ≈ 0.28 × font
+size, baseline-aligned, `currentColor`. No pictorial mark. Codebase atom: `Logo size={n}`.
+
+### 2.2 Color — chrome tokens
+Color belongs to the customer's canvas; the instrument is monochrome. The accent IS the ink.
+
+| Token | Light (default file value) | Dark |
+|---|---|---|
+| `--bg` / `--panel` | `#FCFCFB` | `#0E0E0D` |
+| `--raised` / `--chip` | `#FFFFFF` / `#F4F3F0` | `#161615` |
+| `--line` / `--line2` | `#E7E6E2` / `#CFCEC8` | `#262624` / `#3A3A37` |
+| `--t1` / `--t2` / `--t3` | `#161513` / `#63615B` / `#9A978F` | `#F4F4F2` / `#A6A6A1` / `#73736E` |
+| `--iris` (accent = ink) | `#161513` | `#F4F4F2` |
+| `--iris-dim` | `rgba(22,21,19,.06)` | `rgba(244,244,242,.09)` |
+| `--btn-ink` (text on accent) | `#FFFFFF` | `#0E0E0D` |
+| `--warn` / `--err` / `--live` | `#9A7B3F` / `#B05E5E` / `#5B7F5E` | `#C2A36B` / `#C97070` / `#8BA88E` |
+
+Amber (`--warn`) is reserved for AI-scope affordances (pick outline, scope chips, draw
+strokes) and the unmapped-component warning. The prototype ships **dark by default**
+(user preference); the codebase mirrors this (`index.html data-theme="dark"`).
+
+### 2.3 Surfaces & elevation
+Flat. Radius **2px everywhere** in chrome (customer canvas keeps its own `radius-md`
+token). No floating shadows — separation is a hairline: popovers/menus use
+`box-shadow: 0 0 0 1px var(--line2)`. Canvas frames sit on the board with a 1px
+`rgba(0,0,0,.14)` ring.
+
+### 2.4 Typography
+- **UI**: Instrument Sans 400/500/600 — 13px base, 11–12.5px controls.
+- **Labels**: Fragment Mono, 9–11px, uppercase, letter-spacing .06–.14em — all section
+ headers, metadata, and "engraved" captions.
+- **Customer content**: the system's own font (demo: Bricolage Grotesque) — never used
+ for chrome.
+- Scale discipline: chrome text never below 8px (activity-bar labels), canvas display
+ type responsive per view (§6).
+
+### 2.5 Iconography
+Hand-drawn 16-grid SVG, 1.5px stroke, round caps, `currentColor`. Set: chat (spark),
+layers, library, tokens, history, inspect (sliders), polish (spark-4), code (chevrons),
+pick (crosshair), draw (pen), eye / eye-off, mic. No icon fonts, no emoji.
+
+### 2.6 Motion
+120–450ms, `cubic-bezier(.2,.7,.2,1)`. Keyframes: `riseIn`, `popIn`, `fadeIn`,
+`lineFlash` (code sync), `nodePulse` (token ripple). Everything respects
+`prefers-reduced-motion`.
+
+### 2.7 Voice
+Plainspoken, no hype, no emoji. Refusals teach ("You don't have a component for this
+yet"), fixes explain the *why* in the user's own tokens.
+
+---
+
+## 3. Screens
+
+| Screen | Purpose | Key elements |
+|---|---|---|
+| **Sign in** | design-partner entry | email → straight through; SSO stubs; provisioning note |
+| **Home** | projects + systems | project grid (1 live + placeholders), search, Solstice system card (ramp, open tokens, re-import), "bring a design system" |
+| **Onboarding** | the re-theme reveal | two paths: paste tokens (parse → contrast checks) or 5-token quick path (brand, neutrals, radius, type, spacing) with live mini-kit; ends "Bring it to life →" |
+| **Editor** | the canvas (§4–5) | |
+| **Settings** | system + governance | token source card, component mappings ("merge bar"), craft standard picker, export defaults, plan, delete |
+| **Profile** | account | identity card, theme preference, workspace members (Maya/Devon), keyboard map, sign out |
+| **System update** | US-6 re-import | paste → diff (CHANGED/ADDED/REMOVED + swatches) → impact line → apply-with-undo → ripple |
+| **Landing** | marketing (§10) | |
+
+Navigation: project dropdown (top-left) is the hub — all projects, new project, export,
+copy TSX, import tokens, upload component, settings, profile, onboarding demo. Escape and
+outside-click dismiss. Avatar → Profile everywhere.
+
+---
+
+## 4. Editor anatomy
+
+### 4.1 Top bar (36px)
+Wordmark · project dropdown · `✓ saved` · **view switcher** (centered: Desktop / Tablet /
+Mobile / ⧉ All) · Preview · **Export** (primary) · theme toggle ◐ · avatar.
+
+### 4.2 Activity bar (44px, fixed left)
+Toggleable icon+label buttons, grouped by dividers:
+`✦ Chat` │ `Layers · Library · Tokens · History` │ `Inspect · Polish` │ `Code`.
+Click = open that panel/tab; click again = collapse. Active = `--iris-dim` chip. At
+overlay widths (<1000px) panels are mutually exclusive and float over the board.
+
+### 4.3 Panels (resizable via edge-drag; « » collapse chevrons)
+- **Chat** (284px) — vertical AI thread (§7): bubbles, proposal Accept/Discard inline,
+ refusal actions, model picker (haiku/sonnet), image attachments, scope chip.
+- **Board rail** (216px) — LAYERS: collapsible tree (caret-expand, double-click reveal),
+ per-frame **variant chips** (§6); LIBRARY: 10-component base kit with mapping badges,
+ isolation ◉, add +, drag-to-canvas, patterns, custom `.tsx/.jsx` upload; TOKENS: brand
+ ramp + curated swatches + hex, radius stepper, type picker, neutrals, spacing base —
+ every change ripples live (`nodePulse`); HISTORY: labelled steps, restore cursor (§8).
+- **Inspector** (244px) — selection card, ✦ Edit-with-AI (scoped), content/variant/weight/
+ tone/size controls, gap/pad steppers (system-scale steps), Tailwind utilities
+ (collapsible; w/h/max-w/shadow/opacity selects + custom string), token bindings,
+ FRAME settings (name, width ±80, **active view** line). Polish tab: §7.3.
+- **Code** (380px, dark `#161311` always) — file segs (`{Frame}.tsx` / `tokens.css`),
+ line-numbered TSX with click-line→select-node sync and `lineFlash` on change; **edit
+ mode** (toggle → syntax-highlighted textarea → apply parses back via the generated
+ grammar, line-referenced errors); footer `prettier ✓ tsc ✓`.
+- **Isolation** (over board) — variant × state grid per component on a paper stage, real
+ runtime, Esc exits.
+
+### 4.4 Board
+Infinite surface: scroll pans, ⌘/ctrl-scroll zooms at cursor, two-finger pinch, drag
+empty board pans, background click deselects. Zoom cluster (− % + · fit · + frame ·
+board backdrop toggle). Frames render as **view clusters** (§6) with a draggable name
+tab (`Home · 34 nodes · one codebase`). Selection = ink ring; hover = neutral ring;
+AI-target = amber ring. Tool pill (bottom-center): **Pick (V) · Draw (D) · Overlays (O)
+· Voice (stub)** — icon buttons with key hints.
+
+### 4.5 Preview
+Full-screen overlay, real hover/focus states, active view's width fills the screen
+(mobile/tablet render as a centered device sheet).
+
+### 4.6 Keyboard
+`⌘K` commands · `✦` focus composer · `⌘Z/⇧⌘Z` undo/redo · `⌘0` fit · `⌘1` 100% ·
+`⌘±` zoom · `V/D/O` tools · `⌫` delete selection · `Esc` dismiss cascade (menu → modes →
+refusal → proposal → scope → selection).
+
+---
+
+## 5. Canvas node model
+
+`board → frame[] → (stack | card | heading | text | button | badge | input | avatar |
+image | divider | custom)*`. Layout via `dir/gap/pad/padX/padY/align/justify/maxW/bg`
+(4px scale). Content props per type; `tw` bag for curated utility classes. Frames carry
+`x/y/w/views/view`. IDs are stable; comment anchors and code-line mapping key off them.
+
+---
+
+## 6. Responsive variants — one tree, three widths
+
+- Views: **desktop** (frame's own `w`, default 1280) · **tablet 768** · **mobile 390**.
+- A frame renders every enabled view side by side (120px gap) — same children, so an
+ edit anywhere updates every view. Captions (`tablet · 768`) label each block; the
+ **active view** (drives preview, code, export width) is ink-highlighted.
+- Renderer responds to view width: row-stacks wrap ≤900, cards get flex-basis 240,
+ display type steps down (xl: 54 → 44 → 34px) ≤900/≤480.
+- Switcher semantics: Desktop/Tablet/Mobile = show only that view + make it active;
+ **⧉ All** = all three. Layer chips: label click = select frame + make active (opens
+ frame settings); eye icon = show/hide that view (min 1). Cluster growth shifts
+ right-neighbors by the width delta — clusters never collide.
+
+---
+
+## 7. AI, constrained
+
+### 7.1 Compose
+Prompt → system prompt embeds the registry JSON (types + allowed props) + brand voice
+rules (one primary CTA per group, 4px spacing, no lorem). Response is sanitized: unknown
+type ⇒ **refusal**, numeric props clamped to scale, strings capped. Result stages as a
+**dashed ghost** on the active frame — Accept commits (history label `AI compose —
+accepted`), Discard drops.
+
+### 7.2 Scoped edits
+Pick tool (V) or Inspector → "✦ Edit with AI": amber target ring + composer scope chip
+(`h1 · Scheduling…`), inline bubble at cursor, type-aware quick actions (punchier /
+shorter / demote / colorize ×n…). Edit proposals ghost beside the original and swap in
+on Accept.
+
+### 7.3 Polish (the Impeccable pass)
+Ranked issues (HIGH/MED/LOW): weak hierarchy (display heading at 400), competing primary
+CTAs, off-scale gaps (`gap-[18px]` drift), uneven sibling padding, AA contrast failures
+(with measured ratios). Each: title — *why it matters* — one-click fix **in the user's
+tokens**. Fix-all; empty state "Impeccable. Nothing to fix." Craft standard selectable
+in Settings (Veect default / Impeccable / custom DESIGN.md).
+
+### 7.4 Refusal protocol
+Off-registry ask (3D globe, video, charts…) ⇒ "You don't have a component for this yet"
++ reason + **Compose from primitives** / Not now. Never generic markup. Models: Haiku 4.5
+default, Sonnet 4.5 option; offline heuristics keep demos deterministic.
+
+---
+
+## 8. History
+
+Every mutation is a labelled step (`Add Button`, `Polish — Two primary actions`,
+`View → tablet`, `AI edit — accepted`…). History tab lists steps newest-first with age.
+Clicking restores that state and sets a **cursor**: newer steps dim (35%); the next
+meaningful edit truncates the dimmed trail. `⌘Z` walks steps; redo restores.
+
+---
+
+## 9. Code generation & round-trip
+
+| Canvas | Export | Notes |
+|---|---|---|
+| heading/text | `
/
` + Tailwind | size/weight/tone classes, neutral ramp |
+| button | `