# Progress Log

## Codebase Patterns

### Project Structure
- Components in `src/components/`, views in `src/components/views/`
- Data files in `src/data/` — consultations.ts, medications.ts, problems.ts, investigations.ts, documents.ts, patient.ts
- Types in `src/types/pmr.ts` (PMR interfaces) and `src/types/index.ts` (Phase type)
- Hooks in `src/hooks/` — useScrollCondensation.ts, useBreakpoint.ts
- Contexts in `src/contexts/` — AccessibilityContext.tsx
- Path alias: `@/` maps to `./src/`

### Phase Management
- App.tsx controls phase: 'boot' -> 'ecg' -> 'login' -> 'pmr'
- BootSequence.tsx handles terminal animation
- ECGAnimation.tsx handles heartbeat + letter tracing + flatline exit
- LoginScreen.tsx bridges to PMRInterface.tsx

### Data Architecture (CORRECT — do not modify)
- All data files are populated with accurate CV content from References/CV_v4.md
- 5 consultation entries (roles), 18 medications (skills), 11 problems (achievements), 6 investigations (projects), 5 documents (education)
- Types are properly defined in pmr.ts — Consultation, Medication, Problem, Investigation, Document, Patient, ViewId

### Design System Requirements (from ref-design-system.md)
- Light-mode ONLY — no dark mode
- NHS blue: #005EB8 (primary interactive)
- Border radius: 4px for cards/inputs
- Borders: 1px solid #E5E7EB on tables and cards, combined with multi-layered shadows for depth
- Card shadows: 0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.03)
- Table row height: 40px, card padding: 16-24px, main content padding: 24px
- Fonts: [UI font] (Elvaro Grotesque or Blumir from Fonts/ dir), Geist Mono (coded entries, timestamps, data values)
- Base spacing unit: 4px — generous but structured, more whitespace than real clinical systems

### Known Dependencies
- React 18.3.1, TypeScript, Vite
- Tailwind CSS for utility classes
- Framer Motion 11.15.0 for animations
- Lucide React 0.468.0 for icons
- fuse.js will need to be installed for Task 12

### Sidebar Label Convention (IMPORTANT)
- Sidebar uses CV-friendly labels, NOT clinical jargon
- Summary (same), Experience (not Consultations), Skills (not Medications), Achievements (not Problems), Projects (not Investigations), Education (not Documents), Contact (not Referrals)
- The clinical metaphor is in the VIEW LAYOUT, not the navigation labels
- Each view should look like its clinical equivalent but the nav label tells the user what CV section they're looking at

### Visual Review (Playwright MCP)
- Dev server runs on `http://localhost:5173` throughout the loop
- Use Playwright MCP tools (`mcp__playwright__browser_navigate`, `mcp__playwright__browser_take_screenshot`, `mcp__playwright__browser_snapshot`) to verify visual output
- App has boot→ECG→login→PMR sequence (~15s on first load). Use `mcp__playwright__browser_wait_for` with `time: 15` before screenshotting.
- Once in PMR phase, navigate views via hash routes: `#summary`, `#experience`, `#skills`, `#achievements`, `#projects`, `#education`, `#contact`
- If browser tools fail, skip visual review and note in iteration log — don't block progress

### Critical Styling Notes
- Design direction is **Clinical Luxury** — clinical structure, premium execution
- Premium UI font loaded from Fonts/ directory (Elvaro Grotesque or Blumir, NOT Inter/Roboto)
- Geist Mono for coded entries and timestamps (NOT Fira Code)
- Multi-layered shadows on cards — NOT flat/borderless
- Clinical alert uses spring animation (Framer Motion type: "spring"), not ease-out
- View switching INSTANT — no crossfade, no slide between views
- Login typing: 80ms/char username, 60ms/dot password. Button is USER-INTERACTIVE (not auto-triggered)
- Consultation expand/collapse: height animation ONLY, no opacity fade on content

## Iteration Log

