The live timeline caught it: iPhone buffered 513s, readyState stuck at 1, canplay NEVER fired -> play() never called -> eternal Loading. Now: first BUFFER_APPENDED (hls.js's own 'data is in the pipe' signal) triggers play, canplay still works, plus a 3s safety retry if still paused with a non-empty buffer. play() calls are guarded + idempotent. Desktop + mobile emu regression: playing. assets v=14.