16799ad7c7
The update banner only fires when the browser sees sw.js change, but the cache name was a hand-bumped constant — so a deploy that touched only app.js/index.html/style.css left sw.js byte-identical, no new worker installed, and the banner never showed (and cached assets never refreshed). Have the server render sw.js at serve time, substituting a __BUILD_HASH__ placeholder with a SHA-256 over the assets the worker caches (index.html, style.css, app.js, manifest.json, icon.svg). Any asset change now yields a new cache name and a byte-different sw.js, which is exactly the signal that makes the browser install a new worker. The hash is memoised and only recomputed when a file's size/modtime changes, so it needs no server restart. Served unsubstituted (dev/static host), sw.js stays a valid constant.