Phones kept running old JS because their cached index.html pointed at an old hashed asset. Both images now bake the commit SHA as BUILD_VERSION; the backend exposes it at GET /api/version and the client reloads once with a cache-busting query when its own baked version differs. Guarded by sessionStorage so it cannot loop. /api/version is unauthenticated like /api/health.