# V3-03 — Distance and speed units **Phase** Foundations · **Depends on** V3-02 · **Size** S · **Status** Not started ## Goal Imperial as well as metric, chosen once and applied everywhere. ## Context Everything is hardcoded metric: `formatDistance` switches m/km at 1000, `formatSpeed` prints km/h, elevation prints metres. Fine in Canada, useless to anyone in the US or UK. Cheap, and the kind of thing that makes an app feel unfinished when missing. ## Design A `UnitSystem` enum (`metric`, `imperial`) in `Config`, defaulting from the device locale on first launch. **Conversion belongs in formatting only.** Storage stays SI — metres, km/h, metres of altitude — forever. Converting at the storage layer would corrupt every existing ride and break the parity harness. | Value | Metric | Imperial | |---|---|---| | Distance | m / km | ft / mi | | Speed | km/h | mph | | Elevation | m | ft | ## Implementation 1. `UnitSystem` in `Config`; default from `Platform.localeName` 2. Extend `ui/format.dart` — every formatter takes the unit system 3. Thread it through: record screen, trips list, trip detail, charts, map legend 4. Exports stay SI regardless. GPX is metres by specification; changing that breaks consumers. ## Acceptance criteria - [ ] Switching units updates every screen immediately - [ ] Stored values are unchanged — verified by exporting before and after - [ ] GPX/GeoJSON output is byte-identical across the two settings - [ ] First launch picks a sensible default from the locale ## Tests - Formatter tests for both systems, including the m→km and ft→mi boundaries - **A test asserting export output does not change with the unit setting** - Widget test toggling units and checking a rendered label ## Risks The obvious trap is converting too deep in the stack. Guard it with the export test. ## Out of scope Temperature, pace (min/km) — pace is arguably right for running, revisit after V3-01.