@sparkvault/sdk 1.1.5 → 1.1.6
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.
- package/dist/identity/views/icons.d.ts +0 -4
- package/dist/identity/views/totp-verify.d.ts +1 -1
- package/dist/sparkvault.cjs.js +24 -106
- package/dist/sparkvault.cjs.js.map +1 -1
- package/dist/sparkvault.esm.js +24 -106
- package/dist/sparkvault.esm.js.map +1 -1
- package/dist/sparkvault.js +1 -1
- package/dist/sparkvault.js.map +1 -1
- package/package.json +1 -1
package/dist/sparkvault.js
CHANGED
|
@@ -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,i,s){super(e),this.name="SparkVaultError",this.code=n,this.statusCode=i,this.details=s,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 i extends t{constructor(e,t){super(e,"authorization_error",403,t),this.name="AuthorizationError",Object.setPrototypeOf(this,i.prototype)}}class s extends t{constructor(e,t){super(e,"validation_error",400,t),this.name="ValidationError",Object.setPrototypeOf(this,s.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,i){const s=t*Math.pow(2,e),o=Math.min(s,n);return o+o*i*Math.random()}function h(e){return new Promise(t=>setTimeout(t,e))}async function p(e,t={}){const n={...d,...t};let i;for(let t=0;t<n.maxAttempts;t++)try{return await e()}catch(e){i=e;if(t===n.maxAttempts-1||!n.isRetryable(e))throw e;const s=c(t,n.baseDelayMs,n.maxDelayMs,n.jitterFactor);await h(s)}throw i}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:i="GET",headers:s={},body:a,timeout:l=this.config.timeout}=n,d=`${this.config.apiBaseUrl}${e}`,c={"Content-Type":"application/json",Accept:"application/json",...s},h=new AbortController,p=setTimeout(()=>h.abort(),l);try{const e=await fetch(d,{method:i,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 i=e.headers.get("content-type");if(i?.includes("application/json")){return await e.json()}const s=await e.text();throw new t(`Unexpected response format: expected JSON but received ${i??"unknown content type"}`,"invalid_response_format",e.status,{body:s.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 s(a,d);case 401:return new n(a,d);case 403:return new i(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:i="GET",headers:s={},body:a,timeout:l=this.config.timeout}=n,d=`${this.config.apiBaseUrl}${e}`,c={"Content-Type":"application/json",...s},h=new AbortController,p=setTimeout(()=>h.abort(),l);try{const e=await fetch(d,{method:i,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 g{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 b(e){let t="";for(let n=0;n<e.byteLength;n++)t+=String.fromCharCode(e[n]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function y(e){return b(new Uint8Array(e))}function f(e){const t=e.replace(/-/g,"+").replace(/_/g,"/"),n=t+"=".repeat((4-t.length%4)%4),i=atob(n),s=new Uint8Array(i.length);for(let e=0;e<i.length;e++)s[e]=i.charCodeAt(e);return s}function k(e){const t=f(e),n=new ArrayBuffer(t.length);return new Uint8Array(n).set(t),n}function v(e){const t=f(e);return Array.from(t)}class w{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 i=`${this.baseUrl}${t}`,s={"Content-Type":"application/json",Accept:"application/json"},o=new AbortController,r=setTimeout(()=>o.abort(),this.timeoutMs);try{const t=await fetch(i,{method:e,headers:s,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",i=e.code||r.code||"api_error";throw new x(n,i,t.status)}return r.data??r}catch(e){if(e instanceof Error&&"AbortError"===e.name)throw new x("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})}async sendTotp(e){return this.request("POST","/totp/send",{recipient:e.recipient,method:e.method})}async verifyTotp(e){return this.request("POST","/totp/verify",{kindling:e.kindling,pin:e.pin,recipient:e.recipient})}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:y(e.credential.rawId),type:e.credential.type,response:{clientDataJSON:y(t.clientDataJSON),attestationObject:y(t.attestationObject)}}})}async startPasskeyVerify(e){const t=await this.request("POST","/passkey/verify",{email:e});return{challenge:t.options.challenge,rpId:t.options.rpId,rpName:"SparkVault Identity",timeout:t.options.timeout,allowCredentials:t.options.allowCredentials,session:t.session}}async completePasskeyVerify(e){const t=e.credential.response;return this.request("POST","/passkey/verify/complete",{session:e.session,credential:{id:e.credential.id,rawId:y(e.credential.rawId),type:e.credential.type,response:{clientDataJSON:y(t.clientDataJSON),authenticatorData:y(t.authenticatorData),signature:y(t.signature),userHandle:t.userHandle?y(t.userHandle):null}}})}getSocialAuthUrl(e,t,n){const i=new URLSearchParams({redirect_uri:t,state:n});return`${this.baseUrl}/social/${e}?${i}`}getEnterpriseAuthUrl(e,t,n){const i=new URLSearchParams({redirect_uri:t,state:n});return`${this.baseUrl}/saml/${e}?${i}`}async sendSparkLink(e){return this.request("POST","/sparklink/send",{email:e,type:"verify_identity",openerOrigin:"undefined"!=typeof window?window.location.origin:void 0})}async checkSparkLinkStatus(e){return this.request("GET",`/sparklink/status/${e}`)}}class x extends Error{constructor(e,t,n){super(e),this.name="IdentityApiError",this.code=t,this.statusCode=n}}const C={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:"magic_link",identityType:"email",name:"SparkLink",description:"Get an instant sign-in magic 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"},enterprise_okta:{id:"enterprise_okta",type:"enterprise",identityType:"email",name:"Okta",description:"Sign in with Okta SSO",icon:"okta",provider:"okta"},enterprise_entra:{id:"enterprise_entra",type:"enterprise",identityType:"email",name:"Microsoft Entra",description:"Sign in with Microsoft Entra ID",icon:"microsoft",provider:"entra"},enterprise_onelogin:{id:"enterprise_onelogin",type:"enterprise",identityType:"email",name:"OneLogin",description:"Sign in with OneLogin SSO",icon:"onelogin",provider:"onelogin"},enterprise_ping:{id:"enterprise_ping",type:"enterprise",identityType:"email",name:"Ping Identity",description:"Sign in with Ping Identity",icon:"ping",provider:"ping"},enterprise_jumpcloud:{id:"enterprise_jumpcloud",type:"enterprise",identityType:"email",name:"JumpCloud",description:"Sign in with JumpCloud SSO",icon:"jumpcloud",provider:"jumpcloud"},hris_bamboohr:{id:"hris_bamboohr",type:"hris",identityType:"email",name:"BambooHR",description:"Sign in with BambooHR",icon:"bamboohr",provider:"bamboohr"},hris_workday:{id:"hris_workday",type:"hris",identityType:"email",name:"Workday",description:"Sign in with Workday",icon:"workday",provider:"workday"},hris_adp:{id:"hris_adp",type:"hris",identityType:"email",name:"ADP",description:"Sign in with ADP",icon:"adp",provider:"adp"},hris_gusto:{id:"hris_gusto",type:"hris",identityType:"email",name:"Gusto",description:"Sign in with Gusto",icon:"gusto",provider:"gusto"},hris_rippling:{id:"hris_rippling",type:"hris",identityType:"email",name:"Rippling",description:"Sign in with Rippling",icon:"rippling",provider:"rippling"}};function E(e){return e.map(e=>C[e]).filter(e=>void 0!==e)}class S{constructor(){this._sdkConfig=null,this._viewState={view:"loading"},this._recipient="",this._identityType="email",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=E(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 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 i=new Date;i.setTime(i.getTime()+24*n*60*60*1e3);const s=`expires=${i.toUTCString()}`;document.cookie=`${e}=${t}; ${s}; 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._passkey.hasPasskey=null,this._passkey.isFallbackMode=!1,this._passkey.pendingResult=null,this._totp.kindling="",this._totp.method=null}}class T{constructor(e,t){this.api=e,this.state=t}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(){try{const e=await this.api.startPasskeyRegister(this.state.recipient),t={challenge:k(e.challenge),rp:{id:e.rpId,name:e.rpName},user:{id:k(e.userId??this.state.recipient),name:this.state.recipient,displayName:this.state.recipient},pubKeyCredParams:[{type:"public-key",alg:-7},{type:"public-key",alg:-257}],timeout:e.timeout,authenticatorSelection:{residentKey:"preferred",userVerification:"preferred"},attestation:"none"},n=await navigator.credentials.create({publicKey:t});if(!n)return{success:!1,error:"Failed to create passkey",errorType:"unknown"};const i=await this.api.completePasskeyRegister({session:e.session,credential:n});return this.state.setPasskeyStatus(!0),{success:!0,result:{token:i.token,identity:i.identity,identityType:i.identity_type}}}catch(e){return this.handleError(e)}}async verify(){try{const e=await this.api.startPasskeyVerify(this.state.recipient),t={challenge:k(e.challenge),rpId:e.rpId,timeout:e.timeout,userVerification:"preferred",allowCredentials:e.allowCredentials?.map(e=>({id:k(e.id),type:e.type,transports:["internal","hybrid","usb","ble","nfc"]}))},n=await navigator.credentials.get({publicKey:t});if(!n)return{success:!1,error:"Failed to verify passkey",errorType:"unknown"};const i=await this.api.completePasskeyVerify({session:e.session,credential:n});return{success:!0,result:{token:i.token,identity:i.identity,identityType:i.identity_type}}}catch(e){return this.handleError(e)}}handleError(e){const t=e instanceof Error?e.message:String(e);return t.includes("not allowed")||t.includes("cancelled")?{success:!1,error:"Authentication was cancelled. Please try again.",errorType:"cancelled"}:t.includes("No credentials")?{success:!1,error:"No passkey found. Create one to continue.",errorType:"not_found"}:{success:!1,error:t||"Passkey authentication failed",errorType:"unknown"}}}class B{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});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});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});return n.verified&&n.token?{success:!0,result:{token:n.token,identity:n.identity??this.state.recipient,identityType:n.identity_type??this.state.identityType}}:(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 L{constructor(e,t){this.api=e,this.state=t}async send(){try{const e=await this.api.sendSparkLink(this.state.recipient);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}}catch{return{verified:!1}}}}const A="sparkvault-identity-styles";function P(e){if(document.getElementById(A))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=A,t.textContent=function(e){const{branding:t,backdropBlur:n}=e,i="dark"===$(t),s=!1!==n,o={primary:"#4F46E5",primaryHover:"#4338CA",primaryLight:i?"rgba(99, 102, 241, 0.15)":"rgba(79, 70, 229, 0.08)",error:"#DC2626",errorLight:i?"rgba(220, 38, 38, 0.15)":"#FEF2F2",errorBorder:i?"rgba(220, 38, 38, 0.3)":"#FECACA",bg:i?"#0F0F0F":"#FFFFFF",bgSubtle:i?"#171717":"#FAFAFA",bgHover:i?"#1A1A1A":"#F5F5F5",bgInput:i?"#0F0F0F":"#FFFFFF",border:i?"#262626":"#E5E5E5",borderHover:i?"#404040":"#D4D4D4",borderFocus:"#4F46E5",textPrimary:i?"#FAFAFA":"#0A0A0A",textSecondary:i?"#A3A3A3":"#525252",textMuted:"#737373",textOnPrimary:"#FFFFFF",shadowSm:i?"0 1px 2px rgba(0, 0, 0, 0.4)":"0 1px 2px rgba(0, 0, 0, 0.04)",shadowLg:i?"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, ${i?"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(${s?"8px":"0"});\n -webkit-backdrop-filter: blur(${s?"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 overflow: hidden;\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;\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: ${i?"#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-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-top: 16px;\n }\n\n .sv-resend-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\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 transition: color 0.15s ease;\n }\n\n .sv-resend-btn:hover:not(:disabled) {\n color: ${o.primary};\n }\n\n .sv-resend-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .sv-resend-btn svg {\n width: 14px;\n height: 14px;\n }\n\n .sv-resend-timer {\n font-size: 13px;\n font-weight: 500;\n color: ${o.textMuted};\n min-width: 24px;\n text-align: right;\n }\n\n .sv-alt-method-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: 16px 0 0 0;\n margin-top: 8px;\n transition: color 0.15s ease;\n }\n\n .sv-alt-method-link:hover {\n color: ${o.primary};\n text-decoration: underline;\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 height: 100%;\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 overflow: hidden;\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;\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 _(e,t=16,n=16){const i=document.createElementNS("http://www.w3.org/2000/svg","svg");return i.setAttribute("viewBox",e),i.setAttribute("width",String(t)),i.setAttribute("height",String(n)),i.setAttribute("fill","none"),i.setAttribute("xmlns","http://www.w3.org/2000/svg"),i}function I(e,t={}){const n=document.createElementNS("http://www.w3.org/2000/svg","path");n.setAttribute("d",e);for(const[e,i]of Object.entries(t))n.setAttribute(e,i);return n}function F(e,t,n,i={}){const s=document.createElementNS("http://www.w3.org/2000/svg","circle");s.setAttribute("cx",String(e)),s.setAttribute("cy",String(t)),s.setAttribute("r",String(n));for(const[e,t]of Object.entries(i))s.setAttribute(e,t);return s}function H(){const e=_("0 0 16 16");return e.appendChild(I("M13.25 4.75L6 12 2.75 8.75",{stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"})),e}function M(){const e=_("0 0 16 16");return e.appendChild(I("M10 12L6 8L10 4",{stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"})),e}function N(){const e=_("0 0 20 20",20,20);return e.appendChild(I("M15 5L5 15M5 5L15 15",{stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"})),e}function R(){const e=_("0 0 56 56",56,56);e.classList.add("sv-passkey-icon"),e.appendChild(F(28,28,26,{fill:"#EEF2FF",stroke:"#E0E7FF","stroke-width":"1"}));const t=document.createElementNS("http://www.w3.org/2000/svg","g");return t.setAttribute("transform","translate(16, 16)"),t.appendChild(I("M12 8c-2.21 0-4 1.79-4 4 0 1.56.9 2.93 2.21 3.6L10 20v2h4v-2l-.21-4.4C15.1 14.93 16 13.56 16 12c0-2.21-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z",{fill:"#4F46E5"})),e.appendChild(t),e}function O(e){switch(e){case"email":default:return z();case"phone":return function(){const e=_("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(I("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=_("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(I("M2 10c0-4.42 3.58-8 8-8 2.65 0 5 1.29 6.47 3.28")),e.appendChild(I("M4 16.58c.42-.35.79-.76 1.1-1.22")),e.appendChild(I("M14.41 17.51c.09-.45.32-1.73.38-2.26")),e.appendChild(I("M10 8.33a1.5 1.5 0 00-1.5 1.5c0 .77-.08 1.88-.2 3")),e.appendChild(I("M7.21 18.33c.16-.5.34-1 .43-1.5")),e.appendChild(I("M11.67 10.93c0 1.79 0 4.79-.75 6.65")),e.appendChild(I("M17.33 13.33c.15-1.5.1-4.02 0-4.5")),e.appendChild(I("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=_("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(I("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(I("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=_("0 0 20 20",20,20);return e.appendChild(I("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(I("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(I("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(I("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=_("0 0 20 20",20,20);return e.setAttribute("fill","currentColor"),e.appendChild(I("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=_("0 0 20 20",20,20);return e.appendChild(I("M1 1h8.33v8.33H1z",{fill:"#F25022"})),e.appendChild(I("M1 10.67h8.33V19H1z",{fill:"#00A4EF"})),e.appendChild(I("M10.67 1H19v8.33h-8.33z",{fill:"#7FBA00"})),e.appendChild(I("M10.67 10.67H19V19h-8.33z",{fill:"#FFB900"})),e}();case"github":return function(){const e=_("0 0 20 20",20,20);return e.setAttribute("fill","currentColor"),e.appendChild(I("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=_("0 0 20 20",20,20);return e.appendChild(I("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=_("0 0 20 20",20,20);return e.appendChild(I("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=_("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(I("M2.5 17.5h15")),e.appendChild(I("M4.17 17.5V5.83L10.83 2.5v15")),e.appendChild(I("M15.83 17.5V9.17l-5-3.33")),e.appendChild(I("M7.5 7.5v.01")),e.appendChild(I("M7.5 10v.01")),e.appendChild(I("M7.5 12.5v.01")),e.appendChild(I("M7.5 15v.01")),e}();case"users":return function(){const e=_("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(I("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(F(7.5,5.83,3.33,{stroke:"currentColor","stroke-width":"1.5",fill:"none"})),e.appendChild(I("M19.17 17.5v-1.67a3.33 3.33 0 00-2.5-3.22")),e.appendChild(I("M13.33 2.6a3.33 3.33 0 010 6.46")),e}()}}function z(){const e=_("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(I("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(I("M18 5l-8 5.5L2 5")),e}const D={companyName:"",logoLight:null,logoDark:null};class V{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:D,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);P({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 i=document.createElement("div");i.id="sparkvault-identity-modal",i.className="sv-modal";const s=this.createHeader(e.branding);this.headerElement=s;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.textContent="SparkVault",a.appendChild(l),r.appendChild(a),i.appendChild(s),i.appendChild(o),i.appendChild(r),n.appendChild(i),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:i,header:s,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 i="dark"===this.effectiveTheme?e.logoDark||e.logoLight:e.logoLight||e.logoDark;if(i){const t=document.createElement("img");t.className="sv-logo",t.src=i,t.alt=e.companyName,n.appendChild(t);const s=document.createElement("span");s.className="sv-sr-only",s.id="sv-modal-title",s.textContent=e.companyName,n.appendChild(s)}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(N()),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 j(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function U(e,t){const n=document.createElement("div");return e&&(n.className=e),n}function q(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function K(e){const t=document.createElement("div");return t.className="sv-error",t.setAttribute("role","alert"),t.textContent=e,t}function W(e){const t=e.trim().toLowerCase();return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)&&t.length<=254?t:null}function G(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 J(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 Y{constructor(e={}){this.message=e.message??"Loading..."}render(){const e=U("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 X{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=U("sv-email-view"),t=U("sv-email-header"),n=document.createElement("p");n.className="sv-email-subtitle",n.textContent=this.getSubtitleText(),t.appendChild(n),e.appendChild(t),this.errorContainer=U("sv-email-error-container"),this.props.error&&this.errorContainer.appendChild(K(this.props.error)),e.appendChild(this.errorContainer);const i=U("sv-email-form"),s=U("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),s.appendChild(this.labelElement),s.appendChild(this.inputElement),i.appendChild(s),this.submitButton=document.createElement("button"),this.submitButton.className="sv-btn sv-btn-email-primary",this.submitButton.textContent="Continue",this.submitButton.addEventListener("click",this.boundHandleSubmit),i.appendChild(this.submitButton),e.appendChild(i),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=J(e);if(n!==e){this.inputElement.value=n;const i=Math.min(t+(n.length-e.length),n.length);this.inputElement.setSelectionRange(i,i)}}t!==("phone"===this.detectedType)&&this.labelElement&&(this.labelElement.textContent=this.getLabelText())}else if(this.allowsPhone){const t=this.inputElement.selectionStart??e.length,n=J(e);if(n!==e){this.inputElement.value=n;const i=Math.min(t+(n.length-e.length),n.length);this.inputElement.setSelectionRange(i,i)}}}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&&q(this.errorContainer),this.inputElement?.classList.remove("sv-email-input-error")}showError(e){this.errorContainer&&(q(this.errorContainer),this.errorContainer.appendChild(K(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=G(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=W(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=G(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=W(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 Q{constructor(e){this.backLink=null,this.methodButtons=[],this.methodClickHandlers=new Map,this.props=e,this.boundHandleBack=()=>this.props.onBack()}render(){const e=U();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=j(this.props.email);const i=U();for(const e of this.props.methods){const t=this.createMethodButton(e);this.methodButtons.push(t),i.appendChild(t)}return e.appendChild(this.backLink),e.appendChild(t),e.appendChild(n),e.appendChild(i),e}createBackLink(){const e=document.createElement("button");return e.className="sv-back-link",e.appendChild(M()),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=U("sv-method-content"),i=this.getMethodIcon(e);n.appendChild(i);const s=U("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,s.appendChild(o),s.appendChild(r),n.appendChild(s),t.appendChild(n);const a=()=>this.props.onSelectMethod(e);return this.methodClickHandlers.set(t,a),t.addEventListener("click",a),t}getMethodIcon(e){const t=U("sv-method-icon");return t.appendChild(O(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 Z{constructor(e){this.inputElements=[],this.submitButton=null,this.resendButton=null,this.timerDisplay=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=U(),t=document.createElement("h3");t.className="sv-title",t.textContent="Enter verification code";const n=document.createElement("p");n.className="sv-subtitle",n.innerHTML=`Enter the 6 digit code that has been sent to <strong>${j(this.props.email)}</strong> to log in.`,this.errorContainer=U(),this.props.error&&this.errorContainer.appendChild(K(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 s=U("sv-resend-row");return this.resendButton=document.createElement("button"),this.resendButton.className="sv-resend-btn",this.resendButton.appendChild(function(){const e=_("0 0 16 16");return e.appendChild(I("M13.5 8a5.5 5.5 0 11-1.21-3.45",{stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round","stroke-linejoin":"round",fill:"none"})),e.appendChild(I("M13.5 2.5v2.5H11",{stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round","stroke-linejoin":"round",fill:"none"})),e}()),this.resendButton.appendChild(document.createTextNode("Resend code")),this.resendButton.addEventListener("click",this.boundHandleResend),this.timerDisplay=document.createElement("span"),this.timerDisplay.className="sv-resend-timer",s.appendChild(this.resendButton),s.appendChild(this.timerDisplay),this.backLink=document.createElement("button"),this.backLink.className="sv-alt-method-link",this.backLink.textContent="Log in another way",this.backLink.addEventListener("click",this.boundHandleBack),e.appendChild(t),e.appendChild(n),e.appendChild(this.errorContainer),e.appendChild(i),e.appendChild(this.submitButton),e.appendChild(s),e.appendChild(this.backLink),this.focusTimeoutId=setTimeout(()=>this.inputElements[0]?.focus(),50),e}createInputGroup(){const e=U("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 i={input:e=>this.handleInput(e,t),keydown:e=>this.handleKeyDown(e,t),paste:e=>this.handlePaste(e),focus:()=>n.select()};this.inputHandlers.set(n,i),n.addEventListener("input",i.input),n.addEventListener("keydown",i.keydown),n.addEventListener("paste",i.paste),n.addEventListener("focus",i.focus),this.inputElements.push(n),e.appendChild(n)}return e}handleInput(e,t){const n=e.target,i=n.value.replace(/\D/g,"");i.length>0?(n.value=i[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&&q(this.errorContainer),this.inputElements.forEach(e=>e.classList.remove("sv-input-error"))}showError(e){this.errorContainer&&(q(this.errorContainer),this.errorContainer.appendChild(K(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 g({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.timerDisplay&&(this.timerDisplay.textContent=`${e}s`,this.resendButton.disabled=!0)},onComplete:()=>{this.resendButton&&this.timerDisplay&&(this.timerDisplay.textContent="",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 ee{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=U();this.backLink=this.createBackLink();const t=U();t.appendChild(R());const n=document.createElement("h3");n.className="sv-title",n.textContent="register"===this.props.mode?"Set up a passkey":"Use your passkey";const i=document.createElement("p");i.className="sv-subtitle","register"===this.props.mode?i.innerHTML=`Create a passkey for<br><strong>${j(this.props.email)}</strong><br>to sign in faster next time`:i.innerHTML=`Use your passkey to verify<br><strong>${j(this.props.email)}</strong>`,this.errorContainer=U(),this.props.error&&this.errorContainer.appendChild(K(this.props.error));const s=document.createElement("p");s.className="sv-passkey-description",s.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(i),e.appendChild(this.errorContainer),e.appendChild(s),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(M()),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&&(q(this.errorContainer),this.errorContainer.appendChild(K(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 te{constructor(e){this.addButton=null,this.skipLink=null,this.props=e,this.boundHandleAdd=()=>this.props.onAddPasskey(),this.boundHandleSkip=()=>this.props.onSkip()}render(){const e=U(),t=U();t.appendChild(R());const n=document.createElement("h3");n.className="sv-title",n.textContent="Add a passkey for faster sign-in?";const i=document.createElement("p");i.className="sv-subtitle",i.innerHTML=`Next time, sign in instantly as<br><strong>${j(this.props.email)}</strong>`;const s=document.createElement("div");s.className="sv-passkey-prompt-benefits";const o=document.createElement("ul"),r=["No codes to type or wait for","Phishing-resistant security","Works with Face ID, Touch ID, or Windows Hello"];for(const e of r){const t=document.createElement("li");t.appendChild(H()),t.appendChild(document.createTextNode(e)),o.appendChild(t)}return s.appendChild(o),this.addButton=document.createElement("button"),this.addButton.className="sv-btn sv-btn-primary",this.addButton.textContent="Add Passkey",this.addButton.addEventListener("click",this.boundHandleAdd),this.skipLink=document.createElement("button"),this.skipLink.className="sv-skip-link",this.skipLink.textContent="Not now",this.skipLink.addEventListener("click",this.boundHandleSkip),e.appendChild(t),e.appendChild(n),e.appendChild(i),e.appendChild(s),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 ne{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=U();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>${j(this.props.email)}</strong>`,this.waitingSection=U("sv-sparklink-waiting");const i=document.createElement("div");i.className="sv-spinner sv-spinner-small",i.setAttribute("role","status"),i.setAttribute("aria-label","Waiting for verification");const s=document.createElement("span");s.className="sv-sparklink-waiting-text",s.textContent="Waiting for you to click the link...",this.waitingSection.appendChild(i),this.waitingSection.appendChild(s),this.countdownSection=U("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=U("sv-resend-container");this.resendButton=document.createElement("button"),this.resendButton.className="sv-back-link",this.resendButton.textContent="Resend link",this.resendButton.addEventListener("click",this.boundHandleResend),r.appendChild(this.resendButton);const a=U("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(M()),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 g({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=U("sv-sparklink-expired-icon");e.appendChild(function(){const e=_("0 0 48 48",48,48);e.classList.add("sv-expired-icon"),e.appendChild(F(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(I("M14 4L2 24h24L14 4z",{fill:"#737373"})),t.appendChild(I("M14 10v5",{stroke:"#FFFFFF","stroke-width":"2","stroke-linecap":"round"})),t.appendChild(F(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 ie{constructor(e){this.retryButton=null,this.closeButton=null,this.props=e,this.boundHandleRetry=()=>this.props.onRetry(),this.boundHandleClose=()=>this.props.onClose()}render(){const e=U();e.className="sv-error-view";const t=U();t.className="sv-error-icon-container",t.appendChild(function(){const e=_("0 0 48 48",48,48);e.classList.add("sv-error-icon"),e.appendChild(F(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(I("M14 4L2 24h24L14 4z",{fill:"#DC2626"})),t.appendChild(I("M14 10v5",{stroke:"#FFFFFF","stroke-width":"2","stroke-linecap":"round"})),t.appendChild(F(14,19,1,{fill:"#FFFFFF"})),e.appendChild(t),e}());const n=U();n.className="sv-error-content";const i=document.createElement("h3");i.className="sv-error-title",i.textContent="Something went wrong";const s=document.createElement("p");s.className="sv-error-message",s.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(i),n.appendChild(s),n.appendChild(o);const l=U();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 se{constructor(e,t,n,i){this.currentView=null,this.focusTimeoutId=null,this.pollingInterval=null,this.messageListener=null,this.container=e,this.api=t,this.options=n,this.callbacks=i,this.verificationState=new S,this.passkeyHandler=new T(t,this.verificationState),this.totpHandler=new B(t,this.verificationState),this.sparkLinkHandler=new L(t,this.verificationState),n.email?this.verificationState.setIdentity(n.email,"email"):n.phone&&this.verificationState.setIdentity(n.phone,"phone")}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){const n=this.normalizeError(e);this.setState({view:"error",message:n.message,code:n instanceof t?n.code:"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,q(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 Y({message:"Loading..."});case"identity-input":return new X({allowedTypes:this.getAllowedIdentityTypes(),initialValue:this.recipient,error:e.error,onSubmit:e=>this.handleIdentitySubmit(e)});case"method-select":return new Q({email:e.email,methods:e.methods,onSelectMethod:e=>this.handleMethodSelect(e),onBack:()=>this.showIdentityInput()});case"totp-verify":return new Z({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 ee({email:e.email,mode:e.mode,error:e.error,onStart:()=>this.handlePasskeyStart(),onBack:n?()=>this.handlePasskeyRegistrationSkip():()=>this.showMethodSelect(),onFallback:t.hasPasskey&&!t.isFallbackMode?()=>this.handlePasskeyFallback():void 0})}case"passkey-prompt":return new te({email:e.email,onAddPasskey:()=>this.handlePasskeyPromptAdd(e.pendingResult),onSkip:()=>this.handlePasskeyPromptSkip(e.pendingResult)});case"sparklink-waiting":return new ne({email:e.email,expiresAt:e.expiresAt,onResend:()=>this.handleSparkLinkResend(),onFallback:()=>this.handleSparkLinkFallback(),onBack:()=>this.showMethodSelect(),onExpired:()=>this.stopSparkLinkPolling()});case"oauth-pending":return new Y({message:`Connecting to ${e.provider}...`});case"error":return new ie({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=E(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:i}=this.verificationState;if(n&&"email"===this.identityType&&!i.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"magic_link":{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){const n=this.normalizeError(e);this.setState({view:"error",message:n.message,code:n instanceof t?n.code:"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(await this.shouldShowPasskeyPrompt())return void this.setState({view:"passkey-prompt",email:this.recipient,pendingResult:n.result});this.close(),this.callbacks.onSuccess(n.result)}else 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();t.success&&t.result?(this.close(),this.callbacks.onSuccess(t.result)):"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;e&&(this.verificationState.setPendingResult(null),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 i=this.api.getSocialAuthUrl(e,n,t);window.location.href=i}normalizeError(e){return e instanceof x?new t(e.message,e.code):e instanceof Error?e:new Error(String(e))}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();t.success&&t.result?(this.close(),this.callbacks.onSuccess(t.result)):(t.errorType,this.verificationState.setPendingResult(null),this.close(),this.callbacks.onSuccess(e))}handlePasskeyPromptSkip(e){this.verificationState.dismissPasskeyPrompt(),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:i}=e.data;t&&n&&this.handleSparkLinkVerified({token:t,identity:n,identityType:i||"email"})},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"})},2e3)}async handleSparkLinkVerified(e){this.stopSparkLinkPolling(),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 oe={showHeader:!0,showCloseButton:!0,showFooter:!0};class re{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={...oe,...t}}createLoading(e,t){if(this.container)return;this.onCloseCallback=t;if(P({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.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 i="dark"===this.effectiveTheme?e.logoDark||e.logoLight:e.logoLight||e.logoDark;if(i){const t=document.createElement("img");t.className="sv-logo",t.src=i,t.alt=e.companyName,n.appendChild(t);const s=document.createElement("span");s.className="sv-sr-only",s.textContent=e.companyName,n.appendChild(s)}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(N()),this.closeBtnClickHandler=()=>this.handleClose(),this.closeBtn.addEventListener("click",this.closeBtnClickHandler),t.appendChild(this.closeBtn)),t}handleClose(){this.onCloseCallback&&this.onCloseCallback()}}class ae{constructor(e){this.renderer=null,this.config=e,this.api=new w(e),e.preloadConfig&&this.api.preloadConfig()}async pop(e={}){return this.renderer&&this.renderer.close(),new Promise((t,n)=>{const i=new V;this.renderer=new se(i,this.api,e,{onSuccess:n=>{e.onSuccess?.(n),t(n)},onError:t=>{e.onError?.(t),n(t)},onCancel:()=>{const t=new a;e.onCancel?.(),n(t)}}),this.renderer.start().catch(t=>{e.onError?.(t),n(t)})})}async render(e){if(!(e.target&&e.target instanceof HTMLElement))throw new s("target must be a valid HTMLElement");return this.renderer&&this.renderer.close(),new Promise((t,n)=>{const i=new re(e.target,{showHeader:e.showHeader,showCloseButton:e.showCloseButton,showFooter:e.showFooter});this.renderer=new se(i,this.api,e,{onSuccess:n=>{e.onSuccess?.(n),t(n)},onError:t=>{e.onError?.(t),n(t)},onCancel:()=>{const t=new a;e.onCancel?.(),n(t)}}),this.renderer.start().catch(t=>{e.onError?.(t),n(t)})})}async verifyToken(e){if(!e||"string"!=typeof e)throw new s("Token is required");const t=e.split(".");if(3!==t.length)throw new s("Invalid token format");const[,n]=t,i=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}(i))throw new s("Invalid token payload structure");const o=Math.floor(Date.now()/1e3);if(i.exp<o)throw new s("Token has expired");const r=`${this.config.apiBaseUrl}/apps/identity/${this.config.accountId}`;if(i.iss!==r)throw new s("Invalid token issuer");return i}close(){this.renderer&&(this.renderer.close(),this.renderer=null)}async verify(e={}){return this.pop(e)}}class le{constructor(e){this.http=e}async create(e){this.validateCreateOptions(e);const t=this.encodePayload(e.payload),n=await this.http.post("/v1/sparks",{payload:t,ttl:e.ttl??3600,max_reads:e.maxReads??1,password:e.password,name:e.name});return{id:n.data.spark_id,url:n.data.url,expiresAt:n.data.expires_at,maxReads:n.data.max_reads,name:n.data.name}}async read(e,t){if(!e)throw new s("Spark ID is required");const n=e.startsWith("spk_")?e:`spk_${e}`,i={};t&&(i["X-Spark-Password"]=t);const o=await this.http.get(`/v1/sparks/${n}`,{headers:i});return{data:this.decodePayload(o.data.payload),readAt:o.data.read_at,burned:o.data.burned,readsRemaining:o.data.reads_remaining}}validateCreateOptions(e){if(!e.payload)throw new s("Payload is required");if(void 0!==e.ttl&&(e.ttl<60||e.ttl>604800))throw new s("TTL must be between 60 and 604800 seconds (1 minute to 7 days)");if(void 0!==e.maxReads&&(e.maxReads<1||e.maxReads>100))throw new s("maxReads must be between 1 and 100")}encodePayload(e){return"string"==typeof e?e:b(e)}decodePayload(e){return e}}class de{constructor(e,t){this.http=t}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 s("Vault ID is required");if(!t)throw new s("VMK is required");const n=e.startsWith("vlt_")?e:`vlt_${e}`,i=await this.http.post(`/v1/vaults/${n}/unseal`,{vmk:t});return{id:i.data.vault_id,name:i.data.name,vatToken:i.data.vat_token,expiresAt:i.data.expires_at,ingotCount:i.data.ingot_count,storageBytes:i.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"),i=t.contentType??t.file.type??"application/octet-stream",s=await this.http.post(`/v1/vaults/${e.id}/ingots`,{name:n,content_type:i,size_bytes:t.file.size},{headers:{Authorization:`Bearer ${e.vatToken}`}});return{id:s.data.ingot_id,name:s.data.name,contentType:s.data.content_type,size:s.data.size_bytes,createdAt:s.data.created_at,type:s.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 s("Vault name is required");if(e.name.length>255)throw new s("Vault name must be 255 characters or less")}validateUploadOptions(e){if(!e.file)throw new s("File is required");if(0===e.file.size)throw new s("File cannot be empty")}}const ce=["hex","base64","base64url","alphanumeric","alphanumeric-mixed","password","numeric","uuid","bytes"];function he(e){return ce.includes(e)}class pe{constructor(e){this.http=e}async generate(e){this.validateOptions(e);const t=await this.http.post("/v1/apps/rng/generate",{num_bytes:e.bytes,format:e.format??"base64url"}),n=t.data.format??"base64url";if(!he(n))throw new s(`Server returned invalid format: ${n}`);return{value:"bytes"===n?v(t.data.random_bytes_b64):t.data.random_bytes_b64,bytes:t.data.num_bytes,format:n,referenceId:t.data.reference_id}}async uuid(){const e=await this.generate({bytes:16,format:"uuid"});if("string"!=typeof e.value)throw new s("Expected string value for uuid format");return e.value}async hex(e){const t=await this.generate({bytes:e,format:"hex"});if("string"!=typeof t.value)throw new s("Expected string value for hex format");return t.value}async alphanumeric(e){const t=await this.generate({bytes:e,format:"alphanumeric-mixed"});if("string"!=typeof t.value)throw new s("Expected string value for alphanumeric format");return t.value}async password(e){const t=await this.generate({bytes:e,format:"password"});if("string"!=typeof t.value)throw new s("Expected string value for password format");return t.value}validateOptions(e){if(!e.bytes||"number"!=typeof e.bytes)throw new s("bytes is required and must be a number");if(e.bytes<1||e.bytes>1024)throw new s("bytes must be between 1 and 1024");if(e.format&&!he(e.format))throw new s(`Invalid format. Must be one of: ${ce.join(", ")}`)}}let ue=!1;function me(e){ue=e,e&&ge("info","Debug mode enabled")}function ge(e,t,...n){const i=`[SparkVault] ${(new Date).toISOString().split("T")[1].slice(0,-1)} ${t}`;switch(e){case"debug":ue&&console.debug(i,...n);break;case"info":ue&&console.log(i,...n);break;case"warn":ue&&console.warn(i,...n);break;case"error":console.error(i,...n)}}const be={debug:(e,...t)=>ge("debug",e,...t),info:(e,...t)=>ge("info",e,...t),warn:(e,...t)=>ge("warn",e,...t),error:(e,...t)=>ge("error",e,...t),group:e=>{ue&&console.group(`[SparkVault] ${e}`)},groupEnd:()=>{ue&&console.groupEnd()}},ye={initialized:!1,instance:null,config:null,observer:null,isAuthenticating:!1};function fe(e){if(!e||"string"!=typeof e)return null;const t=e.split(".");let n=window;for(const i of t){if(!n||"object"!=typeof n||!(i in n))return be.warn(`Function "${e}" not found on window`),null;n=n[i]}return"function"==typeof n?n:(be.warn(`"${e}" is not a function`),null)}function ke(e){const t=ye.config;if(t){if(be.error("Verification error:",e.message),t.errorFunction){const n=fe(t.errorFunction);if(n){be.debug(`Calling error function: ${t.errorFunction}`);try{n(e)}catch(e){be.error("Error in error function:",e)}}}if(t.errorUrl){const n=new URL(t.errorUrl,window.location.origin);n.searchParams.set("error",e.message),be.debug(`Redirecting to error URL: ${n.toString()}`),window.location.href=n.toString()}}}async function ve(){if(ye.instance)if(ye.isAuthenticating)be.debug("Already authenticating, skipping");else{ye.isAuthenticating=!0,be.info("Starting verification...");try{const e=await ye.instance.identity.pop();if(!e)return be.info("User cancelled verification"),void(ye.isAuthenticating=!1);await async function(e){const t=ye.config;if(t){if(be.info("Verification successful",{identity:e.identity,identityType:e.identityType,hasToken:!!e.token}),t.successFunction){const n=fe(t.successFunction);if(n){be.debug(`Calling success function: ${t.successFunction}`);try{n(e)}catch(e){be.error("Error in success function:",e)}}}if(t.successUrl){be.debug(`POSTing to success URL: ${t.successUrl}`);try{const n=await fetch(t.successUrl,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},credentials:"include",body:JSON.stringify({token:e.token,identity:e.identity,identityType:e.identityType})}),i=await n.json();if(be.debug("Success URL response:",i),i.redirectUrl||i.redirect_url){const e=i.redirectUrl||i.redirect_url;be.info(`Redirecting to: ${e}`),window.location.href=e}else!0===i.reload&&(be.info("Reloading page"),window.location.reload())}catch(e){be.error("Failed to POST to success URL:",e),ke(e instanceof Error?e:new Error(String(e)))}}}}(e)}catch(e){if(e&&"object"==typeof e&&"name"in e){const t=e;if("UserCancelledError"===t.name||"user_cancelled"===t.code)return be.info("User cancelled verification"),void(ye.isAuthenticating=!1)}ke(e instanceof Error?e:new Error(String(e)))}finally{ye.isAuthenticating=!1}}else be.error("SDK not initialized")}function we(){const e=ye.config;if(!e?.attachSelector)return;const t=document.querySelectorAll(e.attachSelector);be.debug(`Found ${t.length} elements matching "${e.attachSelector}"`),t.forEach(e=>{"true"!==e.dataset.sparkvaultAttached&&(e.dataset.sparkvaultAttached="true",be.debug("Attaching click handler to element:",e),e.addEventListener("click",e=>{be.debug("Auth element clicked"),e.preventDefault(),e.stopPropagation(),ve()}))})}function xe(){const e=ye.config;e?.attachSelector&&("undefined"!=typeof MutationObserver?(ye.observer=new MutationObserver(e=>{let t=!1;for(const n of e)if(n.addedNodes.length>0){t=!0;break}t&&we()}),ye.observer.observe(document.body,{childList:!0,subtree:!0}),be.debug("MutationObserver started for dynamic elements")):be.warn("MutationObserver not available, dynamic elements will not be auto-attached"))}class Ce{constructor(e){!function(e){if(!e.accountId)throw new s("accountId is required",{field:"accountId"});if("string"!=typeof e.accountId)throw new s("accountId must be a string",{field:"accountId",received:typeof e.accountId});if(!e.accountId.startsWith("acc_"))throw new s('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}}(e);const t=new u(this.config);this.identity=new ae(this.config),this.sparks=new le(t),this.vaults=new de(this.config,t),this.rng=new pe(t)}static init(e){return new Ce(e)}static get version(){return"__VERSION__"}}"undefined"!=typeof window&&(window.SparkVault=Ce,function(e){if(ye.initialized)return void be.debug("Auto-init already completed");if("undefined"==typeof window||"undefined"==typeof document)return;const t=function(){if(document.currentScript instanceof HTMLScriptElement)return document.currentScript;const e=document.querySelectorAll('script[src*="sparkvault"]');for(const t of e)if(t.dataset.accountId)return t;return e[0]||null}();if(!t)return;const n=function(e){const t=e.dataset.timeout;return{accountId:e.dataset.accountId||null,attachSelector:e.dataset.attachSelector||null,successUrl:e.dataset.successUrl||null,successFunction:e.dataset.successFunction||null,errorUrl:e.dataset.errorUrl||null,errorFunction:e.dataset.errorFunction||null,debug:"true"===e.dataset.debug,preloadConfig:"false"!==e.dataset.preloadConfig,timeout:t?parseInt(t,10):3e4}}(t);if(ye.config=n,n.debug&&me(!0),be.info("Auto-init starting..."),be.group("Configuration"),be.debug("Account ID:",n.accountId||"(not set)"),be.debug("Attach Selector:",n.attachSelector||"(not set)"),be.debug("Success URL:",n.successUrl||"(not set)"),be.debug("Success Function:",n.successFunction||"(not set)"),be.debug("Error URL:",n.errorUrl||"(not set)"),be.debug("Error Function:",n.errorFunction||"(not set)"),be.debug("Debug:",n.debug),be.groupEnd(),!n.accountId)return void be.debug("No data-account-id attribute, skipping auto-init");try{ye.instance=e.init({accountId:n.accountId,preloadConfig:n.preloadConfig,timeout:n.timeout}),ye.initialized=!0,be.info("SDK initialized successfully")}catch(e){return void be.error("Failed to initialize SDK:",e)}n.attachSelector&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{we(),xe()}):(we(),xe()));const i=window.SparkVault;i&&(i.identity={pop:ve,render:e=>ye.instance?ye.instance.identity.render(e):Promise.reject(new Error("SDK not initialized")),authenticate:ve,verify:e=>ye.instance?ye.instance.identity.pop(e):Promise.reject(new Error("SDK not initialized"))},i.getInstance=()=>ye.instance),be.info("Auto-init complete")}(Ce)),e.AuthenticationError=n,e.AuthorizationError=i,e.NetworkError=o,e.PopupBlockedError=l,e.SparkVault=Ce,e.SparkVaultError=t,e.TimeoutError=r,e.UserCancelledError=a,e.ValidationError=s,e.default=Ce,e.logger=be,e.setDebugMode=me,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,i,s){super(e),this.name="SparkVaultError",this.code=n,this.statusCode=i,this.details=s,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 i extends t{constructor(e,t){super(e,"authorization_error",403,t),this.name="AuthorizationError",Object.setPrototypeOf(this,i.prototype)}}class s extends t{constructor(e,t){super(e,"validation_error",400,t),this.name="ValidationError",Object.setPrototypeOf(this,s.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,i){const s=t*Math.pow(2,e),o=Math.min(s,n);return o+o*i*Math.random()}function h(e){return new Promise(t=>setTimeout(t,e))}async function p(e,t={}){const n={...d,...t};let i;for(let t=0;t<n.maxAttempts;t++)try{return await e()}catch(e){i=e;if(t===n.maxAttempts-1||!n.isRetryable(e))throw e;const s=c(t,n.baseDelayMs,n.maxDelayMs,n.jitterFactor);await h(s)}throw i}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:i="GET",headers:s={},body:a,timeout:l=this.config.timeout}=n,d=`${this.config.apiBaseUrl}${e}`,c={"Content-Type":"application/json",Accept:"application/json",...s},h=new AbortController,p=setTimeout(()=>h.abort(),l);try{const e=await fetch(d,{method:i,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 i=e.headers.get("content-type");if(i?.includes("application/json")){return await e.json()}const s=await e.text();throw new t(`Unexpected response format: expected JSON but received ${i??"unknown content type"}`,"invalid_response_format",e.status,{body:s.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 s(a,d);case 401:return new n(a,d);case 403:return new i(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:i="GET",headers:s={},body:a,timeout:l=this.config.timeout}=n,d=`${this.config.apiBaseUrl}${e}`,c={"Content-Type":"application/json",...s},h=new AbortController,p=setTimeout(()=>h.abort(),l);try{const e=await fetch(d,{method:i,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 g{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 b(e){let t="";for(let n=0;n<e.byteLength;n++)t+=String.fromCharCode(e[n]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function y(e){return b(new Uint8Array(e))}function f(e){const t=e.replace(/-/g,"+").replace(/_/g,"/"),n=t+"=".repeat((4-t.length%4)%4),i=atob(n),s=new Uint8Array(i.length);for(let e=0;e<i.length;e++)s[e]=i.charCodeAt(e);return s}function k(e){const t=f(e),n=new ArrayBuffer(t.length);return new Uint8Array(n).set(t),n}function v(e){const t=f(e);return Array.from(t)}class w{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 i=`${this.baseUrl}${t}`,s={"Content-Type":"application/json",Accept:"application/json"},o=new AbortController,r=setTimeout(()=>o.abort(),this.timeoutMs);try{const t=await fetch(i,{method:e,headers:s,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",i=e.code||r.code||"api_error";throw new x(n,i,t.status)}return r.data??r}catch(e){if(e instanceof Error&&"AbortError"===e.name)throw new x("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})}async sendTotp(e){return this.request("POST","/totp/send",{recipient:e.recipient,method:e.method})}async verifyTotp(e){return this.request("POST","/totp/verify",{kindling:e.kindling,pin:e.pin,recipient:e.recipient})}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:y(e.credential.rawId),type:e.credential.type,response:{clientDataJSON:y(t.clientDataJSON),attestationObject:y(t.attestationObject)}}})}async startPasskeyVerify(e){const t=await this.request("POST","/passkey/verify",{email:e});return{challenge:t.options.challenge,rpId:t.options.rpId,rpName:"SparkVault Identity",timeout:t.options.timeout,allowCredentials:t.options.allowCredentials,session:t.session}}async completePasskeyVerify(e){const t=e.credential.response;return this.request("POST","/passkey/verify/complete",{session:e.session,credential:{id:e.credential.id,rawId:y(e.credential.rawId),type:e.credential.type,response:{clientDataJSON:y(t.clientDataJSON),authenticatorData:y(t.authenticatorData),signature:y(t.signature),userHandle:t.userHandle?y(t.userHandle):null}}})}getSocialAuthUrl(e,t,n){const i=new URLSearchParams({redirect_uri:t,state:n});return`${this.baseUrl}/social/${e}?${i}`}getEnterpriseAuthUrl(e,t,n){const i=new URLSearchParams({redirect_uri:t,state:n});return`${this.baseUrl}/saml/${e}?${i}`}async sendSparkLink(e){return this.request("POST","/sparklink/send",{email:e,type:"verify_identity",openerOrigin:"undefined"!=typeof window?window.location.origin:void 0})}async checkSparkLinkStatus(e){return this.request("GET",`/sparklink/status/${e}`)}}class x extends Error{constructor(e,t,n){super(e),this.name="IdentityApiError",this.code=t,this.statusCode=n}}const C={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:"magic_link",identityType:"email",name:"SparkLink",description:"Get an instant sign-in magic 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"},enterprise_okta:{id:"enterprise_okta",type:"enterprise",identityType:"email",name:"Okta",description:"Sign in with Okta SSO",icon:"okta",provider:"okta"},enterprise_entra:{id:"enterprise_entra",type:"enterprise",identityType:"email",name:"Microsoft Entra",description:"Sign in with Microsoft Entra ID",icon:"microsoft",provider:"entra"},enterprise_onelogin:{id:"enterprise_onelogin",type:"enterprise",identityType:"email",name:"OneLogin",description:"Sign in with OneLogin SSO",icon:"onelogin",provider:"onelogin"},enterprise_ping:{id:"enterprise_ping",type:"enterprise",identityType:"email",name:"Ping Identity",description:"Sign in with Ping Identity",icon:"ping",provider:"ping"},enterprise_jumpcloud:{id:"enterprise_jumpcloud",type:"enterprise",identityType:"email",name:"JumpCloud",description:"Sign in with JumpCloud SSO",icon:"jumpcloud",provider:"jumpcloud"},hris_bamboohr:{id:"hris_bamboohr",type:"hris",identityType:"email",name:"BambooHR",description:"Sign in with BambooHR",icon:"bamboohr",provider:"bamboohr"},hris_workday:{id:"hris_workday",type:"hris",identityType:"email",name:"Workday",description:"Sign in with Workday",icon:"workday",provider:"workday"},hris_adp:{id:"hris_adp",type:"hris",identityType:"email",name:"ADP",description:"Sign in with ADP",icon:"adp",provider:"adp"},hris_gusto:{id:"hris_gusto",type:"hris",identityType:"email",name:"Gusto",description:"Sign in with Gusto",icon:"gusto",provider:"gusto"},hris_rippling:{id:"hris_rippling",type:"hris",identityType:"email",name:"Rippling",description:"Sign in with Rippling",icon:"rippling",provider:"rippling"}};function E(e){return e.map(e=>C[e]).filter(e=>void 0!==e)}class S{constructor(){this._sdkConfig=null,this._viewState={view:"loading"},this._recipient="",this._identityType="email",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=E(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 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 i=new Date;i.setTime(i.getTime()+24*n*60*60*1e3);const s=`expires=${i.toUTCString()}`;document.cookie=`${e}=${t}; ${s}; 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._passkey.hasPasskey=null,this._passkey.isFallbackMode=!1,this._passkey.pendingResult=null,this._totp.kindling="",this._totp.method=null}}class T{constructor(e,t){this.api=e,this.state=t}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(){try{const e=await this.api.startPasskeyRegister(this.state.recipient),t={challenge:k(e.challenge),rp:{id:e.rpId,name:e.rpName},user:{id:k(e.userId??this.state.recipient),name:this.state.recipient,displayName:this.state.recipient},pubKeyCredParams:[{type:"public-key",alg:-7},{type:"public-key",alg:-257}],timeout:e.timeout,authenticatorSelection:{residentKey:"preferred",userVerification:"preferred"},attestation:"none"},n=await navigator.credentials.create({publicKey:t});if(!n)return{success:!1,error:"Failed to create passkey",errorType:"unknown"};const i=await this.api.completePasskeyRegister({session:e.session,credential:n});return this.state.setPasskeyStatus(!0),{success:!0,result:{token:i.token,identity:i.identity,identityType:i.identity_type}}}catch(e){return this.handleError(e)}}async verify(){try{const e=await this.api.startPasskeyVerify(this.state.recipient),t={challenge:k(e.challenge),rpId:e.rpId,timeout:e.timeout,userVerification:"preferred",allowCredentials:e.allowCredentials?.map(e=>({id:k(e.id),type:e.type,transports:["internal","hybrid","usb","ble","nfc"]}))},n=await navigator.credentials.get({publicKey:t});if(!n)return{success:!1,error:"Failed to verify passkey",errorType:"unknown"};const i=await this.api.completePasskeyVerify({session:e.session,credential:n});return{success:!0,result:{token:i.token,identity:i.identity,identityType:i.identity_type}}}catch(e){return this.handleError(e)}}handleError(e){const t=e instanceof Error?e.message:String(e);return t.includes("not allowed")||t.includes("cancelled")?{success:!1,error:"Authentication was cancelled. Please try again.",errorType:"cancelled"}:t.includes("No credentials")?{success:!1,error:"No passkey found. Create one to continue.",errorType:"not_found"}:{success:!1,error:t||"Passkey authentication failed",errorType:"unknown"}}}class B{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});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});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});return n.verified&&n.token?{success:!0,result:{token:n.token,identity:n.identity??this.state.recipient,identityType:n.identity_type??this.state.identityType}}:(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 L{constructor(e,t){this.api=e,this.state=t}async send(){try{const e=await this.api.sendSparkLink(this.state.recipient);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}}catch{return{verified:!1}}}}const A="sparkvault-identity-styles";function P(e){if(document.getElementById(A))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=A,t.textContent=function(e){const{branding:t,backdropBlur:n}=e,i="dark"===_(t),s=!1!==n,o={primary:"#4F46E5",primaryHover:"#4338CA",primaryLight:i?"rgba(99, 102, 241, 0.15)":"rgba(79, 70, 229, 0.08)",error:"#DC2626",errorLight:i?"rgba(220, 38, 38, 0.15)":"#FEF2F2",errorBorder:i?"rgba(220, 38, 38, 0.3)":"#FECACA",bg:i?"#0F0F0F":"#FFFFFF",bgSubtle:i?"#171717":"#FAFAFA",bgHover:i?"#1A1A1A":"#F5F5F5",bgInput:i?"#0F0F0F":"#FFFFFF",border:i?"#262626":"#E5E5E5",borderHover:i?"#404040":"#D4D4D4",borderFocus:"#4F46E5",textPrimary:i?"#FAFAFA":"#0A0A0A",textSecondary:i?"#A3A3A3":"#525252",textMuted:"#737373",textOnPrimary:"#FFFFFF",shadowSm:i?"0 1px 2px rgba(0, 0, 0, 0.4)":"0 1px 2px rgba(0, 0, 0, 0.04)",shadowLg:i?"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, ${i?"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(${s?"8px":"0"});\n -webkit-backdrop-filter: blur(${s?"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 overflow: hidden;\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;\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: ${i?"#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 height: 100%;\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 overflow: hidden;\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;\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 I(e,t=16,n=16){const i=document.createElementNS("http://www.w3.org/2000/svg","svg");return i.setAttribute("viewBox",e),i.setAttribute("width",String(t)),i.setAttribute("height",String(n)),i.setAttribute("fill","none"),i.setAttribute("xmlns","http://www.w3.org/2000/svg"),i}function $(e,t={}){const n=document.createElementNS("http://www.w3.org/2000/svg","path");n.setAttribute("d",e);for(const[e,i]of Object.entries(t))n.setAttribute(e,i);return n}function F(e,t,n,i={}){const s=document.createElementNS("http://www.w3.org/2000/svg","circle");s.setAttribute("cx",String(e)),s.setAttribute("cy",String(t)),s.setAttribute("r",String(n));for(const[e,t]of Object.entries(i))s.setAttribute(e,t);return s}function H(){const e=I("0 0 16 16");return e.appendChild($("M13.25 4.75L6 12 2.75 8.75",{stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"})),e}function M(){const e=I("0 0 16 16");return e.appendChild($("M10 12L6 8L10 4",{stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"})),e}function N(){const e=I("0 0 20 20",20,20);return e.appendChild($("M15 5L5 15M5 5L15 15",{stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"})),e}function R(){const e=I("0 0 56 56",56,56);e.classList.add("sv-passkey-icon"),e.appendChild(F(28,28,26,{fill:"#EEF2FF",stroke:"#E0E7FF","stroke-width":"1"}));const t=document.createElementNS("http://www.w3.org/2000/svg","g");return t.setAttribute("transform","translate(16, 16)"),t.appendChild($("M12 8c-2.21 0-4 1.79-4 4 0 1.56.9 2.93 2.21 3.6L10 20v2h4v-2l-.21-4.4C15.1 14.93 16 13.56 16 12c0-2.21-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z",{fill:"#4F46E5"})),e.appendChild(t),e}function O(e){switch(e){case"email":default:return z();case"phone":return function(){const e=I("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($("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=I("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($("M2 10c0-4.42 3.58-8 8-8 2.65 0 5 1.29 6.47 3.28")),e.appendChild($("M4 16.58c.42-.35.79-.76 1.1-1.22")),e.appendChild($("M14.41 17.51c.09-.45.32-1.73.38-2.26")),e.appendChild($("M10 8.33a1.5 1.5 0 00-1.5 1.5c0 .77-.08 1.88-.2 3")),e.appendChild($("M7.21 18.33c.16-.5.34-1 .43-1.5")),e.appendChild($("M11.67 10.93c0 1.79 0 4.79-.75 6.65")),e.appendChild($("M17.33 13.33c.15-1.5.1-4.02 0-4.5")),e.appendChild($("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=I("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($("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($("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=I("0 0 20 20",20,20);return e.appendChild($("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($("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($("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($("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=I("0 0 20 20",20,20);return e.setAttribute("fill","currentColor"),e.appendChild($("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=I("0 0 20 20",20,20);return e.appendChild($("M1 1h8.33v8.33H1z",{fill:"#F25022"})),e.appendChild($("M1 10.67h8.33V19H1z",{fill:"#00A4EF"})),e.appendChild($("M10.67 1H19v8.33h-8.33z",{fill:"#7FBA00"})),e.appendChild($("M10.67 10.67H19V19h-8.33z",{fill:"#FFB900"})),e}();case"github":return function(){const e=I("0 0 20 20",20,20);return e.setAttribute("fill","currentColor"),e.appendChild($("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=I("0 0 20 20",20,20);return e.appendChild($("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=I("0 0 20 20",20,20);return e.appendChild($("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=I("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($("M2.5 17.5h15")),e.appendChild($("M4.17 17.5V5.83L10.83 2.5v15")),e.appendChild($("M15.83 17.5V9.17l-5-3.33")),e.appendChild($("M7.5 7.5v.01")),e.appendChild($("M7.5 10v.01")),e.appendChild($("M7.5 12.5v.01")),e.appendChild($("M7.5 15v.01")),e}();case"users":return function(){const e=I("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($("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(F(7.5,5.83,3.33,{stroke:"currentColor","stroke-width":"1.5",fill:"none"})),e.appendChild($("M19.17 17.5v-1.67a3.33 3.33 0 00-2.5-3.22")),e.appendChild($("M13.33 2.6a3.33 3.33 0 010 6.46")),e}()}}function z(){const e=I("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($("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($("M18 5l-8 5.5L2 5")),e}const V={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:V,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);P({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 i=document.createElement("div");i.id="sparkvault-identity-modal",i.className="sv-modal";const s=this.createHeader(e.branding);this.headerElement=s;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.textContent="SparkVault",a.appendChild(l),r.appendChild(a),i.appendChild(s),i.appendChild(o),i.appendChild(r),n.appendChild(i),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:i,header:s,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 i="dark"===this.effectiveTheme?e.logoDark||e.logoLight:e.logoLight||e.logoDark;if(i){const t=document.createElement("img");t.className="sv-logo",t.src=i,t.alt=e.companyName,n.appendChild(t);const s=document.createElement("span");s.className="sv-sr-only",s.id="sv-modal-title",s.textContent=e.companyName,n.appendChild(s)}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(N()),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 j(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function U(e,t){const n=document.createElement("div");return e&&(n.className=e),n}function q(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function K(e){const t=document.createElement("div");return t.className="sv-error",t.setAttribute("role","alert"),t.textContent=e,t}function W(e){const t=e.trim().toLowerCase();return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)&&t.length<=254?t:null}function G(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 J(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 Y{constructor(e={}){this.message=e.message??"Loading..."}render(){const e=U("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 X{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=U("sv-email-view"),t=U("sv-email-header"),n=document.createElement("p");n.className="sv-email-subtitle",n.textContent=this.getSubtitleText(),t.appendChild(n),e.appendChild(t),this.errorContainer=U("sv-email-error-container"),this.props.error&&this.errorContainer.appendChild(K(this.props.error)),e.appendChild(this.errorContainer);const i=U("sv-email-form"),s=U("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),s.appendChild(this.labelElement),s.appendChild(this.inputElement),i.appendChild(s),this.submitButton=document.createElement("button"),this.submitButton.className="sv-btn sv-btn-email-primary",this.submitButton.textContent="Continue",this.submitButton.addEventListener("click",this.boundHandleSubmit),i.appendChild(this.submitButton),e.appendChild(i),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=J(e);if(n!==e){this.inputElement.value=n;const i=Math.min(t+(n.length-e.length),n.length);this.inputElement.setSelectionRange(i,i)}}t!==("phone"===this.detectedType)&&this.labelElement&&(this.labelElement.textContent=this.getLabelText())}else if(this.allowsPhone){const t=this.inputElement.selectionStart??e.length,n=J(e);if(n!==e){this.inputElement.value=n;const i=Math.min(t+(n.length-e.length),n.length);this.inputElement.setSelectionRange(i,i)}}}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&&q(this.errorContainer),this.inputElement?.classList.remove("sv-email-input-error")}showError(e){this.errorContainer&&(q(this.errorContainer),this.errorContainer.appendChild(K(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=G(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=W(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=G(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=W(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 Q{constructor(e){this.backLink=null,this.methodButtons=[],this.methodClickHandlers=new Map,this.props=e,this.boundHandleBack=()=>this.props.onBack()}render(){const e=U();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=j(this.props.email);const i=U();for(const e of this.props.methods){const t=this.createMethodButton(e);this.methodButtons.push(t),i.appendChild(t)}return e.appendChild(this.backLink),e.appendChild(t),e.appendChild(n),e.appendChild(i),e}createBackLink(){const e=document.createElement("button");return e.className="sv-back-link",e.appendChild(M()),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=U("sv-method-content"),i=this.getMethodIcon(e);n.appendChild(i);const s=U("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,s.appendChild(o),s.appendChild(r),n.appendChild(s),t.appendChild(n);const a=()=>this.props.onSelectMethod(e);return this.methodClickHandlers.set(t,a),t.addEventListener("click",a),t}getMethodIcon(e){const t=U("sv-method-icon");return t.appendChild(O(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 Z{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=U();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",i=document.createElement("p");i.className="sv-subtitle",i.innerHTML=`We sent a 6-digit code to your ${n}<br><strong>${j(this.props.email)}</strong>`,this.errorContainer=U(),this.props.error&&this.errorContainer.appendChild(K(this.props.error));const s=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=U("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(i),e.appendChild(this.errorContainer),e.appendChild(s),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(M()),e.appendChild(document.createTextNode(" Choose another method")),e.addEventListener("click",this.boundHandleBack),e}createInputGroup(){const e=U("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 i={input:e=>this.handleInput(e,t),keydown:e=>this.handleKeyDown(e,t),paste:e=>this.handlePaste(e),focus:()=>n.select()};this.inputHandlers.set(n,i),n.addEventListener("input",i.input),n.addEventListener("keydown",i.keydown),n.addEventListener("paste",i.paste),n.addEventListener("focus",i.focus),this.inputElements.push(n),e.appendChild(n)}return e}handleInput(e,t){const n=e.target,i=n.value.replace(/\D/g,"");i.length>0?(n.value=i[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&&q(this.errorContainer),this.inputElements.forEach(e=>e.classList.remove("sv-input-error"))}showError(e){this.errorContainer&&(q(this.errorContainer),this.errorContainer.appendChild(K(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 g({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 ee{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=U();this.backLink=this.createBackLink();const t=U();t.appendChild(R());const n=document.createElement("h3");n.className="sv-title",n.textContent="register"===this.props.mode?"Set up a passkey":"Use your passkey";const i=document.createElement("p");i.className="sv-subtitle","register"===this.props.mode?i.innerHTML=`Create a passkey for<br><strong>${j(this.props.email)}</strong><br>to sign in faster next time`:i.innerHTML=`Use your passkey to verify<br><strong>${j(this.props.email)}</strong>`,this.errorContainer=U(),this.props.error&&this.errorContainer.appendChild(K(this.props.error));const s=document.createElement("p");s.className="sv-passkey-description",s.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(i),e.appendChild(this.errorContainer),e.appendChild(s),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(M()),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&&(q(this.errorContainer),this.errorContainer.appendChild(K(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 te{constructor(e){this.addButton=null,this.skipLink=null,this.props=e,this.boundHandleAdd=()=>this.props.onAddPasskey(),this.boundHandleSkip=()=>this.props.onSkip()}render(){const e=U(),t=U();t.appendChild(R());const n=document.createElement("h3");n.className="sv-title",n.textContent="Add a passkey for faster sign-in?";const i=document.createElement("p");i.className="sv-subtitle",i.innerHTML=`Next time, sign in instantly as<br><strong>${j(this.props.email)}</strong>`;const s=document.createElement("div");s.className="sv-passkey-prompt-benefits";const o=document.createElement("ul"),r=["No codes to type or wait for","Phishing-resistant security","Works with Face ID, Touch ID, or Windows Hello"];for(const e of r){const t=document.createElement("li");t.appendChild(H()),t.appendChild(document.createTextNode(e)),o.appendChild(t)}return s.appendChild(o),this.addButton=document.createElement("button"),this.addButton.className="sv-btn sv-btn-primary",this.addButton.textContent="Add Passkey",this.addButton.addEventListener("click",this.boundHandleAdd),this.skipLink=document.createElement("button"),this.skipLink.className="sv-skip-link",this.skipLink.textContent="Not now",this.skipLink.addEventListener("click",this.boundHandleSkip),e.appendChild(t),e.appendChild(n),e.appendChild(i),e.appendChild(s),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 ne{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=U();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>${j(this.props.email)}</strong>`,this.waitingSection=U("sv-sparklink-waiting");const i=document.createElement("div");i.className="sv-spinner sv-spinner-small",i.setAttribute("role","status"),i.setAttribute("aria-label","Waiting for verification");const s=document.createElement("span");s.className="sv-sparklink-waiting-text",s.textContent="Waiting for you to click the link...",this.waitingSection.appendChild(i),this.waitingSection.appendChild(s),this.countdownSection=U("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=U("sv-resend-container");this.resendButton=document.createElement("button"),this.resendButton.className="sv-back-link",this.resendButton.textContent="Resend link",this.resendButton.addEventListener("click",this.boundHandleResend),r.appendChild(this.resendButton);const a=U("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(M()),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 g({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=U("sv-sparklink-expired-icon");e.appendChild(function(){const e=I("0 0 48 48",48,48);e.classList.add("sv-expired-icon"),e.appendChild(F(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($("M14 4L2 24h24L14 4z",{fill:"#737373"})),t.appendChild($("M14 10v5",{stroke:"#FFFFFF","stroke-width":"2","stroke-linecap":"round"})),t.appendChild(F(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 ie{constructor(e){this.retryButton=null,this.closeButton=null,this.props=e,this.boundHandleRetry=()=>this.props.onRetry(),this.boundHandleClose=()=>this.props.onClose()}render(){const e=U();e.className="sv-error-view";const t=U();t.className="sv-error-icon-container",t.appendChild(function(){const e=I("0 0 48 48",48,48);e.classList.add("sv-error-icon"),e.appendChild(F(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($("M14 4L2 24h24L14 4z",{fill:"#DC2626"})),t.appendChild($("M14 10v5",{stroke:"#FFFFFF","stroke-width":"2","stroke-linecap":"round"})),t.appendChild(F(14,19,1,{fill:"#FFFFFF"})),e.appendChild(t),e}());const n=U();n.className="sv-error-content";const i=document.createElement("h3");i.className="sv-error-title",i.textContent="Something went wrong";const s=document.createElement("p");s.className="sv-error-message",s.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(i),n.appendChild(s),n.appendChild(o);const l=U();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 se{constructor(e,t,n,i){this.currentView=null,this.focusTimeoutId=null,this.pollingInterval=null,this.messageListener=null,this.container=e,this.api=t,this.options=n,this.callbacks=i,this.verificationState=new S,this.passkeyHandler=new T(t,this.verificationState),this.totpHandler=new B(t,this.verificationState),this.sparkLinkHandler=new L(t,this.verificationState),n.email?this.verificationState.setIdentity(n.email,"email"):n.phone&&this.verificationState.setIdentity(n.phone,"phone")}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){const n=this.normalizeError(e);this.setState({view:"error",message:n.message,code:n instanceof t?n.code:"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,q(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 Y({message:"Loading..."});case"identity-input":return new X({allowedTypes:this.getAllowedIdentityTypes(),initialValue:this.recipient,error:e.error,onSubmit:e=>this.handleIdentitySubmit(e)});case"method-select":return new Q({email:e.email,methods:e.methods,onSelectMethod:e=>this.handleMethodSelect(e),onBack:()=>this.showIdentityInput()});case"totp-verify":return new Z({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 ee({email:e.email,mode:e.mode,error:e.error,onStart:()=>this.handlePasskeyStart(),onBack:n?()=>this.handlePasskeyRegistrationSkip():()=>this.showMethodSelect(),onFallback:t.hasPasskey&&!t.isFallbackMode?()=>this.handlePasskeyFallback():void 0})}case"passkey-prompt":return new te({email:e.email,onAddPasskey:()=>this.handlePasskeyPromptAdd(e.pendingResult),onSkip:()=>this.handlePasskeyPromptSkip(e.pendingResult)});case"sparklink-waiting":return new ne({email:e.email,expiresAt:e.expiresAt,onResend:()=>this.handleSparkLinkResend(),onFallback:()=>this.handleSparkLinkFallback(),onBack:()=>this.showMethodSelect(),onExpired:()=>this.stopSparkLinkPolling()});case"oauth-pending":return new Y({message:`Connecting to ${e.provider}...`});case"error":return new ie({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=E(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:i}=this.verificationState;if(n&&"email"===this.identityType&&!i.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"magic_link":{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){const n=this.normalizeError(e);this.setState({view:"error",message:n.message,code:n instanceof t?n.code:"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(await this.shouldShowPasskeyPrompt())return void this.setState({view:"passkey-prompt",email:this.recipient,pendingResult:n.result});this.close(),this.callbacks.onSuccess(n.result)}else 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();t.success&&t.result?(this.close(),this.callbacks.onSuccess(t.result)):"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;e&&(this.verificationState.setPendingResult(null),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 i=this.api.getSocialAuthUrl(e,n,t);window.location.href=i}normalizeError(e){return e instanceof x?new t(e.message,e.code):e instanceof Error?e:new Error(String(e))}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();t.success&&t.result?(this.close(),this.callbacks.onSuccess(t.result)):(t.errorType,this.verificationState.setPendingResult(null),this.close(),this.callbacks.onSuccess(e))}handlePasskeyPromptSkip(e){this.verificationState.dismissPasskeyPrompt(),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:i}=e.data;t&&n&&this.handleSparkLinkVerified({token:t,identity:n,identityType:i||"email"})},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"})},2e3)}async handleSparkLinkVerified(e){this.stopSparkLinkPolling(),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 oe={showHeader:!0,showCloseButton:!0,showFooter:!0};class re{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={...oe,...t}}createLoading(e,t){if(this.container)return;this.onCloseCallback=t;if(P({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.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 i="dark"===this.effectiveTheme?e.logoDark||e.logoLight:e.logoLight||e.logoDark;if(i){const t=document.createElement("img");t.className="sv-logo",t.src=i,t.alt=e.companyName,n.appendChild(t);const s=document.createElement("span");s.className="sv-sr-only",s.textContent=e.companyName,n.appendChild(s)}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(N()),this.closeBtnClickHandler=()=>this.handleClose(),this.closeBtn.addEventListener("click",this.closeBtnClickHandler),t.appendChild(this.closeBtn)),t}handleClose(){this.onCloseCallback&&this.onCloseCallback()}}class ae{constructor(e){this.renderer=null,this.config=e,this.api=new w(e),e.preloadConfig&&this.api.preloadConfig()}async pop(e={}){return this.renderer&&this.renderer.close(),new Promise((t,n)=>{const i=new D;this.renderer=new se(i,this.api,e,{onSuccess:n=>{e.onSuccess?.(n),t(n)},onError:t=>{e.onError?.(t),n(t)},onCancel:()=>{const t=new a;e.onCancel?.(),n(t)}}),this.renderer.start().catch(t=>{e.onError?.(t),n(t)})})}async render(e){if(!(e.target&&e.target instanceof HTMLElement))throw new s("target must be a valid HTMLElement");return this.renderer&&this.renderer.close(),new Promise((t,n)=>{const i=new re(e.target,{showHeader:e.showHeader,showCloseButton:e.showCloseButton,showFooter:e.showFooter});this.renderer=new se(i,this.api,e,{onSuccess:n=>{e.onSuccess?.(n),t(n)},onError:t=>{e.onError?.(t),n(t)},onCancel:()=>{const t=new a;e.onCancel?.(),n(t)}}),this.renderer.start().catch(t=>{e.onError?.(t),n(t)})})}async verifyToken(e){if(!e||"string"!=typeof e)throw new s("Token is required");const t=e.split(".");if(3!==t.length)throw new s("Invalid token format");const[,n]=t,i=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}(i))throw new s("Invalid token payload structure");const o=Math.floor(Date.now()/1e3);if(i.exp<o)throw new s("Token has expired");const r=`${this.config.apiBaseUrl}/apps/identity/${this.config.accountId}`;if(i.iss!==r)throw new s("Invalid token issuer");return i}close(){this.renderer&&(this.renderer.close(),this.renderer=null)}async verify(e={}){return this.pop(e)}}class le{constructor(e){this.http=e}async create(e){this.validateCreateOptions(e);const t=this.encodePayload(e.payload),n=await this.http.post("/v1/sparks",{payload:t,ttl:e.ttl??3600,max_reads:e.maxReads??1,password:e.password,name:e.name});return{id:n.data.spark_id,url:n.data.url,expiresAt:n.data.expires_at,maxReads:n.data.max_reads,name:n.data.name}}async read(e,t){if(!e)throw new s("Spark ID is required");const n=e.startsWith("spk_")?e:`spk_${e}`,i={};t&&(i["X-Spark-Password"]=t);const o=await this.http.get(`/v1/sparks/${n}`,{headers:i});return{data:this.decodePayload(o.data.payload),readAt:o.data.read_at,burned:o.data.burned,readsRemaining:o.data.reads_remaining}}validateCreateOptions(e){if(!e.payload)throw new s("Payload is required");if(void 0!==e.ttl&&(e.ttl<60||e.ttl>604800))throw new s("TTL must be between 60 and 604800 seconds (1 minute to 7 days)");if(void 0!==e.maxReads&&(e.maxReads<1||e.maxReads>100))throw new s("maxReads must be between 1 and 100")}encodePayload(e){return"string"==typeof e?e:b(e)}decodePayload(e){return e}}class de{constructor(e,t){this.http=t}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 s("Vault ID is required");if(!t)throw new s("VMK is required");const n=e.startsWith("vlt_")?e:`vlt_${e}`,i=await this.http.post(`/v1/vaults/${n}/unseal`,{vmk:t});return{id:i.data.vault_id,name:i.data.name,vatToken:i.data.vat_token,expiresAt:i.data.expires_at,ingotCount:i.data.ingot_count,storageBytes:i.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"),i=t.contentType??t.file.type??"application/octet-stream",s=await this.http.post(`/v1/vaults/${e.id}/ingots`,{name:n,content_type:i,size_bytes:t.file.size},{headers:{Authorization:`Bearer ${e.vatToken}`}});return{id:s.data.ingot_id,name:s.data.name,contentType:s.data.content_type,size:s.data.size_bytes,createdAt:s.data.created_at,type:s.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 s("Vault name is required");if(e.name.length>255)throw new s("Vault name must be 255 characters or less")}validateUploadOptions(e){if(!e.file)throw new s("File is required");if(0===e.file.size)throw new s("File cannot be empty")}}const ce=["hex","base64","base64url","alphanumeric","alphanumeric-mixed","password","numeric","uuid","bytes"];function he(e){return ce.includes(e)}class pe{constructor(e){this.http=e}async generate(e){this.validateOptions(e);const t=await this.http.post("/v1/apps/rng/generate",{num_bytes:e.bytes,format:e.format??"base64url"}),n=t.data.format??"base64url";if(!he(n))throw new s(`Server returned invalid format: ${n}`);return{value:"bytes"===n?v(t.data.random_bytes_b64):t.data.random_bytes_b64,bytes:t.data.num_bytes,format:n,referenceId:t.data.reference_id}}async uuid(){const e=await this.generate({bytes:16,format:"uuid"});if("string"!=typeof e.value)throw new s("Expected string value for uuid format");return e.value}async hex(e){const t=await this.generate({bytes:e,format:"hex"});if("string"!=typeof t.value)throw new s("Expected string value for hex format");return t.value}async alphanumeric(e){const t=await this.generate({bytes:e,format:"alphanumeric-mixed"});if("string"!=typeof t.value)throw new s("Expected string value for alphanumeric format");return t.value}async password(e){const t=await this.generate({bytes:e,format:"password"});if("string"!=typeof t.value)throw new s("Expected string value for password format");return t.value}validateOptions(e){if(!e.bytes||"number"!=typeof e.bytes)throw new s("bytes is required and must be a number");if(e.bytes<1||e.bytes>1024)throw new s("bytes must be between 1 and 1024");if(e.format&&!he(e.format))throw new s(`Invalid format. Must be one of: ${ce.join(", ")}`)}}let ue=!1;function me(e){ue=e,e&&ge("info","Debug mode enabled")}function ge(e,t,...n){const i=`[SparkVault] ${(new Date).toISOString().split("T")[1].slice(0,-1)} ${t}`;switch(e){case"debug":ue&&console.debug(i,...n);break;case"info":ue&&console.log(i,...n);break;case"warn":ue&&console.warn(i,...n);break;case"error":console.error(i,...n)}}const be={debug:(e,...t)=>ge("debug",e,...t),info:(e,...t)=>ge("info",e,...t),warn:(e,...t)=>ge("warn",e,...t),error:(e,...t)=>ge("error",e,...t),group:e=>{ue&&console.group(`[SparkVault] ${e}`)},groupEnd:()=>{ue&&console.groupEnd()}},ye={initialized:!1,instance:null,config:null,observer:null,isAuthenticating:!1};function fe(e){if(!e||"string"!=typeof e)return null;const t=e.split(".");let n=window;for(const i of t){if(!n||"object"!=typeof n||!(i in n))return be.warn(`Function "${e}" not found on window`),null;n=n[i]}return"function"==typeof n?n:(be.warn(`"${e}" is not a function`),null)}function ke(e){const t=ye.config;if(t){if(be.error("Verification error:",e.message),t.errorFunction){const n=fe(t.errorFunction);if(n){be.debug(`Calling error function: ${t.errorFunction}`);try{n(e)}catch(e){be.error("Error in error function:",e)}}}if(t.errorUrl){const n=new URL(t.errorUrl,window.location.origin);n.searchParams.set("error",e.message),be.debug(`Redirecting to error URL: ${n.toString()}`),window.location.href=n.toString()}}}async function ve(){if(ye.instance)if(ye.isAuthenticating)be.debug("Already authenticating, skipping");else{ye.isAuthenticating=!0,be.info("Starting verification...");try{const e=await ye.instance.identity.pop();if(!e)return be.info("User cancelled verification"),void(ye.isAuthenticating=!1);await async function(e){const t=ye.config;if(t){if(be.info("Verification successful",{identity:e.identity,identityType:e.identityType,hasToken:!!e.token}),t.successFunction){const n=fe(t.successFunction);if(n){be.debug(`Calling success function: ${t.successFunction}`);try{n(e)}catch(e){be.error("Error in success function:",e)}}}if(t.successUrl){be.debug(`POSTing to success URL: ${t.successUrl}`);try{const n=await fetch(t.successUrl,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},credentials:"include",body:JSON.stringify({token:e.token,identity:e.identity,identityType:e.identityType})}),i=await n.json();if(be.debug("Success URL response:",i),i.redirectUrl||i.redirect_url){const e=i.redirectUrl||i.redirect_url;be.info(`Redirecting to: ${e}`),window.location.href=e}else!0===i.reload&&(be.info("Reloading page"),window.location.reload())}catch(e){be.error("Failed to POST to success URL:",e),ke(e instanceof Error?e:new Error(String(e)))}}}}(e)}catch(e){if(e&&"object"==typeof e&&"name"in e){const t=e;if("UserCancelledError"===t.name||"user_cancelled"===t.code)return be.info("User cancelled verification"),void(ye.isAuthenticating=!1)}ke(e instanceof Error?e:new Error(String(e)))}finally{ye.isAuthenticating=!1}}else be.error("SDK not initialized")}function we(){const e=ye.config;if(!e?.attachSelector)return;const t=document.querySelectorAll(e.attachSelector);be.debug(`Found ${t.length} elements matching "${e.attachSelector}"`),t.forEach(e=>{"true"!==e.dataset.sparkvaultAttached&&(e.dataset.sparkvaultAttached="true",be.debug("Attaching click handler to element:",e),e.addEventListener("click",e=>{be.debug("Auth element clicked"),e.preventDefault(),e.stopPropagation(),ve()}))})}function xe(){const e=ye.config;e?.attachSelector&&("undefined"!=typeof MutationObserver?(ye.observer=new MutationObserver(e=>{let t=!1;for(const n of e)if(n.addedNodes.length>0){t=!0;break}t&&we()}),ye.observer.observe(document.body,{childList:!0,subtree:!0}),be.debug("MutationObserver started for dynamic elements")):be.warn("MutationObserver not available, dynamic elements will not be auto-attached"))}class Ce{constructor(e){!function(e){if(!e.accountId)throw new s("accountId is required",{field:"accountId"});if("string"!=typeof e.accountId)throw new s("accountId must be a string",{field:"accountId",received:typeof e.accountId});if(!e.accountId.startsWith("acc_"))throw new s('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}}(e);const t=new u(this.config);this.identity=new ae(this.config),this.sparks=new le(t),this.vaults=new de(this.config,t),this.rng=new pe(t)}static init(e){return new Ce(e)}static get version(){return"__VERSION__"}}"undefined"!=typeof window&&(window.SparkVault=Ce,function(e){if(ye.initialized)return void be.debug("Auto-init already completed");if("undefined"==typeof window||"undefined"==typeof document)return;const t=function(){if(document.currentScript instanceof HTMLScriptElement)return document.currentScript;const e=document.querySelectorAll('script[src*="sparkvault"]');for(const t of e)if(t.dataset.accountId)return t;return e[0]||null}();if(!t)return;const n=function(e){const t=e.dataset.timeout;return{accountId:e.dataset.accountId||null,attachSelector:e.dataset.attachSelector||null,successUrl:e.dataset.successUrl||null,successFunction:e.dataset.successFunction||null,errorUrl:e.dataset.errorUrl||null,errorFunction:e.dataset.errorFunction||null,debug:"true"===e.dataset.debug,preloadConfig:"false"!==e.dataset.preloadConfig,timeout:t?parseInt(t,10):3e4}}(t);if(ye.config=n,n.debug&&me(!0),be.info("Auto-init starting..."),be.group("Configuration"),be.debug("Account ID:",n.accountId||"(not set)"),be.debug("Attach Selector:",n.attachSelector||"(not set)"),be.debug("Success URL:",n.successUrl||"(not set)"),be.debug("Success Function:",n.successFunction||"(not set)"),be.debug("Error URL:",n.errorUrl||"(not set)"),be.debug("Error Function:",n.errorFunction||"(not set)"),be.debug("Debug:",n.debug),be.groupEnd(),!n.accountId)return void be.debug("No data-account-id attribute, skipping auto-init");try{ye.instance=e.init({accountId:n.accountId,preloadConfig:n.preloadConfig,timeout:n.timeout}),ye.initialized=!0,be.info("SDK initialized successfully")}catch(e){return void be.error("Failed to initialize SDK:",e)}n.attachSelector&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{we(),xe()}):(we(),xe()));const i=window.SparkVault;i&&(i.identity={pop:ve,render:e=>ye.instance?ye.instance.identity.render(e):Promise.reject(new Error("SDK not initialized")),authenticate:ve,verify:e=>ye.instance?ye.instance.identity.pop(e):Promise.reject(new Error("SDK not initialized"))},i.getInstance=()=>ye.instance),be.info("Auto-init complete")}(Ce)),e.AuthenticationError=n,e.AuthorizationError=i,e.NetworkError=o,e.PopupBlockedError=l,e.SparkVault=Ce,e.SparkVaultError=t,e.TimeoutError=r,e.UserCancelledError=a,e.ValidationError=s,e.default=Ce,e.logger=be,e.setDebugMode=me,Object.defineProperty(e,"__esModule",{value:!0})});
|
|
2
2
|
//# sourceMappingURL=sparkvault.js.map
|