← All Reports

SPIKE-010: Dashboard UX Wireframes and IA

Spike 2026-02-22 Dashboard UX, IA, Wireframes, Readiness Score Complete
11
Deliverables
5
Team Members
6
Docs Produced
1
Day

Overview

SPIKE-010 was a UX-only spike to produce the information architecture, user flows, wireframes, readiness score specification, and visual design direction for the Data Hub dashboard. No implementation code was written. The spike focused entirely on defining the user experience layer that will guide future development.

The team of five (UX Researcher, UI Designer, Product Owner, Business Analyst, Scrum Master) worked from established inputs: a pastel-on-charcoal visual direction, progressive disclosure as the core navigation model (Glance to Domain to Detail), and the readiness score as the hero element. Data sources include Strava activities, Google Fit heart rate and sleep, Garmin Index S2 body composition, estimated nutrition logs, and Ozempic timeline tracking.

Six documentation artefacts were produced covering the full UX surface: site map and navigation, core user flows, wireframes for all page types, a readiness score UX specification, pastel design direction tokens, and acceptance criteria for the UX MVP.

Deliverables

ID Title Effort Status Owner
D-01 Information architecture map (site map + navigation model) M Complete UX Researcher
D-02 Core user flows (morning check, post-ride review, weekly review, log meal, log meds) M Complete UX Researcher
D-03 Wireframes: Glance page L Complete UI Designer
D-04 Wireframes: Domain pages (Training, Recovery, Body, Nutrition, Timeline) L Complete UI Designer
D-05 Wireframes: Detail drilldowns (Sleep, HR, CTL/ATL/TSB, Weight, Ride) L Complete UI Designer
D-06 Readiness score UX spec (score, factors, confidence, empty states) M Complete UX Researcher
D-07 Pastel design direction notes (tokens at UX level) S Complete UI Designer
D-08 Acceptance criteria for UX MVP M Complete Product Owner
D-09 Final report HTML M Complete Business Analyst
D-10 Reports index update card S Complete Business Analyst
D-11 Backlog update SPIKE-010 S Complete Scrum Master

Key Decisions

Technical Notes

All artefacts are located in docs/spike-ux-dashboard/:

This spike produced no implementation code. All outputs are design documentation intended to guide future development sprints.

Artefacts (Embedded)

This section embeds the UX artefacts produced in this spike for easy reading.

Information Architecture

docs/spike-ux-dashboard/ia.md

Information Architecture Map

**Spike:** SPIKE-010, UX Dashboard Wireframes

**Author:** UX Researcher

**Date:** 2026-02-22

**Status:** Complete

---

1. Progressive Disclosure Model

The dashboard uses a three-layer progressive disclosure model. Each layer serves a different user intent and time commitment.

| Layer | Name | Intent | Time Budget |

|-------|------|--------|-------------|

| L1 | Glance | "How am I doing right now?" | Under 10 seconds |

| L2 | Domain | "Tell me more about this area." | 1 to 3 minutes |

| L3 | Detail | "I want to explore this deeply." | Open-ended |

**Layer transitions:**

  • Glance to Domain: user taps a domain card or navigates via sidebar/bottom bar
  • Domain to Detail: user taps a specific metric, chart, or data row within a domain page
  • Any layer back to Glance: tap the home/dashboard icon or breadcrumb root

---

2. Site Map

Dashboard (Glance)                          [L1]
├── Training                                [L2]
│   ├── Ride Detail (per activity)          [L3]
│   ├── CTL / ATL / TSB (Fitness & Form)    [L3]
│   └── Weekly Volume History               [L3]
├── Recovery                                [L2]
│   ├── Sleep Detail (per night)            [L3]
│   ├── HR Detail (resting HR trend)        [L3]
│   └── Readiness History                   [L3]
├── Body                                    [L2]
│   ├── Weight Detail (trend + history)     [L3]
│   └── Body Comp Detail (future)           [L3]
├── Nutrition                               [L2]
│   ├── Meal Detail (per entry)             [L3]
│   └── Daily Summary                       [L3]
└── Timeline                                [L2]
    ├── Ozempic Compliance Grid             [L3]
    └── Event Detail (per log entry)        [L3]

---

3. Page Inventory

3.1 Glance Page (L1)

The single entry point. Displays a curated set of hero cards that answer "how am I doing today?" at a glance.

**Hero element:** Readiness Score card, prominently placed at the top of the page. Large score number (0 to 100), color-coded ring, top contributing factor, and a single-line insight.

**Supporting cards (below the hero):**

| Card | Data Source | Purpose |

|------|------------|---------|

| Race Countdown | Static config | Days until Race to the Sun (30 May 2026) |

| Today Snapshot | Google Fit | Steps, resting HR, sleep summary |

| Latest Activity | Strava | Most recent ride or workout |

| Fitness / Form Chart | Strava (computed) | CTL, ATL, TSB sparkline |

| Weight Trend | Garmin Index S2 / Google Fit | Current weight, 7-day direction |

| Weekly Volume | Strava | Total hours and km this week vs. last |

**Card interaction:** Each card links to its parent domain page (L2). The Latest Activity card links directly to the Ride Detail page (L3) for that specific activity.

3.2 Domain Pages (L2)

Each domain page expands one area with richer data, charts, and recent history.

Training (L2)
  • Recent activities list (last 7 to 14 days)
  • Weekly volume bar chart (hours and distance)
  • Fitness and Form chart (CTL / ATL / TSB over 6 to 12 weeks)
  • Race countdown with training phase context
  • Links to: Ride Detail (L3), CTL/ATL/TSB Detail (L3), Weekly Volume History (L3)
Recovery (L2)
  • Readiness score breakdown (all factors with individual scores)
  • Sleep summary for last night (total duration, stages bar)
  • Resting HR trend (7-day sparkline)
  • Recovery advice based on readiness level
  • Links to: Sleep Detail (L3), HR Detail (L3), Readiness History (L3)
Body (L2)
  • Current weight with trend arrow (up, down, stable)
  • Weight chart (30-day or 90-day trend)
  • Body composition metrics (if available from Garmin Index S2: body fat %, muscle mass)
  • Target weight indicator (if configured)
  • Links to: Weight Detail (L3), Body Comp Detail (L3, future)
Nutrition (L2)
  • Today's logged meals list
  • Calorie and macro summary for the day (if logged)
  • Quick-add meal button (prominent CTA)
  • 7-day logging streak indicator
  • Links to: Meal Detail (L3), Daily Summary (L3)
Timeline (L2)
  • Ozempic dose log (recent entries, compliance calendar grid)
  • Upcoming dose reminder
  • Combined timeline of key events across all domains (rides, weight check-ins, doses)
  • Quick-log Ozempic button (prominent CTA)
  • Links to: Ozempic Compliance Grid (L3), Event Detail (L3)

3.3 Detail Drilldown Pages (L3)

Each detail page provides deep exploration of a single metric or event.

| Detail Page | Parent Domain | Content |

|------------|---------------|---------|

| Ride Detail | Training | Full activity stats, map, HR/power charts, lap splits, segment efforts |

| CTL/ATL/TSB Detail | Training | Extended fitness/form chart, date range picker, trend analysis |

| Weekly Volume History | Training | Week-over-week comparison, volume targets |

| Sleep Detail | Recovery | Per-night sleep stage chart, duration trend, consistency score |

| HR Detail | Recovery | Resting HR 30-day trend, morning vs. evening readings, anomaly highlights |

| Readiness History | Recovery | Daily readiness scores over time, factor contribution trends |

| Weight Detail | Body | Full weight history chart, rate of change, annotations for milestones |

| Body Comp Detail | Body | Body fat %, muscle mass trends (future, when Garmin data available) |

| Meal Detail | Nutrition | Single meal entry: foods, macros, notes |

| Daily Summary | Nutrition | Full day nutrition totals, macro breakdown chart |

| Ozempic Compliance Grid | Timeline | Calendar heatmap of doses, streak tracking, dose history |

| Event Detail | Timeline | Single event record with all metadata |

---

4. Navigation Model

4.1 Desktop: Persistent Sidebar

The sidebar is always visible on screens wider than 768px.

┌─────────────────────────────────────────────┐
│ [Logo/Title]           │                    │
│                        │                    │
│ ● Dashboard (Glance)   │   Page Content     │
│                        │                    │
│ ○ Training             │                    │
│ ○ Recovery             │                    │
│ ○ Body                 │                    │
│ ○ Nutrition            │                    │
│ ○ Timeline             │                    │
│                        │                    │
│ ─────────────          │                    │
│ [Race Countdown Mini]  │                    │
└─────────────────────────────────────────────┘

**Sidebar behaviour:**

  • Active domain is highlighted with an accent indicator
  • Dashboard (Glance) is always the first item
  • Race countdown appears at the bottom of the sidebar as a persistent, compact widget
  • Sidebar does not collapse. Content area has sufficient width at 768px+.

4.2 Mobile: Bottom Navigation Bar

On screens narrower than 768px, navigation moves to a fixed bottom bar.

┌───────────────────────────┐
│       Page Content        │
│                           │
│                           │
│                           │
├───────────────────────────┤
│  Home Train Recov Body    │
│       Nutr  Time          │
└───────────────────────────┘

**Bottom bar behaviour:**

  • Six items: Home (Glance), Training, Recovery, Body, Nutrition, Timeline
  • Active item is highlighted with colour and label
  • Scrollable if needed on very narrow screens (under 320px), but six items should fit
  • Tapping an already-active domain scrolls to top of that page

4.3 Detail Page Navigation

When the user drills into a detail page (L3), the navigation context changes:

  • **Desktop:** Sidebar remains visible. A breadcrumb appears above the content area.
  • **Mobile:** Bottom bar remains visible. A back arrow and breadcrumb appear at the top.

---

5. URL Structure

All routes use a flat, readable pattern.

| Page | URL | Layer |

|------|-----|-------|

| Glance (Dashboard) | / | L1 |

| Training | /training | L2 |

| Recovery | /recovery | L2 |

| Body | /body | L2 |

| Nutrition | /nutrition | L2 |

| Timeline | /timeline | L2 |

| Ride Detail | /training/rides/:id | L3 |

| CTL/ATL/TSB Detail | /training/fitness-form | L3 |

| Weekly Volume History | /training/volume | L3 |

| Sleep Detail | /recovery/sleep/:date | L3 |

| HR Detail | /recovery/heart-rate | L3 |

| Readiness History | /recovery/readiness | L3 |

| Weight Detail | /body/weight | L3 |

| Body Comp Detail | /body/composition | L3 |

| Meal Detail | /nutrition/meals/:id | L3 |

| Daily Summary | /nutrition/day/:date | L3 |

| Ozempic Compliance | /timeline/ozempic | L3 |

| Event Detail | /timeline/events/:id | L3 |

**Conventions:**

  • Domain pages sit at the first path segment
  • Detail pages nest under their domain
  • Dynamic segments use :id (activity ID) or :date (ISO date, YYYY-MM-DD)
  • No deeper nesting than two levels

---

6. Breadcrumb Pattern

Breadcrumbs provide orientation, especially when deep-linking into detail pages.

**Format:** Domain > Page Title

**Examples:**

| Page | Breadcrumb |

|------|------------|

| Ride Detail | Training > Morning Ride (21 Feb) |

| Sleep Detail | Recovery > Sleep (21 Feb) |

