diff --git a/status/dist/core.js b/status/dist/core.js
index e00f19b..7523faa 100644
--- a/status/dist/core.js
+++ b/status/dist/core.js
@@ -1,4 +1,4 @@
-(function(l){"use strict";class o{constructor(){this.errors=[],this.maxErrors=50}logError(h,f,m={}){const b={timestamp:new Date().toISOString(),context:h,message:f instanceof Error?f.message:f,stack:f instanceof Error?f.stack:null,metadata:m};this.errors.push(b),this.errors.length>this.maxErrors&&this.errors.shift(),console.error(`[Onboarding Error] ${h}:`,f,m)}recoverFromError(h,f){switch(this.classifyError(h)){case"ELEMENT_NOT_FOUND":return this.logError("Element Not Found",h,{currentStep:f}),{action:"SKIP_STEP",nextStep:f+1,message:"Target element not found, skipping to next step"};case"STORAGE_UNAVAILABLE":return this.logError("Storage Unavailable",h),{action:"USE_MEMORY_STORAGE",message:"Local storage unavailable, using in-memory storage"};case"DRIVER_NOT_LOADED":return this.logError("Driver.js Not Loaded",h),{action:"ABORT_TOUR",message:"Driver.js library not loaded, cannot start tour"};case"INVALID_TOOLTIP":return this.logError("Invalid Tooltip Configuration",h,{currentStep:f}),{action:"SKIP_STEP",nextStep:f+1,message:"Invalid tooltip configuration, skipping"};case"THEME_DETECTION_FAILED":return this.logError("Theme Detection Failed",h),{action:"USE_DEFAULT_THEME",message:"Using default dark theme"};default:return this.logError("Unknown Error",h,{currentStep:f}),{action:"ABORT_TOUR",message:"Unexpected error occurred, aborting tour"}}}classifyError(h){const f=h.message||h.toString();return f.includes("element")&&f.includes("not found")?"ELEMENT_NOT_FOUND":f.includes("storage")||f.includes("quota")?"STORAGE_UNAVAILABLE":f.includes("driver")||f.includes("undefined")?"DRIVER_NOT_LOADED":f.includes("invalid")||f.includes("validation")?"INVALID_TOOLTIP":f.includes("theme")?"THEME_DETECTION_FAILED":"UNKNOWN"}getErrors(){return[...this.errors]}clearErrors(){this.errors=[]}getStatistics(){const h={total:this.errors.length,byContext:{},byType:{},recent:this.errors.slice(-10)};return this.errors.forEach(f=>{h.byContext[f.context]=(h.byContext[f.context]||0)+1;const m=this.classifyError({message:f.message});h.byType[m]=(h.byType[m]||0)+1}),h}handleDriverLoadFailure(){this.logError("Driver.js Load Failure","Driver.js library failed to load");const h=document.createElement("div");return h.id="onboarding-fallback",h.style.cssText=`
+(function(l){"use strict";class o{constructor(){this.errors=[],this.maxErrors=50}logError(b,f,v={}){const g={timestamp:new Date().toISOString(),context:b,message:f instanceof Error?f.message:f,stack:f instanceof Error?f.stack:null,metadata:v};this.errors.push(g),this.errors.length>this.maxErrors&&this.errors.shift(),console.error(`[Onboarding Error] ${b}:`,f,v)}recoverFromError(b,f){switch(this.classifyError(b)){case"ELEMENT_NOT_FOUND":return this.logError("Element Not Found",b,{currentStep:f}),{action:"SKIP_STEP",nextStep:f+1,message:"Target element not found, skipping to next step"};case"STORAGE_UNAVAILABLE":return this.logError("Storage Unavailable",b),{action:"USE_MEMORY_STORAGE",message:"Local storage unavailable, using in-memory storage"};case"DRIVER_NOT_LOADED":return this.logError("Driver.js Not Loaded",b),{action:"ABORT_TOUR",message:"Driver.js library not loaded, cannot start tour"};case"INVALID_TOOLTIP":return this.logError("Invalid Tooltip Configuration",b,{currentStep:f}),{action:"SKIP_STEP",nextStep:f+1,message:"Invalid tooltip configuration, skipping"};case"THEME_DETECTION_FAILED":return this.logError("Theme Detection Failed",b),{action:"USE_DEFAULT_THEME",message:"Using default dark theme"};default:return this.logError("Unknown Error",b,{currentStep:f}),{action:"ABORT_TOUR",message:"Unexpected error occurred, aborting tour"}}}classifyError(b){const f=b.message||b.toString();return f.includes("element")&&f.includes("not found")?"ELEMENT_NOT_FOUND":f.includes("storage")||f.includes("quota")?"STORAGE_UNAVAILABLE":f.includes("driver")||f.includes("undefined")?"DRIVER_NOT_LOADED":f.includes("invalid")||f.includes("validation")?"INVALID_TOOLTIP":f.includes("theme")?"THEME_DETECTION_FAILED":"UNKNOWN"}getErrors(){return[...this.errors]}clearErrors(){this.errors=[]}getStatistics(){const b={total:this.errors.length,byContext:{},byType:{},recent:this.errors.slice(-10)};return this.errors.forEach(f=>{b.byContext[f.context]=(b.byContext[f.context]||0)+1;const v=this.classifyError({message:f.message});b.byType[v]=(b.byType[v]||0)+1}),b}handleDriverLoadFailure(){this.logError("Driver.js Load Failure","Driver.js library failed to load");const b=document.createElement("div");return b.id="onboarding-fallback",b.style.cssText=`
position: fixed;
bottom: 20px;
right: 20px;
@@ -10,20 +10,20 @@
z-index: 9999;
max-width: 300px;
font-size: 14px;
- `,h.innerHTML=`
+ `,b.innerHTML=`
Welcome to DashCaddy!
The interactive tour is unavailable, but you can explore the dashboard freely.
Check the documentation for help getting started.
- `,document.body.appendChild(h),setTimeout(()=>{h.parentNode&&h.parentNode.removeChild(h)},1e4),!0}handleStorageUnavailable(){this.logError("Storage Unavailable","Local storage is not available");const h={data:{},getItem(f){return this.data[f]||null},setItem(f,m){this.data[f]=m},removeItem(f){delete this.data[f]},clear(){this.data={}}};return console.warn("[ErrorHandler] Using in-memory storage - progress will not persist"),h}sendToErrorTracking(h){}}l.ErrorHandler=o,console.log("[ErrorHandler] Module loaded")})(window),(function(){try{var o=typeof localStorage<"u"&&localStorage.getItem("dashcaddy-health-settings")||null;if(o){var g=JSON.parse(o);g.statsPollingInterval&&g.statsPollingInterval>=5&&g.statsPollingInterval<=3600&&(window.__DC_STATS_OVERRIDE=g.statsPollingInterval*1e3)}}catch{}})();const DC={NAME:"DashCaddy",POLL:{DASHBOARD:1e4,LOGS:3e3,STATS:typeof window<"u"&&window.__DC_STATS_OVERRIDE||5e3,WEATHER:6e5,HEALTH:1e3,DEPLOY_SSL:5e3},DELAYS:{BTN_RESET:2e3,RELOAD:5e3,MODAL_CLOSE:500,PORT_CHECK:500,DEPLOY_INIT:3e3},DEFAULTS:{DNS_PORT:"5380",SERVICE_PORT:"8080",TTL:300,CADDYFILE:"C:\\caddy\\Caddyfile"}},errorHandler=new ErrorHandler,_cachedCfg=JSON.parse(localStorage.getItem("dashcaddy_site_config")||"null"),SITE={tld:_cachedCfg&&_cachedCfg.tld||".home",dnsIp:"",dnsPort:DC.DEFAULTS.DNS_PORT,dnsServers:{},configurationType:_cachedCfg&&_cachedCfg.configurationType||"homelab",domain:_cachedCfg&&_cachedCfg.domain||"",defaults:_cachedCfg&&_cachedCfg.defaults||{},routingMode:_cachedCfg&&_cachedCfg.routingMode||"subdomain",onboardingCompleted:!1};window.__dashcaddySiteConfigLoaded=(async function(){try{const h=await fetch("/api/v1/config");if(h.ok){const f=await h.json();if(f.tld&&(SITE.tld=f.tld.startsWith(".")?f.tld:"."+f.tld),f.dns&&(SITE.dnsIp=f.dns.ip||"",SITE.dnsPort=f.dns.port||DC.DEFAULTS.DNS_PORT),f.dnsServers&&typeof f.dnsServers=="object")for(const[b,r]of Object.entries(f.dnsServers))b!=="__proto__"&&b!=="constructor"&&b!=="prototype"&&(SITE.dnsServers[b]=r);f.configurationType&&(SITE.configurationType=f.configurationType),f.domain&&(SITE.domain=f.domain),f.defaults&&(SITE.defaults=f.defaults),f.routingMode&&(SITE.routingMode=f.routingMode),SITE.onboardingCompleted=f.onboardingCompleted===!0,localStorage.setItem("dashcaddy_site_config",JSON.stringify({tld:SITE.tld,configurationType:SITE.configurationType,domain:SITE.domain,routingMode:SITE.routingMode})),renderDnsCards();const m=document.getElementById("manage-tokens");m&&(m.style.display=Object.keys(SITE.dnsServers).length?"":"none")}}catch{}document.querySelectorAll("[data-tld]").forEach(h=>h.textContent=SITE.tld);const o=document.getElementById("edit-tld-suffix");o&&(o.textContent=SITE.tld);const g=document.getElementById("external-proxy-ip");g&&SITE.dnsIp&&(g.value=SITE.dnsIp,g.placeholder=SITE.dnsIp)})();function buildDomain(l){return l+SITE.tld}function buildServiceUrl(l){return SITE.routingMode==="subdirectory"&&SITE.domain?"https://"+SITE.domain+"/"+l:SITE.configurationType==="public"&&SITE.domain?"https://"+l+"."+SITE.domain:"https://"+buildDomain(l)}function getDnsServerAddr(l){const o=SITE.dnsServers[l];return o?`${o.ip}:${o.port}`:buildDomain(l)}function getPrimaryDnsId(){if(!SITE.dnsIp)return null;for(const[l,o]of Object.entries(SITE.dnsServers))if(o.ip===SITE.dnsIp)return l;return null}function renderDnsCards(){const l=document.querySelector(".top");if(!l)return;const o=Object.keys(SITE.dnsServers);if(!o.length)return;const g='',h=l.firstElementChild;o.forEach(f=>{const m=escapeHtml(f),b=escapeHtml((SITE.dnsServers[f].name||f).toUpperCase()),r=document.createElement("div");r.className="card",r.setAttribute("data-app",f),r.setAttribute("data-status","off"),r.innerHTML=`--
`,l.insertBefore(r,h)})}window.renderDnsCards=renderDnsCards;let csrfToken=null;async function getCSRFToken(){if(csrfToken)return csrfToken;try{const l=await fetch("/api/v1/csrf-token");if(!l.ok)throw new Error("Failed to fetch CSRF token");return csrfToken=(await l.json()).token,csrfToken}catch(l){throw errorHandler.logError("[CSRF] Get Token",l,{function:"getCSRFToken"}),l}}async function secureFetch(l,o={}){const g=(o.method||"GET").toUpperCase(),h=!["GET","HEAD","OPTIONS"].includes(g);if(h)try{const m=await getCSRFToken();o.headers={...o.headers,"X-CSRF-Token":m}}catch(m){errorHandler.logError("[CSRF] Add to Request",m,{function:"secureFetch"})}o.signal||(o={...o,signal:AbortSignal.timeout(15e3)}),o.credentials=o.credentials||"same-origin";const f=await fetch(l,o);if(h&&f.status===403)try{const m=await f.clone().json();if(m.error&&(m.error.includes("DC-100")||m.error.includes("DC-101"))){csrfToken=null;const b=await getCSRFToken();return o.headers={...o.headers,"X-CSRF-Token":b},o.signal=AbortSignal.timeout(15e3),fetch(l,o)}}catch{}return f}async function postJSON(l,o){const g=await secureFetch(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)}),h=await g.json();if(!g.ok||h.success===!1)throw new Error(h.error||`Request failed (${g.status})`);return h}async function getJSON(l){const o=await secureFetch(l);if(!o.ok){let g=`Request failed (${o.status})`;try{g=(await o.json()).error||g}catch{}throw new Error(g)}return o.json()}async function deleteAPI(l){const o=await secureFetch(l,{method:"DELETE"}),g=await o.json();if(!o.ok||g.success===!1)throw new Error(g.error||`Delete failed (${o.status})`);return g}async function withButton(l,o,g,h={}){const f=l.innerHTML,{successText:m="\u2705",resetDelay:b=DC.DELAYS.BTN_RESET}=h;l.disabled=!0,l.innerHTML=o;try{const r=await g();return l.innerHTML=m,setTimeout(()=>{l.innerHTML=f,l.disabled=!1},b),r}catch(r){throw l.innerHTML=f,l.disabled=!1,r}}function openModal(l){document.getElementById(l)?.classList.add("show")}function closeModal(l){document.getElementById(l)?.classList.remove("show")}function wireModal(l,...o){l&&(l.addEventListener("click",g=>{g.target===l&&l.classList.remove("show")}),o.forEach(g=>{g&&typeof g.addEventListener=="function"&&g.addEventListener("click",()=>l.classList.remove("show"))}))}function showNotification(l,o="info",g=3e3){const h=document.querySelector(".deploy-notification");h&&h.remove();const f={info:{bg:"#2196F3",fg:"#fff"},success:{bg:"var(--ok-bg)",fg:"var(--ok-fg)"},error:{bg:"#f44336",fg:"#fff"},warning:{bg:"#ff9800",fg:"#fff"}},m=f[o]||f.info,b=document.createElement("div");b.className="deploy-notification",b.textContent=l,b.style.cssText=`
+ `,document.body.appendChild(b),setTimeout(()=>{b.parentNode&&b.parentNode.removeChild(b)},1e4),!0}handleStorageUnavailable(){this.logError("Storage Unavailable","Local storage is not available");const b={data:{},getItem(f){return this.data[f]||null},setItem(f,v){this.data[f]=v},removeItem(f){delete this.data[f]},clear(){this.data={}}};return console.warn("[ErrorHandler] Using in-memory storage - progress will not persist"),b}sendToErrorTracking(b){}}l.ErrorHandler=o,console.log("[ErrorHandler] Module loaded")})(window),(function(){try{var o=typeof localStorage<"u"&&localStorage.getItem("dashcaddy-health-settings")||null;if(o){var m=JSON.parse(o);m.statsPollingInterval&&m.statsPollingInterval>=5&&m.statsPollingInterval<=3600&&(window.__DC_STATS_OVERRIDE=m.statsPollingInterval*1e3)}}catch{}})();const DC={NAME:"DashCaddy",POLL:{DASHBOARD:1e4,LOGS:3e3,STATS:typeof window<"u"&&window.__DC_STATS_OVERRIDE||5e3,WEATHER:6e5,HEALTH:1e3,DEPLOY_SSL:5e3},DELAYS:{BTN_RESET:2e3,RELOAD:5e3,MODAL_CLOSE:500,PORT_CHECK:500,DEPLOY_INIT:3e3},DEFAULTS:{DNS_PORT:"5380",SERVICE_PORT:"8080",TTL:300,CADDYFILE:"C:\\caddy\\Caddyfile"}},errorHandler=new ErrorHandler,_cachedCfg=JSON.parse(localStorage.getItem("dashcaddy_site_config")||"null"),SITE={tld:_cachedCfg&&_cachedCfg.tld||".home",dnsIp:"",dnsPort:DC.DEFAULTS.DNS_PORT,dnsServers:{},configurationType:_cachedCfg&&_cachedCfg.configurationType||"homelab",domain:_cachedCfg&&_cachedCfg.domain||"",defaults:_cachedCfg&&_cachedCfg.defaults||{},routingMode:_cachedCfg&&_cachedCfg.routingMode||"subdomain",onboardingCompleted:!1};window.__dashcaddySiteConfigLoaded=(async function(){try{const b=await fetch("/api/v1/config");if(b.ok){const f=await b.json();if(f.tld&&(SITE.tld=f.tld.startsWith(".")?f.tld:"."+f.tld),f.dns&&(SITE.dnsIp=f.dns.ip||"",SITE.dnsPort=f.dns.port||DC.DEFAULTS.DNS_PORT),f.dnsServers&&typeof f.dnsServers=="object")for(const[g,r]of Object.entries(f.dnsServers))g!=="__proto__"&&g!=="constructor"&&g!=="prototype"&&(SITE.dnsServers[g]=r);f.configurationType&&(SITE.configurationType=f.configurationType),f.domain&&(SITE.domain=f.domain),f.defaults&&(SITE.defaults=f.defaults),f.routingMode&&(SITE.routingMode=f.routingMode),SITE.onboardingCompleted=f.onboardingCompleted===!0,localStorage.setItem("dashcaddy_site_config",JSON.stringify({tld:SITE.tld,configurationType:SITE.configurationType,domain:SITE.domain,routingMode:SITE.routingMode})),renderDnsCards();const v=document.getElementById("manage-tokens");v&&(v.style.display=Object.keys(SITE.dnsServers).length?"":"none")}}catch{}document.querySelectorAll("[data-tld]").forEach(b=>b.textContent=SITE.tld);const o=document.getElementById("edit-tld-suffix");o&&(o.textContent=SITE.tld);const m=document.getElementById("external-proxy-ip");m&&SITE.dnsIp&&(m.value=SITE.dnsIp,m.placeholder=SITE.dnsIp)})();function buildDomain(l){return l+SITE.tld}function buildServiceUrl(l){return SITE.routingMode==="subdirectory"&&SITE.domain?"https://"+SITE.domain+"/"+l:SITE.configurationType==="public"&&SITE.domain?"https://"+l+"."+SITE.domain:"https://"+buildDomain(l)}function getDnsServerAddr(l){const o=SITE.dnsServers[l];return o?`${o.ip}:${o.port}`:buildDomain(l)}function getPrimaryDnsId(){if(!SITE.dnsIp)return null;for(const[l,o]of Object.entries(SITE.dnsServers))if(o.ip===SITE.dnsIp)return l;return null}function renderDnsCards(){const l=document.querySelector(".top");if(!l)return;const o=Object.keys(SITE.dnsServers);if(!o.length)return;const m='',b=l.firstElementChild;o.forEach(f=>{const v=escapeHtml(f),g=escapeHtml((SITE.dnsServers[f].name||f).toUpperCase()),r=document.createElement("div");r.className="card",r.setAttribute("data-app",f),r.setAttribute("data-status","off"),r.innerHTML=`--
`,l.insertBefore(r,b)})}window.renderDnsCards=renderDnsCards;let csrfToken=null;async function getCSRFToken(){if(csrfToken)return csrfToken;try{const l=await fetch("/api/v1/csrf-token");if(!l.ok)throw new Error("Failed to fetch CSRF token");return csrfToken=(await l.json()).token,csrfToken}catch(l){throw errorHandler.logError("[CSRF] Get Token",l,{function:"getCSRFToken"}),l}}async function secureFetch(l,o={}){const m=(o.method||"GET").toUpperCase(),b=!["GET","HEAD","OPTIONS"].includes(m);if(b)try{const v=await getCSRFToken();o.headers={...o.headers,"X-CSRF-Token":v}}catch(v){errorHandler.logError("[CSRF] Add to Request",v,{function:"secureFetch"})}o.signal||(o={...o,signal:AbortSignal.timeout(15e3)}),o.credentials=o.credentials||"same-origin";const f=await fetch(l,o);if(b&&f.status===403)try{const v=await f.clone().json();if(v.error&&(v.error.includes("DC-100")||v.error.includes("DC-101"))){csrfToken=null;const g=await getCSRFToken();return o.headers={...o.headers,"X-CSRF-Token":g},o.signal=AbortSignal.timeout(15e3),fetch(l,o)}}catch{}return f}async function postJSON(l,o){const m=await secureFetch(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)}),b=await m.json();if(!m.ok||b.success===!1)throw new Error(b.error||`Request failed (${m.status})`);return b}async function getJSON(l){const o=await secureFetch(l);if(!o.ok){let m=`Request failed (${o.status})`;try{m=(await o.json()).error||m}catch{}throw new Error(m)}return o.json()}async function deleteAPI(l){const o=await secureFetch(l,{method:"DELETE"}),m=await o.json();if(!o.ok||m.success===!1)throw new Error(m.error||`Delete failed (${o.status})`);return m}async function withButton(l,o,m,b={}){const f=l.innerHTML,{successText:v="\u2705",resetDelay:g=DC.DELAYS.BTN_RESET}=b;l.disabled=!0,l.innerHTML=o;try{const r=await m();return l.innerHTML=v,setTimeout(()=>{l.innerHTML=f,l.disabled=!1},g),r}catch(r){throw l.innerHTML=f,l.disabled=!1,r}}function openModal(l){document.getElementById(l)?.classList.add("show")}function closeModal(l){document.getElementById(l)?.classList.remove("show")}function wireModal(l,...o){l&&(l.addEventListener("click",m=>{m.target===l&&l.classList.remove("show")}),o.forEach(m=>{m&&typeof m.addEventListener=="function"&&m.addEventListener("click",()=>l.classList.remove("show"))}))}function showNotification(l,o="info",m=3e3){const b=document.querySelector(".deploy-notification");b&&b.remove();const f={info:{bg:"#2196F3",fg:"#fff"},success:{bg:"var(--ok-bg)",fg:"var(--ok-fg)"},error:{bg:"#f44336",fg:"#fff"},warning:{bg:"#ff9800",fg:"#fff"}},v=f[o]||f.info,g=document.createElement("div");g.className="deploy-notification",g.textContent=l,g.style.cssText=`
position: fixed; top: 20px; right: 20px;
- background: ${m.bg}; color: ${m.fg};
+ background: ${v.bg}; color: ${v.fg};
padding: 16px 24px; border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,.3);
z-index: 10000; animation: slideIn 0.3s ease-out;
max-width: 400px; white-space: pre-line; font-size: 14px;
- `,document.body.appendChild(b),g>0&&setTimeout(()=>b.remove(),g)}function timeAgo(l){const o=Date.now()-new Date(l).getTime();return o<6e4?"just now":o<36e5?Math.floor(o/6e4)+"m ago":o<864e5?Math.floor(o/36e5)+"h ago":Math.floor(o/864e5)+"d ago"}function safeGet(l,o=null){try{const g=localStorage.getItem(l);return g!==null?g:o}catch{return o}}function safeSet(l,o){try{localStorage.setItem(l,o)}catch{}}function safeRemove(l){try{localStorage.removeItem(l)}catch{}}function safeSessionGet(l,o=null){try{const g=sessionStorage.getItem(l);return g!==null?g:o}catch{return o}}function safeSessionSet(l,o){try{sessionStorage.setItem(l,o)}catch{}}function safeGetJSON(l,o=null){try{const g=localStorage.getItem(l);return g?JSON.parse(g):o}catch{return o}}function escapeHtml(l){return String(l??"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function injectModal(l,o){document.getElementById(l)||document.body.insertAdjacentHTML("beforeend",o)}const DC_BUS={_handlers:{},on(l,o){var g;((g=this._handlers)[l]||(g[l]=[])).push(o)},off(l,o){this._handlers[l]=this._handlers[l]?.filter(g=>g!==o)},emit(l,o){this._handlers[l]?.forEach(g=>g(o))}},AppState={_apps:[],getApps(){return this._apps},setApps(l){this._apps=l,window.APPS=l,DC_BUS.emit("apps:changed",l)},findApp(l){return this._apps.find(o=>o.id===l)},addApp(l){this._apps.push(l),window.APPS=this._apps,DC_BUS.emit("apps:changed",this._apps)},removeApp(l){const o=this._apps.findIndex(g=>g.id===l);return o>-1&&(this._apps.splice(o,1),window.APPS=this._apps,DC_BUS.emit("apps:changed",this._apps)),o>-1},updateApp(l,o){const g=this._apps.find(h=>h.id===l);if(g){for(const[h,f]of Object.entries(o))h!=="__proto__"&&h!=="constructor"&&h!=="prototype"&&(g[h]=f);DC_BUS.emit("apps:changed",this._apps)}return g}};(function(){function l(){const h=document.createElement("div");return h.className="skeleton-card",h.innerHTML='',h}function o(h){const f=document.getElementById("cards");if(!(!f||f.querySelector(".card"))){h=h||6;for(let m=0;m.4,P={};return P.hover=S?p(x,B,.35):p(x,$,.08),P["card-hover"]=p(x,P.hover,.5),P.base=p(B,x,.6),P["fg-muted"]=p(k,B,.35),P.success=I,P.error=E,P.warning=S?"#d68a00":"#f39c12",P}function n(C,B){var $=B.lightBg||B.bg&&s(B.bg)>.4,k=B.accent||B["accent-strong"]||"#888888",x=d(k);return $?":root."+C+` body {
+ `,document.body.appendChild(g),m>0&&setTimeout(()=>g.remove(),m)}function timeAgo(l){const o=Date.now()-new Date(l).getTime();return o<6e4?"just now":o<36e5?Math.floor(o/6e4)+"m ago":o<864e5?Math.floor(o/36e5)+"h ago":Math.floor(o/864e5)+"d ago"}function safeGet(l,o=null){try{const m=localStorage.getItem(l);return m!==null?m:o}catch{return o}}function safeSet(l,o){try{localStorage.setItem(l,o)}catch{}}function safeRemove(l){try{localStorage.removeItem(l)}catch{}}function safeSessionGet(l,o=null){try{const m=sessionStorage.getItem(l);return m!==null?m:o}catch{return o}}function safeSessionSet(l,o){try{sessionStorage.setItem(l,o)}catch{}}function safeGetJSON(l,o=null){try{const m=localStorage.getItem(l);return m?JSON.parse(m):o}catch{return o}}function escapeHtml(l){return String(l??"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function injectModal(l,o){document.getElementById(l)||document.body.insertAdjacentHTML("beforeend",o)}const DC_BUS={_handlers:{},on(l,o){var m;((m=this._handlers)[l]||(m[l]=[])).push(o)},off(l,o){this._handlers[l]=this._handlers[l]?.filter(m=>m!==o)},emit(l,o){this._handlers[l]?.forEach(m=>m(o))}},AppState={_apps:[],getApps(){return this._apps},setApps(l){this._apps=l,window.APPS=l,DC_BUS.emit("apps:changed",l)},findApp(l){return this._apps.find(o=>o.id===l)},addApp(l){this._apps.push(l),window.APPS=this._apps,DC_BUS.emit("apps:changed",this._apps)},removeApp(l){const o=this._apps.findIndex(m=>m.id===l);return o>-1&&(this._apps.splice(o,1),window.APPS=this._apps,DC_BUS.emit("apps:changed",this._apps)),o>-1},updateApp(l,o){const m=this._apps.find(b=>b.id===l);if(m){for(const[b,f]of Object.entries(o))b!=="__proto__"&&b!=="constructor"&&b!=="prototype"&&(m[b]=f);DC_BUS.emit("apps:changed",this._apps)}return m}};(function(){function l(){const b=document.createElement("div");return b.className="skeleton-card",b.innerHTML='',b}function o(b){const f=document.getElementById("cards");if(!(!f||f.querySelector(".card"))){b=b||6;for(let v=0;v.4,A={};return A.hover=S?p(x,B,.35):p(x,$,.08),A["card-hover"]=p(x,A.hover,.5),A.base=p(B,x,.6),A["fg-muted"]=p(k,B,.35),A.success=I,A.error=E,A.warning=S?"#d68a00":"#f39c12",A}function n(C,B){var $=B.lightBg||B.bg&&s(B.bg)>.4,k=B.accent||B["accent-strong"]||"#888888",x=d(k);return $?":root."+C+` body {
background:
radial-gradient(1200px 800px at 10% -10%, rgba(`+x.r+","+x.g+","+x.b+`, .08), transparent 60%),
radial-gradient(1000px 700px at 110% 10%, rgba(`+x.r+","+x.g+","+x.b+`, .05), transparent 55%),
@@ -44,10 +44,10 @@
background: color-mix(in srgb, var(--accent) 18%, transparent);
border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
}
-`}function t(){return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function i(){m.forEach(function(C){document.documentElement.style.removeProperty("--"+C)})}function y(C,B){var $=C.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"");$||($="custom"),h.indexOf($)!==-1&&($=$+"-custom");for(var k=safeGetJSON(o,{}),x=$,I=2;k[$]&&$!==B;)$=x+"-"+I++;return $}function u(C){var B=document.getElementById("user-theme-styles");B&&B.remove(),f.length=h.length,Object.keys(v).forEach(function(E){h.indexOf(E)===-1&&delete v[E]});var $=C||safeGetJSON(o,{}),k=Object.keys($);if(k=k.filter(function(E){return h.indexOf(E)===-1}),!!k.length){var x="";k.forEach(function(E){var S=$[E];f.indexOf(E)===-1&&f.push(E);var P={};m.forEach(function(O){S[O]&&(P[O]=S[O])}),P["card-bg"]=S["card-base"]||S.bg,S.lightBg&&(P.lightBg=!0);var D=e(P);r.forEach(function(O){!P[O]&&D[O]&&(P[O]=D[O])}),v[E]=P,x+=":root."+E+` {
-`,m.forEach(function(O){P[O]&&(x+=" --"+O+": "+P[O]+`;
+`}function t(){return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function i(){v.forEach(function(C){document.documentElement.style.removeProperty("--"+C)})}function y(C,B){var $=C.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"");$||($="custom"),b.indexOf($)!==-1&&($=$+"-custom");for(var k=safeGetJSON(o,{}),x=$,I=2;k[$]&&$!==B;)$=x+"-"+I++;return $}function u(C){var B=document.getElementById("user-theme-styles");B&&B.remove(),f.length=b.length,Object.keys(h).forEach(function(E){b.indexOf(E)===-1&&delete h[E]});var $=C||safeGetJSON(o,{}),k=Object.keys($);if(k=k.filter(function(E){return b.indexOf(E)===-1}),!!k.length){var x="";k.forEach(function(E){var S=$[E];f.indexOf(E)===-1&&f.push(E);var A={};v.forEach(function(O){S[O]&&(A[O]=S[O])}),A["card-bg"]=S["card-base"]||S.bg,S.lightBg&&(A.lightBg=!0);var D=e(A);r.forEach(function(O){!A[O]&&D[O]&&(A[O]=D[O])}),h[E]=A,x+=":root."+E+` {
+`,v.forEach(function(O){A[O]&&(x+=" --"+O+": "+A[O]+`;
`)}),x+=`}
-`,x+=n(E,P),x+=c(E,P)});var I=document.createElement("style");I.id="user-theme-styles",I.textContent=x,document.head.appendChild(I)}}function w(){secureFetch("/api/v1/themes").then(function(C){return C.json()}).then(function(C){if(!(!C.success||!C.themes)){var B=C.themes,$=safeGetJSON(o,{});if(JSON.stringify(B)!==JSON.stringify($)){safeSet(o,JSON.stringify(B)),u(B);var k=safeGet(l);k&&f.indexOf(k)!==-1&&L(k)}}}).catch(function(){})}function T(){var C=safeGetJSON(g);if(C){var B=C.name||"Custom",$=y(B),k={name:B};m.forEach(function(E){C[E]&&(k[E]=C[E])});var x=safeGetJSON(o,{});x[$]=k,safeSet(o,JSON.stringify(x)),safeGet(l)==="custom"&&safeSet(l,$),safeRemove(g);var I={};m.forEach(function(E){k[E]&&(I[E]=k[E])}),fetch("/api/v1/themes/"+$,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:B,colors:I})}).catch(function(){})}}function L(C){document.documentElement.classList.add("theme-transitioning"),f.forEach(function(x){x!=="dark"&&document.documentElement.classList.remove(x)}),i(),C!=="dark"&&document.documentElement.classList.add(C),safeSet(l,C);var B=v[C],$=document.querySelector('meta[name="theme-color"]');$&&B&&$.setAttribute("content",B.bg);var k=B&&B.lightBg;!k&&B&&B.bg&&(k=s(B.bg)>.4),k?document.documentElement.classList.add("light-bg"):document.documentElement.classList.remove("light-bg"),setTimeout(function(){document.documentElement.classList.remove("theme-transitioning")},300)}T(),u();var A=safeGet(l);A==="red"&&(A="black",safeSet(l,"black")),A&&A!=="dark"&&f.indexOf(A)===-1&&(A=null),L(A||t()),w(),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",function(C){safeGet(l)||L(C.matches?"dark":"light")}),window.THEMES=f,window.BUILTIN_THEMES=h,window.THEME_COLORS=v,window.THEME_PROPS=m,window.BASE_PROPS=b,window.DERIVED_PROPS=r,window.USER_THEMES_KEY=o,window.applyTheme=L,window.clearCustomProperties=i,window.injectUserThemeStyles=u,window.syncThemesFromServer=w,window.slugifyThemeName=y,window.getActiveTheme=function(){return safeGet(l)||t()},window.deriveExtendedColors=e,window.hexToRgb=d,window.rgbToHex=a,window.blendColors=p})(),(function(){let l=null;async function o(){if(l)return l;try{const d=await fetch("/api/v1/auth/login/methods",{cache:"no-store"});if(!d.ok)throw new Error(`methods HTTP ${d.status}`);const a=await d.json();return l=Array.isArray(a.providers)?a.providers:[],l}catch(d){return console.warn("[auth-gate] methods fetch failed; falling back to TOTP-only",d),[]}}function g(d){const a=document.getElementById("totp-overlay");if(!a)return;const p=a.querySelector(".totp-card");if(!p)return;const s=p.innerHTML;p.dataset.originalBody||(p.dataset.originalBody=s);const e=d.map(n=>{const c=n.config&&(n.config.label||n.name)||n.name;return`