The --inset-* tokens were light in both themes, so the dial stayed a white
panel on a dark chassis. In dark they now sit just below --surface, which
reads as recessed into the faceplate rather than laid on top of it.
Nothing outside the dial and the knobs reads these tokens, so the three
values carry the whole change.
Co-authored-by: Cursor <cursoragent@cursor.com>
feat: robust streaming, now-playing titles and a dedicated tuning model
Three parts of the app that were tangled into embed.ts now stand on their
own, each testable from a bench page under test/.
- stream-engine.ts: one <audio> element kept alive, HLS via hls.js (light
build, dynamically imported) for .m3u8 and a hardened native path for
everything else. Reconnects only once the buffer has run dry, so a
network blip costs no audible gap.
- now-playing.ts: resolves the track on air from side-channel endpoints
rather than in-band ICY metadata, which no browser can read cross-origin.
- tuning.ts: the antenna's 36° of travel stretched over a 280° arc means
8x the encoder noise and 8x the mechanical sag. A one-euro filter,
station magnetism, capture hysteresis and a bounded UI/antenna offset
turn that into a needle that holds still and a station that does not
flicker. The needle is pinned whenever the antenna is at rest, so
nothing on the dial moves when nothing is moving.
Stations carry tags and country instead of a single genre string.
Co…