### Iteration 1 — Task 1: Design system foundation and font setup
**Completed:** Task 1
**Changes made:**
- Added Geist Mono font to Google Fonts import in index.html (replacing reliance on Fira Code for PMR components)
- Extended Tailwind config PMR color tokens: added card, text-primary, text-secondary, text-on-dark variants, border colors, selected-row, alert colors
- Fixed borderRadius.card from 16px to 4px (clinical system requirement)
- Added borderRadius.login: 12px (exception for login card per spec)
- Added boxShadow.pmr: minimal clinical shadow
- Added PMR-specific CSS custom properties in index.css (--pmr-* variables)
- Added utility classes: .pmr-theme, .font-inter, .font-geist-mono

**Codebase patterns discovered:**
- The project uses both legacy design tokens (for boot/ECG phases) AND new PMR tokens (for clinical interface) — both need to coexist
- Geist Mono is the correct font for coded entries, timestamps, and clinical codes in PMR (NOT Fira Code)
- Border radius convention: 4px default for clinical components, 12px for login card only, 16px for legacy components
- CSS custom properties namespaced with --pmr- prefix for clarity

**Quality checks:** All passed (typecheck, lint, build)
**Visual review:** N/A (configuration task, no visual component)

**Issues encountered:** None

**Design decisions:**
- Kept legacy tokens in place to avoid breaking boot/ECG components
- Used --pmr- namespace for all PMR tokens to distinguish from legacy design system
- Extended Tailwind colors rather than replacing them — allows both themes to work simultaneously

### IMPORTANT — Design Guidance is Pre-Baked
Do NOT invoke the `/frontend-design` skill at runtime — it was pre-run and the output is embedded in each ref file under "Design Guidance (from /frontend-design)". Previous iterations STALLED because the skill output consumed the entire context window. The guidance is now in the ref files — just read and implement.

### ECG Reference Implementation
`ECGCombined.tsx` in the project root is a Remotion version of the ECG animation with a superior mask-based text reveal technique. Task 1b references this for the canvas implementation.

### Iteration 2 — Task 1b: Rebuild boot sequence and ECG animation
**Completed:** Task 1b
**Changes made:**
- **BootSequence.tsx**: Completely refactored from hardcoded HTML strings to config-driven architecture
  - Created type-safe `BootConfig`, `BootLine`, `BootLineType` interfaces
  - Individual line components: `BootLineHeader`, `BootLineStatus`, `BootLineSeparator`, `BootLineField`, `BootLineModule`, `BootLineReady`
  - Added CRT scanlines overlay during boot phase
  - Cursor now captures its screen position via ref and passes to parent via `onCursorPositionReady` callback
  - Cursor morph animation: block cursor scales down to 0 width over 300ms before ECG starts
  - Reduced motion support: instant boot completion, skips to ECG immediately
  
- **ECGAnimation.tsx**: Rebuilt with mask-based text reveal technique from ECGCombined.tsx
  - Added `startPosition` prop to receive cursor position from BootSequence
  - ECG trace now starts from cursor position (with `startOffsetX`) instead of x=0
  - Implemented offscreen canvas pre-rendering for text stroke
  - Mask-based text reveal: clipping region follows trace head, revealing pre-rendered text
  - Added connector lines between letters at baseline using `CONNECTOR_PROFILES`
  - Letter profiles define connector insets for natural-looking baseline connections
  - Multi-layer neon glow: outer (6px, 25% opacity), inner (2px solid)
  - Flatline draw phase extends to right edge after text completion
  - Background transitions from black to `#1E293B` (login background)
  - Reduced motion support: instant transition to PMR phase
  
- **App.tsx**: Updated to pass cursor position between BootSequence and ECGAnimation
  - Added `cursorPosition` state
  - `handleCursorPositionReady` captures position from BootSequence
  - Passed to ECGAnimation as `startPosition` prop

**Codebase patterns discovered:**
- Canvas animation performance: pre-render text to offscreen canvas, then drawImage through clip region
- Cursor-to-dot transition requires DOM ref position capture, not just CSS animation
- World-space coordinates (headWX) vs screen-space coordinates (headSX) separation is critical
- Viewport scrolling logic: offset calculated as `headWX - headSX` keeps trace visible
- Connector profiles per character (C, O, D, L, E have special insets) make letter connections look natural
- Background color transition handled via CSS transition on container, not canvas fill

