diff --git a/status/dist/core.js b/status/dist/core.js
index 93f6586..211af02 100644
--- a/status/dist/core.js
+++ b/status/dist/core.js
@@ -1,4 +1,4 @@
-(function(l){"use strict";class n{constructor(){this.errors=[],this.maxErrors=50}logError(b,f,u={}){const v={timestamp:new Date().toISOString(),context:b,message:f instanceof Error?f.message:f,stack:f instanceof Error?f.stack:null,metadata:u};this.errors.push(v),this.errors.length>this.maxErrors&&this.errors.shift(),console.error(`[Onboarding Error] ${b}:`,f,u)}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 u=this.classifyError({message:f.message});b.byType[u]=(b.byType[u]||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=`
+(function(l){"use strict";class a{constructor(){this.errors=[],this.maxErrors=50}logError(b,f,m={}){const y={timestamp:new Date().toISOString(),context:b,message:f instanceof Error?f.message:f,stack:f instanceof Error?f.stack:null,metadata:m};this.errors.push(y),this.errors.length>this.maxErrors&&this.errors.shift(),console.error(`[Onboarding Error] ${b}:`,f,m)}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 m=this.classifyError({message:f.message});b.byType[m]=(b.byType[m]||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;
@@ -16,14 +16,14 @@
The interactive tour is unavailable, but you can explore the dashboard freely.
Check the documentation for help getting started.
- `,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,u){this.data[f]=u},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=n,console.log("[ErrorHandler] Module loaded")})(window);const DC={NAME:"DashCaddy",POLL:{DASHBOARD:1e4,LOGS:3e3,STATS: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[v,s]of Object.entries(f.dnsServers))v!=="__proto__"&&v!=="constructor"&&v!=="prototype"&&(SITE.dnsServers[v]=s);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 u=document.getElementById("manage-tokens");u&&(u.style.display=Object.keys(SITE.dnsServers).length?"":"none")}}catch{}document.querySelectorAll("[data-tld]").forEach(b=>b.textContent=SITE.tld);const n=document.getElementById("edit-tld-suffix");n&&(n.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 n=SITE.dnsServers[l];return n?`${n.ip}:${n.port}`:buildDomain(l)}function getPrimaryDnsId(){if(!SITE.dnsIp)return null;for(const[l,n]of Object.entries(SITE.dnsServers))if(n.ip===SITE.dnsIp)return l;return null}function renderDnsCards(){const l=document.querySelector(".top");if(!l)return;const n=Object.keys(SITE.dnsServers);if(!n.length)return;const g='',b=l.firstElementChild;n.forEach(f=>{const u=escapeHtml(f),v=escapeHtml((SITE.dnsServers[f].name||f).toUpperCase()),s=document.createElement("div");s.className="card",s.setAttribute("data-app",f),s.setAttribute("data-status","off"),s.innerHTML=`--
`,l.insertBefore(s,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,n={}){const g=(n.method||"GET").toUpperCase(),b=!["GET","HEAD","OPTIONS"].includes(g);if(b)try{const u=await getCSRFToken();n.headers={...n.headers,"X-CSRF-Token":u}}catch(u){errorHandler.logError("[CSRF] Add to Request",u,{function:"secureFetch"})}n.signal||(n={...n,signal:AbortSignal.timeout(15e3)});const f=await fetch(l,n);if(b&&f.status===403)try{const u=await f.clone().json();if(u.error&&(u.error.includes("DC-100")||u.error.includes("DC-101"))){csrfToken=null;const v=await getCSRFToken();return n.headers={...n.headers,"X-CSRF-Token":v},n.signal=AbortSignal.timeout(15e3),fetch(l,n)}}catch{}return f}async function postJSON(l,n){const g=await secureFetch(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)}),b=await g.json();if(!g.ok||b.success===!1)throw new Error(b.error||`Request failed (${g.status})`);return b}async function getJSON(l){const n=await secureFetch(l);if(!n.ok){let g=`Request failed (${n.status})`;try{g=(await n.json()).error||g}catch{}throw new Error(g)}return n.json()}async function deleteAPI(l){const n=await secureFetch(l,{method:"DELETE"}),g=await n.json();if(!n.ok||g.success===!1)throw new Error(g.error||`Delete failed (${n.status})`);return g}async function withButton(l,n,g,b={}){const f=l.innerHTML,{successText:u="\u2705",resetDelay:v=DC.DELAYS.BTN_RESET}=b;l.disabled=!0,l.innerHTML=n;try{const s=await g();return l.innerHTML=u,setTimeout(()=>{l.innerHTML=f,l.disabled=!1},v),s}catch(s){throw l.innerHTML=f,l.disabled=!1,s}}function openModal(l){document.getElementById(l)?.classList.add("show")}function closeModal(l){document.getElementById(l)?.classList.remove("show")}function wireModal(l,...n){l&&(l.addEventListener("click",g=>{g.target===l&&l.classList.remove("show")}),n.forEach(g=>{g&&typeof g.addEventListener=="function"&&g.addEventListener("click",()=>l.classList.remove("show"))}))}function showNotification(l,n="info",g=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"}},u=f[n]||f.info,v=document.createElement("div");v.className="deploy-notification",v.textContent=l,v.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,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"),b}sendToErrorTracking(b){}}l.ErrorHandler=a,console.log("[ErrorHandler] Module loaded")})(window);const DC={NAME:"DashCaddy",POLL:{DASHBOARD:1e4,LOGS:3e3,STATS: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[y,s]of Object.entries(f.dnsServers))y!=="__proto__"&&y!=="constructor"&&y!=="prototype"&&(SITE.dnsServers[y]=s);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(b=>b.textContent=SITE.tld);const a=document.getElementById("edit-tld-suffix");a&&(a.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 a=SITE.dnsServers[l];return a?`${a.ip}:${a.port}`:buildDomain(l)}function getPrimaryDnsId(){if(!SITE.dnsIp)return null;for(const[l,a]of Object.entries(SITE.dnsServers))if(a.ip===SITE.dnsIp)return l;return null}function renderDnsCards(){const l=document.querySelector(".top");if(!l)return;const a=Object.keys(SITE.dnsServers);if(!a.length)return;const g='',b=l.firstElementChild;a.forEach(f=>{const m=escapeHtml(f),y=escapeHtml((SITE.dnsServers[f].name||f).toUpperCase()),s=document.createElement("div");s.className="card",s.setAttribute("data-app",f),s.setAttribute("data-status","off"),s.innerHTML=`--
`,l.insertBefore(s,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,a={}){const g=(a.method||"GET").toUpperCase(),b=!["GET","HEAD","OPTIONS"].includes(g);if(b)try{const m=await getCSRFToken();a.headers={...a.headers,"X-CSRF-Token":m}}catch(m){errorHandler.logError("[CSRF] Add to Request",m,{function:"secureFetch"})}a.signal||(a={...a,signal:AbortSignal.timeout(15e3)});const f=await fetch(l,a);if(b&&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 y=await getCSRFToken();return a.headers={...a.headers,"X-CSRF-Token":y},a.signal=AbortSignal.timeout(15e3),fetch(l,a)}}catch{}return f}async function postJSON(l,a){const g=await secureFetch(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)}),b=await g.json();if(!g.ok||b.success===!1)throw new Error(b.error||`Request failed (${g.status})`);return b}async function getJSON(l){const a=await secureFetch(l);if(!a.ok){let g=`Request failed (${a.status})`;try{g=(await a.json()).error||g}catch{}throw new Error(g)}return a.json()}async function deleteAPI(l){const a=await secureFetch(l,{method:"DELETE"}),g=await a.json();if(!a.ok||g.success===!1)throw new Error(g.error||`Delete failed (${a.status})`);return g}async function withButton(l,a,g,b={}){const f=l.innerHTML,{successText:m="\u2705",resetDelay:y=DC.DELAYS.BTN_RESET}=b;l.disabled=!0,l.innerHTML=a;try{const s=await g();return l.innerHTML=m,setTimeout(()=>{l.innerHTML=f,l.disabled=!1},y),s}catch(s){throw l.innerHTML=f,l.disabled=!1,s}}function openModal(l){document.getElementById(l)?.classList.add("show")}function closeModal(l){document.getElementById(l)?.classList.remove("show")}function wireModal(l,...a){l&&(l.addEventListener("click",g=>{g.target===l&&l.classList.remove("show")}),a.forEach(g=>{g&&typeof g.addEventListener=="function"&&g.addEventListener("click",()=>l.classList.remove("show"))}))}function showNotification(l,a="info",g=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"}},m=f[a]||f.info,y=document.createElement("div");y.className="deploy-notification",y.textContent=l,y.style.cssText=`
position: fixed; top: 20px; right: 20px;
- background: ${u.bg}; color: ${u.fg};
+ background: ${m.bg}; color: ${m.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(v),g>0&&setTimeout(()=>v.remove(),g)}function timeAgo(l){const n=Date.now()-new Date(l).getTime();return n<6e4?"just now":n<36e5?Math.floor(n/6e4)+"m ago":n<864e5?Math.floor(n/36e5)+"h ago":Math.floor(n/864e5)+"d ago"}function safeGet(l,n=null){try{const g=localStorage.getItem(l);return g!==null?g:n}catch{return n}}function safeSet(l,n){try{localStorage.setItem(l,n)}catch{}}function safeRemove(l){try{localStorage.removeItem(l)}catch{}}function safeSessionGet(l,n=null){try{const g=sessionStorage.getItem(l);return g!==null?g:n}catch{return n}}function safeSessionSet(l,n){try{sessionStorage.setItem(l,n)}catch{}}function safeGetJSON(l,n=null){try{const g=localStorage.getItem(l);return g?JSON.parse(g):n}catch{return n}}function escapeHtml(l){return String(l??"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function injectModal(l,n){document.getElementById(l)||document.body.insertAdjacentHTML("beforeend",n)}const DC_BUS={_handlers:{},on(l,n){var g;((g=this._handlers)[l]||(g[l]=[])).push(n)},off(l,n){this._handlers[l]=this._handlers[l]?.filter(g=>g!==n)},emit(l,n){this._handlers[l]?.forEach(g=>g(n))}},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(n=>n.id===l)},addApp(l){this._apps.push(l),window.APPS=this._apps,DC_BUS.emit("apps:changed",this._apps)},removeApp(l){const n=this._apps.findIndex(g=>g.id===l);return n>-1&&(this._apps.splice(n,1),window.APPS=this._apps,DC_BUS.emit("apps:changed",this._apps)),n>-1},updateApp(l,n){const g=this._apps.find(b=>b.id===l);if(g){for(const[b,f]of Object.entries(n))b!=="__proto__"&&b!=="constructor"&&b!=="prototype"&&(g[b]=f);DC_BUS.emit("apps:changed",this._apps)}return g}};(function(){function l(){const b=document.createElement("div");return b.className="skeleton-card",b.innerHTML='',b}function n(b){const f=document.getElementById("cards");if(!(!f||f.querySelector(".card"))){b=b||6;for(let u=0;u.4,A={};return A.hover=S?m(x,B,.35):m(x,$,.08),A["card-hover"]=m(x,A.hover,.5),A.base=m(B,x,.6),A["fg-muted"]=m(C,B,.35),A.success=I,A.error=k,A.warning=S?"#d68a00":"#f39c12",A}function o(E,B){var $=B.lightBg||B.bg&&i(B.bg)>.4,C=B.accent||B["accent-strong"]||"#888888",x=c(C);return $?":root."+E+` body {
+ `,document.body.appendChild(y),g>0&&setTimeout(()=>y.remove(),g)}function timeAgo(l){const a=Date.now()-new Date(l).getTime();return a<6e4?"just now":a<36e5?Math.floor(a/6e4)+"m ago":a<864e5?Math.floor(a/36e5)+"h ago":Math.floor(a/864e5)+"d ago"}function safeGet(l,a=null){try{const g=localStorage.getItem(l);return g!==null?g:a}catch{return a}}function safeSet(l,a){try{localStorage.setItem(l,a)}catch{}}function safeRemove(l){try{localStorage.removeItem(l)}catch{}}function safeSessionGet(l,a=null){try{const g=sessionStorage.getItem(l);return g!==null?g:a}catch{return a}}function safeSessionSet(l,a){try{sessionStorage.setItem(l,a)}catch{}}function safeGetJSON(l,a=null){try{const g=localStorage.getItem(l);return g?JSON.parse(g):a}catch{return a}}function escapeHtml(l){return String(l??"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function injectModal(l,a){document.getElementById(l)||document.body.insertAdjacentHTML("beforeend",a)}const DC_BUS={_handlers:{},on(l,a){var g;((g=this._handlers)[l]||(g[l]=[])).push(a)},off(l,a){this._handlers[l]=this._handlers[l]?.filter(g=>g!==a)},emit(l,a){this._handlers[l]?.forEach(g=>g(a))}},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(a=>a.id===l)},addApp(l){this._apps.push(l),window.APPS=this._apps,DC_BUS.emit("apps:changed",this._apps)},removeApp(l){const a=this._apps.findIndex(g=>g.id===l);return a>-1&&(this._apps.splice(a,1),window.APPS=this._apps,DC_BUS.emit("apps:changed",this._apps)),a>-1},updateApp(l,a){const g=this._apps.find(b=>b.id===l);if(g){for(const[b,f]of Object.entries(a))b!=="__proto__"&&b!=="constructor"&&b!=="prototype"&&(g[b]=f);DC_BUS.emit("apps:changed",this._apps)}return g}};(function(){function l(){const b=document.createElement("div");return b.className="skeleton-card",b.innerHTML='',b}function a(b){const f=document.getElementById("cards");if(!(!f||f.querySelector(".card"))){b=b||6;for(let m=0;m.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(C,B,.35),A.success=I,A.error=k,A.warning=S?"#d68a00":"#f39c12",A}function n(E,B){var $=B.lightBg||B.bg&&i(B.bg)>.4,C=B.accent||B["accent-strong"]||"#888888",x=c(C);return $?":root."+E+` 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,20 +44,20 @@
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 r(){u.forEach(function(E){document.documentElement.style.removeProperty("--"+E)})}function h(E,B){var $=E.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"");$||($="custom"),b.indexOf($)!==-1&&($=$+"-custom");for(var C=safeGetJSON(n,{}),x=$,I=2;C[$]&&$!==B;)$=x+"-"+I++;return $}function p(E){var B=document.getElementById("user-theme-styles");B&&B.remove(),f.length=b.length,Object.keys(y).forEach(function(k){b.indexOf(k)===-1&&delete y[k]});var $=E||safeGetJSON(n,{}),C=Object.keys($);if(C=C.filter(function(k){return b.indexOf(k)===-1}),!!C.length){var x="";C.forEach(function(k){var S=$[k];f.indexOf(k)===-1&&f.push(k);var A={};u.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);s.forEach(function(O){!A[O]&&D[O]&&(A[O]=D[O])}),y[k]=A,x+=":root."+k+` {
-`,u.forEach(function(O){A[O]&&(x+=" --"+O+": "+A[O]+`;
+`}function t(){return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function r(){m.forEach(function(E){document.documentElement.style.removeProperty("--"+E)})}function v(E,B){var $=E.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"");$||($="custom"),b.indexOf($)!==-1&&($=$+"-custom");for(var C=safeGetJSON(a,{}),x=$,I=2;C[$]&&$!==B;)$=x+"-"+I++;return $}function u(E){var B=document.getElementById("user-theme-styles");B&&B.remove(),f.length=b.length,Object.keys(h).forEach(function(k){b.indexOf(k)===-1&&delete h[k]});var $=E||safeGetJSON(a,{}),C=Object.keys($);if(C=C.filter(function(k){return b.indexOf(k)===-1}),!!C.length){var x="";C.forEach(function(k){var S=$[k];f.indexOf(k)===-1&&f.push(k);var A={};m.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);s.forEach(function(O){!A[O]&&D[O]&&(A[O]=D[O])}),h[k]=A,x+=":root."+k+` {
+`,m.forEach(function(O){A[O]&&(x+=" --"+O+": "+A[O]+`;
`)}),x+=`}
-`,x+=o(k,A),x+=d(k,A)});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(E){return E.json()}).then(function(E){if(!(!E.success||!E.themes)){var B=E.themes,$=safeGetJSON(n,{});if(JSON.stringify(B)!==JSON.stringify($)){safeSet(n,JSON.stringify(B)),p(B);var C=safeGet(l);C&&f.indexOf(C)!==-1&&L(C)}}}).catch(function(){})}function T(){var E=safeGetJSON(g);if(E){var B=E.name||"Custom",$=h(B),C={name:B};u.forEach(function(k){E[k]&&(C[k]=E[k])});var x=safeGetJSON(n,{});x[$]=C,safeSet(n,JSON.stringify(x)),safeGet(l)==="custom"&&safeSet(l,$),safeRemove(g);var I={};u.forEach(function(k){C[k]&&(I[k]=C[k])}),fetch("/api/v1/themes/"+$,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:B,colors:I})}).catch(function(){})}}function L(E){document.documentElement.classList.add("theme-transitioning"),f.forEach(function(x){x!=="dark"&&document.documentElement.classList.remove(x)}),r(),E!=="dark"&&document.documentElement.classList.add(E),safeSet(l,E);var B=y[E],$=document.querySelector('meta[name="theme-color"]');$&&B&&$.setAttribute("content",B.bg);var C=B&&B.lightBg;!C&&B&&B.bg&&(C=i(B.bg)>.4),C?document.documentElement.classList.add("light-bg"):document.documentElement.classList.remove("light-bg"),setTimeout(function(){document.documentElement.classList.remove("theme-transitioning")},300)}T(),p();var P=safeGet(l);P==="red"&&(P="black",safeSet(l,"black")),P&&P!=="dark"&&f.indexOf(P)===-1&&(P=null),L(P||t()),w(),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",function(E){safeGet(l)||L(E.matches?"dark":"light")}),window.THEMES=f,window.BUILTIN_THEMES=b,window.THEME_COLORS=y,window.THEME_PROPS=u,window.BASE_PROPS=v,window.DERIVED_PROPS=s,window.USER_THEMES_KEY=n,window.applyTheme=L,window.clearCustomProperties=r,window.injectUserThemeStyles=p,window.syncThemesFromServer=w,window.slugifyThemeName=h,window.getActiveTheme=function(){return safeGet(l)||t()},window.deriveExtendedColors=e,window.hexToRgb=c,window.rgbToHex=a,window.blendColors=m})(),(function(){let l=null;async function n(){if(l)return l;try{const y=await fetch("/api/v1/auth/login/methods",{cache:"no-store"});if(!y.ok)throw new Error(`methods HTTP ${y.status}`);const c=await y.json();return l=Array.isArray(c.providers)?c.providers:[],l}catch(y){return console.warn("[auth-gate] methods fetch failed; falling back to TOTP-only",y),[]}}function g(y){const c=document.getElementById("totp-overlay");if(!c)return;const a=c.querySelector(".totp-card");if(!a)return;const m=a.innerHTML;a.dataset.originalBody||(a.dataset.originalBody=m);const i=y.map(e=>{const o=e.config&&(e.config.label||e.name)||e.name;return`