);
}
```
`switchTo(descriptor)` is the function that:
1. (If google) `await ensureGoogleProtocol()`.
2. Calls `map.setStyle(descriptor.style)`.
3. Updates `useMapPrefStore` so the choice persists.
The actual setStyle triggers `styledata` → `mapReady` flips false → flips true once loaded → children remount.
### Persistence
Zustand `persist` middleware on `useMapPrefStore`. Key `trm-map-prefs`. Selected basemap survives reloads.
On first visit, default to `esri-satellite` — the most universally useful for rally context.
## Acceptance criteria
- [ ] `pnpm typecheck`, `pnpm lint`, `pnpm format:check`, `pnpm build` clean.
- [ ] `/monitor` shows the basemap switcher top-right with three buttons (Satellite / Topo / Street).
- [ ] Clicking a button changes the basemap. The selected button is visually highlighted.
- [ ] Reloading the page restores the previously selected basemap.
- [ ] If `googleMapsKey` is set in `config.json`, a fourth "Google" button appears. Clicking it loads Google satellite tiles via the adapter.
- [ ] If `googleMapsKey` is empty/missing, the Google button is hidden — and `maplibre-google-maps` is not imported (verify in DevTools network tab; the chunk should not appear).
- [ ] After a basemap switch, `useMapReady()` flips to false then back to true within ~500ms. Console shows no errors.
## Risks / open questions
- **OSM tile-server policy.** `tile.openstreetmap.org` is rate-limited and asks for a User-Agent. For low-traffic dogfood it's fine; for production we'd self-host or switch to a paid OSM mirror (MapTiler / Stadia). Document but defer.
- **Esri ToS.** Esri's "World Imagery" raster service is free for non-commercial use with attribution. Confirm before going public; for a closed dogfood it's clearly fine.
- **OpenTopoMap rate limits.** The official tile servers are best-effort. Same defer-until-it-bites posture.
- **Google's official Map Tiles API requires session tokens.** `maplibre-google-maps` handles this internally per the adapter's docs. Confirm the API key has the Map Tiles API enabled (not just JS Maps).
- **Style switcher placement.** Top-right floats over the map; if Phase 3.4's per-device detail panel goes top-right too, we'll need to reposition. For Phase 2 v1, top-right is fine.
## Done
(Filled in when the task lands.)