diff --git a/.claude/commands/html-report.md b/.claude/commands/html-report.md
new file mode 100644
index 0000000..7fff2e4
--- /dev/null
+++ b/.claude/commands/html-report.md
@@ -0,0 +1,135 @@
+# /html-report - Generate Application Tracker Dashboard
+
+Generate a self-contained HTML dashboard from `job_search_tracker.csv` and the application archives under `documents/applications/`. The output is a single `.html` file — no server, no dependencies — that can be opened directly in a browser.
+
+## Step 0: Parse Arguments
+
+- No argument → output to `reports/application-dashboard.html`
+- A path argument (e.g. `/html-report ~/Desktop/report.html`) → use that path
+- `--open` flag → after writing, tell the user to open the file (cannot open a browser directly)
+
+Create `reports/` if it does not exist.
+
+---
+
+## Step 1: Collect Data
+
+Read in parallel:
+
+1. **`job_search_tracker.csv`** — the primary source. Parse every row into a record with fields:
+ `date`, `company`, `sector`, `role`, `role_type`, `channel`, `status`, `contact_person`, `fit_rating`, `notes`, `cv_file`, `cover_letter_file`, `source`
+
+2. **`documents/applications/*/outcome.md`** — for each resolved application, read the outcome file to get the exact interview stages reached (the checkboxes) and any notes. Merge this into the matching tracker row by company+role fuzzy match (lowercase, ignore punctuation). If an archive exists for a row but there is no match, attach it as extra context anyway.
+
+Status normalisation — map tracker values to five canonical buckets before computing stats:
+- `applied` → **Active** (resume submitted, no further signal)
+- `interview` → **Interview**
+- `offer` → **Offer**
+- `hired` → **Hired**
+- `rejected` / `no_response` / `no response` / `offer_declined` / `interview_only` / `withdrawn` → **Rejected/Closed**
+
+---
+
+## Step 2: Compute Summary Stats
+
+From the normalised data compute:
+
+- **Total applications**
+- **By status bucket:** count per bucket
+- **By sector:** count per unique sector value
+- **By channel:** online vs referral vs other
+- **By year/season:** group by the `date` field (which may be a year like `2025` or a full date)
+- **Funnel rates:** what % progressed past resume screen (reached Interview or beyond)
+- **Rejection rate:** Rejected/Closed ÷ Total with a resolved status (exclude Active)
+
+---
+
+## Step 3: Generate the HTML
+
+Write a single self-contained HTML file. All CSS is inline in a `