# Handoff: Rules Map — Lightbulb Zigzag Dashboard

## About the Design Files

The files in this bundle are **design references created in HTML** — interactive prototypes showing intended look and behavior, not production code to copy directly. The task is to **recreate these designs in the SHEP Bar Prep SvelteKit codebase** using its established patterns, components, and token system.

## Fidelity

**High-fidelity.** The lightbulb rendering, glass effects, filament glow, zigzag layout, expansion panels, and interaction patterns are all final-intent. Reproduce them faithfully.

## Reference Files

| File | Purpose |
|---|---|
| `Rules Map.html` | The full interactive map — page header, session card, pannable/zoomable zigzag canvas with 8 lightbulb nodes, expansion panels, theme picker. **This is the primary reference.** |
| `bulb-test.html` | Isolated bulb test — 6 bulbs at different heat levels (0→1.0) showing the full warm-up progression. Use this to verify your bulb component matches at each heat level. |
| `colors_and_type.css` | SHEP design tokens — all colors (OKLCH), typography (5 font families), spacing, radii, shadows, motion, 6 themes × light/dark. Drop into any HTML to get the full token system. |

## Architecture Overview

The page has four vertical sections:

1. **Tab bar** — Essays / Rule Drills / Rules Map (active) / Sessions
2. **Page header** — "Eight subjects. One path." headline + 85-day countdown to July 28, 2026
3. **Today's session card** — curated 5-rule session (3 warm-up + 1 stale + 1 weak) with glass-tray pills and "Begin today's session" CTA
4. **The map** — pannable/zoomable canvas containing 8 lightbulb nodes in a vertical asymmetric zigzag, connected by dashed bezier curves

## The Lightbulb Component

Each subject is rendered as a realistic lightbulb SVG. This is the centerpiece of the design.

### Anatomy (all in one inline SVG, viewBox `20 10 120 190`)

1. **Outer halo** — `<circle>` with radialGradient, opacity scales with heat
2. **Glass envelope** — A19-shape `<path>`, fill transitions from neutral (`oklch(92% 0.003 264 / 0.35)` at heat=0) to subject-colored (`oklch({88-heat*20}% {0.005+heat*0.08} {hue} / {0.30+heat*0.30})` at heat=1)
3. **Inner glow** — same glass path, filled with radialGradient from filament center outward, opacity = heat
4. **§ filament** — three layered `<text>` elements rendering the § character in Cormorant Garamond:
   - **Bloom** (softest, widest) — subject-colored, `feGaussianBlur stdDeviation={3+heat*6}`, opacity `heat*0.7`
   - **Glow** (medium) — white fill, `feGaussianBlur stdDeviation={1.5+heat*3}`, opacity `heat*0.8`
   - **Wire** (crisp) — stroke-only (no fill), stroke color transitions from gray (`oklch(45% 0.01 264)`) to warm white (`oklch(95% 0.02 80)`), strokeWidth `0.8+heat*0.4`, opacity `0.3+heat*0.7`
5. **Lead wires** — two thin vertical `<path>` lines from base cap up to the § bottom
6. **Glass highlight** — crescent `<path>` on upper-left, filled with linearGradient (white→transparent), opacity `0.5+heat*0.2`
7. **Base cap** — minimal graphite trapezoid + two faint horizontal lines + ellipse tip
8. **Plaque** — rounded rect below the base with subject abbreviation (Cinzel 11px 700) + divider + confidence % (Geist 9px 600)

### Heat-driven properties

| Heat | Glass | Filament wire | Glow/bloom | Halo |
|---|---|---|---|---|
| 0 (untouched) | Clear neutral, faint stroke | Gray wire, 30% opacity | None | None |
| 0.15 (cold) | Very faint hue tint | Gray wire, ~40% opacity | Barely visible | Barely visible |
| 0.5 (warming) | Moderate hue + opacity | Transitioning to white | Medium glow | Soft halo |
| 0.8 (warm) | Strong subject color | Near-white, crisp | Strong bloom | Visible colored halo |
| 1.0 (hot) | Full subject color, 60% opacity | White-hot, full opacity | Maximum bloom + glow | Full saturated halo |

### Drop shadow (CSS on `.bulb-svg`)

