diff --git a/.dockerignore b/.dockerignore
index 99a4800..75e7472 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -31,5 +31,4 @@ data.csv
# Docker itself
Dockerfile
docker-compose.yml
-.dockerignore
-
+.dockerignore
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 1c69b55..454e8af 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.idea
.venv
+dist/
__pycache__/
diff --git a/AGENTS.md b/AGENTS.md
deleted file mode 100644
index 1182e67..0000000
--- a/AGENTS.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# AGENTS.md — The Hope Pub Quiz Dashboard
-
-## Overview
-Single-page Flask dashboard that reads `data.csv` (one row per quiz night) and renders summary statistics, a player cost table, and five Plotly charts via a Jinja2 template. No database — the CSV is the sole data store.
-
-## Running the App
-```bash
-uv sync # install deps from uv.lock / pyproject.toml
-PYTHONPATH=src python src/app.py # run from project root — resolves data.csv correctly
-```
-> **Path gotcha:** `app.py` reads `data.csv` with a bare relative path, so the CWD must be the **project root**. `PYTHONPATH=src` puts `src/` on the import path so local modules resolve.
-
-## Data Shape (`data.csv`)
-- **One row = one quiz night**
-- Columns: `Date` (DD/MM/YYYY), `Absolute Position`, `Relative Position` (float 0–1, 0=1st/best, 1=last), `Number of Players`, `Number of Teams`, `Points on Scattergories`, then one binary column per player (1=attended, 0=absent).
-- Date parsing uses `dayfirst=True`; the DataFrame is sorted ascending by date on every load.
-
-## Module Responsibilities
-| File | Role |
-|---|---|
-| `src/app.py` | Flask route, five Plotly chart builders, data loading |
-| `src/stats.py` | `generate_stats(df)` → `(stats_dict, highlights_list)` tuple |
-| `src/player_table.py` | `generate_player_table(df)` → flat list-of-lists; cost hard-coded at **£3/quiz** |
-| `src/constants.py` | Player names, regression features, colour scheme, `ordinal(n)` helper |
-| `src/templates/index.html` | Renders `highlights` list, `stats` dict, `player_table`, and `plots` dict |
-
-## Key Conventions
-
-### Adding/Removing a Player
-1. Update `constants.PLAYER_NAME_COLUMNS` (ordered list — controls display order everywhere).
-2. Update `constants.FEATURE_COLUMNS` (set — controls which columns feed the regression model).
-3. Add the new binary column to `data.csv`.
-
-### `generate_stats` Return Value
-Returns a **tuple** `(stats, highlights)`:
-- `highlights` — list of `{"label": str, "value": str, "detail": str}` dicts; rendered as 6 KPI cards.
-- `stats` — plain `dict` of human-readable `label: value` pairs; rendered as a secondary list.
-
-The `index()` route unpacks both: `stats, highlights = generate_stats(df)`.
-
-### Plots Pipeline
-1. Build a Plotly figure in `app.py` using `Relative Position` directly (or `Relative Position * 100` for percentile display), where **lower = better**.
-2. Serialise: `json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)`.
-3. Store in the `plots` dict under a **snake_case key** (e.g. `"position_trend"`).
-4. The template renders every entry automatically: `Plotly.newPlot("{{ key }}", ...)` — key is both the `
` and JS target.
-
-Current charts (in render order): `position_trend`, `player_impact`, `scattergories_vs_position`, `player_participation`, `calendar`.
-
-### `player_table` Structure
-`[0]` = header row, `[1:-1]` = data rows sorted by appearances descending, `[-1]` = totals footer. The template uses `player_table[1:-1]` for `
` and `player_table[-1]` for ``.
-
-### `Relative Position` Convention
-Raw data stores `Relative Position` (0=best). The dashboard keeps this convention everywhere: lower values are better in stats, tables, and chart labels. If a chart uses percentile text, it is `Relative Position * 100` (still lower = better).
-
-### `ordinal(n)` Helper
-Lives in `constants.py`. Returns e.g. `"1st"`, `"22nd"`, `"63rd"`. Import where needed: `from constants import ordinal`.
-
-### Player Impact Chart
-Shows average relative percentile when each player attends. Only players with **>= 3 appearances** are shown (`MIN_APPEARANCES = 3` in `generate_player_impact`). Green bar = below overall average (better); red = above (worse).
-
-## Frontend
-No build step. Tailwind CSS (`cdn.tailwindcss.com`) and Plotly (`cdn.plot.ly/plotly-3.0.1.min.js`) loaded from CDN. Charts use `{ responsive: true, displayModeBar: false }` config.
diff --git a/dist/index.html b/dist/index.html
deleted file mode 100644
index d72648a..0000000
--- a/dist/index.html
+++ /dev/null
@@ -1,506 +0,0 @@
-
-
-
-
-
-
- The Hope Pub Quiz
-
-
-
-
-
-
-
-
-
-
Performance Dashboard
-
The Hope Pub Quiz
-
Trying to convince ourselves we're not stupid
-
-
-
-
-
-
-
- At a Glance
-
-
-
- 23
- Quizzes Played
- Since Mar 2025
-
-
-
- 61st
- Avg. Relative Percentile
- Lower = better finishing position
-
-
-
- 4th place
- Best Finish
- Out of 12 teams
-
-
-
- 68th
- Recent Form
- ↑ declining vs overall (lower is better)
-
-
-
- 5
- Top-Half Finishes
- 22% of all games
-
-
-
- 4 wks
- Best Streak
- Consecutive weeks attended
-
-
-
-
-
-
-
-
-
-
-
More Stats
-
-
-
-
- Top-half finishes
- - 5 of 23 (22%)
-
-
-
-
- Average teams per night
- - 11.8
-
-
-
-
- Average squad size
- - 2.9 players
-
-
-
-
- Average Scattergories score
- - 6.9 pts
-
-
-
-
- Best Scattergories score
- - 11 pts
-
-
-
-
- Longest individual streak
- - 14 weeks (Jay)
-
-
-
-
-
-
-
-
Squad
-
-
-
-
- |
- Player
- |
-
-
- Appearances
- |
-
-
- Avg. Relative Percentile
- |
-
-
- Spent
- |
-
-
-
-
-
-
-
- |
- Jay
- |
-
-
- 22
- |
-
-
- 62nd
- |
-
-
- £66.00
- |
-
-
-
-
-
- |
- Ciaran
- |
-
-
- 20
- |
-
-
- 62nd
- |
-
-
- £60.00
- |
-
-
-
-
-
- |
- Sam
- |
-
-
- 9
- |
-
-
- 59th
- |
-
-
- £27.00
- |
-
-
-
-
-
- |
- Ellora
- |
-
-
- 5
- |
-
-
- 52nd
- |
-
-
- £15.00
- |
-
-
-
-
-
- |
- Drew
- |
-
-
- 3
- |
-
-
- 56th
- |
-
-
- £9.00
- |
-
-
-
-
-
- |
- Tom
- |
-
-
- 2
- |
-
-
- 62nd
- |
-
-
- £6.00
- |
-
-
-
-
-
- |
- Chloe
- |
-
-
- 2
- |
-
-
- 75th
- |
-
-
- £6.00
- |
-
-
-
-
-
- |
- Theo
- |
-
-
- 1
- |
-
-
- 50th
- |
-
-
- £3.00
- |
-
-
-
-
-
- |
- Jamie
- |
-
-
- 1
- |
-
-
- 50th
- |
-
-
- £3.00
- |
-
-
-
-
-
- |
- Christine
- |
-
-
- 1
- |
-
-
- 50th
- |
-
-
- £3.00
- |
-
-
-
-
-
- |
- Mide
- |
-
-
- 1
- |
-
-
- 100th
- |
-
-
- £3.00
- |
-
-
-
-
-
-
-
- |
- Total
- |
-
-
- 67
- |
-
-
- —
- |
-
-
- £201.00
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file