08e0d090

Rebuild centjes-chart: pure report IR, diagrams backend, tests

## Summary

Restructures the `centjes-chart` package around a **backend-agnostic, pure report** so the data pipeline is total and testable and rendering is fully isolated. Net diff is the `centjes-chart` package plus its wiring (no `Balance.hs` change — the earlier WIP re-export hack is reverted by this work).

## Architecture

| Module | Role |
|---|---|
| `Centjes.Chart.Report` | Pure IR (`ChartReport`/`ChartSeries`), typed `ChartError` with a composing `ToReport`, total `produceChartReport`, `sampleStepFunction` |
| `Centjes.Chart.Render` | The *only* place precise money becomes `Double`; stacked-area SVG via `Chart` + **`Chart-diagrams`** |
| `Command/All` + `OptParse` | Wires Report→Render; currency / filter / output / title are settings |

## Highlights

- **No partial functions** in the pipeline — `fromJust`/`minimum`/`maximum` replaced by typed `ChartError`s rendered through the usual diagnostic machinery.
- **Exactness to the edge** — `really-safe-money` values flow into the IR; only the renderer converts to `Double`.
- **Dropped the cairo C dependency** by rendering through the pure-Haskell `Chart-diagrams` backend (confirmed in both the stack snapshot and nixpkgs).
- **No hardcoding** — currency, account filter (reusing `Centjes.Filter`), output path and title are CLI/config settings.
- **Daily revaluation** — held balances are re-valued at each day's exchange rate; the per-day price graph is shared across accounts.

## Tests (new `centjes-chart-test`)

- `sampleStepFunction` property tests (carry-forward invariants)
- A valid `ChartReport` golden
- Error goldens for every `ChartError` constructor: unknown-currency, no-data, missing-price

## Verification

- `stack test --pedantic` — 7/7
- pre-commit (ormolu, hlint, hpack, cabal2nix, deadnix, nixpkgs-fmt) — pass
- Nix `weeder-check` — compiles `-O2 -Werror`, no dead code
- End-to-end: `centjes-chart all` writes a valid 1920x1080 SVG (incl. into a nonexistent output dir via `ensureDir`)

## Notes / out of scope

- One chart kind today (assets stacked-area); the IR is built so additional kinds are mostly new `produce*Report` + subcommands.
- Stacked-area assumes non-negative series; negative balances invert a band (inherent charting limitation, pre-existing).

Suite timing

Time to Start Worker time Duration Time to finish Idle
Config 1m00s 34s 34s 1m34s 1m00s
Eval 1m34s 57s 57s 2m32s 0s
Build 2m32s 28s 1m11s 3m44s 58s
Suite 1m00s 2m00s 2m44s 3m44s 1m59s

Timeline

0s1m10s1m20s1m30s1m40s1m50s2m2m10s2m20s2m30s2m40s3m40s