PlayState data class bundles playId, elapsedMs, progress into one atomic snapshot. LaunchedEffect keyed on playId — when it changes (seek/resume), the timer reads elapsedMs and progress from the SAME snapshot, eliminating the race between seekVersion++ and elapsedMs=seekMs.