Root cause of 'eternal spinner': a browser without working hls.js
(no MSE, or hls.min.js blocked) threw 'HLS not supported' which the
old showStreamError hid UNDER the spinner overlay, and there was no
fallback — so the player sat on Loading… forever with no /stream
request ever leaving the browser (matched the prod log exactly).
- no-hls.js + canPlayType(mpegurl) -> v.src = proxied m3u8, native
<video> plays it (iOS < 17 path). Verified: readyState 4, playing,
no hls.js at all.
- no-hls.js + no native HLS -> VISIBLE 'Stream error: ...'
overlay with Retry (was: hidden under the spinner)
- every play() catch routes through the visible overlay now
Desktop hls.js regression: still plays. assets v=6.