- src/map/assets/icons/: 9 placeholder SVGs (background plate, direction
arrow, 7 categories: rally-car / quad / ssv / motorcycle / runner /
hiker / default). Hand-authored simple silhouettes; replaced in
Phase 3.8 with the branded set.
- src/map/core/categories.ts: SpriteCategory/SpriteColor types,
mapCategoryToSprite() normaliser, inferColor() helper.
- src/map/core/sprite-preload.ts: idempotent preloadSprites() with
memoised promise, whenSpritesReady() alias, getSpriteRegistry()
read-only access, installSprites(map) for the mapReady flow.
Composition pipeline draws the plate + composites a tinted icon
centred on it (category sprites) or tints the arrow alone (direction
sprites). Tint via canvas globalCompositeOperation: 'source-in'.
- src/main.tsx: void preloadSprites() fired at boot; the promise is
memoised so mapReady flow awaits the same instance.
- src/map/core/map-view.tsx: onStyleData() awaits whenSpritesReady()
AND _map.loaded() before installing sprites and flipping mapReady
true. Sprites reinstall on every style swap.
Registry: 7 categories x 4 colours + 4 direction-only entries = 32
total. ~160KB in memory.
Deviations:
1. Direction sprites have no plate (it's a separate symbol layer in
2.5 overlaid on the device sprite; double-plate would look wrong).
2. Hardcoded the design-system palette (#2E8C4A / #E8412B / #0E0E0C /
#2563C8) directly. When 3.8 lands, these rebind to TRM tokens via
CSS variables.