Fox 2026-06-05: "listeners should still be synced it should just be
that the gesture to change feeds after they are all live should be
immediately to make that feed full sized... reverse your bs and do
what I asked."
Reverted the listener-video-no-delay shortcut. Listener video keeps
playoutDelayHint matched to audio (lip-sync preserved). The actual
fix for slow tile-switching: REUSE the existing thumb tile's <video>
element instead of building a fresh one when promoting to spotlight.
Before: setSpotlight built a brand-new <video> in #spotlight and
assigned the existing MediaStream as its srcObject. The fresh video
element had no decoder state — had to wait for the next keyframe
inside the receiver's 4s-deep buffer to show its first frame,
manifesting as up to 4s of "blank" on listener mobile.
After: setSpotlight DOM-moves the existing thumb tile (with its
already-playing <video> element) into #spotlight. The video element
NEVER stops — no keyframe wait, no re-buffer, no decode-init.
clearSpotlightDOM does the reverse: moves the spotlit tile back to
its thumbs container. Browser DOM-move on an HTMLMediaElement does
not reset playback state.
Supporting changes:
1. CSS: viewing-badge default-hidden; only visible in .tile-thumb.viewing
context (which no longer exists post-DOM-move since the tile IS
the spotlight, no separate thumb to mark).
2. CSS: .tile-thumb .fs-btn { display: none } — buildTile always
includes the fullscreen button now (so we don't have to add it
dynamically when moving thumb → spotlight). The button is hidden
when the tile is in thumb context.
3. buildTile: always builds fsBtn (used to be !isThumb only).
4. setSpotlight: same-target click = no-op (idempotent); previous
spotlight is moved back via clearSpotlightDOM before installing
the new one.
5. The mid-spotlight supplant path (publisher republishes the same
kind): the entry-video swap also IS the spotlight-video swap now
(same DOM node) — removed the redundant second swap, just mirror
the new entry.video reference into spotlight.video.
6. Game spotlight unchanged — game tiles are static iframes built
fresh each spotlight, not DOM-moved.
End result: lip-sync stays correct for every role (audio + video
share the same playoutDelayHint), tile switching is instant.