diff --git a/widgets/files.zip b/widgets/files.zip
deleted file mode 100644
index 575c7b5..0000000
Binary files a/widgets/files.zip and /dev/null differ
diff --git a/widgets/w1_system_health_indicator copy.json b/widgets/w1_system_health_indicator copy.json
deleted file mode 100644
index d455650..0000000
--- a/widgets/w1_system_health_indicator copy.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "html": "
\nConnecting\u2026
\n",
-
- "css": "*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n:root {\n --bg: #09111a;\n --text0: #d8eeff;\n --text1: #6a9bbf;\n --green: #00e09a;\n --amber: #ffb830;\n --red: #ff3a5a;\n --dim: #1a2d42;\n --border: #1e3050;\n --mono: 'Courier New', monospace;\n}\nbody {\n background: var(--bg);\n color: var(--text0);\n font-family: var(--mono);\n height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n user-select: none;\n}\n#circles { display: flex; gap: 12px; align-items: center; justify-content: center; margin-bottom: 10px; }\n.circle-wrap { display: flex; flex-direction: column; align-items: center; gap: 4px; }\n.circle {\n width: 36px; height: 36px;\n border-radius: 50%;\n border: 2px solid var(--border);\n background: var(--dim);\n transition: background 0.3s, border-color 0.3s, box-shadow 0.3s;\n}\n.circle.green { background: var(--green); border-color: var(--green); box-shadow: 0 0 12px var(--green); }\n.circle.amber { background: var(--amber); border-color: var(--amber); box-shadow: 0 0 12px var(--amber); }\n.circle.red { background: var(--red); border-color: var(--red); animation: flash-red 1s ease-in-out infinite; }\n@keyframes flash-red {\n 0%, 100% { box-shadow: 0 0 16px var(--red); }\n 50% { box-shadow: 0 0 32px var(--red), 0 0 8px #ff000088; }\n}\n.circle-label { font-size: 9px; color: var(--text1); text-transform: uppercase; letter-spacing: 0.08em; }\n.circle-label.active { color: var(--text0); font-weight: bold; }\n#message { font-size: 11px; color: var(--text1); text-align: center; max-width: 180px; line-height: 1.4; min-height: 16px; }\n#message.green { color: var(--green); }\n#message.amber { color: var(--amber); }\n#message.red { color: var(--red); }\n#footer { margin-top: 6px; font-size: 9px; color: #2a4060; }",
-
- "js": "// Config\nvar VARIABLE = 'rov_failsafe';\nvar POLL_MS = 500;\nvar STATE_GREEN = 0, STATE_AMBER = 1, STATE_RED = 2;\nvar MESSAGES = {};\nMESSAGES[STATE_GREEN] = 'Systems nominal';\nMESSAGES[STATE_AMBER] = 'Parameter degraded \u2014 check system';\nMESSAGES[STATE_RED] = 'CRITICAL \u2014 Safe action triggered';\n\n// DOM\nvar cGreen = document.getElementById('c-green');\nvar cAmber = document.getElementById('c-amber');\nvar cRed = document.getElementById('c-red');\nvar lGreen = document.getElementById('l-green');\nvar lAmber = document.getElementById('l-amber');\nvar lRed = document.getElementById('l-red');\nvar msgEl = document.getElementById('message');\nvar footEl = document.getElementById('footer');\n\nfunction applyState(state, reason) {\n [cGreen, cAmber, cRed].forEach(function(c) { c.classList.remove('green','amber','red'); });\n [lGreen, lAmber, lRed].forEach(function(l) { l.classList.remove('active'); });\n msgEl.classList.remove('green','amber','red');\n if (state === STATE_GREEN) {\n cGreen.classList.add('green'); lGreen.classList.add('active');\n msgEl.classList.add('green'); msgEl.textContent = reason || MESSAGES[STATE_GREEN];\n } else if (state === STATE_AMBER) {\n cAmber.classList.add('amber'); lAmber.classList.add('active');\n msgEl.classList.add('amber'); msgEl.textContent = reason || MESSAGES[STATE_AMBER];\n } else if (state === STATE_RED) {\n cRed.classList.add('red'); lRed.classList.add('active');\n msgEl.classList.add('red'); msgEl.textContent = reason || MESSAGES[STATE_RED];\n } else {\n msgEl.textContent = 'Waiting for data\u2026';\n }\n}\n\nfunction poll() {\n try {\n if (typeof window.cockpit === 'undefined' || typeof window.cockpit.getDataLakeValue !== 'function') {\n applyState(null); footEl.textContent = 'API not ready'; return;\n }\n var raw = window.cockpit.getDataLakeValue(VARIABLE);\n if (raw === null || raw === undefined) {\n applyState(null); footEl.textContent = 'Waiting for ' + VARIABLE; return;\n }\n applyState(parseInt(raw, 10), null);\n footEl.textContent = 'Updated ' + new Date().toLocaleTimeString();\n } catch(err) {\n applyState(null); footEl.textContent = 'Poll error';\n console.error('[Health Widget]', err);\n }\n}\n\nsetTimeout(poll, 300);\nsetInterval(poll, POLL_MS);"
-}
diff --git a/widgets/w2_mission_status copy.json b/widgets/w2_mission_status copy.json
deleted file mode 100644
index be11e1c..0000000
--- a/widgets/w2_mission_status copy.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "html": "\u2014
\n\n\u2014%
\nWaypoint: \u2014
\n",
-
- "css": "*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n:root {\n --bg: #09111a;\n --border: #1e3050;\n --text0: #d8eeff;\n --text1: #6a9bbf;\n --green: #00e09a;\n --amber: #ffb830;\n --red: #ff3a5a;\n --blue: #00c8f0;\n --dim: #1a2d42;\n --mono: 'Courier New', monospace;\n}\nbody {\n background: var(--bg); color: var(--text0); font-family: var(--mono);\n height: 100vh; display: flex; flex-direction: column;\n align-items: center; justify-content: center;\n overflow: hidden; padding: 10px 14px; gap: 8px;\n}\n#state-label {\n font-size: 18px; font-weight: bold;\n letter-spacing: 0.12em; text-transform: uppercase; color: var(--text1);\n}\n#state-label.idle { color: var(--text1); }\n#state-label.running { color: var(--green); }\n#state-label.paused { color: var(--amber); }\n#state-label.complete { color: var(--blue); }\n#state-label.aborted { color: var(--red); }\n#state-label.waiting { color: var(--dim); }\n#bar-wrap {\n width: 100%; height: 8px;\n background: var(--dim); border-radius: 4px;\n border: 1px solid var(--border); overflow: hidden;\n}\n#bar-fill { height: 100%; width: 0%; border-radius: 4px; background: var(--green); transition: width 0.5s ease, background 0.3s; }\n#pct-label { font-size: 11px; color: var(--text1); align-self: flex-end; margin-top: -4px; }\n#waypoint { font-size: 10px; color: var(--text1); text-align: center; }\n#footer { font-size: 9px; color: #1e3050; }",
-
- "js": "var VAR_STATE = 'rov_mission_state';\nvar VAR_PROGRESS = 'rov_mission_progress';\nvar POLL_MS = 500;\nvar STATE = {0:'IDLE',1:'RUNNING',2:'PAUSED',3:'COMPLETE',4:'ABORTED'};\nvar META = {\n 0:{cls:'idle', bar:'#1a2d42'},\n 1:{cls:'running', bar:'#00e09a'},\n 2:{cls:'paused', bar:'#ffb830'},\n 3:{cls:'complete',bar:'#00c8f0'},\n 4:{cls:'aborted', bar:'#ff3a5a'}\n};\nvar stateLabelEl = document.getElementById('state-label');\nvar barFillEl = document.getElementById('bar-fill');\nvar pctLabelEl = document.getElementById('pct-label');\nvar waypointEl = document.getElementById('waypoint');\nvar footerEl = document.getElementById('footer');\n\nfunction render(state, progress) {\n var stateStr = STATE[state] || '?';\n var meta = META[state] || {cls:'waiting', bar:'#1a2d42'};\n var pct = Math.round((progress || 0) * 100);\n stateLabelEl.className = meta.cls;\n stateLabelEl.textContent = stateStr;\n barFillEl.style.width = pct + '%';\n barFillEl.style.background = meta.bar;\n pctLabelEl.textContent = (state===1||state===2) ? pct+'%' : (state===3 ? '100%' : '\u2014%');\n if (state===1||state===2) waypointEl.textContent = 'Progress ' + pct + '% complete';\n else if (state===3) waypointEl.textContent = 'Mission complete';\n else if (state===4) waypointEl.textContent = 'Mission aborted \u2014 vehicle holding';\n else waypointEl.textContent = 'No mission active';\n footerEl.textContent = 'Updated ' + new Date().toLocaleTimeString();\n}\n\nfunction poll() {\n try {\n if (typeof window.cockpit === 'undefined' || typeof window.cockpit.getDataLakeValue !== 'function') {\n stateLabelEl.className = 'waiting'; stateLabelEl.textContent = '\u2014';\n footerEl.textContent = 'API not ready'; return;\n }\n var raw = window.cockpit.getDataLakeValue(VAR_STATE);\n if (raw === null || raw === undefined) {\n stateLabelEl.className = 'waiting'; stateLabelEl.textContent = '\u2014';\n footerEl.textContent = 'Waiting for ' + VAR_STATE; return;\n }\n var prog = window.cockpit.getDataLakeValue(VAR_PROGRESS);\n render(parseInt(raw,10), parseFloat(prog||0));\n } catch(err) {\n stateLabelEl.className='waiting'; stateLabelEl.textContent='ERR';\n footerEl.textContent='Poll error'; console.error('[MissionStatus]',err);\n }\n}\nsetTimeout(poll,300); setInterval(poll,POLL_MS);"
-}
diff --git a/widgets/w3_abort_button copy.json b/widgets/w3_abort_button copy.json
deleted file mode 100644
index d65395c..0000000
--- a/widgets/w3_abort_button copy.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "html": "\nStandby
\n\n
\n
Abort mission?
Vehicle will hold position
and await instruction.
\n
\n \n \n
\n
\n
\n
",
-
- "css": "*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n:root {\n --bg: #09111a; --text0: #d8eeff; --text1: #8fb3d4;\n --red: #ff3a5a; --red-dim: #7a1a28; --green: #00e09a; --mono: 'Courier New', monospace;\n}\nbody {\n background: var(--bg); color: var(--text0); font-family: var(--mono);\n height: 100vh; display: flex; flex-direction: column;\n align-items: center; justify-content: center;\n overflow: hidden; padding: 12px; gap: 10px;\n}\n#abort-btn {\n width: 100%; padding: 14px 0;\n background: var(--red-dim); border: 2px solid var(--red); border-radius: 6px;\n color: var(--red); font-family: var(--mono); font-size: 15px; font-weight: bold;\n letter-spacing: 0.15em; text-transform: uppercase; cursor: pointer;\n transition: background 0.15s, box-shadow 0.15s;\n}\n#abort-btn:hover { background: #2a0c18; box-shadow: 0 0 16px var(--red); }\n#abort-btn:active { background: var(--red); color: #fff; }\n#abort-btn:disabled { background: #1a0a10; border-color: #3a1020; color: var(--red-dim); cursor: not-allowed; box-shadow: none; }\n#confirm-overlay {\n display: none; position: fixed; inset: 0;\n background: rgba(0,0,0,0.7);\n align-items: center; justify-content: center; z-index: 10;\n}\n#confirm-overlay.active { display: flex; }\n#confirm-box {\n background: #100510; border: 2px solid var(--red); border-radius: 8px;\n padding: 18px; max-width: 220px; text-align: center;\n box-shadow: 0 0 30px var(--red);\n}\n#confirm-box p { font-size: 12px; color: var(--text0); line-height: 1.5; margin-bottom: 14px; }\n.btn-row { display: flex; gap: 8px; justify-content: center; }\n#confirm-yes {\n flex: 1; padding: 8px; background: var(--red); border: none;\n border-radius: 4px; color: #fff; font-family: var(--mono);\n font-size: 12px; font-weight: bold; cursor: pointer; letter-spacing: 0.08em;\n}\n#confirm-yes:hover { background: #ff6080; }\n#confirm-no {\n flex: 1; padding: 8px; background: transparent;\n border: 1px solid var(--text1); border-radius: 4px;\n color: var(--text1); font-family: var(--mono); font-size: 12px; cursor: pointer;\n}\n#confirm-no:hover { border-color: var(--text0); color: var(--text0); }\n#countdown { font-size: 10px; color: var(--red-dim); margin-top: 8px; }\n#status { font-size: 10px; color: var(--text1); text-align: center; min-height: 14px; }\n#status.ok { color: var(--green); }\n#status.err { color: var(--red); }",
-
- "js": "var FASTAPI_HOST = 'http://blueos.local:8081';\nvar AUTO_DISMISS_SEC = 10;\nvar abortBtn = document.getElementById('abort-btn');\nvar overlay = document.getElementById('confirm-overlay');\nvar confirmYes = document.getElementById('confirm-yes');\nvar confirmNo = document.getElementById('confirm-no');\nvar countdownEl = document.getElementById('countdown');\nvar statusEl = document.getElementById('status');\nvar dismissTimer = null;\nvar countdownVal = AUTO_DISMISS_SEC;\n\nfunction showConfirm() {\n overlay.classList.add('active');\n countdownVal = AUTO_DISMISS_SEC;\n countdownEl.textContent = 'Auto-cancel in ' + countdownVal + 's';\n dismissTimer = setInterval(function() {\n countdownVal--;\n countdownEl.textContent = 'Auto-cancel in ' + countdownVal + 's';\n if (countdownVal <= 0) hideConfirm();\n }, 1000);\n}\n\nfunction hideConfirm() {\n overlay.classList.remove('active');\n clearInterval(dismissTimer);\n dismissTimer = null;\n countdownEl.textContent = '';\n}\n\nasync function sendAbort() {\n hideConfirm();\n abortBtn.disabled = true;\n statusEl.className = '';\n statusEl.textContent = 'Sending abort\u2026';\n try {\n var res = await fetch(FASTAPI_HOST + '/abort', {\n method: 'POST',\n headers: {'Content-Type':'application/json'},\n body: JSON.stringify({abort:true}),\n signal: AbortSignal.timeout(5000)\n });\n if (res.ok) {\n statusEl.className = 'ok';\n statusEl.textContent = 'Abort sent \u2014 vehicle holding';\n } else {\n statusEl.className = 'err';\n statusEl.textContent = 'Server error: ' + res.status;\n abortBtn.disabled = false;\n }\n } catch(err) {\n statusEl.className = 'err';\n statusEl.textContent = 'Network error \u2014 check connection';\n abortBtn.disabled = false;\n console.error('[Abort]', err);\n }\n}\n\nabortBtn.addEventListener('click', function() { if (!abortBtn.disabled) showConfirm(); });\nconfirmYes.addEventListener('click', sendAbort);\nconfirmNo.addEventListener('click', hideConfirm);"
-}
diff --git a/widgets/w4_mission_setup_button copy.json b/widgets/w4_mission_setup_button copy.json
deleted file mode 100644
index 1ba13e8..0000000
--- a/widgets/w4_mission_setup_button copy.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "html": "\n\u2014
",
-
- "css": "*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n:root {\n --bg: #09111a; --border: #1e3050;\n --text0: #d8eeff; --text1: #6a9bbf;\n --accent: #00c8f0; --dim: #1a2d42; --amber: #ffb830;\n --mono: 'Courier New', monospace;\n}\nbody {\n background: var(--bg); color: var(--text0); font-family: var(--mono);\n height: 100vh; display: flex; flex-direction: column;\n align-items: center; justify-content: center;\n overflow: hidden; padding: 10px; gap: 6px;\n}\n#setup-btn {\n width: 100%; padding: 10px 0;\n background: transparent; border: 1px solid var(--accent); border-radius: 5px;\n color: var(--accent); font-family: var(--mono); font-size: 12px; font-weight: bold;\n letter-spacing: 0.1em; text-transform: uppercase; cursor: pointer;\n transition: background 0.2s, border-color 0.2s, color 0.2s, opacity 0.3s;\n}\n#setup-btn:hover { background: rgba(0,200,240,0.1); box-shadow: 0 0 10px rgba(0,200,240,0.3); }\n#setup-btn.subdued { border-color: var(--dim); color: var(--text1); opacity: 0.5; }\n#setup-btn.subdued:hover { opacity: 0.75; background: transparent; box-shadow: none; }\n#status { font-size: 9px; color: var(--text1); text-align: center; }\n#status.active-warn { color: var(--amber); }",
-
- "js": "var SETUP_URL = 'http://blueos.local:8081/setup';\nvar VAR_STATE = 'rov_mission_state';\nvar POLL_MS = 1000;\nvar setupBtn = document.getElementById('setup-btn');\nvar statusEl = document.getElementById('status');\n\nsetupBtn.addEventListener('click', function() {\n window.open(SETUP_URL, '_blank', 'noopener,noreferrer');\n});\n\nfunction poll() {\n try {\n if (typeof window.cockpit === 'undefined' || typeof window.cockpit.getDataLakeValue !== 'function') {\n setupBtn.classList.remove('subdued'); statusEl.className = '';\n statusEl.textContent = 'Connecting\u2026'; return;\n }\n var raw = window.cockpit.getDataLakeValue(VAR_STATE);\n if (raw === null || raw === undefined) {\n setupBtn.classList.remove('subdued'); statusEl.className = '';\n statusEl.textContent = 'No mission loaded'; return;\n }\n var state = parseInt(raw, 10);\n if (state === 1 || state === 2) {\n setupBtn.classList.add('subdued'); statusEl.className = 'active-warn';\n statusEl.textContent = state === 1\n ? 'Mission running \u2014 setup changes not recommended'\n : 'Mission paused \u2014 setup changes not recommended';\n } else {\n setupBtn.classList.remove('subdued'); statusEl.className = '';\n statusEl.textContent = state === 0\n ? 'No mission loaded \u2014 configure before diving'\n : 'Ready to configure next mission';\n }\n } catch(err) { console.error('[SetupBtn]', err); }\n}\nsetTimeout(poll, 300); setInterval(poll, POLL_MS);"
-}
diff --git a/widgets/w5_battery_return_budget copy.json b/widgets/w5_battery_return_budget copy.json
deleted file mode 100644
index 673c968..0000000
--- a/widgets/w5_battery_return_budget copy.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "html": "Return Budget\n\n Battery\n --%\n
\n\n Required\n --%\n
\n\n\n
\n Headroom\n --\n
\n
\n
\nWaiting for data\u2026
\n",
-
- "css": "*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n:root {\n --bg: #09111a; --border: #1e3050;\n --text0: #d8eeff; --text1: #6a9bbf;\n --green: #00e09a; --amber: #ffb830; --red: #ff3a5a; --dim: #1a2d42;\n --mono: 'Courier New', monospace;\n}\nbody {\n background: var(--bg); color: var(--text0); font-family: var(--mono);\n height: 100vh; display: flex; flex-direction: column;\n align-items: center; justify-content: center;\n overflow: hidden; padding: 8px 12px; gap: 5px;\n}\n.section-label { font-size: 9px; color: var(--text1); text-transform: uppercase; letter-spacing: 0.1em; align-self: flex-start; }\n.data-row { display: flex; width: 100%; justify-content: space-between; align-items: baseline; gap: 8px; }\n.data-label { font-size: 10px; color: var(--text1); white-space: nowrap; }\n.data-value { font-size: 16px; font-weight: bold; color: var(--text0); text-align: right; min-width: 48px; transition: color 0.3s; }\n.divider { width: 100%; height: 1px; background: var(--border); flex-shrink: 0; }\n#headroom-wrap { width: 100%; }\n#headroom-bar-bg { width: 100%; height: 5px; background: var(--dim); border-radius: 3px; overflow: hidden; margin-top: 2px; }\n#headroom-bar-fill { height: 100%; border-radius: 3px; background: var(--green); transition: width 0.5s ease, background 0.3s; }\n#status-text { font-size: 10px; color: var(--text1); text-align: center; min-height: 12px; transition: color 0.3s; }\n.green { color: var(--green) !important; }\n.amber { color: var(--amber) !important; }\n.red { color: var(--red) !important; }\n#footer { font-size: 9px; color: #1a2d42; margin-top: 1px; }",
-
- "js": "var VAR_BATTERY = 'SYS_STATUS/battery_remaining';\nvar VAR_BUDGET = 'rov_return_budget';\nvar WARN = 15, CRIT = 5, POLL_MS = 1000;\nvar battValEl = document.getElementById('batt-val');\nvar budgetValEl = document.getElementById('budget-val');\nvar headroomValEl = document.getElementById('headroom-val');\nvar headroomFillEl = document.getElementById('headroom-bar-fill');\nvar statusTextEl = document.getElementById('status-text');\nvar footerEl = document.getElementById('footer');\n\nfunction setClass(el, cls) { el.classList.remove('green','amber','red'); if(cls) el.classList.add(cls); }\n\nfunction render(batt, budget, budgetLive) {\n batt = Math.round(batt);\n budget = Math.round(budget);\n var margin = batt - budget;\n battValEl.textContent = batt + '%';\n setClass(battValEl, batt > 40 ? 'green' : batt > 20 ? 'amber' : 'red');\n budgetValEl.textContent = budget + '%';\n headroomValEl.textContent = margin + '%';\n var barPct = batt > 0 ? Math.min(100, Math.max(0, (margin / batt) * 100)) : 0;\n headroomFillEl.style.width = barPct + '%';\n var sev, msg;\n if (margin < CRIT) { sev = 'red'; msg = '\u26a0 CRITICAL \u2014 insufficient return budget'; }\n else if (margin < WARN) { sev = 'amber'; msg = 'Low headroom \u2014 consider returning'; }\n else { sev = 'green'; msg = 'Sufficient return headroom'; }\n var colors = {green:'var(--green)', amber:'var(--amber)', red:'var(--red)'};\n headroomFillEl.style.background = colors[sev];\n setClass(headroomValEl, sev); setClass(statusTextEl, sev);\n statusTextEl.textContent = msg;\n footerEl.textContent = budgetLive\n ? 'Updated ' + new Date().toLocaleTimeString()\n : 'Updated ' + new Date().toLocaleTimeString() + ' (budget: FastAPI pending)';\n}\n\nfunction poll() {\n try {\n if (typeof window.cockpit === 'undefined' || typeof window.cockpit.getDataLakeValue !== 'function') {\n statusTextEl.textContent = 'API not ready'; return;\n }\n var battRaw = window.cockpit.getDataLakeValue(VAR_BATTERY);\n if (battRaw === null || battRaw === undefined) { statusTextEl.textContent = 'Waiting for battery data\u2026'; return; }\n var batt = parseInt(battRaw, 10);\n if (batt === -1) { statusTextEl.textContent = 'Battery % not reported by vehicle'; battValEl.textContent='??%'; return; }\n var budgetRaw = window.cockpit.getDataLakeValue(VAR_BUDGET);\n var budgetLive = budgetRaw !== null && budgetRaw !== undefined;\n render(batt, budgetLive ? parseFloat(budgetRaw) : 0, budgetLive);\n } catch(err) { statusTextEl.textContent='Poll error'; console.error('[Budget]',err); }\n}\nsetTimeout(poll,300); setInterval(poll,POLL_MS);"
-}