add .gitignore

This commit is contained in:
John Gatward committed 2026-03-20 11:16:44 +00:00
1 parent bdcbe6efbd
commit 0e946bcb18
4 files changed
+1 -569

No files matched your search

-1
View File
@@ -32,4 +32,3 @@ data.csv
Dockerfile Dockerfile
docker-compose.yml docker-compose.yml
.dockerignore .dockerignore
+1
View File
@@ -1,4 +1,5 @@
.idea .idea
.venv .venv
dist/
__pycache__/ __pycache__/
-62
View File
@@ -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 `<div id>` 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 `<tbody>` and `player_table[-1]` for `<tfoot>`.
### `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.
-506
View File
@@ -1,506 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>The Hope Pub Quiz</title>
<script src="https://cdn.plot.ly/plotly-3.0.1.min.js" charset="utf-8"></script>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-50 min-h-screen text-gray-900 antialiased">
<!-- ── Header ─────────────────────────────────────────────────────────── -->
<header class="bg-blue-900 text-white shadow-lg">
<div class="max-w-6xl mx-auto px-4 sm:px-8 py-8 sm:py-10">
<p class="text-blue-300 text-xs font-semibold tracking-widest uppercase mb-2">Performance Dashboard</p>
<h1 class="text-3xl sm:text-4xl font-bold tracking-tight">The Hope Pub Quiz</h1>
<p class="text-blue-300 mt-2 text-sm">Trying to convince ourselves we're not stupid</p>
</div>
</header>
<main class="max-w-6xl mx-auto px-4 sm:px-8 py-10 space-y-14">
<!-- ── KPI Cards ───────────────────────────────────────────────────── -->
<section>
<h2 class="text-xs font-semibold tracking-widest uppercase text-gray-400 mb-5">At a Glance</h2>
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-4">
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-5 flex flex-col gap-1">
<span class="text-3xl font-bold text-blue-900 leading-none">23</span>
<span class="text-sm font-medium text-gray-700 mt-2">Quizzes Played</span>
<span class="text-xs text-gray-400 leading-snug">Since Mar 2025</span>
</div>
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-5 flex flex-col gap-1">
<span class="text-3xl font-bold text-blue-900 leading-none">61st</span>
<span class="text-sm font-medium text-gray-700 mt-2">Avg. Relative Percentile</span>
<span class="text-xs text-gray-400 leading-snug">Lower = better finishing position</span>
</div>
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-5 flex flex-col gap-1">
<span class="text-3xl font-bold text-blue-900 leading-none">4th place</span>
<span class="text-sm font-medium text-gray-700 mt-2">Best Finish</span>
<span class="text-xs text-gray-400 leading-snug">Out of 12 teams</span>
</div>
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-5 flex flex-col gap-1">
<span class="text-3xl font-bold text-blue-900 leading-none">68th</span>
<span class="text-sm font-medium text-gray-700 mt-2">Recent Form</span>
<span class="text-xs text-gray-400 leading-snug">↑ declining vs overall (lower is better)</span>
</div>
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-5 flex flex-col gap-1">
<span class="text-3xl font-bold text-blue-900 leading-none">5</span>
<span class="text-sm font-medium text-gray-700 mt-2">Top-Half Finishes</span>
<span class="text-xs text-gray-400 leading-snug">22% of all games</span>
</div>
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-5 flex flex-col gap-1">
<span class="text-3xl font-bold text-blue-900 leading-none">4 wks</span>
<span class="text-sm font-medium text-gray-700 mt-2">Best Streak</span>
<span class="text-xs text-gray-400 leading-snug">Consecutive weeks attended</span>
</div>
</div>
</section>
<!-- ── Stats + Squad Table ─────────────────────────────────────────── -->
<section class="grid grid-cols-1 lg:grid-cols-2 gap-8 items-start">
<!-- Secondary stats -->
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-6">
<h2 class="text-xs font-semibold tracking-widest uppercase text-gray-400 mb-5">More Stats</h2>
<dl class="divide-y divide-gray-50">
<div class="flex justify-between items-baseline py-3 first:pt-0 last:pb-0">
<dt class="text-sm text-gray-500">Top-half finishes</dt>
<dd class="text-sm font-semibold text-gray-900 ml-4 text-right">5 of 23 (22%)</dd>
</div>
<div class="flex justify-between items-baseline py-3 first:pt-0 last:pb-0">
<dt class="text-sm text-gray-500">Average teams per night</dt>
<dd class="text-sm font-semibold text-gray-900 ml-4 text-right">11.8</dd>
</div>
<div class="flex justify-between items-baseline py-3 first:pt-0 last:pb-0">
<dt class="text-sm text-gray-500">Average squad size</dt>
<dd class="text-sm font-semibold text-gray-900 ml-4 text-right">2.9 players</dd>
</div>
<div class="flex justify-between items-baseline py-3 first:pt-0 last:pb-0">
<dt class="text-sm text-gray-500">Average Scattergories score</dt>
<dd class="text-sm font-semibold text-gray-900 ml-4 text-right">6.9 pts</dd>
</div>
<div class="flex justify-between items-baseline py-3 first:pt-0 last:pb-0">
<dt class="text-sm text-gray-500">Best Scattergories score</dt>
<dd class="text-sm font-semibold text-gray-900 ml-4 text-right">11 pts</dd>
</div>
<div class="flex justify-between items-baseline py-3 first:pt-0 last:pb-0">
<dt class="text-sm text-gray-500">Longest individual streak</dt>
<dd class="text-sm font-semibold text-gray-900 ml-4 text-right">14 weeks (Jay)</dd>
</div>
</dl>
</div>
<!-- Squad table -->
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-6 overflow-x-auto">
<h2 class="text-xs font-semibold tracking-widest uppercase text-gray-400 mb-5">Squad</h2>
<table class="w-full text-sm">
<thead>
<tr class="border-b-2 border-gray-100">
<th class="pb-3 text-xs font-semibold text-gray-400 uppercase tracking-wider
text-left">
Player
</th>
<th class="pb-3 text-xs font-semibold text-gray-400 uppercase tracking-wider
text-right">
Appearances
</th>
<th class="pb-3 text-xs font-semibold text-gray-400 uppercase tracking-wider
text-right">
Avg. Relative Percentile
</th>
<th class="pb-3 text-xs font-semibold text-gray-400 uppercase tracking-wider
text-right">
Spent
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-50">
<tr class="hover:bg-gray-50 transition-colors">
<td class="py-3
text-left font-medium text-gray-800">
Jay
</td>
<td class="py-3
text-right text-gray-500">
22
</td>
<td class="py-3
text-right text-gray-500">
62nd
</td>
<td class="py-3
text-right text-gray-500">
£66.00
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors">
<td class="py-3
text-left font-medium text-gray-800">
Ciaran
</td>
<td class="py-3
text-right text-gray-500">
20
</td>
<td class="py-3
text-right text-gray-500">
62nd
</td>
<td class="py-3
text-right text-gray-500">
£60.00
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors">
<td class="py-3
text-left font-medium text-gray-800">
Sam
</td>
<td class="py-3
text-right text-gray-500">
9
</td>
<td class="py-3
text-right text-gray-500">
59th
</td>
<td class="py-3
text-right text-gray-500">
£27.00
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors">
<td class="py-3
text-left font-medium text-gray-800">
Ellora
</td>
<td class="py-3
text-right text-gray-500">
5
</td>
<td class="py-3
text-right text-gray-500">
52nd
</td>
<td class="py-3
text-right text-gray-500">
£15.00
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors">
<td class="py-3
text-left font-medium text-gray-800">
Drew
</td>
<td class="py-3
text-right text-gray-500">
3
</td>
<td class="py-3
text-right text-gray-500">
56th
</td>
<td class="py-3
text-right text-gray-500">
£9.00
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors">
<td class="py-3
text-left font-medium text-gray-800">
Tom
</td>
<td class="py-3
text-right text-gray-500">
2
</td>
<td class="py-3
text-right text-gray-500">
62nd
</td>
<td class="py-3
text-right text-gray-500">
£6.00
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors">
<td class="py-3
text-left font-medium text-gray-800">
Chloe
</td>
<td class="py-3
text-right text-gray-500">
2
</td>
<td class="py-3
text-right text-gray-500">
75th
</td>
<td class="py-3
text-right text-gray-500">
£6.00
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors">
<td class="py-3
text-left font-medium text-gray-800">
Theo
</td>
<td class="py-3
text-right text-gray-500">
1
</td>
<td class="py-3
text-right text-gray-500">
50th
</td>
<td class="py-3
text-right text-gray-500">
£3.00
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors">
<td class="py-3
text-left font-medium text-gray-800">
Jamie
</td>
<td class="py-3
text-right text-gray-500">
1
</td>
<td class="py-3
text-right text-gray-500">
50th
</td>
<td class="py-3
text-right text-gray-500">
£3.00
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors">
<td class="py-3
text-left font-medium text-gray-800">
Christine
</td>
<td class="py-3
text-right text-gray-500">
1
</td>
<td class="py-3
text-right text-gray-500">
50th
</td>
<td class="py-3
text-right text-gray-500">
£3.00
</td>
</tr>
<tr class="hover:bg-gray-50 transition-colors">
<td class="py-3
text-left font-medium text-gray-800">
Mide
</td>
<td class="py-3
text-right text-gray-500">
1
</td>
<td class="py-3
text-right text-gray-500">
100th
</td>
<td class="py-3
text-right text-gray-500">
£3.00
</td>
</tr>
</tbody>
<tfoot>
<tr class="border-t-2 border-gray-200">
<td class="pt-3 font-bold text-gray-700
text-left">
Total
</td>
<td class="pt-3 font-bold text-gray-700
text-right">
67
</td>
<td class="pt-3 font-bold text-gray-700
text-right">
—
</td>
<td class="pt-3 font-bold text-gray-700
text-right">
£201.00
</td>
</tr>
</tfoot>
</table>
</div>
</section>
<!-- ── Charts ──────────────────────────────────────────────────────── -->
<section>
<h2 class="text-xs font-semibold tracking-widest uppercase text-gray-400 mb-5">Charts</h2>
<div class="space-y-8">
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-4 sm:p-6">
<div id="position_trend"></div>
<script>
(function () {
var figure = {"data": [{"customdata": ["Ciaran, Jay", "Ciaran, Jay", "Ciaran, Jay, Sam, Drew", "Ciaran, Jay", "Ciaran, Jay, Sam", "Jay, Theo", "Ciaran, Jay, Sam", "Ciaran, Jay, Sam", "Ciaran, Jay, Sam, Tom", "Ciaran, Jay, Ellora", "Ciaran, Jay", "Ciaran, Jay, Sam, Ellora", "Ciaran, Jay, Ellora", "Ciaran, Jay, Sam, Ellora", "Ciaran, Sam, Tom", "Jay, Drew", "Ciaran, Jay, Ellora", "Ciaran, Jay, Chloe, Jamie", "Ciaran, Jay", "Ciaran, Jay", "Jay, Drew, Christine", "Ciaran, Jay", "Ciaran, Jay, Sam, Chloe, Mide"], "hovertemplate": "<b>%{x|%d %b %Y}</b><br>Relative percentile: %{y:.0f}th<br>Squad: %{customdata}<extra></extra>", "line": {"color": "#1e3a8a", "width": 1.5}, "marker": {"color": "#1e3a8a", "size": 6}, "mode": "lines+markers", "name": "Result", "x": ["2025-03-17T00:00:00.000000000", "2025-03-24T00:00:00.000000000", "2025-03-31T00:00:00.000000000", "2025-04-07T00:00:00.000000000", "2025-04-28T00:00:00.000000000", "2025-05-26T00:00:00.000000000", "2025-06-02T00:00:00.000000000", "2025-06-16T00:00:00.000000000", "2025-06-30T00:00:00.000000000", "2025-07-14T00:00:00.000000000", "2025-07-21T00:00:00.000000000", "2025-07-28T00:00:00.000000000", "2025-08-04T00:00:00.000000000", "2025-09-15T00:00:00.000000000", "2025-09-22T00:00:00.000000000", "2025-09-29T00:00:00.000000000", "2025-11-24T00:00:00.000000000", "2026-01-05T00:00:00.000000000", "2026-01-26T00:00:00.000000000", "2026-02-02T00:00:00.000000000", "2026-02-16T00:00:00.000000000", "2026-02-23T00:00:00.000000000", "2026-03-09T00:00:00.000000000"], "y": {"dtype": "f8", "bdata": "zMzMzMxMUUA0MzMzM1NXQDMzMzMzM0ZAZmZmZmYmTEA0MzMzM3NKQAAAAAAAAElAzczMzMxsVUABAAAAAEBLQDMzMzMz01RAAAAAAABAT0AAAAAAAABJQAAAAAAAwEJAAAAAAAAAREBnZmZmZqZAQJmZmZmZ2URAzczMzMwsUkCamZmZmTlWQAAAAAAAAElAZmZmZmYmTUAAAAAAAIBRQAAAAAAAAElAAAAAAAAATkAAAAAAAABZQA=="}, "type": "scatter"}, {"hovertemplate": "<b>%{x|%d %b %Y}</b><br>5-Game Avg: %{y:.0f}%<extra></extra>", "line": {"color": "#f59e0b", "width": 2.5}, "mode": "lines", "name": "5-Game Avg", "x": ["2025-03-17T00:00:00.000000000", "2025-03-24T00:00:00.000000000", "2025-03-31T00:00:00.000000000", "2025-04-07T00:00:00.000000000", "2025-04-28T00:00:00.000000000", "2025-05-26T00:00:00.000000000", "2025-06-02T00:00:00.000000000", "2025-06-16T00:00:00.000000000", "2025-06-30T00:00:00.000000000", "2025-07-14T00:00:00.000000000", "2025-07-21T00:00:00.000000000", "2025-07-28T00:00:00.000000000", "2025-08-04T00:00:00.000000000", "2025-09-15T00:00:00.000000000", "2025-09-22T00:00:00.000000000", "2025-09-29T00:00:00.000000000", "2025-11-24T00:00:00.000000000", "2026-01-05T00:00:00.000000000", "2026-01-26T00:00:00.000000000", "2026-02-02T00:00:00.000000000", "2026-02-16T00:00:00.000000000", "2026-02-23T00:00:00.000000000", "2026-03-09T00:00:00.000000000"], "y": {"dtype": "f8", "bdata": "zMzMzMxMUUAAAAAAAFBUQN7d3d3dPVFAMzMzMzNzUEBdj8L1KJxPQHI9CtejsE1ArkfhehTuTEByPQrXo/BNQFK4HoXrUVBAzczMzMzMUEDNzMzMzMxQQEjhehSux0xAFa5H4XpUS0AVrkfhelRGQAAAAAAAQERAhetRuB6FRkAqXI/C9ahLQCpcj8L1qExAKlyPwvUoT0AehetRuP5QQLoehetRuE9AE65H4XrUTEAK16NwPepQQA=="}, "type": "scatter"}, {"hoverinfo": "skip", "line": {"color": "#dc2626", "dash": "dot", "width": 1.5}, "mode": "lines", "name": "Trend", "x": ["2025-03-17", "2025-03-18", "2025-03-20", "2025-03-22", "2025-03-24", "2025-03-25", "2025-03-27", "2025-03-29", "2025-03-31", "2025-04-02", "2025-04-03", "2025-04-05", "2025-04-07", "2025-04-09", "2025-04-11", "2025-04-12", "2025-04-14", "2025-04-16", "2025-04-18", "2025-04-20", "2025-04-21", "2025-04-23", "2025-04-25", "2025-04-27", "2025-04-29", "2025-04-30", "2025-05-02", "2025-05-04", "2025-05-06", "2025-05-08", "2025-05-09", "2025-05-11", "2025-05-13", "2025-05-15", "2025-05-16", "2025-05-18", "2025-05-20", "2025-05-22", "2025-05-24", "2025-05-25", "2025-05-27", "2025-05-29", "2025-05-31", "2025-06-02", "2025-06-03", "2025-06-05", "2025-06-07", "2025-06-09", "2025-06-11", "2025-06-12", "2025-06-14", "2025-06-16", "2025-06-18", "2025-06-20", "2025-06-21", "2025-06-23", "2025-06-25", "2025-06-27", "2025-06-29", "2025-06-30", "2025-07-02", "2025-07-04", "2025-07-06", "2025-07-08", "2025-07-09", "2025-07-11", "2025-07-13", "2025-07-15", "2025-07-16", "2025-07-18", "2025-07-20", "2025-07-22", "2025-07-24", "2025-07-25", "2025-07-27", "2025-07-29", "2025-07-31", "2025-08-02", "2025-08-03", "2025-08-05", "2025-08-07", "2025-08-09", "2025-08-11", "2025-08-12", "2025-08-14", "2025-08-16", "2025-08-18", "2025-08-20", "2025-08-21", "2025-08-23", "2025-08-25", "2025-08-27", "2025-08-29", "2025-08-30", "2025-09-01", "2025-09-03", "2025-09-05", "2025-09-07", "2025-09-08", "2025-09-10", "2025-09-12", "2025-09-14", "2025-09-15", "2025-09-17", "2025-09-19", "2025-09-21", "2025-09-23", "2025-09-24", "2025-09-26", "2025-09-28", "2025-09-30", "2025-10-02", "2025-10-03", "2025-10-05", "2025-10-07", "2025-10-09", "2025-10-11", "2025-10-12", "2025-10-14", "2025-10-16", "2025-10-18", "2025-10-20", "2025-10-21", "2025-10-23", "2025-10-25", "2025-10-27", "2025-10-29", "2025-10-30", "2025-11-01Line truncated
Plotly.newPlot("position_trend", figure.data, figure.layout, { responsive: true, displayModeBar: false });
})();
</script>
</div>
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-4 sm:p-6">
<div id="player_impact"></div>
<script>
(function () {
var figure = {"data": [{"hovertemplate": "<b>%{y}</b><br>Avg relative percentile: %{x:.1f}th<extra></extra>", "marker": {"color": ["#16a34a", "#16a34a", "#16a34a", "#dc2626", "#dc2626"]}, "orientation": "h", "text": ["52nd (5 games)", "56th (3 games)", "59th (9 games)", "62nd (20 games)", "62nd (22 games)"], "textposition": "outside", "x": {"dtype": "f8", "bdata": "MzMzMzMzSkCamZmZmdlLQGZmZmZmpk1AzczMzMzMTkAzMzMzM/NOQA=="}, "y": ["Ellora", "Drew", "Sam", "Ciaran", "Jay"], "type": "bar"}], "layout": {"template": {"data": {"barpolar": [{"marker": {"line": {"color": "white", "width": 0.5}, "pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type": "barpolar"}], "bar": [{"error_x": {"color": "#2a3f5f"}, "error_y": {"color": "#2a3f5f"}, "marker": {"line": {"color": "white", "width": 0.5}, "pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type": "bar"}], "carpet": [{"aaxis": {"endlinecolor": "#2a3f5f", "gridcolor": "#C8D4E3", "linecolor": "#C8D4E3", "minorgridcolor": "#C8D4E3", "startlinecolor": "#2a3f5f"}, "baxis": {"endlinecolor": "#2a3f5f", "gridcolor": "#C8D4E3", "linecolor": "#C8D4E3", "minorgridcolor": "#C8D4E3", "startlinecolor": "#2a3f5f"}, "type": "carpet"}], "choropleth": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "choropleth"}], "contourcarpet": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "contourcarpet"}], "contour": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "contour"}], "heatmap": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "heatmap"}], "histogram2dcontour": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2dcontour"}], "histogram2d": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2d"}], "histogram": [{"marker": {"pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type": "histogram"}], "mesh3d": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "mesh3d"}], "parcoords": [{"line": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "parcoords"}], "pie": [{"automargin": true, "type": "pie"}], "scatter3d": [{"line": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatter3d"}], "scattercarpet": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattercarpet"}], "scattergeo": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattergeo"}], "scattergl": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattergl"}], "scattermapbox": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattermapbox"}], "scattermap": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattermap"}], "scatterpolargl": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatterpolargl"}], "scatterpolar": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatterpolar"}], "scatter": [{"fillpattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}, "type": "scatter"}], "scatterternary": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatterternary"}], "surface": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "surface"}], "table": [{"cells": {"fill": {"color": "#EBF0F8"}, "line": {"color": "white"}}, "header": {"fill": {"color": "#C8D4E3"}, "line": {"color": "white"}}, "type": "table"}]}, "layoutLine truncated
Plotly.newPlot("player_impact", figure.data, figure.layout, { responsive: true, displayModeBar: false });
})();
</script>
</div>
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-4 sm:p-6">
<div id="scattergories_vs_position"></div>
<script>
(function () {
var figure = {"data": [{"hovertemplate": "Scattergories Points=%{x}<br>Relative Position Percentile (lower is better)=%{y:.1f}<extra></extra>", "legendgroup": "", "marker": {"color": "#1e3a8a", "symbol": "circle", "opacity": 0.8, "size": 9}, "mode": "markers", "name": "", "orientation": "v", "showlegend": false, "x": {"dtype": "i1", "bdata": "CgQHBgUIBAYICAkECwUFBQYGCgUGCgo="}, "xaxis": "x", "y": {"dtype": "f8", "bdata": "zMzMzMxMUUA0MzMzM1NXQDMzMzMzM0ZAZmZmZmYmTEA0MzMzM3NKQAAAAAAAAElAzczMzMxsVUABAAAAAEBLQDMzMzMz01RAAAAAAABAT0AAAAAAAABJQAAAAAAAwEJAAAAAAAAAREBnZmZmZqZAQJmZmZmZ2URAzczMzMwsUkCamZmZmTlWQAAAAAAAAElAZmZmZmYmTUAAAAAAAIBRQAAAAAAAAElAAAAAAAAATkAAAAAAAABZQA=="}, "yaxis": "y", "type": "scatter"}, {"hovertemplate": "<b>OLS trendline</b><br>Relative Percentile = 0.100314 * Points on Scattergories + 60.3761<br>R<sup>2</sup>=0.000143<br><br>Scattergories Points=%{x}<br>Relative Position Percentile (lower is better)=%{y:.1f} <b>(trend)</b><extra></extra>", "legendgroup": "", "marker": {"color": "#636efa", "symbol": "circle"}, "mode": "lines", "name": "", "showlegend": false, "x": {"dtype": "i1", "bdata": "BAQEBQUFBQUGBgYGBgcICAgJCgoKCgs="}, "xaxis": "x", "y": {"dtype": "f8", "bdata": "3h+ne4BjTkDeH6d7gGNOQN4fp3uAY05AbD9hlldwTkBsP2GWV3BOQGw/YZZXcE5AbD9hlldwTkBsP2GWV3BOQPpeG7EufU5A+l4bsS59TkD6XhuxLn1OQPpeG7EufU5A+l4bsS59TkCIftXLBYpOQBaej+bclk5AFp6P5tyWTkAWno/m3JZOQKS9SQG0o05AMd0DHIuwTkAx3QMci7BOQDHdAxyLsE5AMd0DHIuwTkC//L02Yr1OQA=="}, "yaxis": "y", "type": "scatter", "line": {"color": "#dc2626", "dash": "dot", "width": 2}}], "layout": {"template": {"data": {"barpolar": [{"marker": {"line": {"color": "white", "width": 0.5}, "pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type": "barpolar"}], "bar": [{"error_x": {"color": "#2a3f5f"}, "error_y": {"color": "#2a3f5f"}, "marker": {"line": {"color": "white", "width": 0.5}, "pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type": "bar"}], "carpet": [{"aaxis": {"endlinecolor": "#2a3f5f", "gridcolor": "#C8D4E3", "linecolor": "#C8D4E3", "minorgridcolor": "#C8D4E3", "startlinecolor": "#2a3f5f"}, "baxis": {"endlinecolor": "#2a3f5f", "gridcolor": "#C8D4E3", "linecolor": "#C8D4E3", "minorgridcolor": "#C8D4E3", "startlinecolor": "#2a3f5f"}, "type": "carpet"}], "choropleth": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "choropleth"}], "contourcarpet": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "contourcarpet"}], "contour": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "contour"}], "heatmap": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "heatmap"}], "histogram2dcontour": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2dcontour"}], "histogram2d": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2d"}], "histogram": [{"marker": {"pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type": "histogram"}], "mesh3d": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "mesh3d"}], "parcoords": [{"line": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "parcoords"}], "pie": [{"automargin": true, "type": "pie"}], "scatter3d": [{"line": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatter3d"}], "scattercarpet": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattercarpet"}], "scattergeo": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattergeo"}], "scattergl": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattergl"}], "scattermapbox": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattermapbox"}], "scattermap": [{"marker": {"colorbar":Line truncated
Plotly.newPlot("scattergories_vs_position", figure.data, figure.layout, { responsive: true, displayModeBar: false });
})();
</script>
</div>
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-4 sm:p-6">
<div id="player_participation"></div>
<script>
(function () {
var figure = {"data": [{"coloraxis": "coloraxis", "name": "0", "x": {"dtype": "i1", "bdata": "AAECAwQFBgcICQoLDA0ODxAREhMUFRY="}, "y": ["Ciaran", "Jay", "Sam", "Drew", "Theo", "Tom", "Ellora", "Chloe", "Jamie", "Christine", "Mide"], "z": {"dtype": "i1", "bdata": "AQEBAQEAAQEBAQEBAQEBAAEBAQEAAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQAAAQABAAEBAQAAAQABAQAAAAAAAAABAAABAAAAAAAAAAAAAAAAAQAAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAQABAQEAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ==", "shape": "11, 23"}, "type": "heatmap", "xaxis": "x", "yaxis": "y", "hovertemplate": "Game: %{x}<br>Player: %{y}<br>Attended: %{z}<extra></extra>"}], "layout": {"template": {"data": {"barpolar": [{"marker": {"line": {"color": "white", "width": 0.5}, "pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type": "barpolar"}], "bar": [{"error_x": {"color": "#2a3f5f"}, "error_y": {"color": "#2a3f5f"}, "marker": {"line": {"color": "white", "width": 0.5}, "pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type": "bar"}], "carpet": [{"aaxis": {"endlinecolor": "#2a3f5f", "gridcolor": "#C8D4E3", "linecolor": "#C8D4E3", "minorgridcolor": "#C8D4E3", "startlinecolor": "#2a3f5f"}, "baxis": {"endlinecolor": "#2a3f5f", "gridcolor": "#C8D4E3", "linecolor": "#C8D4E3", "minorgridcolor": "#C8D4E3", "startlinecolor": "#2a3f5f"}, "type": "carpet"}], "choropleth": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "choropleth"}], "contourcarpet": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "contourcarpet"}], "contour": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "contour"}], "heatmap": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "heatmap"}], "histogram2dcontour": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2dcontour"}], "histogram2d": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2d"}], "histogram": [{"marker": {"pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type": "histogram"}], "mesh3d": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "mesh3d"}], "parcoords": [{"line": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "parcoords"}], "pie": [{"automargin": true, "type": "pie"}], "scatter3d": [{"line": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatter3d"}], "scattercarpet": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattercarpet"}], "scattergeo": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattergeo"}], "scattergl": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattergl"}], "scattermapbox": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattermapbox"}], "scattermap": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattermap"}], "scatterpolargl": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatterpolargl"}], "scatterpolar": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatterpolar"}], "scatter": [{"fillpattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}, "type": "scatter"}], "scatterternary": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatterternary"}], "surface": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9Line truncated
Plotly.newPlot("player_participation", figure.data, figure.layout, { responsive: true, displayModeBar: false });
})();
</script>
</div>
<div class="bg-white rounded-xl border border-gray-100 shadow-sm p-4 sm:p-6">
<div id="calendar"></div>
<script>
(function () {
var figure = {"data": [{"colorscale": [[0, "#E4ECF6"], [1, "#1e3a8a"]], "hovertemplate": "%{y}<br>Column %{x}<br>%{text}<br>Attended: %{z}<extra></extra>", "showscale": false, "text": [["ISO week 1", "ISO week 2", "ISO week 3", "ISO week 4", "ISO week 5", "ISO week 6", "ISO week 7", "ISO week 8", "ISO week 9", "ISO week 10", "ISO week 11", "ISO week 12", "ISO week 13"], ["ISO week 14", "ISO week 15", "ISO week 16", "ISO week 17", "ISO week 18", "ISO week 19", "ISO week 20", "ISO week 21", "ISO week 22", "ISO week 23", "ISO week 24", "ISO week 25", "ISO week 26"], ["ISO week 27", "ISO week 28", "ISO week 29", "ISO week 30", "ISO week 31", "ISO week 32", "ISO week 33", "ISO week 34", "ISO week 35", "ISO week 36", "ISO week 37", "ISO week 38", "ISO week 39"], ["ISO week 40", "ISO week 41", "ISO week 42", "ISO week 43", "ISO week 44", "ISO week 45", "ISO week 46", "ISO week 47", "ISO week 48", "ISO week 49", "ISO week 50", "ISO week 51", "ISO week 52"], ["ISO week 1", "ISO week 2", "ISO week 3", "ISO week 4", "ISO week 5", "ISO week 6", "ISO week 7", "ISO week 8", "ISO week 9", "ISO week 10", "ISO week 11", "ISO week 12", "ISO week 13"], ["ISO week 14", "ISO week 15", "ISO week 16", "ISO week 17", "ISO week 18", "ISO week 19", "ISO week 20", "ISO week 21", "ISO week 22", "ISO week 23", "ISO week 24", "ISO week 25", "ISO week 26"], ["ISO week 27", "ISO week 28", "ISO week 29", "ISO week 30", "ISO week 31", "ISO week 32", "ISO week 33", "ISO week 34", "ISO week 35", "ISO week 36", "ISO week 37", "ISO week 38", "ISO week 39"], ["ISO week 40", "ISO week 41", "ISO week 42", "ISO week 43", "ISO week 44", "ISO week 45", "ISO week 46", "ISO week 47", "ISO week 48", "ISO week 49", "ISO week 50", "ISO week 51", "ISO week 52"]], "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], "xgap": 3, "y": ["2025 \u00b7 W1-13", "2025 \u00b7 W14-26", "2025 \u00b7 W27-39", "2025 \u00b7 W40-52", "2026 \u00b7 W1-13", "2026 \u00b7 W14-26", "2026 \u00b7 W27-39", "2026 \u00b7 W40-52"], "ygap": 3, "z": [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], [1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0], [1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], "zmax": 1, "zmin": 0, "type": "heatmap"}], "layout": {"template": {"data": {"barpolar": [{"marker": {"line": {"color": "white", "width": 0.5}, "pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type": "barpolar"}], "bar": [{"error_x": {"color": "#2a3f5f"}, "error_y": {"color": "#2a3f5f"}, "marker": {"line": {"color": "white", "width": 0.5}, "pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type": "bar"}], "carpet": [{"aaxis": {"endlinecolor": "#2a3f5f", "gridcolor": "#C8D4E3", "linecolor": "#C8D4E3", "minorgridcolor": "#C8D4E3", "startlinecolor": "#2a3f5f"}, "baxis": {"endlinecolor": "#2a3f5f", "gridcolor": "#C8D4E3", "linecolor": "#C8D4E3", "minorgridcolor": "#C8D4E3", "startlinecolor": "#2a3f5f"}, "type": "carpet"}], "choropleth": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "choropleth"}], "contourcarpet": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "contourcarpet"}], "contour": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "contour"}], "heatmap": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "heatmap"}], "histogram2dcontour": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2dcontour"}], "histogram2d": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2d"}], "histogram": [{"marker": {"pattern": {"fillmode": "overlay", "size": 10, "solidity": 0.2}}, "type":Line truncated
Plotly.newPlot("calendar", figure.data, figure.layout, { responsive: true, displayModeBar: false });
})();
</script>
</div>
</div>
</section>
</main>
<!-- ── Footer ─────────────────────────────────────────────────────────── -->
<footer class="mt-16 border-t border-gray-200 bg-white">
<div class="max-w-6xl mx-auto px-4 sm:px-8 py-5 flex flex-col sm:flex-row justify-between items-center gap-2 text-xs text-gray-400">
<span>The Hope Pub Quiz Dashboard</span>
<span>23 quizzes · 5 charts</span>
</div>
</footer>
</body>
</html>