diff --git a/frontend/index.html b/frontend/index.html
index 1ff3e70..02ccb1e 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -62,6 +62,10 @@ textarea { resize: vertical; min-height: 120px; }
.offline-work-controls label { display:flex; gap:8px; align-items:center; min-height:44px; }
.offline-work-controls input { width:20px; height:20px; }
.offline-work-controls button { min-height:44px; }
+.install-app-card { display:flex; gap:10px; align-items:center; flex-wrap:wrap; width:100%; padding:10px; border:1px solid #31577f; border-radius:12px; background:#10233a; }
+.install-app-card[hidden] { display:none; }
+.install-app-card p { margin:0; flex:1 1 240px; }
+.install-app-card button { min-height:44px; }
.widget { border: 1px solid #1b2d45; border-radius: 12px; padding: 10px; background: linear-gradient(180deg,#0f1d33,#0b1526); }
.widget h3 { margin: 4px 0 8px; font-size: 13px; color: #7aa1c9; }
.event { padding: 8px 0; border-bottom: 1px solid #1b2d45; }
@@ -343,6 +347,13 @@ textarea { resize: vertical; min-height: 120px; }
+
+
Install Stackchain
Keep mobile work one tap away and launch the saved app shell during an outage.
+
On Safari, tap Share, then choose Add to Home Screen.
+
+
+
+
@@ -792,6 +803,7 @@ textarea { resize: vertical; min-height: 120px; }
+
' in html
+ assert 'createInstallApp({' in html
+ assert "window.matchMedia('(display-mode: standalone)').matches" in html
+ assert "navigator.standalone === true" in html
+ assert "isIosSafari" in html
diff --git a/tests/test_service_worker.py b/tests/test_service_worker.py
index 5eb2298..940abad 100644
--- a/tests/test_service_worker.py
+++ b/tests/test_service_worker.py
@@ -91,11 +91,11 @@ async function dispatchNotificationClick(route) {{
return json.loads(completed.stdout)
-def test_live_section_revisions_ship_in_a_new_shell_cache():
+def test_mobile_install_flow_ships_in_a_new_shell_cache():
source = WORKER.read_text()
- assert "stackchain-dashboard-shell-v23" in source
- assert "BASE + 'static/today-work.js'" in source
+ assert "stackchain-dashboard-shell-v24" in source
+ assert "BASE + 'static/install-app.js'" in source
def test_background_sync_event_flushes_closed_app_issue_outbox_only_for_its_tag():
@@ -207,6 +207,7 @@ def test_install_precaches_complete_subpath_scoped_app_shell():
"/dashboard/static/work-route.js",
"/dashboard/static/context-poller.js",
"/dashboard/static/mobile-task-dock.js",
+ "/dashboard/static/install-app.js",
"/dashboard/static/background-issue-sync.js",
}