The preview snapshot (/api/v3/display/current, the web UI's live preview)
is written only from update_display(), at most once per write interval.
A changed frame pushed inside that interval was skipped and left for the
next update_display() -- which a screen that draws its card once and then
holds it never makes.
Soccer's recent/upcoming cards skip redundant redraws. After an on-demand
start, the card is pushed a few milliseconds after the start's clear wrote
a black frame, so the throttle skipped it and the preview stayed black for
the whole 15 s screen while the panel showed the card. The next screen
looked fine because its first push came after the interval had passed.
DisplayManager now records a skipped changed frame as owed, and the render
loop (_display_once) calls write_owed_snapshot() after each frame, which
writes it once the interval has passed. The cadence is unchanged, an
unchanged frame is never re-encoded, and nothing runs when no frame is owed.
Golden run-loop traces are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>