Light themes:
```css
filter:
  drop-shadow(0 0 calc(8px + var(--heat) * 22px) oklch(from var(--subj) l c h / calc(0.04 + var(--heat) * 0.28)))
  drop-shadow(0 8px 14px rgba(40,30,20,0.10))
  drop-shadow(0 2px 4px rgba(40,30,20,0.07));
```

Dark themes — increase shadow opacity significantly (see CSS in reference file).

### SVG Filters (per-bulb, defined in `<defs>`)

Each bulb gets unique gradient/filter IDs (`glow-b-{subj.id}`, `halo-b-{subj.id}`, etc.) to avoid conflicts.

- `radialGradient` for inner glow — centered at filament (cx=80, cy=85, r=60)
- `radialGradient` for outer halo — centered at glass (cx=80, cy=80, r=90)
- `linearGradient` for glass highlight crescent
- `feGaussianBlur` filter for filament glow (stdDeviation 1.5→4.5)
- `feGaussianBlur` filter for filament bloom (stdDeviation 3→9)

## Subject Data

8 subjects in zigzag order, each with:

```ts
type Subject = {
  id: string;          // 'civpro'
  short: string;       // 'CIV' — shown on plaque
  name: string;        // 'Civil Procedure'
  token: string;       // '--subj-civpro' — CSS custom property
  weight: string;      // 'most-tested' | 'frequent' | 'tested'
  meeRank: number;     // 1..8
  topRules: string[];  // 5 rule names, ordered by MEE frequency
  totalRules: number;  // e.g. 68
  heat: number;        // 0..1 — aggregate confidence (computed from rule data)
  conf: number;        // 0..100 — percentage
  reps: number;        // total drill encounters
};
```

### Subject color palette (OKLCH)

```css
--subj-civpro:    oklch(60% 0.18 260);  /* indigo */
--subj-contracts: oklch(64% 0.16 80);   /* ochre */
--subj-evidence:  oklch(60% 0.14 200);  /* steel */
--subj-conlaw:    oklch(58% 0.17 25);   /* crimson */
--subj-crim:      oklch(50% 0.14 20);   /* maroon */
--subj-realprop:  oklch(56% 0.13 145);  /* viridian */
--subj-torts:     oklch(60% 0.18 35);   /* rust */
--subj-busassoc:  oklch(54% 0.14 50);   /* sienna */
```

## Zigzag Layout

Vertical path, 8 nodes, asymmetric horizontal positions (not a grid):

```js
const VERTICAL_LAYOUT = [
  { xPct: 28, y: 130 },   // CIV — left
  { xPct: 68, y: 320 },   // KK  — right
  { xPct: 36, y: 510 },   // EV  — left-center
  { xPct: 64, y: 700 },   // CON — right
  { xPct: 30, y: 890 },   // CR  — left
  { xPct: 70, y: 1080 },  // RP  — right
  { xPct: 36, y: 1290 },  // TT  — left-center
  { xPct: 66, y: 1490 },  // BA  — right
];
```

`xPct` is a percentage of the world width (1100px). `y` is absolute pixels in a 1720px-tall world.

### Connector path

Dashed bezier curves between bulbs. Each segment goes from below one bulb's plaque (cy+120) to above the next bulb's glass top (cy-54). The horizontal control points create smooth S-curves.

```css
.route-line {
  fill: none;
  stroke: oklch(50% 0.04 60 / 0.20);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-dasharray: 2 6;
}
```

## Pan/Zoom Canvas

The map sits in a 720px-tall viewport with a pannable/zoomable world inside.

- **Drag** background to pan
- **Scroll wheel** to zoom toward cursor
- **+/−/reset** controls (bottom-right glass tray)
- **Click a bulb** → auto-zoom to 115% and pan to center the bulb (with offset to leave room for the expansion panel)
- **Close panel** → reset zoom to fit-all
- Min scale: 0.45, max scale: 2.4
- Smooth 480ms transitions on camera (disabled during drag)

## Expansion Panel

When a bulb is clicked, a 380px-wide glass panel appears adjacent to it:

- Panel anchors to the **right** of left-half bulbs, **left** of right-half bulbs
- Has a CSS triangle pointer toward the bulb
- Glass treatment: `backdrop-filter: blur(14px) saturate(120%)`, semi-transparent bg, subject-colored border + shadow

### Panel contents

