# 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 (Claude in Chrome) - Dev server runs on `http://localhost:5173` throughout the loop - Use browser tools (`tabs_context_mcp`, `navigate`, `computer` screenshot) to verify visual output - App has boot→ECG→login→PMR sequence (~15s on first load). Wait 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