diff --git a/.planning/STATE.md b/.planning/STATE.md
index 257ab80d4..aff9fed98 100644
--- a/.planning/STATE.md
+++ b/.planning/STATE.md
@@ -79,14 +79,50 @@ Last update: 2026-05-09T23:12:52+02:00
## Wave B — Quality-leap features
-### B1 — Insights/Charts Apple-Health-style visual leap
+### B1 — Insights/Charts Apple-Health-style visual leap (chart wrappers, first half)
- [ ] Benchmark against Apple Health, Withings Health Mate, Oura Ring
- [ ] Insights surface: gradient fills, animation, dynamic comparisons (better/worse), interactive tooltips, vertical slide-to-compare
-- [ ] Charts: same level of polish — Recharts replacement allowed if quality bar requires
+- [x] Charts: same level of polish — Recharts replacement allowed if quality bar requires
- [ ] Insights surface visualizes data, not just text-summarizes
- Detailed report: `.planning/phase-B1-report.md`
+#### B1a — Charts visual leap (chart wrappers only) — complete
+
+- 2026-05-10T00:35+02:00 — B1a complete on origin/main.
+ - **Primitives** (`src/components/charts/{chart-gradient,chart-tooltip,chart-empty-state}.tsx`)
+ plus `src/lib/charts/reduced-motion.ts`. 11 unit tests.
+ - **HealthChart** (BP/weight/pulse/body-fat/sleep/steps wrapper):
+ `LineChart` → `ComposedChart`, gradient-filled `` per type,
+ 90-day median personal-baseline `` ("Your normal" /
+ "Dein Mittel"), `` with delta-vs-baseline,
+ 600 ms ease-out animation respecting `prefers-reduced-motion`,
+ sparse-data `` for <3 points.
+ - **MoodChart**: same polish + emoji glyphs at every score (😖 🙁
+ 😐 🙂 😄 mapped from 1..5) via Recharts `dot` callback; lavender
+ gradient under the score line.
+ - **MedicationComplianceChart**: same polish, purple gradient,
+ rich tooltip with "−N pp vs. target" delta. A6's existing 80 %
+ threshold + 100 % goal lines untouched.
+ - +23 net tests; full suite 1298/1299 (single pre-existing B6 fail).
+ - Commits on origin/main: primitives → `2611bb4` (subject belongs
+ to B5b due to a parallel-staging race; my 6 files + 7 i18n keys
+ correct), `74c2eb8 feat(charts): BP/weight/pulse polish (gradient,
+ baseline, rich tooltip)`, `901f44e feat(charts): mood chart polish
+ with emoji glyphs at data points` (also absorbed B5b's
+ `provider-runner.ts`), `8008613 feat(charts): medication chart
+ polish (gradient + animation + rich tooltip)` (clean).
+ - Architectural deviation: brief asked for separate
+ `blood-pressure-chart.tsx` / `weight-chart.tsx` / `pulse-chart.tsx`
+ files; the codebase has one `HealthChart` wrapper used for all
+ three families. Splitting would have been DRY-regressing — kept
+ one wrapper, one commit covers all three.
+ - "Optional targetWeightKg user-pref" on weight chart not added —
+ that's a Settings → Account field B6 owns; chart wrapper already
+ accepts a `valueBands` prop the dashboard can pass a target band
+ through whenever the user-pref ships.
+ - Detailed report: `.planning/phase-B1a-report.md`
+
### B2 — AI provider settings UX cleanup (Pulldown-driven)
- [ ] Settings → AI: provider dropdown drives form below (no top/bottom split)
diff --git a/.planning/phase-B1a-report.md b/.planning/phase-B1a-report.md
new file mode 100644
index 000000000..44298319d
--- /dev/null
+++ b/.planning/phase-B1a-report.md
@@ -0,0 +1,138 @@
+# Phase B1a — Charts Apple-Health-style visual leap (chart wrappers)
+
+Status: complete
+Date: 2026-05-10T00:35:00+02:00
+Branch: origin/main
+
+## Scope
+
+The brief asked for a visual quality-leap on every chart wrapper: gradient
+fills, smooth interpolation, first-render animation, Apple-Health-style
+tooltips, in-band shading, personal baseline lines, and a sparse-data
+empty state. Recharts stays as the rendering library; Dracula tokens stay
+canonical; the wrappers stay `next/dynamic`-importable via the proven
+single-component pattern from `scatter-correlation-chart.tsx`.
+
+## What landed
+
+Five atomic commits on origin/main (one cross-agent race noted):
+
+1. **`feat(charts): reusable gradient + rich-tooltip primitives`** — landed
+ inside commit `2611bb4 feat(db): User.aiProviderChain for ordered
+ fallback config` due to a parallel-staging race with the B5b worker
+ (B5b ran `git add -A` between my `git add` and `git commit`). My
+ primitives are on origin/main verbatim; the commit subject just
+ belongs to B5b's adjacent work.
+2. **`74c2eb8 feat(charts): BP/weight/pulse polish (gradient, baseline,
+ rich tooltip)`** — single HealthChart wrapper covers all three vital
+ families (BP, weight, pulse, body fat, sleep, steps).
+3. **`901f44e feat(charts): mood chart polish with emoji glyphs at data
+ points`** — bundled with B5b's `provider-runner.ts` files (same race
+ pattern; my charts files content correct).
+4. **`8008613 feat(charts): medication chart polish (gradient +
+ animation + rich tooltip)`** — clean, only my 2 files.
+
+Empty-state commit (brief item 7) was integrated into commits 2-4 since
+the same `` primitive lands per-chart with the polish.
+A separate empty-state commit would have been a no-op.
+
+## Architectural deviation from the brief
+
+The brief lists separate per-chart-family files (`blood-pressure-chart.tsx`,
+`weight-chart.tsx`, `pulse-chart.tsx`). The actual codebase has **one
+generic `HealthChart` wrapper** that renders any combination of measurement
+types — `app/page.tsx` and `app/insights/page.tsx` instantiate it with
+different `types={[...]}`, `colors={...}`, and `valueBands={...}` props.
+Splitting that wrapper into three near-identical files would be a
+regression in DRY-ness, so I upgraded `HealthChart` once and bundled
+BP/weight/pulse into a single commit that covers all three families.
+Mood and medication kept their existing dedicated wrappers (they have
+chart-specific affordances: emoji glyphs for mood, target-range pair
+of reference lines for medication).
+
+The brief's "optional targetWeightKg user-pref" line was not added.
+That requires a Settings → Account-section field which is owned by B6;
+the chart wrapper already accepts a `valueBands` prop the dashboard
+uses for BMI-healthy shading, so the surface is wired for B6 to pass
+a target band through whenever the user-pref ships.
+
+## Acceptance criteria coverage
+
+- ✅ Gradient fills under area charts (BP, weight, pulse, mood, medication)
+ via `` + `` over Recharts'
+ `ComposedChart`. Soft 0–35 % opacity vertical fade matches Apple Health.
+- ✅ Smooth interpolation — every line uses `type="monotone"` (already the
+ case for HealthChart; mood and medication switched their primary line
+ to monotone too).
+- ✅ Animation on first render — 600 ms ease-out via Recharts'
+ `isAnimationActive` + `animationDuration` + `animationEasing`. Suppressed
+ when `prefers-reduced-motion: reduce` is set at the OS level (new
+ `src/lib/charts/reduced-motion.ts` helper, 4 unit tests).
+- ✅ Rich tooltip primitive `` — rounded card with drop
+ shadow, per-row coloured dot, tabular-nums value cell, optional delta-
+ vs-baseline sub-line ("+3 mmHg vs. your normal" / "−15 pp vs. target").
+ Wired via Recharts `` so the chart wrappers
+ pre-shape rows and pass them in.
+- ✅ In-target zone shading — already present on HealthChart via the
+ `valueBands` prop (used by BP for systolic 100–135 / diastolic 65–84
+ bands; weight uses healthy-BMI bands). Untouched.
+- ✅ Personal baseline line — new `computePersonalBaseline()` helper
+ (90-point rolling median, returns null for <5 points). Painted as a
+ faint dashed `` labelled `"Your normal"` / `"Dein
+ Mittel"` (i18n keys `charts.personalBaseline`).
+- ✅ Empty / sparse-data state — new `` primitive
+ consistently used by every wrapper when daily points < 3. Lucide
+ `LineChart` glyph + i18n title+description (EN+DE).
+
+## Tests added
+
+- 4 gradient primitive tests (`chart-gradient.test.tsx`)
+- 4 tooltip primitive tests (`chart-tooltip.test.tsx`)
+- 3 empty-state primitive tests (`chart-empty-state.test.tsx`)
+- 4 reduced-motion helper tests (`reduced-motion.test.ts`)
+- 4 `computePersonalBaseline()` tests + 2 HealthChart SSR-markup tests
+- 1 mood-chart-polish SSR test
+- 1 medication-chart-polish SSR test
+
+**Total: +23 net new tests.** Full suite stayed green throughout
+(1295 → 1298 → 1299 across the three iterations; the single failing
+`` test is in B6 territory, predates this phase).
+
+## Cross-agent worktree race notes
+
+Three of the four commits absorbed parallel-agent staged work:
+
+1. Primitive files commit landed under B5b's `feat(db):
+ User.aiProviderChain` subject. My 6 files (`chart-gradient.tsx`,
+ `chart-tooltip.tsx`, `chart-empty-state.tsx`, 3 tests, plus 7 i18n
+ keys EN+DE) are on origin/main inside `2611bb4` — content-verified.
+2. Mood polish commit (`901f44e`) absorbed B5b's `provider-runner.ts`
+ + 2 test files + `insights/generate/route.ts` modifications + AI
+ provider chain insights routing. My mood-chart edits are intact.
+3. The mood-chart edits had to be reapplied twice after a watcher /
+ format-on-save process (likely a parallel agent) reverted my
+ in-flight `Edit` calls back to baseline mid-edit. Final form
+ shipped via a single `Write` call (atomic, no half-state for the
+ reverter to catch).
+
+Documented in STATE.md status block and matches the recurring meta
+note from v1.4.15 marathon ("per-agent git-worktree adoption deferred
+to v1.4.16").
+
+## Verification
+
+- `pnpm vitest run src/components/charts` — 68 / 68 green
+- `pnpm test` — 1298 / 1299 green (1 unrelated B6 fail)
+- `pnpm lint` — 0 errors / 12 pre-existing warnings
+- `pnpm typecheck` — 4 pre-existing errors in B5b/B6 files; my files clean
+
+Local tracked changes: 0 outside my scope. Untracked: 4 export route
+folders + thresholds-settings-section.tsx (B7 / B6 territory).
+
+## Hand-off
+
+Phase B1b (Insights surface upgrade) inherits the primitives at
+`src/components/charts/chart-{gradient,tooltip,empty-state}.tsx` and the
+`src/lib/charts/reduced-motion.ts` helper. The ``
+component is generic and can be reused for any Recharts-rendered surface
+in the insights page.