1. **Eyebrow** — "Civil Procedure · most-tested" (Cinzel, subject color)
2. **Headline** — "{conf}% confident across {totalRules} rules" (Cormorant italic)
3. **Stats row** — Reps / Touched / MEE rank
4. **Top 5 rules list** — each row: rank number, rule name, sparkline, confidence %. Clickable → expands inline to show detail (stats + body + Practice CTA)
5. **Search bar** — searches remaining rules beyond top 5 (`Search {n} more rules in {subject}…`)
6. **Actions** — "Drill {subject}" primary button (subject-colored) + "Open subject page" ghost

## Today's Session Card

Glass-tray pills (not rounded pills — 10px border-radius rectangles):

```css
.session-pill {
  padding: 10px 14px;
  border-radius: 10px;
  background: var(--glass-tray-bg);
  backdrop-filter: blur(10px) saturate(120%);
  border: 1px solid var(--glass-tray-stroke);
  box-shadow: var(--glass-tray-shadow);
  /* No gradient, no sheen — just real lift */
}
```

Each pill has: colored dot (8×8 rounded-square) + kind label (WARM-UP/STALE/WEAK in Cinzel) + rule name.

## Side Labels (per bulb)

Positioned to the side of each bulb (right for left-half bulbs, left for right-half):

- MEE rank + weight eyebrow ("01 · most-tested")
- Subject name (Cormorant 17px)
- Confidence bar (60px wide, 4px tall, filled proportionally with subject color)
- Stats ("12 reps · 68 rules")

## Theme Support

6 themes via `data-theme` + `.dark` on `<html>`:

| Theme | data-theme | dark class |
|---|---|---|
| Paper (default) | paper | no |
| Classic | (none) | no |
| Academia | academia | no |
| Sage | sage | no |
| Midnight | midnight | yes |
| Night | night | yes |

All colors use semantic tokens from `colors_and_type.css`. Subject palette colors are theme-independent. Glass tray tokens (`--glass-tray-bg`, `--glass-tray-stroke`, `--glass-tray-shadow`) have light/dark variants.

The theme picker is a fixed-position glass tray (top-right) with 6 color swatches.

## Svelte Component Structure (suggested)

```
src/lib/components/rules-map/
  RulesMap.svelte           # page-level orchestrator
  RulesMapCanvas.svelte     # pan/zoom viewport + world
  Bulb.svelte               # single lightbulb SVG (the core visual)
  BulbLabel.svelte           # side label (name, confidence bar, stats)
  ExpansionPanel.svelte     # rule list + search + drill CTA
  SessionCard.svelte        # today's session with glass-tray pills
  ZigzagPath.svelte         # SVG connector between bulbs
  types.ts                  # Subject, Rule types
```

## Acceptance Checklist

- [ ] 8 lightbulbs render in a vertical asymmetric zigzag
- [ ] Each bulb's glass tints and § filament glows proportionally to heat (0→1)
- [ ] Untouched bulbs (heat=0) are clear glass with gray wire — no color, no glow
- [ ] Hot bulbs (heat=1) have full subject-colored glass, white-hot filament, visible halo
- [ ] Dashed bezier connector path weaves between all 8 bulbs
- [ ] Canvas is pannable (drag) and zoomable (scroll wheel + controls)
- [ ] Clicking a bulb auto-zooms + pans to it, opening the expansion panel
- [ ] Expansion panel shows top-5 rules, inline rule expansion, search bar
- [ ] Session card pills are glass trays (10px radius, backdrop-filter, no gradient)
- [ ] Side labels show subject name, confidence bar, stats
- [ ] Theme picker cycles through all 6 themes — bulbs, glass, shadows all adapt
- [ ] Works in all 6 themes × light/dark without hardcoded colors (except subject palette)
- [ ] Map/Readiness toggle exists (Readiness table is a separate future component)
- [ ] Hero countdown shows days until July 28, 2026

## Out of Scope

- Readiness table view (the Map ↔ Readiness toggle exists but the table is a separate ticket)
- Empty state (new user, no rules practiced)
- Mobile responsive layout
- Rule drill flow (clicking "Practice this rule" — that's the PracticeModal component)
- Real data fetching — use mock data matching the structure in the reference
- The bulb rendering uses React JSX in the reference; port to Svelte's SVG template syntax
