diff --git a/.dockerignore b/.dockerignore
index 574fd24..e0efe7b 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -8,3 +8,7 @@ __pycache__
.mypy_cache
README.md
build.sh
+
+# Rebuilt fresh in the frontend stage; never copy a local build into the image.
+frontend/node_modules
+static/vendor
diff --git a/.gitignore b/.gitignore
index 5095444..e863611 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,7 @@ __pycache__/
.mypy_cache/
.idea/
.vscode/
+
+# Frontend vendor bundle is produced at Docker build time (frontend/build.mjs).
+node_modules/
+static/vendor/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13f0e39..dc19660 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,17 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
+## [0.5.0] - 2026-07-05
+
+### Changed
+- **All browser dependencies are now vendored into the image — nothing loads from a CDN at runtime.** A new `frontend/` esbuild step (run in a Node stage of the Dockerfile) bundles CodeMirror into a single self-contained `static/vendor/codemirror.js` and copies cropperjs + Tesseract.js (engine, wasm core, and the Dutch `nld` language pack) into `static/vendor/`. The page imports only from `/static/vendor/…`. Google Fonts (Material Symbols, Cinzel) is the one remaining external ``; it degrades gracefully and isn't in the failing path.
+
+### Fixed
+- Empty language and model dropdowns whenever esm.sh was degraded. The editor's top-level `import`s pulled `@codemirror/*` from esm.sh, whose transitive graph contained an **unpinned** `@codemirror/view@^6.27.0` range URL. During esm.sh's storage outage that range returned an error body as `text/plain`, so the browser blocked the module for a disallowed MIME type — and a single failed top-level import aborts the whole `