@sparkvault/sdk 1.9.1 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).SparkVault={})}(this,function(e){"use strict";class t extends Error{constructor(e,n,s,i){super(e),this.name="SparkVaultError",this.code=n,this.statusCode=s,this.details=i,Object.setPrototypeOf(this,t.prototype)}}class n extends t{constructor(e,t){super(e,"authentication_error",401,t),this.name="AuthenticationError",Object.setPrototypeOf(this,n.prototype)}}class s extends t{constructor(e,t){super(e,"authorization_error",403,t),this.name="AuthorizationError",Object.setPrototypeOf(this,s.prototype)}}class i extends t{constructor(e,t){super(e,"validation_error",400,t),this.name="ValidationError",Object.setPrototypeOf(this,i.prototype)}}class o extends t{constructor(e,t){super(e,"network_error",void 0,t),this.name="NetworkError",Object.setPrototypeOf(this,o.prototype)}}class r extends t{constructor(e="Request timed out"){super(e,"timeout_error"),this.name="TimeoutError",Object.setPrototypeOf(this,r.prototype)}}class a extends t{constructor(e="User cancelled the operation"){super(e,"user_cancelled"),this.name="UserCancelledError",Object.setPrototypeOf(this,a.prototype)}}class l extends t{constructor(){super("Popup was blocked. Please allow popups for this site.","popup_blocked"),this.name="PopupBlockedError",Object.setPrototypeOf(this,l.prototype)}}const d={maxAttempts:3,baseDelayMs:200,maxDelayMs:5e3,jitterFactor:.2,isRetryable:function(e){if(e instanceof TypeError)return!0;if(e instanceof DOMException&&"AbortError"===e.name)return!0;if("object"==typeof e&&null!==e&&"statusCode"in e){const t=e.statusCode;return t>=500||429===t}return!1}};function c(e,t,n,s){const i=t*Math.pow(2,e),o=Math.min(i,n);return o+o*s*Math.random()}function h(e){return new Promise(t=>setTimeout(t,e))}async function p(e,t={}){const n={...d,...t};let s;for(let t=0;t<n.maxAttempts;t++)try{return await e()}catch(e){s=e;if(t===n.maxAttempts-1||!n.isRetryable(e))throw e;const i=c(t,n.baseDelayMs,n.maxDelayMs,n.jitterFactor);await h(i)}throw s}class u{constructor(e){this.config=e}async request(e,t={}){const{retry:n}=t;if(n){return p(()=>this.executeRequest(e,t),{..."object"==typeof n?n:{},isRetryable:e=>this.isRetryableError(e)})}return this.executeRequest(e,t)}async executeRequest(e,n){const{method:s="GET",headers:i={},body:a,timeout:l=this.config.timeout}=n,d=`${this.config.apiBaseUrl}${e}`,c={"Content-Type":"application/json",Accept:"application/json",...i},h=new AbortController,p=setTimeout(()=>h.abort(),l);try{const e=await fetch(d,{method:s,headers:c,body:a?JSON.stringify(a):void 0,signal:h.signal});clearTimeout(p);const t=await this.parseResponse(e);if(!e.ok)throw this.createErrorFromResponse(e.status,t);return{data:t,status:e.status,headers:e.headers}}catch(e){if(clearTimeout(p),e instanceof t)throw e;if(e instanceof DOMException&&"AbortError"===e.name)throw new r;if(e instanceof TypeError)throw new o("Network request failed. Please check your connection.");throw new t(e instanceof Error?e.message:"Unknown error","unknown_error")}}isRetryableError(e){return e instanceof o||e instanceof r||e instanceof t&&void 0!==e.statusCode&&(e.statusCode>=500||429===e.statusCode)}async parseResponse(e){const n=e.headers.get("content-length");if(204===e.status||"0"===n)return null;const s=e.headers.get("content-type");if(s?.includes("application/json")){return await e.json()}const i=await e.text();throw new t(`Unexpected response format: expected JSON but received ${s??"unknown content type"}`,"invalid_response_format",e.status,{body:i.slice(0,500)})}createErrorFromResponse(e,o){const r="object"==typeof o&&null!==o?o:{},a=("string"==typeof r.message?r.message:null)??("string"==typeof r.error?r.error:null)??"Request failed",l="string"==typeof r.code?r.code:"api_error",d="object"==typeof r.details&&null!==r.details?r.details:void 0;switch(e){case 400:return new i(a,d);case 401:return new n(a,d);case 403:return new s(a,d);default:return new t(a,l,e,d)}}get(e,t){return this.request(e,{...t,method:"GET"})}post(e,t,n){return this.request(e,{...n,method:"POST",body:t})}put(e,t,n){return this.request(e,{...n,method:"PUT",body:t})}patch(e,t,n){return this.request(e,{...n,method:"PATCH",body:t})}delete(e,t){return this.request(e,{...t,method:"DELETE"})}async requestRaw(e,t={}){const{retry:n}=t;if(n){return p(()=>this.executeRequestRaw(e,t),{..."object"==typeof n?n:{},isRetryable:e=>this.isRetryableError(e)})}return this.executeRequestRaw(e,t)}async executeRequestRaw(e,n){const{method:s="GET",headers:i={},body:a,timeout:l=this.config.timeout}=n,d=`${this.config.apiBaseUrl}${e}`,c={"Content-Type":"application/json",...i},h=new AbortController,p=setTimeout(()=>h.abort(),l);try{const e=await fetch(d,{method:s,headers:c,body:a?JSON.stringify(a):void 0,signal:h.signal});if(clearTimeout(p),!e.ok){const t=await e.json().catch(()=>({}));throw this.createErrorFromResponse(e.status,t)}return e.blob()}catch(e){if(clearTimeout(p),e instanceof t)throw e;if(e instanceof DOMException&&"AbortError"===e.name)throw new r;if(e instanceof TypeError)throw new o("Network request failed. Please check your connection.");throw new t(e instanceof Error?e.message:"Unknown error","unknown_error")}}}class m{constructor(e){this.intervalId=null,this.secondsRemaining=e.duration,this.onTick=e.onTick,this.onComplete=e.onComplete}start(){this.stop(),this.onTick(this.secondsRemaining),this.intervalId=setInterval(()=>{this.secondsRemaining--,this.onTick(this.secondsRemaining),this.secondsRemaining<=0&&(this.stop(),this.onComplete())},1e3)}stop(){null!==this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null)}isRunning(){return null!==this.intervalId}getRemainingSeconds(){return this.secondsRemaining}}class v{constructor(e){this.intervalId=null,Number.isFinite(e.expiresAt)?this.expiresAt=e.expiresAt:(console.warn("[ExpirationTimer] Invalid expiresAt:",e.expiresAt),this.expiresAt=0),this.onTick=e.onTick,this.onExpired=e.onExpired}calculateRemaining(){const e=Math.floor(Date.now()/1e3),t=this.expiresAt-e;return!Number.isFinite(t)||t<0?0:Math.ceil(t)}start(){this.stop();const e=this.calculateRemaining();this.onTick(e),e<=0?this.onExpired():this.intervalId=setInterval(()=>{const e=this.calculateRemaining();this.onTick(e),e<=0&&(this.stop(),this.onExpired())},1e3)}stop(){null!==this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null)}isRunning(){return null!==this.intervalId}}function g(e){return function(e){let t="";for(let n=0;n<e.byteLength;n++)t+=String.fromCharCode(e[n]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}(new Uint8Array(e))}class b{constructor(e,t=3e4){this.configCache=null,this.config=e,this.timeoutMs=t}get baseUrl(){return`${this.config.identityBaseUrl}/${this.config.accountId}`}async request(e,t,n){const s=`${this.baseUrl}${t}`,i={"Content-Type":"application/json",Accept:"application/json"},o=new AbortController,r=setTimeout(()=>o.abort(),this.timeoutMs);try{const t=await fetch(s,{method:e,headers:i,body:n?JSON.stringify(n):void 0,signal:o.signal}),r=await t.json();if(!t.ok){const e=r.error||{},n=e.message||r.message||"Request failed",s=e.code||r.code||"api_error";throw new f(n,s,t.status)}return r.data??r}catch(e){if(e instanceof Error&&"AbortError"===e.name)throw new f("Request timed out","timeout",0);throw e}finally{clearTimeout(r)}}async getConfig(){return this.configCache||(this.configCache=this.request("GET","/config")),this.configCache}preloadConfig(){this.configCache||(this.configCache=this.request("GET","/config"))}isConfigPreloaded(){return null!==this.configCache}async checkPasskey(e){return this.request("POST","/passkey/check",{email:e})}buildAuthContextParams(e){if(!e)return{};const t={};return e.authRequestId&&(t.auth_request_id=e.authRequestId),e.simpleMode&&(t.simple_mode={success_url:e.simpleMode.successUrl,failure_url:e.simpleMode.failureUrl,state:e.simpleMode.state}),t}async sendTotp(e){return this.request("POST","/totp/send",{recipient:e.recipient,method:e.method,...this.buildAuthContextParams(e.authContext)})}async verifyTotp(e){return this.request("POST","/totp/verify",{kindling:e.kindling,pin:e.pin,recipient:e.recipient,...this.buildAuthContextParams(e.authContext)})}async startPasskeyRegister(e){const t=await this.request("POST","/passkey/register",{email:e});return{challenge:t.options.challenge,rpId:t.options.rp.id,rpName:t.options.rp.name,userId:t.options.user.id,userName:t.options.user.name,timeout:t.options.timeout,session:t.session}}async completePasskeyRegister(e){const t=e.credential.response;return this.request("POST","/passkey/register/complete",{session:e.session,credential:{id:e.credential.id,rawId:g(e.credential.rawId),type:e.credential.type,response:{clientDataJSON:g(t.clientDataJSON),attestationObject:g(t.attestationObject)}}})}async startPasskeyVerify(e,t){const n=await this.request("POST","/passkey/verify",{email:e,...this.buildAuthContextParams(t)});return{challenge:n.options.challenge,rpId:n.options.rpId,rpName:"SparkVault Identity",timeout:n.options.timeout,allowCredentials:n.options.allowCredentials,session:n.session}}async completePasskeyVerify(e){const t=e.credential.response;return this.request("POST","/passkey/verify/complete",{session:e.session,...this.buildAuthContextParams(e.authContext),credential:{id:e.credential.id,rawId:g(e.credential.rawId),type:e.credential.type,response:{clientDataJSON:g(t.clientDataJSON),authenticatorData:g(t.authenticatorData),signature:g(t.signature),userHandle:t.userHandle?g(t.userHandle):null}}})}getSocialAuthUrl(e,t,n){const s=new URLSearchParams({redirect_uri:t,state:n});return`${this.baseUrl}/social/${e}?${s}`}async sendSparkLink(e,t){return this.request("POST","/sparklink/send",{email:e,type:"verify_identity",openerOrigin:"undefined"!=typeof window?window.location.origin:void 0,...this.buildAuthContextParams(t)})}async checkSparkLinkStatus(e,t){return this.request("GET",`/sparklink/status/${e}`)}}class f extends Error{constructor(e,t,n){super(e),this.name="IdentityApiError",this.code=t,this.statusCode=n}}const k={totp_email:{id:"totp_email",type:"totp",identityType:"email",name:"Email code",description:"Get a one-time code via email",icon:"email"},totp_sms:{id:"totp_sms",type:"totp",identityType:"phone",name:"Text message",description:"Get a one-time code via SMS",icon:"sms"},totp_voice:{id:"totp_voice",type:"totp",identityType:"phone",name:"Voice call",description:"Get a one-time code via phone call",icon:"phone"},passkey:{id:"passkey",type:"passkey",identityType:"email",name:"Passkey",description:"Use biometrics or security key",icon:"passkey"},sparklink:{id:"sparklink",type:"sparklink",identityType:"email",name:"SparkLink",description:"Get an instant sign-in link via email",icon:"link"},social_google:{id:"social_google",type:"social",identityType:"email",name:"Google",description:"Sign in with Google",icon:"google",provider:"google"},social_apple:{id:"social_apple",type:"social",identityType:"email",name:"Apple",description:"Sign in with Apple",icon:"apple",provider:"apple"},social_microsoft:{id:"social_microsoft",type:"social",identityType:"email",name:"Microsoft",description:"Sign in with Microsoft",icon:"microsoft",provider:"microsoft"},social_github:{id:"social_github",type:"social",identityType:"email",name:"GitHub",description:"Sign in with GitHub",icon:"github",provider:"github"},social_facebook:{id:"social_facebook",type:"social",identityType:"email",name:"Facebook",description:"Sign in with Facebook",icon:"facebook",provider:"facebook"},social_linkedin:{id:"social_linkedin",type:"social",identityType:"email",name:"LinkedIn",description:"Sign in with LinkedIn",icon:"linkedin",provider:"linkedin"}};function y(e){return e.map(e=>k[e]).filter(e=>void 0!==e)}class x{constructor(){this._sdkConfig=null,this._viewState={view:"loading"},this._recipient="",this._identityType="email",this._authContext=void 0,this._passkey={hasPasskey:null,isFallbackMode:!1,pendingResult:null},this._totp={kindling:"",method:null},this.listeners=new Set}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){for(const e of this.listeners)e(this._viewState)}get sdkConfig(){return this._sdkConfig}setConfig(e){this._sdkConfig=e}get accountId(){return this._sdkConfig?.accountId??""}getAvailableMethods(){if(!this._sdkConfig)return[];let e=y(this._sdkConfig.methods??[]);return e="phone"===this._identityType?e.filter(e=>"totp"===e.type&&"totp_sms"===e.id):e.filter(e=>"email"===e.identityType),e}isPasskeyEnabled(){return this._sdkConfig?.methods?.includes("passkey")??!1}get viewState(){return this._viewState}setViewState(e){this._viewState=e,this.notify()}get recipient(){return this._recipient}get identityType(){return this._identityType}setIdentity(e,t){this._recipient=e,this._identityType=t}getAllowedIdentityTypes(){return this._sdkConfig?.allowedIdentityTypes??["email"]}get authContext(){return this._authContext}setAuthContext(e){this._authContext=e}get passkey(){return this._passkey}setPasskeyStatus(e){this._passkey.hasPasskey=e}enablePasskeyFallback(){this._passkey.isFallbackMode=!0}setPendingResult(e){this._passkey.pendingResult=e}shouldShowPasskeyPrompt(){return"email"===this._identityType&&(!!this.isPasskeyEnabled()&&(!this.isPasskeyPromptDismissed()&&!0!==this._passkey.hasPasskey))}isPasskeyPromptDismissed(){return null!==function(e){const t=document.cookie.match(new RegExp(`(^| )${e}=([^;]+)`));return t?t[2]:null}(`sv_passkey_prompt_dismissed_${this.accountId}`)}dismissPasskeyPrompt(){!function(e,t,n){const s=new Date;s.setTime(s.getTime()+24*n*60*60*1e3);const i=`expires=${s.toUTCString()}`;document.cookie=`${e}=${t}; ${i}; path=/; SameSite=Lax`}(`sv_passkey_prompt_dismissed_${this.accountId}`,"1",30)}get totp(){return this._totp}setKindling(e){this._totp.kindling=e}setTotpMethod(e){this._totp.method=e}reset(){this._viewState={view:"loading"},this._recipient="",this._identityType="email",this._authContext=void 0,this._passkey.hasPasskey=null,this._passkey.isFallbackMode=!1,this._passkey.pendingResult=null,this._totp.kindling="",this._totp.method=null}}class w{constructor(e,t){this.api=e,this.state=t;const n=e.config;this.baseUrl=n.identityBaseUrl,this.accountId=n.accountId}async checkPasskey(){try{const e=await this.api.checkPasskey(this.state.recipient);return this.state.setPasskeyStatus(e.hasPasskey),{emailValid:e.email_valid,hasPasskey:e.hasPasskey}}catch(e){return console.warn("Passkey check failed:",e),this.state.setPasskeyStatus(null),null}}async register(){const e=await this.openPasskeyPopup("register");return e.success&&this.state.setPasskeyStatus(!0),e}async verify(){return this.openPasskeyPopup("verify")}async openPasskeyPopup(e){return new Promise(t=>{const n=new URLSearchParams({mode:e,email:this.state.recipient,origin:window.location.origin}),s=this.state.authContext;s?.authRequestId&&n.set("auth_request_id",s.authRequestId),s?.simpleMode&&(n.set("simple_mode","true"),n.set("success_url",s.simpleMode.successUrl),n.set("failure_url",s.simpleMode.failureUrl),s.simpleMode.state&&n.set("state",s.simpleMode.state));const i=`${this.baseUrl}/${this.accountId}/passkey/popup?${n}`,o=Math.round((window.screen.width-450)/2),r=Math.round((window.screen.height-500)/2),a=window.open(i,"sparkvault-passkey",`width=450,height=500,left=${o},top=${r},popup=1`);if(!a)return void t({success:!1,error:"Popup was blocked. Please allow popups for this site.",errorType:"popup_blocked"});let l=!1;const d=()=>{window.removeEventListener("message",c),clearTimeout(h),clearInterval(p)},c=e=>{if(!this.isValidMessageOrigin(e.origin))return;const n=e.data;"sparkvault-passkey-result"===n?.type&&(l=!0,d(),n.success&&n.data?t({success:!0,result:{token:n.data.token||"",identity:n.data.identity||this.state.recipient,identityType:n.data.identity_type||"email",redirect:n.data.redirect}}):t(this.handlePopupError(n.error,n.message)))};window.addEventListener("message",c);const h=setTimeout(()=>{l||(l=!0,d(),a.close(),t({success:!1,error:"Passkey operation timed out. Please try again.",errorType:"unknown"}))},6e4),p=setInterval(()=>{a.closed&&!l&&(l=!0,d(),t({success:!1,error:"Authentication was cancelled.",errorType:"cancelled"}))},500)})}isValidMessageOrigin(e){try{const t=new URL(e).hostname;return!("sparkvault.com"!==t&&!t.endsWith(".sparkvault.com"))||"localhost"===t}catch{return!1}}handlePopupError(e,t){return"cancelled"===e?{success:!1,error:t||"Authentication was cancelled. Please try again.",errorType:"cancelled"}:t?.includes("No passkey")||t?.includes("not found")?{success:!1,error:"No passkey found. Create one to continue.",errorType:"not_found"}:{success:!1,error:t||e||"Passkey authentication failed",errorType:"unknown"}}}class C{constructor(e,t){this.api=e,this.state=t}async send(e){try{const t=await this.api.sendTotp({recipient:this.state.recipient,method:e,authContext:this.state.authContext});return this.state.setKindling(t.kindling),this.state.setTotpMethod(e),{success:!0,kindling:t.kindling,expiresAt:t.expires_at}}catch(e){return{success:!1,error:e instanceof Error?e.message:"Failed to send code"}}}async resend(){const e=this.state.totp.method;if(!e)return{success:!1,error:"No TOTP method set"};try{const t=await this.api.sendTotp({recipient:this.state.recipient,method:e,authContext:this.state.authContext});return this.state.setKindling(t.kindling),{success:!0,kindling:t.kindling,expiresAt:t.expires_at}}catch(e){return{success:!1,error:`Failed to resend: ${e instanceof Error?e.message:"Failed to resend code"}`}}}async verify(e){const t=this.state.totp.kindling;if(!t)return{success:!1,error:"No kindling available"};try{const n=await this.api.verifyTotp({kindling:t,pin:e,recipient:this.state.recipient,authContext:this.state.authContext});return n.verified&&n.token?{success:!0,result:{token:n.token,identity:n.identity??this.state.recipient,identityType:n.identity_type??this.state.identityType,redirect:n.redirect}}:(n.kindling&&this.state.setKindling(n.kindling),{success:!1,newKindling:n.kindling,retryAfter:n.retry_after,backoffExpires:n.expires_at,error:"Invalid code. Please try again."})}catch(e){return{success:!1,error:e instanceof Error?e.message:"Verification failed"}}}}class E{constructor(e,t){this.api=e,this.state=t}async send(){try{const e=await this.api.sendSparkLink(this.state.recipient,this.state.authContext);return{success:!0,sparkId:e.sparkId,expiresAt:e.expiresAt}}catch(e){return{success:!1,error:e instanceof Error?e.message:"Failed to send SparkLink"}}}async checkStatus(e){try{const t=await this.api.checkSparkLinkStatus(e);return{verified:t.verified,token:t.token,identity:t.identity,identityType:t.identityType,redirect:t.redirect}}catch{return{verified:!1}}}}const S="sparkvault-identity-styles";function B(e){if(document.getElementById(S))return e.branding,void function(e){const t=document.getElementById("sparkvault-identity-overlay");t&&(!1!==e?t.classList.add("sv-blur"):t.classList.remove("sv-blur"))}(e.backdropBlur);const t=document.createElement("style");t.id=S,t.textContent=function(e){const{branding:t,backdropBlur:n}=e,s="dark"===$(t),i=!1!==n,o={primary:"#4F46E5",primaryHover:"#4338CA",primaryLight:s?"rgba(99, 102, 241, 0.15)":"rgba(79, 70, 229, 0.08)",error:"#DC2626",errorLight:s?"rgba(220, 38, 38, 0.15)":"#FEF2F2",errorBorder:s?"rgba(220, 38, 38, 0.3)":"#FECACA",bg:s?"#0F0F0F":"#FFFFFF",bgSubtle:s?"#171717":"#FAFAFA",bgHover:s?"#1A1A1A":"#F5F5F5",bgInput:s?"#0F0F0F":"#FFFFFF",border:s?"#262626":"#E5E5E5",borderHover:s?"#404040":"#D4D4D4",borderFocus:"#4F46E5",textPrimary:s?"#FAFAFA":"#0A0A0A",textSecondary:s?"#A3A3A3":"#525252",textMuted:"#737373",textOnPrimary:"#FFFFFF",shadowSm:s?"0 1px 2px rgba(0, 0, 0, 0.4)":"0 1px 2px rgba(0, 0, 0, 0.04)",shadowLg:s?"0 8px 24px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05)":"0 8px 24px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.04)"};return`\n /* ========================================\n OVERLAY & MODAL CONTAINER\n ======================================== */\n\n .sv-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, ${s?"0.7":"0.5"});\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n padding: 16px;\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n .sv-overlay.sv-blur {\n backdrop-filter: blur(${i?"8px":"0"});\n -webkit-backdrop-filter: blur(${i?"8px":"0"});\n }\n\n .sv-modal {\n background: ${o.bg};\n border-radius: 16px;\n box-shadow: ${o.shadowLg};\n width: 100%;\n max-width: 400px;\n max-height: calc(100vh - 32px);\n display: flex;\n flex-direction: column;\n color: ${o.textPrimary};\n }\n\n /* ========================================\n HEADER\n ======================================== */\n\n .sv-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-bottom: 1px solid ${o.border};\n background: ${o.bg};\n flex-shrink: 0;\n }\n\n .sv-header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n }\n\n .sv-logo {\n height: 28px;\n width: auto;\n max-width: 140px;\n object-fit: contain;\n flex-shrink: 0;\n }\n\n .sv-company-name {\n font-size: 15px;\n font-weight: 600;\n letter-spacing: -0.01em;\n margin: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .sv-close-btn {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n color: ${o.textMuted};\n transition: background 0.15s ease, color 0.15s ease;\n }\n\n .sv-close-btn:hover {\n background: ${o.bgHover};\n color: ${o.textPrimary};\n }\n\n .sv-close-btn:focus-visible {\n outline: 2px solid ${o.borderFocus};\n outline-offset: 2px;\n }\n\n .sv-close-btn:active {\n transform: scale(0.96);\n }\n\n /* ========================================\n BODY\n ======================================== */\n\n .sv-body {\n padding: 24px 28px;\n overflow-y: auto;\n flex: 1;\n }\n\n /* ========================================\n TYPOGRAPHY\n ======================================== */\n\n .sv-title {\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.02em;\n line-height: 1.3;\n margin: 0 0 6px 0;\n text-align: center;\n color: ${o.textPrimary};\n }\n\n .sv-subtitle {\n font-size: 14px;\n font-weight: 400;\n line-height: 1.5;\n color: ${o.textSecondary};\n margin: 0 0 16px 0;\n text-align: center;\n }\n\n .sv-subtitle strong {\n color: ${o.textPrimary};\n font-weight: 500;\n }\n\n /* ========================================\n FORM ELEMENTS\n ======================================== */\n\n .sv-form-group {\n margin-bottom: 16px;\n }\n\n .sv-label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n letter-spacing: -0.005em;\n margin-bottom: 6px;\n color: ${o.textPrimary};\n }\n\n .sv-input {\n width: 100%;\n padding: 10px 12px;\n font-size: 15px;\n font-weight: 400;\n line-height: 1.4;\n border: 1px solid ${o.border};\n border-radius: 8px;\n background: ${o.bgInput};\n color: ${o.textPrimary};\n box-sizing: border-box;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n }\n\n .sv-input:hover:not(:focus):not(:disabled) {\n border-color: ${o.borderHover};\n }\n\n .sv-input:focus {\n outline: none;\n border-color: ${o.borderFocus};\n box-shadow: 0 0 0 3px ${o.primaryLight};\n }\n\n .sv-input::placeholder {\n color: ${o.textMuted};\n }\n\n .sv-input:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .sv-input.sv-input-error,\n .sv-input.sv-email-input-error {\n border-color: ${o.error};\n }\n\n .sv-input.sv-input-error:focus,\n .sv-input.sv-email-input-error:focus {\n box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.1);\n }\n\n /* ========================================\n BUTTONS\n ======================================== */\n\n .sv-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 10px 16px;\n font-size: 14px;\n font-weight: 500;\n letter-spacing: -0.005em;\n line-height: 1.4;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.15s ease, transform 0.1s ease, box-shadow 0.15s ease;\n box-sizing: border-box;\n }\n\n .sv-btn:focus-visible {\n outline: 2px solid ${o.borderFocus};\n outline-offset: 2px;\n }\n\n .sv-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .sv-btn:active:not(:disabled) {\n transform: scale(0.98);\n }\n\n /* Primary Button */\n .sv-btn-primary,\n .sv-btn-email-primary {\n background: ${o.primary};\n color: ${o.textOnPrimary};\n box-shadow: ${o.shadowSm};\n }\n\n .sv-btn-primary:hover:not(:disabled),\n .sv-btn-email-primary:hover:not(:disabled) {\n background: ${o.primaryHover};\n }\n\n /* Secondary Button */\n .sv-btn-secondary {\n background: ${o.bgSubtle};\n color: ${o.textPrimary};\n border: 1px solid ${o.border};\n }\n\n .sv-btn-secondary:hover:not(:disabled) {\n background: ${o.bgHover};\n border-color: ${o.borderHover};\n }\n\n /* Method Button */\n .sv-btn-method {\n background: ${o.bg};\n border: 1px solid ${o.border};\n color: ${o.textPrimary};\n text-align: left;\n padding: 12px 14px;\n margin-bottom: 8px;\n }\n\n .sv-btn-method:hover:not(:disabled) {\n background: ${o.bgSubtle};\n border-color: ${o.borderHover};\n }\n\n .sv-btn-method:last-child {\n margin-bottom: 0;\n }\n\n /* Error Buttons */\n .sv-btn-error-primary {\n background: ${o.error};\n color: ${o.textOnPrimary};\n box-shadow: ${o.shadowSm};\n }\n\n .sv-btn-error-primary:hover:not(:disabled) {\n background: #B91C1C;\n }\n\n .sv-btn-error-secondary {\n background: ${o.bgSubtle};\n color: ${o.textSecondary};\n border: 1px solid ${o.border};\n }\n\n .sv-btn-error-secondary:hover:not(:disabled) {\n background: ${o.bgHover};\n color: ${o.textPrimary};\n }\n\n /* ========================================\n METHOD SELECTION\n ======================================== */\n\n .sv-method-content {\n display: flex;\n align-items: center;\n gap: 12px;\n width: 100%;\n }\n\n .sv-method-icon {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: ${o.textSecondary};\n }\n\n .sv-method-icon svg {\n width: 20px;\n height: 20px;\n }\n\n .sv-method-text {\n flex: 1;\n min-width: 0;\n }\n\n .sv-method-name {\n font-weight: 500;\n font-size: 14px;\n color: ${o.textPrimary};\n }\n\n .sv-method-description {\n font-size: 12px;\n color: ${o.textMuted};\n margin-top: 1px;\n }\n\n /* ========================================\n TOTP INPUT\n ======================================== */\n\n .sv-totp-input-group {\n display: flex;\n gap: 8px;\n justify-content: center;\n margin-bottom: 16px;\n }\n\n .sv-totp-digit {\n width: 44px;\n height: 52px;\n text-align: center;\n font-size: 20px;\n font-weight: 600;\n letter-spacing: 0;\n border: 1px solid ${o.border};\n border-radius: 8px;\n background: ${o.bgInput};\n color: ${o.textPrimary};\n box-sizing: border-box;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n }\n\n .sv-totp-digit:hover:not(:focus):not(:disabled) {\n border-color: ${o.borderHover};\n }\n\n .sv-totp-digit:focus {\n outline: none;\n border-color: ${o.borderFocus};\n box-shadow: 0 0 0 3px ${o.primaryLight};\n }\n\n .sv-totp-digit:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* ========================================\n BACK LINK\n ======================================== */\n\n .sv-back-link {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: ${o.textSecondary};\n font-size: 13px;\n font-weight: 500;\n text-decoration: none;\n cursor: pointer;\n background: none;\n border: none;\n padding: 0;\n margin-bottom: 16px;\n transition: color 0.15s ease;\n }\n\n .sv-back-link:hover:not(:disabled) {\n color: ${o.primary};\n }\n\n .sv-back-link:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .sv-back-link svg {\n width: 14px;\n height: 14px;\n }\n\n /* ========================================\n ERROR MESSAGE\n ======================================== */\n\n .sv-error {\n background: ${o.errorLight};\n border: 1px solid ${o.errorBorder};\n color: ${o.error};\n padding: 10px 12px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 450;\n margin-bottom: 16px;\n }\n\n /* ========================================\n LOADING STATE\n ======================================== */\n\n .sv-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n gap: 16px;\n }\n\n .sv-spinner {\n width: 28px;\n height: 28px;\n border: 2.5px solid ${o.border};\n border-top-color: ${o.primary};\n border-radius: 50%;\n animation: sv-spin 0.7s linear infinite;\n }\n\n .sv-loading-text {\n font-size: 14px;\n color: ${o.textSecondary};\n margin: 0;\n }\n\n @keyframes sv-spin {\n to { transform: rotate(360deg); }\n }\n\n /* ========================================\n FOOTER\n ======================================== */\n\n .sv-footer {\n padding: 10px 20px;\n border-top: 1px solid ${o.border};\n text-align: center;\n background: ${o.bgSubtle};\n flex-shrink: 0;\n }\n\n .sv-footer-text {\n font-size: 10px;\n color: ${o.textMuted};\n letter-spacing: 0.02em;\n }\n\n .sv-footer-link {\n color: ${o.textSecondary};\n text-decoration: none;\n font-weight: 500;\n transition: color 0.15s ease;\n }\n\n .sv-footer-link:hover {\n color: ${o.primary};\n }\n\n /* ========================================\n IDENTITY INPUT VIEW\n ======================================== */\n\n .sv-email-view {\n display: flex;\n flex-direction: column;\n }\n\n .sv-email-header {\n margin-bottom: 16px;\n }\n\n .sv-email-subtitle {\n font-size: 14px;\n font-weight: 500;\n line-height: 1.4;\n color: ${o.textSecondary};\n margin: 0;\n }\n\n .sv-email-error-container {\n width: 100%;\n }\n\n .sv-email-form {\n width: 100%;\n }\n\n .sv-email-input-wrapper {\n margin-bottom: 14px;\n }\n\n .sv-email-label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n letter-spacing: -0.005em;\n margin-bottom: 6px;\n color: ${o.textPrimary};\n }\n\n .sv-email-input {\n width: 100%;\n padding: 10px 12px;\n font-size: 15px;\n font-weight: 400;\n line-height: 1.4;\n border: 1px solid ${o.border};\n border-radius: 8px;\n background: ${o.bgInput};\n color: ${o.textPrimary};\n box-sizing: border-box;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n }\n\n .sv-email-input:hover:not(:focus):not(:disabled) {\n border-color: ${o.borderHover};\n }\n\n .sv-email-input:focus {\n outline: none;\n border-color: ${o.borderFocus};\n box-shadow: 0 0 0 3px ${o.primaryLight};\n }\n\n .sv-email-input::placeholder {\n color: ${o.textMuted};\n }\n\n .sv-email-input-error {\n border-color: ${o.error};\n }\n\n .sv-email-input-error:focus {\n box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.1);\n }\n\n /* ========================================\n ERROR VIEW\n ======================================== */\n\n .sv-error-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n }\n\n .sv-error-icon-container {\n margin-bottom: 16px;\n }\n\n .sv-error-icon {\n width: 48px;\n height: 48px;\n }\n\n .sv-error-content {\n margin-bottom: 24px;\n }\n\n .sv-error-title {\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.02em;\n margin: 0 0 8px 0;\n color: ${o.textPrimary};\n }\n\n .sv-error-message {\n font-size: 14px;\n line-height: 1.5;\n color: ${o.textSecondary};\n margin: 0 0 16px 0;\n }\n\n .sv-error-code-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n background: ${o.errorLight};\n border: 1px solid ${o.errorBorder};\n border-radius: 6px;\n }\n\n .sv-error-code-label {\n font-size: 10px;\n font-weight: 600;\n color: ${s?"#F87171":"#991B1B"};\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .sv-error-code-value {\n font-size: 11px;\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, 'Cascadia Mono', monospace;\n color: ${o.error};\n font-weight: 500;\n }\n\n .sv-error-actions {\n display: flex;\n gap: 10px;\n width: 100%;\n }\n\n .sv-error-actions .sv-btn {\n flex: 1;\n }\n\n /* ========================================\n TOTP VIEW\n ======================================== */\n\n .sv-resend-container {\n text-align: center;\n margin-top: 16px;\n }\n\n /* ========================================\n PASSKEY VIEW\n ======================================== */\n\n .sv-passkey-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 12px;\n display: block;\n }\n\n .sv-passkey-description {\n font-size: 13px;\n color: ${o.textMuted};\n text-align: center;\n margin: 0 0 16px 0;\n line-height: 1.4;\n }\n\n .sv-fallback-link {\n display: block;\n width: 100%;\n text-align: center;\n color: ${o.textSecondary};\n font-size: 13px;\n font-weight: 400;\n text-decoration: none;\n cursor: pointer;\n background: none;\n border: none;\n padding: 12px 0 0 0;\n margin-top: 8px;\n transition: color 0.15s ease;\n }\n\n .sv-fallback-link:hover {\n color: ${o.primary};\n text-decoration: underline;\n }\n\n /* ========================================\n PASSKEY PROMPT VIEW\n ======================================== */\n\n .sv-passkey-prompt-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 12px;\n display: block;\n color: ${o.primary};\n }\n\n .sv-passkey-prompt-benefits {\n background: ${o.bgSubtle};\n border-radius: 8px;\n padding: 10px 14px;\n margin-bottom: 16px;\n }\n\n .sv-passkey-prompt-benefits ul {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n .sv-passkey-prompt-benefits li {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n color: ${o.textSecondary};\n padding: 6px 0;\n }\n\n .sv-passkey-prompt-benefits li svg {\n width: 16px;\n height: 16px;\n color: ${o.primary};\n flex-shrink: 0;\n }\n\n .sv-skip-link {\n display: block;\n width: 100%;\n text-align: center;\n color: ${o.textMuted};\n font-size: 13px;\n font-weight: 400;\n text-decoration: none;\n cursor: pointer;\n background: none;\n border: none;\n padding: 12px 0 0 0;\n transition: color 0.15s ease;\n }\n\n .sv-skip-link:hover {\n color: ${o.textSecondary};\n text-decoration: underline;\n }\n\n /* ========================================\n SPARKLINK WAITING VIEW\n ======================================== */\n\n .sv-sparklink-icon-container {\n display: flex;\n justify-content: center;\n margin-bottom: 16px;\n }\n\n .sv-sparklink-icon {\n width: 56px;\n height: 56px;\n color: ${o.primary};\n background: ${o.primaryLight};\n padding: 14px;\n border-radius: 50%;\n box-sizing: content-box;\n }\n\n .sv-sparklink-waiting {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n background: ${o.bgSubtle};\n border-radius: 10px;\n padding: 14px 18px;\n margin: 16px 0;\n }\n\n .sv-spinner-small {\n width: 18px;\n height: 18px;\n border: 2px solid ${o.border};\n border-top-color: ${o.primary};\n border-radius: 50%;\n animation: sv-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n\n .sv-sparklink-waiting-text {\n font-size: 13px;\n font-weight: 450;\n color: ${o.textSecondary};\n }\n\n .sv-sparklink-countdown {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n font-size: 13px;\n color: ${o.textMuted};\n margin-bottom: 20px;\n }\n\n .sv-sparklink-countdown-time {\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, 'Cascadia Mono', monospace;\n font-weight: 600;\n font-size: 14px;\n color: ${o.textPrimary};\n min-width: 40px;\n }\n\n .sv-sparklink-fallback {\n text-align: center;\n margin-top: 12px;\n padding-top: 16px;\n border-top: 1px solid ${o.border};\n }\n\n .sv-sparklink-fallback .sv-back-link {\n margin-bottom: 0;\n font-size: 12px;\n color: ${o.textMuted};\n }\n\n .sv-sparklink-fallback .sv-back-link:hover:not(:disabled) {\n color: ${o.textSecondary};\n }\n\n /* SparkLink Expired State */\n .sv-sparklink-expired {\n flex-direction: column;\n gap: 12px;\n background: ${o.bgSubtle};\n border: 1px solid ${o.border};\n }\n\n .sv-sparklink-expired-icon {\n display: flex;\n justify-content: center;\n }\n\n .sv-sparklink-expired-icon svg {\n width: 48px;\n height: 48px;\n }\n\n .sv-sparklink-expired-text {\n font-size: 14px;\n font-weight: 500;\n color: ${o.textSecondary};\n text-align: center;\n }\n\n .sv-expired-icon {\n width: 48px;\n height: 48px;\n }\n\n /* ========================================\n DIVIDER\n ======================================== */\n\n .sv-divider {\n display: flex;\n align-items: center;\n margin: 20px 0;\n }\n\n .sv-divider::before,\n .sv-divider::after {\n content: '';\n flex: 1;\n height: 1px;\n background: ${o.border};\n }\n\n .sv-divider-text {\n padding: 0 12px;\n font-size: 12px;\n color: ${o.textMuted};\n }\n\n /* ========================================\n ACCESSIBILITY\n ======================================== */\n\n .sv-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* ========================================\n INLINE CONTAINER\n ======================================== */\n\n .sv-inline-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n background: ${o.bg};\n color: ${o.textPrimary};\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n border-radius: 12px;\n border: 1px solid ${o.border};\n }\n\n .sv-inline-header {\n border-bottom: 1px solid ${o.border};\n }\n\n .sv-inline-body {\n display: flex;\n flex-direction: column;\n justify-content: center;\n min-height: 200px;\n flex: 1;\n }\n\n /* ========================================\n RESPONSIVE\n ======================================== */\n\n @media (max-width: 480px) {\n .sv-modal {\n max-width: 100%;\n max-height: 100%;\n border-radius: 0;\n }\n\n .sv-overlay {\n padding: 0;\n }\n\n .sv-body {\n padding: 24px 28px;\n }\n\n .sv-totp-digit {\n width: 40px;\n height: 48px;\n font-size: 18px;\n }\n\n .sv-error-actions {\n flex-direction: column;\n }\n }\n\n @media (max-width: 360px) {\n .sv-totp-input-group {\n gap: 6px;\n }\n\n .sv-totp-digit {\n width: 36px;\n height: 44px;\n font-size: 16px;\n }\n }\n `}(e),document.head.appendChild(t)}function $(e){return e?.themeMode??"light"}function T(e,t=16,n=16){const s=document.createElementNS("http://www.w3.org/2000/svg","svg");return s.setAttribute("viewBox",e),s.setAttribute("width",String(t)),s.setAttribute("height",String(n)),s.setAttribute("fill","none"),s.setAttribute("xmlns","http://www.w3.org/2000/svg"),s}function L(e,t={}){const n=document.createElementNS("http://www.w3.org/2000/svg","path");n.setAttribute("d",e);for(const[e,s]of Object.entries(t))n.setAttribute(e,s);return n}function M(e,t,n,s={}){const i=document.createElementNS("http://www.w3.org/2000/svg","circle");i.setAttribute("cx",String(e)),i.setAttribute("cy",String(t)),i.setAttribute("r",String(n));for(const[e,t]of Object.entries(s))i.setAttribute(e,t);return i}function I(){const e=T("0 0 16 16");return e.appendChild(L("M13.25 4.75L6 12 2.75 8.75",{stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"})),e}function H(){const e=T("0 0 16 16");return e.appendChild(L("M10 12L6 8L10 4",{stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"})),e}function A(){const e=T("0 0 20 20",20,20);return e.appendChild(L("M15 5L5 15M5 5L15 15",{stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"})),e}function P(){const e=T("0 0 56 56",56,56);e.classList.add("sv-passkey-icon"),e.appendChild(M(28,28,26,{fill:"#EEF2FF",stroke:"#E0E7FF","stroke-width":"1"}));const t=document.createElementNS("http://www.w3.org/2000/svg","g");t.setAttribute("transform","translate(28, 28)");const n={stroke:"#4F46E5","stroke-width":"2.5","stroke-linecap":"round","stroke-linejoin":"round",fill:"none"};return t.appendChild(M(-6,-6,6,n)),t.appendChild(L("M-1.5 -1.5L10 10",n)),t.appendChild(L("M6 6L6 10",n)),t.appendChild(L("M10 10L10 14",n)),e.appendChild(t),e}function N(e){switch(e){case"email":default:return F();case"phone":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(L("M18 14v2.5a1.5 1.5 0 01-1.64 1.5 14.85 14.85 0 01-6.47-2.3 14.63 14.63 0 01-4.5-4.5A14.85 14.85 0 013.1 4.64 1.5 1.5 0 014.59 3H7.1a1.5 1.5 0 011.5 1.29 9.63 9.63 0 00.52 2.11 1.5 1.5 0 01-.34 1.58l-.95.95a12 12 0 004.5 4.5l.95-.95a1.5 1.5 0 011.58-.34 9.63 9.63 0 002.11.52A1.5 1.5 0 0118 14z")),e}();case"fingerprint":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(L("M2 10c0-4.42 3.58-8 8-8 2.65 0 5 1.29 6.47 3.28")),e.appendChild(L("M4 16.58c.42-.35.79-.76 1.1-1.22")),e.appendChild(L("M14.41 17.51c.09-.45.32-1.73.38-2.26")),e.appendChild(L("M10 8.33a1.5 1.5 0 00-1.5 1.5c0 .77-.08 1.88-.2 3")),e.appendChild(L("M7.21 18.33c.16-.5.34-1 .43-1.5")),e.appendChild(L("M11.67 10.93c0 1.79 0 4.79-.75 6.65")),e.appendChild(L("M17.33 13.33c.15-1.5.1-4.02 0-4.5")),e.appendChild(L("M7.5 5.67a4.5 4.5 0 016.75 3.9c0 .35 0 .88-.02 1.5")),e}();case"link":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(L("M8.33 10.83a3.75 3.75 0 005.66.41l2.25-2.25a3.75 3.75 0 00-5.3-5.3l-1.29 1.28")),e.appendChild(L("M11.67 9.17a3.75 3.75 0 00-5.66-.41l-2.25 2.25a3.75 3.75 0 005.3 5.3l1.28-1.28")),e}();case"google":return function(){const e=T("0 0 20 20",20,20);return e.appendChild(L("M18.8 10.21c0-.65-.06-1.28-.17-1.88H10v3.55h4.93c-.21 1.14-.87 2.11-1.84 2.76v2.3h2.98c1.73-1.6 2.73-3.96 2.73-6.73z",{fill:"#4285F4"})),e.appendChild(L("M10 19.17c2.48 0 4.55-.82 6.07-2.23l-2.98-2.3c-.82.55-1.86.88-3.09.88-2.38 0-4.4-1.6-5.13-3.77H1.82v2.37c1.52 3.02 4.6 5.05 8.18 5.05z",{fill:"#34A853"})),e.appendChild(L("M4.87 11.74c-.18-.55-.29-1.14-.29-1.74s.1-1.19.29-1.74V5.89H1.82c-.6 1.23-.94 2.6-.94 4.11s.34 2.88.94 4.11l3.05-2.37z",{fill:"#FBBC05"})),e.appendChild(L("M10 4.48c1.35 0 2.55.47 3.5 1.37l2.63-2.63C14.53 1.74 12.47.83 10 .83c-3.58 0-6.66 2.03-8.18 5.05l3.05 2.37c.72-2.17 2.75-3.77 5.13-3.77z",{fill:"#EA4335"})),e}();case"apple":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("fill","currentColor"),e.appendChild(L("M14.21 16.9c-.82.79-1.71.67-2.57.29-.91-.38-1.74-.4-2.7 0-1.2.51-1.83.37-2.55-.29C2.7 12.71 3.3 6.33 7.54 6.09c1.13.06 1.91.61 2.57.66.98-.2 1.92-.78 2.97-.7 1.26.1 2.21.6 2.83 1.5-2.6 1.56-1.98 4.99.4 5.94-.48 1.25-1.09 2.49-2.11 3.41h.01zM10.02 6.04c-.12-1.86 1.38-3.39 3.11-3.54.24 2.15-1.95 3.75-3.11 3.54z")),e}();case"microsoft":return function(){const e=T("0 0 20 20",20,20);return e.appendChild(L("M1 1h8.33v8.33H1z",{fill:"#F25022"})),e.appendChild(L("M1 10.67h8.33V19H1z",{fill:"#00A4EF"})),e.appendChild(L("M10.67 1H19v8.33h-8.33z",{fill:"#7FBA00"})),e.appendChild(L("M10.67 10.67H19V19h-8.33z",{fill:"#FFB900"})),e}();case"github":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("fill","currentColor"),e.appendChild(L("M10 0C4.48 0 0 4.48 0 10c0 4.42 2.86 8.17 6.84 9.49.5.09.68-.22.68-.48 0-.24-.01-1.02-.01-1.86-2.51.46-3.16-.61-3.36-1.18-.11-.29-.6-1.18-1.02-1.41-.35-.19-.85-.65-.01-.66.79-.01 1.35.72 1.54 1.02.9 1.51 2.34 1.09 2.91.83.09-.65.35-1.09.64-1.34-2.22-.25-4.55-1.11-4.55-4.94 0-1.09.39-1.99 1.02-2.69-.1-.25-.45-1.27.1-2.65 0 0 .84-.27 2.75 1.02.8-.22 1.65-.33 2.5-.33s1.7.11 2.5.33c1.91-1.3 2.75-1.02 2.75-1.02.55 1.38.2 2.4.1 2.65.64.7 1.02 1.59 1.02 2.69 0 3.84-2.34 4.69-4.57 4.94.36.31.67.91.67 1.85 0 1.34-.01 2.41-.01 2.75 0 .27.18.58.69.48C17.14 18.16 20 14.42 20 10c0-5.52-4.48-10-10-10z")),e}();case"facebook":return function(){const e=T("0 0 20 20",20,20);return e.appendChild(L("M20 10.06c0-5.52-4.48-10-10-10S0 4.53 0 10.06c0 4.99 3.66 9.13 8.44 9.88v-6.99H5.9v-2.89h2.54V7.85c0-2.51 1.49-3.89 3.78-3.89 1.09 0 2.24.2 2.24.2v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56v1.88h2.77l-.44 2.89h-2.33v6.99c4.78-.75 8.44-4.89 8.44-9.88z",{fill:"#1877F2"})),e}();case"linkedin":return function(){const e=T("0 0 20 20",20,20);return e.appendChild(L("M17.04 17.04h-2.96v-4.64c0-1.11-.02-2.53-1.54-2.53-1.55 0-1.78 1.2-1.78 2.45v4.73H7.8V7.5h2.84v1.3h.04c.4-.75 1.36-1.54 2.81-1.54 3 0 3.56 1.98 3.56 4.55v5.24zM4.45 6.19c-.95 0-1.72-.77-1.72-1.72s.77-1.72 1.72-1.72 1.72.77 1.72 1.72-.77 1.72-1.72 1.72zm1.48 10.85H2.96V7.5h2.97v9.54zM18.52 0H1.47C.66 0 0 .65 0 1.44v17.12c0 .79.66 1.44 1.48 1.44h17.04c.82 0 1.48-.65 1.48-1.44V1.44c0-.79-.66-1.44-1.48-1.44z",{fill:"#0A66C2"})),e}();case"building":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(L("M2.5 17.5h15")),e.appendChild(L("M4.17 17.5V5.83L10.83 2.5v15")),e.appendChild(L("M15.83 17.5V9.17l-5-3.33")),e.appendChild(L("M7.5 7.5v.01")),e.appendChild(L("M7.5 10v.01")),e.appendChild(L("M7.5 12.5v.01")),e.appendChild(L("M7.5 15v.01")),e}();case"users":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(L("M14.17 17.5v-1.67a3.33 3.33 0 00-3.34-3.33H4.17a3.33 3.33 0 00-3.34 3.33v1.67")),e.appendChild(M(7.5,5.83,3.33,{stroke:"currentColor","stroke-width":"1.5",fill:"none"})),e.appendChild(L("M19.17 17.5v-1.67a3.33 3.33 0 00-2.5-3.22")),e.appendChild(L("M13.33 2.6a3.33 3.33 0 010 6.46")),e}()}}function F(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(L("M3 4h14c.55 0 1 .45 1 1v10c0 .55-.45 1-1 1H3c-.55 0-1-.45-1-1V5c0-.55.45-1 1-1z")),e.appendChild(L("M18 5l-8 5.5L2 5")),e}const _={companyName:"",logoLight:null,logoDark:null};class D{constructor(){this.elements=null,this.onCloseCallback=null,this.keydownHandler=null,this.overlayClickHandler=null,this.closeBtnClickHandler=null,this.closeBtn=null,this.backdropBlur=!0,this.effectiveTheme="light",this.headerElement=null}createLoading(e,t){return this.create({branding:_,backdropBlur:e.backdropBlur},t)}updateBranding(e){if(!this.elements)return;this.effectiveTheme=$(e);const t=this.createHeader(e);this.headerElement?.parentNode&&this.headerElement.parentNode.replaceChild(t,this.headerElement),this.headerElement=t}updateBackdropBlur(e){this.elements&&(this.backdropBlur=e,e?this.elements.overlay.classList.add("sv-blur"):this.elements.overlay.classList.remove("sv-blur"))}create(e,t){if(this.elements)return this.elements;this.onCloseCallback=t,this.backdropBlur=!1!==e.backdropBlur,this.effectiveTheme=$(e.branding);B({branding:e.branding,backdropBlur:e.backdropBlur});const n=document.createElement("div");n.id="sparkvault-identity-overlay",n.className=this.backdropBlur?"sv-overlay sv-blur":"sv-overlay",n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-labelledby","sv-modal-title");const s=document.createElement("div");s.id="sparkvault-identity-modal",s.className="sv-modal";const i=this.createHeader(e.branding);this.headerElement=i;const o=document.createElement("div");o.className="sv-body";const r=document.createElement("div");r.className="sv-footer";const a=document.createElement("span");a.className="sv-footer-text",a.appendChild(document.createTextNode("Secured by "));const l=document.createElement("a");l.href="https://sparkvault.com",l.target="_blank",l.rel="noopener noreferrer",l.className="sv-footer-link",l.style.color="inherit",l.style.textDecoration="none",l.textContent="SparkVault",a.appendChild(l),r.appendChild(a),s.appendChild(i),s.appendChild(o),s.appendChild(r),n.appendChild(s),this.overlayClickHandler=e=>{e.target===n&&this.handleClose()},n.addEventListener("click",this.overlayClickHandler),this.keydownHandler=e=>{"Escape"===e.key&&this.handleClose()},document.addEventListener("keydown",this.keydownHandler),document.body.appendChild(n),document.body.style.overflow="hidden",this.elements={overlay:n,modal:s,header:i,body:o,footer:r};const d=o.querySelector("input, button");return d instanceof HTMLElement&&d.focus(),this.elements}createHeader(e){const t=document.createElement("div");t.className="sv-header";const n=document.createElement("div");n.className="sv-header-title";const s="dark"===this.effectiveTheme?e.logoDark||e.logoLight:e.logoLight||e.logoDark;if(s){const t=document.createElement("img");t.className="sv-logo",t.src=s,t.alt=e.companyName,n.appendChild(t);const i=document.createElement("span");i.className="sv-sr-only",i.id="sv-modal-title",i.textContent=e.companyName,n.appendChild(i)}else{const t=document.createElement("h2");t.className="sv-company-name",t.id="sv-modal-title",t.textContent=e.companyName,n.appendChild(t)}return this.closeBtn=document.createElement("button"),this.closeBtn.className="sv-close-btn",this.closeBtn.setAttribute("aria-label","Close"),this.closeBtn.appendChild(A()),this.closeBtnClickHandler=()=>this.handleClose(),this.closeBtn.addEventListener("click",this.closeBtnClickHandler),t.appendChild(n),t.appendChild(this.closeBtn),t}handleClose(){this.onCloseCallback&&this.onCloseCallback()}getBody(){return this.elements?.body??null}isOpen(){return null!==this.elements}destroy(){this.keydownHandler&&(document.removeEventListener("keydown",this.keydownHandler),this.keydownHandler=null),this.elements&&this.overlayClickHandler&&(this.elements.overlay.removeEventListener("click",this.overlayClickHandler),this.overlayClickHandler=null),this.closeBtn&&this.closeBtnClickHandler&&(this.closeBtn.removeEventListener("click",this.closeBtnClickHandler),this.closeBtnClickHandler=null,this.closeBtn=null),this.elements&&(this.elements.overlay.remove(),this.elements=null),this.headerElement=null,document.body.style.overflow="",this.onCloseCallback=null}}function z(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function R(e,t){const n=document.createElement("div");return e&&(n.className=e),n}function O(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function U(e){const t=document.createElement("div");return t.className="sv-error",t.setAttribute("role","alert"),t.textContent=e,t}function V(e){const t=e.trim().toLowerCase();return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)&&t.length<=254?t:null}function j(e){const t=e.trim().startsWith("+"),n=e.replace(/\D/g,"");return 10===n.length?`+1${n}`:11===n.length&&n.startsWith("1")||t&&n.length>=10&&n.length<=15?`+${n}`:null}function q(e){const t=e.replace(/\D/g,""),n=t.startsWith("1")&&t.length>10?t.slice(1):t;return 0===n.length?"":n.length<=3?`(${n}`:n.length<=6?`(${n.slice(0,3)}) ${n.slice(3)}`:`(${n.slice(0,3)}) ${n.slice(3,6)}-${n.slice(6,10)}`}class K{constructor(e={}){this.message=e.message??"Loading..."}render(){const e=R("sv-loading"),t=document.createElement("div");t.className="sv-spinner",t.setAttribute("role","status"),t.setAttribute("aria-label","Loading");const n=document.createElement("p");return n.className="sv-loading-text",n.textContent=this.message,e.appendChild(t),e.appendChild(n),e}destroy(){}}class W{constructor(e){this.inputElement=null,this.labelElement=null,this.submitButton=null,this.errorContainer=null,this.focusTimeoutId=null,this.detectedType="email",this.props=e,this.boundHandleInput=()=>this.handleInput(),this.boundHandleKeyDown=e=>{"Enter"===e.key&&(e.preventDefault(),this.handleSubmit())},this.boundHandleSubmit=()=>this.handleSubmit()}get allowsBoth(){return this.props.allowedTypes.includes("email")&&this.props.allowedTypes.includes("phone")}get allowsEmail(){return this.props.allowedTypes.includes("email")}get allowsPhone(){return this.props.allowedTypes.includes("phone")}render(){const e=R("sv-email-view"),t=R("sv-email-header"),n=document.createElement("p");n.className="sv-email-subtitle",n.textContent=this.getSubtitleText(),t.appendChild(n),e.appendChild(t),this.errorContainer=R("sv-email-error-container"),this.props.error&&this.errorContainer.appendChild(U(this.props.error)),e.appendChild(this.errorContainer);const s=R("sv-email-form"),i=R("sv-email-input-wrapper");return this.labelElement=document.createElement("label"),this.labelElement.className="sv-email-label",this.labelElement.htmlFor="sv-identity-input",this.labelElement.textContent=this.getLabelText(),this.inputElement=document.createElement("input"),this.inputElement.type=this.allowsPhone&&!this.allowsEmail?"tel":"text",this.inputElement.id="sv-identity-input",this.inputElement.className="sv-email-input",this.inputElement.placeholder=this.getPlaceholderText(),this.inputElement.autocomplete=this.allowsBoth?"on":this.allowsEmail?"email":"tel",this.inputElement.required=!0,this.inputElement.maxLength=254,this.props.initialValue&&(this.inputElement.value=this.props.initialValue,this.detectAndUpdateType(this.props.initialValue)),this.inputElement.addEventListener("input",this.boundHandleInput),this.inputElement.addEventListener("keydown",this.boundHandleKeyDown),i.appendChild(this.labelElement),i.appendChild(this.inputElement),s.appendChild(i),this.submitButton=document.createElement("button"),this.submitButton.className="sv-btn sv-btn-email-primary",this.submitButton.textContent="Continue",this.submitButton.addEventListener("click",this.boundHandleSubmit),s.appendChild(this.submitButton),e.appendChild(s),this.focusTimeoutId=setTimeout(()=>this.inputElement?.focus(),50),e}getSubtitleText(){return this.allowsBoth?"Enter your email or phone to continue":this.allowsPhone?"Enter your phone number to continue":"Enter your email to continue"}getLabelText(){return this.allowsBoth?"phone"===this.detectedType?"Phone number":"Email address":this.allowsPhone?"Phone number":"Email address"}getPlaceholderText(){return this.allowsBoth?"you@example.com or (555) 123-4567":this.allowsPhone?"(555) 123-4567":"you@example.com"}handleInput(){if(this.clearError(),!this.inputElement)return;const e=this.inputElement.value;if(this.allowsBoth){const t="phone"===this.detectedType;if(this.detectAndUpdateType(e),"phone"===this.detectedType){const t=this.inputElement.selectionStart??e.length,n=q(e);if(n!==e){this.inputElement.value=n;const s=Math.min(t+(n.length-e.length),n.length);this.inputElement.setSelectionRange(s,s)}}t!==("phone"===this.detectedType)&&this.labelElement&&(this.labelElement.textContent=this.getLabelText())}else if(this.allowsPhone){const t=this.inputElement.selectionStart??e.length,n=q(e);if(n!==e){this.inputElement.value=n;const s=Math.min(t+(n.length-e.length),n.length);this.inputElement.setSelectionRange(s,s)}}}detectAndUpdateType(e){!function(e){const t=e.trim();return!!t&&/^[+(\d]/.test(t)&&!/[@.]/.test(t)}(e)?this.detectedType="email":this.detectedType="phone"}clearError(){this.errorContainer&&O(this.errorContainer),this.inputElement?.classList.remove("sv-email-input-error")}showError(e){this.errorContainer&&(O(this.errorContainer),this.errorContainer.appendChild(U(e))),this.inputElement?.classList.add("sv-email-input-error"),this.inputElement?.focus()}handleSubmit(){if(!this.inputElement||!this.submitButton)return;const e=this.inputElement.value;if(this.allowsBoth)if("phone"===this.detectedType){const t=j(e);if(!t)return void this.showError("Please enter a valid phone number");this.submitButton.disabled=!0,this.submitButton.textContent="Please wait...",this.props.onSubmit({value:t,type:"phone"})}else{const t=V(e);if(!t)return void this.showError("Please enter a valid email address");this.submitButton.disabled=!0,this.submitButton.textContent="Please wait...",this.props.onSubmit({value:t,type:"email"})}else if(this.allowsPhone){const t=j(e);if(!t)return void this.showError("Please enter a valid phone number");this.submitButton.disabled=!0,this.submitButton.textContent="Please wait...",this.props.onSubmit({value:t,type:"phone"})}else{const t=V(e);if(!t)return void this.showError("Please enter a valid email address");this.submitButton.disabled=!0,this.submitButton.textContent="Please wait...",this.props.onSubmit({value:t,type:"email"})}}destroy(){null!==this.focusTimeoutId&&(clearTimeout(this.focusTimeoutId),this.focusTimeoutId=null),this.inputElement&&(this.inputElement.removeEventListener("input",this.boundHandleInput),this.inputElement.removeEventListener("keydown",this.boundHandleKeyDown)),this.submitButton&&this.submitButton.removeEventListener("click",this.boundHandleSubmit)}}class G{constructor(e){this.backLink=null,this.methodButtons=[],this.methodClickHandlers=new Map,this.props=e,this.boundHandleBack=()=>this.props.onBack()}render(){const e=R();this.backLink=this.createBackLink();const t=document.createElement("h3");t.className="sv-title",t.textContent="Choose how to sign in";const n=document.createElement("p");n.className="sv-subtitle",n.textContent=z(this.props.email);const s=R();for(const e of this.props.methods){const t=this.createMethodButton(e);this.methodButtons.push(t),s.appendChild(t)}return e.appendChild(this.backLink),e.appendChild(t),e.appendChild(n),e.appendChild(s),e}createBackLink(){const e=document.createElement("button");return e.className="sv-back-link",e.appendChild(H()),e.appendChild(document.createTextNode(" Change email")),e.addEventListener("click",this.boundHandleBack),e}createMethodButton(e){const t=document.createElement("button");t.className="sv-btn sv-btn-method";const n=R("sv-method-content"),s=this.getMethodIcon(e);n.appendChild(s);const i=R("sv-method-text"),o=document.createElement("div");o.className="sv-method-name",o.textContent=e.name;const r=document.createElement("div");r.className="sv-method-description",r.textContent=e.description,i.appendChild(o),i.appendChild(r),n.appendChild(i),t.appendChild(n);const a=()=>this.props.onSelectMethod(e);return this.methodClickHandlers.set(t,a),t.addEventListener("click",a),t}getMethodIcon(e){const t=R("sv-method-icon");return t.appendChild(N(e.icon)),t}destroy(){this.backLink&&this.backLink.removeEventListener("click",this.boundHandleBack);for(const e of this.methodButtons){const t=this.methodClickHandlers.get(e);t&&e.removeEventListener("click",t)}this.methodClickHandlers.clear(),this.methodButtons=[]}}class Y{constructor(e){this.inputElements=[],this.submitButton=null,this.resendButton=null,this.backLink=null,this.errorContainer=null,this.resendTimer=null,this.backoffTimer=null,this.backoffSeconds=0,this.focusTimeoutId=null,this.autoSubmitTimeoutId=null,this.isSubmitting=!1,this.inputHandlers=new Map,this.props=e,this.boundHandleBack=()=>this.props.onBack(),this.boundHandleResend=()=>this.handleResend(),this.boundHandleSubmit=()=>this.handleSubmit()}render(){const e=R();this.backLink=this.createBackLink();const t=document.createElement("h3");t.className="sv-title",t.textContent="Enter verification code";const n="email"===this.props.method?"email":"phone",s=document.createElement("p");s.className="sv-subtitle",s.innerHTML=`We sent a 6-digit code to your ${n}<br><strong>${z(this.props.email)}</strong>`,this.errorContainer=R(),this.props.error&&this.errorContainer.appendChild(U(this.props.error));const i=this.createInputGroup();if(this.submitButton=document.createElement("button"),this.submitButton.className="sv-btn sv-btn-primary",this.submitButton.textContent="Verify",this.submitButton.addEventListener("click",this.boundHandleSubmit),this.props.backoffExpires){Math.max(0,Math.ceil(this.props.backoffExpires-Date.now()/1e3))>0&&this.startBackoff(this.props.backoffExpires)}const o=R("sv-resend-container");return this.resendButton=document.createElement("button"),this.resendButton.className="sv-back-link",this.resendButton.textContent="Resend code",this.resendButton.addEventListener("click",this.boundHandleResend),o.appendChild(this.resendButton),e.style.display="flex",e.style.flexDirection="column",e.appendChild(t),e.appendChild(s),e.appendChild(this.errorContainer),e.appendChild(i),e.appendChild(this.submitButton),e.appendChild(o),e.appendChild(this.backLink),this.focusTimeoutId=setTimeout(()=>this.inputElements[0]?.focus(),50),e}createBackLink(){const e=document.createElement("button");return e.className="sv-back-link",e.style.order="-1",e.appendChild(H()),e.appendChild(document.createTextNode(" Choose another method")),e.addEventListener("click",this.boundHandleBack),e}createInputGroup(){const e=R("sv-totp-input-group");for(let t=0;t<6;t++){const n=document.createElement("input");n.type="text",n.inputMode="numeric",n.pattern="[0-9]*",n.maxLength=1,n.className="sv-totp-digit",n.autocomplete="one-time-code",n.setAttribute("aria-label",`Digit ${t+1} of 6`);const s={input:e=>this.handleInput(e,t),keydown:e=>this.handleKeyDown(e,t),paste:e=>this.handlePaste(e),focus:()=>n.select()};this.inputHandlers.set(n,s),n.addEventListener("input",s.input),n.addEventListener("keydown",s.keydown),n.addEventListener("paste",s.paste),n.addEventListener("focus",s.focus),this.inputElements.push(n),e.appendChild(n)}return e}handleInput(e,t){const n=e.target,s=n.value.replace(/\D/g,"");s.length>0?(n.value=s[0],this.clearError(),t<5?this.inputElements[t+1].focus():this.checkAutoSubmit()):n.value=""}handleKeyDown(e,t){const n=e.target;"Backspace"===e.key?""===n.value&&t>0&&(this.inputElements[t-1].focus(),this.inputElements[t-1].value=""):"ArrowLeft"===e.key&&t>0?this.inputElements[t-1].focus():"ArrowRight"===e.key&&t<5?this.inputElements[t+1].focus():"Enter"===e.key&&this.handleSubmit()}handlePaste(e){e.preventDefault();const t=(e.clipboardData?.getData("text")??"").replace(/\D/g,"").slice(0,6);for(let e=0;e<t.length;e++)this.inputElements[e]&&(this.inputElements[e].value=t[e]);if(t.length>0){this.clearError();const e=Math.min(t.length,5);this.inputElements[e].focus()}6===t.length&&this.checkAutoSubmit()}checkAutoSubmit(){6===this.getCode().length&&(this.autoSubmitTimeoutId=setTimeout(()=>this.handleSubmit(),100))}getCode(){return this.inputElements.map(e=>e.value).join("")}clearError(){this.errorContainer&&O(this.errorContainer),this.inputElements.forEach(e=>e.classList.remove("sv-input-error"))}showError(e){this.errorContainer&&(O(this.errorContainer),this.errorContainer.appendChild(U(e))),this.inputElements.forEach(e=>e.classList.add("sv-input-error")),this.inputElements[0]?.focus()}handleSubmit(){if(!this.submitButton)return;if(this.isSubmitting)return;if(this.backoffSeconds>0)return;const e=function(e){const t=e.replace(/\D/g,"");return 6===t.length?t:null}(this.getCode());e?(null!==this.autoSubmitTimeoutId&&(clearTimeout(this.autoSubmitTimeoutId),this.autoSubmitTimeoutId=null),this.isSubmitting=!0,this.submitButton.disabled=!0,this.submitButton.textContent="Verifying...",this.props.onSubmit(e)):this.showError("Please enter the complete 6-digit code")}startBackoff(e){this.disableInputs(!0),this.backoffTimer=new v({expiresAt:e,onTick:e=>{this.backoffSeconds=e,this.updateSubmitButton()},onExpired:()=>{this.backoffSeconds=0,this.updateSubmitButton(),this.disableInputs(!1),this.inputElements[0]?.focus()}}),this.backoffTimer.start()}updateSubmitButton(){this.submitButton&&(this.backoffSeconds>0?(this.submitButton.textContent=`Wait ${this.backoffSeconds}s`,this.submitButton.disabled=!0):(this.submitButton.textContent="Verify",this.submitButton.disabled=!1))}disableInputs(e){this.inputElements.forEach(t=>{t.disabled=e})}handleResend(){!this.resendTimer?.isRunning()&&this.resendButton&&(this.resendTimer=new m({duration:30,onTick:e=>{this.resendButton&&(this.resendButton.textContent=`Resend code (${e}s)`,this.resendButton.disabled=!0)},onComplete:()=>{this.resendButton&&(this.resendButton.textContent="Resend code",this.resendButton.disabled=!1)}}),this.resendTimer.start(),this.props.onResend())}destroy(){null!==this.focusTimeoutId&&(clearTimeout(this.focusTimeoutId),this.focusTimeoutId=null),null!==this.autoSubmitTimeoutId&&(clearTimeout(this.autoSubmitTimeoutId),this.autoSubmitTimeoutId=null),this.resendTimer?.stop(),this.resendTimer=null,this.backoffTimer?.stop(),this.backoffTimer=null;for(const e of this.inputElements){const t=this.inputHandlers.get(e);t&&(e.removeEventListener("input",t.input),e.removeEventListener("keydown",t.keydown),e.removeEventListener("paste",t.paste),e.removeEventListener("focus",t.focus))}this.inputHandlers.clear(),this.inputElements=[],this.submitButton&&this.submitButton.removeEventListener("click",this.boundHandleSubmit),this.resendButton&&this.resendButton.removeEventListener("click",this.boundHandleResend),this.backLink&&this.backLink.removeEventListener("click",this.boundHandleBack)}}class J{constructor(e){this.actionButton=null,this.errorContainer=null,this.backLink=null,this.fallbackLink=null,this.props=e,this.boundHandleBack=()=>this.props.onBack(),this.boundHandleStart=()=>this.handleStart(),this.boundHandleFallback=e.onFallback?()=>e.onFallback():null}render(){const e=R();this.backLink=this.createBackLink();const t=R();t.appendChild(P());const n=document.createElement("h3");n.className="sv-title",n.textContent="register"===this.props.mode?"Set up a passkey":"Use your passkey";const s=document.createElement("p");s.className="sv-subtitle","register"===this.props.mode?s.innerHTML=`Create a passkey for<br><strong>${z(this.props.email)}</strong><br>to sign in faster next time`:s.innerHTML=`Use your passkey to verify<br><strong>${z(this.props.email)}</strong>`,this.errorContainer=R(),this.props.error&&this.errorContainer.appendChild(U(this.props.error));const i=document.createElement("p");i.className="sv-passkey-description",i.textContent="register"===this.props.mode?"Use Face ID, Touch ID, Windows Hello, or a security key.":"Your device will prompt you to verify your identity.";const o="register"===this.props.mode?"Create Passkey":"Continue with Passkey";return this.actionButton=document.createElement("button"),this.actionButton.className="sv-btn sv-btn-primary",this.actionButton.textContent=o,this.actionButton.addEventListener("click",this.boundHandleStart),e.appendChild(this.backLink),e.appendChild(t),e.appendChild(n),e.appendChild(s),e.appendChild(this.errorContainer),e.appendChild(i),e.appendChild(this.actionButton),this.props.onFallback&&this.boundHandleFallback&&(this.fallbackLink=this.createFallbackLink(),e.appendChild(this.fallbackLink)),e}createFallbackLink(){const e=document.createElement("button");return e.className="sv-fallback-link",e.textContent="Having trouble? Use email code instead",this.boundHandleFallback&&e.addEventListener("click",this.boundHandleFallback),e}createBackLink(){const e=document.createElement("button");return e.className="sv-back-link",e.appendChild(H()),e.appendChild(document.createTextNode(" Choose another method")),e.addEventListener("click",this.boundHandleBack),e}handleStart(){this.actionButton&&(this.actionButton.disabled=!0,this.actionButton.textContent="Waiting for device...",this.props.onStart())}showError(e){this.errorContainer&&(O(this.errorContainer),this.errorContainer.appendChild(U(e))),this.actionButton&&(this.actionButton.disabled=!1,this.actionButton.textContent="Try Again")}destroy(){this.backLink&&this.backLink.removeEventListener("click",this.boundHandleBack),this.actionButton&&this.actionButton.removeEventListener("click",this.boundHandleStart),this.fallbackLink&&this.boundHandleFallback&&this.fallbackLink.removeEventListener("click",this.boundHandleFallback)}}class Q{constructor(e){this.addButton=null,this.skipLink=null,this.props=e,this.boundHandleAdd=()=>this.props.onAddPasskey(),this.boundHandleSkip=()=>this.props.onSkip()}render(){const e=R(),t=R();t.appendChild(P());const n=document.createElement("h3");n.className="sv-title",n.textContent="Add a passkey for faster sign-in?";const s=document.createElement("p");s.className="sv-subtitle",s.innerHTML=`Next time, sign in instantly as<br><strong>${z(this.props.email)}</strong>`;const i=R();this.props.error&&i.appendChild(U(this.props.error));const o=document.createElement("div");o.className="sv-passkey-prompt-benefits";const r=document.createElement("ul"),a=["No codes to type or wait for","Phishing-resistant security","Works with Face ID, Touch ID, or Windows Hello"];for(const e of a){const t=document.createElement("li");t.appendChild(I()),t.appendChild(document.createTextNode(e)),r.appendChild(t)}return o.appendChild(r),this.addButton=document.createElement("button"),this.addButton.className="sv-btn sv-btn-primary",this.addButton.textContent=this.props.error?"Try Again":"Add Passkey",this.addButton.addEventListener("click",this.boundHandleAdd),this.skipLink=document.createElement("button"),this.skipLink.className="sv-skip-link",this.skipLink.textContent=this.props.error?"Skip for now":"Not now",this.skipLink.addEventListener("click",this.boundHandleSkip),e.appendChild(t),e.appendChild(n),e.appendChild(s),e.appendChild(i),e.appendChild(o),e.appendChild(this.addButton),e.appendChild(this.skipLink),e}destroy(){this.addButton&&this.addButton.removeEventListener("click",this.boundHandleAdd),this.skipLink&&this.skipLink.removeEventListener("click",this.boundHandleSkip)}}class Z{constructor(e){this.expirationTimer=null,this.resendTimer=null,this.countdownElement=null,this.countdownSection=null,this.waitingSection=null,this.resendButton=null,this.backLink=null,this.fallbackButton=null,this.props=e,this.boundHandleBack=()=>this.props.onBack(),this.boundHandleResend=()=>this.handleResend(),this.boundHandleFallback=()=>this.props.onFallback()}render(){const e=R();this.backLink=this.createBackLink();const t=document.createElement("h3");t.className="sv-title",t.textContent="Check your email";const n=document.createElement("p");n.className="sv-subtitle",n.innerHTML=`We sent a secure link to<br><strong>${z(this.props.email)}</strong>`,this.waitingSection=R("sv-sparklink-waiting");const s=document.createElement("div");s.className="sv-spinner sv-spinner-small",s.setAttribute("role","status"),s.setAttribute("aria-label","Waiting for verification");const i=document.createElement("span");i.className="sv-sparklink-waiting-text",i.textContent="Waiting for you to click the link...",this.waitingSection.appendChild(s),this.waitingSection.appendChild(i),this.countdownSection=R("sv-sparklink-countdown");const o=document.createElement("span");o.textContent="Link expires in ",this.countdownElement=document.createElement("span"),this.countdownElement.className="sv-sparklink-countdown-time",this.startExpirationTimer(),this.countdownSection.appendChild(o),this.countdownSection.appendChild(this.countdownElement);const r=R("sv-resend-container");this.resendButton=document.createElement("button"),this.resendButton.className="sv-btn sv-btn-secondary",this.resendButton.textContent="Resend link",this.resendButton.addEventListener("click",this.boundHandleResend),r.appendChild(this.resendButton);const a=R("sv-sparklink-fallback");return this.fallbackButton=document.createElement("button"),this.fallbackButton.className="sv-back-link",this.fallbackButton.textContent="Use verification code instead",this.fallbackButton.addEventListener("click",this.boundHandleFallback),a.appendChild(this.fallbackButton),e.appendChild(this.backLink),e.appendChild(t),e.appendChild(n),e.appendChild(this.waitingSection),e.appendChild(this.countdownSection),e.appendChild(r),e.appendChild(a),e}createBackLink(){const e=document.createElement("button");return e.className="sv-back-link",e.appendChild(H()),e.appendChild(document.createTextNode(" Choose another method")),e.addEventListener("click",this.boundHandleBack),e}formatTime(e){if(!Number.isFinite(e)||e<0)return"--:--";return`${Math.floor(e/60)}:${Math.floor(e%60).toString().padStart(2,"0")}`}startExpirationTimer(){if(!Number.isFinite(this.props.expiresAt)||this.props.expiresAt<=0)return this.countdownElement&&(this.countdownElement.textContent="--:--"),void console.warn("[SparkVault] Invalid expiresAt timestamp:",this.props.expiresAt);const e=Math.floor(this.props.expiresAt/1e3);this.expirationTimer=new v({expiresAt:e,onTick:e=>{this.countdownElement&&(this.countdownElement.textContent=this.formatTime(e))},onExpired:()=>{this.showExpiredState()}}),this.expirationTimer.start()}showExpiredState(){if(this.props.onExpired(),this.waitingSection){this.waitingSection.innerHTML="",this.waitingSection.classList.add("sv-sparklink-expired");const e=R("sv-sparklink-expired-icon");e.appendChild(function(){const e=T("0 0 48 48",48,48);e.classList.add("sv-expired-icon"),e.appendChild(M(24,24,22,{fill:"#F5F5F5",stroke:"#E5E5E5","stroke-width":"1"}));const t=document.createElementNS("http://www.w3.org/2000/svg","g");return t.setAttribute("transform","translate(10, 12)"),t.appendChild(L("M14 4L2 24h24L14 4z",{fill:"#737373"})),t.appendChild(L("M14 10v5",{stroke:"#FFFFFF","stroke-width":"2","stroke-linecap":"round"})),t.appendChild(M(14,19,1,{fill:"#FFFFFF"})),e.appendChild(t),e}()),this.waitingSection.appendChild(e);const t=document.createElement("span");t.className="sv-sparklink-expired-text",t.textContent="This link has expired",this.waitingSection.appendChild(t)}this.countdownSection&&(this.countdownSection.style.display="none")}handleResend(){!this.resendTimer?.isRunning()&&this.resendButton&&(this.resendTimer=new m({duration:30,onTick:e=>{this.resendButton&&(this.resendButton.textContent=`Resend link (${e}s)`,this.resendButton.disabled=!0)},onComplete:()=>{this.resendButton&&(this.resendButton.textContent="Resend link",this.resendButton.disabled=!1)}}),this.resendTimer.start(),this.props.onResend())}destroy(){this.expirationTimer?.stop(),this.expirationTimer=null,this.resendTimer?.stop(),this.resendTimer=null,this.backLink&&this.backLink.removeEventListener("click",this.boundHandleBack),this.resendButton&&this.resendButton.removeEventListener("click",this.boundHandleResend),this.fallbackButton&&this.fallbackButton.removeEventListener("click",this.boundHandleFallback)}}class X{constructor(e){this.retryButton=null,this.closeButton=null,this.props=e,this.boundHandleRetry=()=>this.props.onRetry(),this.boundHandleClose=()=>this.props.onClose()}render(){const e=R();e.className="sv-error-view";const t=R();t.className="sv-error-icon-container",t.appendChild(function(){const e=T("0 0 48 48",48,48);e.classList.add("sv-error-icon"),e.appendChild(M(24,24,22,{fill:"#FEF2F2",stroke:"#FECACA","stroke-width":"1"}));const t=document.createElementNS("http://www.w3.org/2000/svg","g");return t.setAttribute("transform","translate(10, 12)"),t.appendChild(L("M14 4L2 24h24L14 4z",{fill:"#DC2626"})),t.appendChild(L("M14 10v5",{stroke:"#FFFFFF","stroke-width":"2","stroke-linecap":"round"})),t.appendChild(M(14,19,1,{fill:"#FFFFFF"})),e.appendChild(t),e}());const n=R();n.className="sv-error-content";const s=document.createElement("h3");s.className="sv-error-title",s.textContent="Something went wrong";const i=document.createElement("p");i.className="sv-error-message",i.textContent=this.props.message;const o=document.createElement("div");o.className="sv-error-code-badge";const r=document.createElement("span");r.className="sv-error-code-label",r.textContent="Error code";const a=document.createElement("code");a.className="sv-error-code-value",a.textContent=this.props.code,o.appendChild(r),o.appendChild(a),n.appendChild(s),n.appendChild(i),n.appendChild(o);const l=R();return l.className="sv-error-actions",this.retryButton=document.createElement("button"),this.retryButton.className="sv-btn sv-btn-error-primary",this.retryButton.textContent="Try Again",this.retryButton.addEventListener("click",this.boundHandleRetry),this.closeButton=document.createElement("button"),this.closeButton.className="sv-btn sv-btn-error-secondary",this.closeButton.textContent="Close",this.closeButton.addEventListener("click",this.boundHandleClose),l.appendChild(this.retryButton),l.appendChild(this.closeButton),e.appendChild(t),e.appendChild(n),e.appendChild(l),e}destroy(){this.retryButton&&this.retryButton.removeEventListener("click",this.boundHandleRetry),this.closeButton&&this.closeButton.removeEventListener("click",this.boundHandleClose)}}class ee{constructor(e,t,n,s){this.currentView=null,this.focusTimeoutId=null,this.pollingInterval=null,this.messageListener=null,this.container=e,this.api=t,this.options=n,this.callbacks=s,this.verificationState=new x,this.passkeyHandler=new w(t,this.verificationState),this.totpHandler=new C(t,this.verificationState),this.sparkLinkHandler=new E(t,this.verificationState),n.email?this.verificationState.setIdentity(n.email,"email"):n.phone&&this.verificationState.setIdentity(n.phone,"phone"),n.authContext&&this.verificationState.setAuthContext(n.authContext)}get recipient(){return this.verificationState.recipient}get identityType(){return this.verificationState.identityType}get sdkConfig(){return this.verificationState.sdkConfig}async start(){let e=null;if(this.api.isConfigPreloaded()){const t=new Promise(e=>setTimeout(()=>e(null),50)),n=await Promise.race([this.api.getConfig().then(e=>e),t]);null!==n&&(e=n)}this.container.createLoading({backdropBlur:this.options.backdropBlur},()=>this.handleClose());try{e||(this.setState({view:"loading"}),e=await this.api.getConfig()),this.verificationState.setConfig(e),e.branding&&this.container.updateBranding(e.branding);const t=void 0!==this.options.backdropBlur?this.options.backdropBlur:void 0===e.branding?.backdrop_blur||e.branding.backdrop_blur;this.container.updateBackdropBlur(t),this.recipient?this.showMethodSelect():this.showIdentityInput()}catch(e){this.handleErrorWithRecovery(e,"config_error")}}close(){null!==this.focusTimeoutId&&(clearTimeout(this.focusTimeoutId),this.focusTimeoutId=null),this.stopSparkLinkPolling(),this.destroyCurrentView(),this.container.destroy()}handleClose(){this.close(),this.callbacks.onCancel()}get viewState(){return this.verificationState.viewState}setState(e){this.verificationState.setViewState(e),this.render()}render(){const e=this.container.getBody();if(!e)return;this.destroyCurrentView();const t=this.createViewForState(this.viewState);this.currentView=t,O(e),e.appendChild(t.render());const n=e.querySelector("input:not([disabled]), button.sv-btn-primary:not([disabled]), button.sv-btn-email-primary:not([disabled]), button.sv-btn-method:not([disabled])");n&&(null!==this.focusTimeoutId&&clearTimeout(this.focusTimeoutId),this.focusTimeoutId=setTimeout(()=>n.focus(),50))}destroyCurrentView(){this.currentView?.destroy&&this.currentView.destroy(),this.currentView=null}createViewForState(e){switch(e.view){case"loading":return new K({message:"Loading..."});case"identity-input":return new W({allowedTypes:this.getAllowedIdentityTypes(),initialValue:this.recipient,error:e.error,onSubmit:e=>this.handleIdentitySubmit(e)});case"method-select":return new G({email:e.email,methods:e.methods,onSelectMethod:e=>this.handleMethodSelect(e),onBack:()=>this.showIdentityInput()});case"totp-verify":return new Y({email:e.email,method:e.method,error:e.error,backoffExpires:e.backoffExpires,onSubmit:e=>this.handleTotpSubmit(e),onResend:()=>this.handleTotpResend(),onBack:()=>this.showMethodSelect()});case"passkey":{const{passkey:t}=this.verificationState,n="register"===e.mode&&null!==t.pendingResult;return new J({email:e.email,mode:e.mode,error:e.error,onStart:()=>this.handlePasskeyStart(),onBack:n?()=>this.handlePasskeyRegistrationSkip():"verify"===e.mode?()=>this.handlePasskeyFallback():()=>this.showMethodSelect(),onFallback:t.hasPasskey&&!t.isFallbackMode?()=>this.handlePasskeyFallback():void 0})}case"passkey-prompt":return new Q({email:e.email,onAddPasskey:()=>this.handlePasskeyPromptAdd(e.pendingResult),onSkip:()=>this.handlePasskeyPromptSkip(e.pendingResult),error:e.error});case"sparklink-waiting":return new Z({email:e.email,expiresAt:e.expiresAt,onResend:()=>this.handleSparkLinkResend(),onFallback:()=>this.handleSparkLinkFallback(),onBack:()=>this.showMethodSelect(),onExpired:()=>this.stopSparkLinkPolling()});case"oauth-pending":return new K({message:`Connecting to ${e.provider}...`});case"error":return new X({message:e.message,code:e.code,onRetry:()=>this.showIdentityInput(),onClose:()=>this.handleClose()})}}showIdentityInput(e){this.setState({view:"identity-input",error:e})}async showMethodSelect(){if(!this.sdkConfig)return;const e=this.sdkConfig.methods??[];if(!Array.isArray(e))return void this.setState({view:"error",message:"Invalid configuration",code:"invalid_config"});let t=y(e);if(t=t.filter(e=>"phone"===this.identityType?"totp"===e.type&&"totp_sms"===e.id:"email"===e.identityType||void 0===e.identityType),0===t.length)return void this.setState({view:"error",message:"No authentication methods available",code:"no_methods"});const n=t.some(e=>"passkey"===e.type),{passkey:s}=this.verificationState;if(n&&"email"===this.identityType&&!s.isFallbackMode){this.setState({view:"loading"});const e=await this.passkeyHandler.checkPasskey();if(null!==e&&!e.emailValid)return void this.showIdentityInput("This email domain cannot receive messages. Please check your email address.");if(!0===e?.hasPasskey)return void this.setState({view:"passkey",email:this.recipient,mode:"verify"});t=t.filter(e=>"passkey"!==e.type)}0!==t.length?1!==t.length?this.setState({view:"method-select",email:this.recipient,methods:t}):this.handleMethodSelect(t[0]):this.setState({view:"error",message:"No authentication methods available",code:"no_methods"})}handleIdentitySubmit(e){this.verificationState.setIdentity(e.value,e.type),this.showMethodSelect()}getAllowedIdentityTypes(){return this.verificationState.getAllowedIdentityTypes()}async handleMethodSelect(e){try{switch(e.type){case"totp":{const t="totp_sms"===e.id?"sms":"email";this.setState({view:"loading"});const n=await this.totpHandler.send(t);if(!n.success)return void this.setState({view:"error",message:n.error??"Failed to send code",code:"totp_send_failed"});this.setState({view:"totp-verify",email:this.recipient,method:t,kindling:this.verificationState.totp.kindling});break}case"passkey":this.setState({view:"passkey",email:this.recipient,mode:"verify"});break;case"sparklink":{this.setState({view:"loading"});const e=await this.sparkLinkHandler.send();if(!e.success)return void this.setState({view:"error",message:e.error??"Failed to send SparkLink",code:"sparklink_send_failed"});this.setState({view:"sparklink-waiting",email:this.recipient,sparkId:e.sparkId,expiresAt:e.expiresAt}),this.startSparkLinkPolling(e.sparkId);break}case"social":e.provider&&this.handleSocialLogin(e.provider);break;default:this.setState({view:"error",message:`Authentication method not supported: ${e.type}`,code:"unsupported_method"})}}catch(e){this.handleErrorWithRecovery(e,"unknown")}}async handleTotpSubmit(e){if("totp-verify"!==this.viewState.view)return;const t=this.verificationState.totp.method??"email",n=await this.totpHandler.verify(e);if(n.success&&n.result){if(n.result.redirect)return this.close(),void(window.location.href=n.result.redirect);if(await this.shouldShowPasskeyPrompt())return void this.setState({view:"passkey-prompt",email:this.recipient,pendingResult:n.result});this.close(),this.callbacks.onSuccess(n.result)}else{if((n.error?.toLowerCase()??"").includes("expired")){const e=await this.totpHandler.resend();return e.success?void this.setState({view:"totp-verify",email:this.recipient,method:t,kindling:this.verificationState.totp.kindling,error:"Code expired. We've sent a new one."}):void this.setState({view:"totp-verify",email:this.recipient,method:t,kindling:this.verificationState.totp.kindling,error:e.error??"Code expired and failed to resend."})}this.setState({view:"totp-verify",email:this.recipient,method:t,kindling:this.verificationState.totp.kindling,error:n.error??"Invalid code. Please try again.",backoffExpires:n.backoffExpires})}}async handleTotpResend(){if("totp-verify"!==this.viewState.view)return;const e=this.verificationState.totp.method??"email",t=await this.totpHandler.resend();t.success||this.setState({view:"totp-verify",email:this.recipient,method:e,kindling:this.verificationState.totp.kindling,error:t.error??"Failed to resend code"})}async handlePasskeyStart(){if("passkey"!==this.viewState.view)return;const e=this.viewState.mode,t="register"===e?await this.passkeyHandler.register():await this.passkeyHandler.verify();if(t.success&&t.result){if(t.result.redirect)return this.close(),void(window.location.href=t.result.redirect);this.close(),this.callbacks.onSuccess(t.result)}else"not_found"===t.errorType?this.setState({view:"passkey",email:this.recipient,mode:"register",error:t.error}):this.setState({view:"passkey",email:this.recipient,mode:e,error:t.error})}async handlePasskeyFallback(){this.verificationState.enablePasskeyFallback(),await this.showMethodSelect()}handlePasskeyRegistrationSkip(){const e=this.verificationState.passkey.pendingResult;if(e){if(this.verificationState.setPendingResult(null),e.redirect)return this.close(),void(window.location.href=e.redirect);this.close(),this.callbacks.onSuccess(e)}}handleSocialLogin(e){this.setState({view:"oauth-pending",provider:e});const t=function(){const e=new Uint8Array(16);return crypto.getRandomValues(e),Array.from(e,e=>e.toString(16).padStart(2,"0")).join("")}(),n=window.location.origin+window.location.pathname;sessionStorage.setItem("sv_oauth_state",t),sessionStorage.setItem("sv_oauth_email",this.recipient);const s=this.api.getSocialAuthUrl(e,n,t);window.location.href=s}normalizeError(e){return e instanceof f?new t(e.message,e.code):e instanceof Error?e:new Error(String(e))}isSessionExpiredError(e){const n=e.message.toLowerCase(),s=e instanceof t?e.code:"";return n.includes("expired")||n.includes("session")||n.includes("invalid token")||n.includes("token invalid")||"token_expired"===s||"session_expired"===s||"kindling_expired"===s}handleErrorWithRecovery(e,n){const s=this.normalizeError(e);this.isSessionExpiredError(s)?this.showIdentityInput("Your session has expired. Please try again."):this.setState({view:"error",message:s.message,code:s instanceof t?s.code:n})}async shouldShowPasskeyPrompt(){if(!this.verificationState.shouldShowPasskeyPrompt())return!1;const{passkey:e}=this.verificationState;if(null===e.hasPasskey){const e=await this.passkeyHandler.checkPasskey();return null!==e&&e.emailValid&&!e.hasPasskey}return!e.hasPasskey}async handlePasskeyPromptAdd(e){this.verificationState.setPendingResult(e),this.setState({view:"loading"});const t=await this.passkeyHandler.register();if(t.success&&t.result){if(t.result.redirect)return this.close(),void(window.location.href=t.result.redirect);this.close(),this.callbacks.onSuccess(t.result)}else if("cancelled"===t.errorType)this.setState({view:"passkey-prompt",email:this.recipient,pendingResult:e});else{const n=t.error||"Failed to create passkey. Please try again.";this.setState({view:"passkey-prompt",email:this.recipient,pendingResult:e,error:n})}}handlePasskeyPromptSkip(e){if(this.verificationState.dismissPasskeyPrompt(),e.redirect)return this.close(),void(window.location.href=e.redirect);this.close(),this.callbacks.onSuccess(e)}startSparkLinkPolling(e){this.stopSparkLinkPolling(),this.messageListener=e=>{if(!e.data||"object"!=typeof e.data)return;if("sparklink_verified"!==e.data.type)return;const{token:t,identity:n,identityType:s,redirect:i}=e.data;t&&n&&this.handleSparkLinkVerified({token:t,identity:n,identityType:s||"email",redirect:i})},window.addEventListener("message",this.messageListener),this.pollingInterval=setInterval(async()=>{const t=await this.sparkLinkHandler.checkStatus(e);t.verified&&t.token&&t.identity&&this.handleSparkLinkVerified({token:t.token,identity:t.identity,identityType:t.identityType||"email",redirect:t.redirect})},2e3)}async handleSparkLinkVerified(e){if(this.stopSparkLinkPolling(),e.redirect)return this.close(),void(window.location.href=e.redirect);await this.shouldShowPasskeyPrompt()?this.setState({view:"passkey-prompt",email:this.recipient,pendingResult:e}):(this.close(),this.callbacks.onSuccess(e))}stopSparkLinkPolling(){this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=null),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null)}async handleSparkLinkResend(){const e=await this.sparkLinkHandler.send();e.success&&e.sparkId&&(this.stopSparkLinkPolling(),this.setState({view:"sparklink-waiting",email:this.recipient,sparkId:e.sparkId,expiresAt:e.expiresAt}),this.startSparkLinkPolling(e.sparkId))}async handleSparkLinkFallback(){this.stopSparkLinkPolling(),this.setState({view:"loading"});const e=await this.totpHandler.send("email");e.success?this.setState({view:"totp-verify",email:this.recipient,method:"email",kindling:this.verificationState.totp.kindling}):this.setState({view:"error",message:e.error??"Failed to send code",code:"totp_send_failed"})}}const te={showHeader:!0,showCloseButton:!0,showFooter:!0};class ne{constructor(e,t={}){this.container=null,this.header=null,this.body=null,this.footer=null,this.closeBtn=null,this.onCloseCallback=null,this.closeBtnClickHandler=null,this.effectiveTheme="light",this.targetElement=e,this.containerOptions={...te,...t}}createLoading(e,t){if(this.container)return;this.onCloseCallback=t;if(B({branding:{companyName:"",logoLight:null,logoDark:null},backdropBlur:!1}),this.container=document.createElement("div"),this.container.className="sv-inline-container",this.containerOptions.showHeader&&(this.header=this.createHeader({companyName:"",logoLight:null,logoDark:null}),this.container.appendChild(this.header)),this.body=document.createElement("div"),this.body.className="sv-body sv-inline-body",this.container.appendChild(this.body),this.containerOptions.showFooter){this.footer=document.createElement("div"),this.footer.className="sv-footer";const e=document.createElement("span");e.className="sv-footer-text",e.appendChild(document.createTextNode("Secured by "));const t=document.createElement("a");t.href="https://sparkvault.com",t.target="_blank",t.rel="noopener noreferrer",t.className="sv-footer-link",t.style.color="inherit",t.style.textDecoration="none",t.textContent="SparkVault",e.appendChild(t),this.footer.appendChild(e),this.container.appendChild(this.footer)}this.targetElement.innerHTML="",this.targetElement.appendChild(this.container)}updateBranding(e){if(this.container&&(this.effectiveTheme=$(e),this.containerOptions.showHeader&&this.header)){const t=this.createHeader(e);this.container.replaceChild(t,this.header),this.header=t}}updateBackdropBlur(e){}getBody(){return this.body}isOpen(){return null!==this.container}destroy(){this.closeBtn&&this.closeBtnClickHandler&&(this.closeBtn.removeEventListener("click",this.closeBtnClickHandler),this.closeBtnClickHandler=null,this.closeBtn=null),this.container&&this.container.parentNode&&this.container.remove(),this.container=null,this.header=null,this.body=null,this.footer=null,this.onCloseCallback=null}createHeader(e){const t=document.createElement("div");t.className="sv-header sv-inline-header";const n=document.createElement("div");n.className="sv-header-title";const s="dark"===this.effectiveTheme?e.logoDark||e.logoLight:e.logoLight||e.logoDark;if(s){const t=document.createElement("img");t.className="sv-logo",t.src=s,t.alt=e.companyName,n.appendChild(t);const i=document.createElement("span");i.className="sv-sr-only",i.textContent=e.companyName,n.appendChild(i)}else if(e.companyName){const t=document.createElement("h2");t.className="sv-company-name",t.textContent=e.companyName,n.appendChild(t)}return t.appendChild(n),this.containerOptions.showCloseButton&&(this.closeBtn=document.createElement("button"),this.closeBtn.className="sv-close-btn",this.closeBtn.setAttribute("aria-label","Close"),this.closeBtn.appendChild(A()),this.closeBtnClickHandler=()=>this.handleClose(),this.closeBtn.addEventListener("click",this.closeBtnClickHandler),t.appendChild(this.closeBtn)),t}handleClose(){this.onCloseCallback&&this.onCloseCallback()}}function se(e){"loading"===document.readyState?document.addEventListener("DOMContentLoaded",e,{once:!0}):e()}class ie{constructor(e){this.renderer=null,this.attachedElements=new Map,this.config=e,this.api=new b(e),e.preloadConfig&&this.api.preloadConfig()}async verify(e={}){this.renderer&&this.renderer.close();const t=!!e.target?this.createInlineContainer(e.target):new D,n={...e,backdropBlur:e.backdropBlur??this.config.backdropBlur};return new Promise((s,i)=>{this.renderer=new ee(t,this.api,n,{onSuccess:t=>{e.onSuccess?.(t),s(t)},onError:t=>{e.onError?.(t),i(t)},onCancel:()=>{const t=new a;e.onCancel?.(),i(t)}}),this.renderer.start().catch(t=>{e.onError?.(t),i(t)})})}attach(e,t={}){let n=null;const s=async e=>{e.preventDefault();try{await this.verify({email:t.email,phone:t.phone,authContext:t.authContext,onSuccess:t.onSuccess,onError:t.onError,onCancel:t.onCancel})}catch{}};return se(()=>{document.querySelectorAll(e).forEach(e=>{e.addEventListener("click",s),this.attachedElements.set(e,()=>{e.removeEventListener("click",s)})}),n=new MutationObserver(t=>{t.forEach(t=>{t.addedNodes.forEach(t=>{t instanceof Element&&(t.matches(e)&&(t.addEventListener("click",s),this.attachedElements.set(t,()=>{t.removeEventListener("click",s)})),t.querySelectorAll(e).forEach(e=>{this.attachedElements.has(e)||(e.addEventListener("click",s),this.attachedElements.set(e,()=>{e.removeEventListener("click",s)}))}))})})}),n.observe(document.body,{childList:!0,subtree:!0})}),()=>{n?.disconnect(),this.attachedElements.forEach(e=>e()),this.attachedElements.clear()}}async verifyToken(e){if(!e||"string"!=typeof e)throw new i("Token is required");const t=e.split(".");if(3!==t.length)throw new i("Invalid token format");const[,n]=t,s=JSON.parse(function(e){const t=e.replace(/-/g,"+").replace(/_/g,"/"),n=t+"=".repeat((4-t.length%4)%4);return atob(n)}(n));if(!function(e){if("object"!=typeof e||null===e)return!1;const t=e;return"string"==typeof t.iss&&"string"==typeof t.sub&&"string"==typeof t.aud&&"number"==typeof t.exp&&"number"==typeof t.iat&&"string"==typeof t.identity&&"string"==typeof t.identity_type&&"number"==typeof t.verified_at&&"string"==typeof t.method}(s))throw new i("Invalid token payload structure");const o=Math.floor(Date.now()/1e3);if(s.exp<o)throw new i("Token has expired");const r=`${this.config.apiBaseUrl}/apps/identity/${this.config.accountId}`;if(s.iss!==r)throw new i("Invalid token issuer");return s}close(){this.renderer&&(this.renderer.close(),this.renderer=null)}createInlineContainer(e){let t;if("string"==typeof e){const n=document.querySelector(e);if(!(n&&n instanceof HTMLElement))throw new i(`Target selector "${e}" did not match any element`);t=n}else{if(!(e instanceof HTMLElement))throw new i("Target must be a CSS selector string or HTMLElement");t=e}return new ne(t)}async pop(e={}){return this.verify(e)}async render(e){return this.verify(e)}}class oe extends t{constructor(e,t,n){super(e,t,n),this.httpStatus=n,this.name="UploadApiError"}}class re{constructor(e){this.config=e,this.timeoutMs=e.timeout??3e4}async getVaultUploadInfo(e){const t=`${this.config.apiBaseUrl}/v1/files/in/${e}`,n=await this.request(t,{method:"GET"});if(!function(e){if("object"!=typeof e||null===e)return!1;const t=e;return"string"==typeof t.vault_id&&"string"==typeof t.vault_name&&"number"==typeof t.max_size_bytes&&"object"==typeof t.branding&&null!==t.branding&&"string"==typeof t.branding.organization_name&&"object"==typeof t.encryption&&null!==t.encryption&&"string"==typeof t.encryption.algorithm&&("active"===t.forge_status||"inactive"===t.forge_status)}(n.data))throw new oe("Invalid vault upload info response from server","invalid_response",n.status);const s=n.data;return{vaultId:s.vault_id,vaultName:s.vault_name,maxSizeBytes:s.max_size_bytes,branding:{organizationName:s.branding.organization_name,logoLightUrl:s.branding.logo_light_url,logoDarkUrl:s.branding.logo_dark_url},encryption:{algorithm:s.encryption.algorithm,keyDerivation:s.encryption.key_derivation,postQuantum:s.encryption.post_quantum},forgeStatus:s.forge_status}}async initiateUpload(e,t,n,s){const i=`${this.config.apiBaseUrl}/v1/files/in/${e}/upload`,o=await this.request(i,{method:"POST",body:JSON.stringify({filename:t,size_bytes:n,content_type:s})});if(!function(e){if("object"!=typeof e||null===e)return!1;const t=e;return"string"==typeof t.forge_url&&"string"==typeof t.ingot_id}(o.data))throw new oe("Invalid initiate upload response from server","invalid_response",o.status);return{forgeUrl:o.data.forge_url,ingotId:o.data.ingot_id}}async request(e,n){const s=new AbortController,i=setTimeout(()=>s.abort(),this.timeoutMs);try{const t=await fetch(e,{method:n.method,headers:{"Content-Type":"application/json",Accept:"application/json"},body:n.body,signal:s.signal}),i=await t.json();if(!t.ok){const e="object"==typeof i&&null!==i?i:{},n="object"==typeof e.error&&null!==e.error?e.error:e,s=("string"==typeof n.message?n.message:null)??("string"==typeof n.error?n.error:null)??"Request failed",o="string"==typeof n.code?n.code:"api_error";throw new oe(s,o,t.status)}return{data:"object"==typeof i&&null!==i&&"data"in i?i.data:i,status:t.status}}catch(s){if(s instanceof t)throw s;if(s instanceof DOMException&&"AbortError"===s.name)throw new r(`Request timed out after ${this.timeoutMs}ms`);if(s instanceof TypeError)throw new o(`Network request failed: ${s.message}`,{url:e,method:n.method});throw new o(s instanceof Error?`Request failed: ${s.message}`:"Request failed: Unknown error")}finally{clearTimeout(i)}}}const ae="sparkvault-upload-styles";function le(e){if(document.getElementById(ae))return;const t=document.createElement("style");t.id=ae,t.textContent=function(e){const{backdropBlur:t}=e,n=!1!==t,s={primary:"#4F46E5",primaryHover:"#4338CA",primaryLight:"rgba(79, 70, 229, 0.08)",error:"#DC2626",errorLight:"#FEF2F2",errorBorder:"#FECACA",success:"#059669",bg:"#FFFFFF",bgSubtle:"#FAFAFA",bgHover:"#F5F5F5",bgDark:"#0F0F0F",bgDarkSubtle:"#171717",border:"#E5E5E5",borderHover:"#D4D4D4",borderDark:"#262626",textPrimary:"#0A0A0A",textSecondary:"#525252",textMuted:"#737373",textOnPrimary:"#FFFFFF",textOnDark:"#FAFAFA",textMutedOnDark:"#A3A3A3",shadowSm:"0 1px 2px rgba(0, 0, 0, 0.04)",shadowLg:"0 8px 24px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.04)"};return`\n /* ========================================\n OVERLAY & MODAL CONTAINER\n ======================================== */\n\n .svu-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n padding: 16px;\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n .svu-overlay.svu-blur {\n backdrop-filter: blur(${n?"8px":"0"});\n -webkit-backdrop-filter: blur(${n?"8px":"0"});\n }\n\n .svu-modal {\n background: ${s.bg};\n border-radius: 16px;\n box-shadow: ${s.shadowLg};\n width: 100%;\n max-width: 480px;\n max-height: calc(100vh - 32px);\n display: flex;\n flex-direction: column;\n color: ${s.textPrimary};\n overflow: hidden;\n }\n\n .svu-modal.svu-with-sidebar {\n max-width: 900px;\n flex-direction: row;\n }\n\n .svu-modal-main {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n }\n\n /* Dark card variant for uploading/ceremony states */\n .svu-modal.svu-dark {\n background: ${s.bgDark};\n color: ${s.textOnDark};\n }\n\n /* ========================================\n HEADER\n ======================================== */\n\n .svu-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-bottom: 1px solid ${s.border};\n background: ${s.bg};\n flex-shrink: 0;\n }\n\n .svu-dark .svu-header {\n border-bottom-color: ${s.borderDark};\n background: ${s.bgDark};\n }\n\n .svu-header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n }\n\n .svu-logo {\n height: 28px;\n width: auto;\n max-width: 140px;\n object-fit: contain;\n flex-shrink: 0;\n }\n\n .svu-company-name {\n font-size: 15px;\n font-weight: 600;\n letter-spacing: -0.01em;\n margin: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .svu-close-btn {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n color: ${s.textMuted};\n transition: background 0.15s ease, color 0.15s ease;\n }\n\n .svu-close-btn:hover {\n background: ${s.bgHover};\n color: ${s.textPrimary};\n }\n\n .svu-dark .svu-close-btn {\n color: ${s.textMutedOnDark};\n }\n\n .svu-dark .svu-close-btn:hover {\n background: ${s.bgDarkSubtle};\n color: ${s.textOnDark};\n }\n\n /* ========================================\n BODY\n ======================================== */\n\n .svu-body {\n padding: 24px 28px;\n overflow-y: auto;\n flex: 1;\n }\n\n /* ========================================\n FOOTER\n ======================================== */\n\n .svu-footer {\n padding: 10px 20px;\n border-top: 1px solid ${s.border};\n text-align: center;\n background: ${s.bgSubtle};\n flex-shrink: 0;\n }\n\n .svu-dark .svu-footer {\n border-top-color: ${s.borderDark};\n background: ${s.bgDarkSubtle};\n }\n\n .svu-footer-text {\n font-size: 10px;\n color: ${s.textMuted};\n letter-spacing: 0.02em;\n }\n\n .svu-dark .svu-footer-text {\n color: ${s.textMutedOnDark};\n }\n\n .svu-footer-link {\n color: ${s.textSecondary};\n text-decoration: none;\n font-weight: 500;\n transition: color 0.15s ease;\n }\n\n .svu-footer-link:hover {\n color: ${s.primary};\n }\n\n /* ========================================\n LOADING STATE\n ======================================== */\n\n .svu-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n gap: 16px;\n }\n\n .svu-spinner {\n width: 28px;\n height: 28px;\n border: 2.5px solid ${s.border};\n border-top-color: ${s.primary};\n border-radius: 50%;\n animation: svu-spin 0.7s linear infinite;\n }\n\n .svu-loading-text {\n font-size: 14px;\n color: ${s.textSecondary};\n margin: 0;\n }\n\n @keyframes svu-spin {\n to { transform: rotate(360deg); }\n }\n\n /* ========================================\n FORM VIEW\n ======================================== */\n\n .svu-form-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n\n .svu-title {\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.02em;\n line-height: 1.3;\n margin: 0 0 6px 0;\n text-align: center;\n color: ${s.textPrimary};\n }\n\n .svu-dark .svu-title {\n color: ${s.textOnDark};\n }\n\n .svu-subtitle {\n font-size: 14px;\n font-weight: 400;\n line-height: 1.5;\n color: ${s.textSecondary};\n margin: 0 0 20px 0;\n text-align: center;\n }\n\n .svu-subtitle strong {\n color: ${s.textPrimary};\n font-weight: 500;\n }\n\n /* Drop Zone */\n .svu-drop-zone {\n width: 100%;\n border: 2px dashed ${s.border};\n border-radius: 12px;\n padding: 32px 24px;\n text-align: center;\n cursor: pointer;\n transition: border-color 0.15s ease, background 0.15s ease;\n margin-bottom: 16px;\n }\n\n .svu-drop-zone:hover {\n border-color: ${s.borderHover};\n background: ${s.bgSubtle};\n }\n\n .svu-drop-zone.svu-dragging {\n border-color: ${s.primary};\n background: ${s.primaryLight};\n }\n\n .svu-drop-zone.svu-has-file {\n border-style: solid;\n border-color: ${s.primary};\n background: ${s.primaryLight};\n cursor: default;\n }\n\n .svu-drop-icon {\n color: ${s.textMuted};\n margin-bottom: 12px;\n }\n\n .svu-drop-text {\n font-size: 14px;\n font-weight: 500;\n color: ${s.textPrimary};\n margin: 0 0 4px 0;\n }\n\n .svu-drop-subtext {\n font-size: 13px;\n color: ${s.textSecondary};\n margin: 0 0 8px 0;\n }\n\n .svu-drop-hint {\n font-size: 12px;\n color: ${s.textMuted};\n margin: 0;\n }\n\n /* Selected File */\n .svu-selected-file {\n display: flex;\n align-items: center;\n gap: 12px;\n width: 100%;\n }\n\n .svu-file-icon {\n color: ${s.primary};\n flex-shrink: 0;\n }\n\n .svu-file-info {\n flex: 1;\n min-width: 0;\n text-align: left;\n }\n\n .svu-file-name {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: ${s.textPrimary};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .svu-file-size {\n display: block;\n font-size: 12px;\n color: ${s.textSecondary};\n }\n\n .svu-remove-file {\n flex-shrink: 0;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 50%;\n cursor: pointer;\n font-size: 18px;\n color: ${s.textMuted};\n transition: background 0.15s ease, color 0.15s ease;\n }\n\n .svu-remove-file:hover {\n background: ${s.bgHover};\n color: ${s.error};\n }\n\n /* Max Size */\n .svu-max-size {\n font-size: 12px;\n color: ${s.textMuted};\n margin: 0 0 16px 0;\n }\n\n /* Upload Button */\n .svu-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 12px 20px;\n font-size: 14px;\n font-weight: 500;\n letter-spacing: -0.005em;\n line-height: 1.4;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.15s ease, transform 0.1s ease;\n box-sizing: border-box;\n }\n\n .svu-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .svu-btn:active:not(:disabled) {\n transform: scale(0.98);\n }\n\n .svu-btn-primary {\n background: ${s.primary};\n color: ${s.textOnPrimary};\n box-shadow: ${s.shadowSm};\n }\n\n .svu-btn-primary:hover:not(:disabled) {\n background: ${s.primaryHover};\n }\n\n .svu-btn-secondary {\n background: ${s.bgSubtle};\n color: ${s.textPrimary};\n border: 1px solid ${s.border};\n }\n\n .svu-btn-secondary:hover:not(:disabled) {\n background: ${s.bgHover};\n }\n\n /* Error Alert */\n .svu-error-alert {\n background: ${s.errorLight};\n border: 1px solid ${s.errorBorder};\n color: ${s.error};\n padding: 10px 12px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 450;\n margin-bottom: 16px;\n width: 100%;\n }\n\n /* Metadata Grid */\n .svu-metadata {\n width: 100%;\n margin-top: 20px;\n padding-top: 20px;\n border-top: 1px solid ${s.border};\n }\n\n .svu-metadata h4 {\n font-size: 12px;\n font-weight: 600;\n color: ${s.textSecondary};\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin: 0 0 12px 0;\n }\n\n .svu-metadata-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 8px;\n }\n\n .svu-metadata-item {\n font-size: 12px;\n }\n\n .svu-metadata-item.svu-full-width {\n grid-column: span 2;\n }\n\n .svu-metadata-label {\n display: block;\n color: ${s.textMuted};\n margin-bottom: 2px;\n }\n\n .svu-metadata-value {\n display: block;\n color: ${s.textPrimary};\n font-weight: 500;\n }\n\n .svu-metadata-value.svu-status-active {\n color: ${s.success};\n }\n\n /* ========================================\n UPLOADING VIEW\n ======================================== */\n\n .svu-uploading-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n\n .svu-stages {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n margin: 24px 0;\n width: 100%;\n }\n\n .svu-stage {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n opacity: 0.5;\n transition: opacity 0.3s ease;\n }\n\n .svu-stage.svu-active {\n opacity: 1;\n }\n\n .svu-stage span {\n font-size: 11px;\n color: ${s.textMutedOnDark};\n }\n\n .svu-stage-sub {\n font-size: 10px !important;\n color: ${s.primary} !important;\n }\n\n .svu-stage-arrow {\n width: 24px;\n height: 2px;\n background: ${s.borderDark};\n position: relative;\n }\n\n .svu-stage-arrow.svu-active::after {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n height: 100%;\n background: ${s.primary};\n animation: svu-arrow-flow 1s ease-in-out infinite;\n }\n\n @keyframes svu-arrow-flow {\n 0% { width: 0; }\n 50% { width: 100%; }\n 100% { width: 100%; }\n }\n\n /* Progress Bar */\n .svu-progress {\n width: 100%;\n margin: 20px 0;\n }\n\n .svu-progress-bar {\n width: 100%;\n height: 6px;\n background: ${s.borderDark};\n border-radius: 3px;\n overflow: hidden;\n }\n\n .svu-progress-fill {\n height: 100%;\n background: ${s.primary};\n border-radius: 3px;\n transition: width 0.3s ease;\n }\n\n .svu-progress-text {\n display: flex;\n justify-content: space-between;\n margin-top: 8px;\n font-size: 12px;\n color: ${s.textMutedOnDark};\n }\n\n /* Forging Info */\n .svu-forging-info {\n width: 100%;\n margin-top: 20px;\n padding: 16px;\n background: ${s.bgDarkSubtle};\n border-radius: 8px;\n }\n\n .svu-forging-line {\n font-size: 12px;\n color: ${s.textMutedOnDark};\n margin: 0 0 8px 0;\n }\n\n .svu-forging-line:last-child {\n margin-bottom: 0;\n }\n\n .svu-mono {\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, monospace;\n color: ${s.textOnDark};\n }\n\n /* ========================================\n CEREMONY VIEW\n ======================================== */\n\n .svu-ceremony-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n\n .svu-ceremony-steps {\n width: 100%;\n margin: 20px 0;\n }\n\n .svu-ceremony-step {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 0;\n opacity: 0.4;\n transition: opacity 0.3s ease;\n }\n\n .svu-ceremony-step.svu-active {\n opacity: 1;\n }\n\n .svu-ceremony-step.svu-complete {\n opacity: 0.7;\n }\n\n .svu-step-icon {\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: ${s.primary};\n }\n\n .svu-step-text {\n font-size: 13px;\n color: ${s.textOnDark};\n }\n\n /* Spinning loader */\n .svu-step-icon .svu-spin {\n animation: svu-spin 0.7s linear infinite;\n }\n\n /* ========================================\n COMPLETE VIEW\n ======================================== */\n\n .svu-complete-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n animation: svu-scale-in 0.3s ease;\n }\n\n @keyframes svu-scale-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n\n .svu-success-icon {\n color: ${s.success};\n margin-bottom: 16px;\n }\n\n .svu-success-title {\n font-size: 20px;\n font-weight: 600;\n color: ${s.textPrimary};\n margin: 0 0 24px 0;\n text-align: center;\n }\n\n .svu-success-details {\n width: 100%;\n background: ${s.bgSubtle};\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 16px;\n }\n\n .svu-success-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 0;\n font-size: 13px;\n border-bottom: 1px solid ${s.border};\n }\n\n .svu-success-row:last-child {\n border-bottom: none;\n }\n\n .svu-success-label {\n color: ${s.textSecondary};\n }\n\n .svu-success-value {\n color: ${s.textPrimary};\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .svu-copy-btn {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n color: ${s.textMuted};\n transition: background 0.15s ease, color 0.15s ease;\n }\n\n .svu-copy-btn:hover {\n background: ${s.bgHover};\n color: ${s.primary};\n }\n\n .svu-success-guidance {\n font-size: 13px;\n color: ${s.textSecondary};\n text-align: center;\n margin: 0 0 20px 0;\n line-height: 1.5;\n }\n\n /* ========================================\n ERROR VIEW\n ======================================== */\n\n .svu-error-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n }\n\n .svu-error-icon {\n color: ${s.error};\n margin-bottom: 16px;\n }\n\n .svu-error-title {\n font-size: 18px;\n font-weight: 600;\n color: ${s.textPrimary};\n margin: 0 0 8px 0;\n }\n\n .svu-error-message {\n font-size: 14px;\n color: ${s.textSecondary};\n margin: 0 0 16px 0;\n line-height: 1.5;\n }\n\n .svu-error-code {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n background: ${s.errorLight};\n border: 1px solid ${s.errorBorder};\n border-radius: 6px;\n margin-bottom: 20px;\n }\n\n .svu-error-code-label {\n font-size: 10px;\n font-weight: 600;\n color: #991B1B;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .svu-error-code-value {\n font-size: 11px;\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, monospace;\n color: ${s.error};\n font-weight: 500;\n }\n\n /* ========================================\n SECURITY SIDEBAR\n ======================================== */\n\n .svu-sidebar {\n width: 320px;\n background: ${s.bgDark};\n border-left: 1px solid ${s.borderDark};\n color: ${s.textOnDark};\n padding: 20px;\n overflow-y: auto;\n flex-shrink: 0;\n }\n\n .svu-sidebar-toggle {\n display: flex;\n align-items: center;\n gap: 4px;\n background: transparent;\n border: none;\n color: ${s.textMutedOnDark};\n font-size: 12px;\n cursor: pointer;\n padding: 0;\n margin-bottom: 16px;\n }\n\n .svu-sidebar-toggle:hover {\n color: ${s.textOnDark};\n }\n\n .svu-sidebar-logo {\n margin-bottom: 20px;\n }\n\n .svu-sidebar-logo img {\n height: 24px;\n width: auto;\n }\n\n .svu-sidebar-intro h3 {\n font-size: 14px;\n font-weight: 600;\n margin: 0 0 8px 0;\n }\n\n .svu-sidebar-intro p {\n font-size: 12px;\n color: ${s.textMutedOnDark};\n line-height: 1.5;\n margin: 0 0 20px 0;\n }\n\n .svu-sidebar-security h4 {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n font-weight: 600;\n margin: 0 0 12px 0;\n }\n\n .svu-security-intro {\n font-size: 11px;\n color: ${s.textMutedOnDark};\n line-height: 1.5;\n margin: 0 0 16px 0;\n }\n\n .svu-security-intro strong {\n color: ${s.textOnDark};\n }\n\n .svu-key-chain {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 16px;\n }\n\n .svu-key-item {\n display: flex;\n gap: 12px;\n padding: 10px;\n background: ${s.bgDarkSubtle};\n border-radius: 6px;\n }\n\n .svu-key-number {\n width: 20px;\n height: 20px;\n background: ${s.primary};\n color: ${s.textOnPrimary};\n border-radius: 50%;\n font-size: 11px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n\n .svu-key-info {\n flex: 1;\n }\n\n .svu-key-name {\n display: block;\n font-size: 11px;\n font-weight: 600;\n color: ${s.textOnDark};\n margin-bottom: 2px;\n }\n\n .svu-key-algo {\n display: block;\n font-size: 10px;\n color: ${s.primary};\n margin-bottom: 4px;\n }\n\n .svu-key-desc {\n display: block;\n font-size: 10px;\n color: ${s.textMutedOnDark};\n line-height: 1.4;\n }\n\n .svu-key-connector {\n display: flex;\n justify-content: center;\n color: ${s.textMutedOnDark};\n }\n\n .svu-encryption-result {\n display: flex;\n gap: 8px;\n font-size: 10px;\n color: ${s.textMutedOnDark};\n line-height: 1.5;\n padding: 10px;\n background: ${s.bgDarkSubtle};\n border-radius: 6px;\n margin-bottom: 16px;\n }\n\n .svu-encryption-result svg {\n color: ${s.success};\n flex-shrink: 0;\n }\n\n .svu-encryption-result strong {\n color: ${s.textOnDark};\n }\n\n .svu-security-badges {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n }\n\n .svu-tls-badge {\n font-size: 9px;\n font-weight: 500;\n padding: 4px 8px;\n background: ${s.bgDarkSubtle};\n border-radius: 4px;\n color: ${s.textMutedOnDark};\n }\n\n /* ========================================\n INLINE CONTAINER\n ======================================== */\n\n .svu-inline-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n background: ${s.bg};\n color: ${s.textPrimary};\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n border-radius: 12px;\n border: 1px solid ${s.border};\n overflow: hidden;\n }\n\n .svu-inline-container.svu-dark {\n background: ${s.bgDark};\n color: ${s.textOnDark};\n border-color: ${s.borderDark};\n }\n\n /* ========================================\n RESPONSIVE\n ======================================== */\n\n @media (max-width: 768px) {\n .svu-modal.svu-with-sidebar {\n flex-direction: column;\n max-width: 480px;\n }\n\n .svu-sidebar {\n width: 100%;\n border-left: none;\n border-top: 1px solid ${s.borderDark};\n max-height: 300px;\n }\n }\n\n @media (max-width: 480px) {\n .svu-modal {\n max-width: 100%;\n max-height: 100%;\n border-radius: 0;\n }\n\n .svu-overlay {\n padding: 0;\n }\n\n .svu-body {\n padding: 20px;\n }\n }\n\n /* ========================================\n ACCESSIBILITY\n ======================================== */\n\n .svu-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n `}(e),document.head.appendChild(t)}const de={organizationName:"",logoLightUrl:null,logoDarkUrl:null};class ce{constructor(){this.elements=null,this.onCloseCallback=null,this.keydownHandler=null,this.overlayClickHandler=null,this.closeBtnClickHandler=null,this.closeBtn=null,this.backdropBlur=!0,this.isDarkMode=!1,this.headerElement=null,this.branding=de}createLoading(e,t){this.create({branding:de,backdropBlur:e.backdropBlur},t)}create(e,t){if(this.elements)return;this.onCloseCallback=t,this.backdropBlur=!1!==e.backdropBlur,this.branding=e.branding||de,le({branding:this.branding,backdropBlur:this.backdropBlur});const n=document.createElement("div");n.id="sparkvault-upload-overlay",n.className=this.backdropBlur?"svu-overlay svu-blur":"svu-overlay",n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-labelledby","svu-modal-title");const s=document.createElement("div");s.id="sparkvault-upload-modal",s.className="svu-modal";const i=document.createElement("div");i.className="svu-modal-main";const o=this.createHeader(this.branding);this.headerElement=o;const r=document.createElement("div");r.className="svu-body";const a=document.createElement("div");a.className="svu-footer";const l=document.createElement("span");l.className="svu-footer-text",l.appendChild(document.createTextNode("Secured by "));const d=document.createElement("a");d.href="https://sparkvault.com",d.target="_blank",d.rel="noopener noreferrer",d.className="svu-footer-link",d.textContent="SparkVault",l.appendChild(d),a.appendChild(l),i.appendChild(o),i.appendChild(r),i.appendChild(a),s.appendChild(i),n.appendChild(s),this.overlayClickHandler=e=>{e.target===n&&this.handleClose()},n.addEventListener("click",this.overlayClickHandler),this.keydownHandler=e=>{"Escape"===e.key&&this.handleClose()},document.addEventListener("keydown",this.keydownHandler),document.body.appendChild(n),document.body.style.overflow="hidden",this.elements={overlay:n,modal:s,main:i,header:o,body:r,footer:a,sidebar:null}}createHeader(e){const t=document.createElement("div");t.className="svu-header";const n=document.createElement("div");n.className="svu-header-title";const s=this.isDarkMode?e.logoDarkUrl||e.logoLightUrl:e.logoLightUrl||e.logoDarkUrl;if(s){const t=document.createElement("img");t.className="svu-logo",t.src=s,t.alt=e.organizationName,n.appendChild(t);const i=document.createElement("span");i.className="svu-sr-only",i.id="svu-modal-title",i.textContent=e.organizationName,n.appendChild(i)}else if(e.organizationName){const t=document.createElement("h2");t.className="svu-company-name",t.id="svu-modal-title",t.textContent=e.organizationName,n.appendChild(t)}return this.closeBtn=document.createElement("button"),this.closeBtn.className="svu-close-btn",this.closeBtn.setAttribute("aria-label","Close"),this.closeBtn.innerHTML='\n <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M18 6L6 18M6 6l12 12"/>\n </svg>\n ',this.closeBtnClickHandler=()=>this.handleClose(),this.closeBtn.addEventListener("click",this.closeBtnClickHandler),t.appendChild(n),t.appendChild(this.closeBtn),t}updateBranding(e){if(!this.elements)return;this.branding=e;const t=this.createHeader(e);this.headerElement?.parentNode&&this.headerElement.parentNode.replaceChild(t,this.headerElement),this.headerElement=t}updateBackdropBlur(e){this.elements&&(this.backdropBlur=e,e?this.elements.overlay.classList.add("svu-blur"):this.elements.overlay.classList.remove("svu-blur"))}setDarkMode(e){if(!this.elements)return;this.isDarkMode=e,e?this.elements.modal.classList.add("svu-dark"):this.elements.modal.classList.remove("svu-dark");const t=this.createHeader(this.branding);this.headerElement?.parentNode&&this.headerElement.parentNode.replaceChild(t,this.headerElement),this.headerElement=t}toggleSidebar(e){if(this.elements)if(e&&!this.elements.sidebar){const e=this.createSecuritySidebar();this.elements.modal.classList.add("svu-with-sidebar"),this.elements.modal.appendChild(e),this.elements.sidebar=e}else!e&&this.elements.sidebar&&(this.elements.modal.classList.remove("svu-with-sidebar"),this.elements.sidebar.remove(),this.elements.sidebar=null)}createSecuritySidebar(){const e=document.createElement("div");e.className="svu-sidebar";const t=document.createElement("div");if(t.className="svu-sidebar-logo",this.branding.logoDarkUrl){const e=document.createElement("img");e.src=this.branding.logoDarkUrl,e.alt=this.branding.organizationName,t.appendChild(e)}e.appendChild(t);const n=document.createElement("div");n.className="svu-sidebar-intro",n.innerHTML="\n <h3>Secure File Transfer</h3>\n <p>This file is secured with SparkVault's advanced multi-key cryptography and end-to-end encryption.</p>\n ",e.appendChild(n);const s=document.createElement("div");return s.className="svu-sidebar-security",s.innerHTML='\n <h4>\n <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>\n </svg>\n SparkVault\'s Triple Zero-Trust\n </h4>\n <p class="svu-security-intro">\n Decryption requires <strong>three independent Master Keys</strong>, held in three different physical locations, by three separate companies, all cryptographically combined in real-time.\n </p>\n <div class="svu-key-chain">\n <div class="svu-key-item">\n <div class="svu-key-number">1</div>\n <div class="svu-key-info">\n <span class="svu-key-name">Kyber-1024 Post-Quantum Key</span>\n <span class="svu-key-algo">ML-KEM lattice-based encapsulation</span>\n <span class="svu-key-desc">Quantum-resistant key exchange</span>\n </div>\n </div>\n <div class="svu-key-connector">\n <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 5v14M5 12h14"/>\n </svg>\n </div>\n <div class="svu-key-item">\n <div class="svu-key-number">2</div>\n <div class="svu-key-info">\n <span class="svu-key-name">X25519 Ephemeral Key</span>\n <span class="svu-key-algo">Elliptic-curve Diffie-Hellman</span>\n <span class="svu-key-desc">Perfect forward secrecy</span>\n </div>\n </div>\n <div class="svu-key-connector">\n <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 5v14M5 12h14"/>\n </svg>\n </div>\n <div class="svu-key-item">\n <div class="svu-key-number">3</div>\n <div class="svu-key-info">\n <span class="svu-key-name">HKDF-SHA512 Derived Key</span>\n <span class="svu-key-algo">Hardware-bound key derivation</span>\n <span class="svu-key-desc">Vault master key in secure enclave</span>\n </div>\n </div>\n </div>\n <div class="svu-encryption-result">\n <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M20 6L9 17l-5-5"/>\n </svg>\n <span>Combined via <strong>HKDF</strong> producing <strong>AES-256-GCM</strong> authenticated cipher</span>\n </div>\n <div class="svu-security-badges">\n <span class="svu-tls-badge">TLS 1.3 In Transit</span>\n <span class="svu-tls-badge">Encrypted At Rest</span>\n <span class="svu-tls-badge">Zero Knowledge</span>\n </div>\n ',e.appendChild(s),e}handleClose(){this.onCloseCallback&&this.onCloseCallback()}getBody(){return this.elements?.body??null}getSidebar(){return this.elements?.sidebar??null}isOpen(){return null!==this.elements}destroy(){this.keydownHandler&&(document.removeEventListener("keydown",this.keydownHandler),this.keydownHandler=null),this.elements&&this.overlayClickHandler&&(this.elements.overlay.removeEventListener("click",this.overlayClickHandler),this.overlayClickHandler=null),this.closeBtn&&this.closeBtnClickHandler&&(this.closeBtn.removeEventListener("click",this.closeBtnClickHandler),this.closeBtnClickHandler=null,this.closeBtn=null),this.elements&&(this.elements.overlay.remove(),this.elements=null),this.headerElement=null,document.body.style.overflow="",this.onCloseCallback=null}}const he={showHeader:!0,showCloseButton:!0,showFooter:!0};class pe{constructor(e,t={}){this.container=null,this.header=null,this.body=null,this.footer=null,this.sidebar=null,this.closeBtn=null,this.onCloseCallback=null,this.closeBtnClickHandler=null,this.isDarkMode=!1,this.branding={organizationName:"",logoLightUrl:null,logoDarkUrl:null},this.targetElement=e,this.containerOptions={...he,...t}}createLoading(e,t){if(!this.container){if(this.onCloseCallback=t,le({branding:this.branding,backdropBlur:!1}),this.container=document.createElement("div"),this.container.className="svu-inline-container",this.containerOptions.showHeader&&(this.header=this.createHeader(this.branding),this.container.appendChild(this.header)),this.body=document.createElement("div"),this.body.className="svu-body",this.container.appendChild(this.body),this.containerOptions.showFooter){this.footer=document.createElement("div"),this.footer.className="svu-footer";const e=document.createElement("span");e.className="svu-footer-text",e.appendChild(document.createTextNode("Secured by "));const t=document.createElement("a");t.href="https://sparkvault.com",t.target="_blank",t.rel="noopener noreferrer",t.className="svu-footer-link",t.textContent="SparkVault",e.appendChild(t),this.footer.appendChild(e),this.container.appendChild(this.footer)}this.targetElement.innerHTML="",this.targetElement.appendChild(this.container)}}updateBranding(e){if(this.container&&(this.branding=e,this.containerOptions.showHeader&&this.header)){const t=this.createHeader(e);this.container.replaceChild(t,this.header),this.header=t}}updateBackdropBlur(e){}setDarkMode(e){if(this.container&&(this.isDarkMode=e,e?this.container.classList.add("svu-dark"):this.container.classList.remove("svu-dark"),this.containerOptions.showHeader&&this.header)){const e=this.createHeader(this.branding);this.container.replaceChild(e,this.header),this.header=e}}toggleSidebar(e){this.container&&(e&&!this.sidebar?(this.sidebar=this.createSecuritySidebar(),this.container.classList.add("svu-with-sidebar"),this.container.appendChild(this.sidebar)):!e&&this.sidebar&&(this.container.classList.remove("svu-with-sidebar"),this.sidebar.remove(),this.sidebar=null))}getBody(){return this.body}getSidebar(){return this.sidebar}isOpen(){return null!==this.container}destroy(){this.closeBtn&&this.closeBtnClickHandler&&(this.closeBtn.removeEventListener("click",this.closeBtnClickHandler),this.closeBtnClickHandler=null,this.closeBtn=null),this.container&&this.container.parentNode&&this.container.remove(),this.container=null,this.header=null,this.body=null,this.footer=null,this.sidebar=null,this.onCloseCallback=null}createHeader(e){const t=document.createElement("div");t.className="svu-header";const n=document.createElement("div");n.className="svu-header-title";const s=this.isDarkMode?e.logoDarkUrl||e.logoLightUrl:e.logoLightUrl||e.logoDarkUrl;if(s){const t=document.createElement("img");t.className="svu-logo",t.src=s,t.alt=e.organizationName,n.appendChild(t);const i=document.createElement("span");i.className="svu-sr-only",i.textContent=e.organizationName,n.appendChild(i)}else if(e.organizationName){const t=document.createElement("h2");t.className="svu-company-name",t.textContent=e.organizationName,n.appendChild(t)}return t.appendChild(n),this.containerOptions.showCloseButton&&(this.closeBtn=document.createElement("button"),this.closeBtn.className="svu-close-btn",this.closeBtn.setAttribute("aria-label","Close"),this.closeBtn.innerHTML='\n <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M18 6L6 18M6 6l12 12"/>\n </svg>\n ',this.closeBtnClickHandler=()=>this.handleClose(),this.closeBtn.addEventListener("click",this.closeBtnClickHandler),t.appendChild(this.closeBtn)),t}createSecuritySidebar(){const e=document.createElement("div");e.className="svu-sidebar";const t=document.createElement("div");if(t.className="svu-sidebar-logo",this.branding.logoDarkUrl){const e=document.createElement("img");e.src=this.branding.logoDarkUrl,e.alt=this.branding.organizationName,t.appendChild(e)}e.appendChild(t);const n=document.createElement("div");n.className="svu-sidebar-intro",n.innerHTML="\n <h3>Secure File Transfer</h3>\n <p>This file is secured with SparkVault's advanced multi-key cryptography and end-to-end encryption.</p>\n ",e.appendChild(n);const s=document.createElement("div");return s.className="svu-sidebar-security",s.innerHTML='\n <h4>\n <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>\n </svg>\n SparkVault\'s Triple Zero-Trust\n </h4>\n <p class="svu-security-intro">\n Decryption requires <strong>three independent Master Keys</strong>, held in three different physical locations, by three separate companies, all cryptographically combined in real-time.\n </p>\n <div class="svu-key-chain">\n <div class="svu-key-item">\n <div class="svu-key-number">1</div>\n <div class="svu-key-info">\n <span class="svu-key-name">Kyber-1024 Post-Quantum Key</span>\n <span class="svu-key-algo">ML-KEM lattice-based encapsulation</span>\n <span class="svu-key-desc">Quantum-resistant key exchange</span>\n </div>\n </div>\n <div class="svu-key-connector">\n <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 5v14M5 12h14"/>\n </svg>\n </div>\n <div class="svu-key-item">\n <div class="svu-key-number">2</div>\n <div class="svu-key-info">\n <span class="svu-key-name">X25519 Ephemeral Key</span>\n <span class="svu-key-algo">Elliptic-curve Diffie-Hellman</span>\n <span class="svu-key-desc">Perfect forward secrecy</span>\n </div>\n </div>\n <div class="svu-key-connector">\n <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 5v14M5 12h14"/>\n </svg>\n </div>\n <div class="svu-key-item">\n <div class="svu-key-number">3</div>\n <div class="svu-key-info">\n <span class="svu-key-name">HKDF-SHA512 Derived Key</span>\n <span class="svu-key-algo">Hardware-bound key derivation</span>\n <span class="svu-key-desc">Vault master key in secure enclave</span>\n </div>\n </div>\n </div>\n <div class="svu-encryption-result">\n <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M20 6L9 17l-5-5"/>\n </svg>\n <span>Combined via <strong>HKDF</strong> producing <strong>AES-256-GCM</strong> authenticated cipher</span>\n </div>\n <div class="svu-security-badges">\n <span class="svu-tls-badge">TLS 1.3 In Transit</span>\n <span class="svu-tls-badge">Encrypted At Rest</span>\n <span class="svu-tls-badge">Zero Knowledge</span>\n </div>\n ',e.appendChild(s),e}handleClose(){this.onCloseCallback&&this.onCloseCallback()}}const ue=[{text:"File received by Forge",duration:600},{text:"Deriving Triple Zero-Trust encryption keys",duration:800},{text:"Encrypting with AES-256-GCM",duration:900},{text:"Generating cryptographic signatures",duration:700},{text:"Storing in post-quantum protected vault",duration:600},{text:"Verifying integrity",duration:400}];function me(e){const t=new Uint8Array(e.match(/.{2}/g).map(e=>parseInt(e,16)));return btoa(String.fromCharCode(...t)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function ve(e){if(0===e)return"0 B";const t=Math.floor(Math.log(e)/Math.log(1024));return`${parseFloat((e/Math.pow(1024,t)).toFixed(2))} ${["B","KB","MB","GB","TB"][t]}`}class ge{constructor(e,t,n,s){this.viewState={view:"loading"},this.config=null,this.selectedFile=null,this.fileInputElement=null,this.uploadModal=null,this.isInlineMode=!1,this.pasteHandler=null,this.container=e,this.api=t,this.options=n,this.callbacks=s,this.isInlineMode=!!n.target}async start(){this.container.createLoading({backdropBlur:this.options.backdropBlur},()=>this.handleClose()),this.setState({view:"loading"});try{const[e]=await Promise.all([this.api.getVaultUploadInfo(this.options.vaultId),new Promise(e=>setTimeout(e,1e3))]);this.config=e,this.container.updateBranding(e.branding),this.setState({view:"form",config:e})}catch(e){this.handleApiError(e)}}close(){this.cleanupFileInput(),this.cleanupPasteHandler(),this.uploadModal&&(this.uploadModal.destroy(),this.uploadModal=null),this.container.destroy()}handleClose(){this.close(),this.callbacks.onCancel()}setState(e){this.viewState=e,this.render()}render(){const e="uploading"===this.viewState.view||"ceremony"===this.viewState.view;if(this.isInlineMode&&e)return void this.renderInModal();this.uploadModal&&(this.uploadModal.destroy(),this.uploadModal=null);const t=this.container.getBody();if(!t)return;t.innerHTML="";const n=this.container;switch("setDarkMode"in n&&!this.isInlineMode&&(n.setDarkMode(e),this.container.toggleSidebar(e)),this.viewState.view){case"loading":t.appendChild(this.renderLoading());break;case"form":t.appendChild(this.renderForm(this.viewState.config,this.viewState.error));break;case"uploading":t.appendChild(this.renderUploading(this.viewState));break;case"ceremony":t.appendChild(this.renderCeremony(this.viewState));break;case"complete":t.appendChild(this.renderComplete(this.viewState.result,this.viewState.config));break;case"error":t.appendChild(this.renderError(this.viewState))}}renderInModal(){this.uploadModal||(this.uploadModal=new ce,this.uploadModal.createLoading({backdropBlur:this.options.backdropBlur??!0},()=>{}),this.config&&this.uploadModal.updateBranding(this.config.branding));const e=this.uploadModal.getBody();e&&(e.innerHTML="",this.uploadModal.setDarkMode(!0),this.uploadModal.toggleSidebar(!0),"uploading"===this.viewState.view?e.appendChild(this.renderUploading(this.viewState)):"ceremony"===this.viewState.view&&e.appendChild(this.renderCeremony(this.viewState)))}renderLoading(){const e=document.createElement("div");return e.className="svu-loading",e.innerHTML='\n <div class="svu-spinner"></div>\n <p class="svu-loading-text">Establishing secure connection...</p>\n ',e}renderForm(e,t){const n=document.createElement("div");n.className="svu-form-view";const s=document.createElement("h2");s.className="svu-title",s.textContent="Cryptographically Secure File Transfer",n.appendChild(s);const i=document.createElement("p");if(i.className="svu-subtitle",i.innerHTML=`Destination Vault: <strong>${this.escapeHtml(e.vaultName)}</strong>`,n.appendChild(i),t){const e=document.createElement("div");e.className="svu-error-alert",e.textContent=t,n.appendChild(e)}const o=document.createElement("div");if(o.className="svu-drop-zone"+(this.selectedFile?" svu-has-file":""),this.selectedFile){o.innerHTML=`\n <div class="svu-selected-file">\n <div class="svu-file-icon">\n <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/>\n <path d="M14 2v6h6"/>\n </svg>\n </div>\n <div class="svu-file-info">\n <span class="svu-file-name">${this.escapeHtml(this.selectedFile.name)}</span>\n <span class="svu-file-size">${ve(this.selectedFile.size)}</span>\n </div>\n <button class="svu-remove-file" aria-label="Remove file">&times;</button>\n </div>\n `;const e=o.querySelector(".svu-remove-file");e&&e.addEventListener("click",e=>{e.stopPropagation(),this.selectedFile=null,this.render()})}else o.innerHTML='\n <div class="svu-drop-icon">\n <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/>\n <path d="M17 8l-5-5-5 5"/>\n <path d="M12 3v12"/>\n </svg>\n </div>\n <p class="svu-drop-text">Drag & drop a file here</p>\n <p class="svu-drop-subtext">or click to select</p>\n <p class="svu-drop-hint">Paste from clipboard also supported (Ctrl+V)</p>\n ';o.addEventListener("dragover",e=>{e.preventDefault(),o.classList.add("svu-dragging")}),o.addEventListener("dragleave",e=>{e.preventDefault(),o.classList.remove("svu-dragging")}),o.addEventListener("drop",t=>{t.preventDefault(),o.classList.remove("svu-dragging");const n=t.dataTransfer?.files[0];n&&this.handleFileSelect(n,e)}),o.addEventListener("click",()=>{this.selectedFile||this.openFileSelector(e)}),n.appendChild(o);const r=document.createElement("p");if(r.className="svu-max-size",r.textContent=`Max upload size: ${ve(e.maxSizeBytes)}`,n.appendChild(r),this.selectedFile){const t=document.createElement("button");t.className="svu-btn svu-btn-primary",t.textContent="Upload Securely",t.onclick=()=>this.startUpload(e),n.appendChild(t)}const a=document.createElement("div");return a.className="svu-metadata",a.innerHTML=`\n <h4>Transfer Details</h4>\n <div class="svu-metadata-grid">\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Recipient</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.branding.organizationName)}</span>\n </div>\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Vault</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.vaultName)}</span>\n </div>\n <div class="svu-metadata-item svu-full-width">\n <span class="svu-metadata-label">Vault ID</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.vaultId)}</span>\n </div>\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Encryption</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.encryption.algorithm)}</span>\n </div>\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Key Derivation</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.encryption.keyDerivation)}</span>\n </div>\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Post-Quantum</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.encryption.postQuantum)}</span>\n </div>\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Forge Status</span>\n <span class="svu-metadata-value ${"active"===e.forgeStatus?"svu-status-active":""}">\n ${"active"===e.forgeStatus?"● Active":"○ Inactive"}\n </span>\n </div>\n </div>\n `,n.appendChild(a),this.setupPasteHandler(e),n}renderUploading(e){const t=document.createElement("div");t.className="svu-uploading-view";const n=me(e.ingotId.replace("ing_",""));return t.innerHTML=`\n <h2 class="svu-title">Streaming & Encrypting</h2>\n\n <div class="svu-stages">\n <div class="svu-stage svu-active">\n <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <rect x="2" y="3" width="20" height="18" rx="2"/>\n <path d="M2 9h20"/>\n <circle cx="6" cy="6" r="1" fill="currentColor"/>\n <circle cx="10" cy="6" r="1" fill="currentColor"/>\n </svg>\n <span>Your Browser</span>\n </div>\n <div class="svu-stage-arrow svu-active"></div>\n <div class="svu-stage svu-active">\n <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>\n </svg>\n <span>SparkVault</span>\n <span class="svu-stage-sub">Encrypting</span>\n </div>\n <div class="svu-stage-arrow"></div>\n <div class="svu-stage">\n <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <rect x="3" y="3" width="18" height="18" rx="2"/>\n <circle cx="12" cy="12" r="3"/>\n <path d="M12 9V6M12 18v-3M9 12H6M18 12h-3"/>\n </svg>\n <span>${this.escapeHtml(this.config?.branding.organizationName||"")}</span>\n </div>\n </div>\n\n <div class="svu-progress">\n <div class="svu-progress-bar">\n <div class="svu-progress-fill" style="width: ${e.progress}%"></div>\n </div>\n <div class="svu-progress-text">\n <span>${e.progress}%</span>\n <span>${ve(e.bytesUploaded)} / ${ve(e.file.size)}</span>\n </div>\n </div>\n\n <div class="svu-forging-info">\n <p class="svu-forging-line">Forging Ingot ID: <span class="svu-mono">${n}</span></p>\n <p class="svu-forging-line">Secure Transfer Channel: <span class="svu-mono">${e.requestId}</span></p>\n </div>\n `,t}renderCeremony(e){const t=document.createElement("div");t.className="svu-ceremony-view";const n=me(e.ingotId.replace("ing_",""));let s="";return ue.forEach((t,n)=>{const i=n<e.step,o=n===e.step;s+=`\n <div class="svu-ceremony-step ${i?"svu-complete":""} ${o?"svu-active":""}">\n <span class="svu-step-icon">\n ${i?'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>':o?'<svg class="svu-spin" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/></svg>':""}\n </span>\n <span class="svu-step-text">${t.text}</span>\n </div>\n `}),t.innerHTML=`\n <h2 class="svu-title">Securing Your File</h2>\n\n <div class="svu-ceremony-steps">\n ${s}\n </div>\n\n <div class="svu-forging-info">\n <p class="svu-forging-line">Forging Ingot ID: <span class="svu-mono">${n}</span></p>\n <p class="svu-forging-line">Secure Transfer Channel: <span class="svu-mono">${e.requestId}</span></p>\n </div>\n `,t}renderComplete(e,t){const n=document.createElement("div");n.className="svu-complete-view",n.innerHTML=`\n <div class="svu-success-icon">\n <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>\n <path d="M22 4L12 14.01l-3-3"/>\n </svg>\n </div>\n\n <h2 class="svu-success-title">Your file has been securely sent!</h2>\n\n <div class="svu-success-details">\n <div class="svu-success-row">\n <span class="svu-success-label">Ingot ID</span>\n <span class="svu-success-value svu-mono">\n ${this.escapeHtml(e.ingotId)}\n <button class="svu-copy-btn" aria-label="Copy Ingot ID">\n <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>\n <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/>\n </svg>\n </button>\n </span>\n </div>\n <div class="svu-success-row">\n <span class="svu-success-label">File</span>\n <span class="svu-success-value">${this.escapeHtml(e.filename)}</span>\n </div>\n <div class="svu-success-row">\n <span class="svu-success-label">Size</span>\n <span class="svu-success-value">${ve(e.sizeBytes)}</span>\n </div>\n <div class="svu-success-row">\n <span class="svu-success-label">Timestamp</span>\n <span class="svu-success-value svu-mono">${this.escapeHtml(e.uploadTime)}</span>\n </div>\n <div class="svu-success-row">\n <span class="svu-success-label">Encryption</span>\n <span class="svu-success-value">AES-256-GCM</span>\n </div>\n </div>\n\n <p class="svu-success-guidance">\n ${this.escapeHtml(t.branding.organizationName)} has been notified and can access this file\n from their SparkVault dashboard. Save the Ingot ID above for your records.\n </p>\n\n <button class="svu-btn svu-btn-secondary">\n <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M1 4v6h6M23 20v-6h-6"/>\n <path d="M20.49 9A9 9 0 005.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 013.51 15"/>\n </svg>\n Upload Another File\n </button>\n `;const s=n.querySelector(".svu-copy-btn");s&&s.addEventListener("click",()=>{navigator.clipboard.writeText(e.ingotId)});const i=n.querySelector(".svu-btn-secondary");return i&&i.addEventListener("click",()=>{this.selectedFile=null,this.setState({view:"form",config:t})}),n}renderError(e){const t=document.createElement("div");t.className="svu-error-view";const n=e.httpStatus||404,s=402===n?"Service Unavailable":"Resource Not Found";return t.innerHTML=`\n <div class="svu-error-icon">\n <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <circle cx="12" cy="12" r="10"/>\n <path d="M12 8v4M12 16h.01"/>\n </svg>\n </div>\n\n <h2 class="svu-error-title">${n} - ${s}</h2>\n\n <p class="svu-error-message">${this.escapeHtml(e.message)}</p>\n\n <div class="svu-error-code">\n <span class="svu-error-code-label">Code</span>\n <span class="svu-error-code-value">${this.escapeHtml(e.code)}</span>\n </div>\n `,t}handleFileSelect(e,t){e.size>t.maxSizeBytes?this.setState({view:"form",config:t,error:`File size exceeds maximum allowed (${ve(t.maxSizeBytes)})`}):(this.selectedFile=e,this.setState({view:"form",config:t}))}openFileSelector(e){this.cleanupFileInput(),this.fileInputElement=document.createElement("input"),this.fileInputElement.type="file",this.fileInputElement.style.display="none",this.fileInputElement.onchange=()=>{const t=this.fileInputElement?.files?.[0];t&&this.handleFileSelect(t,e)},document.body.appendChild(this.fileInputElement),this.fileInputElement.click()}cleanupFileInput(){this.fileInputElement&&(this.fileInputElement.remove(),this.fileInputElement=null)}setupPasteHandler(e){this.cleanupPasteHandler(),this.pasteHandler=t=>{const n=t.clipboardData?.items;if(n)for(const t of n)if("file"===t.kind){const n=t.getAsFile();if(n){this.handleFileSelect(n,e);break}}},document.addEventListener("paste",this.pasteHandler)}cleanupPasteHandler(){this.pasteHandler&&(document.removeEventListener("paste",this.pasteHandler),this.pasteHandler=null)}async startUpload(e){if(!this.selectedFile)return;const t=this.selectedFile,n=`${Date.now().toString(16)}${Math.random().toString(16).substring(2,10)}`;try{const{forgeUrl:s,ingotId:i}=await this.api.initiateUpload(e.vaultId,t.name,t.size,t.type||"application/octet-stream");this.setState({view:"uploading",file:t,ingotId:i,requestId:n,progress:0,bytesUploaded:0}),await this.uploadWithTus(t,s,i,n),await this.runCeremony(t,i,n);const o=new Date,r=`${o.getUTCFullYear()}-${String(o.getUTCMonth()+1).padStart(2,"0")}-${String(o.getUTCDate()).padStart(2,"0")} ${String(o.getUTCHours()).padStart(2,"0")}:${String(o.getUTCMinutes()).padStart(2,"0")}:${String(o.getUTCSeconds()).padStart(2,"0")} UTC`,a={ingotId:i,vaultId:e.vaultId,filename:t.name,sizeBytes:t.size,uploadTime:r};this.callbacks.onProgress?.({bytesUploaded:t.size,bytesTotal:t.size,percentage:100,phase:"complete"}),this.setState({view:"complete",result:a,config:e}),this.callbacks.onSuccess(a)}catch(e){this.handleApiError(e)}}async uploadWithTus(e,t,n,s){return new Promise((i,o)=>{const r=new XMLHttpRequest;r.timeout=6e5,r.upload.onprogress=t=>{if(t.lengthComputable){const i=Math.round(t.loaded/t.total*100);this.setState({view:"uploading",file:e,ingotId:n,requestId:s,progress:i,bytesUploaded:t.loaded}),this.callbacks.onProgress?.({bytesUploaded:t.loaded,bytesTotal:t.total,percentage:i,phase:"uploading"})}},r.onload=()=>{r.status>=200&&r.status<300?i():o(new Error(`Upload failed with status ${r.status}`))},r.onerror=()=>o(new Error("Upload failed")),r.ontimeout=()=>o(new Error("Upload timed out")),r.open("POST",t),r.setRequestHeader("Content-Type",e.type||"application/octet-stream"),r.send(e)})}async runCeremony(e,t,n){for(let s=0;s<ue.length;s++)this.setState({view:"ceremony",file:e,ingotId:t,requestId:n,step:s,complete:!1}),this.callbacks.onProgress?.({bytesUploaded:e.size,bytesTotal:e.size,percentage:100,phase:"ceremony"}),await new Promise(e=>setTimeout(e,ue[s].duration));this.setState({view:"ceremony",file:e,ingotId:t,requestId:n,step:ue.length,complete:!0}),await new Promise(e=>setTimeout(e,500))}handleApiError(e){if(e instanceof oe)404===e.httpStatus?this.setState({view:"error",message:"The requested upload endpoint could not be located. This may occur if the upload link has expired, been disabled, or was entered incorrectly.",code:"NOT_FOUND",httpStatus:404}):402===e.httpStatus?this.setState({view:"error",message:"This upload endpoint has been temporarily disabled due to an account billing issue. Please contact the organization's administrator.",code:"PAYMENT_REQUIRED",httpStatus:402}):this.setState({view:"error",message:e.message,code:e.code,httpStatus:e.httpStatus}),this.callbacks.onError(e);else if(e instanceof Error)this.setState({view:"error",message:e.message,code:"UNKNOWN_ERROR"}),this.callbacks.onError(e);else{const e=new Error("An unexpected error occurred");this.setState({view:"error",message:e.message,code:"UNKNOWN_ERROR"}),this.callbacks.onError(e)}}escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}}class be{constructor(e){this.renderer=null,this.attachedElements=new Map,this.config=e,this.api=new re(e)}async upload(e){if(!e.vaultId)throw new i("vaultId is required");this.renderer&&this.renderer.close();const t=!!e.target?this.createInlineContainer(e.target):new ce,n={...e,backdropBlur:e.backdropBlur??this.config.backdropBlur};return new Promise((s,i)=>{this.renderer=new ge(t,this.api,n,{onSuccess:t=>{e.onSuccess?.(t),s(t)},onError:t=>{e.onError?.(t),i(t)},onCancel:()=>{const t=new a;e.onCancel?.(),i(t)},onProgress:e.onProgress}),this.renderer.start().catch(t=>{e.onError?.(t),i(t)})})}attach(e,t){if(!t.vaultId)throw new i("vaultId is required");let n=null;const s=async e=>{e.preventDefault();try{await this.upload({vaultId:t.vaultId,onSuccess:t.onSuccess,onError:t.onError,onCancel:t.onCancel,onProgress:t.onProgress})}catch{}};return se(()=>{document.querySelectorAll(e).forEach(e=>{e.addEventListener("click",s),this.attachedElements.set(e,()=>{e.removeEventListener("click",s)})}),n=new MutationObserver(t=>{t.forEach(t=>{t.addedNodes.forEach(t=>{t instanceof Element&&(t.matches(e)&&(t.addEventListener("click",s),this.attachedElements.set(t,()=>{t.removeEventListener("click",s)})),t.querySelectorAll(e).forEach(e=>{this.attachedElements.has(e)||(e.addEventListener("click",s),this.attachedElements.set(e,()=>{e.removeEventListener("click",s)}))}))})})}),n.observe(document.body,{childList:!0,subtree:!0})}),()=>{n?.disconnect(),this.attachedElements.forEach(e=>e()),this.attachedElements.clear()}}close(){this.renderer&&(this.renderer.close(),this.renderer=null)}createInlineContainer(e){let t;if("string"==typeof e){const n=document.querySelector(e);if(!(n&&n instanceof HTMLElement))throw new i(`Target selector "${e}" did not match any element`);t=n}else{if(!(e instanceof HTMLElement))throw new i("Target must be a CSS selector string or HTMLElement");t=e}return new pe(t)}async pop(e){return this.upload(e)}async render(e){return this.upload(e)}}class fe{constructor(e,t){this.http=t,this.uploadModule=new be(e);const n=e=>this.uploadModule.upload(e);n.attach=(e,t)=>this.uploadModule.attach(e,t),n.close=()=>this.uploadModule.close(),this.upload=n}async create(e){this.validateCreateOptions(e);const t=await this.http.post("/v1/vaults",{name:e.name,hosted_vmk:e.hostedVmk??!1});return{id:t.data.vault_id,name:t.data.name,vmk:t.data.vmk,createdAt:t.data.created_at,hostedVmk:t.data.hosted_vmk}}async unseal(e,t){if(!e)throw new i("Vault ID is required");if(!t)throw new i("VMK is required");const n=e.startsWith("vlt_")?e:`vlt_${e}`,s=await this.http.post(`/v1/vaults/${n}/unseal`,{vmk:t});return{id:s.data.vault_id,name:s.data.name,vatToken:s.data.vat_token,expiresAt:s.data.expires_at,ingotCount:s.data.ingot_count,storageBytes:s.data.storage_bytes}}async list(){return(await this.http.get("/v1/vaults")).data.vaults.map(e=>({id:e.vault_id,name:e.name,createdAt:e.created_at,ingotCount:e.ingot_count,storageBytes:e.storage_bytes,hostedVmk:e.hosted_vmk}))}async delete(e,t){const n=e.startsWith("vlt_")?e:`vlt_${e}`;await this.http.delete(`/v1/vaults/${n}`,{headers:{"X-VMK":t}})}async uploadIngot(e,t){this.validateUploadOptions(t);const n=t.name??(t.file instanceof File?t.file.name:"unnamed"),s=t.contentType??t.file.type??"application/octet-stream",i=await this.http.post(`/v1/vaults/${e.id}/ingots`,{name:n,content_type:s,size_bytes:t.file.size},{headers:{Authorization:`Bearer ${e.vatToken}`}});return{id:i.data.ingot_id,name:i.data.name,contentType:i.data.content_type,size:i.data.size_bytes,createdAt:i.data.created_at,type:i.data.ingot_type}}async downloadIngot(e,t){const n=t.startsWith("ing_")?t:`ing_${t}`;return this.http.requestRaw(`/v1/vaults/${e.id}/ingots/${n}/download`,{method:"POST",headers:{Authorization:`Bearer ${e.vatToken}`}})}async listIngots(e){return(await this.http.get(`/v1/vaults/${e.id}/ingots`,{headers:{Authorization:`Bearer ${e.vatToken}`}})).data.ingots.map(e=>({id:e.ingot_id,name:e.name,contentType:e.content_type,size:e.size_bytes,createdAt:e.created_at,type:e.ingot_type}))}async deleteIngot(e,t){const n=t.startsWith("ing_")?t:`ing_${t}`;await this.http.delete(`/v1/vaults/${e.id}/ingots/${n}`,{headers:{Authorization:`Bearer ${e.vatToken}`}})}validateCreateOptions(e){if(!e.name?.trim())throw new i("Vault name is required");if(e.name.length>255)throw new i("Vault name must be 255 characters or less")}validateUploadOptions(e){if(!e.file)throw new i("File is required");if(0===e.file.size)throw new i("File cannot be empty")}}let ke=!1;function ye(e){ke=e,e&&xe("info","Debug mode enabled")}function xe(e,t,...n){const s=`[SparkVault] ${(new Date).toISOString().split("T")[1].slice(0,-1)} ${t}`;switch(e){case"debug":ke&&console.debug(s,...n);break;case"info":ke&&console.log(s,...n);break;case"warn":ke&&console.warn(s,...n);break;case"error":console.error(s,...n)}}const we={debug:(e,...t)=>xe("debug",e,...t),info:(e,...t)=>xe("info",e,...t),warn:(e,...t)=>xe("warn",e,...t),error:(e,...t)=>xe("error",e,...t),group:e=>{ke&&console.group(`[SparkVault] ${e}`)},groupEnd:()=>{ke&&console.groupEnd()}};class Ce{constructor(e){!function(e){if(!e.accountId)throw new i("accountId is required",{field:"accountId"});if("string"!=typeof e.accountId)throw new i("accountId must be a string",{field:"accountId",received:typeof e.accountId});if(!e.accountId.startsWith("acc_"))throw new i('accountId must start with "acc_"',{field:"accountId",received:e.accountId})}(e),this.config=function(e){return{accountId:e.accountId,timeout:e.timeout??3e4,apiBaseUrl:"https://api.sparkvault.com",identityBaseUrl:"https://api.sparkvault.com/v1/apps/identity",preloadConfig:!1!==e.preloadConfig,backdropBlur:!1!==e.backdropBlur}}(e);const t=new u(this.config);this.identity=new ie(this.config),this.vaults=new fe(this.config,t)}static init(e){return new Ce(e)}static get version(){return"__VERSION__"}}if("undefined"!=typeof window){const e=function(e){if("undefined"==typeof window||"undefined"==typeof document)return null;const t=document.currentScript;if(!t)return null;const n=t.dataset.accountId;if("true"===t.dataset.debug&&ye(!0),!n)return we.debug("No data-account-id attribute, skipping auto-init"),null;we.info("Auto-initializing SDK...");try{const t=e.init({accountId:n});return we.info("SDK initialized successfully"),t}catch(e){return we.error("Failed to initialize SDK:",e),null}}(Ce);window.SparkVault=e??Ce}e.AuthenticationError=n,e.AuthorizationError=s,e.NetworkError=o,e.PopupBlockedError=l,e.SparkVault=Ce,e.SparkVaultError=t,e.TimeoutError=r,e.UserCancelledError=a,e.ValidationError=i,e.default=Ce,e.logger=we,e.setDebugMode=ye,Object.defineProperty(e,"__esModule",{value:!0})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).SparkVault={})}(this,function(e){"use strict";class t extends Error{constructor(e,n,s,i){super(e),this.name="SparkVaultError",this.code=n,this.statusCode=s,this.details=i,Object.setPrototypeOf(this,t.prototype)}}class n extends t{constructor(e,t){super(e,"authentication_error",401,t),this.name="AuthenticationError",Object.setPrototypeOf(this,n.prototype)}}class s extends t{constructor(e,t){super(e,"authorization_error",403,t),this.name="AuthorizationError",Object.setPrototypeOf(this,s.prototype)}}class i extends t{constructor(e,t){super(e,"validation_error",400,t),this.name="ValidationError",Object.setPrototypeOf(this,i.prototype)}}class o extends t{constructor(e,t){super(e,"network_error",void 0,t),this.name="NetworkError",Object.setPrototypeOf(this,o.prototype)}}class r extends t{constructor(e="Request timed out"){super(e,"timeout_error"),this.name="TimeoutError",Object.setPrototypeOf(this,r.prototype)}}class a extends t{constructor(e="User cancelled the operation"){super(e,"user_cancelled"),this.name="UserCancelledError",Object.setPrototypeOf(this,a.prototype)}}class l extends t{constructor(){super("Popup was blocked. Please allow popups for this site.","popup_blocked"),this.name="PopupBlockedError",Object.setPrototypeOf(this,l.prototype)}}const d={maxAttempts:3,baseDelayMs:200,maxDelayMs:5e3,jitterFactor:.2,isRetryable:function(e){if(e instanceof TypeError)return!0;if(e instanceof DOMException&&"AbortError"===e.name)return!0;if("object"==typeof e&&null!==e&&"statusCode"in e){const t=e.statusCode;return t>=500||429===t}return!1}};function c(e,t,n,s){const i=t*Math.pow(2,e),o=Math.min(i,n);return o+o*s*Math.random()}function h(e){return new Promise(t=>setTimeout(t,e))}async function p(e,t={}){const n={...d,...t};let s;for(let t=0;t<n.maxAttempts;t++)try{return await e()}catch(e){s=e;if(t===n.maxAttempts-1||!n.isRetryable(e))throw e;const i=c(t,n.baseDelayMs,n.maxDelayMs,n.jitterFactor);await h(i)}throw s}class u{constructor(e){this.config=e}async request(e,t={}){const{retry:n}=t;if(n){return p(()=>this.executeRequest(e,t),{..."object"==typeof n?n:{},isRetryable:e=>this.isRetryableError(e)})}return this.executeRequest(e,t)}async executeRequest(e,n){const{method:s="GET",headers:i={},body:a,timeout:l=this.config.timeout}=n,d=`${this.config.apiBaseUrl}${e}`,c={"Content-Type":"application/json",Accept:"application/json",...i},h=new AbortController,p=setTimeout(()=>h.abort(),l);try{const e=await fetch(d,{method:s,headers:c,body:a?JSON.stringify(a):void 0,signal:h.signal});clearTimeout(p);const t=await this.parseResponse(e);if(!e.ok)throw this.createErrorFromResponse(e.status,t);return{data:t,status:e.status,headers:e.headers}}catch(e){if(clearTimeout(p),e instanceof t)throw e;if(e instanceof DOMException&&"AbortError"===e.name)throw new r;if(e instanceof TypeError)throw new o("Network request failed. Please check your connection.");throw new t(e instanceof Error?e.message:"Unknown error","unknown_error")}}isRetryableError(e){return e instanceof o||e instanceof r||e instanceof t&&void 0!==e.statusCode&&(e.statusCode>=500||429===e.statusCode)}async parseResponse(e){const n=e.headers.get("content-length");if(204===e.status||"0"===n)return null;const s=e.headers.get("content-type");if(s?.includes("application/json")){return await e.json()}const i=await e.text();throw new t(`Unexpected response format: expected JSON but received ${s??"unknown content type"}`,"invalid_response_format",e.status,{body:i.slice(0,500)})}createErrorFromResponse(e,o){const r="object"==typeof o&&null!==o?o:{},a=("string"==typeof r.message?r.message:null)??("string"==typeof r.error?r.error:null)??"Request failed",l="string"==typeof r.code?r.code:"api_error",d="object"==typeof r.details&&null!==r.details?r.details:void 0;switch(e){case 400:return new i(a,d);case 401:return new n(a,d);case 403:return new s(a,d);default:return new t(a,l,e,d)}}get(e,t){return this.request(e,{...t,method:"GET"})}post(e,t,n){return this.request(e,{...n,method:"POST",body:t})}put(e,t,n){return this.request(e,{...n,method:"PUT",body:t})}patch(e,t,n){return this.request(e,{...n,method:"PATCH",body:t})}delete(e,t){return this.request(e,{...t,method:"DELETE"})}async requestRaw(e,t={}){const{retry:n}=t;if(n){return p(()=>this.executeRequestRaw(e,t),{..."object"==typeof n?n:{},isRetryable:e=>this.isRetryableError(e)})}return this.executeRequestRaw(e,t)}async executeRequestRaw(e,n){const{method:s="GET",headers:i={},body:a,timeout:l=this.config.timeout}=n,d=`${this.config.apiBaseUrl}${e}`,c={"Content-Type":"application/json",...i},h=new AbortController,p=setTimeout(()=>h.abort(),l);try{const e=await fetch(d,{method:s,headers:c,body:a?JSON.stringify(a):void 0,signal:h.signal});if(clearTimeout(p),!e.ok){const t=await e.json().catch(()=>({}));throw this.createErrorFromResponse(e.status,t)}return e.blob()}catch(e){if(clearTimeout(p),e instanceof t)throw e;if(e instanceof DOMException&&"AbortError"===e.name)throw new r;if(e instanceof TypeError)throw new o("Network request failed. Please check your connection.");throw new t(e instanceof Error?e.message:"Unknown error","unknown_error")}}}class m{constructor(e){this.intervalId=null,this.secondsRemaining=e.duration,this.onTick=e.onTick,this.onComplete=e.onComplete}start(){this.stop(),this.onTick(this.secondsRemaining),this.intervalId=setInterval(()=>{this.secondsRemaining--,this.onTick(this.secondsRemaining),this.secondsRemaining<=0&&(this.stop(),this.onComplete())},1e3)}stop(){null!==this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null)}isRunning(){return null!==this.intervalId}getRemainingSeconds(){return this.secondsRemaining}}class v{constructor(e){this.intervalId=null,Number.isFinite(e.expiresAt)?this.expiresAt=e.expiresAt:(console.warn("[ExpirationTimer] Invalid expiresAt:",e.expiresAt),this.expiresAt=0),this.onTick=e.onTick,this.onExpired=e.onExpired}calculateRemaining(){const e=Math.floor(Date.now()/1e3),t=this.expiresAt-e;return!Number.isFinite(t)||t<0?0:Math.ceil(t)}start(){this.stop();const e=this.calculateRemaining();this.onTick(e),e<=0?this.onExpired():this.intervalId=setInterval(()=>{const e=this.calculateRemaining();this.onTick(e),e<=0&&(this.stop(),this.onExpired())},1e3)}stop(){null!==this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null)}isRunning(){return null!==this.intervalId}}function g(e){return function(e){let t="";for(let n=0;n<e.byteLength;n++)t+=String.fromCharCode(e[n]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}(new Uint8Array(e))}class b{constructor(e,t=3e4){this.configCache=null,this.config=e,this.timeoutMs=t}get baseUrl(){return`${this.config.identityBaseUrl}/${this.config.accountId}`}async request(e,t,n){const s=`${this.baseUrl}${t}`,i={"Content-Type":"application/json",Accept:"application/json"},o=new AbortController,r=setTimeout(()=>o.abort(),this.timeoutMs);try{const t=await fetch(s,{method:e,headers:i,body:n?JSON.stringify(n):void 0,signal:o.signal}),r=await t.json();if(!t.ok){const e=r.error||{},n=e.message||r.message||"Request failed",s=e.code||r.code||"api_error";throw new f(n,s,t.status)}return r.data??r}catch(e){if(e instanceof Error&&"AbortError"===e.name)throw new f("Request timed out","timeout",0);throw e}finally{clearTimeout(r)}}async getConfig(){return this.configCache||(this.configCache=this.request("GET","/config")),this.configCache}preloadConfig(){this.configCache||(this.configCache=this.request("GET","/config"))}isConfigPreloaded(){return null!==this.configCache}async checkPasskey(e){return this.request("POST","/passkey/check",{email:e})}buildAuthContextParams(e){if(!e)return{};const t={};return e.authRequestId&&(t.auth_request_id=e.authRequestId),e.simpleMode&&(t.simple_mode={success_url:e.simpleMode.successUrl,failure_url:e.simpleMode.failureUrl,state:e.simpleMode.state}),t}async sendTotp(e){return this.request("POST","/totp/send",{recipient:e.recipient,method:e.method,...this.buildAuthContextParams(e.authContext)})}async verifyTotp(e){return this.request("POST","/totp/verify",{kindling:e.kindling,pin:e.pin,recipient:e.recipient,...this.buildAuthContextParams(e.authContext)})}async startPasskeyRegister(e){const t=await this.request("POST","/passkey/register",{email:e});return{challenge:t.options.challenge,rpId:t.options.rp.id,rpName:t.options.rp.name,userId:t.options.user.id,userName:t.options.user.name,timeout:t.options.timeout,session:t.session}}async completePasskeyRegister(e){const t=e.credential.response;return this.request("POST","/passkey/register/complete",{session:e.session,credential:{id:e.credential.id,rawId:g(e.credential.rawId),type:e.credential.type,response:{clientDataJSON:g(t.clientDataJSON),attestationObject:g(t.attestationObject)}}})}async startPasskeyVerify(e,t){const n=await this.request("POST","/passkey/verify",{email:e,...this.buildAuthContextParams(t)});return{challenge:n.options.challenge,rpId:n.options.rpId,rpName:"SparkVault Identity",timeout:n.options.timeout,allowCredentials:n.options.allowCredentials,session:n.session}}async completePasskeyVerify(e){const t=e.credential.response;return this.request("POST","/passkey/verify/complete",{session:e.session,...this.buildAuthContextParams(e.authContext),credential:{id:e.credential.id,rawId:g(e.credential.rawId),type:e.credential.type,response:{clientDataJSON:g(t.clientDataJSON),authenticatorData:g(t.authenticatorData),signature:g(t.signature),userHandle:t.userHandle?g(t.userHandle):null}}})}getSocialAuthUrl(e,t,n){const s=new URLSearchParams({redirect_uri:t,state:n});return`${this.baseUrl}/social/${e}?${s}`}async sendSparkLink(e,t){return this.request("POST","/sparklink/send",{email:e,type:"verify_identity",openerOrigin:"undefined"!=typeof window?window.location.origin:void 0,...this.buildAuthContextParams(t)})}async checkSparkLinkStatus(e,t){return this.request("GET",`/sparklink/status/${e}`)}}class f extends Error{constructor(e,t,n){super(e),this.name="IdentityApiError",this.code=t,this.statusCode=n}}const k={totp_email:{id:"totp_email",type:"totp",identityType:"email",name:"Email code",description:"Get a one-time code via email",icon:"email"},totp_sms:{id:"totp_sms",type:"totp",identityType:"phone",name:"Text message",description:"Get a one-time code via SMS",icon:"sms"},totp_voice:{id:"totp_voice",type:"totp",identityType:"phone",name:"Voice call",description:"Get a one-time code via phone call",icon:"phone"},passkey:{id:"passkey",type:"passkey",identityType:"email",name:"Passkey",description:"Use biometrics or security key",icon:"passkey"},sparklink:{id:"sparklink",type:"sparklink",identityType:"email",name:"SparkLink",description:"Get an instant sign-in link via email",icon:"link"},social_google:{id:"social_google",type:"social",identityType:"email",name:"Google",description:"Sign in with Google",icon:"google",provider:"google"},social_apple:{id:"social_apple",type:"social",identityType:"email",name:"Apple",description:"Sign in with Apple",icon:"apple",provider:"apple"},social_microsoft:{id:"social_microsoft",type:"social",identityType:"email",name:"Microsoft",description:"Sign in with Microsoft",icon:"microsoft",provider:"microsoft"},social_github:{id:"social_github",type:"social",identityType:"email",name:"GitHub",description:"Sign in with GitHub",icon:"github",provider:"github"},social_facebook:{id:"social_facebook",type:"social",identityType:"email",name:"Facebook",description:"Sign in with Facebook",icon:"facebook",provider:"facebook"},social_linkedin:{id:"social_linkedin",type:"social",identityType:"email",name:"LinkedIn",description:"Sign in with LinkedIn",icon:"linkedin",provider:"linkedin"}};function y(e){return e.map(e=>k[e]).filter(e=>void 0!==e)}class x{constructor(){this._sdkConfig=null,this._viewState={view:"loading"},this._recipient="",this._identityType="email",this._authContext=void 0,this._passkey={hasPasskey:null,isFallbackMode:!1,pendingResult:null},this._totp={kindling:"",method:null},this.listeners=new Set}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){for(const e of this.listeners)e(this._viewState)}get sdkConfig(){return this._sdkConfig}setConfig(e){this._sdkConfig=e}get accountId(){return this._sdkConfig?.accountId??""}getAvailableMethods(){if(!this._sdkConfig)return[];let e=y(this._sdkConfig.methods??[]);return e="phone"===this._identityType?e.filter(e=>"totp"===e.type&&"totp_sms"===e.id):e.filter(e=>"email"===e.identityType),e}isPasskeyEnabled(){return this._sdkConfig?.methods?.includes("passkey")??!1}get viewState(){return this._viewState}setViewState(e){this._viewState=e,this.notify()}get recipient(){return this._recipient}get identityType(){return this._identityType}setIdentity(e,t){this._recipient=e,this._identityType=t}getAllowedIdentityTypes(){return this._sdkConfig?.allowedIdentityTypes??["email"]}get authContext(){return this._authContext}setAuthContext(e){this._authContext=e}get passkey(){return this._passkey}setPasskeyStatus(e){this._passkey.hasPasskey=e}enablePasskeyFallback(){this._passkey.isFallbackMode=!0}setPendingResult(e){this._passkey.pendingResult=e}shouldShowPasskeyPrompt(){return"email"===this._identityType&&(!!this.isPasskeyEnabled()&&(!this.isPasskeyPromptDismissed()&&!0!==this._passkey.hasPasskey))}isPasskeyPromptDismissed(){return null!==function(e){const t=document.cookie.match(new RegExp(`(^| )${e}=([^;]+)`));return t?t[2]:null}(`sv_passkey_prompt_dismissed_${this.accountId}`)}dismissPasskeyPrompt(){!function(e,t,n){const s=new Date;s.setTime(s.getTime()+24*n*60*60*1e3);const i=`expires=${s.toUTCString()}`;document.cookie=`${e}=${t}; ${i}; path=/; SameSite=Lax`}(`sv_passkey_prompt_dismissed_${this.accountId}`,"1",30)}get totp(){return this._totp}setKindling(e){this._totp.kindling=e}setTotpMethod(e){this._totp.method=e}reset(){this._viewState={view:"loading"},this._recipient="",this._identityType="email",this._authContext=void 0,this._passkey.hasPasskey=null,this._passkey.isFallbackMode=!1,this._passkey.pendingResult=null,this._totp.kindling="",this._totp.method=null}}class w{constructor(e,t){this.api=e,this.state=t;const n=e.config;this.baseUrl=n.identityBaseUrl,this.accountId=n.accountId}async checkPasskey(){try{const e=await this.api.checkPasskey(this.state.recipient);return this.state.setPasskeyStatus(e.hasPasskey),{emailValid:e.email_valid,hasPasskey:e.hasPasskey}}catch(e){return console.warn("Passkey check failed:",e),this.state.setPasskeyStatus(null),null}}async register(){const e=await this.openPasskeyPopup("register");return e.success&&this.state.setPasskeyStatus(!0),e}async verify(){return this.openPasskeyPopup("verify")}async openPasskeyPopup(e){return new Promise(t=>{const n=new URLSearchParams({mode:e,email:this.state.recipient,origin:window.location.origin}),s=this.state.authContext;s?.authRequestId&&n.set("auth_request_id",s.authRequestId),s?.simpleMode&&(n.set("simple_mode","true"),n.set("success_url",s.simpleMode.successUrl),n.set("failure_url",s.simpleMode.failureUrl),s.simpleMode.state&&n.set("state",s.simpleMode.state));const i=`${this.baseUrl}/${this.accountId}/passkey/popup?${n}`,o=Math.round((window.screen.width-450)/2),r=Math.round((window.screen.height-500)/2),a=window.open(i,"sparkvault-passkey",`width=450,height=500,left=${o},top=${r},popup=1`);if(!a)return void t({success:!1,error:"Popup was blocked. Please allow popups for this site.",errorType:"popup_blocked"});let l=!1;const d=()=>{window.removeEventListener("message",c),clearTimeout(h),clearInterval(p)},c=e=>{if(!this.isValidMessageOrigin(e.origin))return;const n=e.data;"sparkvault-passkey-result"===n?.type&&(l=!0,d(),n.success&&n.data?t({success:!0,result:{token:n.data.token||"",identity:n.data.identity||this.state.recipient,identityType:n.data.identity_type||"email",redirect:n.data.redirect}}):t(this.handlePopupError(n.error,n.message)))};window.addEventListener("message",c);const h=setTimeout(()=>{l||(l=!0,d(),a.close(),t({success:!1,error:"Passkey operation timed out. Please try again.",errorType:"unknown"}))},6e4),p=setInterval(()=>{a.closed&&!l&&(l=!0,d(),t({success:!1,error:"Authentication was cancelled.",errorType:"cancelled"}))},500)})}isValidMessageOrigin(e){try{const t=new URL(e).hostname;return!("sparkvault.com"!==t&&!t.endsWith(".sparkvault.com"))||"localhost"===t}catch{return!1}}handlePopupError(e,t){return"cancelled"===e?{success:!1,error:t||"Authentication was cancelled. Please try again.",errorType:"cancelled"}:t?.includes("No passkey")||t?.includes("not found")?{success:!1,error:"No passkey found. Create one to continue.",errorType:"not_found"}:{success:!1,error:t||e||"Passkey authentication failed",errorType:"unknown"}}}class C{constructor(e,t){this.api=e,this.state=t}async send(e){try{const t=await this.api.sendTotp({recipient:this.state.recipient,method:e,authContext:this.state.authContext});return this.state.setKindling(t.kindling),this.state.setTotpMethod(e),{success:!0,kindling:t.kindling,expiresAt:t.expires_at}}catch(e){return{success:!1,error:e instanceof Error?e.message:"Failed to send code"}}}async resend(){const e=this.state.totp.method;if(!e)return{success:!1,error:"No TOTP method set"};try{const t=await this.api.sendTotp({recipient:this.state.recipient,method:e,authContext:this.state.authContext});return this.state.setKindling(t.kindling),{success:!0,kindling:t.kindling,expiresAt:t.expires_at}}catch(e){return{success:!1,error:`Failed to resend: ${e instanceof Error?e.message:"Failed to resend code"}`}}}async verify(e){const t=this.state.totp.kindling;if(!t)return{success:!1,error:"No kindling available"};try{const n=await this.api.verifyTotp({kindling:t,pin:e,recipient:this.state.recipient,authContext:this.state.authContext});return n.verified&&n.token?{success:!0,result:{token:n.token,identity:n.identity??this.state.recipient,identityType:n.identity_type??this.state.identityType,redirect:n.redirect}}:(n.kindling&&this.state.setKindling(n.kindling),{success:!1,newKindling:n.kindling,retryAfter:n.retry_after,backoffExpires:n.expires_at,error:"Invalid code. Please try again."})}catch(e){return{success:!1,error:e instanceof Error?e.message:"Verification failed"}}}}class E{constructor(e,t){this.api=e,this.state=t}async send(){try{const e=await this.api.sendSparkLink(this.state.recipient,this.state.authContext);return{success:!0,sparkId:e.sparkId,expiresAt:e.expiresAt}}catch(e){return{success:!1,error:e instanceof Error?e.message:"Failed to send SparkLink"}}}async checkStatus(e){try{const t=await this.api.checkSparkLinkStatus(e);return{verified:t.verified,token:t.token,identity:t.identity,identityType:t.identityType,redirect:t.redirect}}catch{return{verified:!1}}}}const S="sparkvault-identity-styles";function B(e){if(document.getElementById(S))return e.branding,void function(e){const t=document.getElementById("sparkvault-identity-overlay");t&&(!1!==e?t.classList.add("sv-blur"):t.classList.remove("sv-blur"))}(e.backdropBlur);const t=document.createElement("style");t.id=S,t.textContent=function(e){const{branding:t,backdropBlur:n}=e,s="dark"===$(t),i=!1!==n,o={primary:"#4F46E5",primaryHover:"#4338CA",primaryLight:s?"rgba(99, 102, 241, 0.15)":"rgba(79, 70, 229, 0.08)",error:"#DC2626",errorLight:s?"rgba(220, 38, 38, 0.15)":"#FEF2F2",errorBorder:s?"rgba(220, 38, 38, 0.3)":"#FECACA",bg:s?"#0F0F0F":"#FFFFFF",bgSubtle:s?"#171717":"#FAFAFA",bgHover:s?"#1A1A1A":"#F5F5F5",bgInput:s?"#0F0F0F":"#FFFFFF",border:s?"#262626":"#E5E5E5",borderHover:s?"#404040":"#D4D4D4",borderFocus:"#4F46E5",textPrimary:s?"#FAFAFA":"#0A0A0A",textSecondary:s?"#A3A3A3":"#525252",textMuted:"#737373",textOnPrimary:"#FFFFFF",shadowSm:s?"0 1px 2px rgba(0, 0, 0, 0.4)":"0 1px 2px rgba(0, 0, 0, 0.04)",shadowLg:s?"0 8px 24px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05)":"0 8px 24px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.04)"};return`\n /* ========================================\n OVERLAY & MODAL CONTAINER\n ======================================== */\n\n .sv-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, ${s?"0.7":"0.5"});\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n padding: 16px;\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n .sv-overlay.sv-blur {\n backdrop-filter: blur(${i?"8px":"0"});\n -webkit-backdrop-filter: blur(${i?"8px":"0"});\n }\n\n .sv-modal {\n background: ${o.bg};\n border-radius: 16px;\n box-shadow: ${o.shadowLg};\n width: 100%;\n max-width: 400px;\n max-height: calc(100vh - 32px);\n display: flex;\n flex-direction: column;\n color: ${o.textPrimary};\n }\n\n /* ========================================\n HEADER\n ======================================== */\n\n .sv-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-bottom: 1px solid ${o.border};\n background: ${o.bg};\n flex-shrink: 0;\n }\n\n .sv-header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n }\n\n .sv-logo {\n height: 28px;\n width: auto;\n max-width: 140px;\n object-fit: contain;\n flex-shrink: 0;\n }\n\n .sv-company-name {\n font-size: 15px;\n font-weight: 600;\n letter-spacing: -0.01em;\n margin: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .sv-close-btn {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n color: ${o.textMuted};\n transition: background 0.15s ease, color 0.15s ease;\n }\n\n .sv-close-btn:hover {\n background: ${o.bgHover};\n color: ${o.textPrimary};\n }\n\n .sv-close-btn:focus-visible {\n outline: 2px solid ${o.borderFocus};\n outline-offset: 2px;\n }\n\n .sv-close-btn:active {\n transform: scale(0.96);\n }\n\n /* ========================================\n BODY\n ======================================== */\n\n .sv-body {\n padding: 24px 28px;\n overflow-y: auto;\n flex: 1;\n }\n\n /* ========================================\n TYPOGRAPHY\n ======================================== */\n\n .sv-title {\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.02em;\n line-height: 1.3;\n margin: 0 0 6px 0;\n text-align: center;\n color: ${o.textPrimary};\n }\n\n .sv-subtitle {\n font-size: 14px;\n font-weight: 400;\n line-height: 1.5;\n color: ${o.textSecondary};\n margin: 0 0 16px 0;\n text-align: center;\n }\n\n .sv-subtitle strong {\n color: ${o.textPrimary};\n font-weight: 500;\n }\n\n /* ========================================\n FORM ELEMENTS\n ======================================== */\n\n .sv-form-group {\n margin-bottom: 16px;\n }\n\n .sv-label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n letter-spacing: -0.005em;\n margin-bottom: 6px;\n color: ${o.textPrimary};\n }\n\n .sv-input {\n width: 100%;\n padding: 10px 12px;\n font-size: 15px;\n font-weight: 400;\n line-height: 1.4;\n border: 1px solid ${o.border};\n border-radius: 8px;\n background: ${o.bgInput};\n color: ${o.textPrimary};\n box-sizing: border-box;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n }\n\n .sv-input:hover:not(:focus):not(:disabled) {\n border-color: ${o.borderHover};\n }\n\n .sv-input:focus {\n outline: none;\n border-color: ${o.borderFocus};\n box-shadow: 0 0 0 3px ${o.primaryLight};\n }\n\n .sv-input::placeholder {\n color: ${o.textMuted};\n }\n\n .sv-input:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .sv-input.sv-input-error,\n .sv-input.sv-email-input-error {\n border-color: ${o.error};\n }\n\n .sv-input.sv-input-error:focus,\n .sv-input.sv-email-input-error:focus {\n box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.1);\n }\n\n /* ========================================\n BUTTONS\n ======================================== */\n\n .sv-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 10px 16px;\n font-size: 14px;\n font-weight: 500;\n letter-spacing: -0.005em;\n line-height: 1.4;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.15s ease, transform 0.1s ease, box-shadow 0.15s ease;\n box-sizing: border-box;\n }\n\n .sv-btn:focus-visible {\n outline: 2px solid ${o.borderFocus};\n outline-offset: 2px;\n }\n\n .sv-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .sv-btn:active:not(:disabled) {\n transform: scale(0.98);\n }\n\n /* Primary Button */\n .sv-btn-primary,\n .sv-btn-email-primary {\n background: ${o.primary};\n color: ${o.textOnPrimary};\n box-shadow: ${o.shadowSm};\n }\n\n .sv-btn-primary:hover:not(:disabled),\n .sv-btn-email-primary:hover:not(:disabled) {\n background: ${o.primaryHover};\n }\n\n /* Secondary Button */\n .sv-btn-secondary {\n background: ${o.bgSubtle};\n color: ${o.textPrimary};\n border: 1px solid ${o.border};\n }\n\n .sv-btn-secondary:hover:not(:disabled) {\n background: ${o.bgHover};\n border-color: ${o.borderHover};\n }\n\n /* Method Button */\n .sv-btn-method {\n background: ${o.bg};\n border: 1px solid ${o.border};\n color: ${o.textPrimary};\n text-align: left;\n padding: 12px 14px;\n margin-bottom: 8px;\n }\n\n .sv-btn-method:hover:not(:disabled) {\n background: ${o.bgSubtle};\n border-color: ${o.borderHover};\n }\n\n .sv-btn-method:last-child {\n margin-bottom: 0;\n }\n\n /* Error Buttons */\n .sv-btn-error-primary {\n background: ${o.error};\n color: ${o.textOnPrimary};\n box-shadow: ${o.shadowSm};\n }\n\n .sv-btn-error-primary:hover:not(:disabled) {\n background: #B91C1C;\n }\n\n .sv-btn-error-secondary {\n background: ${o.bgSubtle};\n color: ${o.textSecondary};\n border: 1px solid ${o.border};\n }\n\n .sv-btn-error-secondary:hover:not(:disabled) {\n background: ${o.bgHover};\n color: ${o.textPrimary};\n }\n\n /* ========================================\n METHOD SELECTION\n ======================================== */\n\n .sv-method-content {\n display: flex;\n align-items: center;\n gap: 12px;\n width: 100%;\n }\n\n .sv-method-icon {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: ${o.textSecondary};\n }\n\n .sv-method-icon svg {\n width: 20px;\n height: 20px;\n }\n\n .sv-method-text {\n flex: 1;\n min-width: 0;\n }\n\n .sv-method-name {\n font-weight: 500;\n font-size: 14px;\n color: ${o.textPrimary};\n }\n\n .sv-method-description {\n font-size: 12px;\n color: ${o.textMuted};\n margin-top: 1px;\n }\n\n /* ========================================\n TOTP INPUT\n ======================================== */\n\n .sv-totp-input-group {\n display: flex;\n gap: 8px;\n justify-content: center;\n margin-bottom: 16px;\n }\n\n .sv-totp-digit {\n width: 44px;\n height: 52px;\n text-align: center;\n font-size: 20px;\n font-weight: 600;\n letter-spacing: 0;\n border: 1px solid ${o.border};\n border-radius: 8px;\n background: ${o.bgInput};\n color: ${o.textPrimary};\n box-sizing: border-box;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n }\n\n .sv-totp-digit:hover:not(:focus):not(:disabled) {\n border-color: ${o.borderHover};\n }\n\n .sv-totp-digit:focus {\n outline: none;\n border-color: ${o.borderFocus};\n box-shadow: 0 0 0 3px ${o.primaryLight};\n }\n\n .sv-totp-digit:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* ========================================\n BACK LINK\n ======================================== */\n\n .sv-back-link {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: ${o.textSecondary};\n font-size: 13px;\n font-weight: 500;\n text-decoration: none;\n cursor: pointer;\n background: none;\n border: none;\n padding: 0;\n margin-bottom: 16px;\n transition: color 0.15s ease;\n }\n\n .sv-back-link:hover:not(:disabled) {\n color: ${o.primary};\n }\n\n .sv-back-link:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .sv-back-link svg {\n width: 14px;\n height: 14px;\n }\n\n /* ========================================\n ERROR MESSAGE\n ======================================== */\n\n .sv-error {\n background: ${o.errorLight};\n border: 1px solid ${o.errorBorder};\n color: ${o.error};\n padding: 10px 12px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 450;\n margin-bottom: 16px;\n }\n\n /* ========================================\n LOADING STATE\n ======================================== */\n\n .sv-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n gap: 16px;\n }\n\n .sv-spinner {\n width: 28px;\n height: 28px;\n border: 2.5px solid ${o.border};\n border-top-color: ${o.primary};\n border-radius: 50%;\n animation: sv-spin 0.7s linear infinite;\n }\n\n .sv-loading-text {\n font-size: 14px;\n color: ${o.textSecondary};\n margin: 0;\n }\n\n @keyframes sv-spin {\n to { transform: rotate(360deg); }\n }\n\n /* ========================================\n FOOTER\n ======================================== */\n\n .sv-footer {\n padding: 10px 20px;\n border-top: 1px solid ${o.border};\n text-align: center;\n background: ${o.bgSubtle};\n flex-shrink: 0;\n }\n\n .sv-footer-text {\n font-size: 10px;\n color: ${o.textMuted};\n letter-spacing: 0.02em;\n }\n\n .sv-footer-link {\n color: ${o.textSecondary};\n text-decoration: none;\n font-weight: 500;\n transition: color 0.15s ease;\n }\n\n .sv-footer-link:hover {\n color: ${o.primary};\n }\n\n /* ========================================\n IDENTITY INPUT VIEW\n ======================================== */\n\n .sv-email-view {\n display: flex;\n flex-direction: column;\n }\n\n .sv-email-header {\n margin-bottom: 16px;\n }\n\n .sv-email-subtitle {\n font-size: 14px;\n font-weight: 500;\n line-height: 1.4;\n color: ${o.textSecondary};\n margin: 0;\n }\n\n .sv-email-error-container {\n width: 100%;\n }\n\n .sv-email-form {\n width: 100%;\n }\n\n .sv-email-input-wrapper {\n margin-bottom: 14px;\n }\n\n .sv-email-label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n letter-spacing: -0.005em;\n margin-bottom: 6px;\n color: ${o.textPrimary};\n }\n\n .sv-email-input {\n width: 100%;\n padding: 10px 12px;\n font-size: 15px;\n font-weight: 400;\n line-height: 1.4;\n border: 1px solid ${o.border};\n border-radius: 8px;\n background: ${o.bgInput};\n color: ${o.textPrimary};\n box-sizing: border-box;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n }\n\n .sv-email-input:hover:not(:focus):not(:disabled) {\n border-color: ${o.borderHover};\n }\n\n .sv-email-input:focus {\n outline: none;\n border-color: ${o.borderFocus};\n box-shadow: 0 0 0 3px ${o.primaryLight};\n }\n\n .sv-email-input::placeholder {\n color: ${o.textMuted};\n }\n\n .sv-email-input-error {\n border-color: ${o.error};\n }\n\n .sv-email-input-error:focus {\n box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.1);\n }\n\n /* ========================================\n ERROR VIEW\n ======================================== */\n\n .sv-error-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n }\n\n .sv-error-icon-container {\n margin-bottom: 16px;\n }\n\n .sv-error-icon {\n width: 48px;\n height: 48px;\n }\n\n .sv-error-content {\n margin-bottom: 24px;\n }\n\n .sv-error-title {\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.02em;\n margin: 0 0 8px 0;\n color: ${o.textPrimary};\n }\n\n .sv-error-message {\n font-size: 14px;\n line-height: 1.5;\n color: ${o.textSecondary};\n margin: 0 0 16px 0;\n }\n\n .sv-error-code-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n background: ${o.errorLight};\n border: 1px solid ${o.errorBorder};\n border-radius: 6px;\n }\n\n .sv-error-code-label {\n font-size: 10px;\n font-weight: 600;\n color: ${s?"#F87171":"#991B1B"};\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .sv-error-code-value {\n font-size: 11px;\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, 'Cascadia Mono', monospace;\n color: ${o.error};\n font-weight: 500;\n }\n\n .sv-error-actions {\n display: flex;\n gap: 10px;\n width: 100%;\n }\n\n .sv-error-actions .sv-btn {\n flex: 1;\n }\n\n /* ========================================\n TOTP VIEW\n ======================================== */\n\n .sv-resend-container {\n text-align: center;\n margin-top: 16px;\n }\n\n /* ========================================\n PASSKEY VIEW\n ======================================== */\n\n .sv-passkey-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 12px;\n display: block;\n }\n\n .sv-passkey-description {\n font-size: 13px;\n color: ${o.textMuted};\n text-align: center;\n margin: 0 0 16px 0;\n line-height: 1.4;\n }\n\n .sv-fallback-link {\n display: block;\n width: 100%;\n text-align: center;\n color: ${o.textSecondary};\n font-size: 13px;\n font-weight: 400;\n text-decoration: none;\n cursor: pointer;\n background: none;\n border: none;\n padding: 12px 0 0 0;\n margin-top: 8px;\n transition: color 0.15s ease;\n }\n\n .sv-fallback-link:hover {\n color: ${o.primary};\n text-decoration: underline;\n }\n\n /* ========================================\n PASSKEY PROMPT VIEW\n ======================================== */\n\n .sv-passkey-prompt-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 12px;\n display: block;\n color: ${o.primary};\n }\n\n .sv-passkey-prompt-benefits {\n background: ${o.bgSubtle};\n border-radius: 8px;\n padding: 10px 14px;\n margin-bottom: 16px;\n }\n\n .sv-passkey-prompt-benefits ul {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n .sv-passkey-prompt-benefits li {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n color: ${o.textSecondary};\n padding: 6px 0;\n }\n\n .sv-passkey-prompt-benefits li svg {\n width: 16px;\n height: 16px;\n color: ${o.primary};\n flex-shrink: 0;\n }\n\n .sv-skip-link {\n display: block;\n width: 100%;\n text-align: center;\n color: ${o.textMuted};\n font-size: 13px;\n font-weight: 400;\n text-decoration: none;\n cursor: pointer;\n background: none;\n border: none;\n padding: 12px 0 0 0;\n transition: color 0.15s ease;\n }\n\n .sv-skip-link:hover {\n color: ${o.textSecondary};\n text-decoration: underline;\n }\n\n /* ========================================\n SPARKLINK WAITING VIEW\n ======================================== */\n\n .sv-sparklink-icon-container {\n display: flex;\n justify-content: center;\n margin-bottom: 16px;\n }\n\n .sv-sparklink-icon {\n width: 56px;\n height: 56px;\n color: ${o.primary};\n background: ${o.primaryLight};\n padding: 14px;\n border-radius: 50%;\n box-sizing: content-box;\n }\n\n .sv-sparklink-waiting {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n background: ${o.bgSubtle};\n border-radius: 10px;\n padding: 14px 18px;\n margin: 16px 0;\n }\n\n .sv-spinner-small {\n width: 18px;\n height: 18px;\n border: 2px solid ${o.border};\n border-top-color: ${o.primary};\n border-radius: 50%;\n animation: sv-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n\n .sv-sparklink-waiting-text {\n font-size: 13px;\n font-weight: 450;\n color: ${o.textSecondary};\n }\n\n .sv-sparklink-countdown {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n font-size: 13px;\n color: ${o.textMuted};\n margin-bottom: 20px;\n }\n\n .sv-sparklink-countdown-time {\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, 'Cascadia Mono', monospace;\n font-weight: 600;\n font-size: 14px;\n color: ${o.textPrimary};\n min-width: 40px;\n }\n\n .sv-sparklink-fallback {\n text-align: center;\n margin-top: 12px;\n padding-top: 16px;\n border-top: 1px solid ${o.border};\n }\n\n .sv-sparklink-fallback .sv-back-link {\n margin-bottom: 0;\n font-size: 12px;\n color: ${o.textMuted};\n }\n\n .sv-sparklink-fallback .sv-back-link:hover:not(:disabled) {\n color: ${o.textSecondary};\n }\n\n /* SparkLink Expired State */\n .sv-sparklink-expired {\n flex-direction: column;\n gap: 12px;\n background: ${o.bgSubtle};\n border: 1px solid ${o.border};\n }\n\n .sv-sparklink-expired-icon {\n display: flex;\n justify-content: center;\n }\n\n .sv-sparklink-expired-icon svg {\n width: 48px;\n height: 48px;\n }\n\n .sv-sparklink-expired-text {\n font-size: 14px;\n font-weight: 500;\n color: ${o.textSecondary};\n text-align: center;\n }\n\n .sv-expired-icon {\n width: 48px;\n height: 48px;\n }\n\n /* ========================================\n DIVIDER\n ======================================== */\n\n .sv-divider {\n display: flex;\n align-items: center;\n margin: 20px 0;\n }\n\n .sv-divider::before,\n .sv-divider::after {\n content: '';\n flex: 1;\n height: 1px;\n background: ${o.border};\n }\n\n .sv-divider-text {\n padding: 0 12px;\n font-size: 12px;\n color: ${o.textMuted};\n }\n\n /* ========================================\n ACCESSIBILITY\n ======================================== */\n\n .sv-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* ========================================\n INLINE CONTAINER\n ======================================== */\n\n .sv-inline-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n background: ${o.bg};\n color: ${o.textPrimary};\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n border-radius: 12px;\n border: 1px solid ${o.border};\n }\n\n .sv-inline-header {\n border-bottom: 1px solid ${o.border};\n }\n\n .sv-inline-body {\n display: flex;\n flex-direction: column;\n justify-content: center;\n min-height: 200px;\n flex: 1;\n }\n\n /* ========================================\n RESPONSIVE\n ======================================== */\n\n @media (max-width: 480px) {\n .sv-modal {\n max-width: 100%;\n max-height: 100%;\n border-radius: 0;\n }\n\n .sv-overlay {\n padding: 0;\n }\n\n .sv-body {\n padding: 24px 28px;\n }\n\n .sv-totp-digit {\n width: 40px;\n height: 48px;\n font-size: 18px;\n }\n\n .sv-error-actions {\n flex-direction: column;\n }\n }\n\n @media (max-width: 360px) {\n .sv-totp-input-group {\n gap: 6px;\n }\n\n .sv-totp-digit {\n width: 36px;\n height: 44px;\n font-size: 16px;\n }\n }\n `}(e),document.head.appendChild(t)}function $(e){return e?.themeMode??"light"}function T(e,t=16,n=16){const s=document.createElementNS("http://www.w3.org/2000/svg","svg");return s.setAttribute("viewBox",e),s.setAttribute("width",String(t)),s.setAttribute("height",String(n)),s.setAttribute("fill","none"),s.setAttribute("xmlns","http://www.w3.org/2000/svg"),s}function M(e,t={}){const n=document.createElementNS("http://www.w3.org/2000/svg","path");n.setAttribute("d",e);for(const[e,s]of Object.entries(t))n.setAttribute(e,s);return n}function L(e,t,n,s={}){const i=document.createElementNS("http://www.w3.org/2000/svg","circle");i.setAttribute("cx",String(e)),i.setAttribute("cy",String(t)),i.setAttribute("r",String(n));for(const[e,t]of Object.entries(s))i.setAttribute(e,t);return i}function I(){const e=T("0 0 16 16");return e.appendChild(M("M13.25 4.75L6 12 2.75 8.75",{stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"})),e}function H(){const e=T("0 0 16 16");return e.appendChild(M("M10 12L6 8L10 4",{stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"})),e}function A(){const e=T("0 0 20 20",20,20);return e.appendChild(M("M15 5L5 15M5 5L15 15",{stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"})),e}function P(){const e=T("0 0 56 56",56,56);e.classList.add("sv-passkey-icon"),e.appendChild(L(28,28,26,{fill:"#EEF2FF",stroke:"#E0E7FF","stroke-width":"1"}));const t=document.createElementNS("http://www.w3.org/2000/svg","g");t.setAttribute("transform","translate(28, 28)");const n={stroke:"#4F46E5","stroke-width":"2.5","stroke-linecap":"round","stroke-linejoin":"round",fill:"none"};return t.appendChild(L(-6,-6,6,n)),t.appendChild(M("M-1.5 -1.5L10 10",n)),t.appendChild(M("M6 6L6 10",n)),t.appendChild(M("M10 10L10 14",n)),e.appendChild(t),e}function F(e){switch(e){case"email":default:return N();case"phone":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(M("M18 14v2.5a1.5 1.5 0 01-1.64 1.5 14.85 14.85 0 01-6.47-2.3 14.63 14.63 0 01-4.5-4.5A14.85 14.85 0 013.1 4.64 1.5 1.5 0 014.59 3H7.1a1.5 1.5 0 011.5 1.29 9.63 9.63 0 00.52 2.11 1.5 1.5 0 01-.34 1.58l-.95.95a12 12 0 004.5 4.5l.95-.95a1.5 1.5 0 011.58-.34 9.63 9.63 0 002.11.52A1.5 1.5 0 0118 14z")),e}();case"fingerprint":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(M("M2 10c0-4.42 3.58-8 8-8 2.65 0 5 1.29 6.47 3.28")),e.appendChild(M("M4 16.58c.42-.35.79-.76 1.1-1.22")),e.appendChild(M("M14.41 17.51c.09-.45.32-1.73.38-2.26")),e.appendChild(M("M10 8.33a1.5 1.5 0 00-1.5 1.5c0 .77-.08 1.88-.2 3")),e.appendChild(M("M7.21 18.33c.16-.5.34-1 .43-1.5")),e.appendChild(M("M11.67 10.93c0 1.79 0 4.79-.75 6.65")),e.appendChild(M("M17.33 13.33c.15-1.5.1-4.02 0-4.5")),e.appendChild(M("M7.5 5.67a4.5 4.5 0 016.75 3.9c0 .35 0 .88-.02 1.5")),e}();case"link":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(M("M8.33 10.83a3.75 3.75 0 005.66.41l2.25-2.25a3.75 3.75 0 00-5.3-5.3l-1.29 1.28")),e.appendChild(M("M11.67 9.17a3.75 3.75 0 00-5.66-.41l-2.25 2.25a3.75 3.75 0 005.3 5.3l1.28-1.28")),e}();case"google":return function(){const e=T("0 0 20 20",20,20);return e.appendChild(M("M18.8 10.21c0-.65-.06-1.28-.17-1.88H10v3.55h4.93c-.21 1.14-.87 2.11-1.84 2.76v2.3h2.98c1.73-1.6 2.73-3.96 2.73-6.73z",{fill:"#4285F4"})),e.appendChild(M("M10 19.17c2.48 0 4.55-.82 6.07-2.23l-2.98-2.3c-.82.55-1.86.88-3.09.88-2.38 0-4.4-1.6-5.13-3.77H1.82v2.37c1.52 3.02 4.6 5.05 8.18 5.05z",{fill:"#34A853"})),e.appendChild(M("M4.87 11.74c-.18-.55-.29-1.14-.29-1.74s.1-1.19.29-1.74V5.89H1.82c-.6 1.23-.94 2.6-.94 4.11s.34 2.88.94 4.11l3.05-2.37z",{fill:"#FBBC05"})),e.appendChild(M("M10 4.48c1.35 0 2.55.47 3.5 1.37l2.63-2.63C14.53 1.74 12.47.83 10 .83c-3.58 0-6.66 2.03-8.18 5.05l3.05 2.37c.72-2.17 2.75-3.77 5.13-3.77z",{fill:"#EA4335"})),e}();case"apple":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("fill","currentColor"),e.appendChild(M("M14.21 16.9c-.82.79-1.71.67-2.57.29-.91-.38-1.74-.4-2.7 0-1.2.51-1.83.37-2.55-.29C2.7 12.71 3.3 6.33 7.54 6.09c1.13.06 1.91.61 2.57.66.98-.2 1.92-.78 2.97-.7 1.26.1 2.21.6 2.83 1.5-2.6 1.56-1.98 4.99.4 5.94-.48 1.25-1.09 2.49-2.11 3.41h.01zM10.02 6.04c-.12-1.86 1.38-3.39 3.11-3.54.24 2.15-1.95 3.75-3.11 3.54z")),e}();case"microsoft":return function(){const e=T("0 0 20 20",20,20);return e.appendChild(M("M1 1h8.33v8.33H1z",{fill:"#F25022"})),e.appendChild(M("M1 10.67h8.33V19H1z",{fill:"#00A4EF"})),e.appendChild(M("M10.67 1H19v8.33h-8.33z",{fill:"#7FBA00"})),e.appendChild(M("M10.67 10.67H19V19h-8.33z",{fill:"#FFB900"})),e}();case"github":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("fill","currentColor"),e.appendChild(M("M10 0C4.48 0 0 4.48 0 10c0 4.42 2.86 8.17 6.84 9.49.5.09.68-.22.68-.48 0-.24-.01-1.02-.01-1.86-2.51.46-3.16-.61-3.36-1.18-.11-.29-.6-1.18-1.02-1.41-.35-.19-.85-.65-.01-.66.79-.01 1.35.72 1.54 1.02.9 1.51 2.34 1.09 2.91.83.09-.65.35-1.09.64-1.34-2.22-.25-4.55-1.11-4.55-4.94 0-1.09.39-1.99 1.02-2.69-.1-.25-.45-1.27.1-2.65 0 0 .84-.27 2.75 1.02.8-.22 1.65-.33 2.5-.33s1.7.11 2.5.33c1.91-1.3 2.75-1.02 2.75-1.02.55 1.38.2 2.4.1 2.65.64.7 1.02 1.59 1.02 2.69 0 3.84-2.34 4.69-4.57 4.94.36.31.67.91.67 1.85 0 1.34-.01 2.41-.01 2.75 0 .27.18.58.69.48C17.14 18.16 20 14.42 20 10c0-5.52-4.48-10-10-10z")),e}();case"facebook":return function(){const e=T("0 0 20 20",20,20);return e.appendChild(M("M20 10.06c0-5.52-4.48-10-10-10S0 4.53 0 10.06c0 4.99 3.66 9.13 8.44 9.88v-6.99H5.9v-2.89h2.54V7.85c0-2.51 1.49-3.89 3.78-3.89 1.09 0 2.24.2 2.24.2v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56v1.88h2.77l-.44 2.89h-2.33v6.99c4.78-.75 8.44-4.89 8.44-9.88z",{fill:"#1877F2"})),e}();case"linkedin":return function(){const e=T("0 0 20 20",20,20);return e.appendChild(M("M17.04 17.04h-2.96v-4.64c0-1.11-.02-2.53-1.54-2.53-1.55 0-1.78 1.2-1.78 2.45v4.73H7.8V7.5h2.84v1.3h.04c.4-.75 1.36-1.54 2.81-1.54 3 0 3.56 1.98 3.56 4.55v5.24zM4.45 6.19c-.95 0-1.72-.77-1.72-1.72s.77-1.72 1.72-1.72 1.72.77 1.72 1.72-.77 1.72-1.72 1.72zm1.48 10.85H2.96V7.5h2.97v9.54zM18.52 0H1.47C.66 0 0 .65 0 1.44v17.12c0 .79.66 1.44 1.48 1.44h17.04c.82 0 1.48-.65 1.48-1.44V1.44c0-.79-.66-1.44-1.48-1.44z",{fill:"#0A66C2"})),e}();case"building":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(M("M2.5 17.5h15")),e.appendChild(M("M4.17 17.5V5.83L10.83 2.5v15")),e.appendChild(M("M15.83 17.5V9.17l-5-3.33")),e.appendChild(M("M7.5 7.5v.01")),e.appendChild(M("M7.5 10v.01")),e.appendChild(M("M7.5 12.5v.01")),e.appendChild(M("M7.5 15v.01")),e}();case"users":return function(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(M("M14.17 17.5v-1.67a3.33 3.33 0 00-3.34-3.33H4.17a3.33 3.33 0 00-3.34 3.33v1.67")),e.appendChild(L(7.5,5.83,3.33,{stroke:"currentColor","stroke-width":"1.5",fill:"none"})),e.appendChild(M("M19.17 17.5v-1.67a3.33 3.33 0 00-2.5-3.22")),e.appendChild(M("M13.33 2.6a3.33 3.33 0 010 6.46")),e}()}}function N(){const e=T("0 0 20 20",20,20);return e.setAttribute("stroke","currentColor"),e.setAttribute("stroke-width","1.5"),e.setAttribute("stroke-linecap","round"),e.setAttribute("stroke-linejoin","round"),e.appendChild(M("M3 4h14c.55 0 1 .45 1 1v10c0 .55-.45 1-1 1H3c-.55 0-1-.45-1-1V5c0-.55.45-1 1-1z")),e.appendChild(M("M18 5l-8 5.5L2 5")),e}const _={companyName:"",logoLight:null,logoDark:null};class z{constructor(){this.elements=null,this.onCloseCallback=null,this.keydownHandler=null,this.overlayClickHandler=null,this.closeBtnClickHandler=null,this.closeBtn=null,this.backdropBlur=!0,this.effectiveTheme="light",this.headerElement=null}createLoading(e,t){return this.create({branding:_,backdropBlur:e.backdropBlur},t)}updateBranding(e){if(!this.elements)return;this.effectiveTheme=$(e);const t=this.createHeader(e);this.headerElement?.parentNode&&this.headerElement.parentNode.replaceChild(t,this.headerElement),this.headerElement=t}updateBackdropBlur(e){this.elements&&(this.backdropBlur=e,e?this.elements.overlay.classList.add("sv-blur"):this.elements.overlay.classList.remove("sv-blur"))}create(e,t){if(this.elements)return this.elements;this.onCloseCallback=t,this.backdropBlur=!1!==e.backdropBlur,this.effectiveTheme=$(e.branding);B({branding:e.branding,backdropBlur:e.backdropBlur});const n=document.createElement("div");n.id="sparkvault-identity-overlay",n.className=this.backdropBlur?"sv-overlay sv-blur":"sv-overlay",n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-labelledby","sv-modal-title");const s=document.createElement("div");s.id="sparkvault-identity-modal",s.className="sv-modal";const i=this.createHeader(e.branding);this.headerElement=i;const o=document.createElement("div");o.className="sv-body";const r=document.createElement("div");r.className="sv-footer";const a=document.createElement("span");a.className="sv-footer-text",a.appendChild(document.createTextNode("Secured by "));const l=document.createElement("a");l.href="https://sparkvault.com",l.target="_blank",l.rel="noopener noreferrer",l.className="sv-footer-link",l.style.color="inherit",l.style.textDecoration="none",l.textContent="SparkVault",a.appendChild(l),r.appendChild(a),s.appendChild(i),s.appendChild(o),s.appendChild(r),n.appendChild(s),this.overlayClickHandler=e=>{e.target===n&&this.handleClose()},n.addEventListener("click",this.overlayClickHandler),this.keydownHandler=e=>{"Escape"===e.key&&this.handleClose()},document.addEventListener("keydown",this.keydownHandler),document.body.appendChild(n),document.body.style.overflow="hidden",this.elements={overlay:n,modal:s,header:i,body:o,footer:r};const d=o.querySelector("input, button");return d instanceof HTMLElement&&d.focus(),this.elements}createHeader(e){const t=document.createElement("div");t.className="sv-header";const n=document.createElement("div");n.className="sv-header-title";const s="dark"===this.effectiveTheme?e.logoDark||e.logoLight:e.logoLight||e.logoDark;if(s){const t=document.createElement("img");t.className="sv-logo",t.src=s,t.alt=e.companyName,n.appendChild(t);const i=document.createElement("span");i.className="sv-sr-only",i.id="sv-modal-title",i.textContent=e.companyName,n.appendChild(i)}else{const t=document.createElement("h2");t.className="sv-company-name",t.id="sv-modal-title",t.textContent=e.companyName,n.appendChild(t)}return this.closeBtn=document.createElement("button"),this.closeBtn.className="sv-close-btn",this.closeBtn.setAttribute("aria-label","Close"),this.closeBtn.appendChild(A()),this.closeBtnClickHandler=()=>this.handleClose(),this.closeBtn.addEventListener("click",this.closeBtnClickHandler),t.appendChild(n),t.appendChild(this.closeBtn),t}handleClose(){this.onCloseCallback&&this.onCloseCallback()}getBody(){return this.elements?.body??null}isOpen(){return null!==this.elements}destroy(){this.keydownHandler&&(document.removeEventListener("keydown",this.keydownHandler),this.keydownHandler=null),this.elements&&this.overlayClickHandler&&(this.elements.overlay.removeEventListener("click",this.overlayClickHandler),this.overlayClickHandler=null),this.closeBtn&&this.closeBtnClickHandler&&(this.closeBtn.removeEventListener("click",this.closeBtnClickHandler),this.closeBtnClickHandler=null,this.closeBtn=null),this.elements&&(this.elements.overlay.remove(),this.elements=null),this.headerElement=null,document.body.style.overflow="",this.onCloseCallback=null}}function D(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function R(e,t){const n=document.createElement("div");return e&&(n.className=e),n}function O(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function U(e){const t=document.createElement("div");return t.className="sv-error",t.setAttribute("role","alert"),t.textContent=e,t}function V(e){const t=e.trim().toLowerCase();return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)&&t.length<=254?t:null}function j(e){const t=e.trim().startsWith("+"),n=e.replace(/\D/g,"");return 10===n.length?`+1${n}`:11===n.length&&n.startsWith("1")||t&&n.length>=10&&n.length<=15?`+${n}`:null}function q(e){const t=e.replace(/\D/g,""),n=t.startsWith("1")&&t.length>10?t.slice(1):t;return 0===n.length?"":n.length<=3?`(${n}`:n.length<=6?`(${n.slice(0,3)}) ${n.slice(3)}`:`(${n.slice(0,3)}) ${n.slice(3,6)}-${n.slice(6,10)}`}class K{constructor(e={}){this.message=e.message??"Loading..."}render(){const e=R("sv-loading"),t=document.createElement("div");t.className="sv-spinner",t.setAttribute("role","status"),t.setAttribute("aria-label","Loading");const n=document.createElement("p");return n.className="sv-loading-text",n.textContent=this.message,e.appendChild(t),e.appendChild(n),e}destroy(){}}class W{constructor(e){this.inputElement=null,this.labelElement=null,this.submitButton=null,this.errorContainer=null,this.focusTimeoutId=null,this.detectedType="email",this.props=e,this.boundHandleInput=()=>this.handleInput(),this.boundHandleKeyDown=e=>{"Enter"===e.key&&(e.preventDefault(),this.handleSubmit())},this.boundHandleSubmit=()=>this.handleSubmit()}get allowsBoth(){return this.props.allowedTypes.includes("email")&&this.props.allowedTypes.includes("phone")}get allowsEmail(){return this.props.allowedTypes.includes("email")}get allowsPhone(){return this.props.allowedTypes.includes("phone")}render(){const e=R("sv-email-view"),t=R("sv-email-header"),n=document.createElement("p");n.className="sv-email-subtitle",n.textContent=this.getSubtitleText(),t.appendChild(n),e.appendChild(t),this.errorContainer=R("sv-email-error-container"),this.props.error&&this.errorContainer.appendChild(U(this.props.error)),e.appendChild(this.errorContainer);const s=R("sv-email-form"),i=R("sv-email-input-wrapper");return this.labelElement=document.createElement("label"),this.labelElement.className="sv-email-label",this.labelElement.htmlFor="sv-identity-input",this.labelElement.textContent=this.getLabelText(),this.inputElement=document.createElement("input"),this.inputElement.type=this.allowsPhone&&!this.allowsEmail?"tel":"text",this.inputElement.id="sv-identity-input",this.inputElement.className="sv-email-input",this.inputElement.placeholder=this.getPlaceholderText(),this.inputElement.autocomplete=this.allowsBoth?"on":this.allowsEmail?"email":"tel",this.inputElement.required=!0,this.inputElement.maxLength=254,this.props.initialValue&&(this.inputElement.value=this.props.initialValue,this.detectAndUpdateType(this.props.initialValue)),this.inputElement.addEventListener("input",this.boundHandleInput),this.inputElement.addEventListener("keydown",this.boundHandleKeyDown),i.appendChild(this.labelElement),i.appendChild(this.inputElement),s.appendChild(i),this.submitButton=document.createElement("button"),this.submitButton.className="sv-btn sv-btn-email-primary",this.submitButton.textContent="Continue",this.submitButton.addEventListener("click",this.boundHandleSubmit),s.appendChild(this.submitButton),e.appendChild(s),this.focusTimeoutId=setTimeout(()=>this.inputElement?.focus(),50),e}getSubtitleText(){return this.allowsBoth?"Enter your email or phone to continue":this.allowsPhone?"Enter your phone number to continue":"Enter your email to continue"}getLabelText(){return this.allowsBoth?"phone"===this.detectedType?"Phone number":"Email address":this.allowsPhone?"Phone number":"Email address"}getPlaceholderText(){return this.allowsBoth?"you@example.com or (555) 123-4567":this.allowsPhone?"(555) 123-4567":"you@example.com"}handleInput(){if(this.clearError(),!this.inputElement)return;const e=this.inputElement.value;if(this.allowsBoth){const t="phone"===this.detectedType;if(this.detectAndUpdateType(e),"phone"===this.detectedType){const t=this.inputElement.selectionStart??e.length,n=q(e);if(n!==e){this.inputElement.value=n;const s=Math.min(t+(n.length-e.length),n.length);this.inputElement.setSelectionRange(s,s)}}t!==("phone"===this.detectedType)&&this.labelElement&&(this.labelElement.textContent=this.getLabelText())}else if(this.allowsPhone){const t=this.inputElement.selectionStart??e.length,n=q(e);if(n!==e){this.inputElement.value=n;const s=Math.min(t+(n.length-e.length),n.length);this.inputElement.setSelectionRange(s,s)}}}detectAndUpdateType(e){!function(e){const t=e.trim();return!!t&&/^[+(\d]/.test(t)&&!/[@.]/.test(t)}(e)?this.detectedType="email":this.detectedType="phone"}clearError(){this.errorContainer&&O(this.errorContainer),this.inputElement?.classList.remove("sv-email-input-error")}showError(e){this.errorContainer&&(O(this.errorContainer),this.errorContainer.appendChild(U(e))),this.inputElement?.classList.add("sv-email-input-error"),this.inputElement?.focus()}handleSubmit(){if(!this.inputElement||!this.submitButton)return;const e=this.inputElement.value;if(this.allowsBoth)if("phone"===this.detectedType){const t=j(e);if(!t)return void this.showError("Please enter a valid phone number");this.submitButton.disabled=!0,this.submitButton.textContent="Please wait...",this.props.onSubmit({value:t,type:"phone"})}else{const t=V(e);if(!t)return void this.showError("Please enter a valid email address");this.submitButton.disabled=!0,this.submitButton.textContent="Please wait...",this.props.onSubmit({value:t,type:"email"})}else if(this.allowsPhone){const t=j(e);if(!t)return void this.showError("Please enter a valid phone number");this.submitButton.disabled=!0,this.submitButton.textContent="Please wait...",this.props.onSubmit({value:t,type:"phone"})}else{const t=V(e);if(!t)return void this.showError("Please enter a valid email address");this.submitButton.disabled=!0,this.submitButton.textContent="Please wait...",this.props.onSubmit({value:t,type:"email"})}}destroy(){null!==this.focusTimeoutId&&(clearTimeout(this.focusTimeoutId),this.focusTimeoutId=null),this.inputElement&&(this.inputElement.removeEventListener("input",this.boundHandleInput),this.inputElement.removeEventListener("keydown",this.boundHandleKeyDown)),this.submitButton&&this.submitButton.removeEventListener("click",this.boundHandleSubmit)}}class G{constructor(e){this.backLink=null,this.methodButtons=[],this.methodClickHandlers=new Map,this.props=e,this.boundHandleBack=()=>this.props.onBack()}render(){const e=R();this.backLink=this.createBackLink();const t=document.createElement("h3");t.className="sv-title",t.textContent="Choose how to sign in";const n=document.createElement("p");n.className="sv-subtitle",n.textContent=D(this.props.email);const s=R();for(const e of this.props.methods){const t=this.createMethodButton(e);this.methodButtons.push(t),s.appendChild(t)}return e.appendChild(this.backLink),e.appendChild(t),e.appendChild(n),e.appendChild(s),e}createBackLink(){const e=document.createElement("button");return e.className="sv-back-link",e.appendChild(H()),e.appendChild(document.createTextNode(" Change email")),e.addEventListener("click",this.boundHandleBack),e}createMethodButton(e){const t=document.createElement("button");t.className="sv-btn sv-btn-method";const n=R("sv-method-content"),s=this.getMethodIcon(e);n.appendChild(s);const i=R("sv-method-text"),o=document.createElement("div");o.className="sv-method-name",o.textContent=e.name;const r=document.createElement("div");r.className="sv-method-description",r.textContent=e.description,i.appendChild(o),i.appendChild(r),n.appendChild(i),t.appendChild(n);const a=()=>this.props.onSelectMethod(e);return this.methodClickHandlers.set(t,a),t.addEventListener("click",a),t}getMethodIcon(e){const t=R("sv-method-icon");return t.appendChild(F(e.icon)),t}destroy(){this.backLink&&this.backLink.removeEventListener("click",this.boundHandleBack);for(const e of this.methodButtons){const t=this.methodClickHandlers.get(e);t&&e.removeEventListener("click",t)}this.methodClickHandlers.clear(),this.methodButtons=[]}}class Y{constructor(e){this.inputElements=[],this.submitButton=null,this.resendButton=null,this.backLink=null,this.errorContainer=null,this.resendTimer=null,this.backoffTimer=null,this.backoffSeconds=0,this.focusTimeoutId=null,this.autoSubmitTimeoutId=null,this.isSubmitting=!1,this.inputHandlers=new Map,this.props=e,this.boundHandleBack=()=>this.props.onBack(),this.boundHandleResend=()=>this.handleResend(),this.boundHandleSubmit=()=>this.handleSubmit()}render(){const e=R();this.backLink=this.createBackLink();const t=document.createElement("h3");t.className="sv-title",t.textContent="Enter verification code";const n="email"===this.props.method?"email":"phone",s=document.createElement("p");s.className="sv-subtitle",s.innerHTML=`We sent a 6-digit code to your ${n}<br><strong>${D(this.props.email)}</strong>`,this.errorContainer=R(),this.props.error&&this.errorContainer.appendChild(U(this.props.error));const i=this.createInputGroup();if(this.submitButton=document.createElement("button"),this.submitButton.className="sv-btn sv-btn-primary",this.submitButton.textContent="Verify",this.submitButton.addEventListener("click",this.boundHandleSubmit),this.props.backoffExpires){Math.max(0,Math.ceil(this.props.backoffExpires-Date.now()/1e3))>0&&this.startBackoff(this.props.backoffExpires)}const o=R("sv-resend-container");return this.resendButton=document.createElement("button"),this.resendButton.className="sv-back-link",this.resendButton.textContent="Resend code",this.resendButton.addEventListener("click",this.boundHandleResend),o.appendChild(this.resendButton),e.style.display="flex",e.style.flexDirection="column",e.appendChild(t),e.appendChild(s),e.appendChild(this.errorContainer),e.appendChild(i),e.appendChild(this.submitButton),e.appendChild(o),e.appendChild(this.backLink),this.focusTimeoutId=setTimeout(()=>this.inputElements[0]?.focus(),50),e}createBackLink(){const e=document.createElement("button");return e.className="sv-back-link",e.style.order="-1",e.appendChild(H()),e.appendChild(document.createTextNode(" Choose another method")),e.addEventListener("click",this.boundHandleBack),e}createInputGroup(){const e=R("sv-totp-input-group");for(let t=0;t<6;t++){const n=document.createElement("input");n.type="text",n.inputMode="numeric",n.pattern="[0-9]*",n.maxLength=1,n.className="sv-totp-digit",n.autocomplete="one-time-code",n.setAttribute("aria-label",`Digit ${t+1} of 6`);const s={input:e=>this.handleInput(e,t),keydown:e=>this.handleKeyDown(e,t),paste:e=>this.handlePaste(e),focus:()=>n.select()};this.inputHandlers.set(n,s),n.addEventListener("input",s.input),n.addEventListener("keydown",s.keydown),n.addEventListener("paste",s.paste),n.addEventListener("focus",s.focus),this.inputElements.push(n),e.appendChild(n)}return e}handleInput(e,t){const n=e.target,s=n.value.replace(/\D/g,"");s.length>0?(n.value=s[0],this.clearError(),t<5?this.inputElements[t+1].focus():this.checkAutoSubmit()):n.value=""}handleKeyDown(e,t){const n=e.target;"Backspace"===e.key?""===n.value&&t>0&&(this.inputElements[t-1].focus(),this.inputElements[t-1].value=""):"ArrowLeft"===e.key&&t>0?this.inputElements[t-1].focus():"ArrowRight"===e.key&&t<5?this.inputElements[t+1].focus():"Enter"===e.key&&this.handleSubmit()}handlePaste(e){e.preventDefault();const t=(e.clipboardData?.getData("text")??"").replace(/\D/g,"").slice(0,6);for(let e=0;e<t.length;e++)this.inputElements[e]&&(this.inputElements[e].value=t[e]);if(t.length>0){this.clearError();const e=Math.min(t.length,5);this.inputElements[e].focus()}6===t.length&&this.checkAutoSubmit()}checkAutoSubmit(){6===this.getCode().length&&(this.autoSubmitTimeoutId=setTimeout(()=>this.handleSubmit(),100))}getCode(){return this.inputElements.map(e=>e.value).join("")}clearError(){this.errorContainer&&O(this.errorContainer),this.inputElements.forEach(e=>e.classList.remove("sv-input-error"))}showError(e){this.errorContainer&&(O(this.errorContainer),this.errorContainer.appendChild(U(e))),this.inputElements.forEach(e=>e.classList.add("sv-input-error")),this.inputElements[0]?.focus()}handleSubmit(){if(!this.submitButton)return;if(this.isSubmitting)return;if(this.backoffSeconds>0)return;const e=function(e){const t=e.replace(/\D/g,"");return 6===t.length?t:null}(this.getCode());e?(null!==this.autoSubmitTimeoutId&&(clearTimeout(this.autoSubmitTimeoutId),this.autoSubmitTimeoutId=null),this.isSubmitting=!0,this.submitButton.disabled=!0,this.submitButton.textContent="Verifying...",this.props.onSubmit(e)):this.showError("Please enter the complete 6-digit code")}startBackoff(e){this.disableInputs(!0),this.backoffTimer=new v({expiresAt:e,onTick:e=>{this.backoffSeconds=e,this.updateSubmitButton()},onExpired:()=>{this.backoffSeconds=0,this.updateSubmitButton(),this.disableInputs(!1),this.inputElements[0]?.focus()}}),this.backoffTimer.start()}updateSubmitButton(){this.submitButton&&(this.backoffSeconds>0?(this.submitButton.textContent=`Wait ${this.backoffSeconds}s`,this.submitButton.disabled=!0):(this.submitButton.textContent="Verify",this.submitButton.disabled=!1))}disableInputs(e){this.inputElements.forEach(t=>{t.disabled=e})}handleResend(){!this.resendTimer?.isRunning()&&this.resendButton&&(this.resendTimer=new m({duration:30,onTick:e=>{this.resendButton&&(this.resendButton.textContent=`Resend code (${e}s)`,this.resendButton.disabled=!0)},onComplete:()=>{this.resendButton&&(this.resendButton.textContent="Resend code",this.resendButton.disabled=!1)}}),this.resendTimer.start(),this.props.onResend())}destroy(){null!==this.focusTimeoutId&&(clearTimeout(this.focusTimeoutId),this.focusTimeoutId=null),null!==this.autoSubmitTimeoutId&&(clearTimeout(this.autoSubmitTimeoutId),this.autoSubmitTimeoutId=null),this.resendTimer?.stop(),this.resendTimer=null,this.backoffTimer?.stop(),this.backoffTimer=null;for(const e of this.inputElements){const t=this.inputHandlers.get(e);t&&(e.removeEventListener("input",t.input),e.removeEventListener("keydown",t.keydown),e.removeEventListener("paste",t.paste),e.removeEventListener("focus",t.focus))}this.inputHandlers.clear(),this.inputElements=[],this.submitButton&&this.submitButton.removeEventListener("click",this.boundHandleSubmit),this.resendButton&&this.resendButton.removeEventListener("click",this.boundHandleResend),this.backLink&&this.backLink.removeEventListener("click",this.boundHandleBack)}}class J{constructor(e){this.actionButton=null,this.errorContainer=null,this.backLink=null,this.fallbackLink=null,this.props=e,this.boundHandleBack=()=>this.props.onBack(),this.boundHandleStart=()=>this.handleStart(),this.boundHandleFallback=e.onFallback?()=>e.onFallback():null}render(){const e=R();this.backLink=this.createBackLink();const t=R();t.appendChild(P());const n=document.createElement("h3");n.className="sv-title",n.textContent="register"===this.props.mode?"Set up a passkey":"Use your passkey";const s=document.createElement("p");s.className="sv-subtitle","register"===this.props.mode?s.innerHTML=`Create a passkey for<br><strong>${D(this.props.email)}</strong><br>to sign in faster next time`:s.innerHTML=`Use your passkey to verify<br><strong>${D(this.props.email)}</strong>`,this.errorContainer=R(),this.props.error&&this.errorContainer.appendChild(U(this.props.error));const i=document.createElement("p");i.className="sv-passkey-description",i.textContent="register"===this.props.mode?"Use Face ID, Touch ID, Windows Hello, or a security key.":"Your device will prompt you to verify your identity.";const o="register"===this.props.mode?"Create Passkey":"Continue with Passkey";return this.actionButton=document.createElement("button"),this.actionButton.className="sv-btn sv-btn-primary",this.actionButton.textContent=o,this.actionButton.addEventListener("click",this.boundHandleStart),e.appendChild(this.backLink),e.appendChild(t),e.appendChild(n),e.appendChild(s),e.appendChild(this.errorContainer),e.appendChild(i),e.appendChild(this.actionButton),this.props.onFallback&&this.boundHandleFallback&&(this.fallbackLink=this.createFallbackLink(),e.appendChild(this.fallbackLink)),e}createFallbackLink(){const e=document.createElement("button");return e.className="sv-fallback-link",e.textContent="Having trouble? Use email code instead",this.boundHandleFallback&&e.addEventListener("click",this.boundHandleFallback),e}createBackLink(){const e=document.createElement("button");return e.className="sv-back-link",e.appendChild(H()),e.appendChild(document.createTextNode(" Choose another method")),e.addEventListener("click",this.boundHandleBack),e}handleStart(){this.actionButton&&(this.actionButton.disabled=!0,this.actionButton.textContent="Waiting for device...",this.props.onStart())}showError(e){this.errorContainer&&(O(this.errorContainer),this.errorContainer.appendChild(U(e))),this.actionButton&&(this.actionButton.disabled=!1,this.actionButton.textContent="Try Again")}destroy(){this.backLink&&this.backLink.removeEventListener("click",this.boundHandleBack),this.actionButton&&this.actionButton.removeEventListener("click",this.boundHandleStart),this.fallbackLink&&this.boundHandleFallback&&this.fallbackLink.removeEventListener("click",this.boundHandleFallback)}}class Q{constructor(e){this.addButton=null,this.skipLink=null,this.props=e,this.boundHandleAdd=()=>this.props.onAddPasskey(),this.boundHandleSkip=()=>this.props.onSkip()}render(){const e=R(),t=R();t.appendChild(P());const n=document.createElement("h3");n.className="sv-title",n.textContent="Add a passkey for faster sign-in?";const s=document.createElement("p");s.className="sv-subtitle",s.innerHTML=`Next time, sign in instantly as<br><strong>${D(this.props.email)}</strong>`;const i=R();this.props.error&&i.appendChild(U(this.props.error));const o=document.createElement("div");o.className="sv-passkey-prompt-benefits";const r=document.createElement("ul"),a=["No codes to type or wait for","Phishing-resistant security","Works with Face ID, Touch ID, or Windows Hello"];for(const e of a){const t=document.createElement("li");t.appendChild(I()),t.appendChild(document.createTextNode(e)),r.appendChild(t)}return o.appendChild(r),this.addButton=document.createElement("button"),this.addButton.className="sv-btn sv-btn-primary",this.addButton.textContent=this.props.error?"Try Again":"Add Passkey",this.addButton.addEventListener("click",this.boundHandleAdd),this.skipLink=document.createElement("button"),this.skipLink.className="sv-skip-link",this.skipLink.textContent=this.props.error?"Skip for now":"Not now",this.skipLink.addEventListener("click",this.boundHandleSkip),e.appendChild(t),e.appendChild(n),e.appendChild(s),e.appendChild(i),e.appendChild(o),e.appendChild(this.addButton),e.appendChild(this.skipLink),e}destroy(){this.addButton&&this.addButton.removeEventListener("click",this.boundHandleAdd),this.skipLink&&this.skipLink.removeEventListener("click",this.boundHandleSkip)}}class Z{constructor(e){this.expirationTimer=null,this.resendTimer=null,this.countdownElement=null,this.countdownSection=null,this.waitingSection=null,this.resendButton=null,this.backLink=null,this.fallbackButton=null,this.props=e,this.boundHandleBack=()=>this.props.onBack(),this.boundHandleResend=()=>this.handleResend(),this.boundHandleFallback=()=>this.props.onFallback()}render(){const e=R();this.backLink=this.createBackLink();const t=document.createElement("h3");t.className="sv-title",t.textContent="Check your email";const n=document.createElement("p");n.className="sv-subtitle",n.innerHTML=`We sent a secure link to<br><strong>${D(this.props.email)}</strong>`,this.waitingSection=R("sv-sparklink-waiting");const s=document.createElement("div");s.className="sv-spinner sv-spinner-small",s.setAttribute("role","status"),s.setAttribute("aria-label","Waiting for verification");const i=document.createElement("span");i.className="sv-sparklink-waiting-text",i.textContent="Waiting for you to click the link...",this.waitingSection.appendChild(s),this.waitingSection.appendChild(i),this.countdownSection=R("sv-sparklink-countdown");const o=document.createElement("span");o.textContent="Link expires in ",this.countdownElement=document.createElement("span"),this.countdownElement.className="sv-sparklink-countdown-time",this.startExpirationTimer(),this.countdownSection.appendChild(o),this.countdownSection.appendChild(this.countdownElement);const r=R("sv-resend-container");this.resendButton=document.createElement("button"),this.resendButton.className="sv-btn sv-btn-secondary",this.resendButton.textContent="Resend link",this.resendButton.addEventListener("click",this.boundHandleResend),r.appendChild(this.resendButton);const a=R("sv-sparklink-fallback");return this.fallbackButton=document.createElement("button"),this.fallbackButton.className="sv-back-link",this.fallbackButton.textContent="Use verification code instead",this.fallbackButton.addEventListener("click",this.boundHandleFallback),a.appendChild(this.fallbackButton),e.appendChild(this.backLink),e.appendChild(t),e.appendChild(n),e.appendChild(this.waitingSection),e.appendChild(this.countdownSection),e.appendChild(r),e.appendChild(a),e}createBackLink(){const e=document.createElement("button");return e.className="sv-back-link",e.appendChild(H()),e.appendChild(document.createTextNode(" Choose another method")),e.addEventListener("click",this.boundHandleBack),e}formatTime(e){if(!Number.isFinite(e)||e<0)return"--:--";return`${Math.floor(e/60)}:${Math.floor(e%60).toString().padStart(2,"0")}`}startExpirationTimer(){if(!Number.isFinite(this.props.expiresAt)||this.props.expiresAt<=0)return this.countdownElement&&(this.countdownElement.textContent="--:--"),void console.warn("[SparkVault] Invalid expiresAt timestamp:",this.props.expiresAt);const e=Math.floor(this.props.expiresAt/1e3);this.expirationTimer=new v({expiresAt:e,onTick:e=>{this.countdownElement&&(this.countdownElement.textContent=this.formatTime(e))},onExpired:()=>{this.showExpiredState()}}),this.expirationTimer.start()}showExpiredState(){if(this.props.onExpired(),this.waitingSection){this.waitingSection.innerHTML="",this.waitingSection.classList.add("sv-sparklink-expired");const e=R("sv-sparklink-expired-icon");e.appendChild(function(){const e=T("0 0 48 48",48,48);e.classList.add("sv-expired-icon"),e.appendChild(L(24,24,22,{fill:"#F5F5F5",stroke:"#E5E5E5","stroke-width":"1"}));const t=document.createElementNS("http://www.w3.org/2000/svg","g");return t.setAttribute("transform","translate(10, 12)"),t.appendChild(M("M14 4L2 24h24L14 4z",{fill:"#737373"})),t.appendChild(M("M14 10v5",{stroke:"#FFFFFF","stroke-width":"2","stroke-linecap":"round"})),t.appendChild(L(14,19,1,{fill:"#FFFFFF"})),e.appendChild(t),e}()),this.waitingSection.appendChild(e);const t=document.createElement("span");t.className="sv-sparklink-expired-text",t.textContent="This link has expired",this.waitingSection.appendChild(t)}this.countdownSection&&(this.countdownSection.style.display="none")}handleResend(){!this.resendTimer?.isRunning()&&this.resendButton&&(this.resendTimer=new m({duration:30,onTick:e=>{this.resendButton&&(this.resendButton.textContent=`Resend link (${e}s)`,this.resendButton.disabled=!0)},onComplete:()=>{this.resendButton&&(this.resendButton.textContent="Resend link",this.resendButton.disabled=!1)}}),this.resendTimer.start(),this.props.onResend())}destroy(){this.expirationTimer?.stop(),this.expirationTimer=null,this.resendTimer?.stop(),this.resendTimer=null,this.backLink&&this.backLink.removeEventListener("click",this.boundHandleBack),this.resendButton&&this.resendButton.removeEventListener("click",this.boundHandleResend),this.fallbackButton&&this.fallbackButton.removeEventListener("click",this.boundHandleFallback)}}class X{constructor(e){this.retryButton=null,this.closeButton=null,this.props=e,this.boundHandleRetry=()=>this.props.onRetry(),this.boundHandleClose=()=>this.props.onClose()}render(){const e=R();e.className="sv-error-view";const t=R();t.className="sv-error-icon-container",t.appendChild(function(){const e=T("0 0 48 48",48,48);e.classList.add("sv-error-icon"),e.appendChild(L(24,24,22,{fill:"#FEF2F2",stroke:"#FECACA","stroke-width":"1"}));const t=document.createElementNS("http://www.w3.org/2000/svg","g");return t.setAttribute("transform","translate(10, 12)"),t.appendChild(M("M14 4L2 24h24L14 4z",{fill:"#DC2626"})),t.appendChild(M("M14 10v5",{stroke:"#FFFFFF","stroke-width":"2","stroke-linecap":"round"})),t.appendChild(L(14,19,1,{fill:"#FFFFFF"})),e.appendChild(t),e}());const n=R();n.className="sv-error-content";const s=document.createElement("h3");s.className="sv-error-title",s.textContent="Something went wrong";const i=document.createElement("p");i.className="sv-error-message",i.textContent=this.props.message;const o=document.createElement("div");o.className="sv-error-code-badge";const r=document.createElement("span");r.className="sv-error-code-label",r.textContent="Error code";const a=document.createElement("code");a.className="sv-error-code-value",a.textContent=this.props.code,o.appendChild(r),o.appendChild(a),n.appendChild(s),n.appendChild(i),n.appendChild(o);const l=R();return l.className="sv-error-actions",this.retryButton=document.createElement("button"),this.retryButton.className="sv-btn sv-btn-error-primary",this.retryButton.textContent="Try Again",this.retryButton.addEventListener("click",this.boundHandleRetry),this.closeButton=document.createElement("button"),this.closeButton.className="sv-btn sv-btn-error-secondary",this.closeButton.textContent="Close",this.closeButton.addEventListener("click",this.boundHandleClose),l.appendChild(this.retryButton),l.appendChild(this.closeButton),e.appendChild(t),e.appendChild(n),e.appendChild(l),e}destroy(){this.retryButton&&this.retryButton.removeEventListener("click",this.boundHandleRetry),this.closeButton&&this.closeButton.removeEventListener("click",this.boundHandleClose)}}class ee{constructor(e,t,n,s){this.currentView=null,this.focusTimeoutId=null,this.pollingInterval=null,this.messageListener=null,this.container=e,this.api=t,this.options=n,this.callbacks=s,this.verificationState=new x,this.passkeyHandler=new w(t,this.verificationState),this.totpHandler=new C(t,this.verificationState),this.sparkLinkHandler=new E(t,this.verificationState),n.email?this.verificationState.setIdentity(n.email,"email"):n.phone&&this.verificationState.setIdentity(n.phone,"phone"),n.authContext&&this.verificationState.setAuthContext(n.authContext)}get recipient(){return this.verificationState.recipient}get identityType(){return this.verificationState.identityType}get sdkConfig(){return this.verificationState.sdkConfig}async start(){let e=null;if(this.api.isConfigPreloaded()){const t=new Promise(e=>setTimeout(()=>e(null),50)),n=await Promise.race([this.api.getConfig().then(e=>e),t]);null!==n&&(e=n)}this.container.createLoading({backdropBlur:this.options.backdropBlur},()=>this.handleClose());try{e||(this.setState({view:"loading"}),e=await this.api.getConfig()),this.verificationState.setConfig(e),e.branding&&this.container.updateBranding(e.branding);const t=void 0!==this.options.backdropBlur?this.options.backdropBlur:void 0===e.branding?.backdrop_blur||e.branding.backdrop_blur;this.container.updateBackdropBlur(t),this.recipient?this.showMethodSelect():this.showIdentityInput()}catch(e){this.handleErrorWithRecovery(e,"config_error")}}close(){null!==this.focusTimeoutId&&(clearTimeout(this.focusTimeoutId),this.focusTimeoutId=null),this.stopSparkLinkPolling(),this.destroyCurrentView(),this.container.destroy()}handleClose(){this.close(),this.callbacks.onCancel()}get viewState(){return this.verificationState.viewState}setState(e){this.verificationState.setViewState(e),this.render()}render(){const e=this.container.getBody();if(!e)return;this.destroyCurrentView();const t=this.createViewForState(this.viewState);this.currentView=t,O(e),e.appendChild(t.render());const n=e.querySelector("input:not([disabled]), button.sv-btn-primary:not([disabled]), button.sv-btn-email-primary:not([disabled]), button.sv-btn-method:not([disabled])");n&&(null!==this.focusTimeoutId&&clearTimeout(this.focusTimeoutId),this.focusTimeoutId=setTimeout(()=>n.focus(),50))}destroyCurrentView(){this.currentView?.destroy&&this.currentView.destroy(),this.currentView=null}createViewForState(e){switch(e.view){case"loading":return new K({message:"Loading..."});case"identity-input":return new W({allowedTypes:this.getAllowedIdentityTypes(),initialValue:this.recipient,error:e.error,onSubmit:e=>this.handleIdentitySubmit(e)});case"method-select":return new G({email:e.email,methods:e.methods,onSelectMethod:e=>this.handleMethodSelect(e),onBack:()=>this.showIdentityInput()});case"totp-verify":return new Y({email:e.email,method:e.method,error:e.error,backoffExpires:e.backoffExpires,onSubmit:e=>this.handleTotpSubmit(e),onResend:()=>this.handleTotpResend(),onBack:()=>this.showMethodSelect()});case"passkey":{const{passkey:t}=this.verificationState,n="register"===e.mode&&null!==t.pendingResult;return new J({email:e.email,mode:e.mode,error:e.error,onStart:()=>this.handlePasskeyStart(),onBack:n?()=>this.handlePasskeyRegistrationSkip():"verify"===e.mode?()=>this.handlePasskeyFallback():()=>this.showMethodSelect(),onFallback:t.hasPasskey&&!t.isFallbackMode?()=>this.handlePasskeyFallback():void 0})}case"passkey-prompt":return new Q({email:e.email,onAddPasskey:()=>this.handlePasskeyPromptAdd(e.pendingResult),onSkip:()=>this.handlePasskeyPromptSkip(e.pendingResult),error:e.error});case"sparklink-waiting":return new Z({email:e.email,expiresAt:e.expiresAt,onResend:()=>this.handleSparkLinkResend(),onFallback:()=>this.handleSparkLinkFallback(),onBack:()=>this.showMethodSelect(),onExpired:()=>this.stopSparkLinkPolling()});case"oauth-pending":return new K({message:`Connecting to ${e.provider}...`});case"error":return new X({message:e.message,code:e.code,onRetry:()=>this.showIdentityInput(),onClose:()=>this.handleClose()})}}showIdentityInput(e){this.setState({view:"identity-input",error:e})}async showMethodSelect(){if(!this.sdkConfig)return;const e=this.sdkConfig.methods??[];if(!Array.isArray(e))return void this.setState({view:"error",message:"Invalid configuration",code:"invalid_config"});let t=y(e);if(t=t.filter(e=>"phone"===this.identityType?"totp"===e.type&&"totp_sms"===e.id:"email"===e.identityType||void 0===e.identityType),0===t.length)return void this.setState({view:"error",message:"No authentication methods available",code:"no_methods"});const n=t.some(e=>"passkey"===e.type),{passkey:s}=this.verificationState;if(n&&"email"===this.identityType&&!s.isFallbackMode){this.setState({view:"loading"});const e=await this.passkeyHandler.checkPasskey();if(null!==e&&!e.emailValid)return void this.showIdentityInput("This email domain cannot receive messages. Please check your email address.");if(!0===e?.hasPasskey)return void this.setState({view:"passkey",email:this.recipient,mode:"verify"});t=t.filter(e=>"passkey"!==e.type)}0!==t.length?1!==t.length?this.setState({view:"method-select",email:this.recipient,methods:t}):this.handleMethodSelect(t[0]):this.setState({view:"error",message:"No authentication methods available",code:"no_methods"})}handleIdentitySubmit(e){this.verificationState.setIdentity(e.value,e.type),this.showMethodSelect()}getAllowedIdentityTypes(){return this.verificationState.getAllowedIdentityTypes()}async handleMethodSelect(e){try{switch(e.type){case"totp":{const t="totp_sms"===e.id?"sms":"email";this.setState({view:"loading"});const n=await this.totpHandler.send(t);if(!n.success)return void this.setState({view:"error",message:n.error??"Failed to send code",code:"totp_send_failed"});this.setState({view:"totp-verify",email:this.recipient,method:t,kindling:this.verificationState.totp.kindling});break}case"passkey":this.setState({view:"passkey",email:this.recipient,mode:"verify"});break;case"sparklink":{this.setState({view:"loading"});const e=await this.sparkLinkHandler.send();if(!e.success)return void this.setState({view:"error",message:e.error??"Failed to send SparkLink",code:"sparklink_send_failed"});this.setState({view:"sparklink-waiting",email:this.recipient,sparkId:e.sparkId,expiresAt:e.expiresAt}),this.startSparkLinkPolling(e.sparkId);break}case"social":e.provider&&this.handleSocialLogin(e.provider);break;default:this.setState({view:"error",message:`Authentication method not supported: ${e.type}`,code:"unsupported_method"})}}catch(e){this.handleErrorWithRecovery(e,"unknown")}}async handleTotpSubmit(e){if("totp-verify"!==this.viewState.view)return;const t=this.verificationState.totp.method??"email",n=await this.totpHandler.verify(e);if(n.success&&n.result){if(n.result.redirect)return this.close(),void(window.location.href=n.result.redirect);if(await this.shouldShowPasskeyPrompt())return void this.setState({view:"passkey-prompt",email:this.recipient,pendingResult:n.result});this.close(),this.callbacks.onSuccess(n.result)}else{if((n.error?.toLowerCase()??"").includes("expired")){const e=await this.totpHandler.resend();return e.success?void this.setState({view:"totp-verify",email:this.recipient,method:t,kindling:this.verificationState.totp.kindling,error:"Code expired. We've sent a new one."}):void this.setState({view:"totp-verify",email:this.recipient,method:t,kindling:this.verificationState.totp.kindling,error:e.error??"Code expired and failed to resend."})}this.setState({view:"totp-verify",email:this.recipient,method:t,kindling:this.verificationState.totp.kindling,error:n.error??"Invalid code. Please try again.",backoffExpires:n.backoffExpires})}}async handleTotpResend(){if("totp-verify"!==this.viewState.view)return;const e=this.verificationState.totp.method??"email",t=await this.totpHandler.resend();t.success||this.setState({view:"totp-verify",email:this.recipient,method:e,kindling:this.verificationState.totp.kindling,error:t.error??"Failed to resend code"})}async handlePasskeyStart(){if("passkey"!==this.viewState.view)return;const e=this.viewState.mode,t="register"===e?await this.passkeyHandler.register():await this.passkeyHandler.verify();if(t.success&&t.result){if(t.result.redirect)return this.close(),void(window.location.href=t.result.redirect);this.close(),this.callbacks.onSuccess(t.result)}else"not_found"===t.errorType?this.setState({view:"passkey",email:this.recipient,mode:"register",error:t.error}):this.setState({view:"passkey",email:this.recipient,mode:e,error:t.error})}async handlePasskeyFallback(){this.verificationState.enablePasskeyFallback(),await this.showMethodSelect()}handlePasskeyRegistrationSkip(){const e=this.verificationState.passkey.pendingResult;if(e){if(this.verificationState.setPendingResult(null),e.redirect)return this.close(),void(window.location.href=e.redirect);this.close(),this.callbacks.onSuccess(e)}}handleSocialLogin(e){this.setState({view:"oauth-pending",provider:e});const t=function(){const e=new Uint8Array(16);return crypto.getRandomValues(e),Array.from(e,e=>e.toString(16).padStart(2,"0")).join("")}(),n=window.location.origin+window.location.pathname;sessionStorage.setItem("sv_oauth_state",t),sessionStorage.setItem("sv_oauth_email",this.recipient);const s=this.api.getSocialAuthUrl(e,n,t);window.location.href=s}normalizeError(e){return e instanceof f?new t(e.message,e.code):e instanceof Error?e:new Error(String(e))}isSessionExpiredError(e){const n=e.message.toLowerCase(),s=e instanceof t?e.code:"";return n.includes("expired")||n.includes("session")||n.includes("invalid token")||n.includes("token invalid")||"token_expired"===s||"session_expired"===s||"kindling_expired"===s}handleErrorWithRecovery(e,n){const s=this.normalizeError(e);this.isSessionExpiredError(s)?this.showIdentityInput("Your session has expired. Please try again."):this.setState({view:"error",message:s.message,code:s instanceof t?s.code:n})}async shouldShowPasskeyPrompt(){if(!this.verificationState.shouldShowPasskeyPrompt())return!1;const{passkey:e}=this.verificationState;if(null===e.hasPasskey){const e=await this.passkeyHandler.checkPasskey();return null!==e&&e.emailValid&&!e.hasPasskey}return!e.hasPasskey}async handlePasskeyPromptAdd(e){this.verificationState.setPendingResult(e),this.setState({view:"loading"});const t=await this.passkeyHandler.register();if(t.success&&t.result){if(t.result.redirect)return this.close(),void(window.location.href=t.result.redirect);this.close(),this.callbacks.onSuccess(t.result)}else if("cancelled"===t.errorType)this.setState({view:"passkey-prompt",email:this.recipient,pendingResult:e});else{const n=t.error||"Failed to create passkey. Please try again.";this.setState({view:"passkey-prompt",email:this.recipient,pendingResult:e,error:n})}}handlePasskeyPromptSkip(e){if(this.verificationState.dismissPasskeyPrompt(),e.redirect)return this.close(),void(window.location.href=e.redirect);this.close(),this.callbacks.onSuccess(e)}startSparkLinkPolling(e){this.stopSparkLinkPolling(),this.messageListener=e=>{if(!e.data||"object"!=typeof e.data)return;if("sparklink_verified"!==e.data.type)return;const{token:t,identity:n,identityType:s,redirect:i}=e.data;t&&n&&this.handleSparkLinkVerified({token:t,identity:n,identityType:s||"email",redirect:i})},window.addEventListener("message",this.messageListener),this.pollingInterval=setInterval(async()=>{const t=await this.sparkLinkHandler.checkStatus(e);t.verified&&t.token&&t.identity&&this.handleSparkLinkVerified({token:t.token,identity:t.identity,identityType:t.identityType||"email",redirect:t.redirect})},2e3)}async handleSparkLinkVerified(e){if(this.stopSparkLinkPolling(),e.redirect)return this.close(),void(window.location.href=e.redirect);await this.shouldShowPasskeyPrompt()?this.setState({view:"passkey-prompt",email:this.recipient,pendingResult:e}):(this.close(),this.callbacks.onSuccess(e))}stopSparkLinkPolling(){this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=null),this.messageListener&&(window.removeEventListener("message",this.messageListener),this.messageListener=null)}async handleSparkLinkResend(){const e=await this.sparkLinkHandler.send();e.success&&e.sparkId&&(this.stopSparkLinkPolling(),this.setState({view:"sparklink-waiting",email:this.recipient,sparkId:e.sparkId,expiresAt:e.expiresAt}),this.startSparkLinkPolling(e.sparkId))}async handleSparkLinkFallback(){this.stopSparkLinkPolling(),this.setState({view:"loading"});const e=await this.totpHandler.send("email");e.success?this.setState({view:"totp-verify",email:this.recipient,method:"email",kindling:this.verificationState.totp.kindling}):this.setState({view:"error",message:e.error??"Failed to send code",code:"totp_send_failed"})}}const te={showHeader:!1,showCloseButton:!1,showFooter:!0};class ne{constructor(e,t={}){this.container=null,this.header=null,this.body=null,this.footer=null,this.closeBtn=null,this.onCloseCallback=null,this.closeBtnClickHandler=null,this.effectiveTheme="light",this.targetElement=e,this.containerOptions={...te,...t}}createLoading(e,t){if(this.container)return;this.onCloseCallback=t;if(B({branding:{companyName:"",logoLight:null,logoDark:null},backdropBlur:!1}),this.container=document.createElement("div"),this.container.className="sv-inline-container",this.containerOptions.showHeader&&(this.header=this.createHeader({companyName:"",logoLight:null,logoDark:null}),this.container.appendChild(this.header)),this.body=document.createElement("div"),this.body.className="sv-body sv-inline-body",this.container.appendChild(this.body),this.containerOptions.showFooter){this.footer=document.createElement("div"),this.footer.className="sv-footer";const e=document.createElement("span");e.className="sv-footer-text",e.appendChild(document.createTextNode("Secured by "));const t=document.createElement("a");t.href="https://sparkvault.com",t.target="_blank",t.rel="noopener noreferrer",t.className="sv-footer-link",t.style.color="inherit",t.style.textDecoration="none",t.textContent="SparkVault",e.appendChild(t),this.footer.appendChild(e),this.container.appendChild(this.footer)}this.targetElement.innerHTML="",this.targetElement.appendChild(this.container)}updateBranding(e){if(this.container&&(this.effectiveTheme=$(e),this.containerOptions.showHeader&&this.header)){const t=this.createHeader(e);this.container.replaceChild(t,this.header),this.header=t}}updateBackdropBlur(e){}getBody(){return this.body}isOpen(){return null!==this.container}destroy(){this.closeBtn&&this.closeBtnClickHandler&&(this.closeBtn.removeEventListener("click",this.closeBtnClickHandler),this.closeBtnClickHandler=null,this.closeBtn=null),this.container&&this.container.parentNode&&this.container.remove(),this.container=null,this.header=null,this.body=null,this.footer=null,this.onCloseCallback=null}createHeader(e){const t=document.createElement("div");t.className="sv-header sv-inline-header";const n=document.createElement("div");n.className="sv-header-title";const s="dark"===this.effectiveTheme?e.logoDark||e.logoLight:e.logoLight||e.logoDark;if(s){const t=document.createElement("img");t.className="sv-logo",t.src=s,t.alt=e.companyName,n.appendChild(t);const i=document.createElement("span");i.className="sv-sr-only",i.textContent=e.companyName,n.appendChild(i)}else if(e.companyName){const t=document.createElement("h2");t.className="sv-company-name",t.textContent=e.companyName,n.appendChild(t)}return t.appendChild(n),this.containerOptions.showCloseButton&&(this.closeBtn=document.createElement("button"),this.closeBtn.className="sv-close-btn",this.closeBtn.setAttribute("aria-label","Close"),this.closeBtn.appendChild(A()),this.closeBtnClickHandler=()=>this.handleClose(),this.closeBtn.addEventListener("click",this.closeBtnClickHandler),t.appendChild(this.closeBtn)),t}handleClose(){this.onCloseCallback&&this.onCloseCallback()}}function se(e){"loading"===document.readyState?document.addEventListener("DOMContentLoaded",e,{once:!0}):e()}class ie{constructor(e){this.renderer=null,this.attachedElements=new Map,this.config=e,this.api=new b(e),e.preloadConfig&&this.api.preloadConfig()}async verify(e={}){this.renderer&&this.renderer.close();const t=!!e.target?this.createInlineContainer(e.target):new z,n={...e,backdropBlur:e.backdropBlur??this.config.backdropBlur};return new Promise((s,i)=>{this.renderer=new ee(t,this.api,n,{onSuccess:t=>{e.onSuccess?.(t),s(t)},onError:t=>{e.onError?.(t),i(t)},onCancel:()=>{const t=new a;e.onCancel?.(),i(t)}}),this.renderer.start().catch(t=>{e.onError?.(t),i(t)})})}attach(e,t={}){let n=null;const s=async e=>{e.preventDefault();try{await this.verify({email:t.email,phone:t.phone,authContext:t.authContext,onSuccess:t.onSuccess,onError:t.onError,onCancel:t.onCancel})}catch{}};return se(()=>{document.querySelectorAll(e).forEach(e=>{e.addEventListener("click",s),this.attachedElements.set(e,()=>{e.removeEventListener("click",s)})}),n=new MutationObserver(t=>{t.forEach(t=>{t.addedNodes.forEach(t=>{t instanceof Element&&(t.matches(e)&&(t.addEventListener("click",s),this.attachedElements.set(t,()=>{t.removeEventListener("click",s)})),t.querySelectorAll(e).forEach(e=>{this.attachedElements.has(e)||(e.addEventListener("click",s),this.attachedElements.set(e,()=>{e.removeEventListener("click",s)}))}))})})}),function(e){if(document.body)e();else{const t=()=>{document.body?e():window.requestAnimationFrame(t)};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",t,{once:!0}):window.requestAnimationFrame(t)}}(()=>{n?.observe(document.body,{childList:!0,subtree:!0})})}),()=>{n?.disconnect(),this.attachedElements.forEach(e=>e()),this.attachedElements.clear()}}async verifyToken(e){if(!e||"string"!=typeof e)throw new i("Token is required");const t=e.split(".");if(3!==t.length)throw new i("Invalid token format");const[,n]=t,s=JSON.parse(function(e){const t=e.replace(/-/g,"+").replace(/_/g,"/"),n=t+"=".repeat((4-t.length%4)%4);return atob(n)}(n));if(!function(e){if("object"!=typeof e||null===e)return!1;const t=e;return"string"==typeof t.iss&&"string"==typeof t.sub&&"string"==typeof t.aud&&"number"==typeof t.exp&&"number"==typeof t.iat&&"string"==typeof t.identity&&"string"==typeof t.identity_type&&"number"==typeof t.verified_at&&"string"==typeof t.method}(s))throw new i("Invalid token payload structure");const o=Math.floor(Date.now()/1e3);if(s.exp<o)throw new i("Token has expired");const r=`${this.config.apiBaseUrl}/apps/identity/${this.config.accountId}`;if(s.iss!==r)throw new i("Invalid token issuer");return s}close(){this.renderer&&(this.renderer.close(),this.renderer=null)}createInlineContainer(e){let t;if("string"==typeof e){const n=document.querySelector(e);if(!(n&&n instanceof HTMLElement))throw new i(`Target selector "${e}" did not match any element`);t=n}else{if(!(e instanceof HTMLElement))throw new i("Target must be a CSS selector string or HTMLElement");t=e}return new ne(t)}async pop(e={}){return this.verify(e)}async render(e){return this.verify(e)}}class oe extends t{constructor(e,t,n){super(e,t,n),this.httpStatus=n,this.name="UploadApiError"}}class re{constructor(e){this.config=e,this.timeoutMs=e.timeout??3e4}async getVaultUploadInfo(e){const t=`${this.config.apiBaseUrl}/v1/files/in/${e}`,n=await this.request(t,{method:"GET"});if(!function(e){if("object"!=typeof e||null===e)return!1;const t=e;return"string"==typeof t.vault_id&&"string"==typeof t.vault_name&&"number"==typeof t.max_size_bytes&&"object"==typeof t.branding&&null!==t.branding&&"string"==typeof t.branding.organization_name&&"object"==typeof t.encryption&&null!==t.encryption&&"string"==typeof t.encryption.algorithm&&("active"===t.forge_status||"inactive"===t.forge_status)}(n.data))throw new oe("Invalid vault upload info response from server","invalid_response",n.status);const s=n.data;return{vaultId:s.vault_id,vaultName:s.vault_name,maxSizeBytes:s.max_size_bytes,branding:{organizationName:s.branding.organization_name,logoLightUrl:s.branding.logo_light_url,logoDarkUrl:s.branding.logo_dark_url},encryption:{algorithm:s.encryption.algorithm,keyDerivation:s.encryption.key_derivation,postQuantum:s.encryption.post_quantum},forgeStatus:s.forge_status}}async initiateUpload(e,t,n,s){const i=`${this.config.apiBaseUrl}/v1/files/in/${e}/upload`,o=await this.request(i,{method:"POST",body:JSON.stringify({filename:t,size_bytes:n,content_type:s})});if(!function(e){if("object"!=typeof e||null===e)return!1;const t=e;return"string"==typeof t.forge_url&&"string"==typeof t.ingot_id}(o.data))throw new oe("Invalid initiate upload response from server","invalid_response",o.status);return{forgeUrl:o.data.forge_url,ingotId:o.data.ingot_id}}async request(e,n){const s=new AbortController,i=setTimeout(()=>s.abort(),this.timeoutMs);try{const t=await fetch(e,{method:n.method,headers:{"Content-Type":"application/json",Accept:"application/json"},body:n.body,signal:s.signal}),i=await t.json();if(!t.ok){const e="object"==typeof i&&null!==i?i:{},n="object"==typeof e.error&&null!==e.error?e.error:e,s=("string"==typeof n.message?n.message:null)??("string"==typeof n.error?n.error:null)??"Request failed",o="string"==typeof n.code?n.code:"api_error";throw new oe(s,o,t.status)}return{data:"object"==typeof i&&null!==i&&"data"in i?i.data:i,status:t.status}}catch(s){if(s instanceof t)throw s;if(s instanceof DOMException&&"AbortError"===s.name)throw new r(`Request timed out after ${this.timeoutMs}ms`);if(s instanceof TypeError)throw new o(`Network request failed: ${s.message}`,{url:e,method:n.method});throw new o(s instanceof Error?`Request failed: ${s.message}`:"Request failed: Unknown error")}finally{clearTimeout(i)}}}const ae="sparkvault-upload-styles";function le(e){if(document.getElementById(ae))return;const t=document.createElement("style");t.id=ae,t.textContent=function(e){const{backdropBlur:t}=e,n=!1!==t,s={primary:"#4F46E5",primaryHover:"#4338CA",primaryLight:"rgba(79, 70, 229, 0.08)",error:"#DC2626",errorLight:"#FEF2F2",errorBorder:"#FECACA",success:"#059669",bg:"#FFFFFF",bgSubtle:"#FAFAFA",bgHover:"#F5F5F5",bgDark:"#0F0F0F",bgDarkSubtle:"#171717",border:"#E5E5E5",borderHover:"#D4D4D4",borderDark:"#262626",textPrimary:"#0A0A0A",textSecondary:"#525252",textMuted:"#737373",textOnPrimary:"#FFFFFF",textOnDark:"#FAFAFA",textMutedOnDark:"#A3A3A3",shadowSm:"0 1px 2px rgba(0, 0, 0, 0.04)",shadowLg:"0 8px 24px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.04)"};return`\n /* ========================================\n OVERLAY & MODAL CONTAINER\n ======================================== */\n\n .svu-overlay {\n /* Force full-screen overlay regardless of page styles */\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n width: 100vw !important;\n height: 100vh !important;\n max-width: none !important;\n max-height: none !important;\n margin: 0 !important;\n background: rgba(0, 0, 0, 0.5);\n display: flex !important;\n align-items: center;\n justify-content: center;\n z-index: 999999 !important;\n padding: 16px;\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n /* Break out of any containing block that could affect fixed positioning */\n transform: none !important;\n filter: none !important;\n contain: none !important;\n isolation: isolate;\n }\n\n .svu-overlay.svu-blur {\n backdrop-filter: blur(${n?"8px":"0"});\n -webkit-backdrop-filter: blur(${n?"8px":"0"});\n }\n\n .svu-modal {\n background: ${s.bg};\n border-radius: 16px;\n box-shadow: ${s.shadowLg};\n width: 100%;\n max-width: 480px;\n max-height: calc(100vh - 32px);\n display: flex;\n flex-direction: column;\n color: ${s.textPrimary};\n overflow: hidden;\n }\n\n .svu-modal.svu-with-sidebar {\n max-width: 900px;\n flex-direction: row;\n }\n\n .svu-modal-main {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n }\n\n /* Dark card variant for uploading/ceremony states */\n .svu-modal.svu-dark {\n background: ${s.bgDark};\n color: ${s.textOnDark};\n }\n\n /* ========================================\n HEADER\n ======================================== */\n\n .svu-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-bottom: 1px solid ${s.border};\n background: ${s.bg};\n flex-shrink: 0;\n }\n\n .svu-dark .svu-header {\n border-bottom-color: ${s.borderDark};\n background: ${s.bgDark};\n }\n\n .svu-header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n }\n\n .svu-logo {\n height: 28px;\n width: auto;\n max-width: 140px;\n object-fit: contain;\n flex-shrink: 0;\n }\n\n .svu-company-name {\n font-size: 15px;\n font-weight: 600;\n letter-spacing: -0.01em;\n margin: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .svu-close-btn {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n color: ${s.textMuted};\n transition: background 0.15s ease, color 0.15s ease;\n }\n\n .svu-close-btn:hover {\n background: ${s.bgHover};\n color: ${s.textPrimary};\n }\n\n .svu-dark .svu-close-btn {\n color: ${s.textMutedOnDark};\n }\n\n .svu-dark .svu-close-btn:hover {\n background: ${s.bgDarkSubtle};\n color: ${s.textOnDark};\n }\n\n /* ========================================\n BODY\n ======================================== */\n\n .svu-body {\n padding: 24px 28px;\n overflow-y: auto;\n flex: 1;\n }\n\n /* ========================================\n FOOTER\n ======================================== */\n\n .svu-footer {\n padding: 10px 20px;\n border-top: 1px solid ${s.border};\n text-align: center;\n background: ${s.bgSubtle};\n flex-shrink: 0;\n }\n\n .svu-dark .svu-footer {\n border-top-color: ${s.borderDark};\n background: ${s.bgDarkSubtle};\n }\n\n .svu-footer-text {\n font-size: 10px;\n color: ${s.textMuted};\n letter-spacing: 0.02em;\n }\n\n .svu-dark .svu-footer-text {\n color: ${s.textMutedOnDark};\n }\n\n .svu-footer-link {\n color: ${s.textSecondary};\n text-decoration: none;\n font-weight: 500;\n transition: color 0.15s ease;\n }\n\n .svu-footer-link:hover {\n color: ${s.primary};\n }\n\n /* ========================================\n LOADING STATE\n ======================================== */\n\n .svu-loading {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n gap: 16px;\n }\n\n .svu-spinner {\n width: 28px;\n height: 28px;\n border: 2.5px solid ${s.border};\n border-top-color: ${s.primary};\n border-radius: 50%;\n animation: svu-spin 0.7s linear infinite;\n }\n\n .svu-loading-text {\n font-size: 14px;\n color: ${s.textSecondary};\n margin: 0;\n }\n\n @keyframes svu-spin {\n to { transform: rotate(360deg); }\n }\n\n /* ========================================\n FORM VIEW\n ======================================== */\n\n .svu-form-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n\n .svu-title {\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.02em;\n line-height: 1.3;\n margin: 0 0 6px 0;\n text-align: center;\n color: ${s.textPrimary};\n }\n\n .svu-dark .svu-title {\n color: ${s.textOnDark};\n }\n\n .svu-subtitle {\n font-size: 14px;\n font-weight: 400;\n line-height: 1.5;\n color: ${s.textSecondary};\n margin: 0 0 20px 0;\n text-align: center;\n }\n\n .svu-subtitle strong {\n color: ${s.textPrimary};\n font-weight: 500;\n }\n\n /* Drop Zone */\n .svu-drop-zone {\n width: 100%;\n border: 2px dashed ${s.border};\n border-radius: 12px;\n padding: 32px 24px;\n text-align: center;\n cursor: pointer;\n transition: border-color 0.15s ease, background 0.15s ease;\n margin-bottom: 16px;\n }\n\n .svu-drop-zone:hover {\n border-color: ${s.borderHover};\n background: ${s.bgSubtle};\n }\n\n .svu-drop-zone.svu-dragging {\n border-color: ${s.primary};\n background: ${s.primaryLight};\n }\n\n .svu-drop-zone.svu-has-file {\n border-style: solid;\n border-color: ${s.primary};\n background: ${s.primaryLight};\n cursor: default;\n }\n\n .svu-drop-icon {\n color: ${s.textMuted};\n margin-bottom: 12px;\n }\n\n .svu-drop-text {\n font-size: 14px;\n font-weight: 500;\n color: ${s.textPrimary};\n margin: 0 0 4px 0;\n }\n\n .svu-drop-subtext {\n font-size: 13px;\n color: ${s.textSecondary};\n margin: 0 0 8px 0;\n }\n\n .svu-drop-hint {\n font-size: 12px;\n color: ${s.textMuted};\n margin: 0;\n }\n\n /* Selected File */\n .svu-selected-file {\n display: flex;\n align-items: center;\n gap: 12px;\n width: 100%;\n }\n\n .svu-file-icon {\n color: ${s.primary};\n flex-shrink: 0;\n }\n\n .svu-file-info {\n flex: 1;\n min-width: 0;\n text-align: left;\n }\n\n .svu-file-name {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: ${s.textPrimary};\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .svu-file-size {\n display: block;\n font-size: 12px;\n color: ${s.textSecondary};\n }\n\n .svu-remove-file {\n flex-shrink: 0;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 50%;\n cursor: pointer;\n font-size: 18px;\n color: ${s.textMuted};\n transition: background 0.15s ease, color 0.15s ease;\n }\n\n .svu-remove-file:hover {\n background: ${s.bgHover};\n color: ${s.error};\n }\n\n /* Max Size */\n .svu-max-size {\n font-size: 12px;\n color: ${s.textMuted};\n margin: 0 0 16px 0;\n }\n\n /* Upload Button */\n .svu-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n width: 100%;\n padding: 12px 20px;\n font-size: 14px;\n font-weight: 500;\n letter-spacing: -0.005em;\n line-height: 1.4;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.15s ease, transform 0.1s ease;\n box-sizing: border-box;\n }\n\n .svu-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .svu-btn:active:not(:disabled) {\n transform: scale(0.98);\n }\n\n .svu-btn-primary {\n background: ${s.primary};\n color: ${s.textOnPrimary};\n box-shadow: ${s.shadowSm};\n }\n\n .svu-btn-primary:hover:not(:disabled) {\n background: ${s.primaryHover};\n }\n\n .svu-btn-secondary {\n background: ${s.bgSubtle};\n color: ${s.textPrimary};\n border: 1px solid ${s.border};\n }\n\n .svu-btn-secondary:hover:not(:disabled) {\n background: ${s.bgHover};\n }\n\n /* Error Alert */\n .svu-error-alert {\n background: ${s.errorLight};\n border: 1px solid ${s.errorBorder};\n color: ${s.error};\n padding: 10px 12px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 450;\n margin-bottom: 16px;\n width: 100%;\n }\n\n /* Metadata Grid */\n .svu-metadata {\n width: 100%;\n margin-top: 20px;\n padding-top: 20px;\n border-top: 1px solid ${s.border};\n }\n\n .svu-metadata h4 {\n font-size: 12px;\n font-weight: 600;\n color: ${s.textSecondary};\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin: 0 0 12px 0;\n }\n\n .svu-metadata-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 8px;\n }\n\n .svu-metadata-item {\n font-size: 12px;\n }\n\n .svu-metadata-item.svu-full-width {\n grid-column: span 2;\n }\n\n .svu-metadata-label {\n display: block;\n color: ${s.textMuted};\n margin-bottom: 2px;\n }\n\n .svu-metadata-value {\n display: block;\n color: ${s.textPrimary};\n font-weight: 500;\n }\n\n .svu-metadata-value.svu-status-active {\n color: ${s.success};\n }\n\n /* ========================================\n UPLOADING VIEW\n ======================================== */\n\n .svu-uploading-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n\n .svu-stages {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n margin: 24px 0;\n width: 100%;\n }\n\n .svu-stage {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n opacity: 0.5;\n transition: opacity 0.3s ease;\n }\n\n .svu-stage.svu-active {\n opacity: 1;\n }\n\n .svu-stage span {\n font-size: 11px;\n color: ${s.textMutedOnDark};\n }\n\n .svu-stage-sub {\n font-size: 10px !important;\n color: ${s.primary} !important;\n }\n\n .svu-stage-arrow {\n width: 24px;\n height: 2px;\n background: ${s.borderDark};\n position: relative;\n }\n\n .svu-stage-arrow.svu-active::after {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n height: 100%;\n background: ${s.primary};\n animation: svu-arrow-flow 1s ease-in-out infinite;\n }\n\n @keyframes svu-arrow-flow {\n 0% { width: 0; }\n 50% { width: 100%; }\n 100% { width: 100%; }\n }\n\n /* Progress Bar */\n .svu-progress {\n width: 100%;\n margin: 20px 0;\n }\n\n .svu-progress-bar {\n width: 100%;\n height: 6px;\n background: ${s.borderDark};\n border-radius: 3px;\n overflow: hidden;\n }\n\n .svu-progress-fill {\n height: 100%;\n background: ${s.primary};\n border-radius: 3px;\n transition: width 0.3s ease;\n }\n\n .svu-progress-text {\n display: flex;\n justify-content: space-between;\n margin-top: 8px;\n font-size: 12px;\n color: ${s.textMutedOnDark};\n }\n\n /* Forging Info */\n .svu-forging-info {\n width: 100%;\n margin-top: 20px;\n padding: 16px;\n background: ${s.bgDarkSubtle};\n border-radius: 8px;\n }\n\n .svu-forging-line {\n font-size: 12px;\n color: ${s.textMutedOnDark};\n margin: 0 0 8px 0;\n }\n\n .svu-forging-line:last-child {\n margin-bottom: 0;\n }\n\n .svu-mono {\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, monospace;\n color: ${s.textOnDark};\n }\n\n /* ========================================\n CEREMONY VIEW\n ======================================== */\n\n .svu-ceremony-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n\n .svu-ceremony-steps {\n width: 100%;\n margin: 20px 0;\n }\n\n .svu-ceremony-step {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 0;\n opacity: 0.4;\n transition: opacity 0.3s ease;\n }\n\n .svu-ceremony-step.svu-active {\n opacity: 1;\n }\n\n .svu-ceremony-step.svu-complete {\n opacity: 0.7;\n }\n\n .svu-step-icon {\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: ${s.primary};\n }\n\n .svu-step-text {\n font-size: 13px;\n color: ${s.textOnDark};\n }\n\n /* Spinning loader */\n .svu-step-icon .svu-spin {\n animation: svu-spin 0.7s linear infinite;\n }\n\n /* ========================================\n COMPLETE VIEW\n ======================================== */\n\n .svu-complete-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n animation: svu-scale-in 0.3s ease;\n }\n\n @keyframes svu-scale-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n\n .svu-success-icon {\n color: ${s.success};\n margin-bottom: 16px;\n }\n\n .svu-success-title {\n font-size: 20px;\n font-weight: 600;\n color: ${s.textPrimary};\n margin: 0 0 24px 0;\n text-align: center;\n }\n\n .svu-success-details {\n width: 100%;\n background: ${s.bgSubtle};\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 16px;\n }\n\n .svu-success-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 0;\n font-size: 13px;\n border-bottom: 1px solid ${s.border};\n }\n\n .svu-success-row:last-child {\n border-bottom: none;\n }\n\n .svu-success-label {\n color: ${s.textSecondary};\n }\n\n .svu-success-value {\n color: ${s.textPrimary};\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .svu-copy-btn {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n color: ${s.textMuted};\n transition: background 0.15s ease, color 0.15s ease;\n }\n\n .svu-copy-btn:hover {\n background: ${s.bgHover};\n color: ${s.primary};\n }\n\n .svu-success-guidance {\n font-size: 13px;\n color: ${s.textSecondary};\n text-align: center;\n margin: 0 0 20px 0;\n line-height: 1.5;\n }\n\n /* ========================================\n ERROR VIEW\n ======================================== */\n\n .svu-error-view {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n }\n\n .svu-error-icon {\n color: ${s.error};\n margin-bottom: 16px;\n }\n\n .svu-error-title {\n font-size: 18px;\n font-weight: 600;\n color: ${s.textPrimary};\n margin: 0 0 8px 0;\n }\n\n .svu-error-message {\n font-size: 14px;\n color: ${s.textSecondary};\n margin: 0 0 16px 0;\n line-height: 1.5;\n }\n\n .svu-error-code {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n background: ${s.errorLight};\n border: 1px solid ${s.errorBorder};\n border-radius: 6px;\n margin-bottom: 20px;\n }\n\n .svu-error-code-label {\n font-size: 10px;\n font-weight: 600;\n color: #991B1B;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .svu-error-code-value {\n font-size: 11px;\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, monospace;\n color: ${s.error};\n font-weight: 500;\n }\n\n /* ========================================\n SECURITY SIDEBAR\n ======================================== */\n\n .svu-sidebar {\n width: 320px;\n background: ${s.bgDark};\n border-left: 1px solid ${s.borderDark};\n color: ${s.textOnDark};\n padding: 20px;\n overflow-y: auto;\n flex-shrink: 0;\n }\n\n .svu-sidebar-toggle {\n display: flex;\n align-items: center;\n gap: 4px;\n background: transparent;\n border: none;\n color: ${s.textMutedOnDark};\n font-size: 12px;\n cursor: pointer;\n padding: 0;\n margin-bottom: 16px;\n }\n\n .svu-sidebar-toggle:hover {\n color: ${s.textOnDark};\n }\n\n .svu-sidebar-logo {\n margin-bottom: 20px;\n }\n\n .svu-sidebar-logo img {\n height: 24px;\n width: auto;\n }\n\n .svu-sidebar-intro h3 {\n font-size: 14px;\n font-weight: 600;\n margin: 0 0 8px 0;\n }\n\n .svu-sidebar-intro p {\n font-size: 12px;\n color: ${s.textMutedOnDark};\n line-height: 1.5;\n margin: 0 0 20px 0;\n }\n\n .svu-sidebar-security h4 {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n font-weight: 600;\n margin: 0 0 12px 0;\n }\n\n .svu-security-intro {\n font-size: 11px;\n color: ${s.textMutedOnDark};\n line-height: 1.5;\n margin: 0 0 16px 0;\n }\n\n .svu-security-intro strong {\n color: ${s.textOnDark};\n }\n\n .svu-key-chain {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 16px;\n }\n\n .svu-key-item {\n display: flex;\n gap: 12px;\n padding: 10px;\n background: ${s.bgDarkSubtle};\n border-radius: 6px;\n }\n\n .svu-key-number {\n width: 20px;\n height: 20px;\n background: ${s.primary};\n color: ${s.textOnPrimary};\n border-radius: 50%;\n font-size: 11px;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n\n .svu-key-info {\n flex: 1;\n }\n\n .svu-key-name {\n display: block;\n font-size: 11px;\n font-weight: 600;\n color: ${s.textOnDark};\n margin-bottom: 2px;\n }\n\n .svu-key-algo {\n display: block;\n font-size: 10px;\n color: ${s.primary};\n margin-bottom: 4px;\n }\n\n .svu-key-desc {\n display: block;\n font-size: 10px;\n color: ${s.textMutedOnDark};\n line-height: 1.4;\n }\n\n .svu-key-connector {\n display: flex;\n justify-content: center;\n color: ${s.textMutedOnDark};\n }\n\n .svu-encryption-result {\n display: flex;\n gap: 8px;\n font-size: 10px;\n color: ${s.textMutedOnDark};\n line-height: 1.5;\n padding: 10px;\n background: ${s.bgDarkSubtle};\n border-radius: 6px;\n margin-bottom: 16px;\n }\n\n .svu-encryption-result svg {\n color: ${s.success};\n flex-shrink: 0;\n }\n\n .svu-encryption-result strong {\n color: ${s.textOnDark};\n }\n\n .svu-security-badges {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n }\n\n .svu-tls-badge {\n font-size: 9px;\n font-weight: 500;\n padding: 4px 8px;\n background: ${s.bgDarkSubtle};\n border-radius: 4px;\n color: ${s.textMutedOnDark};\n }\n\n /* ========================================\n INLINE CONTAINER\n ======================================== */\n\n .svu-inline-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n background: ${s.bg};\n color: ${s.textPrimary};\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n border-radius: 12px;\n border: 1px solid ${s.border};\n overflow: hidden;\n }\n\n .svu-inline-container.svu-dark {\n background: ${s.bgDark};\n color: ${s.textOnDark};\n border-color: ${s.borderDark};\n }\n\n /* ========================================\n RESPONSIVE\n ======================================== */\n\n @media (max-width: 768px) {\n .svu-modal.svu-with-sidebar {\n flex-direction: column;\n max-width: 480px;\n }\n\n .svu-sidebar {\n width: 100%;\n border-left: none;\n border-top: 1px solid ${s.borderDark};\n max-height: 300px;\n }\n }\n\n @media (max-width: 480px) {\n .svu-modal {\n max-width: 100%;\n max-height: 100%;\n border-radius: 0;\n }\n\n .svu-overlay {\n padding: 0;\n }\n\n .svu-body {\n padding: 20px;\n }\n }\n\n /* ========================================\n ACCESSIBILITY\n ======================================== */\n\n .svu-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n `}(e),document.head.appendChild(t)}const de={organizationName:"",logoLightUrl:null,logoDarkUrl:null};class ce{constructor(){this.elements=null,this.onCloseCallback=null,this.keydownHandler=null,this.overlayClickHandler=null,this.closeBtnClickHandler=null,this.closeBtn=null,this.backdropBlur=!0,this.isDarkMode=!1,this.headerElement=null,this.branding=de}createLoading(e,t){this.create({branding:de,backdropBlur:e.backdropBlur},t)}create(e,t){if(this.elements)return;this.onCloseCallback=t,this.backdropBlur=!1!==e.backdropBlur,this.branding=e.branding||de,le({branding:this.branding,backdropBlur:this.backdropBlur});const n=document.createElement("div");n.id="sparkvault-upload-overlay",n.className=this.backdropBlur?"svu-overlay svu-blur":"svu-overlay",n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-labelledby","svu-modal-title");const s=document.createElement("div");s.id="sparkvault-upload-modal",s.className="svu-modal";const i=document.createElement("div");i.className="svu-modal-main";const o=this.createHeader(this.branding);this.headerElement=o;const r=document.createElement("div");r.className="svu-body";const a=document.createElement("div");a.className="svu-footer";const l=document.createElement("span");l.className="svu-footer-text",l.appendChild(document.createTextNode("Secured by "));const d=document.createElement("a");d.href="https://sparkvault.com",d.target="_blank",d.rel="noopener noreferrer",d.className="svu-footer-link",d.textContent="SparkVault",l.appendChild(d),a.appendChild(l),i.appendChild(o),i.appendChild(r),i.appendChild(a),s.appendChild(i),n.appendChild(s),this.overlayClickHandler=e=>{e.target===n&&this.handleClose()},n.addEventListener("click",this.overlayClickHandler),this.keydownHandler=e=>{"Escape"===e.key&&this.handleClose()},document.addEventListener("keydown",this.keydownHandler),document.body.appendChild(n),document.body.style.overflow="hidden",this.elements={overlay:n,modal:s,main:i,header:o,body:r,footer:a,sidebar:null}}createHeader(e){const t=document.createElement("div");t.className="svu-header";const n=document.createElement("div");n.className="svu-header-title";const s=this.isDarkMode?e.logoDarkUrl||e.logoLightUrl:e.logoLightUrl||e.logoDarkUrl;if(s){const t=document.createElement("img");t.className="svu-logo",t.src=s,t.alt=e.organizationName,n.appendChild(t);const i=document.createElement("span");i.className="svu-sr-only",i.id="svu-modal-title",i.textContent=e.organizationName,n.appendChild(i)}else if(e.organizationName){const t=document.createElement("h2");t.className="svu-company-name",t.id="svu-modal-title",t.textContent=e.organizationName,n.appendChild(t)}return this.closeBtn=document.createElement("button"),this.closeBtn.className="svu-close-btn",this.closeBtn.setAttribute("aria-label","Close"),this.closeBtn.innerHTML='\n <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M18 6L6 18M6 6l12 12"/>\n </svg>\n ',this.closeBtnClickHandler=()=>this.handleClose(),this.closeBtn.addEventListener("click",this.closeBtnClickHandler),t.appendChild(n),t.appendChild(this.closeBtn),t}updateBranding(e){if(!this.elements)return;this.branding=e;const t=this.createHeader(e);this.headerElement?.parentNode&&this.headerElement.parentNode.replaceChild(t,this.headerElement),this.headerElement=t}updateBackdropBlur(e){this.elements&&(this.backdropBlur=e,e?this.elements.overlay.classList.add("svu-blur"):this.elements.overlay.classList.remove("svu-blur"))}setDarkMode(e){if(!this.elements)return;if(this.isDarkMode===e)return;this.isDarkMode=e,e?this.elements.modal.classList.add("svu-dark"):this.elements.modal.classList.remove("svu-dark");const t=this.createHeader(this.branding);this.headerElement?.parentNode&&this.headerElement.parentNode.replaceChild(t,this.headerElement),this.headerElement=t}toggleSidebar(e){if(this.elements)if(e&&!this.elements.sidebar){const e=this.createSecuritySidebar();this.elements.modal.classList.add("svu-with-sidebar"),this.elements.modal.appendChild(e),this.elements.sidebar=e}else!e&&this.elements.sidebar&&(this.elements.modal.classList.remove("svu-with-sidebar"),this.elements.sidebar.remove(),this.elements.sidebar=null)}createSecuritySidebar(){const e=document.createElement("div");e.className="svu-sidebar";const t=document.createElement("div");if(t.className="svu-sidebar-logo",this.branding.logoDarkUrl){const e=document.createElement("img");e.src=this.branding.logoDarkUrl,e.alt=this.branding.organizationName,t.appendChild(e)}e.appendChild(t);const n=document.createElement("div");n.className="svu-sidebar-intro",n.innerHTML="\n <h3>Secure File Transfer</h3>\n <p>This file is secured with SparkVault's advanced multi-key cryptography and end-to-end encryption.</p>\n ",e.appendChild(n);const s=document.createElement("div");return s.className="svu-sidebar-security",s.innerHTML='\n <h4>\n <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>\n </svg>\n SparkVault\'s Triple Zero-Trust\n </h4>\n <p class="svu-security-intro">\n Decryption requires <strong>three independent Master Keys</strong>, held in three different physical locations, by three separate companies, all cryptographically combined in real-time.\n </p>\n <div class="svu-key-chain">\n <div class="svu-key-item">\n <div class="svu-key-number">1</div>\n <div class="svu-key-info">\n <span class="svu-key-name">Kyber-1024 Post-Quantum Key</span>\n <span class="svu-key-algo">ML-KEM lattice-based encapsulation</span>\n <span class="svu-key-desc">Quantum-resistant key exchange</span>\n </div>\n </div>\n <div class="svu-key-connector">\n <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 5v14M5 12h14"/>\n </svg>\n </div>\n <div class="svu-key-item">\n <div class="svu-key-number">2</div>\n <div class="svu-key-info">\n <span class="svu-key-name">X25519 Ephemeral Key</span>\n <span class="svu-key-algo">Elliptic-curve Diffie-Hellman</span>\n <span class="svu-key-desc">Perfect forward secrecy</span>\n </div>\n </div>\n <div class="svu-key-connector">\n <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 5v14M5 12h14"/>\n </svg>\n </div>\n <div class="svu-key-item">\n <div class="svu-key-number">3</div>\n <div class="svu-key-info">\n <span class="svu-key-name">HKDF-SHA512 Derived Key</span>\n <span class="svu-key-algo">Hardware-bound key derivation</span>\n <span class="svu-key-desc">Vault master key in secure enclave</span>\n </div>\n </div>\n </div>\n <div class="svu-encryption-result">\n <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M20 6L9 17l-5-5"/>\n </svg>\n <span>Combined via <strong>HKDF</strong> producing <strong>AES-256-GCM</strong> authenticated cipher</span>\n </div>\n <div class="svu-security-badges">\n <span class="svu-tls-badge">TLS 1.3 In Transit</span>\n <span class="svu-tls-badge">Encrypted At Rest</span>\n <span class="svu-tls-badge">Zero Trust</span>\n </div>\n ',e.appendChild(s),e}handleClose(){this.onCloseCallback&&this.onCloseCallback()}getBody(){return this.elements?.body??null}getSidebar(){return this.elements?.sidebar??null}isOpen(){return null!==this.elements}destroy(){this.keydownHandler&&(document.removeEventListener("keydown",this.keydownHandler),this.keydownHandler=null),this.elements&&this.overlayClickHandler&&(this.elements.overlay.removeEventListener("click",this.overlayClickHandler),this.overlayClickHandler=null),this.closeBtn&&this.closeBtnClickHandler&&(this.closeBtn.removeEventListener("click",this.closeBtnClickHandler),this.closeBtnClickHandler=null,this.closeBtn=null),this.elements&&(this.elements.overlay.remove(),this.elements=null),this.headerElement=null,document.body.style.overflow="",this.onCloseCallback=null}}const he={showHeader:!1,showCloseButton:!1,showFooter:!0};class pe{constructor(e,t={}){this.container=null,this.header=null,this.body=null,this.footer=null,this.sidebar=null,this.closeBtn=null,this.onCloseCallback=null,this.closeBtnClickHandler=null,this.isDarkMode=!1,this.branding={organizationName:"",logoLightUrl:null,logoDarkUrl:null},this.targetElement=e,this.containerOptions={...he,...t}}createLoading(e,t){if(!this.container){if(this.onCloseCallback=t,le({branding:this.branding,backdropBlur:!1}),this.container=document.createElement("div"),this.container.className="svu-inline-container",this.containerOptions.showHeader&&(this.header=this.createHeader(this.branding),this.container.appendChild(this.header)),this.body=document.createElement("div"),this.body.className="svu-body",this.container.appendChild(this.body),this.containerOptions.showFooter){this.footer=document.createElement("div"),this.footer.className="svu-footer";const e=document.createElement("span");e.className="svu-footer-text",e.appendChild(document.createTextNode("Secured by "));const t=document.createElement("a");t.href="https://sparkvault.com",t.target="_blank",t.rel="noopener noreferrer",t.className="svu-footer-link",t.textContent="SparkVault",e.appendChild(t),this.footer.appendChild(e),this.container.appendChild(this.footer)}this.targetElement.innerHTML="",this.targetElement.appendChild(this.container)}}updateBranding(e){if(this.container&&(this.branding=e,this.containerOptions.showHeader&&this.header)){const t=this.createHeader(e);this.container.replaceChild(t,this.header),this.header=t}}updateBackdropBlur(e){}setDarkMode(e){if(this.container&&this.isDarkMode!==e&&(this.isDarkMode=e,e?this.container.classList.add("svu-dark"):this.container.classList.remove("svu-dark"),this.containerOptions.showHeader&&this.header)){const e=this.createHeader(this.branding);this.container.replaceChild(e,this.header),this.header=e}}toggleSidebar(e){this.container&&(e&&!this.sidebar?(this.sidebar=this.createSecuritySidebar(),this.container.classList.add("svu-with-sidebar"),this.container.appendChild(this.sidebar)):!e&&this.sidebar&&(this.container.classList.remove("svu-with-sidebar"),this.sidebar.remove(),this.sidebar=null))}getBody(){return this.body}getSidebar(){return this.sidebar}isOpen(){return null!==this.container}destroy(){this.closeBtn&&this.closeBtnClickHandler&&(this.closeBtn.removeEventListener("click",this.closeBtnClickHandler),this.closeBtnClickHandler=null,this.closeBtn=null),this.container&&this.container.parentNode&&this.container.remove(),this.container=null,this.header=null,this.body=null,this.footer=null,this.sidebar=null,this.onCloseCallback=null}createHeader(e){const t=document.createElement("div");t.className="svu-header";const n=document.createElement("div");n.className="svu-header-title";const s=this.isDarkMode?e.logoDarkUrl||e.logoLightUrl:e.logoLightUrl||e.logoDarkUrl;if(s){const t=document.createElement("img");t.className="svu-logo",t.src=s,t.alt=e.organizationName,n.appendChild(t);const i=document.createElement("span");i.className="svu-sr-only",i.textContent=e.organizationName,n.appendChild(i)}else if(e.organizationName){const t=document.createElement("h2");t.className="svu-company-name",t.textContent=e.organizationName,n.appendChild(t)}return t.appendChild(n),this.containerOptions.showCloseButton&&(this.closeBtn=document.createElement("button"),this.closeBtn.className="svu-close-btn",this.closeBtn.setAttribute("aria-label","Close"),this.closeBtn.innerHTML='\n <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M18 6L6 18M6 6l12 12"/>\n </svg>\n ',this.closeBtnClickHandler=()=>this.handleClose(),this.closeBtn.addEventListener("click",this.closeBtnClickHandler),t.appendChild(this.closeBtn)),t}createSecuritySidebar(){const e=document.createElement("div");e.className="svu-sidebar";const t=document.createElement("div");if(t.className="svu-sidebar-logo",this.branding.logoDarkUrl){const e=document.createElement("img");e.src=this.branding.logoDarkUrl,e.alt=this.branding.organizationName,t.appendChild(e)}e.appendChild(t);const n=document.createElement("div");n.className="svu-sidebar-intro",n.innerHTML="\n <h3>Secure File Transfer</h3>\n <p>This file is secured with SparkVault's advanced multi-key cryptography and end-to-end encryption.</p>\n ",e.appendChild(n);const s=document.createElement("div");return s.className="svu-sidebar-security",s.innerHTML='\n <h4>\n <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>\n </svg>\n SparkVault\'s Triple Zero-Trust\n </h4>\n <p class="svu-security-intro">\n Decryption requires <strong>three independent Master Keys</strong>, held in three different physical locations, by three separate companies, all cryptographically combined in real-time.\n </p>\n <div class="svu-key-chain">\n <div class="svu-key-item">\n <div class="svu-key-number">1</div>\n <div class="svu-key-info">\n <span class="svu-key-name">Kyber-1024 Post-Quantum Key</span>\n <span class="svu-key-algo">ML-KEM lattice-based encapsulation</span>\n <span class="svu-key-desc">Quantum-resistant key exchange</span>\n </div>\n </div>\n <div class="svu-key-connector">\n <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 5v14M5 12h14"/>\n </svg>\n </div>\n <div class="svu-key-item">\n <div class="svu-key-number">2</div>\n <div class="svu-key-info">\n <span class="svu-key-name">X25519 Ephemeral Key</span>\n <span class="svu-key-algo">Elliptic-curve Diffie-Hellman</span>\n <span class="svu-key-desc">Perfect forward secrecy</span>\n </div>\n </div>\n <div class="svu-key-connector">\n <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M12 5v14M5 12h14"/>\n </svg>\n </div>\n <div class="svu-key-item">\n <div class="svu-key-number">3</div>\n <div class="svu-key-info">\n <span class="svu-key-name">HKDF-SHA512 Derived Key</span>\n <span class="svu-key-algo">Hardware-bound key derivation</span>\n <span class="svu-key-desc">Vault master key in secure enclave</span>\n </div>\n </div>\n </div>\n <div class="svu-encryption-result">\n <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M20 6L9 17l-5-5"/>\n </svg>\n <span>Combined via <strong>HKDF</strong> producing <strong>AES-256-GCM</strong> authenticated cipher</span>\n </div>\n <div class="svu-security-badges">\n <span class="svu-tls-badge">TLS 1.3 In Transit</span>\n <span class="svu-tls-badge">Encrypted At Rest</span>\n <span class="svu-tls-badge">Zero Trust</span>\n </div>\n ',e.appendChild(s),e}handleClose(){this.onCloseCallback&&this.onCloseCallback()}}const ue=[{text:"File received by Forge",duration:600},{text:"Deriving Triple Zero-Trust encryption keys",duration:800},{text:"Encrypting with AES-256-GCM",duration:900},{text:"Generating cryptographic signatures",duration:700},{text:"Storing in post-quantum protected vault",duration:600},{text:"Verifying integrity",duration:400}];function me(e){const t=new Uint8Array(e.match(/.{2}/g).map(e=>parseInt(e,16)));return btoa(String.fromCharCode(...t)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function ve(e){if(0===e)return"0 B";const t=Math.floor(Math.log(e)/Math.log(1024));return`${parseFloat((e/Math.pow(1024,t)).toFixed(2))} ${["B","KB","MB","GB","TB"][t]}`}class ge{constructor(e,t,n,s){this.viewState={view:"loading"},this.config=null,this.selectedFile=null,this.fileInputElement=null,this.uploadModal=null,this.isInlineMode=!1,this.hybridModalInitialized=!1,this.pasteHandler=null,this.container=e,this.api=t,this.options=n,this.callbacks=s,this.isInlineMode=!!n.target}async start(){this.container.createLoading({backdropBlur:this.options.backdropBlur},()=>this.handleClose()),this.setState({view:"loading"});try{const[e]=await Promise.all([this.api.getVaultUploadInfo(this.options.vaultId),new Promise(e=>setTimeout(e,1e3))]);this.config=e,this.container.updateBranding(e.branding),this.setState({view:"form",config:e})}catch(e){this.handleApiError(e)}}close(){this.cleanupFileInput(),this.cleanupPasteHandler(),this.uploadModal&&(this.uploadModal.destroy(),this.uploadModal=null,this.hybridModalInitialized=!1),this.container.destroy()}handleClose(){this.close(),this.callbacks.onCancel()}setState(e){this.viewState=e,this.render()}render(){const e="uploading"===this.viewState.view||"ceremony"===this.viewState.view;if(this.isInlineMode&&e)return void this.renderInModal();if(this.uploadModal){this.uploadModal.destroy(),this.uploadModal=null,this.hybridModalInitialized=!1;const e=this.container.getBody();e?.parentElement&&(e.parentElement.style.display="")}const t=this.container.getBody();if(!t)return;t.innerHTML="";const n=this.container;switch("setDarkMode"in n&&!this.isInlineMode&&(n.setDarkMode(e),this.container.toggleSidebar(e)),this.viewState.view){case"loading":t.appendChild(this.renderLoading());break;case"form":t.appendChild(this.renderForm(this.viewState.config,this.viewState.error));break;case"uploading":t.appendChild(this.renderUploading(this.viewState));break;case"ceremony":t.appendChild(this.renderCeremony(this.viewState));break;case"complete":t.appendChild(this.renderComplete(this.viewState.result,this.viewState.config));break;case"error":t.appendChild(this.renderError(this.viewState))}}renderInModal(){this.uploadModal||(this.uploadModal=new ce,this.uploadModal.createLoading({backdropBlur:this.options.backdropBlur??!0},()=>{}),this.config&&this.uploadModal.updateBranding(this.config.branding),this.hybridModalInitialized=!1);const e=this.uploadModal.getBody();if(e){if(!this.hybridModalInitialized){const e=this.container.getBody();e?.parentElement&&(e.parentElement.style.display="none"),this.uploadModal.setDarkMode(!0),this.uploadModal.toggleSidebar(!0),this.hybridModalInitialized=!0}e.innerHTML="","uploading"===this.viewState.view?e.appendChild(this.renderUploading(this.viewState)):"ceremony"===this.viewState.view&&e.appendChild(this.renderCeremony(this.viewState))}}renderLoading(){const e=document.createElement("div");return e.className="svu-loading",e.innerHTML='\n <div class="svu-spinner"></div>\n <p class="svu-loading-text">Establishing secure connection...</p>\n ',e}renderForm(e,t){const n=document.createElement("div");n.className="svu-form-view";const s=document.createElement("h2");s.className="svu-title",s.textContent="Cryptographically Secure File Transfer",n.appendChild(s);const i=document.createElement("p");if(i.className="svu-subtitle",i.innerHTML=`Destination Vault: <strong>${this.escapeHtml(e.vaultName)}</strong>`,n.appendChild(i),t){const e=document.createElement("div");e.className="svu-error-alert",e.textContent=t,n.appendChild(e)}const o=document.createElement("div");if(o.className="svu-drop-zone"+(this.selectedFile?" svu-has-file":""),this.selectedFile){o.innerHTML=`\n <div class="svu-selected-file">\n <div class="svu-file-icon">\n <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/>\n <path d="M14 2v6h6"/>\n </svg>\n </div>\n <div class="svu-file-info">\n <span class="svu-file-name">${this.escapeHtml(this.selectedFile.name)}</span>\n <span class="svu-file-size">${ve(this.selectedFile.size)}</span>\n </div>\n <button class="svu-remove-file" aria-label="Remove file">&times;</button>\n </div>\n `;const e=o.querySelector(".svu-remove-file");e&&e.addEventListener("click",e=>{e.stopPropagation(),this.selectedFile=null,this.render()})}else o.innerHTML='\n <div class="svu-drop-icon">\n <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/>\n <path d="M17 8l-5-5-5 5"/>\n <path d="M12 3v12"/>\n </svg>\n </div>\n <p class="svu-drop-text">Drag & drop a file here</p>\n <p class="svu-drop-subtext">or click to select</p>\n <p class="svu-drop-hint">Paste from clipboard also supported (Ctrl+V)</p>\n ';o.addEventListener("dragover",e=>{e.preventDefault(),o.classList.add("svu-dragging")}),o.addEventListener("dragleave",e=>{e.preventDefault(),o.classList.remove("svu-dragging")}),o.addEventListener("drop",t=>{t.preventDefault(),o.classList.remove("svu-dragging");const n=t.dataTransfer?.files[0];n&&this.handleFileSelect(n,e)}),o.addEventListener("click",()=>{this.selectedFile||this.openFileSelector(e)}),n.appendChild(o);const r=document.createElement("p");if(r.className="svu-max-size",r.textContent=`Max upload size: ${ve(e.maxSizeBytes)}`,n.appendChild(r),this.selectedFile){const t=document.createElement("button");t.className="svu-btn svu-btn-primary",t.textContent="Upload Securely",t.onclick=()=>this.startUpload(e),n.appendChild(t)}const a=document.createElement("div");return a.className="svu-metadata",a.innerHTML=`\n <h4>Transfer Details</h4>\n <div class="svu-metadata-grid">\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Recipient</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.branding.organizationName)}</span>\n </div>\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Vault</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.vaultName)}</span>\n </div>\n <div class="svu-metadata-item svu-full-width">\n <span class="svu-metadata-label">Vault ID</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.vaultId)}</span>\n </div>\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Encryption</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.encryption.algorithm)}</span>\n </div>\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Key Derivation</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.encryption.keyDerivation)}</span>\n </div>\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Post-Quantum</span>\n <span class="svu-metadata-value">${this.escapeHtml(e.encryption.postQuantum)}</span>\n </div>\n <div class="svu-metadata-item">\n <span class="svu-metadata-label">Forge Status</span>\n <span class="svu-metadata-value ${"active"===e.forgeStatus?"svu-status-active":""}">\n ${"active"===e.forgeStatus?"● Active":"○ Inactive"}\n </span>\n </div>\n </div>\n `,n.appendChild(a),this.setupPasteHandler(e),n}renderUploading(e){const t=document.createElement("div");t.className="svu-uploading-view";const n=me(e.ingotId.replace("ing_",""));return t.innerHTML=`\n <h2 class="svu-title">Streaming & Encrypting</h2>\n\n <div class="svu-stages">\n <div class="svu-stage svu-active">\n <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <rect x="2" y="3" width="20" height="18" rx="2"/>\n <path d="M2 9h20"/>\n <circle cx="6" cy="6" r="1" fill="currentColor"/>\n <circle cx="10" cy="6" r="1" fill="currentColor"/>\n </svg>\n <span>Your Browser</span>\n </div>\n <div class="svu-stage-arrow svu-active"></div>\n <div class="svu-stage svu-active">\n <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>\n </svg>\n <span>SparkVault</span>\n <span class="svu-stage-sub">Encrypting</span>\n </div>\n <div class="svu-stage-arrow"></div>\n <div class="svu-stage">\n <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <rect x="3" y="3" width="18" height="18" rx="2"/>\n <circle cx="12" cy="12" r="3"/>\n <path d="M12 9V6M12 18v-3M9 12H6M18 12h-3"/>\n </svg>\n <span>${this.escapeHtml(this.config?.branding.organizationName||"")}</span>\n </div>\n </div>\n\n <div class="svu-progress">\n <div class="svu-progress-bar">\n <div class="svu-progress-fill" style="width: ${e.progress}%"></div>\n </div>\n <div class="svu-progress-text">\n <span>${e.progress}%</span>\n <span>${ve(e.bytesUploaded)} / ${ve(e.file.size)}</span>\n </div>\n </div>\n\n <div class="svu-forging-info">\n <p class="svu-forging-line">Forging Ingot ID: <span class="svu-mono">${n}</span></p>\n <p class="svu-forging-line">Secure Transfer Channel: <span class="svu-mono">${e.requestId}</span></p>\n </div>\n `,t}renderCeremony(e){const t=document.createElement("div");t.className="svu-ceremony-view";const n=me(e.ingotId.replace("ing_",""));let s="";return ue.forEach((t,n)=>{const i=n<e.step,o=n===e.step;s+=`\n <div class="svu-ceremony-step ${i?"svu-complete":""} ${o?"svu-active":""}">\n <span class="svu-step-icon">\n ${i?'<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>':o?'<svg class="svu-spin" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/></svg>':""}\n </span>\n <span class="svu-step-text">${t.text}</span>\n </div>\n `}),t.innerHTML=`\n <h2 class="svu-title">Securing Your File</h2>\n\n <div class="svu-ceremony-steps">\n ${s}\n </div>\n\n <div class="svu-forging-info">\n <p class="svu-forging-line">Forging Ingot ID: <span class="svu-mono">${n}</span></p>\n <p class="svu-forging-line">Secure Transfer Channel: <span class="svu-mono">${e.requestId}</span></p>\n </div>\n `,t}renderComplete(e,t){const n=document.createElement("div");n.className="svu-complete-view",n.innerHTML=`\n <div class="svu-success-icon">\n <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>\n <path d="M22 4L12 14.01l-3-3"/>\n </svg>\n </div>\n\n <h2 class="svu-success-title">Your file has been securely sent!</h2>\n\n <div class="svu-success-details">\n <div class="svu-success-row">\n <span class="svu-success-label">Ingot ID</span>\n <span class="svu-success-value svu-mono">\n ${this.escapeHtml(e.ingotId)}\n <button class="svu-copy-btn" aria-label="Copy Ingot ID">\n <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>\n <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/>\n </svg>\n </button>\n </span>\n </div>\n <div class="svu-success-row">\n <span class="svu-success-label">File</span>\n <span class="svu-success-value">${this.escapeHtml(e.filename)}</span>\n </div>\n <div class="svu-success-row">\n <span class="svu-success-label">Size</span>\n <span class="svu-success-value">${ve(e.sizeBytes)}</span>\n </div>\n <div class="svu-success-row">\n <span class="svu-success-label">Timestamp</span>\n <span class="svu-success-value svu-mono">${this.escapeHtml(e.uploadTime)}</span>\n </div>\n <div class="svu-success-row">\n <span class="svu-success-label">Encryption</span>\n <span class="svu-success-value">AES-256-GCM</span>\n </div>\n </div>\n\n <p class="svu-success-guidance">\n ${this.escapeHtml(t.branding.organizationName)} has been notified and can access this file\n from their SparkVault dashboard. Save the Ingot ID above for your records.\n </p>\n\n <button class="svu-btn svu-btn-secondary">\n <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\n <path d="M1 4v6h6M23 20v-6h-6"/>\n <path d="M20.49 9A9 9 0 005.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 013.51 15"/>\n </svg>\n Upload Another File\n </button>\n `;const s=n.querySelector(".svu-copy-btn");s&&s.addEventListener("click",()=>{navigator.clipboard.writeText(e.ingotId)});const i=n.querySelector(".svu-btn-secondary");return i&&i.addEventListener("click",()=>{this.selectedFile=null,this.setState({view:"form",config:t})}),n}renderError(e){const t=document.createElement("div");t.className="svu-error-view";const n=e.httpStatus||404,s=402===n?"Service Unavailable":"Resource Not Found";return t.innerHTML=`\n <div class="svu-error-icon">\n <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">\n <circle cx="12" cy="12" r="10"/>\n <path d="M12 8v4M12 16h.01"/>\n </svg>\n </div>\n\n <h2 class="svu-error-title">${n} - ${s}</h2>\n\n <p class="svu-error-message">${this.escapeHtml(e.message)}</p>\n\n <div class="svu-error-code">\n <span class="svu-error-code-label">Code</span>\n <span class="svu-error-code-value">${this.escapeHtml(e.code)}</span>\n </div>\n `,t}handleFileSelect(e,t){e.size>t.maxSizeBytes?this.setState({view:"form",config:t,error:`File size exceeds maximum allowed (${ve(t.maxSizeBytes)})`}):(this.selectedFile=e,this.setState({view:"form",config:t}))}openFileSelector(e){this.cleanupFileInput(),this.fileInputElement=document.createElement("input"),this.fileInputElement.type="file",this.fileInputElement.style.display="none",this.fileInputElement.onchange=()=>{const t=this.fileInputElement?.files?.[0];t&&this.handleFileSelect(t,e)},document.body.appendChild(this.fileInputElement),this.fileInputElement.click()}cleanupFileInput(){this.fileInputElement&&(this.fileInputElement.remove(),this.fileInputElement=null)}setupPasteHandler(e){this.cleanupPasteHandler(),this.pasteHandler=t=>{const n=t.clipboardData?.items;if(n)for(const t of n)if("file"===t.kind){const n=t.getAsFile();if(n){this.handleFileSelect(n,e);break}}},document.addEventListener("paste",this.pasteHandler)}cleanupPasteHandler(){this.pasteHandler&&(document.removeEventListener("paste",this.pasteHandler),this.pasteHandler=null)}async startUpload(e){if(!this.selectedFile)return;const t=this.selectedFile,n=`${Date.now().toString(16)}${Math.random().toString(16).substring(2,10)}`;try{const{forgeUrl:s,ingotId:i}=await this.api.initiateUpload(e.vaultId,t.name,t.size,t.type||"application/octet-stream");this.setState({view:"uploading",file:t,ingotId:i,requestId:n,progress:0,bytesUploaded:0}),await this.uploadWithTus(t,s,i,n),await this.runCeremony(t,i,n);const o=new Date,r=`${o.getUTCFullYear()}-${String(o.getUTCMonth()+1).padStart(2,"0")}-${String(o.getUTCDate()).padStart(2,"0")} ${String(o.getUTCHours()).padStart(2,"0")}:${String(o.getUTCMinutes()).padStart(2,"0")}:${String(o.getUTCSeconds()).padStart(2,"0")} UTC`,a={ingotId:i,vaultId:e.vaultId,filename:t.name,sizeBytes:t.size,uploadTime:r};this.callbacks.onProgress?.({bytesUploaded:t.size,bytesTotal:t.size,percentage:100,phase:"complete"}),this.setState({view:"complete",result:a,config:e}),this.callbacks.onSuccess(a)}catch(e){this.handleApiError(e)}}async uploadWithTus(e,t,n,s){const i="1.0.0",o=new URL(t),r=o.searchParams.get("istk"),a=`${o.origin}${o.pathname}`;if(!r)throw new Error("Missing ISTK in forge URL");const l=await fetch(a,{method:"POST",headers:{"Tus-Resumable":i,"Upload-Length":String(e.size),"X-ISTK":r,"Content-Type":"application/offset+octet-stream"}});if(!l.ok){const e=await l.text();throw new Error(`Failed to create upload session: ${l.status} ${e}`)}const d=l.headers.get("Location"),c=l.headers.get("X-Chunk-Size"),h=c?parseInt(c,10):52428800;if(!d)throw new Error("Server did not return upload location");const p=d.startsWith("http")?d:`${o.origin}${d}`;let u=0;const m=e.size;for(;u<m;){const t=Math.min(u+h,m),o=e.slice(u,t),r=await fetch(p,{method:"PATCH",headers:{"Tus-Resumable":i,"Upload-Offset":String(u),"Content-Type":"application/offset+octet-stream"},body:o});if(!r.ok){const e=await r.text();throw new Error(`Chunk upload failed: ${r.status} ${e}`)}const a=r.headers.get("Upload-Offset");u=a?parseInt(a,10):t;const l=Math.round(u/m*100);this.setState({view:"uploading",file:e,ingotId:n,requestId:s,progress:l,bytesUploaded:u}),this.callbacks.onProgress?.({bytesUploaded:u,bytesTotal:m,percentage:l,phase:"uploading"})}}async runCeremony(e,t,n){for(let s=0;s<ue.length;s++)this.setState({view:"ceremony",file:e,ingotId:t,requestId:n,step:s,complete:!1}),this.callbacks.onProgress?.({bytesUploaded:e.size,bytesTotal:e.size,percentage:100,phase:"ceremony"}),await new Promise(e=>setTimeout(e,ue[s].duration));this.setState({view:"ceremony",file:e,ingotId:t,requestId:n,step:ue.length,complete:!0}),await new Promise(e=>setTimeout(e,500))}handleApiError(e){if(e instanceof oe)404===e.httpStatus?this.setState({view:"error",message:"The requested upload endpoint could not be located. This may occur if the upload link has expired, been disabled, or was entered incorrectly.",code:"NOT_FOUND",httpStatus:404}):402===e.httpStatus?this.setState({view:"error",message:"This upload endpoint has been temporarily disabled due to an account billing issue. Please contact the organization's administrator.",code:"PAYMENT_REQUIRED",httpStatus:402}):this.setState({view:"error",message:e.message,code:e.code,httpStatus:e.httpStatus}),this.callbacks.onError(e);else if(e instanceof Error)this.setState({view:"error",message:e.message,code:"UNKNOWN_ERROR"}),this.callbacks.onError(e);else{const e=new Error("An unexpected error occurred");this.setState({view:"error",message:e.message,code:"UNKNOWN_ERROR"}),this.callbacks.onError(e)}}escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}}class be{constructor(e){this.renderer=null,this.attachedElements=new Map,this.config=e,this.api=new re(e)}async upload(e){if(!e.vaultId)throw new i("vaultId is required");this.renderer&&this.renderer.close();const t=!!e.target?this.createInlineContainer(e.target):new ce,n={...e,backdropBlur:e.backdropBlur??this.config.backdropBlur};return new Promise((s,i)=>{this.renderer=new ge(t,this.api,n,{onSuccess:t=>{e.onSuccess?.(t),s(t)},onError:t=>{e.onError?.(t),i(t)},onCancel:()=>{const t=new a;e.onCancel?.(),i(t)},onProgress:e.onProgress}),this.renderer.start().catch(t=>{e.onError?.(t),i(t)})})}attach(e,t){if(!t.vaultId)throw new i("vaultId is required");let n=null;const s=async e=>{e.preventDefault();try{await this.upload({vaultId:t.vaultId,onSuccess:t.onSuccess,onError:t.onError,onCancel:t.onCancel,onProgress:t.onProgress})}catch{}};return se(()=>{document.querySelectorAll(e).forEach(e=>{e.addEventListener("click",s),this.attachedElements.set(e,()=>{e.removeEventListener("click",s)})}),n=new MutationObserver(t=>{t.forEach(t=>{t.addedNodes.forEach(t=>{t instanceof Element&&(t.matches(e)&&(t.addEventListener("click",s),this.attachedElements.set(t,()=>{t.removeEventListener("click",s)})),t.querySelectorAll(e).forEach(e=>{this.attachedElements.has(e)||(e.addEventListener("click",s),this.attachedElements.set(e,()=>{e.removeEventListener("click",s)}))}))})})}),n.observe(document.body,{childList:!0,subtree:!0})}),()=>{n?.disconnect(),this.attachedElements.forEach(e=>e()),this.attachedElements.clear()}}close(){this.renderer&&(this.renderer.close(),this.renderer=null)}createInlineContainer(e){let t;if("string"==typeof e){const n=document.querySelector(e);if(!(n&&n instanceof HTMLElement))throw new i(`Target selector "${e}" did not match any element`);t=n}else{if(!(e instanceof HTMLElement))throw new i("Target must be a CSS selector string or HTMLElement");t=e}return new pe(t)}async pop(e){return this.upload(e)}async render(e){return this.upload(e)}}class fe{constructor(e,t){this.http=t,this.uploadModule=new be(e);const n=e=>this.uploadModule.upload(e);n.attach=(e,t)=>this.uploadModule.attach(e,t),n.close=()=>this.uploadModule.close(),this.upload=n}async create(e){this.validateCreateOptions(e);const t=await this.http.post("/v1/vaults",{name:e.name,hosted_vmk:e.hostedVmk??!1});return{id:t.data.vault_id,name:t.data.name,vmk:t.data.vmk,createdAt:t.data.created_at,hostedVmk:t.data.hosted_vmk}}async unseal(e,t){if(!e)throw new i("Vault ID is required");if(!t)throw new i("VMK is required");const n=e.startsWith("vlt_")?e:`vlt_${e}`,s=await this.http.post(`/v1/vaults/${n}/unseal`,{vmk:t});return{id:s.data.vault_id,name:s.data.name,vatToken:s.data.vat_token,expiresAt:s.data.expires_at,ingotCount:s.data.ingot_count,storageBytes:s.data.storage_bytes}}async list(){return(await this.http.get("/v1/vaults")).data.vaults.map(e=>({id:e.vault_id,name:e.name,createdAt:e.created_at,ingotCount:e.ingot_count,storageBytes:e.storage_bytes,hostedVmk:e.hosted_vmk}))}async delete(e,t){const n=e.startsWith("vlt_")?e:`vlt_${e}`;await this.http.delete(`/v1/vaults/${n}`,{headers:{"X-VMK":t}})}async uploadIngot(e,t){this.validateUploadOptions(t);const n=t.name??(t.file instanceof File?t.file.name:"unnamed"),s=t.contentType??t.file.type??"application/octet-stream",i=await this.http.post(`/v1/vaults/${e.id}/ingots`,{name:n,content_type:s,size_bytes:t.file.size},{headers:{Authorization:`Bearer ${e.vatToken}`}});return{id:i.data.ingot_id,name:i.data.name,contentType:i.data.content_type,size:i.data.size_bytes,createdAt:i.data.created_at,type:i.data.ingot_type}}async downloadIngot(e,t){const n=t.startsWith("ing_")?t:`ing_${t}`;return this.http.requestRaw(`/v1/vaults/${e.id}/ingots/${n}/download`,{method:"POST",headers:{Authorization:`Bearer ${e.vatToken}`}})}async listIngots(e){return(await this.http.get(`/v1/vaults/${e.id}/ingots`,{headers:{Authorization:`Bearer ${e.vatToken}`}})).data.ingots.map(e=>({id:e.ingot_id,name:e.name,contentType:e.content_type,size:e.size_bytes,createdAt:e.created_at,type:e.ingot_type}))}async deleteIngot(e,t){const n=t.startsWith("ing_")?t:`ing_${t}`;await this.http.delete(`/v1/vaults/${e.id}/ingots/${n}`,{headers:{Authorization:`Bearer ${e.vatToken}`}})}validateCreateOptions(e){if(!e.name?.trim())throw new i("Vault name is required");if(e.name.length>255)throw new i("Vault name must be 255 characters or less")}validateUploadOptions(e){if(!e.file)throw new i("File is required");if(0===e.file.size)throw new i("File cannot be empty")}}let ke=!1;function ye(e){ke=e,e&&xe("info","Debug mode enabled")}function xe(e,t,...n){const s=`[SparkVault] ${(new Date).toISOString().split("T")[1].slice(0,-1)} ${t}`;switch(e){case"debug":ke&&console.debug(s,...n);break;case"info":ke&&console.log(s,...n);break;case"warn":ke&&console.warn(s,...n);break;case"error":console.error(s,...n)}}const we={debug:(e,...t)=>xe("debug",e,...t),info:(e,...t)=>xe("info",e,...t),warn:(e,...t)=>xe("warn",e,...t),error:(e,...t)=>xe("error",e,...t),group:e=>{ke&&console.group(`[SparkVault] ${e}`)},groupEnd:()=>{ke&&console.groupEnd()}};class Ce{constructor(e){!function(e){if(!e.accountId)throw new i("accountId is required",{field:"accountId"});if("string"!=typeof e.accountId)throw new i("accountId must be a string",{field:"accountId",received:typeof e.accountId});if(!e.accountId.startsWith("acc_"))throw new i('accountId must start with "acc_"',{field:"accountId",received:e.accountId})}(e),this.config=function(e){return{accountId:e.accountId,timeout:e.timeout??3e4,apiBaseUrl:"https://api.sparkvault.com",identityBaseUrl:"https://api.sparkvault.com/v1/apps/identity",preloadConfig:!1!==e.preloadConfig,backdropBlur:!1!==e.backdropBlur}}(e);const t=new u(this.config);this.identity=new ie(this.config),this.vaults=new fe(this.config,t)}static init(e){return new Ce(e)}static get version(){return"__VERSION__"}}if("undefined"!=typeof window){const e=function(e){if("undefined"==typeof window||"undefined"==typeof document)return null;const t=document.currentScript;if(!t)return null;const n=t.dataset.accountId;if("true"===t.dataset.debug&&ye(!0),!n)return we.debug("No data-account-id attribute, skipping auto-init"),null;we.info("Auto-initializing SDK...");try{const t=e.init({accountId:n});return we.info("SDK initialized successfully"),t}catch(e){return we.error("Failed to initialize SDK:",e),null}}(Ce);window.SparkVault=e??Ce}e.AuthenticationError=n,e.AuthorizationError=s,e.NetworkError=o,e.PopupBlockedError=l,e.SparkVault=Ce,e.SparkVaultError=t,e.TimeoutError=r,e.UserCancelledError=a,e.ValidationError=i,e.default=Ce,e.logger=we,e.setDebugMode=ye,Object.defineProperty(e,"__esModule",{value:!0})});
2
2
  //# sourceMappingURL=sparkvault.js.map