diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index 8672239..f340049 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -8,7 +8,10 @@
"Bash(start \"\" \"C:\\\\Users\\\\Andy\\\\Ralph Local\\\\Tasks\\\\cv-4-vitals-monitor\\\\4-vitals-monitor.html\")",
"Bash(npx skills find:*)",
"WebSearch",
- "Bash(ls \"C:\\\\Users\\\\Andy\\\\Ralph Local\\\\Tasks\\\\New CV website\\\\designs\"\" 2>nul || echo \"Directory does not exist \")"
+ "Bash(ls \"C:\\\\Users\\\\Andy\\\\Ralph Local\\\\Tasks\\\\New CV website\\\\designs\"\" 2>nul || echo \"Directory does not exist \")",
+ "Bash(npm run typecheck:*)",
+ "Bash(npm run dev:*)",
+ "Bash(npm run build:*)"
]
}
}
diff --git a/.ralph/ralph-loop.state.json b/.ralph/ralph-loop.state.json
index 2c5bb28..305b368 100644
--- a/.ralph/ralph-loop.state.json
+++ b/.ralph/ralph-loop.state.json
@@ -1,6 +1,6 @@
{
"active": true,
- "iteration": 1,
+ "iteration": 2,
"minIterations": 1,
"maxIterations": 0,
"completionPromise": "COMPLETE",
diff --git a/CLAUDE.md b/CLAUDE.md
index 36e61e2..08de8b3 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -4,7 +4,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
-Interactive CV/portfolio website for Andy Charlwood with a distinctive three-phase loading experience: terminal boot sequence → ECG canvas animation → main content. Built as a React SPA with TypeScript and Vite.
+Interactive CV/portfolio for Andy Charlwood, presented as a premium clinical information system. The concept: *what if a GP surgery's patient record system were redesigned by a luxury product studio?* The structure and metaphor of a real clinical system (patient banner, sidebar navigation, record sections) — but elevated with refined typography, considered motion, and atmospheric depth.
+
+**This is NOT a faithful NHS system clone.** It's a showcase portfolio that *evokes* the feel of clinical software while being distinctly beautiful. The clinical metaphor is the creative conceit; the execution should feel premium and elegant.
+
+Built as a React SPA with TypeScript and Vite.
## Commands
@@ -18,57 +22,135 @@ No test framework is configured.
## Architecture
-### Three-Phase UI Flow
+### Four-Phase UI Flow
-`App.tsx` manages a `Phase` state (`'boot'` → `'ecg'` → `'content'`). Each phase renders exclusively:
+`App.tsx` manages a `Phase` state (`'boot'` → `'ecg'` → `'login'` → `'pmr'`). Each phase renders exclusively:
-1. **BootSequence** — Terminal typing animation (~4s), green-on-black aesthetic
-2. **ECGAnimation** — Canvas-based heartbeat animation (~5-6s) with letter tracing, background transitions from black to white
-3. **Content** — FloatingNav + all CV sections (Hero, Skills, Experience, Education, Projects, Contact, Footer)
-
-Total boot-to-content time must be ≤10 seconds.
+1. **BootSequence** — Terminal typing animation (~4s), green-on-black aesthetic. Fira Code font, matrix-green palette. **Locked — do not change.**
+2. **ECGAnimation** — Canvas-based heartbeat animation with mask-based letter tracing. Background transitions from black to `#1E293B`. **Locked — do not change.**
+3. **LoginScreen** — Animated login card on dark background. Auto-types credentials, transitions to PMR. This phase onward is open to design evolution.
+4. **PMRInterface** — The main portfolio experience: patient banner + clinical sidebar + scrollable content views.
### Key Patterns
-- **Scroll reveals**: `useScrollReveal` hook wraps IntersectionObserver with trigger-once semantics. Used by every content section. Never use scroll event listeners.
-- **Active nav tracking**: `useActiveSection` hook tracks which section is in viewport for FloatingNav highlighting.
-- **Staggered animations**: Components use index-based delays (`baseDelay + index * 100`) with Framer Motion.
-- **SVG skill circles**: `Skills.tsx` uses `strokeDashoffset = circumference * (1 - level / 100)` with `-90deg` rotation to start from 12 o'clock.
-- **Canvas ECG**: `ECGAnimation.tsx` does imperative canvas drawing with requestAnimationFrame — flatline → 3 heartbeats (40px→60px→100px) → letter tracing → exit.
+- **Canvas ECG**: `ECGAnimation.tsx` does imperative canvas drawing with requestAnimationFrame — flatline → 3 heartbeats (40px→60px→100px) → mask-based letter tracing → exit.
+- **Clinical sidebar navigation**: `ClinicalSidebar.tsx` provides hash-routed view switching with keyboard shortcuts (Alt+1-7, arrow keys, "/" for search).
+- **Patient banner condensation**: `PatientBanner.tsx` uses IntersectionObserver via `useScrollCondensation` hook — full banner (80px) condenses to 48px on scroll.
+- **Staggered entrance animations**: Framer Motion variants with sequenced delays (banner → sidebar → content).
+- **View switching**: Instant — no crossfade or slide between views. Content fades in once on initial load only.
+- **Expandable rows**: Consultation entries, medication rows, and problem entries expand in-place with height animation.
+- **Responsive breakpoints**: Desktop (full sidebar + banner), Tablet (icon-only sidebar), Mobile (bottom nav bar).
### Path Aliases
`@/` maps to `./src/` (configured in both `vite.config.ts` and `tsconfig.json`).
-### Styling
-
-Tailwind CSS with custom design tokens in `tailwind.config.js`:
-- **Colors**: teal `#00897B` (primary), coral `#FF6B6B` (accent), ECG palette (green/cyan/dim)
-- **Fonts**: Plus Jakarta Sans (primary), Inter Tight (secondary), Fira Code (mono/terminal)
-- **Breakpoints**: xs 480px, sm 640px, md 768px, lg 1024px, xl 1280px
-- Inline styles only for dynamic values that Tailwind can't express (e.g., computed `strokeDashoffset`).
-
### Type System
-All data types live in `src/types/index.ts`. Strict TypeScript — no `any` types. One component per file with typed props interfaces.
+All data types live in `src/types/index.ts` and `src/types/pmr.ts`. Strict TypeScript — no `any` types. One component per file with typed props interfaces.
+
+## Design Direction: Clinical Luxury
+
+The aesthetic direction is **"Clinical Luxury"** — the precision and information density of a medical records system, married to the refinement of high-end product design. Think Bloomberg Terminal redesigned by a Swiss design house.
+
+### Tone
+
+- **Precise, not cold.** Every element has a reason. Spacing is generous but intentional.
+- **Structured, not rigid.** The grid and hierarchy of clinical software, but with room to breathe.
+- **Technical, not sterile.** Monospace data, status indicators, and coded entries create authentic texture.
+- **Elegant, not decorative.** No gratuitous ornament. Beauty comes from proportion, contrast, and type.
+
+### Typography
+
+Typography is the primary vehicle for premium feel. Avoid generic system fonts.
+
+- **UI / Body**: Use a distinctive geometric or humanist sans-serif with character — **not** Inter, Roboto, or system defaults. Choose something with personality that still reads cleanly at small sizes (11-14px range). Candidates: Satoshi, General Sans, Outfit, DM Sans, or similar. The chosen font should feel "designed" rather than "default."
+- **Monospace / Data**: Geist Mono for timestamps, coded entries, registration numbers, and tabular data. This creates the "technical texture" that sells the clinical metaphor.
+- **Terminal phase**: Fira Code — locked, do not change.
+- **Type scale**: Keep it tight. Clinical systems use small text. Headings 15-18px, body 13-14px, labels 11-12px. Precision over drama.
+- **Weight hierarchy**: Use weight (400/500/600/700) rather than size to establish hierarchy. Bold section headers, medium labels, regular body.
+
+### Color Palette
+
+The palette anchors on NHS Blue as the institutional accent, with a predominantly dark sidebar + light content split that creates natural drama.
+
+- **NHS Blue `#005EB8`** — The single strong accent color. Used for active states, links, buttons, interactive elements. This IS the brand color of the clinical metaphor.
+- **Dark sidebar `#1E293B`** — Creates gravitas. The "serious software" feel comes from this dark chrome.
+- **Patient banner `#334155`** — Slightly lighter than sidebar. The information-dense header bar.
+- **Content background** — Not flat gray. Consider a very subtle warm tint, or a faint noise/grain texture overlay on `#F5F7FA` to add depth. The content area should feel like paper, not a spreadsheet.
+- **Cards `#FFFFFF`** — Clean white with refined shadows (layered, not single-value). Cards should feel like they float slightly above the content surface.
+- **Status colors**: Green `#22C55E`, Amber `#F59E0B`, Red `#EF4444` — used sparingly for traffic-light indicators. Always paired with text labels, never as sole signifier.
+- **Text**: Primary `#111827`, Secondary `#6B7280`, Muted `#94A3B8`. Use the full range for hierarchy.
+
+### Shadows & Depth
+
+Real clinical software is flat and border-heavy. This project should use shadows to create subtle layered depth:
+
+- **Cards**: Multi-layered shadow — e.g., `0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.03)`. Gentle, not Material Design dramatic.
+- **Sidebar**: Optional very subtle inner shadow or glow at the right edge where it meets content.
+- **Patient banner**: Subtle drop shadow below to separate from content.
+- **Hover states**: Cards may lift very slightly on hover (1-2px translate + shadow deepen). Keep it restrained.
+
+### Motion
+
+Motion should feel considered and premium, never flashy:
+
+- **Entrance animations**: The PMR interface materializes in sequence — banner slides down → sidebar slides from left → content fades in. Quick (200-300ms) with easing.
+- **Login transition**: Card scales slightly and fades. Background carries over to PMR (both are `#1E293B`-derived).
+- **View switching**: Instant, no transition between views. This preserves the "software application" feel.
+- **Expandable content**: Height-only animation, 200ms ease-out. Content grows/shrinks — no opacity fade.
+- **Hover states**: Subtle, immediate. Background color shifts, not transforms. Think: OS-level responsiveness.
+- **Clinical alert**: Spring animation for entrance (Framer Motion `type: "spring"`). Dismiss: icon crossfade → height collapse.
+- **`prefers-reduced-motion`**: All animations skip to final state. No exceptions.
+
+### Spatial Composition
+
+- **Generous but structured.** More whitespace than a real clinical system. Cards have 16-24px padding. Sections breathe.
+- **Clear visual hierarchy.** Section headers (uppercase, small, tracked-out) → content. No ambiguity about what's a label vs. data.
+- **Two-column summary grid** on desktop, single column on mobile. Cards span full width or half width — no orphan columns.
+- **Tables** use proper `
` markup with styled headers on a light gray background. Alternating row colors. This is where the clinical authenticity lives.
+
+### What Makes It Memorable
+
+The distinctiveness comes from the *contrast between structure and polish*:
+- A dark, serious sidebar next to warm, airy content
+- Small, precise monospace data in a field of generous whitespace
+- NHS blue punching through an otherwise muted palette
+- The clinical metaphor itself — "Patient Record" for a CV is unexpected and charming
+- The boot sequence → ECG → login flow is theatrical in a way that real clinical software never is
+
+## Styling
+
+Tailwind CSS with custom design tokens in `tailwind.config.js`:
+- **Color tokens**: All PMR-prefixed tokens in Tailwind config (`pmr-sidebar`, `pmr-banner`, `pmr-nhsblue`, etc.)
+- **Fonts**: Configured as `font-inter`, `font-geist` (monospace) in Tailwind — these need updating when the primary UI font changes.
+- **Breakpoints**: xs 480px, sm 640px, md 768px, lg 1024px, xl 1280px
+- **Border radius**: 4px default for cards/inputs (clinical precision). 12px exception for login card only.
+- Inline styles only for dynamic values that Tailwind can't express.
+- CSS custom properties in `index.css` for both boot/ECG phase tokens and PMR phase tokens.
## Guardrails
-- Boot sequence text and colors must match `References/concept.html` exactly (CLINICAL TERMINAL v3.2.1 format).
-- ECG animation timing/amplitudes/color transitions must match the concept reference.
-- CV content sourced from `References/CV_v4.md` — roles, dates, and achievement numbers must be accurate.
-- Icons via `lucide-react`, not unicode symbols.
+- **Boot sequence**: Text, colors, and timing must match `References/concept.html` exactly. **Do not modify.**
+- **ECG animation**: Timing, amplitudes, color transitions, and mask-based text reveal must match the concept reference. **Do not modify.**
+- **CV content**: Sourced from `References/CV_v4.md` — roles, dates, and achievement numbers must be accurate.
+- **Icons**: Via `lucide-react`, not unicode symbols.
+- **Accessibility**: WCAG 2.1 AA compliance. Semantic HTML, ARIA attributes, keyboard navigation, `prefers-reduced-motion` support throughout.
+- **No generic aesthetics**: Every design decision should feel intentional. If a component could appear in any random SaaS template, it needs more character.
## Project Structure
```
src/
-├── components/ # One component per file (PascalCase)
-├── hooks/ # Custom hooks (camelCase, use* prefix)
-├── lib/ # Utility functions
-├── types/ # TypeScript interfaces
-├── App.tsx # Phase manager (root component)
-└── index.css # Global styles + Tailwind directives
-Ralph/ # Implementation plan, guardrails, progress tracking
-References/ # Source content (concept.html, CV_v4.md, ECGVideo/)
+├── components/ # One component per file (PascalCase)
+│ └── views/ # PMR content views (SummaryView, ConsultationsView, etc.)
+├── contexts/ # React contexts (AccessibilityContext)
+├── data/ # Static data files (patient, consultations, medications, etc.)
+├── hooks/ # Custom hooks (camelCase, use* prefix)
+├── lib/ # Utility functions
+├── types/ # TypeScript interfaces (index.ts, pmr.ts)
+├── App.tsx # Phase manager (root component)
+└── index.css # Global styles + Tailwind directives
+Ralph/ # Implementation plan, guardrails, progress tracking
+References/ # Source content (concept.html, CV_v4.md, ECGVideo/)
```
diff --git a/Ralph/IMPLEMENTATION_PLAN.md b/Ralph/IMPLEMENTATION_PLAN.md
index cc4ff22..c7c96c6 100644
--- a/Ralph/IMPLEMENTATION_PLAN.md
+++ b/Ralph/IMPLEMENTATION_PLAN.md
@@ -26,7 +26,7 @@ Each task below references a specific file in `Ralph/refs/` — read ONLY that f
- [x] **Task 1b: Rebuild boot sequence and ECG animation.** Read `Ralph/refs/ref-boot-ecg.md` and `Ralph/refs/ref-design-system.md`. Also read `ECGCombined.tsx` in the project root for the Remotion reference implementation of the mask-based text reveal. This task covers the full pre-login animation flow: (a) **Refactor BootSequence.tsx** — replace hardcoded HTML strings with a clean config-driven structure. Each line type (header, field, separator, module, ready) maps to a React component. Keep the same visual output: green-on-black terminal, Fira Code font, 220ms staggered line reveals, `#00ff41` bright green / `#3a6b45` dim green / `#00e5ff` cyan labels. (b) **Cursor → dot transition** — the blinking green cursor at the end of boot must smoothly morph into the ECG's glowing trace dot. Capture the cursor's screen position and pass it to ECGAnimation as a `startPosition` prop. The cursor stops blinking, transitions from block to circular glow (~300ms), then begins moving rightward as the ECG trace dot. (c) **ECG start sync** — ECGAnimation must start its trace from the cursor position (not the far left edge). The first beat begins after a flat gap from the cursor position. Shift the world-space origin so the trace starts where the cursor was. (d) **Mask-based text reveal** — adopt ECGCombined.tsx's technique where pre-rendered stroke-only text is revealed by a wipe mask following the trace head (instead of the current alpha fade approach). Keep the current character spacing (`LETTER_W`, `LETTER_G`, `SPACE_W`) and heartbeat waveform. Add connector lines between letters at baseline. (e) **Keep**: heartbeat shape, beat timing (0.3→0.55→0.85→1.0 amplitude), canvas rendering, viewport scrolling, flatline draw, scanlines, vignette, background transition to `#1E293B`. (f) Respect `prefers-reduced-motion` — with reduced motion, skip animation and show static final frame or jump to login.
-- [ ] **Task 2: Rebuild LoginScreen component.** Read `Ralph/refs/ref-transition-login.md` and `Ralph/refs/ref-design-system.md`. Rebuild `src/components/LoginScreen.tsx` to match the login sequence specification exactly: (a) Dark blue-gray `#1E293B` background. (b) White card: 320px wide, **12px border-radius** (exception to the 4px rule — login cards can be rounder), subtle shadow. (c) NHS-blue shield icon at top with "CareerRecord PMR" branding text. (d) Username field types `A.CHARLWOOD` at 30ms/char in **Geist Mono** font. (e) Password field fills 8 dots at 20ms/dot. (f) Blinking cursor (530ms interval) in active field. (g) "Log In" button: NHS blue `#005EB8`, full width, pressed state darkens to `#004494`. (h) After submit: card scales to 103% and fades out over 200ms. (i) Respect `prefers-reduced-motion`. The login must feel like actually logging into NHS software at 8am on a Monday.
+- [x] **Task 2: Rebuild LoginScreen component.** Read `Ralph/refs/ref-transition-login.md` and `Ralph/refs/ref-design-system.md`. Rebuild `src/components/LoginScreen.tsx` to match the login sequence specification exactly: (a) Dark blue-gray `#1E293B` background. (b) White card: 320px wide, **12px border-radius** (exception to the 4px rule — login cards can be rounder), subtle shadow. (c) NHS-blue shield icon at top with "CareerRecord PMR" branding text. (d) Username field types `A.CHARLWOOD` at 30ms/char in **Geist Mono** font. (e) Password field fills 8 dots at 20ms/dot. (f) Blinking cursor (530ms interval) in active field. (g) "Log In" button: NHS blue `#005EB8`, full width, pressed state darkens to `#004494`. (h) After submit: card scales to 103% and fades out over 200ms. (i) Respect `prefers-reduced-motion`. The login must feel like actually logging into NHS software at 8am on a Monday.
- [ ] **Task 3: Rebuild PatientBanner component.** Read `Ralph/refs/ref-banner-sidebar.md` and `Ralph/refs/ref-design-system.md`. Rebuild `src/components/PatientBanner.tsx` to match the specification exactly: (a) Full banner 80px: background `#334155`, bottom border `1px solid #475569`. Name in Inter 600 **20px** (not 18px), details in Inter 400 14px. Layout must match the ASCII art in the ref file — surname-first format "CHARLWOOD, Andrew (Mr)", DOB/NHS No/Address on second row, phone/email/buttons on third row. (b) Status: green dot + "Active" text. Badge: "Open to opportunities" as blue pill. (c) Action buttons: outlined rectangles with NHS blue text and 1px border, 4px radius. Hover fills with NHS blue bg + white text. (d) Condensed banner 48px: single line with name, NHS number, status, action buttons only. Triggers at 100px scroll via IntersectionObserver. Smooth 200ms height transition. (e) Mobile banner: minimal top bar `CHARLWOOD, A (Mr) | 2211810 | dot` with overflow "..." menu. NHS Number tooltip: "GPhC Registration Number".
diff --git a/Ralph/refs/ref-design-system.md b/Ralph/refs/ref-design-system.md
index ada248e..dcc5eee 100644
--- a/Ralph/refs/ref-design-system.md
+++ b/Ralph/refs/ref-design-system.md
@@ -1,87 +1,163 @@
# Reference: Visual Design System
-> Extracted from goal.md — Visual System section. This is the SINGLE SOURCE OF TRUTH for colors, typography, spacing, borders, and motion throughout the Clinical Record PMR.
+> The SINGLE SOURCE OF TRUTH for colors, typography, spacing, surfaces, and motion throughout the Clinical Record PMR. Aligned with the **Clinical Luxury** direction defined in CLAUDE.md.
+
+---
+
+## Design Philosophy
+
+This is a **premium portfolio** that uses the structure and metaphor of a GP clinical system — not a faithful NHS software clone. Real clinical systems (EMIS Web, SystmOne) are dense, border-heavy, and purely functional. We keep their *structure* (patient banner, sidebar navigation, record sections, tables, status indicators) but elevate the *execution* with refined typography, atmospheric depth, and considered whitespace.
+
+The goal is contrast: clinical precision married to luxury refinement. The "wow" comes from recognizing the clinical metaphor while being surprised by how good it looks.
---
## Color Palette
-This design is **light-mode only**. Clinical record systems operate in light mode — high ambient lighting in consulting rooms demands white backgrounds and dark text. A dark mode would break the metaphor.
+**Light-mode only.** The metaphor demands it — clinical systems operate under bright consulting room lights. No dark mode.
**Backgrounds:**
-- Main content area: `#F5F7FA` (cool light gray — the content background of EMIS/SystmOne)
-- Card/panel surfaces: `#FFFFFF` (white)
-- Sidebar: `#1E293B` (dark blue-gray — EMIS-style dark navigation panel)
-- Patient banner: `#334155` (lighter blue-gray with white text)
-- Login screen background: `#1E293B` (same as sidebar — institutional dark blue-gray)
+- Main content area: `#F5F7FA` — cool light gray base. Add atmospheric depth: a very faint noise/grain texture overlay, or a subtle warm tint, so it feels like quality paper rather than a flat spreadsheet. The content surface should have *presence*.
+- Card/panel surfaces: `#FFFFFF` — clean white. Cards float above the content surface via layered shadows (see Surfaces section).
+- Sidebar: `#1E293B` — dark blue-gray. The gravitas anchor. This dark chrome is what makes it feel like "serious software."
+- Patient banner: `#334155` — lighter blue-gray with white text. Subtle drop shadow below to separate from content.
+- Login screen background: `#1E293B` — same as sidebar. Carries through to PMR entrance seamlessly.
**Text:**
-- Primary text: `#111827` (gray-900 — near-black for maximum readability)
-- Secondary text: `#6B7280` (gray-500)
-- On dark surfaces: `#FFFFFF` (white) and `#94A3B8` (slate-400 for secondary)
+- Primary: `#111827` (gray-900) — near-black for maximum readability
+- Secondary: `#6B7280` (gray-500) — labels, metadata, supporting text
+- Muted: `#94A3B8` (slate-400) — timestamps, tertiary info
+- On dark surfaces: `#FFFFFF` (white primary), `#94A3B8` (slate-400 secondary)
**Accent and status colors:**
-- NHS blue: `#005EB8` — primary interactive color. Used for buttons, active nav states, links, column headers. This is the actual NHS brand blue and will be instantly recognized.
-- Green: `#22C55E` — active/resolved/current states. "Active" status dots, resolved problems, current role indicators.
-- Amber: `#F59E0B` — alerts, in-progress items, notable achievements. The clinical alert banner uses this as its background.
-- Red: `#EF4444` — urgent/critical markers. Used sparingly — only for genuinely important items (e.g., a "priority" flag on the referral form).
-- Gray: `#6B7280` — inactive/historical items. Past roles that are no longer current, historical "medications."
+- **NHS Blue `#005EB8`** — THE accent color. Buttons, active nav states, links, interactive elements. This is the actual NHS brand blue — it will be instantly recognized and is the strongest signal of the clinical metaphor. Use it confidently but not everywhere.
+- Green `#22C55E` — active/resolved/current states. Status dots, current role indicators.
+- Amber `#F59E0B` — alerts, in-progress items. The clinical alert banner background.
+- Red `#EF4444` — urgent/critical. Used very sparingly — only genuinely important items.
+- Gray `#6B7280` — inactive/historical items.
**Traffic light system (used throughout):**
-- Green circle: Active / Resolved / Current
-- Amber circle: In progress / Alert / Notable
-- Red circle: Urgent / Critical (rare)
-- Gray circle: Inactive / Historical
+- Green dot: Active / Resolved / Current
+- Amber dot: In progress / Alert / Notable
+- Red dot: Urgent / Critical (rare)
+- Gray dot: Inactive / Historical
+- **Always paired with text labels.** Color is never the sole signifier (WCAG compliance).
+
+---
## Typography
-Clinical systems use system fonts — Inter or Segoe UI for general text, monospace for coded entries and data values. No decorative fonts, no variable tracking. Functional typography optimized for scanning dense tables.
+Typography is the primary vehicle for the premium feel. The font choice must feel *designed* — intentional and distinctive — while still reading cleanly at small clinical-system sizes (11-14px).
-- **Patient banner name:** Inter 600, 20px (not huge — clinical systems don't emphasize the patient name with large type)
-- **Patient banner details:** Inter 400, 14px
-- **Sidebar navigation labels:** Inter 500, 14px, white
-- **Section headings (within main area):** Inter 600, 18px
-- **Consultation entry titles:** Inter 600, 16px
-- **Body text / descriptions:** Inter 400, 14px, line-height 1.6
-- **Table headers:** Inter 600, 13px, uppercase, letter-spacing 0.03em, gray-500
-- **Table data cells:** Inter 400, 14px
-- **Coded entries / data values:** Geist Mono 400, 13px
-- **Clinical codes (SNOMED-style):** Geist Mono 400, 12px, gray-400
-- **Timestamps:** Geist Mono 400, 12px
-- **Alert banner text:** Inter 500, 14px
+**Font selection:**
+- **UI / Body font**: Choose a distinctive geometric or humanist sans-serif with character. **Do not use** Inter, Roboto, Arial, or system-ui defaults — these read as generic/AI-generated. Candidates: **Satoshi**, **General Sans**, **Outfit**, **DM Sans**, or similar. The chosen font should have personality at 13px. Whichever is selected, configure it as the primary `font-family` across all UI elements.
+- **Monospace / Data font**: **Geist Mono** — for timestamps, coded entries, registration numbers, NHS numbers, tabular data values. This monospace texture is what sells the "clinical software" feel. Falls back to Fira Code.
+- **Terminal phase**: **Fira Code** — locked, do not change.
+
+**Type scale (tight, clinical):**
+- Patient banner name: [UI font] 600, 20px
+- Patient banner details: [UI font] 400, 14px
+- Sidebar navigation labels: [UI font] 500, 14px, white
+- Section headings (main area): [UI font] 600, 15-18px
+- Consultation entry titles: [UI font] 600, 15-16px
+- Body text / descriptions: [UI font] 400, 13-14px, line-height 1.6
+- Table headers: [UI font] 600, 12-13px, uppercase, letter-spacing 0.03-0.05em
+- Table data cells: [UI font] 400, 13-14px
+- Labels / metadata: [UI font] 500, 11-12px
+- Coded entries / data values: Geist Mono 400, 12-13px
+- Clinical codes (SNOMED-style): Geist Mono 400, 11-12px, gray-400
+- Timestamps: Geist Mono 400, 11-12px
+- Alert banner text: [UI font] 500, 14px
+
+**Hierarchy through weight, not size.** Use 400/500/600/700 weight variations within a narrow size range. Bold section headers, medium labels, regular body. This keeps the clinical density while creating clear scannable hierarchy.
+
+---
## Spacing and Layout
+More generous than real clinical software. The clinical metaphor provides structure; the extra breathing room provides luxury.
+
- **Sidebar width:** 220px (fixed, desktop). Collapses to 56px (icon-only) on tablet.
- **Patient banner height:** 80px (full), 48px (condensed/sticky)
-- **Main content max-width:** No max-width — clinical systems fill available space. Content flows within the area between sidebar and viewport edge.
-- **Main content padding:** 24px
-- **Card padding:** 16px (clinical systems are more compact than marketing sites)
-- **Border radius:** 4px for cards and inputs (clinical systems use minimal rounding — 4px, not 12px or 16px)
+- **Main content max-width:** None — fills available space between sidebar and viewport edge.
+- **Main content padding:** 24px (desktop), 16px (mobile)
+- **Card padding:** 16-24px — more generous than real clinical systems. Content should breathe inside cards.
+- **Border radius:** 4px default for cards, inputs, buttons (clinical precision). 12px exception for the login card only.
- **Table row height:** 40px
- **Section spacing:** 24px between content blocks
-- **Base unit:** 4px — tighter spacing than typical, reflecting clinical system density
+- **Base unit:** 4px grid — but use it with more generosity than a real clinical system would
-## Borders and Surfaces
+---
-Borders are the dominant visual structuring element. Clinical systems do not rely on shadows or negative space — they use explicit borders to delineate every element.
+## Surfaces & Depth
-- **All cards:** `1px solid #E5E7EB` (gray-200) border, `4px` border-radius, no shadow (or at most `0 1px 2px rgba(0,0,0,0.03)`)
-- **Table cells:** `1px solid #E5E7EB` borders (all sides)
-- **Sidebar border:** `1px solid #334155` (subtle right border in a slightly lighter shade)
-- **Patient banner border:** `1px solid #475569` bottom border
-- **Input fields:** `1px solid #D1D5DB` border, `4px` radius, `#FFFFFF` background, `8px 12px` padding
-- **Active/selected rows:** `#EFF6FF` background (very subtle blue tint) — this is how EMIS highlights the selected row
+This is where we diverge most from real clinical software. Real systems are flat and border-heavy. This project uses **shadows and layering** to create premium depth — while keeping borders where they're authentically clinical (tables, input fields).
+
+**Cards:**
+- Border: `1px solid #E5E7EB` (keep the clinical border — it's authentic)
+- Shadow: Multi-layered — `0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.03)`. Gentle float, not Material Design dramatic.
+- Border-radius: `4px`
+- Hover: Cards may lift very slightly — 1-2px translateY + shadow deepens to `0 2px 4px rgba(0,0,0,0.06), 0 8px 16px rgba(0,0,0,0.04)`. Restrained, not bouncy.
+- Card headers: Light gray `#F9FAFB` background with `1px solid #E5E7EB` bottom border. Uppercase title in [UI font] 600, 12-13px. This is the most "clinical" element — keep it precise.
+
+**Tables:**
+- Full `
` markup with styled headers — this is where clinical authenticity lives.
+- Table headers: `#F9FAFB` background, `1px solid #E5E7EB` borders.
+- Alternating rows: `#FFFFFF` / `#F9FAFB` — subtle but scannable.
+- Row hover: `#EFF6FF` background (blue tint).
+- Cell borders: `1px solid #E5E7EB` — keep full borders on tables. This is authentic.
+
+**Sidebar:**
+- Background: `#1E293B`
+- Right edge: `1px solid #334155` + optional very subtle glow/shadow where it meets the content area.
+- The sidebar should feel solid and authoritative against the lighter content.
+
+**Patient banner:**
+- Background: `#334155`
+- Bottom: Subtle drop shadow `0 2px 8px rgba(0,0,0,0.12)` to separate from content below.
+- Bottom border: `1px solid #475569`
+
+**Input fields:**
+- Border: `1px solid #D1D5DB`, `4px` radius, `#FFFFFF` background, `8px 12px` padding
+- Focus: NHS blue border + `box-shadow: 0 0 0 3px rgba(0,94,184,0.15)` — refined focus ring.
+
+---
## Motion
-Clinical systems are fast and functional. Animations are minimal and purposeful — no spring physics, no bouncy transitions. Everything is immediate or uses simple ease-out.
+Motion should feel **considered and premium** — never flashy, never gratuitous. Every animation has a purpose: to orient the user, to reward interaction, or to create a moment of polish.
-- **Navigation switches:** Instant content swap. No crossfade, no slide. When you click a sidebar item, the main content area replaces immediately — just like clicking a tab in EMIS.
-- **Consultation expand/collapse:** Height animation, 200ms, `ease-out`. No opacity fade — the content simply grows/shrinks.
-- **Alert banner entrance:** Slide down from top, 250ms, with a subtle spring overshoot (this is the one exception — alerts are meant to demand attention).
-- **Alert acknowledge:** The alert shrinks in height to zero (200ms) with a small green checkmark that flashes briefly.
-- **Hover states:** Background-color transitions, 100ms. No transforms, no lifts. Just color.
-- **Login typing:** Character-by-character reveal using `setInterval` (30ms per character for username, 20ms per dot for password).
-- **Patient banner scroll condensation:** Smooth height transition (200ms) from full (80px) to condensed (48px) as user scrolls past the first 100px of content.
-- **`prefers-reduced-motion`:** Typing animation completes instantly (full text appears), alert slides are replaced with fade-in, expand/collapse is instant.
+**PMR entrance sequence (login → PMR transition):**
+- Patient banner slides down: 200ms, ease-out
+- Sidebar slides from left: 250ms, ease-out, 50ms delay
+- Content fades in: 300ms, 100ms delay after sidebar
+- This staggered materialization is the single most impactful animation moment.
+
+**Navigation switches:** Instant content swap. No crossfade, no slide. This preserves the "software application" feel — clinical systems switch tabs instantly.
+
+**Expandable content:** Height-only animation, 200ms, `ease-out`. Content grows/shrinks — no opacity fade.
+
+**Clinical alert entrance:** Spring animation (Framer Motion `type: "spring"`, moderate damping). This is the one element that *demands attention* — the spring overshoot is earned here.
+
+**Alert acknowledge:** Warning icon cross-fades to green checkmark (200ms) → hold 200ms → alert height collapses (200ms ease-out).
+
+**Hover states:** Subtle and immediate. Background-color transitions at 100ms. Card lifts are 1-2px max with shadow deepening. Think: OS-level responsiveness, not playful bouncing.
+
+**Login typing:** Character-by-character reveal: 30ms/char for username, 20ms/dot for password. Cursor blink at 530ms.
+
+**Patient banner condensation:** Smooth height transition (200ms) from 80px → 48px as user scrolls past 100px. Buttery smooth, no jank.
+
+**`prefers-reduced-motion`:** All animations skip to final state instantly. Typing completes immediately. Alert appears without slide. Expand/collapse is instant. No exceptions.
+
+---
+
+## What Makes This Design Distinctive
+
+The memorability comes from **contrasts**:
+- Dark, serious sidebar next to warm, airy content
+- Small, precise monospace data in generous whitespace fields
+- NHS blue punching through an otherwise muted, restrained palette
+- Clinical structure (tables, status dots, coded entries) executed with luxury refinement (shadows, spacing, typography)
+- The boot → ECG → login theatrical sequence, then suddenly: a premium application
+
+If any component could be dropped into a generic SaaS dashboard without looking out of place, it needs more character.
diff --git a/Screenshot 2026-02-12 001926.png b/Screenshot 2026-02-12 001926.png
new file mode 100644
index 0000000..2cc959a
Binary files /dev/null and b/Screenshot 2026-02-12 001926.png differ
diff --git a/src/App.tsx b/src/App.tsx
index 9165614..30c73b9 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,4 +1,4 @@
-import { useState, useCallback } from 'react'
+import { useState, useRef } from 'react'
import type { Phase } from './types'
import { BootSequence } from './components/BootSequence'
import { ECGAnimation } from './components/ECGAnimation'
@@ -8,43 +8,27 @@ import { AccessibilityProvider } from './contexts/AccessibilityContext'
function App() {
const [phase, setPhase] = useState('boot')
- const [cursorPosition, setCursorPosition] = useState<{ x: number; y: number } | null>(null)
-
- const handleBootComplete = useCallback(() => {
- setPhase('ecg')
- }, [])
-
- const handleCursorPositionReady = useCallback((position: { x: number; y: number }) => {
- setCursorPosition(position)
- }, [])
-
- const handleECGComplete = useCallback(() => {
- setPhase('login')
- }, [])
-
- const handleLoginComplete = useCallback(() => {
- setPhase('pmr')
- }, [])
+ const cursorPositionRef = useRef<{ x: number; y: number } | null>(null)
return (