| Weight Detail | Body > Weight Trend |

| CTL/ATL/TSB | Training > Fitness and Form |

| Meal Detail | Nutrition > Lunch (21 Feb) |

| Ozempic Compliance | Timeline > Ozempic Log |

**Behaviour:**

  • Breadcrumb root (domain name) links back to the domain page (L2)
  • On mobile, breadcrumbs collapse to a back arrow plus the domain name
  • On the Glance page (L1), no breadcrumb is shown
  • On domain pages (L2), no breadcrumb is shown (the sidebar/bottom bar provides context)
  • Breadcrumbs only appear on detail pages (L3)

---

7. Readiness Score Placement

The readiness score is the single most prominent element on the Glance page.

**Position:** First card, top of the content area, spanning the full width on mobile and occupying the primary column on desktop.

**Why hero placement:**

  • Readiness answers the most common question: "Should I train hard today or take it easy?"
  • It synthesises multiple data sources into one actionable number
  • It draws the user's eye first, establishing the daily narrative

**Navigation from readiness:**

  • Tap the readiness card on Glance to go to Recovery domain (L2), where the full breakdown is shown
  • From Recovery, tap individual factors to drill into Sleep Detail, HR Detail, or Readiness History (L3)

---

8. Information Density by Layer

| Layer | Cards/Sections | Charts | Data Points | Actions |

|-------|---------------|--------|-------------|---------|

| L1 Glance | 6 hero cards | 2 sparklines (fitness, weight) | Under 20 visible numbers | Tap to drill down |

| L2 Domain | 3 to 5 sections | 2 to 3 full charts | 20 to 50 visible data points | Tap rows, filter date range |

| L3 Detail | 1 focused view | 1 to 2 detailed charts | 50+ data points | Zoom, pan, date picker, export |

---

9. Empty States and Data Availability

Each page must handle missing data gracefully.

| Scenario | Behaviour |

|----------|-----------|

| No Strava data | Training cards show "Connect Strava" prompt |

| No sleep data | Recovery page shows readiness with reduced confidence; sleep card shows "No sleep data recorded" |

| No weight data | Body page shows "Log your first weigh-in" prompt |

| No meals logged | Nutrition page shows quick-add CTA prominently |

| No Ozempic logs | Timeline shows empty compliance grid with "Log your first dose" prompt |

| Partial data (some sources fresh, some stale) | Stale data cards show a subtle "last updated X ago" label; readiness confidence indicator adjusts |

---

10. Summary

The information architecture follows a clean three-layer model. The Glance page is the hub, domain pages are the spokes, and detail pages are the leaves. Readiness sits at the top of the hierarchy as the hero element. Navigation is persistent (sidebar on desktop, bottom bar on mobile) so the user always knows where they are and can switch context without going "back." Breadcrumbs provide orientation on detail pages only, keeping the interface uncluttered at higher layers.

User Flows

docs/spike-ux-dashboard/flows.md

Core User Flows

**Spike:** SPIKE-010, UX Dashboard Wireframes

**Author:** UX Researcher

**Date:** 2026-02-22

**Status:** Complete

---

1. Morning Check Flow

**Goal:** Answer "How am I doing today? Should I train hard or take it easy?"

**Time budget:** Under 60 seconds for the full flow, under 10 seconds for the glance.

Steps

1. **Open app** (or navigate to dashboard URL)

  • Lands on Glance page (L1)
  • Readiness score hero card loads immediately above the fold

2. **Glance at readiness score**

  • See score (0 to 100), colour-coded ring, status label ("Ready to Train" / "Take It Easy" / "Recovery Day")
  • Read the one-line insight ("Solid recovery overnight. CTL trending up.")
  • Decision point: if score is clear, user is done. If curious, continue.

3. **Review factor pills**

  • Below the score: Sleep 8/10, HRV 7/10, Form +4, Rest 1d
  • Identify which factor is weakest

4. **Tap readiness card** (optional drill-down)

  • Navigates to Recovery domain page (L2)
  • Full readiness breakdown visible: all factors with individual scores and trend indicators

5. **Tap sleep factor** (optional deeper drill-down)

  • Navigates to Sleep Detail (L3)
  • See last night's hypnogram, stages breakdown, comparison to average

6. **Return to Glance**

  • Tap "Dashboard" in sidebar (desktop) or "Home" in bottom bar (mobile)
  • Quick scan of daily snapshot row: weight, steps, RHR
  • Check race countdown: 97 days to go

Exit conditions

  • User knows their readiness level and can plan today's training intensity
  • If readiness is low, user understands which factor is driving it

---

2. Post-Ride Review Flow

**Goal:** Review the ride just completed, understand its impact on fitness/form.

**Time budget:** 2 to 5 minutes.

Steps

1. **Open app after a ride**

  • Lands on Glance page (L1)
  • Latest Activity card shows the ride just synced from Strava

2. **Tap the Latest Activity card**

  • Navigates directly to Ride Detail (L3)
  • See: route map placeholder, stats grid (distance, time, elevation, power, HR, suffer score)

3. **Review stats grid**

  • Key numbers: average power, average HR, suffer score, normalized power
  • Compare mentally to recent rides

4. **Scroll to HR chart**

  • Zone-coloured area chart showing HR across the ride
  • Zone distribution bars below: time in each zone

5. **Scroll to power chart** (if power meter used)

  • Power line with smoothed average and NP overlay
  • Check for consistency or fade towards the end

6. **Check PR badges** (if any set)

  • Small pastel accent pills: "Best 20min Power" etc.

7. **Tap back to Training domain** (via breadcrumb: Training > Morning Ride)

  • See how the ride affected CTL/ATL/TSB
  • Check whether CTL moved up, ATL spiked, TSB shifted

8. **Review training load chart**

  • Time range: 3M default
  • Check race day projection line
  • See whether on track for target CTL of 75

Exit conditions

  • User understands the quality and impact of the ride
  • User can see how the ride fits into the broader training picture
  • If a PR was set, user sees it acknowledged

---

3. Weekly Review Flow

**Goal:** Assess the week's progress across all domains, plan adjustments.

**Time budget:** 3 to 10 minutes.

Steps

1. **Open app (Sunday evening or Monday morning)**

  • Lands on Glance page (L1)
  • Scan weekly summary heatmap at the bottom: which days had training, rest, strength

2. **Review weekly volume**

  • Weekly Volume card: total km, total hours, number of rides, number of strength sessions
  • Compare to previous week and to plan

3. **Navigate to Training domain** (tap sidebar or bottom bar)

  • Review CTL/ATL/TSB chart with 1M or 3M range
  • Check: is CTL rising? Is form acceptable for this training phase?
  • Review weekly volume bars: are volumes consistent?
  • Check goals progress: CTL vs target, consistency score

4. **Navigate to Recovery domain**

  • Review sleep duration chart (7-day stacked bars)
  • Check: is sleep duration meeting the 7h30m goal? Are there patterns (bad nights after hard rides)?
  • Review resting HR trend: any spikes or downward trend?
  • Check readiness history: any low-readiness days that need explanation?

5. **Navigate to Body domain**

  • Review weight trend: is the overall trajectory towards 80kg goal?
  • Check weekly delta: how much change this week?
  • Review body composition if data available
  • Check adherence tracker: how many weigh-in days?

6. **Navigate to Nutrition domain**

  • Review calorie trend for the week: any missed days?
  • Check macro split: is protein consistently above 120g?
  • Review logging streak: is the habit forming?

7. **Navigate to Timeline domain**

  • Review Ozempic compliance grid: any missed doses?
  • Scan the annotated timeline for the week: any unusual patterns?
  • Check correlations: Ozempic vs weight trend

8. **Return to Glance**

  • Final check: readiness for tomorrow's planned session
  • Race countdown: note the week count

Exit conditions

  • User has a comprehensive view of the week across all five domains
  • User can identify what went well and what needs adjustment
  • User can plan the coming week informed by data

---

4. Log Meal Flow

**Goal:** Record a meal with estimated calories and macros.

**Time budget:** Under 30 seconds.

Steps

1. **Navigate to Nutrition domain**

  • Via sidebar (desktop) or bottom bar (mobile)
  • Or tap the Nutrition card on Glance page

2. **Tap "Log Meal" button**

  • Prominent CTA on the Nutrition page
  • Opens a meal entry form (modal on desktop, full page on mobile)

3. **Enter meal details**

  • Meal type selector: Breakfast, Lunch, Dinner, Snack
  • Description field: free text ("Chicken salad, rice, vegetables")
  • Estimated calories: number input with quick presets (300, 500, 700, 1000)
  • Optional: estimated macros (protein, carbs, fat in grams)
  • Optional: photo reference (upload or camera)
  • Optional: notes

4. **Confirm entry**

  • Tap "Save" or "Log"
  • Entry appears in today's food log list
  • Daily calorie total updates
  • Macro donut chart updates

5. **Return to previous view**

  • If came from Glance, return to Glance (updated Nutrition card)
  • If already on Nutrition page, stay there with updated list

Exit conditions

  • Meal is recorded with at minimum: type, description, estimated calories
  • Daily totals reflect the new entry
  • Logging streak counter increments if this is the first entry today

Empty state trigger

  • If no meals logged today, the Nutrition card on Glance shows "No meals logged today. Tap to add."
  • The Nutrition domain page shows the Log Meal CTA prominently with an encouraging empty state

---

5. Log Meds Flow

**Goal:** Record an Ozempic dose.

**Time budget:** Under 15 seconds.

Steps

1. **Navigate to Timeline domain**

  • Via sidebar (desktop) or bottom bar (mobile)
  • Or tap "Log Meds" button on the Nutrition section of Glance page

2. **Tap "Log Meds" button**

  • Prominent CTA on the Timeline page
  • Opens a dose entry form (compact modal or inline form)

3. **Confirm dose details**

  • Date: defaults to today (editable)
  • Medication: Ozempic (pre-selected, single medication for now)
  • Dose: dropdown or quick selector (0.25 mg, 0.5 mg, 1.0 mg, 1.5 mg, 2.0 mg)
  • Defaults to the user's current dose level (0.5 mg)
  • Optional: notes field

