···5454Red (`--neg`) appears in chrome only when money or data is at risk; an expense
5555figure in a table is plain ink, not red.
56565757-### Chart categoricals (colorblind-safe ordering, muted chroma)
5757+### Chart colors
58585959-`--chart-1` `oklch(0.55 0.13 180)` (validated ≥0.1 chroma vs light surface; dark: `oklch(0.65 0.12 190)`) · `--chart-2` `oklch(0.65 0.11 75)` ·
6060-`--chart-3` `oklch(0.55 0.10 300)` · `--chart-4` `oklch(0.68 0.10 130)` ·
6161-`--chart-5` `oklch(0.58 0.11 25)` · `--chart-6` `oklch(0.62 0.08 250)` ·
6262-`--chart-7` `oklch(0.70 0.09 55)`. Dark theme: same hues, lightness +0.10.
5959+- `--chart-1` `oklch(0.55 0.13 180)` light / `oklch(0.65 0.12 190)` dark — the
6060+ single-series mark color (net worth line). Validated ≥0.1 chroma and 3:1
6161+ contrast per surface. (`--chart-2…7` remain as spare series tokens.)
6262+- **Per-category palette** `--cat-0` … `--cat-11`: each category owns a stable
6363+ palette index (`categories.color_index`, assigned at creation, never
6464+ recomputed from position). Generated with
6565+ [poline](https://github.com/meodai/poline) via
6666+ `scripts/generate-category-palette.ts` — a closed-loop hue journey anchored
6767+ at spruce/amber/plum, normalized into OKLCH L 0.54–0.62, C ≥ 0.10 so one set
6868+ passes the lightness band, chroma floor, and 3:1 contrast on BOTH surfaces
6969+ (no dark override). Neighboring slots are interleaved to maximize hue
7070+ distance; charts additionally separate fills with 2px surface gaps and
7171+ always carry a legend, so identity never rests on hue alone. Neutral
7272+ (`--text-faint`) is reserved for "Other" and "Uncategorized" — never a
7373+ category.
63746475## Typography
6576
···11+-- Stable per-category chart color: an index into the --cat-N palette tokens.
22+-- Color follows the entity — assigned once, never recomputed from position.
33+44+ALTER TABLE categories ADD COLUMN color_index INTEGER;
55+66+UPDATE categories SET color_index = (id - 1) % 12;