diff --git a/containers/libreportal/frontend/features/backup/index.js b/containers/libreportal/frontend/features/backup/index.js
new file mode 100644
index 0000000..b80f39b
--- /dev/null
+++ b/containers/libreportal/frontend/features/backup/index.js
@@ -0,0 +1,41 @@
+// features/backup/index.js โ Backup Center as a self-contained feature module.
+//
+// FIRST page migrated to the feature-module contract (docs/frontend-modularization.md).
+// The kernel drives mount()/unmount() for the /backup route instead of
+// spa.js's handleBackup(). The heavy controller (backup-page.js, ~129KB) is
+// still lazy-loaded on first mount, so cold-load cost is unchanged.
+//
+// Snap-out demo: deleting this folder removes the backup route's module
+// registration; the manifest entry then falls back to the legacy handler
+// (and, once handleBackup is retired, to the kernel's not-found route). The
+// full decomposition of backup-page.js into per-tab modules is Phase 5.
+LP.features.register({
+ id: 'backup',
+ routes: ['/backup', '/backup*'],
+ // Controllers the feature needs; lazy-loaded on first mount (idempotent).
+ scripts: [
+ '/js/components/backup/backup-page.js',
+ '/js/components/backup/backup-app-card.js',
+ ],
+
+ async mount(ctx) {
+ await ctx.loadScripts(this.scripts);
+ const html = await ctx.loadFragment('/html/backup-content.html');
+ ctx.setContent(html, 'Backups');
+ if (typeof BackupPage === 'undefined') {
+ throw new Error('BackupPage controller failed to load');
+ }
+ window.backupPage = new BackupPage();
+ await window.backupPage.init();
+ },
+
+ async unmount() {
+ // Best-effort teardown. BackupPage self-guards stale work via
+ // (window.backupPage === this), so nulling the global neutralises any
+ // pending task-refresh repaint; we also drop its coordinator registration.
+ // A proper dispose() (removing the leaked document listeners) lands with
+ // the Phase 5 backup decomposition.
+ try { window.taskRefresh && window.taskRefresh.unregister && window.taskRefresh.unregister('backups'); } catch (_) {}
+ window.backupPage = null;
+ },
+});
diff --git a/containers/libreportal/frontend/index.html b/containers/libreportal/frontend/index.html
index 4980170..3f1890d 100755
--- a/containers/libreportal/frontend/index.html
+++ b/containers/libreportal/frontend/index.html
@@ -108,6 +108,10 @@
loads the page manifest; spa.js consults it for routing. See
docs/frontend-modularization.md. -->
+
+
+