diff --git a/README.md b/README.md
index f1dacea..240a083 100644
--- a/README.md
+++ b/README.md
@@ -229,12 +229,6 @@ python -m data_processing.migrate
2. A browser window will open for SSO authentication
3. Verify your network allows Snowflake connections
-## Documentation
-
-- [CLAUDE.md](CLAUDE.md) — Technical architecture documentation
-- [docs/USER_GUIDE.md](docs/USER_GUIDE.md) — End-user guide
-- [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) — Deployment guide
-
## License
Internal NHS use only. Not for distribution.
diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md
deleted file mode 100644
index 07cbc5f..0000000
--- a/docs/DEPLOYMENT.md
+++ /dev/null
@@ -1,296 +0,0 @@
-# Deployment Guide
-
-This guide covers deployment options for the Patient Pathway Analysis web application built with Dash.
-
-## Overview
-
-The application is a single-process Python Dash app that serves both the frontend and API from one server. It reads pre-computed data from a local SQLite database.
-
-## Development Mode
-
-For local development:
-
-```bash
-# Start development server with hot reload
-python run_dash.py
-
-# Access the application at http://localhost:8050
-```
-
-## Production Deployment Options
-
-### Option 1: Simple Production (Single Server)
-
-The simplest approach for internal deployments:
-
-```bash
-# Run with Gunicorn (Linux/macOS)
-gunicorn dash_app.app:server -b 0.0.0.0:8050 --workers 4
-
-# Or directly with Python
-python run_dash.py
-```
-
-For background execution:
-
-```bash
-# Using nohup (Linux/macOS)
-nohup gunicorn dash_app.app:server -b 0.0.0.0:8050 --workers 4 > dash.log 2>&1 &
-
-# Using PowerShell (Windows)
-Start-Process -NoNewWindow -FilePath "python" -ArgumentList "run_dash.py"
-```
-
-### Option 2: Docker Deployment
-
-Create a `Dockerfile` for containerized deployment:
-
-```dockerfile
-FROM python:3.11-slim
-
-WORKDIR /app
-
-# Install uv for fast dependency management
-RUN pip install uv
-
-# Copy dependency files
-COPY pyproject.toml uv.lock ./
-
-# Install dependencies
-RUN uv sync --no-dev
-
-# Copy application code
-COPY src/ src/
-COPY dash_app/ dash_app/
-COPY data/ data/
-COPY run_dash.py setup_dev.py ./
-
-# Set up Python path
-RUN uv run python setup_dev.py
-
-# Expose port
-EXPOSE 8050
-
-# Start the application
-CMD ["uv", "run", "gunicorn", "dash_app.app:server", "-b", "0.0.0.0:8050", "--workers", "4"]
-```
-
-Build and run:
-
-```bash
-# Build the image
-docker build -t pathway-analysis .
-
-# Run the container
-docker run -p 8050:8050 \
- -v $(pwd)/data:/app/data \
- pathway-analysis
-```
-
-### Option 3: Docker Compose
-
-```yaml
-version: '3.8'
-
-services:
- app:
- build: .
- ports:
- - "8050:8050"
- volumes:
- - ./data:/app/data
- - ./src/config:/app/src/config
- restart: unless-stopped
-```
-
-Run with:
-
-```bash
-docker-compose up -d
-```
-
-## Reverse Proxy Configuration
-
-### Nginx
-
-For production deployments behind nginx:
-
-```nginx
-server {
- listen 80;
- server_name your-server.nhs.uk;
-
- location / {
- proxy_pass http://localhost:8050;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-}
-```
-
-Enable the site:
-
-```bash
-sudo ln -s /etc/nginx/sites-available/pathway-analysis /etc/nginx/sites-enabled/
-sudo nginx -t && sudo systemctl reload nginx
-```
-
-## Process Management
-
-### Systemd (Linux)
-
-```ini
-# /etc/systemd/system/pathway-analysis.service
-[Unit]
-Description=Pathway Analysis Dash App
-After=network.target
-
-[Service]
-Type=simple
-User=www-data
-WorkingDirectory=/opt/pathway-analysis
-ExecStart=/opt/pathway-analysis/.venv/bin/gunicorn dash_app.app:server -b 0.0.0.0:8050 --workers 4
-Restart=always
-RestartSec=10
-
-[Install]
-WantedBy=multi-user.target
-```
-
-Enable and start:
-
-```bash
-sudo systemctl daemon-reload
-sudo systemctl enable pathway-analysis
-sudo systemctl start pathway-analysis
-```
-
-### Windows Service
-
-Use NSSM (Non-Sucking Service Manager) on Windows:
-
-```powershell
-# Install NSSM
-choco install nssm
-
-# Create service
-nssm install PathwayAnalysis "C:\Path\To\python.exe" "run_dash.py"
-nssm set PathwayAnalysis AppDirectory "C:\Path\To\pathway-analysis"
-nssm start PathwayAnalysis
-```
-
-## Environment Configuration
-
-### Production Environment Variables
-
-```bash
-# Database path (if using custom location)
-export PATHWAY_DB_PATH=/var/data/pathways.db
-
-# Snowflake (for data refresh only — not needed for the web app)
-export SNOWFLAKE_ACCOUNT=your-account
-export SNOWFLAKE_WAREHOUSE=your-warehouse
-```
-
-### Snowflake Configuration
-
-Snowflake is only needed for the data refresh CLI command, not for running the web application. Ensure `src/config/snowflake.toml` is configured:
-
-```toml
-[snowflake]
-account = "your-production-account"
-warehouse = "ANALYTICS_WH"
-database = "DATA_HUB"
-schema = "CDM"
-authenticator = "externalbrowser"
-```
-
-## Data Refresh
-
-The web application reads pre-computed data from SQLite. To update the data:
-
-```bash
-# Full refresh (both chart types, all date filters)
-python -m cli.refresh_pathways --chart-type all
-
-# The app will serve new data immediately — no restart needed
-```
-
-Schedule this as a cron job or Windows Task Scheduler task for periodic updates.
-
-## Security Considerations
-
-### Network Security
-
-1. **Firewall Rules**: Only expose port 8050 (or 80/443 behind reverse proxy)
-2. **HTTPS**: Use TLS certificates via reverse proxy (nginx, Caddy)
-3. **VPN**: Consider restricting access to NHS network only
-
-### Data Security
-
-1. **Database Access**: The app uses read-only SQLite access
-2. **No file uploads**: The Dash app does not accept file uploads
-3. **No authentication built in**: Add authentication via reverse proxy or middleware if needed
-
-## Monitoring
-
-### Health Checks
-
-The application serves at `/` — a 200 response indicates the app is running.
-
-### Logging
-
-Dash outputs request logs to stdout. Configure log aggregation as needed:
-
-```bash
-# Redirect logs to file
-gunicorn dash_app.app:server -b 0.0.0.0:8050 --access-logfile /var/log/pathway-analysis/access.log --error-logfile /var/log/pathway-analysis/error.log
-```
-
-## Troubleshooting
-
-### Port already in use
-
-```bash
-# Find process using port 8050
-lsof -i :8050 # Linux/macOS
-netstat -ano | findstr :8050 # Windows
-```
-
-### Database not found
-
-```bash
-# Verify database exists
-ls -la data/pathways.db
-sqlite3 data/pathways.db ".tables"
-
-# Recreate if needed
-python -m data_processing.migrate
-python -m cli.refresh_pathways --chart-type all
-```
-
-### Import errors
-
-```bash
-# Ensure src/ is on Python path
-uv run python setup_dev.py
-
-# Verify imports
-uv run python -c "from dash_app.app import app; print('OK')"
-```
-
----
-
-## Quick Reference
-
-| Environment | Command | Port |
-|-------------|---------|------|
-| Development | `python run_dash.py` | 8050 |
-| Production | `gunicorn dash_app.app:server -b 0.0.0.0:8050 --workers 4` | 8050 |
-| Docker | `docker run -p 8050:8050 pathway-analysis` | 8050 |
-
-For more information, see:
-- [Dash Documentation](https://dash.plotly.com/)
-- [Gunicorn Deployment](https://docs.gunicorn.org/en/stable/deploy.html)
diff --git a/docs/DESIGN_SYSTEM.md b/docs/DESIGN_SYSTEM.md
deleted file mode 100644
index 498b751..0000000
--- a/docs/DESIGN_SYSTEM.md
+++ /dev/null
@@ -1,194 +0,0 @@
-# Design System - HCD Analysis v2.1 (SaaS Redesign)
-
-This document defines the visual design language for the UI redesign. The goal is a **modern SaaS aesthetic** - think Stripe, Linear, Vercel - while staying thematically aligned with the blue color palette.
-
-**Design Philosophy**:
-- The chart is the hero; everything else supports it
-- Minimal chrome, maximum data visibility
-- Clean, confident, spacious - not clinical or governmental
-- Every pixel of vertical space matters
-
-## Color Palette
-
-### Primary Blues (kept from original, used sparingly)
-| Name | Hex | Usage |
-|------|-----|-------|
-| Heritage Blue | `#003087` | Top bar background, strong accents |
-| Primary Blue | `#0066CC` | Interactive elements, links, focus |
-| Vibrant Blue | `#1E88E5` | Hover states, active elements |
-| Sky Blue | `#4FC3F7` | Subtle accents, progress indicators |
-| Pale Blue | `#E3F2FD` | Selected states, subtle backgrounds |
-
-### Neutrals (refined for modern feel)
-| Name | Hex | Usage |
-|------|-----|-------|
-| Slate 900 | `#0F172A` | Primary text (slightly darker) |
-| Slate 700 | `#334155` | Secondary text |
-| Slate 500 | `#64748B` | Muted text, placeholders |
-| Slate 300 | `#CBD5E1` | Borders, dividers |
-| Slate 100 | `#F8FAFC` | Backgrounds (slightly lighter) |
-| White | `#FFFFFF` | Card/modal backgrounds |
-
-### Semantic Colors
-| Name | Hex | Usage |
-|------|-----|-------|
-| Success | `#10B981` | Positive (modern green) |
-| Warning | `#F59E0B` | Caution |
-| Error | `#EF4444` | Errors |
-| Info | `#3B82F6` | Informational |
-
-## Typography
-
-**Font Family:** Inter (primary), system-ui (fallback)
-
-| Style | Size | Weight | Usage |
-|-------|------|--------|-------|
-| Display | 28px | 600 | Page titles (reduced from 32px) |
-| Heading 1 | 18px | 600 | Section headers (reduced from 24px) |
-| Heading 2 | 16px | 600 | Card titles (reduced from 20px) |
-| Heading 3 | 14px | 600 | Subsections |
-| Body | 14px | 400 | Default text |
-| Body Small | 13px | 400 | Secondary info |
-| Caption | 11px | 500 | Labels, metadata (reduced from 12px) |
-| Mono | 13px | 500 | Data values (JetBrains Mono) |
-
-## Spacing Scale (Tighter)
-
-| Token | Value | Usage |
-|-------|-------|-------|
-| xs | 4px | Tight gaps |
-| sm | 6px | Between related elements (was 8px) |
-| md | 8px | Standard gaps (was 12px) |
-| lg | 12px | Section padding (was 16px) |
-| xl | 16px | Card padding (was 24px) |
-| 2xl | 24px | Major gaps (was 32px) |
-| 3xl | 32px | Page margins (was 48px) |
-
-## Layout Specifications
-
-### Page Structure (Target)
-```
-┌─────────────────────────────────────────────────────────────────┐
-│ Logo │ Tabs │ Freshness │ 48px
-├─────────────────────────────────────────────────────────────────┤
-│ [Initiated▾] [LastSeen▾] │ [Drugs▾] [Ind▾] [Dir▾] │ KPI badges │ 48px
-├─────────────────────────────────────────────────────────────────┤
-│ │
-│ I C I C L E C H A R T │ flex
-│ (full viewport width) │
-│ │
-└─────────────────────────────────────────────────────────────────┘
-```
-
-### Top Bar
-- **Height**: 48px (reduced from 64px)
-- **Background**: Heritage Blue
-- **Logo**: 28px height (reduced from 36px)
-- **Tabs**: Small pills, 28px height
-
-### Filter Strip
-- **Height**: 48px (single row)
-- **Layout**: Horizontal flex, all filters inline
-- **Dropdown triggers**: 32px height, 8px padding
-- **No section header** - labels are in dropdown triggers
-- **Background**: Slate 100 or transparent
-
-### KPI Section (Options)
-
-**Option A: Inline badges** (preferred - zero extra height)
-```
-Filters row: [Initiated▾] [LastSeen▾] | [Drugs▾] ... | 12,345 patients • £45.2M • 89 drugs
-```
-
-**Option B: Compact strip** (48px max)
-```
-┌─────┬─────┬─────┬─────┐
-│12.3K│£45M │ 89 │ 7 │ 28px value
-│pts │cost │drugs│trust│ 14px label
-└─────┴─────┴─────┴─────┘
-```
-
-### Chart Container
-- **Width**: Full viewport minus 32px (16px padding each side)
-- **Height**: Fill remaining space (min 500px)
-- **No max-width constraint**
-- **Margins**: Minimal (t:40, l:8, r:8, b:24)
-
-## Component Specifications
-
-### Compact Dropdown Trigger
-- Height: 32px
-- Padding: 8px 12px
-- Border: 1px Slate 300
-- Border radius: 6px
-- Font: 13px
-- Chevron: 14px icon
-
-### Compact KPI Badge
-- Padding: 4px 12px
-- Border radius: 16px (pill)
-- Background: Slate 100
-- Value: 14px mono, weight 600
-- Label: 11px, Slate 500
-
-### Searchable Dropdown Panel
-- Max height: 200px (items area)
-- Item padding: 6px 8px
-- Search input height: 28px
-- Width: 240px min
-
-## Shadows
-
-| Token | Value | Usage |
-|-------|-------|-------|
-| sm | `0 1px 2px rgba(0,0,0,0.04)` | Subtle (lighter) |
-| md | `0 1px 3px rgba(0,0,0,0.06)` | Cards at rest |
-| lg | `0 4px 8px rgba(0,0,0,0.08)` | Dropdowns, hover |
-
-## Border Radius
-
-| Token | Value | Usage |
-|-------|-------|-------|
-| sm | 4px | Small elements |
-| md | 6px | Inputs, buttons |
-| lg | 8px | Cards |
-| full | 9999px | Pills, badges |
-
-## Transitions
-
-All transitions: 150ms ease-out (faster than before)
-
-## Implementation Notes
-
-### Key Changes from v2.0
-1. **Vertical space reduction**: ~210px saved (364px → ~156px overhead)
-2. **Full-width chart**: Remove PAGE_MAX_WIDTH for chart
-3. **Inline KPIs**: Either badges in filter row or minimal strip
-4. **Smaller fonts**: Headlines and captions reduced
-5. **Tighter spacing**: All spacing tokens reduced by ~25%
-
-### CSS Patterns
-```css
-/* Full-height chart container */
-.chart-container {
- height: calc(100vh - 96px); /* viewport minus top bar + filter strip */
- min-height: 500px;
- width: calc(100vw - 32px);
- margin: 0 16px;
-}
-
-/* Filter strip */
-.filter-strip {
- display: flex;
- align-items: center;
- height: 48px;
- gap: 12px;
- padding: 0 16px;
-}
-```
-
-### Dash Implementation
-- Chart container uses `dcc.Loading` wrapper around `dcc.Graph`
-- Full-width layout via CSS class `.chart-card` in `dash_app/assets/nhs.css`
-- Minimum height set via CSS: `min-height: 500px`
-- Margins controlled in `create_icicle_from_nodes()`: `t:40, l:8, r:8, b:24`
diff --git a/docs/PHASE10_DESIGN.md b/docs/PHASE10_DESIGN.md
deleted file mode 100644
index 6380019..0000000
--- a/docs/PHASE10_DESIGN.md
+++ /dev/null
@@ -1,740 +0,0 @@
-# Phase 10 Design Specification
-
-## Aesthetic Direction
-
-**Utilitarian clinical** — authoritative, data-dense, no decoration. Every element earns its screen real estate. The NHS brand palette is law. The hierarchy is:
-
-1. Header (identity + live metrics)
-2. Sub-header (global controls — always visible, always the same)
-3. Sidebar (view switching)
-4. Content (view-specific)
-
-Vertical rhythm: header 56px → sub-header 44px → content starts at 100px from top.
-
----
-
-## 1. Header Redesign
-
-### Layout
-
-```
-┌─────────────────────────────────────────────────────────────────────────┐
-│ [NHS] HCD Analysis │ 3,847 / 11,118 39 / 42 £48.2M / £130.6M │ ● 11,118 patients Updated 2h ago │
-│ BRAND │ patients drugs cost │ FRESHNESS │
-└─────────────────────────────────────────────────────────────────────────┘
-```
-
-The header stays 56px tall. The breadcrumb is REMOVED (it was redundant — the sidebar shows where you are). The middle section becomes **3 inline fraction KPIs**. The right section stays as data freshness.
-
-### HTML Structure (Dash)
-
-```python
-html.Header(className="top-header", children=[
- # Left: brand (unchanged)
- html.Div(className="top-header__brand", children=[
- html.Div("NHS", className="top-header__logo"),
- html.Div(html.Div("HCD Analysis", className="top-header__title")),
- ]),
-
- # Center: fraction KPIs
- html.Div(className="top-header__kpis", children=[
- html.Div(className="header-kpi", children=[
- html.Span("—", id="kpi-filtered-patients", className="header-kpi__num"),
- html.Span(" / ", className="header-kpi__sep"),
- html.Span("—", id="kpi-total-patients", className="header-kpi__den"),
- html.Span("patients", className="header-kpi__label"),
- ]),
- html.Div(className="header-kpi", children=[
- html.Span("—", id="kpi-filtered-drugs", className="header-kpi__num"),
- html.Span(" / ", className="header-kpi__sep"),
- html.Span("—", id="kpi-total-drugs", className="header-kpi__den"),
- html.Span("drugs", className="header-kpi__label"),
- ]),
- html.Div(className="header-kpi", children=[
- html.Span("—", id="kpi-filtered-cost", className="header-kpi__num"),
- html.Span(" / ", className="header-kpi__sep"),
- html.Span("—", id="kpi-total-cost", className="header-kpi__den"),
- html.Span("cost", className="header-kpi__label"),
- ]),
- ]),
-
- # Right: data freshness (unchanged structure, same IDs)
- html.Div(className="top-header__right", children=[
- html.Span(children=[
- html.Span(className="status-dot"),
- html.Span("...", id="header-record-count"),
- ]),
- html.Span(children=[
- "Updated: ",
- html.Span("...", id="header-last-updated"),
- ]),
- ]),
-])
-```
-
-### CSS — New Classes
-
-```css
-/* ── Header KPIs ── */
-.top-header__kpis {
- display: flex;
- align-items: center;
- gap: 24px;
-}
-.header-kpi {
- display: flex;
- align-items: baseline;
- gap: 3px;
- color: rgba(255, 255, 255, 0.6);
- font-size: 13px;
- font-weight: 400;
- white-space: nowrap;
-}
-.header-kpi__num {
- color: var(--nhs-white);
- font-size: 16px;
- font-weight: 700;
- font-variant-numeric: tabular-nums;
-}
-.header-kpi__sep {
- color: rgba(255, 255, 255, 0.35);
- font-weight: 300;
- font-size: 14px;
- margin: 0 1px;
-}
-.header-kpi__den {
- color: rgba(255, 255, 255, 0.5);
- font-size: 13px;
- font-weight: 400;
- font-variant-numeric: tabular-nums;
-}
-.header-kpi__label {
- color: rgba(255, 255, 255, 0.4);
- font-size: 11px;
- font-weight: 600;
- text-transform: uppercase;
- letter-spacing: 0.05em;
- margin-left: 4px;
-}
-```
-
-### CSS — Modified Classes
-
-Remove `.top-header__breadcrumb` usage (delete from header.py, CSS can stay for backward compat or be removed).
-
-### Callback IDs
-
-- **Outputs (filtered values from chart-data)**: `kpi-filtered-patients`, `kpi-filtered-drugs`, `kpi-filtered-cost`
-- **Outputs (total values from reference-data)**: `kpi-total-patients`, `kpi-total-drugs`, `kpi-total-cost`
-- **Existing (unchanged)**: `header-record-count`, `header-last-updated`
-
----
-
-## 2. Global Filter Sub-Header
-
-### Layout
-
-```
-┌─────────────────────────────────────────────────────────────────────────┐
-│ VIEW [By Directory] [By Indication] │ INITIATED [All years ▾] LAST SEEN [Last 6 months ▾] │
-└─────────────────────────────────────────────────────────────────────────┘
-```
-
-Sits directly below the header. Fixed position. Full width minus sidebar. Light blue-grey background (`#E8F0FE` — the same tint used for active sidebar items) with a subtle bottom border. Contains ONLY the chart type toggle and date filters — no drug/trust/directorate buttons.
-
-### HTML Structure (Dash)
-
-```python
-html.Div(className="sub-header", children=[
- # Chart type toggle
- html.Div(className="sub-header__group", children=[
- html.Span("View", className="sub-header__label"),
- html.Div(className="toggle-pills", role="radiogroup",
- **{"aria-label": "Chart view type"}, children=[
- html.Button("By Directory", id="chart-type-directory",
- className="toggle-pill toggle-pill--active",
- role="radio", n_clicks=0, **{"aria-checked": "true"}),
- html.Button("By Indication", id="chart-type-indication",
- className="toggle-pill", role="radio",
- n_clicks=0, **{"aria-checked": "false"}),
- ]),
- ]),
- # Divider
- html.Div(className="sub-header__divider"),
- # Date filters
- html.Div(className="sub-header__group", children=[
- html.Span("Initiated", className="sub-header__label"),
- dcc.Dropdown(id="filter-initiated", ...same options...,
- className="filter-dropdown"),
- ]),
- html.Div(className="sub-header__group", children=[
- html.Span("Last seen", className="sub-header__label"),
- dcc.Dropdown(id="filter-last-seen", ...same options...,
- className="filter-dropdown"),
- ]),
-])
-```
-
-### CSS — New Classes
-
-```css
-/* ── Global Filter Sub-Header ── */
-.sub-header {
- position: fixed;
- top: 56px; /* below main header */
- left: var(--sidebar-w); /* right of sidebar */
- right: 0;
- z-index: 150;
- height: 44px;
- background: #E8F0FE;
- border-bottom: 1px solid #C5D4E8;
- display: flex;
- align-items: center;
- padding: 0 24px;
- gap: 16px;
-}
-.sub-header__group {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-.sub-header__label {
- font-size: 11px;
- font-weight: 700;
- text-transform: uppercase;
- letter-spacing: 0.06em;
- color: var(--nhs-dark-blue);
- white-space: nowrap;
- opacity: 0.6;
-}
-.sub-header__divider {
- width: 1px;
- height: 24px;
- background: rgba(0, 48, 135, 0.15);
-}
-```
-
-### CSS — Modified Classes
-
-`.main` top margin increases from 56px to 100px (56px header + 44px sub-header):
-
-```css
-.main {
- margin-left: var(--sidebar-w);
- margin-top: 100px; /* was 56px */
- padding: 24px;
- min-height: calc(100vh - 100px); /* was 56px */
- display: flex; flex-direction: column; gap: 20px;
-}
-```
-
-`.sidebar` top position increases to 56px (stays below main header, sub-header floats over content area):
-
-Actually, the sidebar should start below the header (56px), and the sub-header should start at the right of the sidebar. The sidebar extends from 56px to bottom. The sub-header is only in the content area.
-
-```
-┌──────────────────────────────────────────────────┐
-│ HEADER (56px) │
-├────────┬─────────────────────────────────────────┤
-│ │ SUB-HEADER (44px) │
-│ SIDE ├─────────────────────────────────────────┤
-│ BAR │ │
-│ (240) │ CONTENT AREA │
-│ │ │
-└────────┴─────────────────────────────────────────┘
-```
-
----
-
-## 3. Trust Comparison Landing Page
-
-### Layout
-
-A clean selector grid. Each button is a card-like element showing the directorate/indication name. Arranged in a responsive grid — 3 columns for ~14 directorates, 4 columns for ~32 indications.
-
-```
-┌─────────────────────────────────────────────────────┐
-│ Trust Comparison │
-│ Select a directorate to compare drug usage across │
-│ trusts. │
-│ │
-│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
-│ │CARDIOLOGY│ │DERMATOL- │ │DIABETIC │ │
-│ │ │ │OGY │ │MEDICINE │ │
-│ │ 847 pts │ │ 423 pts │ │ 312 pts │ │
-│ │ 12 drugs│ │ 8 drugs│ │ 6 drugs│ │
-│ └──────────┘ └──────────┘ └──────────┘ │
-│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
-│ │GASTRO- │ │CLINICAL │ │MEDICAL │ │
-│ │ENTEROLOGY│ │HAEMATOL..│ │ONCOLOGY │ │
-│ │ 298 pts │ │ 567 pts │ │ 234 pts │ │
-│ │ 11 drugs│ │ 15 drugs│ │ 9 drugs│ │
-│ └──────────┘ └──────────┘ └──────────┘ │
-│ ... │
-└─────────────────────────────────────────────────────┘
-```
-
-Each card shows: directorate name (bold), patient count, drug count. Sorted by patient count descending. The blue left border on hover provides the NHS accent.
-
-### HTML Structure (Dash)
-
-```python
-html.Div(className="tc-landing", id="trust-comparison-landing", children=[
- # Header
- html.Div(className="tc-landing__header", children=[
- html.H2("Trust Comparison", className="tc-landing__title"),
- html.P(
- "Select a directorate to compare drug usage across trusts.",
- className="tc-landing__desc",
- id="tc-landing-desc",
- ),
- ]),
- # Grid of directorate cards
- html.Div(className="tc-landing__grid", id="tc-landing-grid", children=[
- # Populated by callback — one per directorate/indication
- # Each card:
- html.Button(
- className="tc-card",
- id={"type": "tc-selector", "index": "CARDIOLOGY"},
- n_clicks=0,
- children=[
- html.Div("CARDIOLOGY", className="tc-card__name"),
- html.Div(className="tc-card__stats", children=[
- html.Span("847 patients", className="tc-card__stat"),
- html.Span("·", className="tc-card__dot"),
- html.Span("12 drugs", className="tc-card__stat"),
- ]),
- ],
- ),
- # ... more cards
- ]),
-])
-```
-
-### CSS — New Classes
-
-```css
-/* ── Trust Comparison Landing ── */
-.tc-landing {
- display: flex;
- flex-direction: column;
- gap: 24px;
-}
-.tc-landing__header {
- padding: 0 0 8px;
-}
-.tc-landing__title {
- font-size: 22px;
- font-weight: 700;
- color: var(--nhs-dark-blue);
- margin-bottom: 4px;
-}
-.tc-landing__desc {
- font-size: 14px;
- color: var(--nhs-mid-grey);
- font-weight: 400;
-}
-.tc-landing__grid {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 12px;
-}
-
-/* Directorate selector cards */
-.tc-card {
- display: flex;
- flex-direction: column;
- gap: 8px;
- padding: 16px 20px;
- background: var(--nhs-white);
- border: 1px solid var(--nhs-pale-grey);
- border-left: 4px solid transparent;
- cursor: pointer;
- text-align: left;
- font-family: inherit;
- transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
-}
-.tc-card:hover {
- border-left-color: var(--nhs-blue);
- background: #FAFCFF;
- box-shadow: 0 1px 4px rgba(0, 48, 135, 0.08);
-}
-.tc-card:focus-visible {
- box-shadow: 0 0 0 3px var(--nhs-yellow);
- z-index: 1;
-}
-.tc-card__name {
- font-size: 14px;
- font-weight: 700;
- color: var(--nhs-dark-blue);
- line-height: 1.3;
-}
-.tc-card__stats {
- display: flex;
- align-items: center;
- gap: 6px;
- font-size: 12px;
- color: var(--nhs-mid-grey);
-}
-.tc-card__stat {
- font-weight: 400;
- font-variant-numeric: tabular-nums;
-}
-.tc-card__dot {
- color: var(--nhs-pale-grey);
-}
-```
-
-For indication mode (~32 buttons), switch to 4 columns:
-
-```css
-/* Use this class when chart_type == "indication" */
-.tc-landing__grid--wide {
- grid-template-columns: repeat(4, 1fr);
-}
-```
-
----
-
-## 4. Trust Comparison 6-Chart Dashboard
-
-### Layout
-
-2-column × 3-row grid of chart cards. Each card has a small title and a `dcc.Graph`. A sticky top bar shows the selected directorate name + back button.
-
-```
-┌─────────────────────────────────────────────────────┐
-│ ← Back RHEUMATOLOGY — Trust Comparison │
-├────────────────────────┬────────────────────────────┤
-│ Market Share │ Cost Waterfall │
-│ ┌──────────────────┐ │ ┌──────────────────────┐ │
-│ │ dcc.Graph │ │ │ dcc.Graph │ │
-│ └──────────────────┘ │ └──────────────────────┘ │
-├────────────────────────┼────────────────────────────┤
-│ Dosing Intervals │ Drug × Trust Heatmap │
-│ ┌──────────────────┐ │ ┌──────────────────────┐ │
-│ │ dcc.Graph │ │ │ dcc.Graph │ │
-│ └──────────────────┘ │ └──────────────────────┘ │
-├────────────────────────┼────────────────────────────┤
-│ Treatment Duration │ Cost Effectiveness │
-│ ┌──────────────────┐ │ ┌──────────────────────┐ │
-│ │ dcc.Graph │ │ │ dcc.Graph │ │
-│ └──────────────────┘ │ └──────────────────────┘ │
-└────────────────────────┴────────────────────────────┘
-```
-
-### HTML Structure (Dash)
-
-```python
-html.Div(className="tc-dashboard", id="trust-comparison-dashboard", children=[
- # Dashboard header with back button
- html.Div(className="tc-dashboard__header", children=[
- html.Button("← Back", id="tc-back-btn", className="tc-dashboard__back",
- n_clicks=0),
- html.H2(id="tc-dashboard-title", className="tc-dashboard__title",
- children="RHEUMATOLOGY — Trust Comparison"),
- ]),
- # 6-chart grid
- html.Div(className="tc-dashboard__grid", children=[
- _tc_chart_cell("Market Share", "tc-chart-market-share"),
- _tc_chart_cell("Cost Waterfall", "tc-chart-cost-waterfall"),
- _tc_chart_cell("Dosing Intervals", "tc-chart-dosing"),
- _tc_chart_cell("Drug × Trust Heatmap", "tc-chart-heatmap"),
- _tc_chart_cell("Treatment Duration", "tc-chart-duration"),
- _tc_chart_cell("Cost Effectiveness", "tc-chart-cost-effectiveness"),
- ]),
-])
-```
-
-Helper for each chart cell:
-```python
-def _tc_chart_cell(title, graph_id):
- return html.Div(className="tc-chart-cell", children=[
- html.Div(title, className="tc-chart-cell__title"),
- dcc.Loading(type="circle", color="#005EB8", children=[
- dcc.Graph(
- id=graph_id,
- config={"displayModeBar": False, "displaylogo": False},
- style={"height": "320px"},
- ),
- ]),
- ])
-```
-
-### CSS — New Classes
-
-```css
-/* ── Trust Comparison Dashboard ── */
-.tc-dashboard {
- display: flex;
- flex-direction: column;
- gap: 16px;
-}
-.tc-dashboard__header {
- display: flex;
- align-items: center;
- gap: 16px;
-}
-.tc-dashboard__back {
- padding: 6px 12px;
- font-size: 14px;
- font-weight: 600;
- font-family: inherit;
- color: var(--nhs-blue);
- background: var(--nhs-white);
- border: 1px solid var(--nhs-pale-grey);
- cursor: pointer;
- transition: background 0.15s;
- white-space: nowrap;
-}
-.tc-dashboard__back:hover {
- background: #E8F0FE;
-}
-.tc-dashboard__back:focus-visible {
- box-shadow: 0 0 0 3px var(--nhs-yellow);
-}
-.tc-dashboard__title {
- font-size: 20px;
- font-weight: 700;
- color: var(--nhs-dark-blue);
-}
-
-/* 2×3 chart grid */
-.tc-dashboard__grid {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 16px;
-}
-
-/* Individual chart cell */
-.tc-chart-cell {
- background: var(--nhs-white);
- border: 1px solid var(--nhs-pale-grey);
- display: flex;
- flex-direction: column;
-}
-.tc-chart-cell__title {
- padding: 10px 16px;
- font-size: 13px;
- font-weight: 700;
- color: var(--nhs-dark-blue);
- text-transform: uppercase;
- letter-spacing: 0.04em;
- border-bottom: 1px solid var(--nhs-pale-grey);
-}
-```
-
----
-
-## 5. Patient Pathways Filter Placement
-
-### Approach
-
-The drug/trust/directorate filter buttons sit in a **secondary filter strip** directly below the global sub-header. This strip is ONLY rendered when `active_view == "patient-pathways"`. It's a slimmer, lighter bar that reads as "view-specific controls" vs the sub-header's "global controls."
-
-```
-┌──────────────────────────────────────────────────────┐ ← HEADER (always)
-├────────────────────────────────────────────────────── │ ← SUB-HEADER (always)
-├──────────────────────────────────────────────────────┤
-│ Drugs (3) Trusts (2) Directorates │ Clear All │ ← PATHWAY FILTERS (Patient Pathways only)
-├──────────────────────────────────────────────────────┤
-│ │
-│ [chart card with tabs + graph] │
-│ │
-└──────────────────────────────────────────────────────┘
-```
-
-This strip uses the existing `.filter-btn` classes. It's rendered as part of the Patient Pathways view content (not fixed position) — it scrolls with the content.
-
-### HTML Structure (Dash)
-
-```python
-# This goes inside the Patient Pathways view, at the top of its content area
-html.Div(className="pathway-filters", id="pathway-filters", children=[
- html.Div(className="pathway-filters__buttons", children=[
- html.Button(children=[
- "Drugs",
- html.Span(id="drug-count-badge",
- className="filter-btn__badge filter-btn__badge--hidden"),
- ], id="open-drug-modal", className="filter-btn", n_clicks=0),
-
- html.Button(children=[
- "Trusts",
- html.Span(id="trust-count-badge",
- className="filter-btn__badge filter-btn__badge--hidden"),
- ], id="open-trust-modal", className="filter-btn", n_clicks=0),
-
- html.Button(children=[
- "Directorates",
- html.Span(id="directorate-count-badge",
- className="filter-btn__badge filter-btn__badge--hidden"),
- ], id="open-directorate-modal", className="filter-btn", n_clicks=0),
- ]),
- html.Button("Clear All", id="clear-all-filters",
- className="filter-btn filter-btn--clear", n_clicks=0),
-])
-```
-
-### CSS — New Classes
-
-```css
-/* ── Patient Pathways Filter Strip ── */
-.pathway-filters {
- background: var(--nhs-white);
- border: 1px solid var(--nhs-pale-grey);
- border-bottom: 2px solid var(--nhs-blue);
- padding: 8px 20px;
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-.pathway-filters__buttons {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-```
-
-The bottom border `2px solid nhs-blue` gives it a subtle "active" feel that connects it visually to the chart content below.
-
----
-
-## Page Structure Summary
-
-### app.py Layout Assembly (Phase 10)
-
-```python
-app.layout = dmc.MantineProvider(children=[
- # State stores
- dcc.Store(id="app-state", storage_type="session", data={
- "chart_type": "directory",
- "initiated": "all",
- "last_seen": "6mo",
- "date_filter_id": "all_6mo",
- "selected_drugs": [],
- "selected_directorates": [],
- "selected_trusts": [],
- "active_view": "patient-pathways",
- "selected_comparison_directorate": None,
- }),
- dcc.Store(id="chart-data", storage_type="memory"),
- dcc.Store(id="reference-data", storage_type="session"),
- dcc.Store(id="active-tab", storage_type="memory", data="icicle"),
- dcc.Location(id="url", refresh=False),
-
- # Page structure
- make_header(), # Fixed, 56px, dark blue
- make_sidebar(), # Fixed, 240px left, below header
- make_sub_header(), # Fixed, 44px, light blue, right of sidebar
- make_modals(), # Filter modals (drug, trust, directorate)
-
- html.Main(className="main", children=[
- # Content switched by active_view
- html.Div(id="view-container", children=[
- # Patient Pathways view
- html.Div(id="patient-pathways-view", children=[
- make_pathway_filters(), # Drug/trust/directorate buttons
- make_chart_card(), # Tab bar + chart (Icicle + Sankey only)
- ]),
- # Trust Comparison view
- html.Div(id="trust-comparison-view", style={"display": "none"}, children=[
- make_tc_landing(), # Directorate selector grid
- make_tc_dashboard(), # 6-chart dashboard (hidden initially)
- ]),
- ]),
- make_footer(),
- ]),
-])
-```
-
-### Sidebar Changes
-
-```python
-def make_sidebar():
- return html.Nav(className="sidebar", **{"aria-label": "Main navigation"}, children=[
- html.Div(className="sidebar__section", children=[
- html.Div("Analysis", className="sidebar__label"),
- _sidebar_item("Patient Pathways", "pathway",
- active=True, item_id="nav-patient-pathways"),
- _sidebar_item("Trust Comparison", "compare",
- active=False, item_id="nav-trust-comparison"),
- ]),
- html.Div(className="sidebar__footer", children=[
- "NHS Norfolk & Waveney ICB",
- html.Br(),
- "High Cost Drugs Programme",
- ]),
- ])
-```
-
-New icon needed for "compare":
-```python
-_ICONS = {
- "pathway": '...', # existing
- "compare": '', # bar chart icon
-}
-```
-
-### View Switching Callback
-
-```python
-@app.callback(
- Output("patient-pathways-view", "style"),
- Output("trust-comparison-view", "style"),
- Output("nav-patient-pathways", "className"),
- Output("nav-trust-comparison", "className"),
- Input("app-state", "data"),
-)
-def switch_view(app_state):
- view = app_state.get("active_view", "patient-pathways")
- show = {}
- hide = {"display": "none"}
- active_cls = "sidebar__item sidebar__item--active"
- inactive_cls = "sidebar__item"
-
- if view == "patient-pathways":
- return show, hide, active_cls, inactive_cls
- else:
- return hide, show, inactive_cls, active_cls
-```
-
----
-
-## CSS Variable Additions
-
-```css
-:root {
- /* ... existing ... */
- --sub-header-h: 44px;
- --header-total-h: 100px; /* 56px header + 44px sub-header */
-}
-```
-
-Update `.main`:
-```css
-.main {
- margin-left: var(--sidebar-w);
- margin-top: var(--header-total-h);
- padding: 24px;
- min-height: calc(100vh - var(--header-total-h));
- display: flex; flex-direction: column; gap: 20px;
-}
-```
-
----
-
-## Responsive Adjustments
-
-```css
-@media (max-width: 1200px) {
- .tc-landing__grid { grid-template-columns: repeat(2, 1fr); }
- .tc-landing__grid--wide { grid-template-columns: repeat(3, 1fr); }
-}
-@media (max-width: 768px) {
- .sidebar { display: none; }
- .main { margin-left: 0; }
- .sub-header { left: 0; }
- .tc-landing__grid { grid-template-columns: 1fr; }
- .tc-dashboard__grid { grid-template-columns: 1fr; }
-}
-```
diff --git a/docs/SNOWFLAKE_REFERENCE.md b/docs/SNOWFLAKE_REFERENCE.md
deleted file mode 100644
index 160bc6a..0000000
--- a/docs/SNOWFLAKE_REFERENCE.md
+++ /dev/null
@@ -1,192 +0,0 @@
-# Snowflake Reference
-
-Essential database context for querying NHS data. Read this every iteration when working with Snowflake.
-
----
-
-## Snowflake MCP Server
-
-Use `mcp__snowflake-mcp__*` functions to explore schema and test queries.
-
-### Schema Discovery (USE THESE FIRST)
-- `test_connection()` - Verify connectivity
-- `list_databases()` - List accessible databases
-- `list_schemas(database_name)` - List schemas in a database
-- `list_tables(database, schema)` - List tables with descriptions
-- `list_views(schema_name, database)` - List views with descriptions
-- `describe_table(table_name, database)` - Get detailed table schema
-- `describe_query(query, database)` - Preview query output columns without execution
-
-### Query Execution
-- `read_data(query, database, max_rows)` - Execute SELECT queries with row limits
-- `read_data_paginated(query, database, page_size, page)` - Paginated results with total count
-- `read_data_pandas(query, database, max_rows, output_format)` - Results in pandas-friendly formats
-
-### Async Query Support (long-running queries)
-- `execute_async(query, database)` - Submit asynchronously, returns query_id
-- `get_query_status(query_id, database)` - Check status
-- `get_async_results(query_id, database, max_rows)` - Retrieve results
-
-### Usage Guidelines
-- **ALWAYS** verify table structures and column names via MCP before writing queries
-- Test with small result sets (`LIMIT 20`) before full execution
-- Use `describe_query` to preview complex query outputs before running
-- Use async queries for operations expected to take >30 seconds
-
----
-
-## Database Overview
-
-| Database | Purpose |
-|----------|---------|
-| `DATA_HUB` | **Analyst-curated** data warehouse - primary source for most queries |
-| `PRIMARY_CARE` | Raw extracts from EMIS and TPP clinical systems |
-| `NATIONAL` | NHS England national datasets (SUS, ECDS, MHSDS, etc.) |
-| `FACTS_AND_DIMENSIONS_ALL_DATA` | External reference data (BNF, SNOMED, QOF clusters) |
-| `REPORTING_DATASETS_ICB` | Reporting outputs and analyst workspaces (includes SCRATCHPAD) |
-
-**Avoid**: `SYSTEM` database.
-
----
-
-## Key Tables and Views
-
-### DATA_HUB.DWH (Dimensions)
-
-| View | Purpose | Key Columns |
-|------|---------|-------------|
-| `DimMedicineAndDevice` | Master medication/device reference | `ProductSnomedCode`, `TherapeuticMoietySnomedCode` (VTM), `BNFParagraphCode`, `StrengthDescription`, `ProductDescription` |
-| `DimPerson` | Patient demographics | `PatientPseudonym`, `PersonKey`, `CurrentGeneralPractice`, `IsCurrentNWRegistered`, `YearMonthBirth` |
-| `DimSnomedCode` | SNOMED code descriptions | `SnomedCode`, `SnomedDescription` |
-| `DimOrganisationAndSite` | GP practices and NHS orgs | `SiteCode`, `OrganisationName`, `OrganisationSubType`, `IsSiteNorfolkAndWaveney`, `IsSiteActive` |
-| `DimDate` | Date dimension | |
-| `DimCondition` | Clinical conditions | Long-term condition flags |
-| `DimDeprivation` | Deprivation rankings by area | |
-
-**CRITICAL**:
-- `ProductDescription` is the correct column for product names. `ProductName` does NOT exist.
-- `IsLatest` does NOT exist in `DimMedicineAndDevice`.
-
-### DATA_HUB.CDM (Common Data Model)
-
-| View | Purpose | Key Columns |
-|------|---------|-------------|
-| `Acute__Conmon__PatientLevelDrugs` | HCD activity data | `PseudoNHSNoLinked`, `InterventionDate`, `DrugName`, `Price Actual` |
-
-**Note**: HCD `PseudoNHSNoLinked` = GP `PatientPseudonym` for patient linkage.
-
-### DATA_HUB.PHM (Population Health Management)
-
-| View | Purpose | Key Columns |
-|------|---------|-------------|
-| `PrimaryCareClinicalCoding` | **Unified** clinical coding (EMIS + TPP, no duplicates) | `PatientPseudonym`, `SNOMEDCode`, `EventDateTime`, `NumericValue` |
-| `PrimaryCareMedication` | **Unified** medication data (EMIS + TPP, no duplicates) | `PatientPseudonym`, `SNOMEDCode`, `DateMedicationStart`, `Quantity` |
-| `ClinicalCodingClusterSnomedCodes` | SNOMED codes grouped by cluster | `ClusterId`, `SnomedCode` |
-| `PersonCohort` | Pre-defined patient cohorts | |
-
-**Prefer DATA_HUB.PHM unified views** over raw PRIMARY_CARE tables.
-
----
-
-## Patient Identifiers
-
-| Identifier | Source | Usage |
-|------------|--------|-------|
-| `PatientPseudonym` | DATA_HUB, NATIONAL | Primary - use for most joins |
-| `PseudoNHSNoLinked` | DATA_HUB.CDM (HCD data) | Links to PatientPseudonym |
-| `PersonKey` | DATA_HUB.DWH.DimPerson | Integer key for person dimension |
-
-### Standard Join Patterns
-```sql
--- HCD Activity to GP Diagnosis
-FROM DATA_HUB.CDM."Acute__Conmon__PatientLevelDrugs" hcd
-LEFT JOIN DATA_HUB.PHM."PrimaryCareClinicalCoding" pcc
- ON hcd."PseudoNHSNoLinked" = pcc."PatientPseudonym"
-
--- Activity to Person Demographics
-FROM DATA_HUB.CDM."Acute__Conmon__PatientLevelDrugs" hcd
-INNER JOIN DATA_HUB.DWH."DimPerson" dp
- ON hcd."PseudoNHSNoLinked" = dp."PatientPseudonym"
-```
-
----
-
-## CRITICAL: Registered Population Filter
-
-**ALWAYS** apply when counting patients:
-
-```sql
-WHERE dp."IsCurrentNWRegistered" = 'Yes'
- AND dp."CurrentGeneralPractice" <> '*'
-```
-
-Without this filter, counts will be ~2x inflated (includes deceased, deregistered, out-of-area patients).
-
----
-
-## Query Development Patterns
-
-### Clinical Condition Detection (GP SNOMED Clusters)
-```sql
--- Get all SNOMED codes for a clinical cluster
-SELECT "SnomedCode"
-FROM DATA_HUB.PHM."ClinicalCodingClusterSnomedCodes"
-WHERE "ClusterId" = 'RARTH_COD' -- Rheumatoid arthritis
-
--- Check if patient has condition
-SELECT DISTINCT pcc."PatientPseudonym"
-FROM DATA_HUB.PHM."PrimaryCareClinicalCoding" pcc
-WHERE pcc."SNOMEDCode" IN (SELECT "SnomedCode" FROM cluster_codes)
- AND pcc."PatientPseudonym" IS NOT NULL
-```
-
-### Available SNOMED Clusters for HCD Indications
-- `RARTH_COD` (155 codes) - Rheumatoid arthritis
-- `PSORIASIS_COD` (116 codes) - Psoriasis
-- `CROHNS_COD` (93 codes) - Crohn's disease
-- `ULCCOLITIS_COD` (62 codes) - Ulcerative colitis
-- `MS_COD` (44 codes) - Multiple sclerosis
-- `DM_COD` / `DMTYPE1_COD` / `DMTYPE2AUDIT_COD` - Diabetes
-
-### Sample HCD Activity Query
-```sql
-SELECT
- hcd."PseudoNHSNoLinked" AS PatientPseudonym,
- hcd."DrugName",
- hcd."InterventionDate",
- hcd."Provider Code",
- hcd."OrganisationName"
-FROM DATA_HUB.CDM."Acute__Conmon__PatientLevelDrugs" hcd
-WHERE hcd."InterventionDate" >= '2024-01-01'
-LIMIT 20
-```
-
----
-
-## Snowflake SQL Syntax
-
-- Double-quote identifiers: `"PatientPseudonym"`
-- Date literals: `'2025-04-01'::DATE`
-- Date functions: `DATEADD('MONTH', -3, date)`, `DATEDIFF('YEAR', d1, d2)`, `LAST_DAY(date)`
-- Boolean: `TRUE`/`FALSE`
-- No `TOP N` - use `LIMIT N`
-- `COALESCE()`, `NULLIF()`, `GREATEST()` work as expected
-
----
-
-## Troubleshooting
-
-### Column not found errors
-1. Use `describe_table(table_name, database)` to get actual column names
-2. Remember: Snowflake identifiers are case-sensitive when quoted
-3. Common mistakes: `ProductName` (wrong) vs `ProductDescription` (correct)
-
-### Empty results
-1. Check patient identifier filtering (`IS NOT NULL`)
-2. Check date ranges
-3. Test with `LIMIT 20` first to see sample data
-
-### Slow queries
-1. Add `LIMIT` during development
-2. Use `describe_query` to validate structure before execution
-3. Consider async execution for large result sets
diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md
deleted file mode 100644
index f404722..0000000
--- a/docs/USER_GUIDE.md
+++ /dev/null
@@ -1,257 +0,0 @@
-# User Guide - NHS Patient Pathway Analysis Tool
-
-This guide explains how to use the NHS High-Cost Drug Patient Pathway Analysis Tool to analyze treatment pathways for secondary care patients.
-
-## Table of Contents
-
-1. [Getting Started](#getting-started)
-2. [Interface Overview](#interface-overview)
-3. [Filtering Data](#filtering-data)
-4. [Using the Drug Browser](#using-the-drug-browser)
-5. [Understanding the Pathway Chart](#understanding-the-pathway-chart)
-6. [GP Indication Matching](#gp-indication-matching)
-7. [Troubleshooting](#troubleshooting)
-
----
-
-## Getting Started
-
-### Accessing the Application
-
-Start the application by running:
-
-```bash
-python run_dash.py
-```
-
-Then open your browser to **http://localhost:8050**
-
-The application automatically loads pre-computed pathway data from SQLite on startup. No additional setup is needed to view existing data.
-
-### Data Freshness
-
-The header bar shows when data was last refreshed:
-- **Patient count**: Total patients in the dataset (e.g., "11,118 patients")
-- **Last updated**: Relative time since the last data refresh (e.g., "2h ago")
-
-To refresh the data, run the CLI command (requires Snowflake access):
-
-```bash
-python -m cli.refresh_pathways --chart-type all
-```
-
----
-
-## Interface Overview
-
-The application is a single-page layout with the following components:
-
-### Header
-- NHS branding and application title ("HCD Analysis")
-- Green status dot with patient count and last-updated time
-
-### Sidebar (Left)
-Navigation items including:
-- **Pathway Overview** — main view (always active)
-- **Drug Selection** — opens the drug browser drawer
-- **Trust Selection** — opens the drawer with trust chips
-- **Indications** — opens the drawer with directorate browser
-
-### KPI Row
-Four summary cards that update dynamically:
-- **Unique Patients** — number of distinct patients matching current filters
-- **Drug Types** — number of distinct drugs in filtered data
-- **Total Cost** — total cost of treatments in the filtered dataset
-- **Indication Match** — GP diagnosis match rate (~93% for indication charts, shown as "—" for directory charts)
-
-### Filter Bar
-- **Chart type toggle**: "By Directory" / "By Indication" pills
-- **Treatment Initiated**: All years, Last 2 years, or Last 1 year
-- **Last Seen**: Last 6 months or Last 12 months
-
-### Chart Card
-- Dynamic subtitle showing the current hierarchy (e.g., "Trust → Directorate → Drug → Pathway")
-- Interactive Plotly icicle chart
-- Loading spinner during data fetch
-
----
-
-## Filtering Data
-
-### Chart Type
-
-Toggle between two views using the pills in the filter bar:
-
-| View | Hierarchy | Best For |
-|------|-----------|----------|
-| **By Directory** | Trust → Directorate → Drug → Pathway | Understanding treatment by medical specialty |
-| **By Indication** | Trust → GP Diagnosis → Drug → Pathway | Understanding treatment by patient condition |
-
-### Date Filters
-
-Two dropdowns control the time window:
-
-| Filter | Options | Effect |
-|--------|---------|--------|
-| **Treatment Initiated** | All years, Last 2 years, Last 1 year | When patients started treatment |
-| **Last Seen** | Last 6 months, Last 12 months | Most recent activity window |
-
-The default is "All years / Last 6 months" — showing all patients who have been active in the last 6 months.
-
-### Drug and Trust Selection
-
-Open the drawer (right panel) by clicking "Drug Selection" or "Trust Selection" in the sidebar:
-
-- **Drug chips**: Click to select/deselect specific drugs. Selected drugs filter the chart.
-- **Trust chips**: Click to select/deselect specific NHS trusts.
-- **Clear All Filters**: Button at the bottom resets all drug and trust selections.
-
-**No selections = show everything.** Leaving chips unselected is the same as selecting all.
-
----
-
-## Using the Drug Browser
-
-The drawer contains three sections:
-
-### All Drugs
-A flat list of all 42 available drugs as selectable chips. Click one or more to filter the chart to those drugs only.
-
-### Trusts
-A list of 7 NHS trusts as selectable chips. Click to filter by specific organizations.
-
-### By Directorate
-An accordion browser organized by clinical directorate:
-
-1. Click a **directorate** (e.g., "CARDIOLOGY") to expand it
-2. Inside, click an **indication** (e.g., "heart failure") to expand further
-3. Each indication shows **drug fragment badges** (e.g., "SACUBITRIL", "IVABRADINE")
-4. Clicking a drug fragment badge selects all full drug names that contain that fragment
-
-For example, clicking the "ADALIMUMAB" badge would select "ADALIMUMAB" in the drug chips above.
-
-### Fragment Matching
-
-Drug fragments are substrings, not exact matches. The fragment "INHALED" would match drugs like "INHALED BECLOMETASONE" and "INHALED FLUTICASONE".
-
-Clicking a fragment toggles its matching drugs:
-- **First click**: Selects all matching drugs
-- **Second click**: Deselects all matching drugs (if all were already selected)
-
----
-
-## Understanding the Pathway Chart
-
-### Hierarchy Structure
-
-The icicle chart displays a hierarchical breakdown:
-
-**Directory view:**
-```
-Root (Regional Total)
- └─ Trust (e.g., "Norfolk and Norwich University Hospitals")
- └─ Directorate (e.g., "RHEUMATOLOGY")
- └─ Drug (e.g., "ADALIMUMAB")
- └─ Pathway (e.g., "ADALIMUMAB → INFLIXIMAB")
-```
-
-**Indication view:**
-```
-Root (Regional Total)
- └─ Trust
- └─ GP Diagnosis (e.g., "rheumatoid arthritis")
- └─ Drug
- └─ Pathway
-```
-
-### Reading the Chart
-
-- **Width** of each section indicates relative patient count
-- **Color intensity** (NHS blue gradient) indicates proportion of parent group
-- **Labels** show the name and patient count
-
-### Interacting with the Chart
-
-| Action | Effect |
-|--------|--------|
-| **Click** a section | Zoom in to show details for that branch |
-| **Click** the parent/root | Zoom back out |
-| **Hover** over a section | See tooltip with patient count, cost, dosing frequency, dates |
-
-### Hover Tooltip Information
-
-When hovering over a chart section, you'll see:
-- Patient count and percentage of parent
-- Total cost and cost per patient
-- First and last seen dates
-- Treatment dosing frequency (for drug nodes)
-- Cost per patient per annum
-
----
-
-## GP Indication Matching
-
-When viewing "By Indication" charts, the application uses pre-computed GP diagnosis matches:
-
-### How It Works
-
-1. During data refresh, each patient's NHS pseudonym is queried against GP primary care records
-2. SNOMED cluster codes map clinical conditions to drug indications
-3. The most recent GP diagnosis match is used for each patient
-4. ~93% of patients are matched to a GP diagnosis
-
-### Unmatched Patients
-
-Patients without a GP diagnosis match appear under their directorate with a "(no GP dx)" suffix (e.g., "RHEUMATOLOGY (no GP dx)").
-
-Reasons for unmatched patients:
-- GP is outside the data coverage area
-- Diagnosis not yet recorded in GP system
-- Condition managed only in secondary care
-- Off-label prescribing
-
----
-
-## Troubleshooting
-
-### No data showing
-
-1. Check the filter bar — are filters too restrictive?
-2. Try clearing all drug/trust selections in the drawer
-3. Widen the date range (e.g., "All years / Last 12 months")
-
-### Chart shows "No matching pathways found"
-
-The current filter combination matches zero patients. Adjust filters or click "Clear All Filters" in the drawer.
-
-### App won't start
-
-```bash
-# Ensure dependencies are installed
-uv sync
-
-# Ensure src/ is on Python path
-uv run python setup_dev.py
-
-# Run with uv
-uv run python run_dash.py
-```
-
-### Stale data
-
-Data is as fresh as the last CLI refresh. Check the header's "Last updated" indicator. To refresh:
-
-```bash
-python -m cli.refresh_pathways --chart-type all
-```
-
----
-
-## Getting Help
-
-If you encounter issues not covered in this guide:
-
-1. Check the [README](../README.md) for installation and setup
-2. Review [DEPLOYMENT.md](./DEPLOYMENT.md) for server configuration
-3. Consult [CLAUDE.md](../CLAUDE.md) for technical architecture details
-4. Contact the Medicines Intelligence team for NHS-specific questions