diff --git a/src/PowderCoating.Web/Views/Shared/_Layout.cshtml b/src/PowderCoating.Web/Views/Shared/_Layout.cshtml
index fc7d47e..4a4b72e 100644
--- a/src/PowderCoating.Web/Views/Shared/_Layout.cshtml
+++ b/src/PowderCoating.Web/Views/Shared/_Layout.cshtml
@@ -351,6 +351,17 @@
gap: 1rem;
}
+ .install-app-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.45rem;
+ white-space: nowrap;
+ }
+
+ .install-app-btn i {
+ font-size: 0.95rem;
+ }
+
.user-avatar {
width: 40px;
height: 40px;
@@ -627,6 +638,10 @@
.stat-card i {
font-size: 1.25rem !important;
}
+
+ .install-app-btn .install-label {
+ display: none;
+ }
}
/* Loading States */
@@ -1434,6 +1449,12 @@
+
+
+
diff --git a/src/PowderCoating.Web/wwwroot/js/install-app.js b/src/PowderCoating.Web/wwwroot/js/install-app.js
new file mode 100644
index 0000000..07ed346
--- /dev/null
+++ b/src/PowderCoating.Web/wwwroot/js/install-app.js
@@ -0,0 +1,107 @@
+// Browser-aware install UX for supported PWAs.
+// Shows a real install button only when the browser exposes the install prompt,
+// and falls back to platform-specific instructions for iOS Safari.
+(function () {
+ 'use strict';
+
+ const installBtn = document.getElementById('installAppBtn');
+ if (!installBtn) return;
+
+ const ua = navigator.userAgent || '';
+ const isIos = /iphone|ipad|ipod/i.test(ua);
+ const isIosSafari = isIos && /safari/i.test(ua) && !/crios|fxios|edgios/i.test(ua);
+
+ let deferredPrompt = null;
+
+ function isStandalone() {
+ return window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true;
+ }
+
+ function hideInstallButton() {
+ installBtn.classList.add('d-none');
+ installBtn.dataset.installKind = '';
+ }
+
+ function showInstallButton(kind, label, iconClass) {
+ installBtn.dataset.installKind = kind;
+ installBtn.classList.remove('d-none');
+ installBtn.querySelector('.install-label').textContent = label;
+ installBtn.querySelector('i').className = `bi ${iconClass}`;
+ installBtn.setAttribute('aria-label', label);
+ installBtn.title = label;
+ }
+
+ function showHelpModal(title, message, stepsHtml) {
+ const modalEl = document.getElementById('installHelpModal');
+ const modalTitleEl = document.getElementById('installHelpModalLabel');
+ const modalMessageEl = document.getElementById('installHelpMessage');
+ const modalStepsEl = document.getElementById('installHelpSteps');
+
+ if (!modalEl || !modalTitleEl || !modalMessageEl || !modalStepsEl) return;
+
+ modalTitleEl.textContent = title;
+ modalMessageEl.textContent = message;
+ modalStepsEl.innerHTML = stepsHtml;
+ bootstrap.Modal.getOrCreateInstance(modalEl).show();
+ }
+
+ function refreshInstallUi() {
+ if (isStandalone()) {
+ hideInstallButton();
+ return;
+ }
+
+ if (deferredPrompt) {
+ showInstallButton('prompt', 'Install App', 'bi-download');
+ return;
+ }
+
+ if (isIosSafari) {
+ showInstallButton('ios', 'Add to Home Screen', 'bi-box-arrow-down');
+ return;
+ }
+
+ hideInstallButton();
+ }
+
+ window.addEventListener('beforeinstallprompt', function (event) {
+ event.preventDefault();
+ deferredPrompt = event;
+ refreshInstallUi();
+ });
+
+ window.addEventListener('appinstalled', function () {
+ deferredPrompt = null;
+ hideInstallButton();
+ });
+
+ window.matchMedia('(display-mode: standalone)').addEventListener?.('change', refreshInstallUi);
+
+ installBtn.addEventListener('click', async function () {
+ const kind = installBtn.dataset.installKind;
+
+ if (kind === 'prompt' && deferredPrompt) {
+ deferredPrompt.prompt();
+
+ try {
+ await deferredPrompt.userChoice;
+ } catch {
+ // Ignore prompt result errors; the browser owns the final install flow.
+ }
+
+ deferredPrompt = null;
+ refreshInstallUi();
+ return;
+ }
+
+ if (kind === 'ios') {
+ showHelpModal(
+ 'Add to Home Screen',
+ 'Safari on iPhone and iPad installs web apps from the Share menu.',
+ 'Tap Share in Safari, then choose Add to Home Screen.'
+ );
+ }
+ });
+
+ refreshInstallUi();
+})();
diff --git a/src/PowderCoating.Web/wwwroot/manifest.json b/src/PowderCoating.Web/wwwroot/manifest.json
index a679d9a..7bf5aba 100644
--- a/src/PowderCoating.Web/wwwroot/manifest.json
+++ b/src/PowderCoating.Web/wwwroot/manifest.json
@@ -2,7 +2,9 @@
"name": "Powder Coating Logix",
"short_name": "PCLogix",
"description": "Powder coating shop management — jobs, quotes, inventory, and scheduling.",
+ "id": "/",
"start_url": "/",
+ "scope": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#1A1A1C",