Fires once per timer run when remaining hits zero.
Plays a beep if audioNotify includes 'host'
Emits TIMER_EXPIRED so players can react
Calls onExpire for host-side visual overlay
Remaining is computed directly from the timer object (not via the
remaining() callback) to avoid reading a stale timersRef that lags
one render behind state. This prevents a false fire immediately after
restart, when timersRef still holds the pre-restart (expired) snapshot.
The firedRef key is cleared whenever a timer is actively running with
time remaining, so a second natural expiry on the same timer correctly
re-fires after the previous run's key has been evicted.
Fires once per timer run when remaining hits zero.
Remaining is computed directly from the timer object (not via the
remaining()callback) to avoid reading a stale timersRef that lags one render behind state. This prevents a false fire immediately after restart, when timersRef still holds the pre-restart (expired) snapshot.The firedRef key is cleared whenever a timer is actively running with time remaining, so a second natural expiry on the same timer correctly re-fires after the previous run's key has been evicted.