/insights/mood was on its 460 KB ceiling. The route paints a calendar, a line chart, a target card and an assessment; under them sit a dozen breakdown cards — both tag axes, the influence board, the two crosstabs, the day-context comparison, four Recharts mini-charts, the correlation cards — and every one of them was a static import, so a cold mount paid for all of it before painting anything. The cluster is its own module now, loaded through next/dynamic with a skeleton, the way the insights mother page already treats its below-the-hero blocks. It takes the resolved aggregate as a prop rather than running a second query, so it cannot draw a different snapshot from the regions above it. The better-days card follows, for one specific reason: it resolves catalogue tag icons, and that resolver statically imports about a hundred and sixty lucide icons. The card sits a screen down and the map now rides the same lazy group as the breakdowns. Only mood-only modules moved. Deferring the shared target and assessment cards was tried and reverted: it bought ten kilobytes on this route and cost thirty-three on the whole-build total, because both are imported by a dozen other insight routes and a route-local lazy group duplicates them. /insights/mood 459 -> 451 KB gz (budget 460, unchanged) total client JS 3296 -> 3297 KB gz (budget 3310, unchanged) The payload shape and the section card frame move to a shared module both halves import; the two structural tests that read the old file now read the union of the two, so a guard cannot go quiet by pointing at the wrong half.
src/components/
React components. The UI primitives are shadcn/ui (new-york style, zinc base) over Radix + Tailwind 4 + Lucide; everything above the primitives is feature-grouped by surface. Components are server components unless they need a hook, state, or browser API, in which case they open with "use client". Charts use Recharts 3 — replacing the chart library requires explicit approval.
Primitives and shell
ui/— shadcn/ui primitives (button, dialog, sheet, table, …). Generated by the shadcn CLI; keep edits minimal and on-style.layout/— the app shell: sidebar, topbar, bottom nav.providers.tsx,version-poller.tsx— top-level context providers and the self-healing stale-shell poller.i18n/— translation context + locale switcher.
Feature groups
insights/— insight status, assessment cards, Coach surface, advisor cards (the largest group).medications/— medication cards, the schedule wizard, intake timeline, detail history.settings/— the section components behind each settings page.measurements/,mood/,dashboard/— entry forms, lists, dashboard tiles.charts/— Recharts wrappers.monitoring/— Umami / GlitchTip client bootstrap.gamification/,targets/,comparison/,doctor-report/,clinician/,onboarding/,admin/— the remaining per-surface groups.
Conventions
Kebab-case filenames; React component exports stay PascalCase regardless. (A handful of PascalCase-filename outliers under medications/ + onboarding/ are pre-existing drift, noted in ../../CLAUDE.md.) Every t() call needs a matching key in messages/en.json — guarded by i18n-call-site-coverage.test.ts. Data reads go through TanStack Query with keys from @/lib/query-keys and unwrap (await res.json()).data. Tests live in __tests__/.