The palette declared a brand green and six chart hues but never defined
the neutrals, so MUI's defaults filled the gap: body text at 60% black,
dividers at 12%, and background.default equal to background.paper. That
is the "washed out" everything sat on - table cells render through
text.secondary, and cards had no ground to sit on.
- Opaque neutral ramp, biased slightly green so it sits with the brand.
background.default is now off-white so paper separates from the page.
- Semantic success/error/warning derived from the brand instead of
Material's stock red/green/orange. Only `main` is set: `light` is
consumed as a saturated colour by status dots, histogram bars and
heatmap cells, so MUI must keep deriving it.
- Dark mode gets the same treatment; it defined background and secondary
but its text and divider were still MUI defaults.
- Geist Variable, self-hosted via @fontsource-variable so air-gapped
installs keep the typeface. Base size stays at 13px.
- h1 700 / h2 600 against 400 body, and table headers 500 -> 600. Two
weights was why everything read at the same visual weight.
All contrast pairs pass WCAG AA in both themes (lowest 5.98:1).
No component markup changed.
Claude-Session: https://claude.ai/code/session_01AbYLvwpEaZFXktHVcqrFRd