d19fe4e8fa
Each altitude change reset the single <video> element's src and re-fetched the scale's footage over the network, compounded by the dev no-store policy applying to /media too — so every scale change paid a full fetch+decode. - simulator/app.py: split the cache middleware — app shell/JS/CSS stay no-store (by-eye dev iteration), but /media is now cacheable (public, max-age, immutable) since the clips are static. - simulator/static/app.js: preload every ring clip (19 base + 5 transition, ~350 MB) into in-memory blob object URLs at startup — non-blocking, the first scale plays immediately while the rest stream in, ordered current scale outward. mediaUrl() serves the cached blob when ready, else falls back to the network. A small progress chip self-removes when done. - tests: assert app assets stay no-store and /media is cacheable. 269 passed, 2 skipped. Frontend verified by-eye by the operator (no headless browser in repo yet). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>