4. **Tap "Log" to confirm**

  • Entry appears in the timeline
  • Ozempic compliance grid updates (today's cell fills in)
  • Streak counter updates

5. **Visual confirmation**

  • Brief success indicator (checkmark animation or toast notification)
  • Compliance grid shows the logged dose
  • If this completes a full week, a subtle "full compliance" indicator appears

Exit conditions

  • Dose is recorded with date and amount
  • Compliance grid reflects the new entry
  • Timeline shows the dose event

Empty state trigger

  • If no dose logged this week, the Ozempic section shows "No dose logged this week. Tap to log."
  • The compliance grid shows empty cells with a gentle prompt

---

Flow Transitions Summary

| Flow | Entry Point | Primary Layer | Deepest Layer | Key Action |

|------|------------|---------------|---------------|------------|

| Morning Check | Glance (L1) | L1 | L3 (Sleep Detail) | Check readiness, plan day |

| Post-Ride Review | Glance (L1) | L3 (Ride Detail) | L3 | Review ride, check fitness impact |

| Weekly Review | Glance (L1) | L2 (all domains) | L3 (optional) | Assess week, plan adjustments |

| Log Meal | Nutrition (L2) | L2 | L2 (form overlay) | Record meal, update daily totals |

| Log Meds | Timeline (L2) | L2 | L2 (form overlay) | Record Ozempic dose, update compliance |

---

Navigation Shortcuts

All flows assume the user can switch between any domain at any time using the persistent navigation (sidebar or bottom bar). No flow requires going "back to home first" to reach another domain. This is a key benefit of the persistent navigation model.

Quick actions available from multiple entry points:

  • **Log Meal:** Nutrition domain CTA, Glance nutrition card
  • **Log Meds:** Timeline domain CTA, Glance nutrition section "Log Meds" button
  • **Check Readiness:** Always visible as hero card on Glance, also on Recovery domain
  • **Race Countdown:** Always visible in top bar (desktop) or as compact chip

Flow: Morning plan and forecast check

Goal: user sees today’s plan, understands why, and sees plan vs actual progress.

1. Open dashboard

2. See Readiness and Today’s Plan card

3. Optional: tap Plan vs Actual strip to open Training or Body domain

4. If plan is acceptable: start day, no interaction

5. If plan needs change: tap Adjust Plan

  • Shorten ride
  • Swap ride type
  • Swap strength A, strength B, mobility
  • Record knee pain score

6. Tap Why to open Plan rationale

7. Later: mark Ride and Strength as done

Success criteria:

  • Plan is visible without scrolling on mobile
  • Why is transparent, factors and rules are shown
  • Forecast bands and variance chips are readable at a glance

Wireframes

docs/spike-ux-dashboard/wireframes.md

Wireframes: Data Hub Dashboard

SPIKE-010 deliverable. Text wireframes for every page in the three-layer progressive disclosure model: Glance, Domain, and Detail.

All wireframes assume a 12-column CSS Grid at 1200px max width, dark mode with pastel accents on deep charcoal.

Notation:

  • [ ... ] = interactive element (button, link, control)
  • +--+ | +--+ = box-drawing for cards and containers
  • (( ... )) = chart or visualization area
  • < ... > = navigation element
  • { ... } = dynamic/computed value
  • === = section divider
  • Columns noted as c1-c12 for grid placement
  • // ... = annotation or responsive note

---

A. Glance Page (Home Dashboard)

The primary view. Everything above the fold answers "How am I doing right now?" in under 10 seconds. The readiness score is the hero element.

+---------------------------------------------------------------------+
|  TOP BAR (64px)                                              c1-c12  |
|                                                                      |
|  Data Hub          < << 22 Feb 2026 >> >        [ 97 days to race ] |
|  (wordmark)         (date navigator)             (countdown chip)    |
+---------------------------------------------------------------------+

// Date nav: tap arrows to shift day. Tap date for calendar picker.
// Countdown chip: pastel accent bg, days until Race to the Sun.
// On mobile: wordmark collapses to icon, countdown moves below date.

==========================================================================

+---------------------------------------------------------------------+
|  READINESS SCORE HERO CARD (160px)                           c1-c12  |
|                                                                      |
|   +--------------------------------------------------------------+  |
|   |                                                              |  |
|   |        .--------.                                            |  |
|   |       /          \    Ready to Train                         |  |
|   |      |     72     |   "Solid recovery overnight.             |  |
|   |      |   /100     |    CTL trending up."                     |  |
|   |       \          /                                           |  |
|   |        '--------'    {status label}                          |  |
|   |       (color ring,   {one-line insight}                      |  |
|   |        gradient fill                                         |  |
|   |        based on score)                                       |  |
|   |                                                              |  |
|   |   Factors:  Sleep 8/10  |  HRV 7/10  |  Form +4  |  Rest 1d |  |
|   |             (mini factor pills, pastel tinted)               |  |
|   |                                                              |  |
|   +--------------------------------------------------------------+  |
|                                                                      |
+---------------------------------------------------------------------+

// Readiness ring: circular progress, gradient from red (0-30) to
//   yellow (30-60) to green (60-100), all pastel muted tones.
// Score number: --text-3xl (32px), extra bold.
// Status label: "Ready to Train" / "Take It Easy" / "Recovery Day".
// Factor pills: small rounded badges with pastel bg tints.
// On mobile: ring above text, stacked vertically. Full width card.

==========================================================================

+---------------------------------------------------------------------+
|  DAILY SNAPSHOT ROW (120px)                                  c1-c12  |
|                                                                      |
|  +--------+ +--------+ +--------+ +--------+ +--------+            |
|  | Weight | |Body Fat| | Steps  | |  RHR   | |  Form  |            |
|  |  83.2  | | 22.1%  | | 8,421  | |  58    | |  +4    |            |
|  | -0.3kg | | -0.2%  | |  ....  | |  ....  | | Fresh  |            |
|  |   v    | |   v    | |(spark) | |(spark) | |(badge) |            |
|  +--------+ +--------+ +--------+ +--------+ +--------+            |
|   c1-c2      c3-c4      c5-c7      c8-c9     c10-c12               |
+---------------------------------------------------------------------+

// Each card: metric label (--text-sm, secondary color),
//   value (--text-xl, bold), delta or sparkline, trend arrow.
// Delta colors: green = improving, red = declining, neutral = stable.
// Cards are clickable, drill down to respective Domain page.
// On mobile: 2-column grid, Form card spans full width below.

==========================================================================

+---------------------------------------------------------------------+
|  TRAINING LOAD CHART (280px)                                 c1-c12  |
|                                                                      |
|  Fitness and Form              [ 1W  1M  3M  6M  1Y  All ]         |
|  CTL 62 | ATL 58 | TSB +4      (time range segmented control)       |
|                                                                      |
|  ((                                                              ))  |
|  ((  .-------.                                          : RACE   ))  |
|  (( /  CTL   \-----------.  .                           :  DAY   ))  |
|  ((/  (area)   \         / \  \------                   :        ))  |
|  ((              \-------/                              :        ))  |
|  ((                                                     :        ))  |
|  ((  ---- ATL (line, dashed) ----                       :        ))  |
|  ((                                                     :        ))  |
|  ((  #### TSB (diverging area, +green / -red) ####      :        ))  |
|  ((                                                     :        ))  |
|  ((  Jan    Feb    Mar    Apr    May                     :        ))  |
|  ((                                                              ))  |
|                                                                      |
+---------------------------------------------------------------------+

// Time range control: top-right, 6 presets. Active uses --accent bg.
// On mobile: chart height shrinks to 200px, fewer x-axis labels.

==========================================================================

+---------------------------------------------------------------------+
|  ACTIVITY FEED (variable height)                             c1-c12  |
|                                                                      |
|  Recent Activities                              [ View All > ]      |
|                                                                      |
|  +----------------------------------------------------------------+ |
|  | Morning Ride                               22 Feb, 07:30       | |
|  | 42.3 km  |  1:45:00  |  187W avg  |  142 bpm  |  SS 78        | |
|  +----------------------------------------------------------------+ |
|  +----------------------------------------------------------------+ |
|  | Recovery Spin                              21 Feb, 17:00       | |
|  | 18.1 km  |  0:45:00  |  112W avg  |  118 bpm  |  SS 32        | |
|  +----------------------------------------------------------------+ |
|  +----------------------------------------------------------------+ |
|  | Strength: Quad Rehab                       21 Feb, 09:00       | |
|  | 0:35:00  |  Leg extensions, step-ups, wall sits                | |
|  +----------------------------------------------------------------+ |
|                                                                      |
+---------------------------------------------------------------------+

// Cards are clickable, drill to Ride Detail.
// Show 3 most recent on Glance. Full list on Training domain.

==========================================================================

+----------------------------------+-----------------------------------+
|  BODY COMPOSITION (c1-c6)        |  SLEEP (c7-c12)                   |
|                                  |                                   |
|  Weight Trend                    |  Last Night                       |
|  83.2 kg  (-0.3 this wk)        |  7h 42m  Quality: 82/100          |
|                                  |                                   |
|  ((                          ))  |  +-----------------------------+  |
|  ((   \                      ))  |  | Deep    ####  1h 20m       |  |
|  ((    \                     ))  |  | Light   ######  3h         |  |
|  ((     \___/ \___           ))  |  | REM     #####  2h          |  |
|  ((               \--        ))  |  | Awake   ##  0h 22m         |  |
|  ((  goal ............       ))  |  +-----------------------------+  |
|  ((                          ))  |                                   |
|  ((  Jan  Feb  Mar           ))  |  7-Day Trend                      |
|                                  |  ((  ....###..  ))                |
|  Body Fat: 22.1%                 |  ((  M T W T F S S  ))           |
|  [View Details >]                |  [View Details >]                 |
|                                  |                                   |
+----------------------------------+-----------------------------------+

// On mobile: stacked vertically, full width each.

==========================================================================

+----------------------------------+-----------------------------------+
|  HEART RATE (c1-c6)              |  NUTRITION (c7-c12)               |
|                                  |                                   |
|  Today's HR                      |  Today's Food                     |
|  RHR: 58 bpm                    |  1,840 / 2,200 kcal              |
|                                  |  [360 remaining]                  |
|                                  |                                   |
|  ((                          ))  |  +-----------------------------+  |
|  ((  HR timeline             ))  |  | Breakfast       520 kcal   |  |
|  ((  zone-colored area       ))  |  | Lunch           680 kcal   |  |
|  ((  06  09  12  15  18      ))  |  | Snack           240 kcal   |  |
|  ((                          ))  |  | Dinner          400 kcal   |  |
|                                  |  +-----------------------------+  |
|  Zone Distribution               |                                   |
|  [Z1 ============= 45%]         |  Macros                           |
|  [Z2 ========= 28%]             |  (( P 32% C 45% F 23% ))        |
|  [Z3 ====== 18%]                |                                   |
|  [Z4 == 6%]                     |  Ozempic: 0.5mg (today)          |
|  [Z5 = 3%]                      |  [Log Meal] [Log Meds]            |
|  [View Details >]                |  [View Details >]                 |
|                                  |                                   |
+----------------------------------+-----------------------------------+

// On mobile: stacked vertically, full width each.

==========================================================================

+---------------------------------------------------------------------+
|  WEEKLY SUMMARY HEATMAP (80px)                               c1-c12  |
|                                                                      |
|  This Week                                                          |
|  +------+------+------+------+------+------+------+                |
|  |  Mon |  Tue |  Wed |  Thu |  Fri |  Sat |  Sun |                |
|  | Ride | Str  | Rest | Ride | Str  | Ride | Rest |                |
|  | 42km | Rehab|      | 35km | Rehab| 65km |      |                |
|  | ###  | ##   | ..   | ##   | ##   | ###  | ..   |                |
|  +------+------+------+------+------+------+------+                |
|                                                                      |
|  Total: 142 km  |  4h 30m  |  3 rides  |  2 strength               |
|                                                                      |
+---------------------------------------------------------------------+

// On mobile: 7 narrow columns, icons only, no text labels.

---

B. Domain Pages

B1. Training Domain Page

+---------------------------------------------------------------------+
|  TRAINING HEADER                                             c3-c12  |
|  Training Overview                       [ 1W  1M  3M  6M  1Y  All] |
|  CTL {62}  ATL {58}  TSB {+4}  Phase: Build                        |
+---------------------------------------------------------------------+

+---------------------------------------------------------------------+
|  FITNESS AND FORM CHART (360px)                              c3-c12  |
|  ((  Full CTL/ATL/TSB chart with phase markers and race line    ))  |
|  ((  Crosshair synced with activity list below                  ))  |
+---------------------------------------------------------------------+

+----------------------------------+-----------------------------------+
|  WEEKLY VOLUME BARS (c3-c7)      |  GOALS PROGRESS (c8-c12)          |
|  ((  Stacked bars: hours, km ))  |  CTL: 62 / 75 target [====...83%] |
|  ((  W1 W2 W3 W4 W5 W6 W7   ))  |  Consistency: 4/5 sessions [80%]  |
+----------------------------------+-----------------------------------+

+---------------------------------------------------------------------+
|  ACTIVITY LIST                                               c3-c12  |
|  | Date     | Activity        | Dist  | Time  | Power | SS  |      |
|  | 22 Feb   | Morning Ride    | 42 km | 1:45  | 187W  | 78  |      |
|  | 21 Feb   | Recovery Spin   | 18 km | 0:45  | 112W  | 32  |      |
|  | 21 Feb   | Quad Rehab      |  --   | 0:35  |  --   | --  |      |
|  [ Load More ]                                                      |
+---------------------------------------------------------------------+

B2. Recovery Domain Page

+---------------------------------------------------------------------+
|  RECOVERY HEADER                                             c3-c12  |
|  Recovery Score: {82/100}  Sleep: {7h 42m}  RHR: {58}              |
+---------------------------------------------------------------------+

+---------------------------------------------------------------------+
|  SLEEP DURATION CHART (280px)                                c3-c12  |
|  ((  Stacked bars: Deep / Light / REM / Awake, 7-day            ))  |
|  ((  Goal line: 7h 30m                                           ))  |
+---------------------------------------------------------------------+

+----------------------------------+-----------------------------------+
|  LAST NIGHT (c3-c7)              |  HR TREND (c8-c12)                |
|  7h 42m, Quality 82/100         |  Resting HR: 58 bpm              |
|  Hypnogram (stepped area)        |  ((  30-day trend line         ))  |
|  Bed: 22:15  Wake: 06:02        |  7-day avg: 57  30-day avg: 59   |
+----------------------------------+-----------------------------------+

+---------------------------------------------------------------------+
|  RECOVERY SCORE BREAKDOWN                                    c3-c12  |
|  | Sleep 8/10 | HRV 7/10 | RHR 8/10 | Rest 6/10 | Training 7/10 | |
|  (each factor: small ring gauge, label, score)                      |
+---------------------------------------------------------------------+

B3. Body Domain Page

+---------------------------------------------------------------------+
|  BODY HEADER                                                 c3-c12  |
|  Weight: {83.2 kg}  Body Fat: {22.1%}  Goal: {80 kg}              |
+---------------------------------------------------------------------+

+---------------------------------------------------------------------+
|  WEIGHT TREND CHART (320px)                                  c3-c12  |
|  ((  Weight line, goal line (dashed), Ozempic dose markers      ))  |
+---------------------------------------------------------------------+

+----------------------------------+-----------------------------------+
|  BODY COMP BREAKDOWN (c3-c7)     |  WEEKLY DELTA (c8-c12)            |
|  Muscle 38.2kg, Fat 18.4kg      |  Weight -0.3kg, BF -0.2%         |
|  Bone 3.2kg, Water 23.4kg       |  Rate: -0.4 kg/week              |
|  BMI: 25.8                      |  Projected goal: 22 Apr           |
+----------------------------------+-----------------------------------+

+---------------------------------------------------------------------+
|  ADHERENCE TRACKER (28-day grid: logged vs missed)           c3-c12  |
|  24/28 days (86%)                                                   |
+---------------------------------------------------------------------+

B4. Nutrition Domain Page

+---------------------------------------------------------------------+
|  NUTRITION HEADER                                            c3-c12  |
|  Today: {1,840 / 2,200 kcal}  Protein: {128g}  Streak: {5 days}  |
+---------------------------------------------------------------------+

+----------------------------------+-----------------------------------+
|  DAILY FOOD LOG (c3-c7)          |  MACRO CHARTS (c8-c12)            |
|  Breakfast 520 kcal              |  ((  Donut: P 32% C 45% F 23% ))  |
|  Lunch 680 kcal                  |  Protein 128/140g [=====.]        |
|  Snack 240 kcal                  |  Carbs 208/250g [=====.]          |
|  Dinner 400 kcal                 |  Fat 47/60g [====..]              |
|  [ Log Meal ]                    |                                   |
+----------------------------------+-----------------------------------+

+---------------------------------------------------------------------+
|  CALORIE TREND (daily bars vs target line)                   c3-c12  |
+---------------------------------------------------------------------+

+---------------------------------------------------------------------+
|  Ozempic Grid + Logging Streak  |  [ Log Meds ]             c3-c12  |
+---------------------------------------------------------------------+

B5. Timeline Domain Page

+---------------------------------------------------------------------+
|  TIMELINE HEADER                                             c3-c12  |
|  Annotated view of all events, doses, and correlations              |
+---------------------------------------------------------------------+

+---------------------------------------------------------------------+
|  ANNOTATED TIMELINE (scrollable)                             c3-c12  |
|  22 Feb -- Ride: Morning Ride (42 km, SS 78)                       |
|  21 Feb -- Recovery Spin, Quad Rehab, Scale: 83.2 kg               |
|  20 Feb -- Tempo Intervals, Ozempic 0.5 mg, Food: 2,180 kcal      |
|  19 Feb -- Rest Day, Scale: 83.5 kg                                |
|  ...     [ Load More ]                                              |
+---------------------------------------------------------------------+

+----------------------------------+-----------------------------------+
|  OZEMPIC DOSE HISTORY (c3-c7)    |  CORRELATIONS (c8-c12)            |
|  ((  Dot plot over months     ))  |  Ozempic vs Weight r=-0.82        |
|  Total: 18 doses, 0.5mg now     |  Training vs Sleep r=+0.64        |
+----------------------------------+-----------------------------------+

---

C. Detail Drilldowns

Detail views open as slide-over panels (400px) on desktop, full page on mobile.

C1. Sleep Detail

+--------------------------------------+
|  [ <- Recovery ]         [ x Close ] |
|  Sleep Detail, 22 Feb 2026           |
|                                      |
|  Duration: 7h 42m  Quality: 82/100  |
|  Bed: 22:15  Wake: 06:02            |
|                                      |
|  HYPNOGRAM                           |
|  (( Awake/REM/Light/Deep stages  ))  |
|  (( 22:30  00:00  02:00  04:00   ))  |
|                                      |
|  STAGES: Deep 1h20m (17%)           |
|  Light 3h (39%), REM 2h (26%)       |
|  Awake 22m (5%)                      |
|                                      |
|  VS AVERAGE: Duration +27m,         |
|  Deep +15m, Quality +6              |
+--------------------------------------+

C2. HR Detail

+--------------------------------------+
|  [ <- Recovery ]         [ x Close ] |
|  Heart Rate, 22 Feb 2026            |
|                                      |
|  RHR: 58  Max: 178  Avg: 72        |
|                                      |
|  DAILY HR TIMELINE                   |
|  (( Zone-colored area chart      ))  |
|                                      |
|  ZONES: Z1 68%, Z2 18%, Z3 8%      |
|  Z4 4%, Z5 2%                        |
|                                      |
|  RHR TREND (30 days)                 |
|  (( Line chart                   ))  |
+--------------------------------------+

C3. CTL/ATL/TSB Detail

+--------------------------------------+
|  [ <- Training ]         [ x Close ] |
|  Fitness and Form                    |
|                                      |
|  CTL: 62  ATL: 58  TSB: +4         |
|                                      |
|  FULL CHART with phase markers       |
|  (( Interactive CTL/ATL/TSB      ))  |
|                                      |
|  PHASES: Base > Build > Peak > Race  |
|  PROJECTION: CTL at race: 71        |
|  Target: 75  Gap: -4                |
+--------------------------------------+

C4. Weight Detail

+--------------------------------------+
|  [ <- Body ]             [ x Close ] |
|  Weight Detail                       |
|                                      |
|  Current: 83.2  Goal: 80.0         |
|  To Go: 3.2 kg  BMI: 25.8          |
|                                      |
|  WEIGHT HISTORY                      |
|  (( Full trend + goal + Ozempic  ))  |
|                                      |
|  BODY COMP TRENDS                    |
|  (( Muscle, fat, water lines     ))  |
|                                      |
|  RATE: -0.4 kg/week                 |
|  Projected goal: 22 Apr 2026        |
+--------------------------------------+

C5. Ride Detail

+--------------------------------------+
|  [ <- Training ]         [ x Close ] |
|  Morning Ride, 22 Feb 07:30         |
|                                      |
|  MAP PLACEHOLDER                     |
|  (( Route on dark tiles          ))  |
|                                      |
|  STATS: 42.3km  1:45  520m elev    |
|  187W avg  142bpm  SS 78            |
|                                      |
|  HR OVER TIME                        |
|  (( Zone-colored area            ))  |
|                                      |
|  POWER OVER TIME                     |
|  (( Line with NP overlay         ))  |
|                                      |
|  PR BADGES (if any)                  |
+--------------------------------------+

---

Responsive Summary

| Breakpoint | Layout | Nav | Detail View | Chart Height |

|------------|--------|-----|-------------|-------------|

| Desktop 1024px+ | 12-column grid | Sidebar | 400px slide-over | 280-360px |

| Tablet 480-768px | 2-column | Top tabs | 320px slide-over | 240px |

| Phone <480px | Single column | Bottom tabs (56px) | Full page | 200px |

Universal: 16px card gap, 24px section gap, 44px touch targets, readiness hero always above fold.

Today Plan and Forecast (New Surfaces)

This section adds the missing coach surfaces for "what should I do today" and "plan vs actual".

Glance: Today Plan card

Layout intent: a single, actionable card that prescribes today’s ride and strength or mobility.

┌────────────────────────────────────────────────────────────┐
│ TODAY’S PLAN                               Readiness 74    │
├────────────────────────────────────────────────────────────┤
│ Ride: Z2 Endurance                         60 to 90 min    │
│ Target: easy steady, conversational pace                   │
│ Why: Sleep ok, Fatigue high, keep consistency              │
│                                                            │
│ Strength: Mobility only                    12 to 15 min    │
│ Knee note: avoid kneeling, stairs slow and controlled      │
│                                                            │
│ [Mark Ride Done]   [Mark Strength Done]     [Adjust Plan]   │
└────────────────────────────────────────────────────────────┘

Adjust Plan opens a modal:

  • Shorten duration
  • Swap ride type (Z2, tempo, intervals, long)
  • Swap strength A, strength B, mobility
  • Add pain score for knees (0 to 10)

Glance: Plan vs Actual strip (forecast band)

Show forecast as a band, not a single line.

┌──────────────────────────┐ ┌──────────────────────────┐
│ WEIGHT: PLAN vs ACTUAL    │ │ FITNESS (CTL): PLAN vs ACT│
│ target band  ┈┈┈┈┈┈┈┈┈┈    │ │ target band  ┈┈┈┈┈┈┈┈┈┈  │
│ actual line  ━━━●━━●━━     │ │ actual line  ━━━╸━━╸━━   │
│ Δ 7d: -0.6 kg  on track   │ │ Δ 7d: +2.1   on track    │
└──────────────────────────┘ └──────────────────────────┘

┌──────────────────────────┐ ┌──────────────────────────┐
│ FATIGUE (ATL): PLAN vs ACT│ │ FORM (TSB): PLAN vs ACT   │
│ target band  ┈┈┈┈┈┈┈┈┈┈   │ │ target band  ┈┈┈┈┈┈┈┈┈┈  │
│ actual line  ━━━╸━━╸━━     │ │ actual line  ━━━╸━━╸━━   │
│ risk: elevated fatigue     │ │ taper readiness: building │
└──────────────────────────┘ └──────────────────────────┘

Rules:

  • Target is a band with upper and lower rails.
  • Actual is a line.
  • Variance chip: ahead, on track, behind.
  • Confidence: based on data freshness and coverage.

Detail: Plan rationale page

┌────────────────────────────────────────────────────────────┐
│ WHY THIS PLAN TODAY                                         │
├────────────────────────────────────────────────────────────┤
│ Readiness: 74 (High confidence)                             │
│ Factors: Sleep 7.7h, HR trend stable, Fatigue elevated      │
│                                                            │
│ Applied rules:                                              │
│ 1) If Fatigue high and Sleep ok, prescribe Z2 not intervals │
│ 2) If knee pain > 3, replace strength with mobility         │
│                                                            │
│ Expected outcomes (forecast):                               │
│ - CTL trend within band by end of week                      │
│ - ATL returns into safe band within 48h                     │
│ - Weight trend stays within band                            │
└────────────────────────────────────────────────────────────┘

Readiness Score UX Spec

docs/spike-ux-dashboard/readiness-spec.md

Readiness Score UX Specification

**Spike:** SPIKE-010, UX Dashboard Wireframes

**Author:** UX Researcher

**Date:** 2026-02-22

**Status:** Complete

---

1. Overview

The readiness score is a daily computed value from 0 to 100 that answers: "Should I train hard today or take it easy?" It is the first-class hero element on the Glance page, the single most prominent piece of information in the entire dashboard.

The score synthesises multiple data sources into one actionable number. It is not a medical metric. It is a personal heuristic that improves as more data flows in.

---

2. Score Range and Colour Coding

| Range | Label | Colour | Guidance |

|-------|-------|--------|----------|

| 80 to 100 | Excellent | Pastel green (#A8E6CF) | Full intensity training. Body is well recovered. |

| 60 to 79 | Good | Pastel teal (#88D8B0) | Normal training. Proceed as planned. |

| 40 to 59 | Moderate | Pastel amber (#FFD3B6) | Reduce intensity. Consider an easy day or active recovery. |

| 20 to 39 | Low | Pastel coral (#FF8B94) | Rest or very light activity only. Check contributing factors. |

| 0 to 19 | Very Low | Pastel red (#FFAAA5) | Rest strongly recommended. Something significant is off. |

The colour applies to:

  • The ring/gauge around the score number
  • The background tint of the hero card (very subtle, 8% opacity)
  • The status label text colour

---

3. Input Factors

Four primary factors contribute to the readiness score. Each factor produces a sub-score from 0 to 10.

3.1 Sleep Quality (weight: 30%)

**Inputs:**

  • Total sleep duration (from Google Fit sleep sessions)
  • Deep sleep percentage
  • Sleep consistency (bedtime variance over 7 days)
  • Time to fall asleep

**Scoring logic:**

  • 7.5h+ sleep, deep > 15%, consistent bedtime: 9 to 10
  • 7h+ sleep, deep > 10%: 7 to 8
  • 6h+ sleep: 4 to 6
  • Under 6h or very fragmented: 0 to 3

**Data source:** Google Fit sleep sessions and segments

3.2 Resting Heart Rate (weight: 25%)

**Inputs:**

  • Today's resting HR (morning reading or overnight minimum)
  • 7-day RHR rolling average
  • Deviation from personal baseline (30-day average)

**Scoring logic:**

  • RHR at or below 7-day average: 8 to 10
  • RHR 1 to 3 bpm above average: 5 to 7
  • RHR 4+ bpm above average: 0 to 4

**Data source:** Google Fit HR samples (overnight/morning window)

3.3 Training Strain (weight: 25%)

**Inputs:**

  • Yesterday's relative effort or suffer score (from Strava)
  • Acute training load (ATL, 7-day rolling average)
  • Ratio of ATL to CTL (acute:chronic workload ratio)

**Scoring logic:**

  • Rest day yesterday, ACWR under 1.0: 9 to 10
  • Light session yesterday, ACWR 0.8 to 1.2: 6 to 8
  • Hard session yesterday, ACWR 1.2 to 1.5: 3 to 5
  • Very hard session, ACWR above 1.5: 0 to 2

**Data source:** Strava activities (suffer score, TSS), computed CTL/ATL

3.4 Consistency (weight: 20%)

**Inputs:**

  • Days since last rest day
  • Training adherence this week (sessions completed vs planned)
  • Recent training load trend (monotonic increase vs varied)

**Scoring logic:**

  • Had a rest day in last 3 days, on track with plan: 8 to 10
  • 4 to 5 consecutive training days: 5 to 7
  • 6+ consecutive days without rest: 2 to 4
  • Very irregular pattern: 0 to 3

**Data source:** Strava activity history, training plan (if configured)

---

4. Score Computation

The overall readiness score is a weighted sum of the four factor sub-scores, scaled to 0 to 100:

readiness = (sleep * 0.30 + rhr * 0.25 + strain * 0.25 + consistency * 0.20) * 10

Each factor sub-score ranges from 0 to 10. The weighted sum ranges from 0 to 10, then multiplied by 10 to produce the 0 to 100 scale.

---

5. Factor Breakdown Display

On the Glance Page (Hero Card)

Factors appear as compact pills below the score:

Factors:  Sleep 8/10  |  RHR 7/10  |  Strain 6/10  |  Consistency 9/10

Each pill:

  • Background: pastel tint matching the factor's individual colour band (green/amber/red based on its sub-score)
  • Text: factor name and score
  • Tappable: navigates to the relevant detail (Sleep Detail, HR Detail, etc.)

On the Recovery Domain Page (Full Breakdown)

Each factor gets a card with:

  • Factor name and icon
  • Sub-score (0 to 10) with a small ring gauge
  • Contributing metrics listed below (e.g. "Sleep: 7h 42m, Deep: 18%, Consistency: good")
  • Trend indicator: improving, stable, or declining vs 7-day average
  • Tap to drill into the relevant detail page (L3)

---

6. Confidence Indicator

Readiness confidence depends on data completeness. Not all sources may be available.

Confidence Levels

| Sources Available | Confidence | Display |

|-------------------|------------|---------|

| All 4 factors have fresh data (under 24h) | High | Score shown normally. No indicator needed. |

| 3 of 4 factors have data | Medium | Score shown with "Based on 3 of 4 inputs" label below. Missing factor named. |

| 2 of 4 factors have data | Low | Score shown with "Limited data" label. Colour intensity reduced (50% opacity ring). Missing factors named. |

| 1 or 0 factors have data | Insufficient | Score not computed. Empty state shown instead (see section 7). |

Visual Treatment

  • **High confidence:** Full colour ring, no extra label
  • **Medium confidence:** Full colour ring with a subtle dashed segment where the missing factor would be. Text: "Missing: [factor name]. Score is approximate."
  • **Low confidence:** Ring at 50% opacity. Text: "Limited data. Missing: [factor names]. Connect more sources for a reliable score."
  • **Insufficient:** No ring, no score. See empty state below.

---

7. Empty States

No Score Available (Insufficient Data)

Displayed when fewer than 2 factors have data.

+--------------------------------------------------------------+
|                                                              |
|         .--------.                                           |
|        /          \                                          |
|       |     --     |    Readiness score needs data           |
|       |            |                                         |
|        \          /     Connect at least two of these:       |
|         '--------'      [ ] Sleep data (Google Fit)          |
|        (empty ring,     [ ] Heart rate (Google Fit)          |
|         dashed border)  [ ] Activities (Strava)              |
|                         [ ] Training plan                    |
|                                                              |
+--------------------------------------------------------------+

Elements:

  • Empty ring with dashed border (muted grey)
  • "--" where the score would be
  • Friendly heading: "Readiness score needs data"
  • Checklist of required data sources with connection status
  • Each unchecked item links to the relevant connector setup (future feature)

Sleep Data Missing

When sleep data specifically is missing but other factors are available:

  • Readiness score computes with remaining factors, re-weighted
  • Sleep factor pill shows: "Sleep: no data" in muted text
  • Confidence indicator: "Based on 3 of 4 inputs. Missing: Sleep."
  • Recovery domain shows: "No sleep data recorded. Wear your device to bed for sleep tracking."

HR Data Missing

  • RHR factor uses last known value (if under 48h old) with "stale" indicator
  • If no HR data at all: factor pill shows "RHR: no data"
  • Confidence adjusts accordingly

No Recent Activities

  • Strain factor defaults to "rest day" assumption (sub-score 9 to 10)
  • Factor pill: "Strain: rest (no recent activity)"
  • This is a valid state, not an error. Rest is good.

All Data Stale (over 48h)

  • Score still displays but with a prominent staleness banner
  • Ring colour muted (50% opacity)
  • Text: "Score based on data from [date]. Sync your devices for an updated score."

---

8. Hero Card Layout (Glance Page)

The readiness hero card is the largest and most prominent element on the dashboard.

Desktop Layout (1024px+)

+---------------------------------------------------------------------+
|                                                                      |
|   +--[Ring]--+     Ready to Train                                   |
|   |          |                                                       |
|   |    72    |     "Solid recovery overnight. CTL trending up."      |
|   |   /100   |                                                       |
|   +----------+     Sleep 8/10  |  RHR 7/10  |  Strain 6/10  |  ... |
|                                                                      |
+---------------------------------------------------------------------+
  • Ring on the left (120px diameter)
  • Score number inside ring: 32px, extra bold, tabular nums
  • Status label to the right: 20px, semibold
  • One-line insight below status: 14px, secondary text colour
  • Factor pills below insight: 12px, pastel-tinted badges

Mobile Layout (under 480px)

+----------------------------------+
|                                  |
|        .--------.                |
|       |    72    |               |
|       |   /100   |               |
|        '--------'                |
|                                  |
|     Ready to Train               |
|     "Solid recovery overnight."  |
|                                  |
|  Sleep 8  RHR 7  Strain 6  C 9  |
|                                  |
+----------------------------------+
  • Ring centred, above text
  • Status label centred below ring
  • Insight text centred
  • Factor pills in a single row, abbreviated labels

---

9. Drilldown Detail (Recovery Domain)

When the user taps the readiness hero card, they navigate to the Recovery domain page. The readiness breakdown is the first section.

Full Breakdown Section

+---------------------------------------------------------------------+
|  Readiness Score: 72/100                              22 Feb 2026   |
|                                                                      |
|  +----------+----------+----------+----------+                      |
|  | Sleep    | RHR      | Strain   | Consist. |                      |
|  | 8/10     | 7/10     | 6/10     | 9/10     |                      |
|  | (ring)   | (ring)   | (ring)   | (ring)   |                      |
|  | 7h 42m   | 58 bpm   | SS 78    | Rest 1d  |                      |
|  | Deep 18% | vs 57avg | ACWR 1.1 | 4/5 sess |                      |
|  +----------+----------+----------+----------+                      |
|                                                                      |
|  Trend: Readiness improving over 7 days (avg 68 -> 72)              |
|  Confidence: High (all sources fresh)                                |
|                                                                      |
+---------------------------------------------------------------------+

Each factor card:

  • Small ring gauge (48px) with colour matching the sub-score
  • Factor name and score
  • Key contributing metric
  • Secondary metric
  • Tappable: links to relevant L3 detail page

Historical Trend

Below the breakdown, a readiness history chart:

  • 30-day line chart of daily readiness scores
  • Colour-coded bands in the background (green zone, amber zone, red zone)
  • Annotations for notable events (hard training blocks, illness, rest weeks)
  • Time range controls: 1W, 1M, 3M

---

10. "One Big Thing" Insight

The readiness card includes a single-line insight that surfaces the most important factor driving today's score.

Logic

1. Identify the factor with the lowest sub-score

2. If that factor is 3+ points below the next lowest, it is "the big thing"

3. If all factors are within 2 points of each other, use the overall trend instead

Examples

| Scenario | Insight |

|----------|---------|

| Sleep factor is 3/10, all others above 7 | "Poor sleep is pulling your score down. Prioritise rest tonight." |

| RHR 4 bpm above average | "Resting heart rate is elevated. Take it easy today." |

| Hard ride yesterday, strain factor is 2/10 | "Yesterday's hard ride increased fatigue. An easy day would help recovery." |

| All factors above 7 | "Solid recovery across the board. Ready for a quality session." |

| CTL rising, form positive | "Fitness is building well. CTL trending up towards race target." |

| All factors above 8, near race | "Peak form. Trust the taper and stay sharp for race day." |

The insight is generated from templates, not free-form AI text. This keeps it predictable and trustworthy.

---

11. Score Update Behaviour

  • Score recalculates whenever new data arrives from any source
  • On the Glance page, the score updates in place without a full page reload
  • A subtle animation (number counter rolls up/down) indicates the score changed
  • The "last updated" timestamp refreshes
  • If the score changes by more than 10 points, the colour transition animates smoothly (ring gradient shifts)

Forecast integration

Readiness must be shown alongside forecast progress to make it actionable.

  • Readiness answers: can I push today?
  • Forecast answers: am I on track for the event?

Display rules:

  • Readiness card shows score plus factors and confidence
  • Plan vs Actual strip shows forecast bands and actuals for weight and CTL, plus ATL and TSB
  • If readiness is low but forecast is behind, UI should suggest a recovery safe alternative, not a hard push

Pastel Direction

docs/spike-ux-dashboard/pastel-direction.md

Pastel Design Direction: Data Hub Dashboard

SPIKE-010 deliverable. UX-level design tokens for the pastel theme. Dark mode only. Muted pastels on deep charcoal.

---

1. Design Philosophy

The Data Hub dashboard uses muted pastels layered onto deep charcoal surfaces. The goal is a calm, data-forward aesthetic that feels warm and personal, not clinical or corporate. Every color choice serves a purpose: either communicating data category, indicating status, or providing visual hierarchy.

Guiding principles:

  • **Calm, not clinical.** Soft tones that feel inviting. Avoid sterile whites or harsh saturated neons.
  • **Data-forward.** Color exists to make data readable. Decoration is minimal. The data is the interface.
  • **Muted, not washed out.** Pastels should have enough saturation to be distinguishable on dark backgrounds while remaining gentle on the eyes.
  • **Consistent domain coding.** Each data domain (Training, Recovery, Body, Nutrition, Meds) gets a dedicated color family. Users build intuition over time.
  • **Accessibility first.** All text/background combinations meet WCAG AA contrast (4.5:1 for normal text, 3:1 for large text). Pastel accents are never used as the sole indicator; pair with shape, position, or label.

---

2. Surface Tokens

Extending the existing Data Hub four-level surface system with pastel tint states for interactive elements.

Base Surfaces (unchanged from SPIKE-007)

| Token | Value | Usage |

|----------------|-----------|------------------------------------|

| --bg | #0f0f0f | Page background |

| --surface | #1a1a1a | Card surfaces, primary containers |

| --surface2 | #242424 | Elevated elements, inputs, popups |

| --surface3 | #2e2e2e | Hover states, active elements |

Pastel Tint Surfaces (new)

For interactive states that need to hint at a domain color without overwhelming the dark base.

| Token | Value | Usage |

|-------------------------|----------------------------|--------------------------------------|

| --surface-hover | #2e2e2e | Default hover on neutral cards |

| --surface-active | #333333 | Default active/pressed state |

| --surface-selected | rgba(108,99,255,0.08) | Selected card/row (accent tint) |

| --surface-training | rgba(129,140,248,0.06) | Training domain card hover |

| --surface-recovery | rgba(196,181,253,0.06) | Recovery domain card hover |

| --surface-body | rgba(251,191,178,0.06) | Body domain card hover |

| --surface-nutrition | rgba(167,223,186,0.06) | Nutrition domain card hover |

| --surface-meds | rgba(216,191,255,0.06) | Meds domain card hover |

Border Tokens

| Token | Value | Usage |

|-----------------|--------------------------|--------------------------------|

| --border | #333333 | Default card borders |

| --border2 | #444444 | Emphasized borders |

| --border-focus | rgba(108,99,255,0.5) | Focus ring color |

---

3. Text Tokens

| Token | Value | Contrast on --surface | Usage |

|-----------------|-----------|-----------------------|---------------------------------|

| --text | #e8e8e8 | 12.4:1 | Primary text, headings, values |

| --text2 | #a0a0a0 | 6.4:1 | Secondary text, labels, captions|

| --text3 | #6b6b6b | 3.2:1 | Tertiary text, hints, disabled |

| --text-disabled| #4a4a4a| 2.0:1 | Disabled controls, placeholders |

Notes:

  • --text3 passes WCAG AA for large text (>18px) only. Use at 14px+ with caution.
  • --text-disabled is intentionally below AA. Disabled elements should not draw attention.
  • Metric values always use --text for maximum readability.
  • Labels and units use --text2.

---

4. Accent Palette

Pastel variants of the SPIKE-007 accent colors. Each has a base, muted, and tint variant.

Primary Accent (Indigo)

| Token | Value | Usage |

|--------------------|--------------------------|------------------------------|

| --accent | #818cf8 | Primary actions, active nav |

| --accent-hover | #93a0ff | Hover on accent elements |

| --accent-muted | rgba(129,140,248,0.12) | Accent backgrounds, pills |

| --accent-subtle | rgba(129,140,248,0.06) | Selected row, faint highlight|

Green (shifted to pastel teal)

| Token | Value | Usage |

|--------------------|--------------------------|------------------------------|

| --green | #6ee7b7 | Positive trends, on track |

| --green-hover | #86efcb | Hover on green elements |

| --green-muted | rgba(110,231,183,0.12) | Green backgrounds, badges |

Yellow (shifted to pastel gold)

| Token | Value | Usage |

|--------------------|--------------------------|------------------------------|

| --yellow | #fde68a | Warnings, caution, behind |

| --yellow-hover | #fef0ad | Hover on yellow elements |

| --yellow-muted | rgba(253,230,138,0.12) | Yellow backgrounds, badges |

Red (shifted to pastel coral)

| Token | Value | Usage |

|--------------------|--------------------------|------------------------------|

| --red | #fca5a5 | Errors, at risk, declining |

| --red-hover | #fdb8b8 | Hover on red elements |

| --red-muted | rgba(252,165,165,0.12) | Red backgrounds, badges |

Comparison to SPIKE-007

| Color | SPIKE-007 (neon) | SPIKE-010 (pastel) | Change |

|---------|-------------------|--------------------|---------------------------|

| Accent | #6C63FF | #818cf8 | Lighter, less saturated |

| Green | #4ECDC4 | #6ee7b7 | Shifted warmer, softer |

| Yellow | #FFE66D | #fde68a | Slightly muted |

| Red | #FF6B6B | #fca5a5 | Significantly softened |

The pastel variants maintain the same hue families but reduce saturation and increase lightness. On the deep charcoal background, they appear gentle yet readable.

---

5. Semantic Colors

Status and feedback colors derived from the accent palette.

| Token | Value | Usage |

|--------------------|--------------------------|------------------------------|

| --success | #6ee7b7 | Success states, confirmations|

| --success-muted | rgba(110,231,183,0.12) | Success backgrounds |

| --warning | #fde68a | Warnings, needs attention |

| --warning-muted | rgba(253,230,138,0.12) | Warning backgrounds |

| --error | #fca5a5 | Errors, failures |

| --error-muted | rgba(252,165,165,0.12) | Error backgrounds |

| --info | #818cf8 | Informational, neutral tips |

| --info-muted | rgba(129,140,248,0.12) | Info backgrounds |

Rules for semantic color usage:

  • Never use --error for neutral data (e.g. a low step count is not an error).
  • Reserve red/green for scores and explicit status labels only.
  • Neutral data uses --text or --text2, not semantic colors.
  • Semantic backgrounds (muted variants) are used for badge/pill backgrounds, not card fills.

---

6. Chart Color Palette

Each data domain has a dedicated color family for charts. Colors are chosen to be distinguishable from each other when viewed side by side.

Training Domain

| Token | Value | Usage |

|------------------------|-----------|---------------------------------|

| --chart-ctl | #818cf8 | CTL (Fitness) area fill |

| --chart-ctl-line | #a5b4fc | CTL line stroke |

| --chart-atl | #fca5a5 | ATL (Fatigue) line |

| --chart-atl-dash | #fca5a5 | ATL dashed stroke |

| --chart-tsb-pos | #6ee7b7 | TSB positive area (above zero) |

| --chart-tsb-neg | #fca5a5 | TSB negative area (below zero) |

| --chart-power | #818cf8 | Power line in ride charts |

| --chart-suffer | #fca5a5 | Suffer score bar fill |

Recovery Domain

| Token | Value | Usage |

|------------------------|-----------|---------------------------------|

| --chart-sleep | #c4b5fd | Sleep duration bars |

| --chart-deep | #6366f1 | Deep sleep stage |

| --chart-light | #a5b4fc | Light sleep stage |

| --chart-rem | #c4b5fd | REM sleep stage |

| --chart-awake | #fde68a | Awake periods |

| --chart-rhr | #c4b5fd | Resting heart rate trend |

| --chart-recovery | #a78bfa | Recovery score ring/gauge |

Body Domain

| Token | Value | Usage |

|------------------------|-----------|---------------------------------|

| --chart-weight | #fbbfb2 | Weight trend line |

| --chart-bodyfat | #fdba74 | Body fat percentage line |

| --chart-muscle | #6ee7b7 | Muscle mass line |

| --chart-bone | #a0a0a0 | Bone mass (neutral) |

| --chart-water | #7dd3fc | Body water percentage |

| --chart-bmi | #fbbfb2 | BMI line (same family as weight)|

Nutrition Domain

| Token | Value | Usage |

|------------------------|-----------|---------------------------------|

| --chart-calories | #fde68a | Calorie bars |

| --chart-protein | #6ee7b7 | Protein in macro donut |

| --chart-carbs | #fde68a | Carbohydrates in macro donut |

| --chart-fat | #fbbfb2 | Fat in macro donut |

| --chart-fibre | #a7dfba | Fibre target bar |

| --chart-target-line | #6b6b6b | Calorie/macro target lines |

Medications Domain

| Token | Value | Usage |

|------------------------|-----------|---------------------------------|

| --chart-ozempic | #d8bfff | Ozempic dose markers/timeline |

| --chart-ozempic-bg | rgba(216,191,255,0.12) | Dose marker backgrounds|

| --chart-dose-low | #c4b5fd | Low dose (0.25 mg) marker |

| --chart-dose-high | #a78bfa | Higher dose (0.5 mg+) marker |

HR Zone Colors (all pastel)

| Token | Value | Zone |

|----------------|-----------|--------------------|

| --zone-1 | #7dd3fc | Recovery (Z1) |

| --zone-2 | #6ee7b7 | Endurance (Z2) |

| --zone-3 | #fde68a | Tempo (Z3) |

| --zone-4 | #fdba74 | Threshold (Z4) |

| --zone-5 | #fca5a5 | Max effort (Z5) |

Zone colors graduate from cool (blue) through warm (coral). Each is readable on --surface backgrounds.

---

7. Readiness Score Colors

The readiness score uses a continuous gradient mapped to three zones. All values are pastel.

| Range | Color | Label | Token |

|---------|-----------|----------------|----------------------|

| 0-30 | #fca5a5 | Poor | --readiness-low |

| 31-60 | #fde68a | Moderate | --readiness-mid |

| 61-100 | #6ee7b7 | Good | --readiness-high |

Ring Gradient Stops

The readiness ring uses a CSS conic gradient with smooth transitions:

--readiness-gradient: conic-gradient(
  from 0deg,
  var(--readiness-low) 0%,
  var(--readiness-mid) 40%,
  var(--readiness-high) 70%,
  var(--readiness-high) 100%
);

Factor Pills

Each readiness factor gets a pastel background tint:

| Factor | Background | Text Color |

|-----------|--------------------------------|------------|

| Sleep | rgba(196,181,253,0.15) | #c4b5fd |

| HRV | rgba(129,140,248,0.15) | #818cf8 |

| Form | rgba(110,231,183,0.15) | #6ee7b7 |

| Rest | rgba(253,230,138,0.15) | #fde68a |

| Training | rgba(129,140,248,0.15) | #818cf8 |

---

8. Typography Tokens

Extending the SPIKE-007 type scale. System font stack, Major Third ratio (1.25).

Font Stack

--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
--font-mono: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;

Weight Scale

| Token | Value | Usage |

|--------------------|-------|--------------------------------------|

| --font-regular | 400 | Body text, descriptions |

| --font-medium | 500 | Labels, secondary headings |

| --font-semibold | 600 | Card titles, table headers |

| --font-bold | 700 | Section headings, emphasis |

| --font-extrabold | 800 | Hero numbers, page titles |

Size Scale (Major Third, 1.25 ratio)

| Token | Size | Line Height | Usage |

|----------------|-------|-------------|--------------------------------|

| --text-3xl | 32px | 1.1 | Hero score number, page titles |

| --text-2xl | 26px | 1.2 | Section headers |

| --text-xl | 20px | 1.3 | Card values, stat numbers |

| --text-lg | 16px | 1.5 | Body text, descriptions |

| --text-md | 14px | 1.5 | Secondary text, form labels |

| --text-sm | 12px | 1.4 | Captions, badges, metadata |

| --text-xs | 11px | 1.3 | Chart axis labels, timestamps |

Numeric Display

All metric values use tabular numerals for alignment:

--numeric: font-variant-numeric: tabular-nums;

Delta values (e.g. -0.3 kg) use --text-md at --font-medium with semantic color.

---

9. Spacing Scale

Base unit: 4px. All spacing is a multiple of the base unit.

| Token | Value | Usage |

|-------------|-------|------------------------------------------|

| --space-1 | 4px | Tight gaps, icon padding |

| --space-2 | 8px | Inline spacing, badge padding |

| --space-3 | 12px | Card inner padding (compact) |

| --space-4 | 16px | Card gap, standard padding |

| --space-5 | 20px | Card inner padding (standard) |

| --space-6 | 24px | Section gap, section padding |

| --space-8 | 32px | Large section gap |

| --space-10| 40px | Page-level padding |

| --space-12| 48px | Hero element vertical padding |

| --space-16| 64px | Top bar height, major section spacing |

Common Patterns

  • Card padding: --space-5 (20px)
  • Card gap (grid): --space-4 (16px)
  • Section gap: --space-6 (24px)
  • Chart internal padding: --space-3 (12px)
  • Badge padding: --space-1 horizontal, --space-2 vertical
  • Top bar height: --space-16 (64px)
  • Bottom tab bar: 56px (14 * base unit)

---

10. Border and Elevation

No box shadows. Depth is communicated entirely through surface color levels and 1px borders.

Border Tokens

| Token | Value | Usage |

|---------------------|--------------------------|-----------------------------|

| --border-default | 1px solid #333333 | Card borders, separators |

| --border-emphasis | 1px solid #444444 | Emphasized sections, tables |

| --border-focus | 2px solid rgba(129,140,248,0.5) | Focus rings |

| --border-accent | 1px solid rgba(129,140,248,0.3) | Active nav indicator|

Border Radius

| Token | Value | Usage |

|------------------|-------|------------------------------------|

| --radius-sm | 4px | Small badges, code blocks |

| --radius-md | 8px | Cards, inputs, buttons |

| --radius-lg | 12px | Large cards, modal panels |

| --radius-full | 9999px| Pills, circular badges, rings |

Elevation Model

No shadows. Hierarchy is expressed through four surface levels:

1. --bg (#0f0f0f): page canvas

2. --surface (#1a1a1a): primary cards

3. --surface2 (#242424): elevated overlays (tooltips, dropdowns)

4. --surface3 (#2e2e2e): active/pressed states within overlays

Each level is ~10 lightness units apart, creating subtle but perceptible depth.

---

11. Interaction States

All interactive elements follow a consistent state model using pastel tones.

Default Card States

| State | Background | Border | Text |

|-----------|------------------|-----------------|----------|

| Default | --surface | --border | --text |

| Hover | --surface3 | --border2 | --text |

| Active | #333333 | --border2 | --text |

| Focus | --surface | --border-focus| --text |

| Disabled | --surface | --border | --text-disabled |

Button States (Primary/Accent)

| State | Background | Text |

|-----------|------------------|------------------|

| Default | --accent | #ffffff |

| Hover | --accent-hover | #ffffff |

| Active | #6c77e0 | #ffffff |

| Focus | --accent + focus ring | #ffffff |

| Disabled | --accent-muted | --text3 |

Button States (Ghost/Secondary)

| State | Background | Text |

|-----------|--------------------|---------------|

| Default | transparent | --accent |

| Hover | --accent-muted | --accent |

| Active | rgba(129,140,248,0.18) | --accent |

| Focus | transparent + focus ring | --accent |

| Disabled | transparent | --text3 |

Navigation States

| State | Indicator | Label Color |

|-----------|------------------------|---------------|

| Default | none | --text2 |

| Hover | --surface3 bg | --text |

| Active | --accent left bar (3px) | --text |

| Disabled | none | --text-disabled |

Chart Interaction States

| Element | Default | Hover/Active |

|----------------|--------------------|----------------------|

| Data point | 6px radius, line color | 8px radius, brighter |

| Crosshair line | --border2 (1px) | --text3 (1px) |

| Tooltip bg | --surface2 | n/a |

| Tooltip border | --border | n/a |

| Time range pill| --surface2 | --accent-muted bg |

| Active range | --accent-muted bg| --accent text |

Focus Management

  • All focusable elements receive a 2px focus ring using --border-focus.
  • Focus ring offset: 2px (using outline-offset).
  • Focus is visible only via keyboard navigation (:focus-visible).
  • Tab order follows visual layout: top bar, hero card, snapshot row, then down the page.

Transition Timing

--transition-fast: 100ms ease-out;   /* hover states, tooltips */
--transition-base: 200ms ease-out;   /* panel slides, fades */
--transition-slow: 300ms ease-out;   /* page transitions */

All transitions respect prefers-reduced-motion: reduce by collapsing to 0ms.

---

Token Summary (CSS Custom Properties)

:root {
  /* Surfaces */
  --bg: #0f0f0f;
  --surface: #1a1a1a;
  --surface2: #242424;
  --surface3: #2e2e2e;
  --surface-hover: #2e2e2e;
  --surface-active: #333333;
  --surface-selected: rgba(108,99,255,0.08);

  /* Text */
  --text: #e8e8e8;
  --text2: #a0a0a0;
  --text3: #6b6b6b;
  --text-disabled: #4a4a4a;

  /* Accents (pastel) */
  --accent: #818cf8;
  --accent-hover: #93a0ff;
  --accent-muted: rgba(129,140,248,0.12);
  --accent-subtle: rgba(129,140,248,0.06);

  --green: #6ee7b7;
  --green-hover: #86efcb;
  --green-muted: rgba(110,231,183,0.12);

  --yellow: #fde68a;
  --yellow-hover: #fef0ad;
  --yellow-muted: rgba(253,230,138,0.12);

  --red: #fca5a5;
  --red-hover: #fdb8b8;
  --red-muted: rgba(252,165,165,0.12);

  /* Semantic */
  --success: #6ee7b7;
  --warning: #fde68a;
  --error: #fca5a5;
  --info: #818cf8;

  /* Borders */
  --border: #333333;
  --border2: #444444;
  --border-focus: rgba(129,140,248,0.5);

  /* Readiness */
  --readiness-low: #fca5a5;
  --readiness-mid: #fde68a;
  --readiness-high: #6ee7b7;

  /* Chart: Training */
  --chart-ctl: #818cf8;
  --chart-atl: #fca5a5;
  --chart-tsb-pos: #6ee7b7;
  --chart-tsb-neg: #fca5a5;

  /* Chart: Recovery */
  --chart-sleep: #c4b5fd;
  --chart-deep: #6366f1;
  --chart-light: #a5b4fc;
  --chart-rem: #c4b5fd;
  --chart-awake: #fde68a;

  /* Chart: Body */
  --chart-weight: #fbbfb2;
  --chart-bodyfat: #fdba74;
  --chart-muscle: #6ee7b7;

  /* Chart: Nutrition */
  --chart-calories: #fde68a;
  --chart-protein: #6ee7b7;
  --chart-carbs: #fde68a;
  --chart-fat: #fbbfb2;

  /* Chart: Meds */
  --chart-ozempic: #d8bfff;

  /* HR Zones */
  --zone-1: #7dd3fc;
  --zone-2: #6ee7b7;
  --zone-3: #fde68a;
  --zone-4: #fdba74;
  --zone-5: #fca5a5;

  /* Radius */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 9999px;

  /* Spacing */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;

  /* Typography */
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --font-mono: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
  --font-regular: 400;
  --font-medium: 500;
  --font-semibold: 600;
  --font-bold: 700;
  --font-extrabold: 800;

  /* Transitions */
  --transition-fast: 100ms ease-out;
  --transition-base: 200ms ease-out;
  --transition-slow: 300ms ease-out;
}

UX Acceptance Criteria

docs/spike-ux-dashboard/ux-acceptance-criteria.md

UX Acceptance Criteria: Data Hub Dashboard MVP

**Spike:** SPIKE-010

**Owner:** Product Owner

**Reviewer:** Scrum Master

**Status:** Complete

**Last updated:** 2026-02-22

All criteria are UX-level, not implementation-level. Each criterion describes observable user experience, not technical behaviour. Criteria use the format AC-XXX with declarative or Given/When/Then statements.

---

1. Glance Page

Readiness Score Hero

**AC-001:** The readiness score card is the most visually prominent element on the Glance page, positioned at the top of the content area.

**AC-002:** The readiness score displays as a number from 0 to 100 with a clear label ("Readiness" or equivalent).

**AC-003:** The readiness score card uses colour coding to communicate state at a glance: green for good (70 to 100), amber for moderate (40 to 69), red for poor (0 to 39).

**AC-004:** Tapping or clicking the readiness score card navigates to a readiness detail view showing the full factor breakdown.

Race Countdown

**AC-005:** A race countdown element is visible on the Glance page showing the number of days remaining until Race to the Sun (30 May 2026).

**AC-006:** The race countdown includes the race name and target date alongside the day count.

**AC-007:** Given the race date has passed, the countdown displays "Race day" or a post-race label instead of a negative number.

Daily Snapshot Row

**AC-008:** A daily snapshot row displays exactly five KPIs: Weight, Body Fat %, Steps, Resting Heart Rate, and Form (TSB).

**AC-009:** Each KPI in the snapshot row shows its current value, unit, and a directional indicator (up, down, or stable compared to previous day or 7-day average).

**AC-010:** Each KPI card in the snapshot row is tappable, navigating to its respective domain page.

**AC-011:** Given a KPI has no data for today, the card displays a placeholder value (e.g. "--") rather than disappearing from the row.

Training Load Chart

**AC-012:** A training load chart (CTL/ATL/TSB or equivalent visualisation) is visible on the Glance page without scrolling on desktop viewports (1024px and above).

**AC-013:** The training load chart shows at minimum the last 6 weeks of data by default.

**AC-014:** Tapping or clicking the training load chart navigates to the Training domain page.

Card Drill-down

**AC-015:** Every data card on the Glance page is interactive. Tapping or clicking navigates to the relevant domain page or detail view.

**AC-016:** Interactive cards provide a visual hover state on desktop and a pressed state on touch devices to confirm interactivity.

Data Freshness

**AC-017:** Every data card on the Glance page includes a freshness indicator showing the time of the last data update.

**AC-018:** Given data is older than 48 hours, the freshness indicator is visually distinct (amber or warning colour) to flag staleness.

---

2. Readiness Score

Score Display

**AC-019:** The readiness score is displayed as a prominent numeric value (0 to 100) with a circular or arc gauge visualisation.

**AC-020:** The score uses a three-tier colour system: green (70 to 100), amber (40 to 69), red (0 to 39).

**AC-021:** A short text label accompanies the score describing the state in plain language (e.g. "Good to go", "Take it easy", "Rest day recommended").

Factor Breakdown

**AC-022:** Below the score, a factor breakdown section lists each contributing factor: Sleep Quality, Resting Heart Rate, Training Strain, Consistency.

**AC-023:** Each factor shows its individual contribution as a bar, percentage, or similar proportional indicator.

**AC-024:** Each factor row is tappable, navigating to the relevant domain detail for that metric.

Confidence Indicator

**AC-025:** When one or more contributing data sources are missing or stale, the readiness score displays a confidence indicator (e.g. "Based on 3 of 4 inputs").

**AC-026:** The confidence indicator clearly names which data sources are missing so the user understands the gap.

Empty State

**AC-027:** Given no contributing data sources have data for today, the readiness score area displays a friendly empty state explaining what data is needed to generate a score.

**AC-028:** The empty state includes guidance on which data sources to connect or sync to enable the score.

---

3. Domain Pages

3a. Training Domain

**AC-029:** The Training page displays a CTL/ATL/TSB chart as its primary visualisation.

**AC-030:** The CTL/ATL/TSB chart supports time range controls: 4 weeks, 8 weeks, 12 weeks, 6 months, or 1 year.

**AC-031:** The chart highlights the Race to the Sun date (30 May 2026) as a vertical marker with a label.

**AC-032:** Below the chart, a list of recent activities shows date, type, duration, distance, and suffer score for each.

**AC-033:** Each activity in the list is tappable, navigating to the ride detail drilldown.

**AC-034:** Summary metrics at the top include: weekly volume, current CTL, current ATL, current TSB.

3b. Recovery Domain

**AC-035:** The Recovery page displays a sleep summary card showing last night's total sleep, sleep score, and bed/wake times.

**AC-036:** The sleep summary card is tappable, navigating to the sleep detail drilldown with full hypnogram.

**AC-037:** A resting heart rate trend chart shows at minimum the last 30 days of RHR data.

**AC-038:** Recovery signals are displayed as a summary section including: average RHR trend direction, sleep consistency, and HRV if available.

**AC-039:** Given sleep data is unavailable, the Recovery page shows an empty state for the sleep card while still displaying HR data if available.

3c. Body Domain

**AC-040:** The Body page displays a weight trend chart showing at minimum 30 days of data by default.

**AC-041:** Body composition metrics are displayed alongside weight: body fat %, muscle mass, bone mass, body water %.

**AC-042:** A weekly delta section shows weight change over the last 7 days and rate of change (kg per week).

**AC-043:** The weight chart supports time range controls: 1 month, 3 months, 6 months, 1 year.

**AC-044:** Given Ozempic doses are logged, the weight chart overlays dose markers on the timeline.

3d. Nutrition Domain

**AC-045:** The Nutrition page displays a food log list showing today's entries with estimated calories and macro breakdown.

**AC-046:** A daily calorie and macro summary is visible at the top of the page.

**AC-047:** A calorie chart shows daily totals for the last 7 days as a bar chart.

**AC-048:** A weekly pattern view shows average calorie intake by day of week over the last 4 weeks.

**AC-049:** Given no food entries exist for today, the Nutrition page displays an empty state with a prompt to log a meal.

3e. Timeline Domain

**AC-050:** The Timeline page displays a chronological annotated timeline of events spanning all data sources.

**AC-051:** Ozempic dose entries are displayed with date, dose amount (mg), and a distinct visual marker.

**AC-052:** Key events include: activities, weight milestones, medication changes, and user annotations.

**AC-053:** The timeline supports filtering by event type via toggle controls.

**AC-054:** Correlation overlays allow viewing two metrics together on the timeline.

---

4. Navigation and Progressive Disclosure

Three-Layer Model

**AC-055:** Given the user is on the Glance page, tapping any card navigates to the relevant Domain page.

**AC-056:** Given the user is on a Domain page, tapping a chart, metric, or list item navigates to the relevant Detail drilldown.

**AC-057:** Given the user is on a Detail view, tapping back returns to the Domain page, not the Glance page.

Back Navigation

**AC-058:** A breadcrumb trail is visible on Detail pages showing the navigation path.

**AC-059:** Each breadcrumb segment is clickable, allowing the user to jump to any ancestor level.

**AC-060:** A back button or arrow is always visible on Detail pages.

Location Awareness

**AC-061:** The current page is visually highlighted in the navigation element (sidebar on desktop, active tab on mobile).

**AC-062:** The page title updates to reflect the current view.

Bookmarkable URLs

**AC-063:** Each Glance, Domain, and Detail view has a unique URL that can be bookmarked.

**AC-064:** Given a user navigates directly to a bookmarked URL, the page loads correctly with full context.

Responsive Navigation

**AC-065:** On desktop (1024px+), navigation is a persistent sidebar with labelled domain links.

**AC-066:** On mobile (below 768px), navigation is a bottom tab bar with domain icons.

**AC-067:** On tablet (768px to 1023px), navigation adapts appropriately per orientation.

---

5. Detail Drilldowns

Sleep Detail

**AC-068:** Displays a full hypnogram showing sleep stages (REM, light, deep, awake) across the night.

**AC-069:** Sleep stage totals shown as breakdown with duration and percentage for each stage.

**AC-070:** A sleep quality score is displayed if available from the data source.

**AC-071:** Bed time and wake time are displayed with total time in bed and total sleep time.

Heart Rate Detail

**AC-072:** Displays a daily heart rate timeline showing all recorded HR samples.

**AC-073:** HR zones are visualised as a summary showing time spent in each zone.

**AC-074:** A resting heart rate trend line covers at minimum the last 30 days.

**AC-075:** Given an activity occurred during the day, the HR timeline highlights the activity period.

CTL/ATL/TSB Detail

**AC-076:** Displays the full CTL/ATL/TSB chart with all available historical data.

**AC-077:** Training phase markers are displayed on the chart if phases are defined.

**AC-078:** The Race to the Sun target date is marked on the chart.

**AC-079:** Hovering or tapping a point shows a tooltip with exact CTL, ATL, and TSB values.

Weight Detail

**AC-080:** Displays the full weight history chart with all available data points.

**AC-081:** Body composition data is displayed alongside or below the weight chart.

**AC-082:** Given Ozempic doses are logged, an Ozempic overlay shows dose dates and amounts.

**AC-083:** Weight milestones or targets are displayed on the chart if defined.

Ride Detail

**AC-084:** Displays a stats grid: distance, duration, elevation gain, average speed, average HR, average power, suffer score.

**AC-085:** A heart rate chart for the ride is displayed showing HR over the duration.

**AC-086:** Given power data is available, a power chart or zones summary is displayed alongside HR.

**AC-087:** The ride title, date, and route name are displayed at the top.

---

6. Responsive Behaviour

**AC-088:** All views are fully functional at phone widths (320px to 479px).

**AC-089:** All views are fully functional at tablet widths (480px to 1023px).

**AC-090:** All views use their full layout at desktop widths (1024px and above).

**AC-091:** All interactive elements have a minimum touch target of 44x44px on viewports below 768px.

**AC-092:** Charts resize fluidly without horizontal scrolling.

**AC-093:** Given orientation changes, charts re-render within 1 second.

---

7. Empty and Error States

**AC-094:** Given a data source has never been connected, relevant cards display a friendly empty state.

**AC-095:** Given data is older than 48 hours, a freshness warning is displayed.

**AC-096:** Given a data fetch fails, the affected section shows an error state with retry action.

**AC-097:** Errors are isolated per section; other sections continue to display normally.

**AC-098:** Loading states use skeleton placeholders that match the loaded content layout.

---

8. Accessibility

**AC-099:** All text meets WCAG AA contrast ratios (4.5:1 body text, 3:1 large text) on the dark background.

**AC-100:** Colour is never the sole means of communicating information. Labels supplement all colour coding.

**AC-101:** All charts include a text summary or description for screen reader users.

**AC-102:** All interactive elements are reachable via keyboard (Tab, Enter, Space, Arrow keys).

**AC-103:** Focus indicators are visible on all interactive elements when focused via keyboard.

**AC-104:** Heading hierarchy is logical and sequential with no skipped levels.

---

Traceability Matrix

| Area | Criteria Range | Count |

|------|---------------|-------|

| Glance Page | AC-001 to AC-018 | 18 |

| Readiness Score | AC-019 to AC-028 | 10 |

| Domain Pages | AC-029 to AC-054 | 26 |

| Navigation and Disclosure | AC-055 to AC-067 | 13 |

| Detail Drilldowns | AC-068 to AC-087 | 20 |

| Responsive Behaviour | AC-088 to AC-093 | 6 |

| Empty and Error States | AC-094 to AC-098 | 5 |

| Accessibility | AC-099 to AC-104 | 6 |

| **Total** | | **104** |

Outcomes

Open Issues