diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json
index a01d1ce..f9f11fb 100644
--- a/apps/web/messages/en.json
+++ b/apps/web/messages/en.json
@@ -180,6 +180,9 @@
"sync_conflicts_back": "Back to settings",
"settings_about": "About",
"settings_about_version": "v{version} · {commit} · {date}",
+ "settings_about_view_changelog": "View changelog",
+ "changelog_modal_title": "Changelog",
+ "changelog_modal_close": "Close",
"sync_offline": "You're offline — changes will sync when you reconnect",
"sync_syncing": "Syncing…",
"undo": "Undo",
diff --git a/apps/web/messages/es.json b/apps/web/messages/es.json
index 625bfb9..d279f27 100644
--- a/apps/web/messages/es.json
+++ b/apps/web/messages/es.json
@@ -180,6 +180,9 @@
"sync_conflicts_back": "Volver a ajustes",
"settings_about": "Acerca de",
"settings_about_version": "v{version} · {commit} · {date}",
+ "settings_about_view_changelog": "Ver historial",
+ "changelog_modal_title": "Historial de cambios",
+ "changelog_modal_close": "Cerrar",
"sync_offline": "Sin conexión — los cambios se sincronizarán al reconectar",
"sync_syncing": "Sincronizando…",
"undo": "Deshacer",
diff --git a/apps/web/src/lib/components/ChangelogModal.svelte b/apps/web/src/lib/components/ChangelogModal.svelte
new file mode 100644
index 0000000..59608e2
--- /dev/null
+++ b/apps/web/src/lib/components/ChangelogModal.svelte
@@ -0,0 +1,174 @@
+
+
+
+{#if open}
+
+
+
+
+ {m.changelog_modal_title()}
+
+
+
+
+
+ {@html html}
+
+
+
+{/if}
diff --git a/apps/web/src/lib/components/ChangelogModal.test.ts b/apps/web/src/lib/components/ChangelogModal.test.ts
new file mode 100644
index 0000000..225e324
--- /dev/null
+++ b/apps/web/src/lib/components/ChangelogModal.test.ts
@@ -0,0 +1,77 @@
+/**
+ * Fase 17.4.1 — ChangelogModal unit tests.
+ *
+ * Mirrors the Spinner.test.ts pattern (Svelte 5 `mount`/`unmount`, no
+ * @testing-library dependency). The modal imports CHANGELOG.md via Vite's
+ * `?raw` query — vitest runs against the same Vite config (see
+ * apps/web/vitest.config.ts) so the import resolves to the file contents at
+ * test time.
+ *
+ * Specs:
+ * CHM-U-01 — `open={false}` renders nothing.
+ * CHM-U-02 — `open={true}` renders the CHANGELOG `