**Quality checks:** All passed (typecheck, lint, build)
- TypeScript: No errors
- ESLint: 1 pre-existing warning in AccessibilityContext.tsx (not our changes)
- Build: Successful, 388KB bundle

**Visual review:** N/A (animation component — visual verification would require browser screenshot)

**Issues encountered:** None

**Design decisions:**
- Kept Fira Code for terminal/boot phase (it's the authentic clinical terminal aesthetic)
- Used ECGCombined.tsx's mask technique but adapted for canvas API (not SVG like the Remotion version)
- Beat amplitudes: 0.3 → 0.55 → 0.85 → 1.0 (same as original implementation)
- Letter spacing: LETTER_W 72px, LETTER_G 10px, SPACE_W 30px (matches original, tighter than ECGCombined)
- Morph animation uses Framer Motion scaleX/width/opacity for smooth cursor-to-dot transition

**Next task:** Task 2 — Set up premium font

## Manual Intervention — 2026-02-12
### Reason: Design direction changed from "Clinical Utilitarian" to "Clinical Luxury"
### Changes made:
- Rewrote IMPLEMENTATION_PLAN.md — leaner format, tasks point to ref files for detail
- Rewrote guardrails.md — updated shadow rules, font rules, login pacing, luxury direction
- Updated all Ralph/refs/*.md files to align with Clinical Luxury direction
- Updated CLAUDE.md with new login typing spec (80ms/char, user-interactive button)
- Updated ref-design-system.md login typing speed
### Tasks reset: Task 2 (LoginScreen — needs new typing speed + interactive button + premium font)
### Tasks added: New Task 2 (font setup) inserted before LoginScreen rebuild (now Task 3)
### Context for next iteration:
- The design direction is "Clinical Luxury" — clinical STRUCTURE, premium EXECUTION
- All ref files now say "Clinical Luxury" not "Clinical Utilitarian" or "faithful reproduction"
- Cards get multi-layered shadows (not flat/borderless)
- Premium font from Fonts/ directory replaces Inter (see CLAUDE.md Typography section)
- Login screen typing is slower (80ms/char, 60ms/dot) and the button is USER-CLICKED
- Sidebar labels are CV-friendly (Experience, Skills, etc.) — clinical metaphor is in the LAYOUT
### New guardrails added:
- Shadow guardrail updated: multi-layered shadows required (was: "no shadows")
- Font guardrail added: use [UI font] from Fonts/, not Inter/Roboto
- Login guardrail added: 80ms/char typing, user-interactive button

### Iteration 3 — Task 2: Set up premium font and update Tailwind config
**Completed:** Task 2
**Changes made:**
- Added @font-face declarations in src/index.css for both premium font candidates:
  - Elvaro Grotesque: 7 weights (Light 300 → Black 900) loaded from WOFF2/WOFF files in Fonts/ directory
  - Blumir: Variable font (100-700 weight range) loaded from WOFF2/WOFF files
- Updated CSS variables: --font-ui (Elvaro Grotesque), --font-ui-alt (Blumir)
- Removed --font-inter, replaced with --font-ui in CSS variables
- Updated Tailwind config fontFamily:
  - Added font-ui: ['Elvaro Grotesque', 'system-ui', 'sans-serif']
  - Added font-ui-alt: ['Blumir', 'system-ui', 'sans-serif']
  - Removed font-inter references
  - Kept font-geist for monospace data (Geist Mono)
  - Kept font-mono for boot/ECG phases (Fira Code)
- Enhanced Tailwind boxShadow tokens for Clinical Luxury:
  - pmr: '0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.03)' (multi-layered card shadow)
  - pmr-hover: '0 2px 4px rgba(0,0,0,0.06), 0 8px 16px rgba(0,0,0,0.04)' (hover lift)
  - pmr-banner: '0 2px 8px rgba(0,0,0,0.12)' (patient banner drop shadow)
- Updated utility class .pmr-theme to use var(--font-ui) instead of var(--font-inter)
- Added .font-ui and .font-ui-alt utility classes
- Fixed ESLint errors in ECGAnimation.tsx (viewOff and headSX should be const, not let)

**Codebase patterns discovered:**
- Font loading strategy: Load both candidate fonts now, switch between them by changing CSS variable only
- Elvaro Grotesque chosen as primary (institutional credibility, slightly condensed, data-dense UI)
- Blumir available as alternative (more refined/luxurious) via font-ui-alt
- Font files are correctly bundled by Vite (visible in build output: 14 Elvaro files + 2 Blumir files)
- Shadow system: pmr (default card), pmr-hover (interactive lift), pmr-banner (separation) — layered depth
- font-display: swap ensures text remains visible during font load

**Quality checks:** All passed
- TypeScript: No errors
- ESLint: 1 pre-existing warning in AccessibilityContext.tsx (not our changes)
- Build: Successful, 391KB bundle (includes all font files)

**Visual review:** N/A (configuration task, no visual component — fonts will be visible in Task 3 LoginScreen onwards)

**Issues encountered:** None

**Design decisions:**
- Started with Elvaro Grotesque as primary (per task instructions: "start with Elvaro, can be swapped later")
- Loaded all 7 weights for Elvaro (300/400/500/600/700/800/900) for full weight hierarchy control
- Loaded Blumir as variable font (single file supports 100-700 range) for efficiency
- Used font-display: swap for optimal loading behavior (text visible immediately, swaps to custom font when loaded)
- Enhanced shadow tokens in Tailwind config (was only single pmr shadow, now includes hover and banner variants)

**Next task:** Task 3 — Rebuild LoginScreen with new typing speed, user-interactive button, and premium font

### Iteration 4 — Task 3: Rebuild LoginScreen
**Completed:** Task 3
**Changes made:**
- Rebuilt LoginScreen.tsx with all spec requirements:
  - Typing speed changed from 30ms/char → 80ms/char for username, 20ms/dot → 60ms/dot for password
  - Pause between username and password changed from 150ms → 300ms
  - Start delay changed from 200ms → 400ms (more breathing room for card entrance)
  - Login button is now user-interactive — disabled/dimmed (opacity 0.6) while typing, fully interactive after typing completes
  - Added `typingComplete` state to track when typing is done
  - Added `buttonHovered` state for hover visual feedback (#004D9F on hover)
  - Button `onClick` handler only fires when `typingComplete` is true
  - Reduced motion: typing completes instantly, button immediately available for user click (NOT auto-triggered)
  - `activeField` state expanded to include `'done'` value for post-typing state
- Font references updated from `'Inter'` → `var(--font-ui)` (Elvaro Grotesque) throughout
- Card shadow upgraded from single-layer `0 1px 2px rgba(0,0,0,0.03)` → multi-layered `0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.03)` per design system
- Timer cleanup improved: all `setTimeout` calls tracked via `timeoutRefs` ref array and cleared on unmount
- Fixed ESLint warning about `timeoutRefs.current` in cleanup by capturing to local variable

**Codebase patterns discovered:**
- `addTimeout` helper pattern: wraps `setTimeout` and tracks IDs in a ref array for cleanup — useful for components with multiple sequential timeouts
- Button hover state should use a slightly darker shade than the base (#004D9F between #005EB8 and #004494 pressed)
- `var(--font-ui)` is the correct way to reference the premium font in inline styles (not the font name directly)

**Quality checks:** All passed
- TypeScript: No errors
- ESLint: 1 pre-existing warning in AccessibilityContext.tsx (not our changes)
- Build: Successful, 392KB bundle

**Visual review:** Skipped — browser tools (Claude in Chrome) not available in this environment

**Issues encountered:** None

**Design decisions:**
- Kept the Geist Mono + Fira Code monospace fallback chain for input fields (authentic clinical terminal feel)
- Button hover color (#004D9F) is a midpoint between NHS blue (#005EB8) and pressed (#004494) — subtle but noticeable
- 400ms start delay gives the card entrance animation time to complete before typing begins
- Button uses `disabled` attribute plus visual opacity change for clear affordance

**Next task:** Task 4 — Rebuild PatientBanner

## Iteration 4 (Task 4: Rebuild PatientBanner)

**Task completed:** Task 4 — Rebuild PatientBanner with premium fonts, tooltip, animations

**Changes made:**
- Rebuilt `src/components/PatientBanner.tsx`:
  - Replaced all `font-inter` references with `font-ui` (Elvaro Grotesque)
  - Added `NHSNumberWithTooltip` component — custom styled tooltip with Framer Motion, 300ms hover delay, arrow indicator (replaces native `title` attribute)
  - Added `AnimatePresence` for smooth crossfade between full/condensed banner states
  - Mobile overflow menu now uses `AnimatePresence` for animated enter/exit
  - Replaced fixed backdrop click handler with `useRef` + `useCallback` + document event listener pattern (click-outside)
  - Mobile banner now uses `patient.nhsNumber` and `patient.status` data instead of hardcoded values
  - DOB value rendered in `font-geist` (monospace) for clinical data texture
  - Badge changed from `rounded-sm` to `rounded-full` (pill shape)
  - Added `shadow-pmr-banner` drop shadow to header
  - Added focus ring styles on action buttons (`focus:ring-2 focus:ring-pmr-nhsblue/40`)
  - `prefers-reduced-motion` support for banner crossfade
- Added `SkipButton` component to `src/App.tsx` — appears after 1.5s during boot/ECG phases, skips to login

**Quality checks:** All passed (typecheck, lint, build — 394.81 KB bundle)

**Visual review:** Completed via Playwright MCP. Banner renders correctly with premium font, NHS blue action buttons, shadow, monospace NHS number, status dot with text label.

**Known issue (pre-existing):** Banner sentinel element has `absolute top-0` positioning inside a non-positioned parent, causing the IntersectionObserver to always report "not intersecting" — banner shows condensed state even at scroll position 0. The full 3-row banner (with DOB, address, phone, email) never displays. This is NOT a regression from Task 4 — the sentinel placement was unchanged. Should be addressed in a future task.

**Codebase patterns discovered:**
- `AnimatePresence mode="wait"` is the right pattern for crossfading between two states (full/condensed banner)
- Custom tooltip with Framer Motion + `onMouseEnter`/`onMouseLeave` with 300ms delay is more styleable than native `title`
- Click-outside pattern: `useRef` on container + `useCallback` for handler + `useEffect` to add/remove document listener

## Manual Intervention — 2025-02-12
### Reason: Replaced Claude in Chrome browser integration with Playwright MCP for visual validation
### Changes made:
- `RALPH_PROMPT.md`: Updated visual review step (step 7) to use Playwright MCP tools (`mcp__playwright__browser_navigate`, `mcp__playwright__browser_take_screenshot`, `mcp__playwright__browser_snapshot`, `mcp__playwright__browser_wait_for`) instead of Chrome extension tools (`tabs_context_mcp`, `computer` screenshot)
- `guardrails.md`: Updated visual review guardrail to reference Playwright MCP tools
- `progress.txt`: Updated "Visual Review" codebase pattern section with Playwright MCP tool names
- `ralph.ps1`: Updated comments referencing Chrome to reference Playwright MCP
### Tasks reset: None
### Tasks added: None
### Context for next iteration:
- Visual review is now done via **Playwright MCP** tools, NOT Claude in Chrome
- Key tools: `mcp__playwright__browser_navigate` (go to URL), `mcp__playwright__browser_take_screenshot` (visual capture), `mcp__playwright__browser_snapshot` (accessibility tree), `mcp__playwright__browser_wait_for` (wait for time/text)
- The dev server still runs on `http://localhost:5173` — workflow is the same, just different tool names
- Previous iterations skipped visual review because Chrome tools weren't available — Playwright MCP should now work
### New guardrails added: None

