@encorekit/web-sdk 1.1.21 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Encore={})}(this,function(e){"use strict";var t;!function(e){e.NETWORK_ERROR="NETWORK_ERROR",e.API_ERROR="API_ERROR",e.CONFIGURATION_ERROR="CONFIGURATION_ERROR",e.INITIALIZATION_ERROR="INITIALIZATION_ERROR",e.STORAGE_ERROR="STORAGE_ERROR",e.INVALID_REQUEST="INVALID_REQUEST",e.UNAUTHORIZED="UNAUTHORIZED",e.NOT_FOUND="NOT_FOUND",e.TIMEOUT="TIMEOUT",e.UNKNOWN_ERROR="UNKNOWN_ERROR"}(t||(t={}));const n="[Encore SDK]",r=new class{#e="none";setLogLevel(e){this.#e=e}getLogLevel(){return this.#e}debug(...e){"debug"===this.#e&&console.debug(n,...e)}info(...e){"debug"===this.#e&&console.info(n,...e)}warn(...e){"debug"===this.#e&&console.warn(n,...e)}error(...e){"debug"===this.#e&&console.error(n,...e)}critical(...e){console.error(n,"[CRITICAL]",...e)}};class i{#t=null;#n=!1;configure(e){if(this.#n)return r.warn("SDK already configured. Ignoring subsequent configure() call."),!1;const t=this.#r(e);if(t)return r.error("Invalid configuration:",t),!1;const n=e.logLevel??"none",i=e.environment??"production";return this.#t={apiKey:e.apiKey,environment:i,baseURL:this.#i(i),analyticsBaseURL:this.#o(i),logLevel:n,redemptionMode:e.redemptionMode??"deferred"},r.setLogLevel(n),this.#n=!0,r.debug("SDK configured successfully",this.#t),!0}#r(e){return e.apiKey&&"string"==typeof e.apiKey&&""!==e.apiKey.trim()?e.environment&&!["production","development","staging","localhost"].includes(e.environment)?"environment must be 'production', 'development', 'staging', or 'localhost'":e.logLevel&&!["none","debug"].includes(e.logLevel)?"logLevel must be 'none' or 'debug'":e.redemptionMode&&!["deferred","immediate"].includes(e.redemptionMode)?"redemptionMode must be 'deferred' or 'immediate'":null:"apiKey is required and must be a non-empty string"}#i(e){switch(e){case"localhost":return"http://localhost:4000/publisher/sdk/v1";case"development":return"https://api.dev.encorekit.com/encore/publisher/sdk/v1";case"staging":return"https://api.staging.encorekit.com/encore/publisher/sdk/v1";default:return"https://api.encorekit.com/encore/publisher/sdk/v1"}}#o(e){switch(e){case"localhost":return"http://localhost:8081/v1";case"development":return"https://api.dev.encorekit.com/analytics/v1";case"staging":return"https://api.staging.encorekit.com/analytics/v1";default:return"https://api.encorekit.com/analytics/v1"}}getConfig(){return this.#t}isConfigured(){return this.#n}getApiKey(){return this.#t?.apiKey??null}getBaseURL(){return this.#t?.baseURL??null}getAnalyticsBaseURL(){return this.#t?.analyticsBaseURL??null}getEnvironment(){return this.#t?.environment??null}getRedemptionMode(){return this.#t?.redemptionMode??"deferred"}reset(){this.#t=null,this.#n=!1,r.debug("Configuration reset")}}const o="transactions";class s{#s;constructor(e){this.#s=e}#a(){const e=this.#s.get(o);return e&&"object"==typeof e?{transactions:e.transactions??{},placements:e.placements??{},latest:e.latest??null}:{transactions:{},placements:{},latest:null}}#l(e){this.#s.set(o,e)}#d(e){return!!e&&"claimed"===e.status&&new Date(e.expiresAt).getTime()>Date.now()}save(e){const t=this.#a();t.transactions[e.id]=e,e.placementId&&(t.placements[e.placementId]=e.id),t.latest=e.id,this.#c(t),this.#l(t),r.debug("TransactionStore: saved transaction",{transactionId:e.id,placement:e.placementId})}getByTransactionId(e){return this.#a().transactions[e]??null}getByPlacement(e){const t=this.#a(),n=t.placements[e];if(!n)return null;const r=t.transactions[n];return this.#d(r)?r:null}getLatest(){const e=this.#a();if(e.latest){const t=e.transactions[e.latest];if(this.#d(t))return t}return Object.values(e.transactions).filter(e=>this.#d(e)).sort((e,t)=>new Date(t.createdAt).getTime()-new Date(e.createdAt).getTime())[0]??null}markRedeemed(e){const t=this.#a(),n=t.transactions[e];n&&(n.status="redeemed",n.redeemedAt=(new Date).toISOString(),n.placementId&&t.placements[n.placementId]===e&&delete t.placements[n.placementId],t.latest===e&&(t.latest=null),this.#l(t),r.debug("TransactionStore: marked redeemed",{transactionId:e}))}#c(e){const t=Date.now();for(const[n,r]of Object.entries(e.transactions))(new Date(r.expiresAt).getTime()<=t||"redeemed"===r.status)&&delete e.transactions[n];for(const[t,n]of Object.entries(e.placements))e.transactions[n]||delete e.placements[t];e.latest&&!e.transactions[e.latest]&&(e.latest=null)}clear(){this.#l({transactions:{},placements:{},latest:null})}}const a="encore_";class l{#h;#u;constructor(){this.#u=new Map,this.#h=this.#p(),r.debug(`StorageManager initialized with ${this.#h}`)}#p(){return this.#m("localStorage")?"localStorage":this.#m("sessionStorage")?"sessionStorage":(r.warn("Neither localStorage nor sessionStorage available, using in-memory storage"),"memory")}#m(e){try{const t="localStorage"===e?localStorage:sessionStorage,n="__encore_test__";return t.setItem(n,"test"),t.removeItem(n),!0}catch{return!1}}#g(){return"localStorage"===this.#h?localStorage:"sessionStorage"===this.#h?sessionStorage:this.#u}#f(e){return`${a}${e}`}set(e,t){if(void 0===t)return void r.warn(`Attempted to store undefined value for key: ${e}`);const n=this.#f(e),i=JSON.stringify(t);try{const t=this.#g();t instanceof Map?t.set(n,i):t.setItem(n,i),r.debug(`Stored ${e} in ${this.#h}`)}catch(e){e instanceof Error&&"QuotaExceededError"===e.name?r.error("Storage quota exceeded:",e):r.error("Failed to store item:",e),"memory"!==this.#h&&(r.warn("Falling back to memory storage for this item"),this.#u.set(n,i))}}get(e){const t=this.#f(e);try{const n=this.#g();let i=null;return i=n instanceof Map?n.get(t)??null:n.getItem(t),null===i||"undefined"===i?("undefined"===i&&(r.warn(`Found corrupted value "undefined" for key: ${e}, cleaning up`),this.remove(e)),null):JSON.parse(i)}catch(t){return r.error(`Failed to retrieve ${e}:`,t),this.remove(e),null}}remove(e){const t=this.#f(e);try{const n=this.#g();n instanceof Map?n.delete(t):n.removeItem(t),r.debug(`Removed ${e} from ${this.#h}`)}catch(t){r.error(`Failed to remove ${e}:`,t)}}clear(){try{const e=this.#g();if(e instanceof Map)for(const t of e.keys())t.startsWith(a)&&e.delete(t);else{const t=[];for(let n=0;n<e.length;n++){const r=e.key(n);r?.startsWith(a)&&t.push(r)}t.forEach(t=>e.removeItem(t))}r.debug(`Cleared all Encore data from ${this.#h}`)}catch(e){r.error("Failed to clear storage:",e)}}getStorageType(){return this.#h}}const d="signal_queue";class c{#s;#b;#v;#y=[];#w=!1;#x;constructor(e,t,n){this.#s=e,this.#b=t,this.#v=n,this.#I(),this.#k(),this.processQueue()}setUserId(e){this.#v!==e&&(r.debug("SignalManager: User ID changed, clearing queue"),this.#v=e,this.#y=[],this.#I())}#I(){try{const e=`${this.#v}_${d}`,t=this.#s.get(e);t&&Array.isArray(t)&&(this.#y=t,r.debug("Loaded grant signal queue from storage",{count:t.length}))}catch(e){r.error("Failed to load grant signal queue from storage:",e)}}#C(){try{const e=`${this.#v}_${d}`;this.#s.set(e,this.#y),r.debug("Saved grant signal queue to storage",{count:this.#y.length})}catch(e){r.error("Failed to save grant signal queue to storage:",e)}}enqueue(e,t){const n=this.#y.some(n=>n.transactionId===e&&n.grantType===t);if(n)return void r.debug("Grant signal already in queue, skipping",{transactionId:e,grantType:t});const i={transactionId:e,grantType:t,timestamp:(new Date).toISOString(),attempts:0};this.#y.push(i),this.#C(),r.info("Grant signal added to queue",{transactionId:e,grantType:t})}dequeue(e){const t=this.#y.findIndex(t=>t.transactionId===e.transactionId&&t.grantType===e.grantType);-1!==t&&(this.#y.splice(t,1),this.#C(),r.debug("Grant signal removed from queue",{transactionId:e.transactionId,grantType:e.grantType}))}getQueue(){return[...this.#y]}clearQueue(){this.#y=[],this.#C(),r.debug("Grant signal queue cleared")}async sendGrantSignal(e,t){try{if(r.debug("Sending grant signal",{transactionId:e,grantType:t}),!await this.#b.sendGrantSignal(e,t))throw new Error("Grant signal failed (returned false)");r.info("Grant signal sent successfully",{transactionId:e,grantType:t})}catch{r.warn("Failed to send grant signal, adding to queue",{transactionId:e,grantType:t}),this.enqueue(e,t)}}async processQueue(){if(this.#w)r.debug("Queue processing already in progress, skipping");else if(0!==this.#y.length){this.#w=!0,r.info("Processing grant signal queue",{count:this.#y.length});try{for(const e of[...this.#y])await this.#E(e),this.#y.length>1&&await this.#$(100)}finally{this.#w=!1}}}async#E(e){if(e.attempts>=3)return r.error("Grant signal max attempts reached, removing from queue",{transactionId:e.transactionId,grantType:e.grantType,attempts:e.attempts}),void this.dequeue(e);if(e.lastAttemptAt){const t=new Date(e.lastAttemptAt).getTime(),n=Date.now(),i=1e3*Math.pow(2,e.attempts-1),o=n-t;if(o<i)return void r.debug("Skipping signal, waiting for backoff period",{transactionId:e.transactionId,remainingWait:i-o})}e.attempts++,e.lastAttemptAt=(new Date).toISOString(),this.#C();try{r.debug("Attempting to send queued grant signal",{transactionId:e.transactionId,grantType:e.grantType,attempt:e.attempts}),await this.#b.sendGrantSignal(e.transactionId,e.grantType)?(r.info("Queued grant signal sent successfully",{transactionId:e.transactionId,grantType:e.grantType}),this.dequeue(e)):r.warn("Grant signal failed, will retry",{transactionId:e.transactionId,attempt:e.attempts})}catch(t){r.warn("Failed to send queued grant signal, will retry",{transactionId:e.transactionId,attempt:e.attempts,error:t})}}#k(){this.#x=function(e){const t=()=>{r.debug("Network status: online"),e()};return window.addEventListener("online",t),()=>{window.removeEventListener("online",t)}}(()=>{r.debug("Network came online, processing grant signal queue"),this.processQueue()})}destroy(){this.#x&&(this.#x(),this.#x=void 0)}#$(e){return new Promise(t=>setTimeout(t,e))}}function h(e,t,n,r){return{name:"sdk_offer_presentation_failed",distinctId:e,properties:{reason:t,presentationId:n,...r&&{variantId:r}}}}function u(e,t){return{name:"sdk_offer_time_spent",distinctId:e,properties:{...t}}}function p(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=15&(crypto.getRandomValues(new Uint8Array(1))[0]??0);return("x"===e?t:3&t|8).toString(16)})}class m{#b;#T;#v;#S=[];#A=null;#N=null;#L=null;#R=new Map;#U=0;constructor(e,t,n){this.#b=e,this.#v=t,this.#T=n??null}async fetchOffers(e,t=!0){if(t&&this.#S.length>0&&this.#A)return r.debug("Using cached offers",{count:this.#S.length}),{offers:[...this.#S]};try{r.debug("Fetching offers from API",{userId:this.#v});const t=await this.#b.fetchOffers(this.#v,e);return 0===t.offers?.length?(r.info("No offers available for user"),this.#S=[],this.#A=Date.now(),{offers:[]}):(this.#S=t.offers,this.#A=Date.now(),r.info("Offers fetched successfully",{count:t.offers.length,offerIds:t.offers.map(e=>e.id)}),{offers:[...t.offers]})}catch(e){throw r.error("Failed to fetch offers",e),e}}beginPresentation(){return this.#N=p(),this.#L=Date.now(),this.#R.clear(),this.#U=0,this.#N}trackOfferPresented(e,t,n,r){if(!this.#T||!this.#N)return;this.#U++;const i=this.#R.get(e.id);var o,s;i?(i.viewCount++,i.startTime=Date.now()):this.#R.set(e.id,{startTime:Date.now(),totalTime:0,viewCount:1}),this.#T.track((o=this.#v,s={campaignId:e.campaignId,creativeId:e.creativeId,...null!=e.merchantName&&{advertiserName:e.merchantName},impressionId:t,offerIndex:n,totalOffers:r,presentationId:this.#N},{name:"sdk_offer_presented",distinctId:o,properties:{...s}}))}trackOfferViewEnd(e){const t=this.#R.get(e);t&&t.startTime>0&&(t.totalTime+=(Date.now()-t.startTime)/1e3,t.startTime=0)}trackOfferClaimed(e,t,n,r,i){var o,s;this.#T&&this.#N&&this.#T.track((o=this.#v,s={campaignId:e.campaignId,creativeId:e.creativeId,...null!=e.merchantName&&{advertiserName:e.merchantName},impressionId:t,transactionId:n,offerIndex:r,totalOffers:i,presentationId:this.#N},{name:"sdk_offer_claimed",distinctId:o,properties:{...s}}))}trackOfferDeclined(e,t,n,r,i){var o,s;this.#T&&this.#N&&this.#T.track((o=this.#v,s={campaignId:e.campaignId,creativeId:e.creativeId,...null!=e.merchantName&&{advertiserName:e.merchantName},impressionId:t,offerIndex:n,totalOffers:r,...null!=i&&{declineReason:i},presentationId:this.#N},{name:"sdk_offer_declined",distinctId:o,properties:{...s}}))}endPresentation(e){if(!this.#T||!this.#N||!this.#L)return;const t=(Date.now()-this.#L)/1e3;for(const[e,t]of this.#R){let n=t.totalTime;t.startTime>0&&(n+=(Date.now()-t.startTime)/1e3);const r=this.#S.find(t=>t.id===e),i=r?this.#S.indexOf(r):0;this.#T.track(u(this.#v,{campaignId:r?.campaignId??"",offerIndex:i,timeSpentSeconds:n,viewCount:t.viewCount,totalOffers:this.#S.length,presentationId:this.#N}))}var n,r;this.#T.track((n=this.#v,r={presentationId:this.#N,totalTimeSpentSeconds:t,totalOffers:this.#S.length,offersViewed:this.#U,...null!=e&&{declineReason:e}},{name:"sdk_offer_sheet_dismissed",distinctId:n,properties:{...r}})),this.#N=null,this.#L=null,this.#R.clear(),this.#U=0}async startTransaction(e){try{r.debug("Starting transaction",{campaignId:e});const t=await this.#b.startTransaction(e,this.#v);if(!t.transactionId)throw new Error(t.error??"No transactionId in response");return r.info("Transaction started",{transactionId:t.transactionId,campaignId:e}),t.transactionId}catch(t){throw r.error("Failed to start transaction",{campaignId:e,error:t}),t}}clearCache(){this.#S=[],this.#A=null,r.debug("Offer cache cleared")}setUserId(e){this.#v=e,this.clearCache(),r.debug("OfferManager user ID updated",{userId:e})}}class g{#b;#O;#M;#D=0;#P=1e3;#_=8e3;#F=null;#z=!1;constructor(e,t){this.#b=e,this.#O=t.transactionId,this.#M=t.timeout??3e4}async poll(){return this.#z?(r.warn("Verification polling already in progress",{transactionId:this.#O}),"pending"):(this.#z=!0,this.#D=Date.now(),r.debug("Starting verification polling",{transactionId:this.#O,timeout:this.#M}),this.#V())}stop(){this.#z=!1,null!==this.#F&&(clearTimeout(this.#F),this.#F=null),r.debug("Verification polling stopped",{transactionId:this.#O})}async#V(){for(;this.#z;){const e=Date.now()-this.#D;if(e>=this.#M)return r.warn("Verification polling timeout",{transactionId:this.#O,elapsed:e}),this.stop(),"pending";try{const e=await this.#b.checkVerificationStatus(this.#O);if(r.debug("Verification status checked",{transactionId:this.#O,status:e.status,rawStatus:e.raw}),"verified"===e.status)return r.info("Transaction verified",{transactionId:this.#O}),this.stop(),"verified";if("failed"===e.status)return r.warn("Transaction failed verification",{transactionId:this.#O}),this.stop(),"failed";await this.#$(this.#P),this.#P=Math.min(2*this.#P,this.#_)}catch(e){r.error("Error checking verification status",{transactionId:this.#O,error:e}),await this.#$(this.#P)}}return"pending"}async#$(e){return new Promise(t=>{this.#F=window.setTimeout(()=>{this.#F=null,t()},e)})}}class f{#B=null;#q=null;#K;#H;#j;#Y;constructor(e,t,n,r){this.#j=e,this.#K=t,this.#Y=n,this.#H=r}onNotGranted(e){return this.#B=e,this}onLoadingStateChange(e){return this.#q=e,this}async show(e){try{this.#q&&this.#q(!0),r.debug("PlacementBuilder: Executing presentation",{id:this.#j});const t=void 0===e?await this.#K(this.#Y):await this.#K(this.#Y,e);return this.#q&&this.#q(!1),"dismissed"===t.status&&t.reason&&this.#B&&this.#B(t.reason),t}catch(e){this.#q&&this.#q(!1),r.error("PlacementBuilder: Error during presentation",e);const n={type:"error",error:{code:t.UNKNOWN_ERROR,message:e instanceof Error?e.message:"Unknown error"}};return this.#B&&this.#B(n),{status:"dismissed",reason:n}}}async redeem(){if(!this.#H)return r.error("PlacementBuilder: redeem() is not available for this placement"),{status:"none"};try{this.#q?.(!0);const e=await this.#H(this.#j);return this.#q?.(!1),e}catch(e){return this.#q?.(!1),r.error("PlacementBuilder: Error during redeem",e),{status:"none"}}}}function b(e,t){let n=e;for(const[e,r]of Object.entries(t))n=n.replace(`:${e}`,encodeURIComponent(r));return n}const v="1.1.21";class y{#G;#W;#Q;constructor(e,t){this.#G=e,this.#W=t,this.#Q=new Map}setApiKey(e){this.#G=e}setBaseURL(e){this.#W=e}async get(e,t){return this.#X({url:`${this.#W}${e}`,options:{method:"GET",...t},apiKey:this.#G})}async post(e,t,n){return this.#X({url:`${this.#W}${e}`,options:{method:"POST",body:t,...n},apiKey:this.#G})}async patch(e,t,n){return this.#X({url:`${this.#W}${e}`,options:{method:"PATCH",body:t,...n},apiKey:this.#G})}async#X(e){const{url:t,options:n,apiKey:i}=e,o=this.#Z(t,n),s=this.#Q.get(o);if(s)return r.debug("Deduplicating request:",t),s;const a=this.#J(t,n,i);this.#Q.set(o,a);try{return await a}finally{this.#Q.delete(o)}}async#J(e,t,n){const i=t.retries??3;let o=null;for(let s=0;s<=i;s++)try{if(s>0){const e=1e3*Math.pow(2,s-1);r.debug(`Retrying request (attempt ${s}/${i}) after ${e}ms`),await this.#$(e)}return await this.#ee(e,t,n)}catch(e){if(o=e,this.#te(e))throw e;if(s===i)throw r.error(`Request failed after ${i} retries:`,e),e;r.warn("Request failed, will retry:",e)}throw o??new Error("Request failed")}async#ee(e,t,n){const i=t.timeout??3e4,o=new AbortController,s=setTimeout(()=>o.abort(),i);try{const i={"X-API-Key":n,"X-Platform":"web","Content-Type":"application/json","User-Agent":`Encore-Web-SDK/${v}`,...t.headers},s={method:t.method,headers:i,signal:o.signal};t.body&&(s.body=JSON.stringify(t.body)),r.debug(`${t.method} ${e}`);const a=await fetch(e,s);return a.ok||await this.#ne(a),await a.json()}catch(e){if(e instanceof Error){if("AbortError"===e.name)throw new Error("Request timeout");if(this.#re(e))throw new Error(`Network error: ${e.message}`)}if(e instanceof Error)throw e;throw new Error(String(e))}finally{clearTimeout(s)}}async#ne(e){let n,r=`HTTP ${e.status}: ${e.statusText}`;try{const t=await e.json();"object"==typeof t&&null!==t&&"string"==typeof t.error&&(r=t.error,n="string"==typeof t.code?t.code:void 0)}catch{}const i=n?{code:n,message:r}:{code:t.API_ERROR,statusCode:e.status,message:r},o=new Error(r);throw Object.assign(o,i),o}#te(e){if("object"==typeof e&&null!==e&&"statusCode"in e&&void 0!==e.statusCode){const t=e.statusCode;return t>=400&&t<500}return!1}#re(e){return e.message.includes("fetch")||e.message.includes("network")||e.message.includes("ECONNREFUSED")||e.message.includes("ETIMEDOUT")}#Z(e,t){const n=t.body?JSON.stringify(t.body):"";return`${t.method}:${e}:${n}`}#$(e){return new Promise(t=>setTimeout(t,e))}#ie(e){switch(e){case"pending_conversion":case"provisional_granted":default:return"pending";case"verified":case"final_granted":return"verified";case"expired":case"failed":return"failed"}}async fetchOffers(e,t){try{const n=await this.post("/offers/search",{userId:e,attributes:t,platform:"web",sdkVersion:v}),r=[];for(const e of n.offers){const t=e.creatives??[];for(const n of t){const t={id:n.id,title:n.title,description:n.quickInstructions??n.subtitle??n.description??"",imageUrl:n.primaryImageUrl,ctaText:n.ctaText,advertiserUrl:n.destinationUrl??e.destinationUrl??"",campaignId:e.id,creativeId:n.id,quickInstructions:n.quickInstructions??"",instructions:(n.instructions??[]).map(e=>({title:e.title,subtitle:e.subtitle,...null!=e.ctaButtonText&&{ctaButtonText:e.ctaButtonText}})),tooltipText:n.tooltipText??"",...e.organization?.name&&{merchantName:e.organization.name},...e.perk&&{perk:e.perk},...n.logoUrl&&{logoUrl:n.logoUrl},...n.trackingParameters&&{trackingParameters:Object.fromEntries(Object.entries(n.trackingParameters).filter(([,e])=>"string"==typeof e||"number"==typeof e||"boolean"==typeof e).map(([e,t])=>[e,String(t)]))}};r.push(t)}}return{offers:r}}catch(e){throw r.error("Failed to fetch offers:",e),e}}async startTransaction(e,t){try{return await this.post("/transactions",{campaignId:e,userId:t})}catch(e){throw r.error("Failed to start transaction:",e),e}}async sendGrantSignal(e,t){try{const n=function(e){return b("/transactions/:transactionId/signals",{transactionId:e})}(e);return(await this.post(n,{grantType:t})).success}catch(e){throw r.error("Failed to send grant signal:",e),e}}async fetchEntitlements(e){try{const t=await this.get(`/entitlements?userId=${encodeURIComponent(e)}`),n=[],i=[{details:t.provisional,scope:"provisional"},{details:t.verified,scope:"verified"}];for(const{details:e,scope:t}of i)if(e){if(e.freeTrial&&n.push({type:"freeTrial",transactionId:"",scope:t,grantedAt:e.freeTrial.startedAt??"",expiresAt:e.freeTrial.expiresAt??""}),e.discounts)for(const r of e.discounts)n.push({type:"discount",value:r.value,unit:r.unit,transactionId:"",scope:t,grantedAt:"",expiresAt:r.expiresAt??""});e.credits&&n.push({type:"credit",value:e.credits.totalAmount,unit:"dollars",transactionId:"",scope:t,grantedAt:"",expiresAt:e.credits.expiresAt??""})}return r.debug("Transformed entitlements response",{totalCount:n.length}),n}catch(e){throw r.error("Failed to fetch entitlements:",e),e}}async checkVerificationStatus(e){try{const t=function(e){return b("/transactions/:transactionId/status",{transactionId:e})}(e),n=await this.get(t);return{status:this.#ie(n.status),raw:n.status}}catch(e){throw r.error("Failed to check verification status:",e),e}}async initUser(e,t){try{return await this.post("/users",{userId:e,...t&&{attributes:t}})}catch(e){throw r.error("Failed to init user:",e),e}}async identifyUser(e,t,n){try{return await this.patch("/users/identify",{currentUserId:e,newUserId:t,...n&&{attributes:n}})}catch(e){throw r.error("Failed to identify user:",e),e}}async fetchConfig(e,t){try{return await this.get(`/config?userId=${encodeURIComponent(e)}&sdkVersion=${encodeURIComponent(t)}`)}catch(e){throw r.error("Failed to fetch config:",e),e}}async reportError(e){try{const t="undefined"!=typeof window?window.location.hostname:"unknown";await this.post("/errors",{errorType:e.errorType,message:e.message,context:e.context,severity:e.severity??"error",timestamp:(new Date).toISOString(),platform:"web",sdkVersion:v,appBundleId:t,...e.stackTrace&&{stackTrace:e.stackTrace},...e.userId&&{userId:e.userId},...e.metadata&&{metadata:e.metadata}})}catch(e){r.warn("Failed to report error (non-blocking):",e)}}async revokeEntitlements(e){try{return(await this.post("/entitlements/revoke",{userId:e})).success}catch(e){throw r.error("Failed to revoke entitlements:",e),e}}}function w(e,t=p()){const n="undefined"!=typeof window?window.location.hostname:"unknown";return{event_id:t,event_name:e.name,event_timestamp:(new Date).toISOString(),distinct_id:e.distinctId,properties:{...e.properties,platform:"web",sdk_version:v,app_bundle_id:n}}}function x(e,t,n,r){return fetch(`${e}/events`,{method:"POST",...r?.keepalive&&{keepalive:!0},headers:{"X-Analytics-Key":t,"X-Platform":"web","Content-Type":"application/json"},body:JSON.stringify(n)})}const I=new Set(["user_identified"]);class k{#G;#oe;#s;constructor(e,t,n){this.#G=e,this.#oe=t,this.#s=n??null}track(e){try{if(this.#se(e))return;const t=w(e);x(this.#oe,this.#G,t).catch(t=>{r.warn("Analytics event dispatch failed",{eventName:e.name,error:t})})}catch{}}#se(e){if(!this.#s||!I.has(e.name))return!1;const t=`analytics_dedup_${e.name}_${e.distinctId}`;try{const e=this.#s.get(t),n=Date.now();return"number"==typeof e&&n-e<864e5||(this.#s.set(t,n),!1)}catch{return!1}}}async function C(e,t){const n=`${e}:${t}`,r=(new TextEncoder).encode(n),i=new Uint8Array(await crypto.subtle.digest("SHA-256",r));let o=0n;for(let e=0;e<8;e++)o=o<<8n|BigInt(i[e]??0);return Number(o%100n)}const E="ncl_assignment",$="ncl_exposure_marker";class T{#s;#ae;#le;#t=null;constructor(e,t,n){this.#s=e,this.#ae=t,this.#le=n}setConfig(e){this.#t=e??null}async getCohort(){try{return await this.#de()}catch(e){return r.warn("[Experiment] getCohort failed — treating as notEnrolled",e),"notEnrolled"}}async#de(){const e=this.#ae();if(!e)return"notEnrolled";const t=this.#t;if(!t?.enabled)return"notEnrolled";const n=this.#s.get(E);if(n?.appAccountId===e&&n?.assignmentVersion===t.assignmentVersion)return n.cohort;if("undefined"==typeof crypto||!crypto.subtle)return r.warn("[Experiment] NCL disabled — crypto.subtle unavailable (page is not a secure context: serve over HTTPS)"),"notEnrolled";const i=await C(e,t.assignmentVersion),o=function(e,t){return e<t?"treatment":"control"}(i,t.rolloutPct);return this.#s.set(E,{cohort:o,appAccountId:e,assignmentVersion:t.assignmentVersion}),r.debug("[Experiment] Assigned NCL cohort",{cohort:o,version:t.assignmentVersion,rolloutPct:t.rolloutPct,bucket:i}),o}logExposure(e){if("notEnrolled"===e)return;const t=this.#t,n=this.#ae();if(!t||!n)return;const r=`${n}:${t.assignmentVersion}`;if(this.#s.get($)===r)return;const i=function(e,t){return{name:"sdk_experiment_exposure",distinctId:e,properties:{experiment:t.experiment,cohort:t.cohort,assignment_version:t.assignmentVersion,app_account_id:e}}}(n,{experiment:"ncl",cohort:e,assignmentVersion:t.assignmentVersion}),o=p();this.#le.enqueue(i,o)&&this.#s.set($,r)}async getState(){const e=this.#ae(),t=this.#t,n=await this.getCohort();let r=null;if(e&&t?.enabled&&"undefined"!=typeof crypto&&crypto.subtle)try{r=await C(e,t.assignmentVersion)}catch{r=null}const i=!!e&&!!t&&this.#s.get($)===`${e}:${t.assignmentVersion}`;return{experiment:"ncl",enabled:t?.enabled??!1,rolloutPct:t?.rolloutPct??null,assignmentVersion:t?.assignmentVersion??null,appAccountId:e,bucket:r,cohort:n,exposureLogged:i}}}const S="ncl_exposure_outbox";class A{#G;#oe;#s;#ce=new Set;constructor(e,t,n){this.#G=e,this.#oe=t,this.#s=n,this.#he()}enqueue(e,t){const n=w(e,t),r=this.#ue();r.some(e=>e.event_id===t)||(r.push(n),this.#pe(r));const i=this.#ue().some(e=>e.event_id===t);return this.#me(n),i}flush(){for(const e of this.#ue())this.#me(e)}async#me(e){if(!this.#ce.has(e.event_id)){this.#ce.add(e.event_id);try{const t=await x(this.#oe,this.#G,e,{keepalive:!0});t.ok?this.#ge(e.event_id):r.warn("Exposure delivery non-OK, will retry",{status:t.status})}catch(e){r.warn("Exposure delivery failed, will retry",{error:e})}finally{this.#ce.delete(e.event_id)}}}#ge(e){const t=this.#ue().filter(t=>t.event_id!==e);this.#pe(t)}#ue(){return this.#s.get(S)??[]}#pe(e){this.#s.set(S,e)}#he(){"undefined"!=typeof window&&"undefined"!=typeof document&&(window.addEventListener("pagehide",()=>this.flush()),document.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&this.flush()}))}}class N{#fe;constructor(){this.#fe=new Map}on(e,t){this.#fe.has(e)||this.#fe.set(e,new Set);const n=this.#fe.get(e);if(!n)throw new Error(`Failed to get handlers for event '${String(e)}'`);return n.add(t),r.debug(`Event listener added for '${String(e)}'`),()=>{this.off(e,t)}}off(e,t){const n=this.#fe.get(e);n&&(n.delete(t),r.debug(`Event listener removed for '${String(e)}'`),0===n.size&&this.#fe.delete(e))}emit(e,t){const n=this.#fe.get(e);if(n&&0!==n.size){r.debug(`Emitting event '${String(e)}'`,{listenerCount:n.size});for(const i of n)try{i(t)}catch(t){r.error(`Error in event handler for '${String(e)}':`,t)}}}removeAllListeners(e){e?(this.#fe.delete(e),r.debug(`All listeners removed for '${String(e)}'`)):(this.#fe.clear(),r.debug("All event listeners removed"))}listenerCount(e){return this.#fe.get(e)?.size??0}hasListeners(e){const t=this.#fe.get(e);return void 0!==t&&t.size>0}}const L="encore-sdk-styles",R="encore-",U="767px",O="12px",M="20px",D="40px";function P(e={}){if(document.getElementById(L))r.debug("Encore styles already injected");else try{const t=document.createElement("style");t.id=L,t.textContent=function(e={}){const t=function(e){const t=[];return e.primaryColor&&t.push(`--encore-primary-color: ${e.primaryColor};`),e.backgroundColor&&t.push(`--encore-bg-color: ${e.backgroundColor};`),e.textColor&&t.push(`--encore-text-color: ${e.textColor};`),e.borderRadius&&t.push(`--encore-border-radius: ${e.borderRadius};`),e.fontFamily&&t.push(`--encore-font-family: ${e.fontFamily};`),t.join("\n ")}(e);return`\n /* CSS Custom Properties (Theme Variables) */\n :root {\n ${t||"/* No custom theme variables */"}\n --encore-primary-color: ${e.primaryColor??"#4F46E5"};\n --encore-bg-color: ${e.backgroundColor??"#ffffff"};\n --encore-text-color: ${e.textColor??"#1F2937"};\n --encore-text-muted: color-mix(in srgb, var(--encore-text-color) 60%, transparent);\n /* One step darker than muted, lighter than full text — lead-in copy that must\n read above captions without competing with the page title. */\n --encore-text-secondary: color-mix(in srgb, var(--encore-text-color) 78%, transparent);\n --encore-surface-color: color-mix(in srgb, var(--encore-text-color) 5%, var(--encore-bg-color));\n --encore-border-color: color-mix(in srgb, var(--encore-text-color) 10%, var(--encore-bg-color));\n --encore-border-radius: ${e.borderRadius??"12px"};\n /* Semantic success hue (thankYou status check). Deliberately independent of the\n accent color: the check states a payment fact; the accent sells the offer CTA. */\n --encore-success-color: #159a5b;\n --encore-font-family: ${e.fontFamily??'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'};\n }\n\n /* Backdrop */\n .${R}backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.55);\n backdrop-filter: blur(2px);\n z-index: 9998;\n opacity: 0;\n transition: opacity 250ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .${R}backdrop.${R}visible {\n opacity: 1;\n }\n\n /* Box-sizing reset scoped to the SDK modal so padding/borders never cause\n horizontal overflow on host pages that don't set border-box globally. */\n .${R}modal,\n .${R}modal *,\n .${R}backdrop {\n box-sizing: border-box;\n }\n\n /* Modal Container */\n .${R}modal {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 9999;\n background-color: var(--encore-bg-color);\n border-radius: 20px;\n box-shadow: 0 24px 48px -12px rgba(0, 0, 0, 0.18), 0 12px 24px -8px rgba(0, 0, 0, 0.08);\n max-width: 800px;\n max-height: 90vh;\n width: calc(100% - 32px);\n overflow-x: hidden;\n overflow-y: auto;\n opacity: 0;\n transform: translate(-50%, -50%) scale(0.96);\n transition: opacity 250ms cubic-bezier(0.4, 0, 0.2, 1), transform 250ms cubic-bezier(0.4, 0, 0.2, 1);\n font-family: var(--encore-font-family);\n color: var(--encore-text-color);\n /* Anchor the inherited typographic properties at the modal root so host\n page styles can't cascade in. Children inherit OUR values, not the\n host's. Interim guard until Shadow DOM — see web-sdk-style-isolation.md. */\n font-size: 16px;\n font-weight: 400;\n font-style: normal;\n line-height: 1.5;\n letter-spacing: normal;\n word-spacing: normal;\n text-align: left;\n text-transform: none;\n text-indent: 0;\n white-space: normal;\n text-shadow: none;\n }\n\n .${R}modal.${R}visible {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n\n /* Close Button */\n .${R}close-button {\n position: absolute;\n top: 20px;\n right: 20px;\n width: 40px;\n height: 40px;\n border: none;\n background-color: var(--encore-surface-color);\n cursor: pointer;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--encore-text-muted);\n transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n z-index: 10;\n }\n\n .${R}close-button:hover {\n background-color: var(--encore-bg-color);\n color: var(--encore-text-color);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n transform: scale(1.05);\n }\n\n .${R}close-button:active {\n transform: scale(0.98);\n }\n\n .${R}close-button:hover {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n .${R}close-button svg {\n width: 18px;\n height: 18px;\n }\n\n /* Body scroll lock */\n body.${R}no-scroll {\n overflow: hidden;\n }\n\n /* Modal Header */\n .${R}modal-header {\n padding: 24px 24px 16px;\n border-bottom: 1px solid var(--encore-border-color);\n }\n\n .${R}modal-title {\n margin: 0;\n font-size: 24px;\n font-weight: 700;\n line-height: 1.25;\n color: var(--encore-text-color);\n }\n\n .${R}modal-subtitle {\n margin: 8px 0 0;\n font-size: 14px;\n color: var(--encore-text-muted);\n line-height: 1.5;\n }\n\n /* Modal Body */\n .${R}modal-body {\n padding: 0;\n position: relative;\n isolation: isolate;\n }\n\n /* Carousel Container */\n .${R}carousel {\n display: flex;\n flex-direction: column;\n overflow: visible;\n }\n\n /* Carousel Header */\n .${R}carousel-header {\n padding: 40px 68px 28px;\n text-align: center;\n }\n\n .${R}carousel-title {\n font-size: 32px;\n font-weight: 510;\n line-height: 1.1;\n color: var(--encore-text-color);\n margin: 0 0 8px 0;\n letter-spacing: -0.3px;\n }\n\n .${R}carousel-subtitle {\n font-size: 16px;\n line-height: 1.2;\n color: var(--encore-text-color);\n margin: 0;\n font-weight: 400;\n }\n\n /* Carousel Cards Container - Horizontal Scroll */\n .${R}carousel-cards {\n display: flex;\n flex-direction: row;\n overflow-x: auto;\n overflow-y: hidden;\n scroll-behavior: smooth;\n scroll-snap-type: x mandatory;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n -ms-overflow-style: none;\n gap: 24px;\n padding: 24px 78px;\n position: relative;\n }\n\n .${R}carousel-cards::-webkit-scrollbar {\n display: none;\n }\n\n /* Carousel Item Wrapper. Only opacity ever animates (active-card dimming) — a\n short 200ms so the highlight reads as tracking the swipe, not trailing it. */\n .${R}carousel-item {\n flex: 0 0 426px;\n width: 426px;\n scroll-snap-align: center;\n scroll-snap-stop: always;\n opacity: 0.5;\n transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: auto;\n }\n\n /* Active carousel item */\n .${R}carousel-item.${R}active {\n opacity: 1;\n z-index: 1;\n }\n\n /* Offer Card */\n .${R}offer-card {\n display: flex;\n flex-direction: column;\n background: var(--encore-bg-color);\n border-radius: 16px;\n box-shadow: 0 2px 12px rgba(116, 112, 133, 0.16);\n overflow: hidden;\n }\n\n /* Offer Banner */\n .${R}offer-banner {\n width: 100%;\n background-color: transparent;\n position: relative;\n padding: 0;\n height: 180px;\n overflow: hidden;\n border-radius: 16px 16px 0 0;\n }\n\n .${R}offer-banner-image {\n width: 100%;\n height: 100%;\n display: block;\n object-fit: cover;\n background-color: var(--encore-surface-color);\n opacity: 0;\n transition: opacity 200ms ease-out;\n }\n\n .${R}offer-banner-image.${R}loaded {\n opacity: 1;\n }\n\n /* Info button on banner */\n .${R}banner-info-button {\n position: absolute;\n top: 20px;\n right: 20px;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background-color: var(--encore-surface-color);\n backdrop-filter: blur(6px);\n border: none;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 150ms ease;\n z-index: 3;\n padding: 0;\n }\n\n .${R}banner-info-button:hover {\n background-color: var(--encore-border-color);\n transform: scale(1.05);\n }\n\n .${R}banner-info-button:focus {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n .${R}banner-info-button svg {\n width: 14px;\n height: 14px;\n color: var(--encore-text-color);\n stroke-width: 2;\n }\n\n /* Tooltip */\n .${R}tooltip {\n position: fixed;\n background-color: rgba(28, 28, 32, 0.95);\n color: #ffffff;\n padding: 8px 12px;\n border-radius: 8px;\n font-size: 12px;\n line-height: 1.4;\n font-weight: 500;\n z-index: 10000;\n pointer-events: none;\n opacity: 0;\n transform: translateY(4px);\n transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1), transform 200ms cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n white-space: normal;\n word-wrap: break-word;\n }\n\n .${R}tooltip-visible {\n opacity: 1;\n transform: translateY(0);\n }\n\n /* Tooltip arrow (optional - using pseudo-element) */\n .${R}tooltip::after {\n content: '';\n position: absolute;\n width: 0;\n height: 0;\n border-style: solid;\n }\n\n .${R}tooltip-top::after {\n bottom: -4px;\n left: 50%;\n transform: translateX(-50%);\n border-width: 4px 4px 0 4px;\n border-color: rgba(28, 28, 32, 0.95) transparent transparent transparent;\n }\n\n .${R}tooltip-bottom::after {\n top: -4px;\n left: 50%;\n transform: translateX(-50%);\n border-width: 0 4px 4px 4px;\n border-color: transparent transparent rgba(28, 28, 32, 0.95) transparent;\n }\n\n .${R}tooltip-left::after {\n right: -4px;\n top: 50%;\n transform: translateY(-50%);\n border-width: 4px 0 4px 4px;\n border-color: transparent transparent transparent rgba(28, 28, 32, 0.95);\n }\n\n .${R}tooltip-right::after {\n left: -4px;\n top: 50%;\n transform: translateY(-50%);\n border-width: 4px 4px 4px 0;\n border-color: transparent rgba(28, 28, 32, 0.95) transparent transparent;\n }\n\n /* Offer Footer */\n .${R}offer-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-top: 1px solid var(--encore-border-color);\n gap: 12px;\n min-height: 74px;\n }\n\n /* Offer Merchant */\n .${R}offer-merchant {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n }\n\n .${R}offer-logo {\n width: 42px;\n height: 42px;\n border-radius: 8px;\n object-fit: contain;\n background-color: var(--encore-surface-color);\n flex-shrink: 0;\n opacity: 0;\n transition: opacity 200ms ease-out;\n }\n\n .${R}offer-logo.${R}loaded {\n opacity: 1;\n }\n\n .${R}offer-merchant-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .${R}offer-merchant-name {\n font-size: 16px;\n font-weight: 590;\n color: var(--encore-text-color);\n line-height: 1.1;\n letter-spacing: -1px;\n }\n\n .${R}offer-merchant-desc {\n font-size: 14px;\n color: var(--encore-text-muted);\n line-height: 1.2;\n letter-spacing: -1px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* Claim Button */\n .${R}button-claim {\n padding: 10px 16px;\n height: 34px;\n min-width: 78px;\n font-size: 14px;\n font-weight: 590;\n border-radius: 999px;\n border: none;\n background: var(--encore-primary-color);\n color: white;\n cursor: pointer;\n transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);\n font-family: var(--encore-font-family);\n white-space: nowrap;\n line-height: 1.2;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .${R}button-claim:hover:not(:disabled) {\n background: color-mix(in srgb, var(--encore-primary-color) 85%, black);\n transform: translateY(-1px);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--encore-primary-color) 30%, transparent);\n }\n\n .${R}button-claim:active {\n transform: translateY(0);\n background: color-mix(in srgb, var(--encore-primary-color) 75%, black);\n }\n\n .${R}button-claim:focus {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n .${R}button-claim:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n }\n\n /* Carousel Navigation */\n .${R}carousel-navigation {\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 40px;\n padding: 28px 32px 32px;\n }\n\n .${R}nav-button {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: 1px solid var(--encore-text-muted);\n background-color: var(--encore-bg-color);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--encore-text-muted);\n transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0;\n }\n\n .${R}nav-button:hover:not(:disabled) {\n border-color: var(--encore-primary-color);\n color: var(--encore-primary-color);\n transform: scale(1.05);\n }\n\n .${R}nav-button:active:not(:disabled) {\n transform: scale(0.95);\n }\n\n .${R}nav-button:focus {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n .${R}nav-button svg {\n width: 20px;\n height: 20px;\n stroke-width: 2;\n }\n\n .${R}nav-button:not(:disabled) {\n border-color: var(--encore-primary-color);\n color: var(--encore-primary-color);\n }\n\n .${R}nav-button:disabled,\n .${R}nav-button.${R}disabled {\n opacity: 1;\n cursor: not-allowed;\n border-color: var(--encore-text-muted);\n color: var(--encore-text-muted);\n }\n\n .${R}nav-button:disabled:hover,\n .${R}nav-button.${R}disabled:hover {\n border-color: var(--encore-text-muted);\n color: var(--encore-text-muted);\n transform: none;\n }\n\n /* Loading State */\n .${R}loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 48px;\n }\n\n .${R}spinner {\n width: 40px;\n height: 40px;\n border: 3px solid var(--encore-border-color);\n border-top-color: var(--encore-primary-color);\n border-radius: 50%;\n animation: ${R}spin 0.8s linear infinite;\n }\n\n @keyframes ${R}spin {\n to { transform: rotate(360deg); }\n }\n\n /* Responsive: Mobile (0-767px) — full-screen vertical sheet */\n @media (max-width: ${U}) {\n /* Modal becomes a full-screen sheet. dvh fallback so mobile browser URL\n bars don't clip the bottom. It is a flex column so the height\n propagates down to the scrollable cards region. */\n .${R}modal {\n display: flex;\n flex-direction: column;\n inset: 0;\n top: 0;\n left: 0;\n transform: none;\n width: 100%;\n height: 100vh;\n height: 100dvh;\n max-width: none;\n max-height: none;\n border-radius: 0;\n margin: 0;\n overflow: hidden;\n }\n\n /* Fade only — override the desktop scale/translate transition. */\n .${R}modal.${R}visible {\n transform: none;\n }\n\n /* Body fills the sheet and forwards the height to the carousel.\n min-height:0 lets the inner scroll container actually overflow. */\n .${R}modal-body {\n display: flex;\n flex: 1 1 auto;\n min-height: 0;\n flex-direction: column;\n }\n\n /* Carousel fills the body so the cards region can flex to fill height. */\n .${R}carousel {\n flex: 1 1 auto;\n min-height: 0;\n overflow: hidden;\n }\n\n /* Sticky header pinned while the list scrolls; respect the notch. 64px side\n The close button is an absolute overlay (top 16 + 40px tall), so the\n header reserves that band VERTICALLY: the title starts below the X and\n spans the full sheet width with slim symmetric side padding — no dead\n 64px gutters squeezing the headline into the X's row. */\n .${R}carousel-header {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--encore-bg-color);\n padding: calc(64px + env(safe-area-inset-top)) 24px 20px;\n }\n\n .${R}carousel-title {\n font-size: 22px;\n }\n\n .${R}carousel-subtitle {\n font-size: 14px;\n }\n\n /* Vertical, full-height scrollable feed. */\n .${R}carousel-cards {\n flex: 1 1 auto;\n min-height: 0;\n flex-direction: column;\n overflow-x: hidden;\n overflow-y: auto;\n scroll-snap-type: none;\n overscroll-behavior: contain;\n gap: 16px;\n padding: 16px 16px calc(16px + env(safe-area-inset-bottom));\n }\n\n /* Full-width stacked cards, no carousel dimming or snapping.\n overflow:visible so each card's shadow isn't clipped by the item box. */\n .${R}carousel-item {\n flex: 0 0 auto;\n width: 100%;\n opacity: 1;\n scroll-snap-align: none;\n overflow: visible;\n }\n\n /* Keep the rounded card + visible shadow on mobile (matches design). */\n .${R}offer-card {\n width: 100%;\n max-width: 100%;\n border-radius: 16px;\n box-shadow: 0 4px 16px rgba(116, 112, 133, 0.22);\n border: 1px solid var(--encore-border-color);\n }\n\n /* Image fills the card edge-to-edge with rounded top corners\n (no inset padding, banner already clips to the card radius). */\n .${R}offer-banner {\n height: auto;\n padding: 0;\n border-radius: 16px 16px 0 0;\n }\n\n .${R}offer-banner-image {\n width: 100%;\n height: auto;\n display: block;\n }\n\n /* Logo + name + description on the left, small claim button pinned far\n right on a single row (no wrap / no full-width stacking). */\n .${R}offer-footer {\n flex-wrap: nowrap;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 14px 16px;\n }\n\n .${R}offer-merchant {\n flex: 1 1 auto;\n min-width: 0;\n }\n\n /* Small button hugging its label, to the far right. */\n .${R}button-claim {\n flex: 0 0 auto;\n width: auto;\n }\n\n /* No nav arrows in vertical mode. */\n .${R}carousel-navigation {\n display: none;\n }\n\n /* Keep the close button reachable inside the sticky header / below the notch. */\n .${R}close-button {\n top: calc(16px + env(safe-area-inset-top));\n right: 16px;\n }\n\n /* Paged carousel keeps its nav bubbles on mobile (the display:none above is\n the responsive/'list' vertical mode only). */\n .${R}carousel-paged .${R}carousel-navigation {\n display: flex;\n }\n\n /* NOTE: the thankYou layout's own mobile overrides live in a dedicated media\n block AFTER the base thankYou section (source order matters at equal\n specificity — a media query adds none). */\n }\n\n /* Responsive: Tablet (768-1023px) */\n @media (min-width: 768px) and (max-width: 1023px) {\n .${R}modal {\n max-width: 600px;\n }\n }\n\n /* Responsive: Desktop (1024px+) */\n @media (min-width: 1024px) {\n .${R}modal {\n max-width: 600px;\n }\n }\n\n /* Accessibility: High Contrast Mode */\n @media (prefers-contrast: high) {\n .${R}button {\n border: 2px solid currentColor;\n }\n }\n\n /* Accessibility: Reduced Motion */\n @media (prefers-reduced-motion: reduce) {\n .${R}backdrop,\n .${R}modal,\n .${R}button,\n .${R}close-button {\n transition: none;\n }\n\n .${R}spinner {\n animation: none;\n }\n }\n\n /* Focus Visible (for keyboard navigation) */\n .${R}button:focus-visible,\n .${R}close-button:focus-visible {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n\n /* ===== Paged carousel (thankYou layout) ===== */\n /* Always-horizontal, one-card-per-view, scroll-snap paged — at every breakpoint.\n Scoped under carousel-paged so the responsive ('list') carousel rules are\n untouched. Each page is slightly narrower than the viewport so the neighboring\n cards PEEK in at the edges at reduced opacity — the same visual grammar as the\n list carousel: "there are more offers, swipe". Desktop caps the page width so\n the peek survives wide modals (no 426px hardcode). */\n .${R}carousel-paged .${R}carousel-cards {\n flex-direction: row;\n align-items: center;\n overflow-x: auto;\n overflow-y: hidden;\n scroll-snap-type: x mandatory;\n /* auto, NOT the base 'smooth': smooth scroll-behavior makes the browser ANIMATE\n its own snap re-alignments (e.g. when an async banner image loads and reflows\n the row mid-gesture), which reads as a laggy post-swipe snap. Native gesture\n snapping is untouched, and programmatic paging still requests smooth\n explicitly via scrollIntoView's behavior option. */\n scroll-behavior: auto;\n gap: 12px;\n /* Horizontal padding is 0 — the symmetric first/last-item gutters below own\n the side spacing so the ACTIVE card is always centered. Vertical padding is\n shadow room: overflow-y:hidden would clip the card shadows without it. */\n padding: ${M} 0;\n /* One "page" = the card slot. 64px total side allowance → 32px gutter each\n side (a ~20px neighbor-peek after the 12px gap at the 375px design width);\n the 480px cap keeps the peek alive in wide modals. Declared once here and\n shared by the item basis and the centering gutters so they can never drift. */\n --encore-page-size: min(100% - 64px, 480px);\n }\n\n .${R}carousel-paged .${R}carousel-item {\n /* Size via width (flex-basis:auto → width), NOT a math function inside the\n flex shorthand: engines that choke on functions in the shorthand drop the\n whole declaration and fall back to the base 426px item — which is exactly a\n clipped, overflowing card on a 375px phone. width + the max-width guard\n make overflow impossible at any breakpoint. */\n flex: 0 0 auto;\n width: var(--encore-page-size);\n max-width: 100%;\n /* Peek legibility: light creatives on the white sheet disappear below ~0.5,\n killing the "more offers" affordance. 0.65 keeps the active card clearly\n dominant while the neighbors stay readable. */\n opacity: 0.65;\n scroll-snap-align: center;\n scroll-snap-stop: always;\n overflow: visible;\n }\n\n /* Symmetric centering gutters: first/last pages get margins of exactly\n (container − page) / 2, so card 0 RESTS CENTERED at scrollLeft 0 (empty gutter\n left, peek right), mid-cards show symmetric peeks, and the last card can reach\n center too. Both the margin percentage and the flex-basis percentage resolve against the same\n container content width, so the math is exact at any size — the natural resting\n position IS the snap position and mount needs no scroll correction. */\n .${R}carousel-paged .${R}carousel-item:first-child {\n margin-inline-start: calc((100% - var(--encore-page-size)) / 2);\n }\n\n .${R}carousel-paged .${R}carousel-item:last-child {\n margin-inline-end: calc((100% - var(--encore-page-size)) / 2);\n }\n\n .${R}carousel-paged .${R}carousel-item.${R}active {\n opacity: 1;\n }\n\n .${R}carousel-paged .${R}offer-card {\n max-width: 460px;\n margin: 0 auto;\n border: 1px solid var(--encore-border-color);\n box-shadow: 0 4px 16px rgba(116, 112, 133, 0.18);\n }\n\n /* Fluid banner: natural aspect, scales with the card. */\n .${R}carousel-paged .${R}offer-banner {\n height: auto;\n }\n\n .${R}carousel-paged .${R}offer-banner-image {\n width: 100%;\n height: auto;\n display: block;\n }\n\n /* Nav row: centered DIRECTLY beneath the cards — these chevrons are carousel\n controls and must read as part of the carousel unit, so they hug it (never\n bottom-anchored away from it). Comfortable 48px circular tap targets in normal\n flow — no overlay, no z-index games. Rhythm: cards→nav gap = the cards' bottom\n shadow gutter + 4px here (16px total inside thankYou); the 20px bottom padding\n is the nav→footer gap (and the sheet's bottom inset when no trust footer is\n configured). */\n .${R}carousel-paged .${R}carousel-navigation {\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 24px;\n padding: 4px 20px ${M};\n }\n\n .${R}carousel-paged .${R}nav-button {\n width: 48px;\n height: 48px;\n background-color: var(--encore-bg-color);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n /* Neutral idle bubble (the accent belongs to the Claim CTA); accent only on hover. */\n .${R}carousel-paged .${R}nav-button:not(:disabled) {\n border-color: var(--encore-border-color);\n color: var(--encore-text-color);\n }\n\n .${R}carousel-paged .${R}nav-button:hover:not(:disabled) {\n border-color: var(--encore-primary-color);\n color: var(--encore-primary-color);\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);\n }\n\n .${R}carousel-paged .${R}nav-button svg {\n width: 22px;\n height: 22px;\n }\n\n .${R}carousel-paged .${R}nav-button:disabled,\n .${R}carousel-paged .${R}nav-button.${R}disabled {\n opacity: 0.35;\n box-shadow: none;\n border-color: var(--encore-border-color);\n color: var(--encore-text-muted);\n }\n\n /* ===== Thank-You layout (placement layout: 'thankYou') ===== */\n /* Pure chrome around the unchanged carousel: status block above, trust footer\n below. Desktop: normal flow inside the content-sized modal (the stack is\n inherently tight there). Mobile: joins the sheet's flex chain — the content\n stack is optically centered while the trust footer stays bottom-pinned. */\n .${R}thankyou {\n display: flex;\n flex-direction: column;\n }\n\n /* SUCCESS UNIT — check → title → receipt as ONE tight visual group. Internal\n gaps are deliberately tight (proximity = grouping); the heading below is the\n sheet's single focal point. No bottom padding: the 32px section break lives\n on the intro header so it is owned in exactly one place. */\n .${R}thankyou-status {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n padding: ${D} 24px 0;\n }\n\n /* "Receipt stamp" check: deep-green disc in a soft halo. Calm by design — no\n pulse, no gradient (a payment fact, stated quietly). Margin-bottom is the\n 12px halo ring + the 12px check→title step of the ladder. */\n .${R}thankyou-check {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 50%;\n background: var(--encore-success-color);\n color: #ffffff;\n box-shadow: 0 0 0 12px color-mix(in srgb, var(--encore-success-color) 14%, var(--encore-bg-color));\n margin: 0 0 calc(12px + ${O});\n }\n\n .${R}thankyou-check svg {\n width: 24px;\n height: 24px;\n }\n\n /* The ONLY large/heavy type on the sheet — everything else defers to it. */\n .${R}thankyou-title {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n line-height: 1.25;\n letter-spacing: -0.2px;\n color: var(--encore-text-color);\n overflow-wrap: anywhere;\n }\n\n /* Receipt line — sits tight under the title (same statement, quieter voice). */\n .${R}thankyou-subtitle {\n margin: 4px 0 0;\n font-size: 14px;\n line-height: 1.5;\n color: var(--encore-text-muted);\n overflow-wrap: anywhere;\n }\n\n /* OFFER CONTEXT — inside thankYou the carousel's own header is a lead-in, not a\n second headline: medium weight, headline-free scale. The title/subtitle pair\n reads as ONE two-line paragraph: identical size, weight, color and line-height\n on both lines, zero margin between them, so the shared 1.35 leading IS the\n inter-line gap — no size, weight or color tier splits them apart. (They stay\n two DOM nodes: the subtitle swaps per-card via textContent.) The pair sits 32px\n BELOW the success unit but only 16px ABOVE the cards (via the cards' top\n padding), so proximity ties it to the offers it introduces. Scoped here so the\n 'list' layout CSS stays byte-identical. position:static defeats the mobile\n sticky-header rule (the status block above already handles the notch). */\n .${R}thankyou .${R}carousel-header {\n position: static;\n padding: 32px 24px 0;\n }\n\n .${R}thankyou .${R}carousel-title {\n margin: 0;\n font-size: 14px;\n font-weight: 500;\n line-height: 1.35;\n letter-spacing: 0;\n color: var(--encore-text-secondary);\n }\n\n .${R}thankyou .${R}carousel-subtitle {\n font-size: 14px;\n font-weight: 500;\n line-height: 1.35;\n color: var(--encore-text-secondary);\n }\n\n /* Context → cards 16px, cards → nav 16px (12px shadow gutter + the nav row's 4px\n top padding): the carousel unit is bound tight to its lead-in above and its\n controls below. The 12px bottom gutter still clears the visible card shadow. */\n .${R}thankyou .${R}carousel-cards {\n padding-top: 16px;\n padding-bottom: ${O};\n }\n\n /* Trust footer — a quiet 12px caption. Its 20px top gap is the nav row's bottom\n padding; 24px closes the sheet. */\n .${R}thankyou-footer {\n margin: 0;\n padding: 0 24px 24px;\n font-size: 12px;\n line-height: 1.5;\n text-align: center;\n color: var(--encore-text-muted);\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n /* Thank-You on the mobile full-screen sheet: join the sheet's flex chain — the\n content stack floats optically centered above the bottom-pinned trust footer,\n and the carousel keeps its internal scroll. Type + rhythm come from the base\n thankYou system above (375px IS its design target) — only sheet-specific bits\n are overridden. This block must stay AFTER the base thankYou rules: it wins by\n source order, not specificity. */\n @media (max-width: ${U}) {\n .${R}thankyou {\n flex: 1 1 auto;\n min-height: 0;\n }\n\n /* Full-height composition: everything except the trust footer is ONE tight,\n internally-rigid stack (success unit → context pair → cards → nav row),\n OPTICALLY CENTERED in the space above the bottom-pinned footer. Two\n zero-basis flex spaces own ALL the slack — this ::before above the stack\n (grow 1) and the carousel's ::after below it (grow 1.35, via the carousel's\n own grow factor) — an above:below ratio of 1:1.35 so the stack rests\n slightly high of true center. The stack's internal ladder gaps never\n stretch, so the composition compresses as a unit on short sheets and\n floats as a unit on tall ones. */\n .${R}thankyou::before {\n content: '';\n flex: 1 1 0;\n min-height: 0;\n }\n\n .${R}thankyou-status {\n flex: 0 0 auto;\n /* Real fixed margin above the check — the floor when the flex space above\n has fully compressed; the notch inset always survives. */\n padding-top: calc(${D} + env(safe-area-inset-top));\n }\n\n .${R}thankyou-check {\n width: 44px;\n height: 44px;\n }\n\n /* The carousel's grow factor IS the below-stack share: its extra height is\n absorbed entirely by the ::after spacer under the nav row, giving the\n 1:1.35 above:below split against the ::before above. Shrinks like before\n (basis auto, min-height 0) so short sheets compress the cards region. */\n .${R}thankyou .${R}carousel {\n flex: 1.35 1 auto;\n min-height: 0;\n }\n\n .${R}thankyou .${R}carousel::after {\n content: '';\n flex: 1 1 0;\n min-height: 0;\n }\n\n /* Nav hugs the cards (rigid ladder gap) — the old auto-margin bottom-anchor\n is gone: the arrows are carousel controls and read as part of the carousel\n unit, not footer furniture. */\n .${R}thankyou .${R}carousel-cards {\n flex: 0 1 auto;\n min-height: 0;\n }\n\n /* Pinned bar: tighter 12px insets than the in-flow desktop footer (the border\n already separates it), plus the home-indicator safe area. */\n .${R}thankyou-footer {\n flex: 0 0 auto;\n border-top: 1px solid var(--encore-border-color);\n background: var(--encore-bg-color);\n padding: 12px 20px calc(12px + env(safe-area-inset-bottom));\n }\n }\n\n /* ===== Success Screen ===== */\n .${R}success-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 3rem 2rem;\n text-align: center;\n min-height: 400px;\n animation: ${R}fade-in 0.3s ease-out;\n }\n\n .${R}success-icon {\n width: 80px;\n height: 80px;\n background: var(--encore-primary-color);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 48px;\n color: white;\n margin-bottom: 1.5rem;\n animation: ${R}scale-in 0.5s ease-out;\n }\n\n .${R}success-title {\n font-size: 1.75rem;\n font-weight: 700;\n color: var(--encore-text-color);\n margin: 0 0 0.75rem 0;\n }\n\n .${R}success-subtitle {\n font-size: 1.125rem;\n color: var(--encore-text-muted);\n margin: 0 0 2rem 0;\n line-height: 1.6;\n }\n\n .${R}success-details {\n background: color-mix(in srgb, var(--encore-primary-color) 5%, transparent);\n border-radius: 12px;\n padding: 1.5rem;\n margin-bottom: 2rem;\n min-width: 280px;\n }\n\n .${R}success-detail {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.5rem 0;\n }\n\n .${R}success-detail:not(:last-child) {\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n }\n\n .${R}success-detail-label {\n font-size: 0.875rem;\n color: var(--encore-text-muted);\n font-weight: 500;\n }\n\n .${R}success-detail-value {\n font-size: 1rem;\n color: var(--encore-text-color);\n font-weight: 600;\n }\n\n .${R}success-button {\n background: var(--encore-primary-color);\n color: white;\n border: none;\n border-radius: var(--encore-border-radius);\n padding: 0.875rem 2.5rem;\n font-size: 1rem;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);\n }\n\n .${R}success-button:hover {\n transform: translateY(-2px);\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);\n }\n\n .${R}success-button:active {\n transform: translateY(0);\n }\n\n .${R}success-button:focus-visible {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n /* Redemption screen (phase 2 — redeem) */\n .${R}redemption-container {\n display: flex;\n flex-direction: column;\n max-width: 440px;\n margin: 0 auto;\n padding: 2rem 1.75rem;\n text-align: center;\n animation: ${R}fade-in 0.3s ease-out;\n }\n\n .${R}redemption-title {\n font-size: 1.6rem;\n font-weight: 700;\n color: var(--encore-text-color);\n margin: 0 0 0.5rem 0;\n line-height: 1.2;\n }\n\n .${R}redemption-subtitle {\n font-size: 1.05rem;\n color: var(--encore-text-muted);\n margin: 0 0 1.75rem 0;\n line-height: 1.5;\n }\n\n .${R}redemption-note {\n font-size: 0.8rem;\n color: var(--encore-text-muted);\n line-height: 1.5;\n margin: 1.25rem 0;\n }\n\n /* Reuses .encore-button-claim (the clean pill) — full-width + prominent. */\n .${R}redemption-cta {\n width: 100%;\n height: auto;\n min-height: 50px;\n padding: 14px 24px;\n font-size: 1rem;\n font-weight: 600;\n }\n\n /* ==========================================================================\n Deferred-flow brand components: dual lockup + deal ledger + primer screen\n ========================================================================== */\n\n /* Shared monogram tile (first paint for every brand logo). */\n .${R}brand-tile-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--encore-primary-color) 12%, var(--encore-bg-color));\n color: var(--encore-primary-color);\n font-weight: 700;\n font-size: 1.2rem;\n }\n\n /* Dual-brand lockup — two tiles settling together ("two brands, one deal"). */\n .${R}lockup {\n display: flex;\n justify-content: center;\n align-items: center;\n margin: 0.5rem 0 1.5rem 0;\n }\n\n .${R}lockup-tile {\n width: 56px;\n height: 56px;\n border-radius: 14px;\n object-fit: cover;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--encore-text-color) 14%, transparent);\n background: var(--encore-bg-color);\n }\n\n .${R}lockup-tile-partner {\n transform: rotate(-4deg);\n animation: ${R}lockup-settle-left 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;\n z-index: 1;\n }\n\n .${R}lockup-tile-host {\n margin-left: -10px;\n transform: rotate(4deg);\n animation: ${R}lockup-settle-right 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;\n z-index: 2;\n }\n\n @keyframes ${R}lockup-settle-left {\n from { transform: rotate(-6deg) translateX(-4px); }\n to { transform: rotate(-4deg) translateX(0); }\n }\n\n @keyframes ${R}lockup-settle-right {\n from { transform: rotate(6deg) translateX(4px); }\n to { transform: rotate(4deg) translateX(0); }\n }\n\n /* Deal ledger — the claim state machine, rendered. */\n .${R}ledger {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--encore-border-color);\n border-radius: 16px;\n background: var(--encore-bg-color);\n box-shadow: 0 1px 3px color-mix(in srgb, var(--encore-text-color) 6%, transparent);\n text-align: left;\n overflow: hidden;\n }\n\n .${R}ledger-row {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n }\n\n .${R}ledger-row + .${R}ledger-row {\n border-top: 1px solid var(--encore-border-color);\n }\n\n .${R}ledger-logo {\n width: 44px;\n height: 44px;\n border-radius: 11px;\n object-fit: cover;\n flex-shrink: 0;\n display: block;\n }\n\n .${R}ledger-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1 1 auto;\n min-width: 0;\n }\n\n .${R}ledger-name {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--encore-text-color);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .${R}ledger-detail {\n font-size: 0.8rem;\n color: var(--encore-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .${R}ledger-status {\n font-size: 0.85rem;\n font-weight: 600;\n flex-shrink: 0;\n }\n\n .${R}ledger-status-active {\n color: var(--encore-success-color);\n }\n\n .${R}ledger-status-pending {\n color: var(--encore-text-muted);\n }\n\n /* Primer screen (S1.5) — centered column, one forward CTA. */\n .${R}primer-container {\n display: flex;\n flex-direction: column;\n justify-content: center;\n max-width: 440px;\n margin: 0 auto;\n padding: 2.5rem 1.75rem 2rem;\n text-align: center;\n animation: ${R}fade-in 0.3s ease-out;\n }\n\n .${R}primer-title {\n font-size: 1.6rem;\n font-weight: 700;\n color: var(--encore-text-color);\n margin: 0 0 0.5rem 0;\n line-height: 1.2;\n }\n\n .${R}primer-subtitle {\n font-size: 1.05rem;\n color: var(--encore-text-muted);\n margin: 0 0 2rem 0;\n line-height: 1.5;\n }\n\n /* Reuses .encore-button-claim (the clean pill) — full-width + prominent. */\n .${R}primer-cta {\n width: 100%;\n height: auto;\n min-height: 50px;\n padding: 14px 24px;\n font-size: 1rem;\n font-weight: 600;\n }\n\n /* ==========================================================================\n Mobile sheet composition for the primer + redemption screens.\n MUST live AFTER the component styles above: these override base paddings/\n margins, and the cascade is source-order at equal specificity.\n ========================================================================== */\n @media (max-width: ${U}) {\n /* Both screens fill the full-height sheet (modal-body is a flex column).\n Top padding clears the close button's overlay band; bottom padding the\n home indicator. */\n .${R}primer-container,\n .${R}redemption-container {\n flex: 1 1 auto;\n max-width: none;\n width: 100%;\n box-sizing: border-box;\n padding: calc(64px + env(safe-area-inset-top)) 24px\n calc(24px + env(safe-area-inset-bottom));\n }\n\n /* Primer: the content block floats centered between two flexible spaces\n (auto margins above the lockup and above the CTA), CTA on the bottom edge. */\n .${R}primer-container .${R}lockup {\n margin-top: auto;\n }\n\n .${R}primer-cta {\n margin-top: auto;\n }\n\n /* Redemption: the content block (variant region + note, note directly\n below the cards) floats centered between two flexible spaces; the CTA\n sits alone on the bottom edge. */\n .${R}redemption-container > :first-child {\n margin-top: auto;\n }\n\n .${R}redemption-cta {\n margin-top: auto;\n }\n }\n\n @keyframes ${R}scale-in {\n from {\n transform: scale(0);\n opacity: 0;\n }\n to {\n transform: scale(1);\n opacity: 1;\n }\n }\n `}(e),document.head.appendChild(t),r.debug("Encore styles injected successfully")}catch(e){r.error("Failed to inject Encore styles:",e)}}function _(e){return`${R}${e}`}class F{#be;#ve=null;#ye=null;#we=null;#xe=null;#Ie=!1;#ke=null;#Ce=[];#Ee=null;constructor(e={}){this.#be=new N,P(e)}show(){this.#Ie?r.warn("Modal is already visible"):(this.#ke=document.activeElement,this.#$e(),this.#ve&&document.body.appendChild(this.#ve),this.#ye&&document.body.appendChild(this.#ye),document.body.classList.add(_("no-scroll")),requestAnimationFrame(()=>{this.#ve?.classList.add(_("visible")),this.#ye?.classList.add(_("visible"))}),this.#Te(),this.#Se(),this.#Ae(),this.#Ie=!0,this.#be.emit("show",void 0),r.debug("Modal shown"))}hide(){this.#Ie&&(this.#ve?.classList.remove(_("visible")),this.#ye?.classList.remove(_("visible")),setTimeout(()=>{this.#Ne(),document.body.classList.remove(_("no-scroll")),this.#ke instanceof HTMLElement&&this.#ke.focus(),this.#Ie=!1,this.#be.emit("hide",void 0),r.debug("Modal hidden")},200))}isVisible(){return this.#Ie}setLabelledBy(e){this.#ye?.setAttribute("aria-labelledby",e)}setContent(e){this.#xe?(this.#xe.innerHTML="",this.#xe.appendChild(e),this.#Se()):r.error("Modal body not initialized")}on(e,t){return this.#be.on(e,t)}#$e(){this.#ve=document.createElement("div"),this.#ve.className=_("backdrop"),this.#ve.setAttribute("aria-hidden","true"),this.#ye=document.createElement("div"),this.#ye.className=_("modal"),this.#ye.setAttribute("role","dialog"),this.#ye.setAttribute("aria-modal","true"),this.#ye.setAttribute("aria-labelledby",_("modal-title")),this.#we=document.createElement("button"),this.#we.className=_("close-button"),this.#we.setAttribute("aria-label","Close modal"),this.#we.innerHTML='\n <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">\n <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />\n </svg>\n ',this.#xe=document.createElement("div"),this.#xe.className=_("modal-body"),this.#ye.appendChild(this.#we),this.#ye.appendChild(this.#xe)}#Ne(){this.#ve?.remove(),this.#ye?.remove(),this.#ve=null,this.#ye=null,this.#we=null,this.#xe=null}#Te(){this.#we?.addEventListener("click",()=>{this.#be.emit("close",void 0),this.hide()}),this.#ve?.addEventListener("click",()=>{this.#be.emit("backdropClick",void 0),this.hide()}),this.#Ee=this.#Le.bind(this),document.addEventListener("keydown",this.#Ee)}#Le(e){if(this.#Ie)switch(e.key){case"Escape":e.preventDefault(),this.#be.emit("close",void 0),this.hide();break;case"Tab":e.preventDefault(),this.#Re(e.shiftKey)}}#Re(e){if(0===this.#Ce.length)return;const t=this.#Ce.findIndex(e=>e===document.activeElement);let n;n=e?t<=0?this.#Ce.length-1:t-1:t>=this.#Ce.length-1?0:t+1,this.#Ce[n]?.focus()}#Se(){if(!this.#ye)return;const e=["button:not([disabled])","a[href]","input:not([disabled])","select:not([disabled])","textarea:not([disabled])",'[tabindex]:not([tabindex="-1"])'].join(", ");this.#Ce=Array.from(this.#ye.querySelectorAll(e))}#Ae(){this.#Ce.length>0&&this.#Ce[0]?.focus()}destroy(){this.#Ee&&document.removeEventListener("keydown",this.#Ee),this.#be.removeAllListeners(),this.hide()}}const z=new Set(["localhost","127.0.0.1","[::1]"]);function V(e){if(!e)return;let t;try{t=new URL(e)}catch{return}return"https:"===t.protocol||"http:"===t.protocol&&z.has(t.hostname)?e:void 0}class B{#Ue=null;#Oe;#Y;#Ie=!1;#Me=null;#De=null;#Pe=null;#_e=null;#Fe=null;constructor(e,t){this.#Oe=e,this.#Y={position:"top",maxWidth:200,...t},this.#ze()}#ze(){this.#Pe=this.#Ve.bind(this),this.#_e=this.#Be.bind(this),this.#Fe=this.#qe.bind(this),this.#Oe.addEventListener("mouseenter",this.#Pe),this.#Oe.addEventListener("mouseleave",this.#_e)}#Ve(){this.#De&&(clearTimeout(this.#De),this.#De=null),this.#Me=window.setTimeout(()=>{this.#Ke()},50)}#Be(){this.#Me&&(clearTimeout(this.#Me),this.#Me=null),this.#De=window.setTimeout(()=>{this.#He()},100)}#Ke(){if(this.#Ie)return;this.#Ue=document.createElement("div"),this.#Ue.className=_("tooltip"),this.#Ue.setAttribute("role","tooltip"),this.#Ue.textContent=this.#Y.content,this.#Y.maxWidth&&(this.#Ue.style.maxWidth=`${this.#Y.maxWidth}px`),this.#Y.position&&this.#Ue.classList.add(_(`tooltip-${this.#Y.position}`));const e=this.#Oe.getBoundingClientRect();this.#Ue.style.top=`${e.top}px`,this.#Ue.style.left=`${e.left}px`,document.body.appendChild(this.#Ue),this.#Fe&&(window.addEventListener("scroll",this.#Fe,!0),window.addEventListener("resize",this.#Fe)),requestAnimationFrame(()=>{requestAnimationFrame(()=>{this.#Ue&&(this.#qe(),this.#Ue.classList.add(_("tooltip-visible")))})}),this.#Ie=!0}#He(){this.#Ie&&this.#Ue&&(this.#Fe&&(window.removeEventListener("scroll",this.#Fe,!0),window.removeEventListener("resize",this.#Fe)),this.#Ue.classList.remove(_("tooltip-visible")),setTimeout(()=>{this.#Ue&&this.#Ue.parentNode&&this.#Ue.parentNode.removeChild(this.#Ue),this.#Ue=null},200),this.#Ie=!1)}#qe(){if(!this.#Ue||!this.#Ie)return;const e=this.#Oe.getBoundingClientRect(),t=this.#Ue.getBoundingClientRect();let n=0,r=0;switch(this.#Y.position??"top"){case"top":n=e.top-t.height-8,r=e.left+e.width/2-t.width/2;break;case"bottom":n=e.bottom+8,r=e.left+e.width/2-t.width/2;break;case"left":n=e.top+e.height/2-t.height/2,r=e.left-t.width-8;break;case"right":n=e.top+e.height/2-t.height/2,r=e.right+8}r=Math.max(8,Math.min(r,window.innerWidth-t.width-8)),n=Math.max(8,Math.min(n,window.innerHeight-t.height-8)),this.#Ue.style.top=`${n}px`,this.#Ue.style.left=`${r}px`}updateContent(e){this.#Y.content=e,this.#Ue&&(this.#Ue.textContent=e,this.#qe())}destroy(){this.#Me&&(clearTimeout(this.#Me),this.#Me=null),this.#De&&(clearTimeout(this.#De),this.#De=null),this.#Ie&&this.#He(),this.#Pe&&this.#Oe.removeEventListener("mouseenter",this.#Pe),this.#_e&&this.#Oe.removeEventListener("mouseleave",this.#_e),this.#Fe&&(window.removeEventListener("scroll",this.#Fe,!0),window.removeEventListener("resize",this.#Fe))}}class q{#be;#je=null;#Ye=null;#Ge=null;constructor(){this.#be=new N}render(e){this.#je=e,this.#Ye=document.createElement("div"),this.#Ye.className=_("offer-card");const t=this.#We(e.imageUrl,e.title,e.tooltipText);this.#Ye.appendChild(t);const n=this.#Qe(e);return this.#Ye.appendChild(n),this.#Ye}on(e,t){return this.#be.on(e,t)}#We(e,t,n){const i=document.createElement("div");i.className=_("offer-banner");const o=document.createElement("img");o.className=_("offer-banner-image"),o.alt=t??"Offer image";const s=V(e);s?(o.src=s,o.complete?o.classList.add(_("loaded")):o.onload=()=>{o.classList.add(_("loaded"))},o.onerror=()=>{r.warn("Failed to load offer image, using fallback"),o.src=this.#Xe(),o.classList.add(_("loaded"))}):(o.src=this.#Xe(),o.classList.add(_("loaded"))),i.appendChild(o);const a=document.createElement("button");return a.className=_("banner-info-button"),a.setAttribute("aria-label","More information"),a.innerHTML='\n <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">\n <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />\n </svg>\n ',a.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),r.debug("Info button clicked for offer",{title:t,tooltipText:n})}),n&&(this.#Ge=new B(a,{content:n,position:"bottom",maxWidth:200}),i.appendChild(a)),i}#Xe(){return`data:image/svg+xml;base64,${btoa('\n <svg xmlns="http://www.w3.org/2000/svg" width="600" height="240" viewBox="0 0 600 240">\n <rect width="600" height="240" fill="#E5E7EB"/>\n <text x="50%" y="50%" text-anchor="middle" dy=".3em" fill="#9CA3AF" font-family="sans-serif" font-size="18">\n Offer Image\n </text>\n </svg>\n ')}`}#Qe(e){const t=document.createElement("div");t.className=_("offer-footer");const n=document.createElement("div");n.className=_("offer-merchant");const i=V(e.logoUrl);if(i){const t=document.createElement("img");t.className=_("offer-logo"),t.src=i,t.alt=`${e.merchantName??"Merchant"} logo`,t.complete?t.classList.add(_("loaded")):t.onload=()=>{t.classList.add(_("loaded"))},t.onerror=()=>{r.warn("Failed to load merchant logo",{merchantName:e.merchantName}),t.style.display="none"},n.appendChild(t)}const o=document.createElement("div");if(o.className=_("offer-merchant-text"),e.merchantName){const t=document.createElement("div");t.className=_("offer-merchant-name"),t.textContent=e.merchantName,o.appendChild(t)}if(e.description){const t=document.createElement("div");t.className=_("offer-merchant-desc"),t.textContent=e.description,o.appendChild(t)}n.appendChild(o);const s=document.createElement("button");return s.className=`${_("button")} ${_("button-claim")}`,s.textContent=e.ctaText??"Claim",s.setAttribute("aria-label",`Claim offer: ${e.title??"offer"}`),s.addEventListener("click",()=>{this.#je&&this.#be.emit("claim",{offer:this.#je})}),t.appendChild(n),t.appendChild(s),t}destroy(){this.#Ge&&(this.#Ge.destroy(),this.#Ge=null),this.#be.removeAllListeners(),this.#Ye=null,this.#je=null}}class K{#be;#Ze;#Je=0;#Ye=null;#et=null;#tt=null;#nt=null;#rt=[];#it=null;#ot=null;#st=null;#at=null;#lt=null;#dt=0;#ct=null;#ht=-1;#ut;#pt=window.matchMedia(`(max-width: ${U})`);#mt=!1;#gt=null;#ft=null;#bt=-1;#vt=!1;constructor(e,t,n){if(this.#be=new N,this.#Ze=e,this.#ut=t,this.#vt="paged"===n?.mode,0===e.length)throw new Error("OfferCarousel requires at least one offer");this.#yt()}render(){this.#Ye=document.createElement("div"),this.#Ye.className=this.#vt?`${_("carousel")} ${_("carousel-paged")}`:_("carousel"),this.#it=document.createElement("div"),this.#it.setAttribute("role","status"),this.#it.setAttribute("aria-live","polite"),this.#it.setAttribute("aria-atomic","true"),this.#it.className="sr-only",this.#it.style.position="absolute",this.#it.style.width="1px",this.#it.style.height="1px",this.#it.style.padding="0",this.#it.style.margin="-1px",this.#it.style.overflow="hidden",this.#it.style.clip="rect(0, 0, 0, 0)",this.#it.style.whiteSpace="nowrap",this.#it.style.border="0";const e=this.#wt();if(this.#Ye.appendChild(e),this.#et=document.createElement("div"),this.#et.className=_("carousel-cards"),this.#Ye.appendChild(this.#it),this.#Ye.appendChild(this.#et),this.#Ze.length>1){const e=this.#xt();this.#Ye.appendChild(e)}return this.#It(),this.#mt=!this.#vt&&this.#pt.matches,this.#kt(),this.#vt||(this.#gt=this.#Ct.bind(this),this.#pt.addEventListener("change",this.#gt)),this.#Ye}#kt(){if(this.#Et(),this.#mt)this.#$t();else{this.#Tt(),this.#St(),this.#At(0,!1),this.#Nt(this.#Je),this.#Lt(this.#Je);const e=this.#Ze[this.#Je];e&&this.#ht!==this.#Je&&(this.#ht=this.#Je,this.#be.emit("offerView",{offer:e,index:this.#Je}))}}#Ct(){const e=this.#pt.matches;e!==this.#mt&&(this.#mt=e,this.#kt())}#Et(){this.#ot&&(document.removeEventListener("keydown",this.#ot),this.#ot=null),this.#et&&this.#st&&(this.#et.removeEventListener("scroll",this.#st),this.#st=null),this.#et&&this.#at&&(this.#et.removeEventListener("scrollend",this.#at),this.#at=null),null!==this.#lt&&(cancelAnimationFrame(this.#lt),this.#lt=null),this.#ct&&(clearTimeout(this.#ct),this.#ct=null),this.#ft&&(this.#ft.disconnect(),this.#ft=null)}#$t(){this.#et&&(this.#ft=new IntersectionObserver(e=>{let t=null;for(const n of e){if(!n.isIntersecting)continue;const e=n.target.getAttribute("data-index");if(null===e)continue;const r=Number(e);(!t||n.intersectionRatio>t.ratio)&&(t={index:r,ratio:n.intersectionRatio})}if(!t)return;const{index:n}=t;if(n===this.#bt)return;this.#bt=n,this.#Je=n;const i=this.#Ze[n];i&&(this.#be.emit("offerView",{offer:i,index:n}),r.debug(`Offer ${n+1} entered view`))},{root:this.#et,threshold:.5}),this.#et.querySelectorAll(`.${_("carousel-item")}`).forEach(e=>this.#ft?.observe(e)))}on(e,t){return this.#be.on(e,t)}getCurrentIndex(){return this.#Je}getTotalOffers(){return this.#Ze.length}next(){const e=Math.min(this.#Je+1,this.#Ze.length-1);e!==this.#Je&&(this.#Je=e,this.#Nt(e),this.#At(e),this.#Rt(`Showing offer ${e+1} of ${this.#Ze.length}`),r.debug(`Navigated to offer ${e+1}`))}previous(){const e=Math.max(this.#Je-1,0);e!==this.#Je&&(this.#Je=e,this.#Nt(e),this.#At(e),this.#Rt(`Showing offer ${e+1} of ${this.#Ze.length}`),r.debug(`Navigated to offer ${e+1}`))}#It(){if(!this.#et)return;const e=this.#et;this.#Ze.forEach((t,n)=>{const r=new q,i=r.render(t),o=document.createElement("div");o.className=_("carousel-item"),o.setAttribute("data-index",n.toString()),o.appendChild(i),r.on("claim",()=>{this.#be.emit("offerClaim",{offer:t,index:n})}),this.#rt.push(r),e.appendChild(o)}),requestAnimationFrame(()=>{e.classList.add(_("cards-ready"))})}#At(e,t=!0){if(!this.#et)return;const n=this.#et.querySelectorAll(`.${_("carousel-item")}`)[e];n&&n.scrollIntoView({behavior:t?"smooth":"auto",block:"nearest",inline:"center"})}#Nt(e){this.#Ut(),this.#Ot(e)}#Lt(e){this.#et&&(this.#dt=e,this.#et.querySelectorAll(`.${_("carousel-item")}`).forEach((t,n)=>{n===e?t.classList.add(_("active")):t.classList.remove(_("active"))}))}#wt(){const e=document.createElement("div");e.className=_("carousel-header");const t=document.createElement("h2");t.className=_("carousel-title"),t.id=_("modal-title"),t.textContent=this.#ut?.customHeadline??this.#ut?.titleText??"Unlock 10 free articles – choose an offer below";const n=document.createElement("p");return n.className=_("carousel-subtitle"),n.textContent=this.#Mt(this.#Je),this.#tt=n,e.appendChild(t),e.appendChild(n),e}#Mt(e){const t=this.#ut?.customSubheadline??this.#ut?.subtitleText??"";if(!this.#vt)return t;const n=this.#Ze[e]?.perk?.trim();return n||t}#Ot(e){if(!this.#vt||!this.#tt)return;const t=this.#Mt(e);this.#tt.textContent!==t&&(this.#tt.textContent=t)}#xt(){const e=document.createElement("div");e.className=_("carousel-navigation");const t=document.createElement("button");t.className=_("nav-button"),t.setAttribute("aria-label","Previous offer"),t.innerHTML=this.#vt?'\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round">\n <path d="M14.5 5.5L8 12l6.5 6.5" />\n </svg>\n ':'\n <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" style="transform: rotate(90deg);">\n <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />\n </svg>\n ',t.addEventListener("click",()=>this.previous());const n=document.createElement("button");return n.className=_("nav-button"),n.setAttribute("aria-label","Next offer"),n.innerHTML=this.#vt?'\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round">\n <path d="M9.5 5.5L16 12l-6.5 6.5" />\n </svg>\n ':'\n <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" style="transform: rotate(270deg);">\n <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />\n </svg>\n ',n.addEventListener("click",()=>this.next()),e.appendChild(t),e.appendChild(n),this.#nt=e,this.#Ut(t,n),e}#Ut(e,t){if(!this.#nt)return;const n=this.#nt.querySelectorAll(`.${_("nav-button")}`),r=e??n[0],i=t??n[1];r&&(r.disabled=0===this.#Je,r.classList.toggle(_("disabled"),0===this.#Je)),i&&(i.disabled=this.#Je===this.#Ze.length-1,i.classList.toggle(_("disabled"),this.#Je===this.#Ze.length-1))}#Tt(){this.#ot=this.#Dt.bind(this),document.addEventListener("keydown",this.#ot)}#Dt(e){if(this.#Ye)switch(e.key){case"ArrowLeft":e.preventDefault(),this.previous();break;case"ArrowRight":e.preventDefault(),this.next()}}#St(){if(!this.#et)return;const e=this.#et,t="onscrollend"in e;this.#st=()=>{null===this.#lt&&(this.#lt=window.requestAnimationFrame(()=>{this.#lt=null,this.#Pt()})),t||(this.#ct&&clearTimeout(this.#ct),this.#ct=window.setTimeout(()=>this.#_t(),120))},e.addEventListener("scroll",this.#st),t&&(this.#at=()=>this.#_t(),e.addEventListener("scrollend",this.#at))}#Ft(){if(!this.#et)return this.#Je;const e=this.#et,t=e.querySelectorAll(`.${_("carousel-item")}`),n=e.getBoundingClientRect(),r=n.left+n.width/2;let i=0,o=1/0;return t.forEach((e,t)=>{const n=e.getBoundingClientRect(),s=Math.abs(r-(n.left+n.width/2));s<o&&(o=s,i=t)}),i}#Pt(){if(!this.#et)return;const e=this.#Ft();e!==this.#dt&&this.#Lt(e)}#_t(){this.#ct&&(clearTimeout(this.#ct),this.#ct=null);const e=this.#Ft();e!==this.#Je&&(this.#Je=e,this.#Nt(e),r.debug(`Gesture settled on offer ${e+1}`)),this.#Lt(e);const t=this.#Je;if(t===this.#ht)return;const n=this.#Ze[t];n&&(this.#ht=t,this.#be.emit("offerView",{offer:n,index:t}))}#Rt(e){this.#it&&(this.#it.textContent=e)}#yt(){this.#Ze.forEach(e=>{e.imageUrl&&((new Image).src=e.imageUrl,r.debug("Preloading offer image",{offerId:e.id,url:e.imageUrl})),e.logoUrl&&((new Image).src=e.logoUrl,r.debug("Preloading merchant logo",{offerId:e.id,url:e.logoUrl}))})}destroy(){this.#Et(),this.#gt&&(this.#pt.removeEventListener("change",this.#gt),this.#gt=null),this.#rt.forEach(e=>e.destroy()),this.#rt=[],this.#be.removeAllListeners(),this.#Ye=null,this.#et=null,this.#tt=null,this.#nt=null,this.#it=null}}const H=["list","thankYou"],j=_("thankyou-title");const Y=e=>{const t=e?.trim();if(void 0!==t&&t.length>0)return t},G=(e,t)=>!(void 0!==e.when.layout&&e.when.layout!==t.layout||void 0!==e.when.mode&&e.when.mode!==t.mode),W=[{context:"list · deferred",field:"appDisplayName",when:{layout:"list",mode:"deferred"},required:!0,resolve:e=>Y(e.display?.appDisplayName)??Y(e.remote.appName)},{context:"list · deferred",field:"trialPeriod",when:{layout:"list",mode:"deferred"},required:!0,resolve:e=>Y(e.display?.trialPeriod)??(e=>{const t=Y(e.entitlementValue);let n=Y(e.entitlementUnit);if(t&&n)return 1===Number(t)&&n.endsWith("s")&&(n=n.slice(0,-1)),`${t} ${n}`})(e.remote)},{context:"list · deferred",field:"logoUrl",when:{layout:"list",mode:"deferred"},required:!0,resolve:e=>Y(e.display?.logoUrl)??Y(e.remote.appIconUrl)},{context:"thankYou",field:"header.title",when:{layout:"thankYou"},resolve:e=>Y(e.header?.title)??"Payment Successful"},{context:"list · immediate",field:"header.title",when:{layout:"list",mode:"immediate"},resolve:e=>Y(e.header?.title)},{context:"list · immediate",field:"header.subtitle",when:{layout:"list",mode:"immediate"},resolve:e=>Y(e.header?.subtitle)},{context:"list · deferred",field:"header.title",when:{layout:"list",mode:"deferred"},resolve:(e,t)=>Y(e.header?.title)??(t.appDisplayName&&t.trialPeriod?Q.s1Title(t.appDisplayName,t.trialPeriod):void 0)},{context:"list · deferred",field:"header.subtitle",when:{layout:"list",mode:"deferred"},resolve:e=>Y(e.header?.subtitle)??Q.s1Subtitle()}],Q={s1Title:(e,t)=>`Get ${e} free for ${t}`,s1Subtitle:()=>"Claim a partner offer to start",primerTitle:(e,t)=>t?`Amazing! Your ${t} free trial of ${e} is ready`:`Amazing! Your ${e} reward is ready`,primerSubtitle:e=>`Next you'll see the ${e} activation screen. No charge today.`,primerCta:(e,t)=>t?`Activate ${e} trial`:`Continue to ${e}`,s2Title:(e,t)=>t?`Congratulations! Your ${t} free trial is active.`:`Congratulations! Your ${e} plan is active.`,s2Subtitle:e=>`All ${e} features are now unlocked.`,s2HostDetail:e=>`Free until ${new Intl.DateTimeFormat(void 0,{month:"long",day:"numeric",year:"numeric"}).format(e)}`,s2Cta:()=>"Claim your reward"};function X(e){const t={},n={},r=[];for(const i of W){if(!G(i,e))continue;const o=i.resolve(e,t);if(void 0!==o)switch(i.field){case"header.title":n.title=o;break;case"header.subtitle":n.subtitle=o;break;default:t[i.field]=o}else i.required&&r.push(i.field)}return r.length>0?{status:"missing",missingFields:r}:{status:"ok",display:t,header:n}}function Z(e,t,n){const r=document.createElement("div");r.className=`${t} ${_("brand-tile-placeholder")}`;const i=e.name?.trim().charAt(0);r.textContent=i?i.toUpperCase():"★";const o=V(e.logoUrl);if(o){const i=document.createElement("img");i.className=t,i.alt=e.name?`${e.name} logo`:"Brand logo",i.width=n,i.height=n,i.decoding="async",i.addEventListener("load",()=>r.replaceWith(i)),i.src=o}return r}function J(e,t){const n=document.createElement("div");n.className=_("lockup"),n.setAttribute("aria-hidden","true");const r=Z(e,`${_("lockup-tile")} ${_("lockup-tile-partner")}`,56),i=Z(t,`${_("lockup-tile")} ${_("lockup-tile-host")}`,56);return n.appendChild(r),n.appendChild(i),n}function ee(e){const t=document.createElement("div");return t.className=_("ledger"),e.forEach(e=>t.appendChild(function(e){const t=document.createElement("div");t.className=_("ledger-row");const n=Z(e.brand,_("ledger-logo"),44),r=document.createElement("div");r.className=_("ledger-text");const i=document.createElement("div");if(i.className=_("ledger-name"),i.textContent=e.name,r.appendChild(i),e.detail){const t=document.createElement("div");t.className=_("ledger-detail"),t.textContent=e.detail,r.appendChild(t)}const o=document.createElement("div");return o.className=`${_("ledger-status")} ${_(`ledger-status-${e.status.tone}`)}`,o.textContent=e.status.text,t.appendChild(n),t.appendChild(r),t.appendChild(o),t}(e))),t}function te(e,t,n){const r=document.createElement("button");return r.className=`encore-button-claim ${t}`,r.setAttribute("type","button"),r.textContent=e,r.addEventListener("click",n),r}const ne=e=>({...e.advertiserName&&{name:e.advertiserName},...(e.logoUrl??e.imageUrl)&&{logoUrl:e.logoUrl??e.imageUrl}}),re=e=>({brand:ne(e),name:e.advertiserName??"Your reward",...e.reward&&{detail:e.reward},status:{text:"Pending",tone:"pending"}}),ie=e=>{const t=document.createElement("h2");return t.className=_("redemption-title"),t.textContent=e,t},oe=e=>{const t=document.createElement("p");return t.className=_("redemption-subtitle"),t.textContent=e,t},se={activation(e){const t=e.host??{},n=t.name??"your app",r=[{brand:{name:n,...t.logoUrl&&{logoUrl:t.logoUrl}},name:n,...t.detail&&{detail:t.detail},status:{text:t.status??"Active",tone:"active"}},re(e)];return[J(ne(e),{name:n,...t.logoUrl&&{logoUrl:t.logoUrl}}),ie(Q.s2Title(n,t.trialPeriod)),oe(Q.s2Subtitle(n)),ee(r)]},reward:e=>[ie("Congratulations!"),oe(e.reward?`Now claim your ${e.reward}`:"Now claim your reward"),ee([re(e)])]};class ae{#Ye=null;#be;#t;constructor(e){this.#t=e,this.#be=new N}render(){const{advertiserName:e,buttonText:t}=this.#t,n=this.#t.variant??"reward",i=document.createElement("div");i.className=_("redemption-container");for(const e of se[n](this.#t))i.appendChild(e);const o=document.createElement("p");o.className=_("redemption-note"),o.textContent=e?`You'll be sent to ${e}. No payment now, cancel anytime.`:"No payment now, cancel anytime.";const s=te(t??Q.s2Cta(),_("redemption-cta"),()=>{r.debug("User tapped the redemption CTA"),this.#be.emit("continue",void 0)});return i.appendChild(o),i.appendChild(s),this.#Ye=i,r.debug("Redemption screen rendered",{variant:n}),i}on(e,t){return this.#be.on(e,t)}destroy(){this.#Ye&&(this.#Ye.remove(),this.#Ye=null),this.#be.removeAllListeners(),r.debug("Redemption screen destroyed")}}class le{#Ye=null;#be;#t;constructor(e){this.#t=e,this.#be=new N}render(){const{display:e,partner:t}=this.#t,n=e.appDisplayName??"your app",i=document.createElement("div");i.className=_("primer-container");const o=t.logoUrl??t.imageUrl,s=J({...t.name&&{name:t.name},...o&&{logoUrl:o}},{name:n,...e.logoUrl&&{logoUrl:e.logoUrl}}),a=document.createElement("h2");a.className=_("primer-title"),a.textContent=Q.primerTitle(n,e.trialPeriod);const l=document.createElement("p");l.className=_("primer-subtitle"),l.textContent=Q.primerSubtitle(n);const d=te(Q.primerCta(n,e.trialPeriod),_("primer-cta"),()=>{r.debug("User tapped the primer CTA"),this.#be.emit("continue",void 0)});return i.appendChild(s),i.appendChild(a),i.appendChild(l),i.appendChild(d),this.#Ye=i,r.debug("Primer screen rendered"),i}on(e,t){return this.#be.on(e,t)}destroy(){this.#Ye&&(this.#Ye.remove(),this.#Ye=null),this.#be.removeAllListeners(),r.debug("Primer screen destroyed")}}class de{#zt=null;#Vt=null;#Bt=!1;start(e){this.#Bt?r.warn("FocusDetector already active"):(this.#zt=()=>{r.debug("Window focused"),e()},this.#Vt=()=>{"visible"===document.visibilityState&&(r.debug("Document became visible"),e())},window.addEventListener("focus",this.#zt),document.addEventListener("visibilitychange",this.#Vt),this.#Bt=!0,r.debug("FocusDetector started"))}stop(){this.#Bt&&(this.#zt&&(window.removeEventListener("focus",this.#zt),this.#zt=null),this.#Vt&&(document.removeEventListener("visibilitychange",this.#Vt),this.#Vt=null),this.#Bt=!1,r.debug("FocusDetector stopped"))}isActive(){return this.#Bt}}const ce="user_id",he="user_attributes",ue=new class{#qt;#s;#b=null;#T=null;#Kt=null;#Ht=null;#jt=null;#Yt=null;#v=null;#Gt={};#Wt=!1;#Qt={};#Xt;#Zt={};#Jt=null;#en=!1;#tn;constructor(){this.#qt=new i,this.#s=new l,this.#tn=new s(this.#s)}configure(e){const t=this.#qt.configure(e);if(t){this.#nn();const e=this.#qt.getBaseURL(),t=this.#qt.getAnalyticsBaseURL(),n=this.#qt.getApiKey()??"";if(e){if(this.#b=new y(n,e),t&&(this.#T=new k(n,t,this.#s),!this.#jt)){const e=new A(n,t,this.#s);this.#jt=new T(this.#s,()=>this.#v,e),e.flush()}this.#v&&(this.#Kt=new c(this.#s,this.#b,this.#v),this.#Ht=new m(this.#b,this.#v,this.#T??void 0)),this.#Yt=new de}if(r.info("Encore SDK initialized successfully",{version:v}),this.#T&&this.#v){const e="undefined"!=typeof window?window.location.hostname:"unknown";this.#T.track(function(e,t,n){return{name:"sdk_initialized",distinctId:e,properties:{sdkVersion:t,appBundleId:n,platform:"web"}}}(this.#v,v,e))}this.#b&&this.#v&&this.#b.initUser(this.#v).catch(e=>{r.warn("Failed to init user on backend (non-blocking)",e)}),this.#rn()}return t}#nn(){const e=this.#s.get(ce);e?(this.#v=e,r.debug("Loaded existing user ID:",e)):(this.#v=p(),this.#s.set(ce,this.#v),r.debug("Generated new user ID:",this.#v));const t=this.#s.get(he);t&&(this.#Gt=t,r.debug("Loaded user attributes"))}#in(){return!!this.#qt.isConfigured()||(r.error("SDK not configured. Call Encore.configure() first."),!1)}identify(e,t){if(!this.#in())return;if(!e||"string"!=typeof e)return void r.error("identify() requires a valid user ID string");const n=this.#v;if(this.#v=e,this.#s.set(ce,e),t&&this.setUserAttributes(t),n!==e&&(r.info("User identified:",e),this.#Kt&&this.#Kt.setUserId(e),this.#Ht&&this.#Ht.setUserId(e),this.#b&&n&&this.#b.identifyUser(n,e,t?.email?{email:t.email}:void 0).catch(e=>{r.warn("Failed to identify user on backend (non-blocking)",e)}),this.#T)){const n="undefined"!=typeof window?window.location.hostname:"unknown";this.#T.track(function(e,t,n,r){return{name:"user_identified",distinctId:e,properties:{userId:t,appBundleId:n,...r&&{attributes:r}}}}(e,e,n,t))}}getCurrentUserId(){return this.#v}getAppAccountId(){return this.#v}reset(){this.#in()&&(r.info("Resetting SDK"),this.#s.clear(),this.#tn.clear(),this.#v=p(),this.#s.set(ce,this.#v),this.#Gt={},this.#Qt={},this.#Zt={},this.#Jt=null,this.#en=!1,this.#Kt&&(this.#Kt.clearQueue(),this.#Kt.destroy(),this.#b&&this.#v&&(this.#Kt=new c(this.#s,this.#b,this.#v))),this.#Ht&&(this.#Ht.clearCache(),this.#b&&this.#v&&(this.#Ht=new m(this.#b,this.#v,this.#T??void 0))),this.#Yt?.isActive()&&this.#Yt.stop(),r.debug("SDK reset complete",{newUserId:this.#v}))}getVersion(){return v}isConfigured(){return this.#qt.isConfigured()}getConfiguration(){return this.#qt.isConfigured()?{apiKey:this.#qt.getApiKey()??"",environment:this.#qt.getEnvironment()??"",baseURL:this.#qt.getBaseURL()??"",analyticsBaseURL:this.#qt.getAnalyticsBaseURL()??"",logLevel:this.#qt.getConfig()?.logLevel??"none",redemptionMode:this.#qt.getRedemptionMode()}:null}async getExperimentState(){return this.#qt.isConfigured()&&this.#jt?this.#jt.getState():null}setLogLevel(e){r.setLogLevel(e)}async revokeEntitlements(){if(!this.#in())return!1;if(!this.#b||!this.#v)return!1;try{return await this.#b.revokeEntitlements(this.#v)}catch(e){return r.error("Failed to revoke entitlements",e),!1}}setUserAttributes(e){this.#in()&&(e&&"object"==typeof e?(this.#Gt={...this.#Gt,...e,custom:{...this.#Gt.custom,...e.custom}},this.#s.set(he,this.#Gt),r.debug("User attributes updated",this.#Gt)):r.error("setUserAttributes() requires a valid UserAttributes object"))}getUserAttributes(){return{...this.#Gt}}async show(e,n,i){if(!this.#in()){const n={type:"error",error:{code:t.INITIALIZATION_ERROR,message:"SDK not configured"}};return e?.onNotGranted?.(n),e?.onError?.(n.error),{status:"dismissed",reason:n}}try{const t=await this.#on(n,i,e?.receipt);return"claimed"===t.status&&"immediate"===this.#qt.getRedemptionMode()?e?.onGranted?.(void 0):"dismissed"===t.status&&t.reason&&e?.onNotGranted?.(t.reason),t}catch(n){r.error("Error during show",n),this.#sn("Show flow error",n);const i={type:"error",error:{code:t.UNKNOWN_ERROR,message:n instanceof Error?n.message:"Unknown error"}};return e?.onError?.(i.error),e?.onNotGranted?.(i),{status:"dismissed",reason:i}}}async presentOffer(e,t){const n=await this.show(e,t);switch(n.status){case"unavailable":return{granted:!1,reason:"noOffersAvailable"};case"dismissed":return{granted:!1,...n.reason&&{reason:n.reason}};case"claimed":return"immediate"===this.#qt.getRedemptionMode()?{granted:!0}:{granted:!1}}}async redeem(e){if(!this.#in())return{status:"dismissed",reason:{type:"error",error:{code:t.INITIALIZATION_ERROR,message:"SDK not configured"}}};const n=this.#an(e);if("list"!==n?.claimLayout||e?.host?.name||await this.#ln(),!n)return r.info("redeem(): no pending transaction to redeem"),{status:"none"};try{this.#Wt||(P({}),this.#Wt=!0);const t=new F,r=await new Promise(r=>{let i=!1;const o=e=>{i||(i=!0,r(e))};t.on("close",()=>o({status:"dismissed"})),t.on("backdropClick",()=>{t.hide(),o({status:"dismissed"})}),t.on("hide",()=>o({status:"dismissed"})),t.show(),this.#dn(t,n,e?.host,()=>{const e=this.#cn();this.#hn(n,e),o({status:"redeemed"}),t.hide()})});return t.destroy(),r}catch(e){return r.error("Error during redeem",e),this.#sn("Redeem flow error",e),{status:"dismissed",reason:{type:"error",error:{code:t.UNKNOWN_ERROR,message:e instanceof Error?e.message:"Unknown error"}}}}}getPendingTransaction(e){return this.#in()?e?this.#tn.getByPlacement(e):this.#tn.getLatest():null}isActive(e,t){return r.error("isActive() has been removed. Entitlement validation must be done server-side. See: https://docs.encorekit.com/server-side-validation"),!1}didGrant(e,t){this.#in()&&(this.#Kt?t&&"string"==typeof t?this.#Kt.sendGrantSignal(t,e):r.error("didGrant() requires a valid transaction ID"):r.error("SignalManager not initialized"))}async waitForVerification(e,t){if(!this.#in())return r.error("SDK not configured"),"failed";if(!this.#b)return r.error("API client not initialized"),"failed";if(!e)return r.error("waitForVerification() requires a transaction ID"),"failed";try{r.debug("Starting verification wait",{transactionId:e,timeout:t});const n={transactionId:e};void 0!==t&&(n.timeout=t);const i=new g(this.#b,n);return await i.poll()}catch(t){return r.error("Error during verification wait",{transactionId:e,error:t}),"failed"}}on(e,t){return r.error("on() event listener has been removed. Entitlement validation must be done server-side. See: https://docs.encorekit.com/server-side-validation"),()=>{}}observeEntitlement(e,t){return r.error("observeEntitlement() has been removed. Entitlement validation must be done server-side. See: https://docs.encorekit.com/server-side-validation"),{subscribe:e=>()=>{}}}placement(e,t){let n,r;return"string"==typeof e?(n=e,r=t):(n=`placement_${p().slice(0,8)}`,r=e),new f(n,async(e,t)=>this.show(t,e,n),r,e=>this.redeem({placement:e}))}#sn(e,t){if(!this.#b)return;const n=t instanceof Error?t.message:String(t),r=t instanceof Error?t.stack:void 0;this.#b.reportError({errorType:t instanceof Error?t.constructor.name:"UnknownError",message:n,context:e,severity:"error",...r&&{stackTrace:r},...this.#v&&{userId:this.#v}}).catch(()=>{}),this.#T&&this.#v&&this.#T.track(function(e,t,n,r){return{name:"sdk_error",distinctId:e,properties:{errorType:t,errorDescription:n,...r&&{context:r}}}}(this.#v,t instanceof Error?t.constructor.name:"UnknownError",n,e))}async#un(e){const t=this.#jt?await this.#jt.getCohort():"notEnrolled";return this.#jt?.logExposure(t),"control"!==t?null:(this.#T&&this.#v&&this.#T.track(h(this.#v,"experiment_control",e,this.#Xt)),r.debug("NCL control cohort — ghost trigger, no UI shown"),{status:"unavailable"})}#pn(e){if(e.ui?.values?.text){const t=e.ui.values.text;this.#Qt={...t},r.debug("Remote config loaded",t)}const t=e.entitlements?.native,n=e.ui?.values?.media;this.#Zt={...t?.value&&{entitlementValue:String(t.value)},...t?.unit&&{entitlementUnit:t.unit},...t?.durationDays&&{trialDurationDays:t.durationDays},...n?.appstoreIcon&&{appIconUrl:n.appstoreIcon}},this.#Xt=e.ui?.variantId??void 0,this.#jt?.setConfig(e.experiments?.ncl),this.#en=!0}#rn(){this.#b&&this.#v&&(this.#Jt=this.#b.fetchConfig(this.#v,v).then(e=>this.#pn(e)).catch(e=>{r.warn("Failed to fetch remote config (non-blocking)",e),this.#Jt=null}))}async#ln(){if(!this.#Jt)return;let e;await Promise.race([this.#Jt,new Promise(t=>{e=setTimeout(t,2500)})]),clearTimeout(e)}#mn(e,t,n){return{layout:e,mode:t,...n?.header&&{header:n.header},...n?.display&&{display:n.display},remote:{...this.#Qt.appName&&{appName:this.#Qt.appName},...this.#Zt}}}async#gn(e,n,i){let o=X(this.#mn(e,n,i));if("ok"===o.status)return{ok:!0,display:o.display,header:o.header};if(this.#Jt||this.#rn(),await this.#ln(),o=X(this.#mn(e,n,i)),"ok"===o.status)return{ok:!0,display:o.display,header:o.header};if(this.#en&&"missing"===o.status){const e={appDisplayName:"appDisplayName (pass display.appDisplayName, or set the app name in the Encore portal)",trialPeriod:"trialPeriod (pass display.trialPeriod, or set the app entitlement value + unit in the Encore portal)",logoUrl:"logoUrl (pass display.logoUrl, or set the app icon in the Encore portal)"},n=`Deferred mode requires host display values that did not resolve: ${o.missingFields.map(t=>e[t]).join("; ")}.`;return r.error(`[Encore SDK] Integration error: ${n}`),{ok:!1,result:{status:"dismissed",reason:{type:"error",error:{code:t.CONFIGURATION_ERROR,message:n}}}}}const s="Could not load the remote display configuration (network). The next show() call will retry.";return r.error(`[Encore SDK] ${s}`),{ok:!1,result:{status:"dismissed",reason:{type:"error",error:{code:t.NETWORK_ERROR,message:s}}}}}#fn(e,t,n){if(!this.#T||!this.#v)return;const r={transactionId:t.id,campaignId:t.offer.campaignId,presentationId:n,...this.#Xt&&{variantId:this.#Xt}};this.#T.track("continue"===e?function(e,t){return{name:"sdk_offer_activation_continue",distinctId:e,properties:{...t}}}(this.#v,r):function(e,t){return{name:"sdk_offer_activation_dismissed",distinctId:e,properties:{...t}}}(this.#v,r))}#bn(e,t,n,i){const o=new le({display:n,partner:{...t.offer.advertiserName&&{name:t.offer.advertiserName},...t.offer.logoUrl&&{logoUrl:t.offer.logoUrl},...t.offer.imageUrl&&{imageUrl:t.offer.imageUrl}}});o.on("continue",()=>{o.destroy(),i()}),e.setContent(o.render()),r.debug("Primer screen displayed",{transactionId:t.id})}async#on(e,n,i){if(!this.#Ht||!this.#b)return r.error("Managers not initialized"),{status:"dismissed",reason:{type:"error",error:{code:t.INITIALIZATION_ERROR,message:"Managers not initialized"}}};const o=(s=e?.layout,void 0===s?"list":"string"==typeof s&&H.includes(s)?s:(r.warn("Unknown placement layout — falling back to 'list'",{layout:s}),"list"));var s;const a=e?.redemptionMode??this.#qt.getRedemptionMode();let l,d;if("thankYou"===o&&"deferred"===a&&r.warn("thankYou + deferred: the claim will NOT redirect — you own calling redeem() after your flow completes. Most post-purchase surfaces want redemptionMode 'immediate'."),"deferred"===a){const t=await this.#gn(o,a,e);if(!t.ok)return t.result;l=t.display,d=t.header}else{const t=X(this.#mn(o,a,e));l="ok"===t.status?t.display:{},d="ok"===t.status?t.header:{}}const c=p();var u;this.#T&&this.#v&&this.#T.track({name:"sdk_offer_presentation_triggered",distinctId:this.#v,properties:{presentationId:c,...(u=this.#Xt)&&{variantId:u}}});const m=await this.#un(c);if(m)return m;try{const s=e?.appearance,u={...s?.accentColor&&{primaryColor:s.accentColor},...s?.backgroundColor&&{backgroundColor:s.backgroundColor},...s?.textColor&&{textColor:s.textColor}};this.#Wt?s&&function(e){const t=document.documentElement;e.primaryColor&&t.style.setProperty("--encore-primary-color",e.primaryColor),e.backgroundColor&&t.style.setProperty("--encore-bg-color",e.backgroundColor),e.textColor&&t.style.setProperty("--encore-text-color",e.textColor),e.borderRadius&&t.style.setProperty("--encore-border-radius",e.borderRadius),e.fontFamily&&t.style.setProperty("--encore-font-family",e.fontFamily),r.debug("Theme variables updated",e)}(u):(P(u),this.#Wt=!0),r.debug("Fetching offers");const{offers:m}=await this.#Ht.fetchOffers(this.#Gt);if(0===m.length)return r.info("No offers available"),this.#T&&this.#v&&this.#T.track(h(this.#v,"noOffersAvailable",c,this.#Xt)),{status:"unavailable"};const g="thankYou"===e?.layout?e:void 0,f="thankYou"===o?g?.offerContext:d,b={...this.#Qt,...f?.title&&{titleText:f.title,customHeadline:f.title},...f?.subtitle&&{subtitleText:f.subtitle,customSubheadline:f.subtitle}},v=this.#vn(b),y=this.#Ht.beginPresentation(),w=new Map;m.forEach(e=>{w.set(e.id,p())}),this.#T&&this.#v&&this.#T.track(function(e,t,n,r){return{name:"sdk_offer_presentation_success",distinctId:e,properties:{presentationId:t,offerCount:n,...r&&{variantId:r}}}}(this.#v,y,m.length,this.#Xt));const x=new F,I=new K(m,b,"thankYou"===o?{mode:"paged"}:void 0),k=await new Promise(s=>{let c=!1;const h=e=>{c||(c=!0,s(e))};let u=null;I.on("offerClaim",e=>{const i=e.offer;r.debug("User claimed offer",{offerId:i.id});const s="immediate"===a?this.#cn():null;(async()=>{try{const e=w.get(i.id)??"",t=m.indexOf(i),r=await this.#yn(i,n,e,t,m.length,v,o);"immediate"===a?(this.#hn(r,s),this.#Ht?.endPresentation(),x.hide(),h({status:"claimed"})):"list"!==o?(u=r,this.#Ht?.endPresentation(),h({status:"claimed"}),x.hide()):(u=r,this.#Ht?.endPresentation(),this.#bn(x,r,l,()=>{this.#fn("continue",r,y),h({status:"claimed"}),x.hide()}))}catch(e){r.error("Error creating transaction",{offerId:i.id,error:e}),this.#sn("Transaction creation error",e),this.#wn(s),this.#Ht?.endPresentation("error"),x.hide(),h(u?{status:"claimed"}:{status:"dismissed",reason:{type:"error",error:{code:t.UNKNOWN_ERROR,message:e instanceof Error?e.message:"Transaction failed"}}})}})()}),I.on("complete",e=>{e.claimed||(r.debug("User declined last offer"),this.#Ht?.endPresentation("userDeclinedLastOffer"),I.destroy(),x.hide(),h({status:"dismissed",reason:"userDeclinedLastOffer"}))}),x.on("close",()=>{if(u)return r.debug("User dismissed the primer"),c||this.#fn("dismissed",u,y),I.destroy(),void h({status:"claimed"});r.debug("User closed modal"),this.#Ht?.endPresentation("userClosedModal"),I.destroy(),h({status:"dismissed",reason:"userClosedModal"})}),x.on("backdropClick",()=>{if(u)return r.debug("User dismissed the primer (backdrop)"),c||this.#fn("dismissed",u,y),I.destroy(),x.hide(),void h({status:"claimed"});r.debug("User clicked outside modal"),this.#Ht?.endPresentation("userClickedOutside"),I.destroy(),x.hide(),h({status:"dismissed",reason:"userClickedOutside"})}),I.on("offerView",e=>{const{offer:t}=e,n=w.get(t.id),r=m.indexOf(t);n&&this.#Ht&&this.#Ht.trackOfferPresented(t,n,r,m.length)}),x.on("hide",()=>{if(u)return c||this.#fn("dismissed",u,y),void h({status:"claimed"});h({status:"dismissed",reason:"userClosedModal"})}),x.show();const p=I.render();let f;f="thankYou"===o?function(e,t={}){const{header:n,footer:i,receipt:o}=t,s=function(e,t){if(!t)return e?.subtitle;const n="string"==typeof t.amount?t.amount.trim():"";if(!n)return r.warn("Receipt is missing a non-empty `amount` — ignoring the receipt"),e?.subtitle;e?.subtitle&&r.warn("Both `receipt` and `header.subtitle` supplied — the receipt owns the subtitle slot; `header.subtitle` is ignored");const i="string"==typeof t.purpose?t.purpose.trim():"";return i?`You paid ${n} for ${i}`:`You paid ${n}`}(n,o),a=document.createElement("div");a.className=_("thankyou");const l="none"!==n?.icon;if(l||n?.title||s){const e=document.createElement("div");if(e.className=_("thankyou-status"),l){const t=document.createElement("span");t.className=_("thankyou-check"),t.setAttribute("aria-hidden","true"),t.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5" /></svg>',e.appendChild(t)}if(n?.title){const t=document.createElement("h2");t.className=_("thankyou-title"),t.id=j,t.textContent=n.title,e.appendChild(t)}if(s){const t=document.createElement("p");t.className=_("thankyou-subtitle"),t.textContent=s,e.appendChild(t)}a.appendChild(e)}if(a.appendChild(e),i?.text){const e=document.createElement("p");e.className=_("thankyou-footer"),e.textContent=i.text,a.appendChild(e)}return a}(p,{header:{...e?.header,...void 0!==d.title&&{title:d.title}},...g?.footer&&{footer:g.footer},...i&&{receipt:i}}):p,x.setContent(f),"thankYou"===o&&d.title&&x.setLabelledBy(j)});return x.destroy(),I.destroy(),k}catch(e){return r.error("Error in offer flow",e),this.#sn("Offer flow error",e),{status:"dismissed",reason:{type:"error",error:{code:t.UNKNOWN_ERROR,message:e instanceof Error?e.message:"Unknown error"}}}}}async#yn(e,t,n,i,o,s,a){if(!this.#Ht)throw new Error("OfferManager not initialized");const l=await this.#Ht.startTransaction(e.campaignId);this.#Ht.trackOfferClaimed(e,n,l,i,o);const d=Date.now(),c={id:l,appAccountId:this.#v??"",...t?{placementId:t}:{},offer:{id:e.id,campaignId:e.campaignId,advertiserUrl:e.advertiserUrl,...e.trackingParameters&&{trackingParameters:e.trackingParameters},...e.merchantName&&{advertiserName:e.merchantName},...s&&{reward:s},...e.imageUrl&&{imageUrl:e.imageUrl},...e.logoUrl&&{logoUrl:e.logoUrl}},status:"claimed",createdAt:new Date(d).toISOString(),expiresAt:new Date(d+6048e5).toISOString(),...a&&{claimLayout:a}};return this.#tn.save(c),r.debug("Transaction created",{transactionId:c.id,placement:t}),c}#hn(e,t){const n=this.#xn(e.offer.advertiserUrl,e.offer.trackingParameters,e.id),i=this.#In(t,n);return i||r.warn("Failed to open advertiser URL (popup blocked?)"),this.didGrant("provisional",e.id),this.#tn.markRedeemed(e.id),i}#xn(e,t,n){try{const i=new URL(e);t&&Object.entries(t).forEach(([e,t])=>{i.searchParams.append(e,String(t))});let o=i.toString();return o=o.replace(/TRANSACTION_ID/g,n),r.debug("Built final advertiser URL",{original:e,final:o,transactionId:n}),o}catch(t){return r.error("Failed to build advertiser URL",{url:e,error:t}),e.replace(/TRANSACTION_ID/g,n)}}#cn(){try{return window.open("","_blank")}catch(e){return r.error("Failed to open blank advertiser window",{error:e}),null}}#In(e,t){try{if(e)return e.location.href=t,e.focus(),!0;const n=window.open(t,"_blank","noopener,noreferrer");return!!n&&(n.focus(),!0)}catch(e){return r.error("Failed to navigate advertiser URL",{url:t,error:e}),!1}}#wn(e){try{e?.close()}catch(e){r.error("Failed to close advertiser window",{error:e})}}#vn(e){const t=e.creditClaimedTitleText?.trim();if(t)return t;const n=e.entitlementValue?.trim(),r=e.entitlementUnit?.trim();if(n&&r){const t=e.appName?.trim();return t?`${n} ${r} of ${t}`:`${n} ${r}`}}#an(e){return e?.placement?this.#tn.getByPlacement(e.placement):this.#tn.getLatest()}#dn(e,t,n,i){try{const{reward:o,advertiserName:s,logoUrl:a,imageUrl:l}=t.offer,d="list"===t.claimLayout?"activation":"reward";let c={};if("activation"===d){const e=X(this.#mn("list","deferred",{display:{...n?.name&&{appDisplayName:n.name},...n?.logoUrl&&{logoUrl:n.logoUrl}}})),t="ok"===e.status?e.display:void 0,r=n?.name??t?.appDisplayName,i=this.#Zt.trialDurationDays,o=i?Q.s2HostDetail(new Date(Date.now()+24*i*60*60*1e3)):void 0,s=n?.logoUrl??t?.logoUrl;r&&(c={host:{name:r,...t?.trialPeriod&&{trialPeriod:t.trialPeriod},...n?.status&&{status:n.status},...o&&{detail:o},...s&&{logoUrl:s}}})}const h=new ae({variant:"activation"===d&&c.host?"activation":"reward",...o&&{reward:o},...s&&{advertiserName:s},...a&&{logoUrl:a},...l&&{imageUrl:l},...c});h.on("continue",()=>{h.destroy(),i()}),e.setContent(h.render()),r.debug("Redemption screen displayed",{transactionId:t.id})}catch(e){r.error("Failed to show redemption screen",e),i()}}};e.Encore=ue,e.default=ue,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).Encore={})}(this,function(e){"use strict";var t;!function(e){e.NETWORK_ERROR="NETWORK_ERROR",e.API_ERROR="API_ERROR",e.CONFIGURATION_ERROR="CONFIGURATION_ERROR",e.INITIALIZATION_ERROR="INITIALIZATION_ERROR",e.STORAGE_ERROR="STORAGE_ERROR",e.INVALID_REQUEST="INVALID_REQUEST",e.UNAUTHORIZED="UNAUTHORIZED",e.NOT_FOUND="NOT_FOUND",e.TIMEOUT="TIMEOUT",e.UNKNOWN_ERROR="UNKNOWN_ERROR"}(t||(t={}));const n=["churn-intervention","post-action-reward"];function i(e){return"string"==typeof e?`'${e.slice(0,64)}'`:typeof e}const r="[Encore SDK]",o=new class{#e="none";setLogLevel(e){this.#e=e}getLogLevel(){return this.#e}debug(...e){"debug"===this.#e&&console.debug(r,...e)}info(...e){"debug"===this.#e&&console.info(r,...e)}warn(...e){"debug"===this.#e&&console.warn(r,...e)}error(...e){"debug"===this.#e&&console.error(r,...e)}critical(...e){console.error(r,"[CRITICAL]",...e)}};class s{#t=null;#n=!1;configure(e){if(this.#n)return o.warn("SDK already configured. Ignoring subsequent configure() call."),!1;const t=this.#i(e);if(t)return o.error("Invalid configuration:",t),!1;const n=e.logLevel??"none",i=e.environment??"production";return this.#t={apiKey:e.apiKey,environment:i,baseURL:this.#r(i),analyticsBaseURL:this.#o(i),logLevel:n,redemptionMode:e.redemptionMode??"deferred"},o.setLogLevel(n),this.#n=!0,o.debug("SDK configured successfully",this.#t),!0}#i(e){return e.apiKey&&"string"==typeof e.apiKey&&""!==e.apiKey.trim()?e.environment&&!["production","development","staging","localhost"].includes(e.environment)?"environment must be 'production', 'development', 'staging', or 'localhost'":e.logLevel&&!["none","debug"].includes(e.logLevel)?"logLevel must be 'none' or 'debug'":e.redemptionMode&&!["deferred","immediate"].includes(e.redemptionMode)?"redemptionMode must be 'deferred' or 'immediate'":null:"apiKey is required and must be a non-empty string"}#r(e){switch(e){case"localhost":return"http://localhost:4000/publisher/sdk/v1";case"development":return"https://api.dev.encorekit.com/encore/publisher/sdk/v1";case"staging":return"https://api.staging.encorekit.com/encore/publisher/sdk/v1";default:return"https://api.encorekit.com/encore/publisher/sdk/v1"}}#o(e){switch(e){case"localhost":return"http://localhost:8081/v1";case"development":return"https://api.dev.encorekit.com/analytics/v1";case"staging":return"https://api.staging.encorekit.com/analytics/v1";default:return"https://api.encorekit.com/analytics/v1"}}getConfig(){return this.#t}isConfigured(){return this.#n}getApiKey(){return this.#t?.apiKey??null}getBaseURL(){return this.#t?.baseURL??null}getAnalyticsBaseURL(){return this.#t?.analyticsBaseURL??null}getEnvironment(){return this.#t?.environment??null}getRedemptionMode(){return this.#t?.redemptionMode??"deferred"}reset(){this.#t=null,this.#n=!1,o.debug("Configuration reset")}}const a="transactions";class l{#s;constructor(e){this.#s=e}#a(){const e=this.#s.get(a);return e&&"object"==typeof e?{transactions:e.transactions??{},placements:e.placements??{},latest:e.latest??null}:{transactions:{},placements:{},latest:null}}#l(e){this.#s.set(a,e)}#d(e){return!!e&&"claimed"===e.status&&new Date(e.expiresAt).getTime()>Date.now()}save(e){const t=this.#a();t.transactions[e.id]=e,e.placementId&&(t.placements[e.placementId]=e.id),t.latest=e.id,this.#c(t),this.#l(t),o.debug("TransactionStore: saved transaction",{transactionId:e.id,placement:e.placementId})}getByTransactionId(e){return this.#a().transactions[e]??null}getByPlacement(e){const t=this.#a(),n=t.placements[e];if(!n)return null;const i=t.transactions[n];return this.#d(i)?i:null}getLatest(){const e=this.#a();if(e.latest){const t=e.transactions[e.latest];if(this.#d(t))return t}return Object.values(e.transactions).filter(e=>this.#d(e)).sort((e,t)=>new Date(t.createdAt).getTime()-new Date(e.createdAt).getTime())[0]??null}markRedeemed(e){const t=this.#a(),n=t.transactions[e];n&&(n.status="redeemed",n.redeemedAt=(new Date).toISOString(),n.placementId&&t.placements[n.placementId]===e&&delete t.placements[n.placementId],t.latest===e&&(t.latest=null),this.#l(t),o.debug("TransactionStore: marked redeemed",{transactionId:e}))}#c(e){const t=Date.now();for(const[n,i]of Object.entries(e.transactions))(new Date(i.expiresAt).getTime()<=t||"redeemed"===i.status)&&delete e.transactions[n];for(const[t,n]of Object.entries(e.placements))e.transactions[n]||delete e.placements[t];e.latest&&!e.transactions[e.latest]&&(e.latest=null)}clear(){this.#l({transactions:{},placements:{},latest:null})}}const d="encore_";class c{#h;#u;constructor(){this.#u=new Map,this.#h=this.#p(),o.debug(`StorageManager initialized with ${this.#h}`)}#p(){return this.#m("localStorage")?"localStorage":this.#m("sessionStorage")?"sessionStorage":(o.warn("Neither localStorage nor sessionStorage available, using in-memory storage"),"memory")}#m(e){try{const t="localStorage"===e?localStorage:sessionStorage,n="__encore_test__";return t.setItem(n,"test"),t.removeItem(n),!0}catch{return!1}}#f(){return"localStorage"===this.#h?localStorage:"sessionStorage"===this.#h?sessionStorage:this.#u}#g(e){return`${d}${e}`}set(e,t){if(void 0===t)return void o.warn(`Attempted to store undefined value for key: ${e}`);const n=this.#g(e),i=JSON.stringify(t);try{const t=this.#f();t instanceof Map?t.set(n,i):t.setItem(n,i),o.debug(`Stored ${e} in ${this.#h}`)}catch(e){e instanceof Error&&"QuotaExceededError"===e.name?o.error("Storage quota exceeded:",e):o.error("Failed to store item:",e),"memory"!==this.#h&&(o.warn("Falling back to memory storage for this item"),this.#u.set(n,i))}}get(e){const t=this.#g(e);try{const n=this.#f();let i=null;return i=n instanceof Map?n.get(t)??null:n.getItem(t),null===i||"undefined"===i?("undefined"===i&&(o.warn(`Found corrupted value "undefined" for key: ${e}, cleaning up`),this.remove(e)),null):JSON.parse(i)}catch(t){return o.error(`Failed to retrieve ${e}:`,t),this.remove(e),null}}remove(e){const t=this.#g(e);try{const n=this.#f();n instanceof Map?n.delete(t):n.removeItem(t),o.debug(`Removed ${e} from ${this.#h}`)}catch(t){o.error(`Failed to remove ${e}:`,t)}}clear(){try{const e=this.#f();if(e instanceof Map)for(const t of e.keys())t.startsWith(d)&&e.delete(t);else{const t=[];for(let n=0;n<e.length;n++){const i=e.key(n);i?.startsWith(d)&&t.push(i)}t.forEach(t=>e.removeItem(t))}o.debug(`Cleared all Encore data from ${this.#h}`)}catch(e){o.error("Failed to clear storage:",e)}}getStorageType(){return this.#h}}const h="signal_queue";class u{#s;#b;#v;#y=[];#w=!1;#x;constructor(e,t,n){this.#s=e,this.#b=t,this.#v=n,this.#I(),this.#C(),this.processQueue()}setUserId(e){this.#v!==e&&(o.debug("SignalManager: User ID changed, clearing queue"),this.#v=e,this.#y=[],this.#I())}#I(){try{const e=`${this.#v}_${h}`,t=this.#s.get(e);t&&Array.isArray(t)&&(this.#y=t,o.debug("Loaded grant signal queue from storage",{count:t.length}))}catch(e){o.error("Failed to load grant signal queue from storage:",e)}}#k(){try{const e=`${this.#v}_${h}`;this.#s.set(e,this.#y),o.debug("Saved grant signal queue to storage",{count:this.#y.length})}catch(e){o.error("Failed to save grant signal queue to storage:",e)}}enqueue(e,t){const n=this.#y.some(n=>n.transactionId===e&&n.grantType===t);if(n)return void o.debug("Grant signal already in queue, skipping",{transactionId:e,grantType:t});const i={transactionId:e,grantType:t,timestamp:(new Date).toISOString(),attempts:0};this.#y.push(i),this.#k(),o.info("Grant signal added to queue",{transactionId:e,grantType:t})}dequeue(e){const t=this.#y.findIndex(t=>t.transactionId===e.transactionId&&t.grantType===e.grantType);-1!==t&&(this.#y.splice(t,1),this.#k(),o.debug("Grant signal removed from queue",{transactionId:e.transactionId,grantType:e.grantType}))}getQueue(){return[...this.#y]}clearQueue(){this.#y=[],this.#k(),o.debug("Grant signal queue cleared")}async sendGrantSignal(e,t){try{if(o.debug("Sending grant signal",{transactionId:e,grantType:t}),!await this.#b.sendGrantSignal(e,t))throw new Error("Grant signal failed (returned false)");o.info("Grant signal sent successfully",{transactionId:e,grantType:t})}catch{o.warn("Failed to send grant signal, adding to queue",{transactionId:e,grantType:t}),this.enqueue(e,t)}}async processQueue(){if(this.#w)o.debug("Queue processing already in progress, skipping");else if(0!==this.#y.length){this.#w=!0,o.info("Processing grant signal queue",{count:this.#y.length});try{for(const e of[...this.#y])await this.#E(e),this.#y.length>1&&await this.#$(100)}finally{this.#w=!1}}}async#E(e){if(e.attempts>=3)return o.error("Grant signal max attempts reached, removing from queue",{transactionId:e.transactionId,grantType:e.grantType,attempts:e.attempts}),void this.dequeue(e);if(e.lastAttemptAt){const t=new Date(e.lastAttemptAt).getTime(),n=Date.now(),i=1e3*Math.pow(2,e.attempts-1),r=n-t;if(r<i)return void o.debug("Skipping signal, waiting for backoff period",{transactionId:e.transactionId,remainingWait:i-r})}e.attempts++,e.lastAttemptAt=(new Date).toISOString(),this.#k();try{o.debug("Attempting to send queued grant signal",{transactionId:e.transactionId,grantType:e.grantType,attempt:e.attempts}),await this.#b.sendGrantSignal(e.transactionId,e.grantType)?(o.info("Queued grant signal sent successfully",{transactionId:e.transactionId,grantType:e.grantType}),this.dequeue(e)):o.warn("Grant signal failed, will retry",{transactionId:e.transactionId,attempt:e.attempts})}catch(t){o.warn("Failed to send queued grant signal, will retry",{transactionId:e.transactionId,attempt:e.attempts,error:t})}}#C(){this.#x=function(e){const t=()=>{o.debug("Network status: online"),e()};return window.addEventListener("online",t),()=>{window.removeEventListener("online",t)}}(()=>{o.debug("Network came online, processing grant signal queue"),this.processQueue()})}destroy(){this.#x&&(this.#x(),this.#x=void 0)}#$(e){return new Promise(t=>setTimeout(t,e))}}const p={userClosedModal:"close_button",userClickedOutside:"dismissed",userDeclinedLastOffer:"last_offer_declined",error:"interrupted",appBackgrounded:"app_backgrounded",appTerminated:"app_terminated"};function m(e){if(null!=e)return p[e]??e}function f(e){return e?{use_case:e}:{}}function g(e,t,n,i,r){return{name:"sdk_offer_presentation_failed",distinctId:e,properties:{reason:t,presentationId:n,...i&&{variantId:i},...f(r)}}}function b(e,t){const{useCase:n,...i}=t;return{name:"sdk_offer_time_spent",distinctId:e,properties:{...i,...f(n)}}}function v(e){const{useCase:t,...n}=e,i={};for(const e in n){const t=n[e];void 0!==t&&(i[e]=t)}return{...i,...f(t)}}function y(e,t){return{name:"sdk_sdui_button_tapped",distinctId:e,properties:{...t.variantId&&{variantId:t.variantId},actionType:t.actionType,presentationId:t.presentationId,currentState:t.currentState}}}function w(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=15&(crypto.getRandomValues(new Uint8Array(1))[0]??0);return("x"===e?t:3&t|8).toString(16)})}class x{#b;#T;#v;#S=[];#A=null;#U=null;#L=null;#N=new Map;#R=0;#O;#M;#D=null;#P=!1;#_=null;constructor(e,t,n){this.#b=e,this.#v=t,this.#T=n??null,this.#z()}async fetchOffers(e,t=!0,n){if(t&&this.#S.length>0&&this.#A)return o.debug("Using cached offers",{count:this.#S.length}),{offers:[...this.#S]};try{o.debug("Fetching offers from API",{userId:this.#v});const t=await this.#b.fetchOffers(this.#v,e,n);return 0===t.offers?.length?(o.info("No offers available for user"),this.#S=[],this.#A=Date.now(),{offers:[]}):(this.#S=t.offers,this.#A=Date.now(),o.info("Offers fetched successfully",{count:t.offers.length,offerIds:t.offers.map(e=>e.id)}),{offers:[...t.offers]})}catch(e){throw o.error("Failed to fetch offers",e),e}}beginPresentation(e,t){return this.#U=w(),this.#L=Date.now(),this.#N.clear(),this.#R=0,this.#D=null,this.#O=e,this.#M=t,this.#P=!1,this.#U}get presentationId(){return this.#U}#F(){return this.#O?{variantId:this.#O}:{}}trackOfferPresented(e,t,n,i,r){if(!this.#T||!this.#U)return;this.#R++,this.#D&&this.#D!==e.id&&this.trackOfferViewEnd(this.#D),this.#D=e.id;const o=this.#N.get(e.id);o?(o.viewCount++,o.startTime=Date.now()):this.#N.set(e.id,{startTime:Date.now(),totalTime:0,viewCount:1,reported:!1}),this.#T.track(function(e,t){const{useCase:n,...i}=t;return{name:"sdk_offer_presented",distinctId:e,properties:{...i,...f(n)}}}(this.#v,{campaignId:e.campaignId,creativeId:e.creativeId,...null!=e.merchantName&&{advertiserName:e.merchantName},impressionId:t,offerIndex:n,totalOffers:i,presentationId:this.#U,...this.#F(),...r&&{useCase:r}}))}trackOfferViewEnd(e){const t=this.#N.get(e);t&&t.startTime>0&&(t.totalTime+=(Date.now()-t.startTime)/1e3,t.startTime=0)}trackOfferClaimed(e,t,n,i,r,o){this.#T&&this.#U&&this.#T.track(function(e,t){const{useCase:n,...i}=t;return{name:"sdk_offer_claimed",distinctId:e,properties:{...i,...f(n)}}}(this.#v,{campaignId:e.campaignId,creativeId:e.creativeId,...null!=e.merchantName&&{advertiserName:e.merchantName},impressionId:t,transactionId:n,offerIndex:i,totalOffers:r,presentationId:this.#U,...this.#F(),...o&&{useCase:o}}))}trackOfferDeclined(e,t,n,i,r,o){if(!this.#T||!this.#U)return;const s=m(r);this.#T.track(function(e,t){const{useCase:n,...i}=t;return{name:"sdk_offer_declined",distinctId:e,properties:{...i,...f(n)}}}(this.#v,{campaignId:e.campaignId,creativeId:e.creativeId,...null!=e.merchantName&&{advertiserName:e.merchantName},impressionId:t,offerIndex:n,totalOffers:i,...null!=s&&{declineReason:s},presentationId:this.#U,...this.#F(),...o&&{useCase:o}}))}endPresentation(e,t){this.#B(e,t,!1),this.#U=null,this.#L=null,this.#N.clear(),this.#R=0,this.#D=null,this.#O=void 0,this.#M=void 0,this.#P=!1}#B(e,t,n,i=!0){if(!this.#T||!this.#U||!this.#L)return;const r=Date.now(),o=(r-this.#L)/1e3,s=n?{keepalive:!0}:void 0;for(const[e,n]of this.#N){let i=n.totalTime;if(n.startTime>0&&(i+=(r-n.startTime)/1e3),n.reported&&i<=0)continue;n.reported=!0;const o=this.#S.find(t=>t.id===e),a=o?this.#S.indexOf(o):0;this.#T.track(b(this.#v,{campaignId:o?.campaignId??"",offerIndex:a,timeSpentSeconds:i,viewCount:n.viewCount,totalOffers:this.#S.length,presentationId:this.#U,...this.#F(),...t&&{useCase:t}}),s),n.totalTime=0,n.startTime>0&&(n.startTime=r)}if(!i||this.#P)return;this.#P=!0;const a=m(e);this.#T.track(function(e,t){const{useCase:n,...i}=t;return{name:"sdk_offer_sheet_dismissed",distinctId:e,properties:{...i,...f(n)}}}(this.#v,{presentationId:this.#U,totalTimeSpentSeconds:o,totalOffers:this.#S.length,offersViewed:this.#R,...null!=a&&{declineReason:a},...this.#F(),...t&&{useCase:t}}),s)}flushForPageLifecycle(e,t){try{this.#B(e?"appTerminated":"appBackgrounded",t,!0,e)}catch{}}#z(){if("undefined"!=typeof window&&"undefined"!=typeof document)try{const e=()=>this.flushForPageLifecycle(!0,this.#M),t=()=>{"hidden"===document.visibilityState&&this.flushForPageLifecycle(!1,this.#M)};window.addEventListener("pagehide",e),document.addEventListener("visibilitychange",t),this.#_=()=>{window.removeEventListener("pagehide",e),document.removeEventListener("visibilitychange",t)}}catch{}}destroy(){this.#_?.(),this.#_=null}async startTransaction(e,t){try{o.debug("Starting transaction",{campaignId:e,useCase:t});const n=await this.#b.startTransaction(e,this.#v,t);if(!n.transactionId)throw new Error(n.error??"No transactionId in response");return o.info("Transaction started",{transactionId:n.transactionId,campaignId:e}),n.transactionId}catch(t){throw o.error("Failed to start transaction",{campaignId:e,error:t}),t}}clearCache(){this.#S=[],this.#A=null,o.debug("Offer cache cleared")}setUserId(e){this.#v=e,this.clearCache(),o.debug("OfferManager user ID updated",{userId:e})}}class I{#b;#V;#H;#q=0;#K=1e3;#j=8e3;#G=null;#Y=!1;constructor(e,t){this.#b=e,this.#V=t.transactionId,this.#H=t.timeout??3e4}async poll(){return this.#Y?(o.warn("Verification polling already in progress",{transactionId:this.#V}),"pending"):(this.#Y=!0,this.#q=Date.now(),o.debug("Starting verification polling",{transactionId:this.#V,timeout:this.#H}),this.#W())}stop(){this.#Y=!1,null!==this.#G&&(clearTimeout(this.#G),this.#G=null),o.debug("Verification polling stopped",{transactionId:this.#V})}async#W(){for(;this.#Y;){const e=Date.now()-this.#q;if(e>=this.#H)return o.warn("Verification polling timeout",{transactionId:this.#V,elapsed:e}),this.stop(),"pending";try{const e=await this.#b.checkVerificationStatus(this.#V);if(o.debug("Verification status checked",{transactionId:this.#V,status:e.status,rawStatus:e.raw}),"verified"===e.status)return o.info("Transaction verified",{transactionId:this.#V}),this.stop(),"verified";if("failed"===e.status)return o.warn("Transaction failed verification",{transactionId:this.#V}),this.stop(),"failed";await this.#$(this.#K),this.#K=Math.min(2*this.#K,this.#j)}catch(e){o.error("Error checking verification status",{transactionId:this.#V,error:e}),await this.#$(this.#K)}}return"pending"}async#$(e){return new Promise(t=>{this.#G=window.setTimeout(()=>{this.#G=null,t()},e)})}}class C{#Q=null;#X=null;#Z;#J;#ee;#te;constructor(e,t,n,i){this.#ee=e,this.#Z=t,this.#te=n,this.#J=i}useCase(e){return this.#te={...this.#te,useCase:e},this}headline(e){return this.#te={...this.#te,header:{...this.#te?.header,title:e}},this}subheadline(e){return this.#te={...this.#te,header:{...this.#te?.header,subtitle:e}},this}onNotGranted(e){return this.#Q=e,this}onLoadingStateChange(e){return this.#X=e,this}async show(e){try{this.#X&&this.#X(!0),o.debug("PlacementBuilder: Executing presentation",{id:this.#ee});const t=void 0===e?await this.#Z(this.#te):await this.#Z(this.#te,e);return this.#X&&this.#X(!1),"dismissed"===t.status&&t.reason&&this.#Q&&this.#Q(t.reason),t}catch(e){this.#X&&this.#X(!1),o.error("PlacementBuilder: Error during presentation",e);const n={type:"error",error:{code:t.UNKNOWN_ERROR,message:e instanceof Error?e.message:"Unknown error"}};return this.#Q&&this.#Q(n),{status:"dismissed",reason:n}}}async redeem(){if(!this.#J)return o.error("PlacementBuilder: redeem() is not available for this placement"),{status:"none"};try{this.#X?.(!0);const e=await this.#J(this.#ee);return this.#X?.(!1),e}catch(e){return this.#X?.(!1),o.error("PlacementBuilder: Error during redeem",e),{status:"none"}}}}function k(e,t){let n=e;for(const[e,i]of Object.entries(t))n=n.replace(`:${e}`,encodeURIComponent(i));return n}const E="2.0.0";class ${#ne;#ie;#re;constructor(e,t){this.#ne=e,this.#ie=t,this.#re=new Map}setApiKey(e){this.#ne=e}setBaseURL(e){this.#ie=e}async get(e,t){return this.#oe({url:`${this.#ie}${e}`,options:{method:"GET",...t},apiKey:this.#ne})}async post(e,t,n){return this.#oe({url:`${this.#ie}${e}`,options:{method:"POST",body:t,...n},apiKey:this.#ne})}async patch(e,t,n){return this.#oe({url:`${this.#ie}${e}`,options:{method:"PATCH",body:t,...n},apiKey:this.#ne})}async#oe(e){const{url:t,options:n,apiKey:i}=e,r=this.#se(t,n),s=this.#re.get(r);if(s)return o.debug("Deduplicating request:",t),s;const a=this.#ae(t,n,i);this.#re.set(r,a);try{return await a}finally{this.#re.delete(r)}}async#ae(e,t,n){const i=t.retries??3;let r=null;for(let s=0;s<=i;s++)try{if(s>0){const e=1e3*Math.pow(2,s-1);o.debug(`Retrying request (attempt ${s}/${i}) after ${e}ms`),await this.#$(e)}return await this.#le(e,t,n)}catch(e){if(r=e,this.#de(e))throw e;if(s===i)throw o.error(`Request failed after ${i} retries:`,e),e;o.warn("Request failed, will retry:",e)}throw r??new Error("Request failed")}async#le(e,t,n){const i=t.timeout??3e4,r=new AbortController,s=setTimeout(()=>r.abort(),i);try{const i={"X-API-Key":n,"X-Platform":"web","Content-Type":"application/json","User-Agent":`Encore-Web-SDK/${E}`,...t.headers},s={method:t.method,headers:i,signal:r.signal};t.body&&(s.body=JSON.stringify(t.body)),o.debug(`${t.method} ${e}`);const a=await fetch(e,s);return a.ok||await this.#ce(a),await a.json()}catch(e){if(e instanceof Error){if("AbortError"===e.name)throw new Error("Request timeout");if(this.#he(e))throw new Error(`Network error: ${e.message}`)}if(e instanceof Error)throw e;throw new Error(String(e))}finally{clearTimeout(s)}}async#ce(e){let n,i=`HTTP ${e.status}: ${e.statusText}`;try{const t=await e.json();"object"==typeof t&&null!==t&&"string"==typeof t.error&&(i=t.error,n="string"==typeof t.code?t.code:void 0)}catch{}const r=n?{code:n,message:i}:{code:t.API_ERROR,statusCode:e.status,message:i},o=new Error(i);throw Object.assign(o,r),o}#de(e){if("object"==typeof e&&null!==e&&"statusCode"in e&&void 0!==e.statusCode){const t=e.statusCode;return t>=400&&t<500}return!1}#he(e){return e.message.includes("fetch")||e.message.includes("network")||e.message.includes("ECONNREFUSED")||e.message.includes("ETIMEDOUT")}#se(e,t){const n=t.body?JSON.stringify(t.body):"";return`${t.method}:${e}:${n}`}#$(e){return new Promise(t=>setTimeout(t,e))}#ue(e){switch(e){case"pending_conversion":case"provisional_granted":default:return"pending";case"verified":case"final_granted":return"verified";case"expired":case"failed":return"failed"}}async fetchOffers(e,t,n){try{const i=await this.post("/offers/search",{userId:e,attributes:t,platform:"web",sdkVersion:E,...n&&{placementId:n.slice(0,64)}}),r=[];for(const e of i.offers){const t=e.creatives??[];for(const n of t){const t={id:n.id,title:n.title,description:n.quickInstructions??n.subtitle??n.description??"",imageUrl:n.primaryImageUrl,ctaText:n.ctaText,advertiserUrl:n.destinationUrl??e.destinationUrl??"",campaignId:e.id,creativeId:n.id,quickInstructions:n.quickInstructions??"",instructions:(n.instructions??[]).map(e=>({title:e.title,subtitle:e.subtitle,...null!=e.ctaButtonText&&{ctaButtonText:e.ctaButtonText}})),tooltipText:n.tooltipText??"",...e.organization?.name&&{merchantName:e.organization.name},...e.perk&&{perk:e.perk},...n.logoUrl&&{logoUrl:n.logoUrl},...n.trackingParameters&&{trackingParameters:Object.fromEntries(Object.entries(n.trackingParameters).filter(([,e])=>"string"==typeof e||"number"==typeof e||"boolean"==typeof e).map(([e,t])=>[e,String(t)]))}};r.push(t)}}return{offers:r}}catch(e){throw o.error("Failed to fetch offers:",e),e}}async startTransaction(e,t,n){try{return await this.post("/transactions",{campaignId:e,userId:t,...n&&"churn-intervention"!==n&&{useCase:n}})}catch(e){throw o.error("Failed to start transaction:",e),e}}async sendGrantSignal(e,t){try{const n=function(e){return k("/transactions/:transactionId/signals",{transactionId:e})}(e);return(await this.post(n,{grantType:t})).success}catch(e){throw o.error("Failed to send grant signal:",e),e}}async fetchEntitlements(e){try{const t=await this.get(`/entitlements?userId=${encodeURIComponent(e)}`),n=[],i=[{details:t.provisional,scope:"provisional"},{details:t.verified,scope:"verified"}];for(const{details:e,scope:t}of i)if(e){if(e.freeTrial&&n.push({type:"freeTrial",transactionId:"",scope:t,grantedAt:e.freeTrial.startedAt??"",expiresAt:e.freeTrial.expiresAt??""}),e.discounts)for(const i of e.discounts)n.push({type:"discount",value:i.value,unit:i.unit,transactionId:"",scope:t,grantedAt:"",expiresAt:i.expiresAt??""});e.credits&&n.push({type:"credit",value:e.credits.totalAmount,unit:"dollars",transactionId:"",scope:t,grantedAt:"",expiresAt:e.credits.expiresAt??""})}return o.debug("Transformed entitlements response",{totalCount:n.length}),n}catch(e){throw o.error("Failed to fetch entitlements:",e),e}}async checkVerificationStatus(e){try{const t=function(e){return k("/transactions/:transactionId/status",{transactionId:e})}(e),n=await this.get(t);return{status:this.#ue(n.status),raw:n.status}}catch(e){throw o.error("Failed to check verification status:",e),e}}async initUser(e,t){try{return await this.post("/users",{userId:e,...t&&{attributes:t}})}catch(e){throw o.error("Failed to init user:",e),e}}async identifyUser(e,t,n){try{return await this.patch("/users/identify",{currentUserId:e,newUserId:t,...n&&{attributes:n}})}catch(e){throw o.error("Failed to identify user:",e),e}}async fetchConfig(e,t,n){try{const i=`/config?userId=${encodeURIComponent(e)}&sdkVersion=${encodeURIComponent(t)}`+(n&&"churn-intervention"!==n?`&useCase=${encodeURIComponent(n)}`:"");return await this.get(i)}catch(e){throw o.error("Failed to fetch config:",e),e}}async reportError(e){try{const t="undefined"!=typeof window?window.location.hostname:"unknown";await this.post("/errors",{errorType:e.errorType,message:e.message,context:e.context,severity:e.severity??"error",timestamp:(new Date).toISOString(),platform:"web",sdkVersion:E,appBundleId:t,...e.stackTrace&&{stackTrace:e.stackTrace},...e.userId&&{userId:e.userId},...e.metadata&&{metadata:e.metadata}})}catch(e){o.warn("Failed to report error (non-blocking):",e)}}async revokeEntitlements(e){try{return(await this.post("/entitlements/revoke",{userId:e})).success}catch(e){throw o.error("Failed to revoke entitlements:",e),e}}}function T(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1_$2").toLowerCase()}function S(e){const t={};for(const[n,i]of Object.entries(e))t[T(n)]=i;return t}function A(e,t=w()){const n="undefined"!=typeof window?window.location.hostname:"unknown";return{event_id:t,event_name:e.name,event_timestamp:(new Date).toISOString(),distinct_id:e.distinctId,properties:{...S(e.properties),platform:"web",sdk_version:E,app_bundle_id:n}}}function U(e,t,n,i){return fetch(`${e}/events`,{method:"POST",...i?.keepalive&&{keepalive:!0},headers:{"X-Analytics-Key":t,"X-Platform":"web","Content-Type":"application/json"},body:JSON.stringify(n)})}const L=new Set(["user_identified"]);class N{#ne;#pe;#s;constructor(e,t,n){this.#ne=e,this.#pe=t,this.#s=n??null}track(e,t){try{if(this.#me(e))return;const n=A(e);U(this.#pe,this.#ne,n,t).catch(t=>{o.warn("Analytics event dispatch failed",{eventName:e.name,error:t})})}catch{}}#me(e){if(!this.#s||!L.has(e.name))return!1;const t=`analytics_dedup_${e.name}_${e.distinctId}`;try{const e=this.#s.get(t),n=Date.now();return"number"==typeof e&&n-e<864e5||(this.#s.set(t,n),!1)}catch{return!1}}}async function R(e,t){const n=`${e}:${t}`,i=(new TextEncoder).encode(n),r=new Uint8Array(await crypto.subtle.digest("SHA-256",i));let o=0n;for(let e=0;e<8;e++)o=o<<8n|BigInt(r[e]??0);return Number(o%100n)}const O="ncl_assignment",M="ncl_exposure_marker";class D{#s;#fe;#ge;#t=null;constructor(e,t,n){this.#s=e,this.#fe=t,this.#ge=n}setConfig(e){this.#t=e??null}async getCohort(){try{return await this.#be()}catch(e){return o.warn("[Experiment] getCohort failed — treating as notEnrolled",e),"notEnrolled"}}async#be(){const e=this.#fe();if(!e)return"notEnrolled";const t=this.#t;if(!t?.enabled)return"notEnrolled";const n=this.#s.get(O);if(n?.appAccountId===e&&n?.assignmentVersion===t.assignmentVersion)return n.cohort;if("undefined"==typeof crypto||!crypto.subtle)return o.warn("[Experiment] NCL disabled — crypto.subtle unavailable (page is not a secure context: serve over HTTPS)"),"notEnrolled";const i=await R(e,t.assignmentVersion),r=function(e,t){return e<t?"treatment":"control"}(i,t.rolloutPct);return this.#s.set(O,{cohort:r,appAccountId:e,assignmentVersion:t.assignmentVersion}),o.debug("[Experiment] Assigned NCL cohort",{cohort:r,version:t.assignmentVersion,rolloutPct:t.rolloutPct,bucket:i}),r}logExposure(e){if("notEnrolled"===e)return;const t=this.#t,n=this.#fe();if(!t||!n)return;const i=`${n}:${t.assignmentVersion}`;if(this.#s.get(M)===i)return;const r=function(e,t){return{name:"sdk_experiment_exposure",distinctId:e,properties:{experiment:t.experiment,cohort:t.cohort,assignment_version:t.assignmentVersion,app_account_id:e}}}(n,{experiment:"ncl",cohort:e,assignmentVersion:t.assignmentVersion}),o=w();this.#ge.enqueue(r,o)&&this.#s.set(M,i)}async getState(){const e=this.#fe(),t=this.#t,n=await this.getCohort();let i=null;if(e&&t?.enabled&&"undefined"!=typeof crypto&&crypto.subtle)try{i=await R(e,t.assignmentVersion)}catch{i=null}const r=!!e&&!!t&&this.#s.get(M)===`${e}:${t.assignmentVersion}`;return{experiment:"ncl",enabled:t?.enabled??!1,rolloutPct:t?.rolloutPct??null,assignmentVersion:t?.assignmentVersion??null,appAccountId:e,bucket:i,cohort:n,exposureLogged:r}}}const P="ncl_exposure_outbox";class _{#ne;#pe;#s;#ve=new Set;constructor(e,t,n){this.#ne=e,this.#pe=t,this.#s=n,this.#z()}enqueue(e,t){const n=A(e,t),i=this.#ye();i.some(e=>e.event_id===t)||(i.push(n),this.#we(i));const r=this.#ye().some(e=>e.event_id===t);return this.#xe(n),r}flush(){for(const e of this.#ye())this.#xe(e)}async#xe(e){if(!this.#ve.has(e.event_id)){this.#ve.add(e.event_id);try{const t=await U(this.#pe,this.#ne,e,{keepalive:!0});t.ok?this.#Ie(e.event_id):o.warn("Exposure delivery non-OK, will retry",{status:t.status})}catch(e){o.warn("Exposure delivery failed, will retry",{error:e})}finally{this.#ve.delete(e.event_id)}}}#Ie(e){const t=this.#ye().filter(t=>t.event_id!==e);this.#we(t)}#ye(){return this.#s.get(P)??[]}#we(e){this.#s.set(P,e)}#z(){"undefined"!=typeof window&&"undefined"!=typeof document&&(window.addEventListener("pagehide",()=>this.flush()),document.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&this.flush()}))}}class z{#Ce;constructor(){this.#Ce=new Map}on(e,t){this.#Ce.has(e)||this.#Ce.set(e,new Set);const n=this.#Ce.get(e);if(!n)throw new Error(`Failed to get handlers for event '${String(e)}'`);return n.add(t),o.debug(`Event listener added for '${String(e)}'`),()=>{this.off(e,t)}}off(e,t){const n=this.#Ce.get(e);n&&(n.delete(t),o.debug(`Event listener removed for '${String(e)}'`),0===n.size&&this.#Ce.delete(e))}emit(e,t){const n=this.#Ce.get(e);if(n&&0!==n.size){o.debug(`Emitting event '${String(e)}'`,{listenerCount:n.size});for(const i of n)try{i(t)}catch(t){o.error(`Error in event handler for '${String(e)}':`,t)}}}removeAllListeners(e){e?(this.#Ce.delete(e),o.debug(`All listeners removed for '${String(e)}'`)):(this.#Ce.clear(),o.debug("All event listeners removed"))}listenerCount(e){return this.#Ce.get(e)?.size??0}hasListeners(e){const t=this.#Ce.get(e);return void 0!==t&&t.size>0}}const F="encore-sdk-styles",B="encore-",V="767px",H="12px",q="20px",K="40px";function j(e={}){if(document.getElementById(F))o.debug("Encore styles already injected");else try{const t=document.createElement("style");t.id=F,t.textContent=function(e={}){const t=function(e){const t=[];return e.primaryColor&&t.push(`--encore-primary-color: ${e.primaryColor};`),e.backgroundColor&&t.push(`--encore-bg-color: ${e.backgroundColor};`),e.textColor&&t.push(`--encore-text-color: ${e.textColor};`),e.borderRadius&&t.push(`--encore-border-radius: ${e.borderRadius};`),e.fontFamily&&t.push(`--encore-font-family: ${e.fontFamily};`),t.join("\n ")}(e);return`\n /* CSS Custom Properties (Theme Variables) */\n :root {\n ${t||"/* No custom theme variables */"}\n --encore-primary-color: ${e.primaryColor??"#4F46E5"};\n --encore-bg-color: ${e.backgroundColor??"#ffffff"};\n --encore-text-color: ${e.textColor??"#1F2937"};\n --encore-text-muted: color-mix(in srgb, var(--encore-text-color) 60%, transparent);\n /* One step darker than muted, lighter than full text — lead-in copy that must\n read above captions without competing with the page title. */\n --encore-text-secondary: color-mix(in srgb, var(--encore-text-color) 78%, transparent);\n --encore-surface-color: color-mix(in srgb, var(--encore-text-color) 5%, var(--encore-bg-color));\n --encore-border-color: color-mix(in srgb, var(--encore-text-color) 10%, var(--encore-bg-color));\n --encore-border-radius: ${e.borderRadius??"12px"};\n /* Semantic success hue (thankYou status check). Deliberately independent of the\n accent color: the check states a payment fact; the accent sells the offer CTA. */\n --encore-success-color: #159a5b;\n --encore-font-family: ${e.fontFamily??'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'};\n }\n\n /* Backdrop */\n .${B}backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.55);\n backdrop-filter: blur(2px);\n z-index: 9998;\n opacity: 0;\n transition: opacity 250ms cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .${B}backdrop.${B}visible {\n opacity: 1;\n }\n\n /* Box-sizing reset scoped to the SDK modal so padding/borders never cause\n horizontal overflow on host pages that don't set border-box globally. */\n .${B}modal,\n .${B}modal *,\n .${B}backdrop {\n box-sizing: border-box;\n }\n\n /* Modal Container */\n .${B}modal {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 9999;\n background-color: var(--encore-bg-color);\n border-radius: 20px;\n box-shadow: 0 24px 48px -12px rgba(0, 0, 0, 0.18), 0 12px 24px -8px rgba(0, 0, 0, 0.08);\n max-width: 800px;\n max-height: 90vh;\n width: calc(100% - 32px);\n overflow-x: hidden;\n overflow-y: auto;\n opacity: 0;\n transform: translate(-50%, -50%) scale(0.96);\n transition: opacity 250ms cubic-bezier(0.4, 0, 0.2, 1), transform 250ms cubic-bezier(0.4, 0, 0.2, 1);\n font-family: var(--encore-font-family);\n color: var(--encore-text-color);\n /* Anchor the inherited typographic properties at the modal root so host\n page styles can't cascade in. Children inherit OUR values, not the\n host's. Interim guard until Shadow DOM — see web-sdk-style-isolation.md. */\n font-size: 16px;\n font-weight: 400;\n font-style: normal;\n line-height: 1.5;\n letter-spacing: normal;\n word-spacing: normal;\n text-align: left;\n text-transform: none;\n text-indent: 0;\n white-space: normal;\n text-shadow: none;\n }\n\n .${B}modal.${B}visible {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n\n /* Close Button */\n .${B}close-button {\n position: absolute;\n top: 20px;\n right: 20px;\n width: 40px;\n height: 40px;\n border: none;\n background-color: var(--encore-surface-color);\n cursor: pointer;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--encore-text-muted);\n transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n z-index: 10;\n }\n\n .${B}close-button:hover {\n background-color: var(--encore-bg-color);\n color: var(--encore-text-color);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n transform: scale(1.05);\n }\n\n .${B}close-button:active {\n transform: scale(0.98);\n }\n\n .${B}close-button:hover {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n .${B}close-button svg {\n width: 18px;\n height: 18px;\n }\n\n /* Body scroll lock */\n body.${B}no-scroll {\n overflow: hidden;\n }\n\n /* Modal Header */\n .${B}modal-header {\n padding: 24px 24px 16px;\n border-bottom: 1px solid var(--encore-border-color);\n }\n\n .${B}modal-title {\n margin: 0;\n font-size: 24px;\n font-weight: 700;\n line-height: 1.25;\n color: var(--encore-text-color);\n }\n\n .${B}modal-subtitle {\n margin: 8px 0 0;\n font-size: 14px;\n color: var(--encore-text-muted);\n line-height: 1.5;\n }\n\n /* Modal Body */\n .${B}modal-body {\n padding: 0;\n position: relative;\n isolation: isolate;\n }\n\n /* Carousel Container */\n .${B}carousel {\n display: flex;\n flex-direction: column;\n overflow: visible;\n }\n\n /* Carousel Header */\n .${B}carousel-header {\n padding: 40px 68px 28px;\n text-align: center;\n }\n\n .${B}carousel-title {\n font-size: 32px;\n font-weight: 510;\n line-height: 1.1;\n color: var(--encore-text-color);\n margin: 0 0 8px 0;\n letter-spacing: -0.3px;\n }\n\n .${B}carousel-subtitle {\n font-size: 16px;\n line-height: 1.2;\n color: var(--encore-text-color);\n margin: 0;\n font-weight: 400;\n }\n\n /* Carousel Cards Container - Horizontal Scroll */\n .${B}carousel-cards {\n display: flex;\n flex-direction: row;\n overflow-x: auto;\n overflow-y: hidden;\n scroll-behavior: smooth;\n scroll-snap-type: x mandatory;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n -ms-overflow-style: none;\n gap: 24px;\n padding: 24px 78px;\n position: relative;\n }\n\n .${B}carousel-cards::-webkit-scrollbar {\n display: none;\n }\n\n /* Carousel Item Wrapper. Only opacity ever animates (active-card dimming) — a\n short 200ms so the highlight reads as tracking the swipe, not trailing it. */\n .${B}carousel-item {\n flex: 0 0 426px;\n width: 426px;\n scroll-snap-align: center;\n scroll-snap-stop: always;\n opacity: 0.5;\n transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: auto;\n }\n\n /* Active carousel item */\n .${B}carousel-item.${B}active {\n opacity: 1;\n z-index: 1;\n }\n\n /* Offer Card */\n .${B}offer-card {\n display: flex;\n flex-direction: column;\n background: var(--encore-bg-color);\n border-radius: 16px;\n box-shadow: 0 2px 12px rgba(116, 112, 133, 0.16);\n overflow: hidden;\n }\n\n /* Offer Banner */\n .${B}offer-banner {\n width: 100%;\n background-color: transparent;\n position: relative;\n padding: 0;\n height: 180px;\n overflow: hidden;\n border-radius: 16px 16px 0 0;\n }\n\n .${B}offer-banner-image {\n width: 100%;\n height: 100%;\n display: block;\n object-fit: cover;\n background-color: var(--encore-surface-color);\n opacity: 0;\n transition: opacity 200ms ease-out;\n }\n\n .${B}offer-banner-image.${B}loaded {\n opacity: 1;\n }\n\n /* Info button on banner */\n .${B}banner-info-button {\n position: absolute;\n top: 20px;\n right: 20px;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background-color: var(--encore-surface-color);\n backdrop-filter: blur(6px);\n border: none;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 150ms ease;\n z-index: 3;\n padding: 0;\n }\n\n .${B}banner-info-button:hover {\n background-color: var(--encore-border-color);\n transform: scale(1.05);\n }\n\n .${B}banner-info-button:focus {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n .${B}banner-info-button svg {\n width: 14px;\n height: 14px;\n color: var(--encore-text-color);\n stroke-width: 2;\n }\n\n /* Tooltip */\n .${B}tooltip {\n position: fixed;\n background-color: rgba(28, 28, 32, 0.95);\n color: #ffffff;\n padding: 8px 12px;\n border-radius: 8px;\n font-size: 12px;\n line-height: 1.4;\n font-weight: 500;\n z-index: 10000;\n pointer-events: none;\n opacity: 0;\n transform: translateY(4px);\n transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1), transform 200ms cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n white-space: normal;\n word-wrap: break-word;\n }\n\n .${B}tooltip-visible {\n opacity: 1;\n transform: translateY(0);\n }\n\n /* Tooltip arrow (optional - using pseudo-element) */\n .${B}tooltip::after {\n content: '';\n position: absolute;\n width: 0;\n height: 0;\n border-style: solid;\n }\n\n .${B}tooltip-top::after {\n bottom: -4px;\n left: 50%;\n transform: translateX(-50%);\n border-width: 4px 4px 0 4px;\n border-color: rgba(28, 28, 32, 0.95) transparent transparent transparent;\n }\n\n .${B}tooltip-bottom::after {\n top: -4px;\n left: 50%;\n transform: translateX(-50%);\n border-width: 0 4px 4px 4px;\n border-color: transparent transparent rgba(28, 28, 32, 0.95) transparent;\n }\n\n .${B}tooltip-left::after {\n right: -4px;\n top: 50%;\n transform: translateY(-50%);\n border-width: 4px 0 4px 4px;\n border-color: transparent transparent transparent rgba(28, 28, 32, 0.95);\n }\n\n .${B}tooltip-right::after {\n left: -4px;\n top: 50%;\n transform: translateY(-50%);\n border-width: 4px 4px 4px 0;\n border-color: transparent rgba(28, 28, 32, 0.95) transparent transparent;\n }\n\n /* Offer Footer */\n .${B}offer-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-top: 1px solid var(--encore-border-color);\n gap: 12px;\n min-height: 74px;\n }\n\n /* Offer Merchant */\n .${B}offer-merchant {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n }\n\n .${B}offer-logo {\n width: 42px;\n height: 42px;\n border-radius: 8px;\n object-fit: contain;\n background-color: var(--encore-surface-color);\n flex-shrink: 0;\n opacity: 0;\n transition: opacity 200ms ease-out;\n }\n\n .${B}offer-logo.${B}loaded {\n opacity: 1;\n }\n\n .${B}offer-merchant-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .${B}offer-merchant-name {\n font-size: 16px;\n font-weight: 590;\n color: var(--encore-text-color);\n line-height: 1.1;\n letter-spacing: -1px;\n }\n\n .${B}offer-merchant-desc {\n font-size: 14px;\n color: var(--encore-text-muted);\n line-height: 1.2;\n letter-spacing: -1px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* Claim Button */\n .${B}button-claim {\n padding: 10px 16px;\n height: 34px;\n min-width: 78px;\n font-size: 14px;\n font-weight: 590;\n border-radius: 999px;\n border: none;\n background: var(--encore-primary-color);\n color: white;\n cursor: pointer;\n transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);\n font-family: var(--encore-font-family);\n white-space: nowrap;\n line-height: 1.2;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .${B}button-claim:hover:not(:disabled) {\n background: color-mix(in srgb, var(--encore-primary-color) 85%, black);\n transform: translateY(-1px);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--encore-primary-color) 30%, transparent);\n }\n\n .${B}button-claim:active {\n transform: translateY(0);\n background: color-mix(in srgb, var(--encore-primary-color) 75%, black);\n }\n\n .${B}button-claim:focus {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n .${B}button-claim:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n }\n\n /* Carousel Navigation */\n .${B}carousel-navigation {\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 40px;\n padding: 28px 32px 32px;\n }\n\n .${B}nav-button {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: 1px solid var(--encore-text-muted);\n background-color: var(--encore-bg-color);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--encore-text-muted);\n transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0;\n }\n\n .${B}nav-button:hover:not(:disabled) {\n border-color: var(--encore-primary-color);\n color: var(--encore-primary-color);\n transform: scale(1.05);\n }\n\n .${B}nav-button:active:not(:disabled) {\n transform: scale(0.95);\n }\n\n .${B}nav-button:focus {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n .${B}nav-button svg {\n width: 20px;\n height: 20px;\n stroke-width: 2;\n }\n\n .${B}nav-button:not(:disabled) {\n border-color: var(--encore-primary-color);\n color: var(--encore-primary-color);\n }\n\n .${B}nav-button:disabled,\n .${B}nav-button.${B}disabled {\n opacity: 1;\n cursor: not-allowed;\n border-color: var(--encore-text-muted);\n color: var(--encore-text-muted);\n }\n\n .${B}nav-button:disabled:hover,\n .${B}nav-button.${B}disabled:hover {\n border-color: var(--encore-text-muted);\n color: var(--encore-text-muted);\n transform: none;\n }\n\n /* Loading State */\n .${B}loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 48px;\n }\n\n .${B}spinner {\n width: 40px;\n height: 40px;\n border: 3px solid var(--encore-border-color);\n border-top-color: var(--encore-primary-color);\n border-radius: 50%;\n animation: ${B}spin 0.8s linear infinite;\n }\n\n @keyframes ${B}spin {\n to { transform: rotate(360deg); }\n }\n\n /* Responsive: Mobile (0-767px) — full-screen vertical sheet */\n @media (max-width: ${V}) {\n /* Modal becomes a full-screen sheet. dvh fallback so mobile browser URL\n bars don't clip the bottom. It is a flex column so the height\n propagates down to the scrollable cards region. */\n .${B}modal {\n display: flex;\n flex-direction: column;\n inset: 0;\n top: 0;\n left: 0;\n transform: none;\n width: 100%;\n height: 100vh;\n height: 100dvh;\n max-width: none;\n max-height: none;\n border-radius: 0;\n margin: 0;\n overflow: hidden;\n }\n\n /* Fade only — override the desktop scale/translate transition. */\n .${B}modal.${B}visible {\n transform: none;\n }\n\n /* Body fills the sheet and forwards the height to the carousel.\n min-height:0 lets the inner scroll container actually overflow. */\n .${B}modal-body {\n display: flex;\n flex: 1 1 auto;\n min-height: 0;\n flex-direction: column;\n }\n\n /* Carousel fills the body so the cards region can flex to fill height. */\n .${B}carousel {\n flex: 1 1 auto;\n min-height: 0;\n overflow: hidden;\n }\n\n /* Sticky header pinned while the list scrolls; respect the notch. 64px side\n The close button is an absolute overlay (top 16 + 40px tall), so the\n header reserves that band VERTICALLY: the title starts below the X and\n spans the full sheet width with slim symmetric side padding — no dead\n 64px gutters squeezing the headline into the X's row. */\n .${B}carousel-header {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--encore-bg-color);\n padding: calc(64px + env(safe-area-inset-top)) 24px 20px;\n }\n\n .${B}carousel-title {\n font-size: 22px;\n }\n\n .${B}carousel-subtitle {\n font-size: 14px;\n }\n\n /* Vertical, full-height scrollable feed. */\n .${B}carousel-cards {\n flex: 1 1 auto;\n min-height: 0;\n flex-direction: column;\n overflow-x: hidden;\n overflow-y: auto;\n scroll-snap-type: none;\n overscroll-behavior: contain;\n gap: 16px;\n padding: 16px 16px calc(16px + env(safe-area-inset-bottom));\n }\n\n /* Full-width stacked cards, no carousel dimming or snapping.\n overflow:visible so each card's shadow isn't clipped by the item box. */\n .${B}carousel-item {\n flex: 0 0 auto;\n width: 100%;\n opacity: 1;\n scroll-snap-align: none;\n overflow: visible;\n }\n\n /* Keep the rounded card + visible shadow on mobile (matches design). */\n .${B}offer-card {\n width: 100%;\n max-width: 100%;\n border-radius: 16px;\n box-shadow: 0 4px 16px rgba(116, 112, 133, 0.22);\n border: 1px solid var(--encore-border-color);\n }\n\n /* Image fills the card edge-to-edge with rounded top corners\n (no inset padding, banner already clips to the card radius). */\n .${B}offer-banner {\n height: auto;\n padding: 0;\n border-radius: 16px 16px 0 0;\n }\n\n .${B}offer-banner-image {\n width: 100%;\n height: auto;\n display: block;\n }\n\n /* Logo + name + description on the left, small claim button pinned far\n right on a single row (no wrap / no full-width stacking). */\n .${B}offer-footer {\n flex-wrap: nowrap;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 14px 16px;\n }\n\n .${B}offer-merchant {\n flex: 1 1 auto;\n min-width: 0;\n }\n\n /* Small button hugging its label, to the far right. */\n .${B}button-claim {\n flex: 0 0 auto;\n width: auto;\n }\n\n /* No nav arrows in vertical mode. */\n .${B}carousel-navigation {\n display: none;\n }\n\n /* Keep the close button reachable inside the sticky header / below the notch. */\n .${B}close-button {\n top: calc(16px + env(safe-area-inset-top));\n right: 16px;\n }\n\n /* Paged carousel keeps its nav bubbles on mobile (the display:none above is\n the responsive/'list' vertical mode only). */\n .${B}carousel-paged .${B}carousel-navigation {\n display: flex;\n }\n\n /* NOTE: the thankYou layout's own mobile overrides live in a dedicated media\n block AFTER the base thankYou section (source order matters at equal\n specificity — a media query adds none). */\n }\n\n /* Responsive: Tablet (768-1023px) */\n @media (min-width: 768px) and (max-width: 1023px) {\n .${B}modal {\n max-width: 600px;\n }\n }\n\n /* Responsive: Desktop (1024px+) */\n @media (min-width: 1024px) {\n .${B}modal {\n max-width: 600px;\n }\n }\n\n /* Accessibility: High Contrast Mode */\n @media (prefers-contrast: high) {\n .${B}button {\n border: 2px solid currentColor;\n }\n }\n\n /* Accessibility: Reduced Motion */\n @media (prefers-reduced-motion: reduce) {\n .${B}backdrop,\n .${B}modal,\n .${B}button,\n .${B}close-button {\n transition: none;\n }\n\n .${B}spinner {\n animation: none;\n }\n }\n\n /* Focus Visible (for keyboard navigation) */\n .${B}button:focus-visible,\n .${B}close-button:focus-visible {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n\n /* ===== Paged carousel (thankYou layout) ===== */\n /* Always-horizontal, one-card-per-view, scroll-snap paged — at every breakpoint.\n Scoped under carousel-paged so the responsive ('list') carousel rules are\n untouched. Each page is slightly narrower than the viewport so the neighboring\n cards PEEK in at the edges at reduced opacity — the same visual grammar as the\n list carousel: "there are more offers, swipe". Desktop caps the page width so\n the peek survives wide modals (no 426px hardcode). */\n .${B}carousel-paged .${B}carousel-cards {\n flex-direction: row;\n align-items: center;\n overflow-x: auto;\n overflow-y: hidden;\n scroll-snap-type: x mandatory;\n /* auto, NOT the base 'smooth': smooth scroll-behavior makes the browser ANIMATE\n its own snap re-alignments (e.g. when an async banner image loads and reflows\n the row mid-gesture), which reads as a laggy post-swipe snap. Native gesture\n snapping is untouched, and programmatic paging still requests smooth\n explicitly via scrollIntoView's behavior option. */\n scroll-behavior: auto;\n gap: 12px;\n /* Horizontal padding is 0 — the symmetric first/last-item gutters below own\n the side spacing so the ACTIVE card is always centered. Vertical padding is\n shadow room: overflow-y:hidden would clip the card shadows without it. */\n padding: ${q} 0;\n /* One "page" = the card slot. 64px total side allowance → 32px gutter each\n side (a ~20px neighbor-peek after the 12px gap at the 375px design width);\n the 480px cap keeps the peek alive in wide modals. Declared once here and\n shared by the item basis and the centering gutters so they can never drift. */\n --encore-page-size: min(100% - 64px, 480px);\n }\n\n .${B}carousel-paged .${B}carousel-item {\n /* Size via width (flex-basis:auto → width), NOT a math function inside the\n flex shorthand: engines that choke on functions in the shorthand drop the\n whole declaration and fall back to the base 426px item — which is exactly a\n clipped, overflowing card on a 375px phone. width + the max-width guard\n make overflow impossible at any breakpoint. */\n flex: 0 0 auto;\n width: var(--encore-page-size);\n max-width: 100%;\n /* Peek legibility: light creatives on the white sheet disappear below ~0.5,\n killing the "more offers" affordance. 0.65 keeps the active card clearly\n dominant while the neighbors stay readable. */\n opacity: 0.65;\n scroll-snap-align: center;\n scroll-snap-stop: always;\n overflow: visible;\n }\n\n /* Symmetric centering gutters: first/last pages get margins of exactly\n (container − page) / 2, so card 0 RESTS CENTERED at scrollLeft 0 (empty gutter\n left, peek right), mid-cards show symmetric peeks, and the last card can reach\n center too. Both the margin percentage and the flex-basis percentage resolve against the same\n container content width, so the math is exact at any size — the natural resting\n position IS the snap position and mount needs no scroll correction. */\n .${B}carousel-paged .${B}carousel-item:first-child {\n margin-inline-start: calc((100% - var(--encore-page-size)) / 2);\n }\n\n .${B}carousel-paged .${B}carousel-item:last-child {\n margin-inline-end: calc((100% - var(--encore-page-size)) / 2);\n }\n\n .${B}carousel-paged .${B}carousel-item.${B}active {\n opacity: 1;\n }\n\n .${B}carousel-paged .${B}offer-card {\n max-width: 460px;\n margin: 0 auto;\n border: 1px solid var(--encore-border-color);\n box-shadow: 0 4px 16px rgba(116, 112, 133, 0.18);\n }\n\n /* Fluid banner: natural aspect, scales with the card. */\n .${B}carousel-paged .${B}offer-banner {\n height: auto;\n }\n\n .${B}carousel-paged .${B}offer-banner-image {\n width: 100%;\n height: auto;\n display: block;\n }\n\n /* Nav row: centered DIRECTLY beneath the cards — these chevrons are carousel\n controls and must read as part of the carousel unit, so they hug it (never\n bottom-anchored away from it). Comfortable 48px circular tap targets in normal\n flow — no overlay, no z-index games. Rhythm: cards→nav gap = the cards' bottom\n shadow gutter + 4px here (16px total inside thankYou); the 20px bottom padding\n is the nav→footer gap (and the sheet's bottom inset when no trust footer is\n configured). */\n .${B}carousel-paged .${B}carousel-navigation {\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 24px;\n padding: 4px 20px ${q};\n }\n\n .${B}carousel-paged .${B}nav-button {\n width: 48px;\n height: 48px;\n background-color: var(--encore-bg-color);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n /* Neutral idle bubble (the accent belongs to the Claim CTA); accent only on hover. */\n .${B}carousel-paged .${B}nav-button:not(:disabled) {\n border-color: var(--encore-border-color);\n color: var(--encore-text-color);\n }\n\n .${B}carousel-paged .${B}nav-button:hover:not(:disabled) {\n border-color: var(--encore-primary-color);\n color: var(--encore-primary-color);\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);\n }\n\n .${B}carousel-paged .${B}nav-button svg {\n width: 22px;\n height: 22px;\n }\n\n .${B}carousel-paged .${B}nav-button:disabled,\n .${B}carousel-paged .${B}nav-button.${B}disabled {\n opacity: 0.35;\n box-shadow: none;\n border-color: var(--encore-border-color);\n color: var(--encore-text-muted);\n }\n\n /* ===== Thank-You layout (placement layout: 'thankYou') ===== */\n /* Pure chrome around the unchanged carousel: status block above, trust footer\n below. Desktop: normal flow inside the content-sized modal (the stack is\n inherently tight there). Mobile: joins the sheet's flex chain — the content\n stack is optically centered while the trust footer stays bottom-pinned. */\n .${B}thankyou {\n display: flex;\n flex-direction: column;\n }\n\n /* SUCCESS UNIT — check → title → receipt as ONE tight visual group. Internal\n gaps are deliberately tight (proximity = grouping); the heading below is the\n sheet's single focal point. No bottom padding: the 32px section break lives\n on the intro header so it is owned in exactly one place. */\n .${B}thankyou-status {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n padding: ${K} 24px 0;\n }\n\n /* "Receipt stamp" check: deep-green disc in a soft halo. Calm by design — no\n pulse, no gradient (a payment fact, stated quietly). Margin-bottom is the\n 12px halo ring + the 12px check→title step of the ladder. */\n .${B}thankyou-check {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: 50%;\n background: var(--encore-success-color);\n color: #ffffff;\n box-shadow: 0 0 0 12px color-mix(in srgb, var(--encore-success-color) 14%, var(--encore-bg-color));\n margin: 0 0 calc(12px + ${H});\n }\n\n .${B}thankyou-check svg {\n width: 24px;\n height: 24px;\n }\n\n /* The ONLY large/heavy type on the sheet — everything else defers to it. */\n .${B}thankyou-title {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n line-height: 1.25;\n letter-spacing: -0.2px;\n color: var(--encore-text-color);\n overflow-wrap: anywhere;\n }\n\n /* Receipt line — sits tight under the title (same statement, quieter voice). */\n .${B}thankyou-subtitle {\n margin: 4px 0 0;\n font-size: 14px;\n line-height: 1.5;\n color: var(--encore-text-muted);\n overflow-wrap: anywhere;\n }\n\n /* OFFER CONTEXT — inside thankYou the carousel's own header is a lead-in, not a\n second headline: medium weight, headline-free scale. The title/subtitle pair\n reads as ONE two-line paragraph: identical size, weight, color and line-height\n on both lines, zero margin between them, so the shared 1.35 leading IS the\n inter-line gap — no size, weight or color tier splits them apart. (They stay\n two DOM nodes: the subtitle swaps per-card via textContent.) The pair sits 32px\n BELOW the success unit but only 16px ABOVE the cards (via the cards' top\n padding), so proximity ties it to the offers it introduces. Scoped here so the\n 'list' layout CSS stays byte-identical. position:static defeats the mobile\n sticky-header rule (the status block above already handles the notch). */\n .${B}thankyou .${B}carousel-header {\n position: static;\n padding: 32px 24px 0;\n }\n\n .${B}thankyou .${B}carousel-title {\n margin: 0;\n font-size: 14px;\n font-weight: 500;\n line-height: 1.35;\n letter-spacing: 0;\n color: var(--encore-text-secondary);\n }\n\n .${B}thankyou .${B}carousel-subtitle {\n font-size: 14px;\n font-weight: 500;\n line-height: 1.35;\n color: var(--encore-text-secondary);\n }\n\n /* Context → cards 16px, cards → nav 16px (12px shadow gutter + the nav row's 4px\n top padding): the carousel unit is bound tight to its lead-in above and its\n controls below. The 12px bottom gutter still clears the visible card shadow. */\n .${B}thankyou .${B}carousel-cards {\n padding-top: 16px;\n padding-bottom: ${H};\n }\n\n /* Trust footer — a quiet 12px caption. Its 20px top gap is the nav row's bottom\n padding; 24px closes the sheet. */\n .${B}thankyou-footer {\n margin: 0;\n padding: 0 24px 24px;\n font-size: 12px;\n line-height: 1.5;\n text-align: center;\n color: var(--encore-text-muted);\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n /* Thank-You on the mobile full-screen sheet: join the sheet's flex chain — the\n content stack floats optically centered above the bottom-pinned trust footer,\n and the carousel keeps its internal scroll. Type + rhythm come from the base\n thankYou system above (375px IS its design target) — only sheet-specific bits\n are overridden. This block must stay AFTER the base thankYou rules: it wins by\n source order, not specificity. */\n @media (max-width: ${V}) {\n .${B}thankyou {\n flex: 1 1 auto;\n min-height: 0;\n }\n\n /* Full-height composition: everything except the trust footer is ONE tight,\n internally-rigid stack (success unit → context pair → cards → nav row),\n OPTICALLY CENTERED in the space above the bottom-pinned footer. Two\n zero-basis flex spaces own ALL the slack — this ::before above the stack\n (grow 1) and the carousel's ::after below it (grow 1.35, via the carousel's\n own grow factor) — an above:below ratio of 1:1.35 so the stack rests\n slightly high of true center. The stack's internal ladder gaps never\n stretch, so the composition compresses as a unit on short sheets and\n floats as a unit on tall ones. */\n .${B}thankyou::before {\n content: '';\n flex: 1 1 0;\n min-height: 0;\n }\n\n .${B}thankyou-status {\n flex: 0 0 auto;\n /* Real fixed margin above the check — the floor when the flex space above\n has fully compressed; the notch inset always survives. */\n padding-top: calc(${K} + env(safe-area-inset-top));\n }\n\n .${B}thankyou-check {\n width: 44px;\n height: 44px;\n }\n\n /* The carousel's grow factor IS the below-stack share: its extra height is\n absorbed entirely by the ::after spacer under the nav row, giving the\n 1:1.35 above:below split against the ::before above. Shrinks like before\n (basis auto, min-height 0) so short sheets compress the cards region. */\n .${B}thankyou .${B}carousel {\n flex: 1.35 1 auto;\n min-height: 0;\n }\n\n .${B}thankyou .${B}carousel::after {\n content: '';\n flex: 1 1 0;\n min-height: 0;\n }\n\n /* Nav hugs the cards (rigid ladder gap) — the old auto-margin bottom-anchor\n is gone: the arrows are carousel controls and read as part of the carousel\n unit, not footer furniture. */\n .${B}thankyou .${B}carousel-cards {\n flex: 0 1 auto;\n min-height: 0;\n }\n\n /* Pinned bar: tighter 12px insets than the in-flow desktop footer (the border\n already separates it), plus the home-indicator safe area. */\n .${B}thankyou-footer {\n flex: 0 0 auto;\n border-top: 1px solid var(--encore-border-color);\n background: var(--encore-bg-color);\n padding: 12px 20px calc(12px + env(safe-area-inset-bottom));\n }\n }\n\n /* ===== Reward parity with the native SDUI variant =====\n Tracks streaksCarousel.json — the SAME screen iOS and Android render — so a\n publisher running reward on more than one platform sees one design rather\n than three. Values are read off that file, not eyeballed from a screenshot:\n\n headline 28 / semibold, centered\n subheadline 16 / regular, centered\n offer card cornerRadius 24\n primary CTA cornerRadius 12 (deliberately NOT the pill the churn\n carousel uses — the variant is explicit)\n gutters 20 leading/trailing\n\n Scoped entirely to the --reward modifier: the payment-status thankYou keeps\n its own tighter scale (20/14), which is correct for a receipt and wrong for a\n celebration. Placed AFTER the mobile thankYou block so it wins on source\n order, matching the convention that block already documents. */\n .${B}thankyou--reward .${B}thankyou-title {\n font-size: 28px;\n font-weight: 600;\n line-height: 1.2;\n letter-spacing: -0.4px;\n text-align: center;\n }\n\n .${B}thankyou--reward .${B}thankyou-subtitle {\n font-size: 16px;\n font-weight: 400;\n line-height: 1.35;\n text-align: center;\n }\n\n .${B}thankyou--reward .${B}thankyou-status {\n padding-left: 20px;\n padding-right: 20px;\n }\n\n .${B}thankyou--reward .${B}offer-card {\n border-radius: 24px;\n }\n\n .${B}thankyou--reward .${B}button-claim {\n border-radius: 12px;\n }\n\n /* On the mobile full-screen sheet the variant is edge-to-edge (fullScreenCover,\n detent 1.0, no drag indicator) with 20px gutters and a 35px bottom inset.\n env() is added to the inset rather than replacing it so the 35 is honoured on\n devices without a home indicator too. */\n @media (max-width: ${V}) {\n .${B}thankyou--reward .${B}carousel-cards {\n padding-left: 20px;\n padding-right: 20px;\n }\n\n .${B}thankyou--reward .${B}thankyou-footer {\n padding-bottom: calc(35px + env(safe-area-inset-bottom));\n }\n }\n\n /* ===== Success Screen ===== */\n .${B}success-container {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 3rem 2rem;\n text-align: center;\n min-height: 400px;\n animation: ${B}fade-in 0.3s ease-out;\n }\n\n .${B}success-icon {\n width: 80px;\n height: 80px;\n background: var(--encore-primary-color);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 48px;\n color: white;\n margin-bottom: 1.5rem;\n animation: ${B}scale-in 0.5s ease-out;\n }\n\n .${B}success-title {\n font-size: 1.75rem;\n font-weight: 700;\n color: var(--encore-text-color);\n margin: 0 0 0.75rem 0;\n }\n\n .${B}success-subtitle {\n font-size: 1.125rem;\n color: var(--encore-text-muted);\n margin: 0 0 2rem 0;\n line-height: 1.6;\n }\n\n .${B}success-details {\n background: color-mix(in srgb, var(--encore-primary-color) 5%, transparent);\n border-radius: 12px;\n padding: 1.5rem;\n margin-bottom: 2rem;\n min-width: 280px;\n }\n\n .${B}success-detail {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.5rem 0;\n }\n\n .${B}success-detail:not(:last-child) {\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n }\n\n .${B}success-detail-label {\n font-size: 0.875rem;\n color: var(--encore-text-muted);\n font-weight: 500;\n }\n\n .${B}success-detail-value {\n font-size: 1rem;\n color: var(--encore-text-color);\n font-weight: 600;\n }\n\n .${B}success-button {\n background: var(--encore-primary-color);\n color: white;\n border: none;\n border-radius: var(--encore-border-radius);\n padding: 0.875rem 2.5rem;\n font-size: 1rem;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);\n }\n\n .${B}success-button:hover {\n transform: translateY(-2px);\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);\n }\n\n .${B}success-button:active {\n transform: translateY(0);\n }\n\n .${B}success-button:focus-visible {\n outline: 2px solid var(--encore-primary-color);\n outline-offset: 2px;\n }\n\n /* Redemption screen (phase 2 — redeem) */\n .${B}redemption-container {\n display: flex;\n flex-direction: column;\n max-width: 440px;\n margin: 0 auto;\n padding: 2rem 1.75rem;\n text-align: center;\n animation: ${B}fade-in 0.3s ease-out;\n }\n\n .${B}redemption-title {\n font-size: 1.6rem;\n font-weight: 700;\n color: var(--encore-text-color);\n margin: 0 0 0.5rem 0;\n line-height: 1.2;\n }\n\n .${B}redemption-subtitle {\n font-size: 1.05rem;\n color: var(--encore-text-muted);\n margin: 0 0 1.75rem 0;\n line-height: 1.5;\n }\n\n .${B}redemption-note {\n font-size: 0.8rem;\n color: var(--encore-text-muted);\n line-height: 1.5;\n margin: 1.25rem 0;\n }\n\n /* Reuses .encore-button-claim (the clean pill) — full-width + prominent. */\n .${B}redemption-cta {\n width: 100%;\n height: auto;\n min-height: 50px;\n padding: 14px 24px;\n font-size: 1rem;\n font-weight: 600;\n }\n\n /* ==========================================================================\n Deferred-flow brand components: dual lockup + deal ledger + primer screen\n ========================================================================== */\n\n /* Shared monogram tile (first paint for every brand logo). */\n .${B}brand-tile-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--encore-primary-color) 12%, var(--encore-bg-color));\n color: var(--encore-primary-color);\n font-weight: 700;\n font-size: 1.2rem;\n }\n\n /* Dual-brand lockup — two tiles settling together ("two brands, one deal"). */\n .${B}lockup {\n display: flex;\n justify-content: center;\n align-items: center;\n margin: 0.5rem 0 1.5rem 0;\n }\n\n .${B}lockup-tile {\n width: 56px;\n height: 56px;\n border-radius: 14px;\n object-fit: cover;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--encore-text-color) 14%, transparent);\n background: var(--encore-bg-color);\n }\n\n .${B}lockup-tile-partner {\n transform: rotate(-4deg);\n animation: ${B}lockup-settle-left 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;\n z-index: 1;\n }\n\n .${B}lockup-tile-host {\n margin-left: -10px;\n transform: rotate(4deg);\n animation: ${B}lockup-settle-right 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;\n z-index: 2;\n }\n\n @keyframes ${B}lockup-settle-left {\n from { transform: rotate(-6deg) translateX(-4px); }\n to { transform: rotate(-4deg) translateX(0); }\n }\n\n @keyframes ${B}lockup-settle-right {\n from { transform: rotate(6deg) translateX(4px); }\n to { transform: rotate(4deg) translateX(0); }\n }\n\n /* Deal ledger — the claim state machine, rendered. */\n .${B}ledger {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--encore-border-color);\n border-radius: 16px;\n background: var(--encore-bg-color);\n box-shadow: 0 1px 3px color-mix(in srgb, var(--encore-text-color) 6%, transparent);\n text-align: left;\n overflow: hidden;\n }\n\n .${B}ledger-row {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n }\n\n .${B}ledger-row + .${B}ledger-row {\n border-top: 1px solid var(--encore-border-color);\n }\n\n .${B}ledger-logo {\n width: 44px;\n height: 44px;\n border-radius: 11px;\n object-fit: cover;\n flex-shrink: 0;\n display: block;\n }\n\n .${B}ledger-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1 1 auto;\n min-width: 0;\n }\n\n .${B}ledger-name {\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--encore-text-color);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .${B}ledger-detail {\n font-size: 0.8rem;\n color: var(--encore-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .${B}ledger-status {\n font-size: 0.85rem;\n font-weight: 600;\n flex-shrink: 0;\n }\n\n .${B}ledger-status-active {\n color: var(--encore-success-color);\n }\n\n .${B}ledger-status-pending {\n color: var(--encore-text-muted);\n }\n\n /* Primer screen (S1.5) — centered column, one forward CTA. */\n .${B}primer-container {\n display: flex;\n flex-direction: column;\n justify-content: center;\n max-width: 440px;\n margin: 0 auto;\n padding: 2.5rem 1.75rem 2rem;\n text-align: center;\n animation: ${B}fade-in 0.3s ease-out;\n }\n\n .${B}primer-title {\n font-size: 1.6rem;\n font-weight: 700;\n color: var(--encore-text-color);\n margin: 0 0 0.5rem 0;\n line-height: 1.2;\n }\n\n .${B}primer-subtitle {\n font-size: 1.05rem;\n color: var(--encore-text-muted);\n margin: 0 0 2rem 0;\n line-height: 1.5;\n }\n\n /* Reuses .encore-button-claim (the clean pill) — full-width + prominent. */\n .${B}primer-cta {\n width: 100%;\n height: auto;\n min-height: 50px;\n padding: 14px 24px;\n font-size: 1rem;\n font-weight: 600;\n }\n\n /* ==========================================================================\n Mobile sheet composition for the primer + redemption screens.\n MUST live AFTER the component styles above: these override base paddings/\n margins, and the cascade is source-order at equal specificity.\n ========================================================================== */\n @media (max-width: ${V}) {\n /* Both screens fill the full-height sheet (modal-body is a flex column).\n Top padding clears the close button's overlay band; bottom padding the\n home indicator. */\n .${B}primer-container,\n .${B}redemption-container {\n flex: 1 1 auto;\n max-width: none;\n width: 100%;\n box-sizing: border-box;\n padding: calc(64px + env(safe-area-inset-top)) 24px\n calc(24px + env(safe-area-inset-bottom));\n }\n\n /* Primer: the content block floats centered between two flexible spaces\n (auto margins above the lockup and above the CTA), CTA on the bottom edge. */\n .${B}primer-container .${B}lockup {\n margin-top: auto;\n }\n\n .${B}primer-cta {\n margin-top: auto;\n }\n\n /* Redemption: the content block (variant region + note, note directly\n below the cards) floats centered between two flexible spaces; the CTA\n sits alone on the bottom edge. */\n .${B}redemption-container > :first-child {\n margin-top: auto;\n }\n\n .${B}redemption-cta {\n margin-top: auto;\n }\n }\n\n @keyframes ${B}scale-in {\n from {\n transform: scale(0);\n opacity: 0;\n }\n to {\n transform: scale(1);\n opacity: 1;\n }\n }\n `}(e),document.head.appendChild(t),o.debug("Encore styles injected successfully")}catch(e){o.error("Failed to inject Encore styles:",e)}}function G(e){return`${B}${e}`}class Y{#ke;#Ee=null;#$e=null;#Te=null;#Se=null;#Ae=!1;#Ue=null;#Le=[];#Ne=null;constructor(e={}){this.#ke=new z,j(e)}show(){this.#Ae?o.warn("Modal is already visible"):(this.#Ue=document.activeElement,this.#Re(),this.#Ee&&document.body.appendChild(this.#Ee),this.#$e&&document.body.appendChild(this.#$e),document.body.classList.add(G("no-scroll")),requestAnimationFrame(()=>{this.#Ee?.classList.add(G("visible")),this.#$e?.classList.add(G("visible"))}),this.#Oe(),this.#Me(),this.#De(),this.#Ae=!0,this.#ke.emit("show",void 0),o.debug("Modal shown"))}hide(){this.#Ae&&(this.#Ee?.classList.remove(G("visible")),this.#$e?.classList.remove(G("visible")),setTimeout(()=>{this.#Pe(),document.body.classList.remove(G("no-scroll")),this.#Ue instanceof HTMLElement&&this.#Ue.focus(),this.#Ae=!1,this.#ke.emit("hide",void 0),o.debug("Modal hidden")},200))}isVisible(){return this.#Ae}setLabelledBy(e){this.#$e?.setAttribute("aria-labelledby",e)}setContent(e){this.#Se?(this.#Se.innerHTML="",this.#Se.appendChild(e),this.#Me()):o.error("Modal body not initialized")}on(e,t){return this.#ke.on(e,t)}#Re(){this.#Ee=document.createElement("div"),this.#Ee.className=G("backdrop"),this.#Ee.setAttribute("aria-hidden","true"),this.#$e=document.createElement("div"),this.#$e.className=G("modal"),this.#$e.setAttribute("role","dialog"),this.#$e.setAttribute("aria-modal","true"),this.#$e.setAttribute("aria-labelledby",G("modal-title")),this.#Te=document.createElement("button"),this.#Te.className=G("close-button"),this.#Te.setAttribute("aria-label","Close modal"),this.#Te.innerHTML='\n <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">\n <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />\n </svg>\n ',this.#Se=document.createElement("div"),this.#Se.className=G("modal-body"),this.#$e.appendChild(this.#Te),this.#$e.appendChild(this.#Se)}#Pe(){this.#Ee?.remove(),this.#$e?.remove(),this.#Ee=null,this.#$e=null,this.#Te=null,this.#Se=null}#Oe(){this.#Te?.addEventListener("click",()=>{this.#ke.emit("close",void 0),this.hide()}),this.#Ee?.addEventListener("click",()=>{this.#ke.emit("backdropClick",void 0),this.hide()}),this.#Ne=this.#_e.bind(this),document.addEventListener("keydown",this.#Ne)}#_e(e){if(this.#Ae)switch(e.key){case"Escape":e.preventDefault(),this.#ke.emit("close",void 0),this.hide();break;case"Tab":e.preventDefault(),this.#ze(e.shiftKey)}}#ze(e){if(0===this.#Le.length)return;const t=this.#Le.findIndex(e=>e===document.activeElement);let n;n=e?t<=0?this.#Le.length-1:t-1:t>=this.#Le.length-1?0:t+1,this.#Le[n]?.focus()}#Me(){if(!this.#$e)return;const e=["button:not([disabled])","a[href]","input:not([disabled])","select:not([disabled])","textarea:not([disabled])",'[tabindex]:not([tabindex="-1"])'].join(", ");this.#Le=Array.from(this.#$e.querySelectorAll(e))}#De(){this.#Le.length>0&&this.#Le[0]?.focus()}destroy(){this.#Ne&&document.removeEventListener("keydown",this.#Ne),this.#ke.removeAllListeners(),this.hide()}}const W=new Set(["localhost","127.0.0.1","[::1]"]);function Q(e){if(!e)return;let t;try{t=new URL(e)}catch{return}return"https:"===t.protocol||"http:"===t.protocol&&W.has(t.hostname)?e:void 0}class X{#Fe=null;#Be;#te;#Ae=!1;#Ve=null;#He=null;#qe=null;#Ke=null;#je=null;constructor(e,t){this.#Be=e,this.#te={position:"top",maxWidth:200,...t},this.#Ge()}#Ge(){this.#qe=this.#Ye.bind(this),this.#Ke=this.#We.bind(this),this.#je=this.#Qe.bind(this),this.#Be.addEventListener("mouseenter",this.#qe),this.#Be.addEventListener("mouseleave",this.#Ke)}#Ye(){this.#He&&(clearTimeout(this.#He),this.#He=null),this.#Ve=window.setTimeout(()=>{this.#Xe()},50)}#We(){this.#Ve&&(clearTimeout(this.#Ve),this.#Ve=null),this.#He=window.setTimeout(()=>{this.#Ze()},100)}#Xe(){if(this.#Ae)return;this.#Fe=document.createElement("div"),this.#Fe.className=G("tooltip"),this.#Fe.setAttribute("role","tooltip"),this.#Fe.textContent=this.#te.content,this.#te.maxWidth&&(this.#Fe.style.maxWidth=`${this.#te.maxWidth}px`),this.#te.position&&this.#Fe.classList.add(G(`tooltip-${this.#te.position}`));const e=this.#Be.getBoundingClientRect();this.#Fe.style.top=`${e.top}px`,this.#Fe.style.left=`${e.left}px`,document.body.appendChild(this.#Fe),this.#je&&(window.addEventListener("scroll",this.#je,!0),window.addEventListener("resize",this.#je)),requestAnimationFrame(()=>{requestAnimationFrame(()=>{this.#Fe&&(this.#Qe(),this.#Fe.classList.add(G("tooltip-visible")))})}),this.#Ae=!0}#Ze(){this.#Ae&&this.#Fe&&(this.#je&&(window.removeEventListener("scroll",this.#je,!0),window.removeEventListener("resize",this.#je)),this.#Fe.classList.remove(G("tooltip-visible")),setTimeout(()=>{this.#Fe&&this.#Fe.parentNode&&this.#Fe.parentNode.removeChild(this.#Fe),this.#Fe=null},200),this.#Ae=!1)}#Qe(){if(!this.#Fe||!this.#Ae)return;const e=this.#Be.getBoundingClientRect(),t=this.#Fe.getBoundingClientRect();let n=0,i=0;switch(this.#te.position??"top"){case"top":n=e.top-t.height-8,i=e.left+e.width/2-t.width/2;break;case"bottom":n=e.bottom+8,i=e.left+e.width/2-t.width/2;break;case"left":n=e.top+e.height/2-t.height/2,i=e.left-t.width-8;break;case"right":n=e.top+e.height/2-t.height/2,i=e.right+8}i=Math.max(8,Math.min(i,window.innerWidth-t.width-8)),n=Math.max(8,Math.min(n,window.innerHeight-t.height-8)),this.#Fe.style.top=`${n}px`,this.#Fe.style.left=`${i}px`}updateContent(e){this.#te.content=e,this.#Fe&&(this.#Fe.textContent=e,this.#Qe())}destroy(){this.#Ve&&(clearTimeout(this.#Ve),this.#Ve=null),this.#He&&(clearTimeout(this.#He),this.#He=null),this.#Ae&&this.#Ze(),this.#qe&&this.#Be.removeEventListener("mouseenter",this.#qe),this.#Ke&&this.#Be.removeEventListener("mouseleave",this.#Ke),this.#je&&(window.removeEventListener("scroll",this.#je,!0),window.removeEventListener("resize",this.#je))}}class Z{#ke;#Je=null;#et=null;#tt=null;constructor(){this.#ke=new z}render(e){this.#Je=e,this.#et=document.createElement("div"),this.#et.className=G("offer-card");const t=this.#nt(e.imageUrl,e.title,e.tooltipText);this.#et.appendChild(t);const n=this.#it(e);return this.#et.appendChild(n),this.#et}on(e,t){return this.#ke.on(e,t)}#nt(e,t,n){const i=document.createElement("div");i.className=G("offer-banner");const r=document.createElement("img");r.className=G("offer-banner-image"),r.alt=t??"Offer image";const s=Q(e);s?(r.src=s,r.complete?r.classList.add(G("loaded")):r.onload=()=>{r.classList.add(G("loaded"))},r.onerror=()=>{o.warn("Failed to load offer image, using fallback"),r.src=this.#rt(),r.classList.add(G("loaded"))}):(r.src=this.#rt(),r.classList.add(G("loaded"))),i.appendChild(r);const a=document.createElement("button");return a.className=G("banner-info-button"),a.setAttribute("aria-label","More information"),a.innerHTML='\n <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">\n <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />\n </svg>\n ',a.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),o.debug("Info button clicked for offer",{title:t,tooltipText:n})}),n&&(this.#tt=new X(a,{content:n,position:"bottom",maxWidth:200}),i.appendChild(a)),i}#rt(){return`data:image/svg+xml;base64,${btoa('\n <svg xmlns="http://www.w3.org/2000/svg" width="600" height="240" viewBox="0 0 600 240">\n <rect width="600" height="240" fill="#E5E7EB"/>\n <text x="50%" y="50%" text-anchor="middle" dy=".3em" fill="#9CA3AF" font-family="sans-serif" font-size="18">\n Offer Image\n </text>\n </svg>\n ')}`}#it(e){const t=document.createElement("div");t.className=G("offer-footer");const n=document.createElement("div");n.className=G("offer-merchant");const i=Q(e.logoUrl);if(i){const t=document.createElement("img");t.className=G("offer-logo"),t.src=i,t.alt=`${e.merchantName??"Merchant"} logo`,t.complete?t.classList.add(G("loaded")):t.onload=()=>{t.classList.add(G("loaded"))},t.onerror=()=>{o.warn("Failed to load merchant logo",{merchantName:e.merchantName}),t.style.display="none"},n.appendChild(t)}const r=document.createElement("div");if(r.className=G("offer-merchant-text"),e.merchantName){const t=document.createElement("div");t.className=G("offer-merchant-name"),t.textContent=e.merchantName,r.appendChild(t)}if(e.description){const t=document.createElement("div");t.className=G("offer-merchant-desc"),t.textContent=e.description,r.appendChild(t)}n.appendChild(r);const s=document.createElement("button");return s.className=`${G("button")} ${G("button-claim")}`,s.textContent=e.ctaText??"Claim",s.setAttribute("aria-label",`Claim offer: ${e.title??"offer"}`),s.addEventListener("click",()=>{this.#Je&&this.#ke.emit("claim",{offer:this.#Je})}),t.appendChild(n),t.appendChild(s),t}destroy(){this.#tt&&(this.#tt.destroy(),this.#tt=null),this.#ke.removeAllListeners(),this.#et=null,this.#Je=null}}class J{#ke;#ot;#st=0;#et=null;#at=null;#lt=null;#dt=null;#ct=[];#ht=null;#ut=null;#pt=null;#mt=null;#ft=null;#gt=0;#bt=null;#vt=-1;#yt=-1;#wt;#xt=window.matchMedia(`(max-width: ${V})`);#It=!1;#Ct=null;#kt=null;#Et=-1;#$t=!1;#Tt=!1;constructor(e,t,n){if(this.#ke=new z,this.#ot=e,this.#wt=t,this.#$t="paged"===n?.mode,this.#Tt=n?.hideHeader??!1,0===e.length)throw new Error("OfferCarousel requires at least one offer");this.#St()}render(){if(this.#et=document.createElement("div"),this.#et.className=this.#$t?`${G("carousel")} ${G("carousel-paged")}`:G("carousel"),this.#ht=document.createElement("div"),this.#ht.setAttribute("role","status"),this.#ht.setAttribute("aria-live","polite"),this.#ht.setAttribute("aria-atomic","true"),this.#ht.className="sr-only",this.#ht.style.position="absolute",this.#ht.style.width="1px",this.#ht.style.height="1px",this.#ht.style.padding="0",this.#ht.style.margin="-1px",this.#ht.style.overflow="hidden",this.#ht.style.clip="rect(0, 0, 0, 0)",this.#ht.style.whiteSpace="nowrap",this.#ht.style.border="0",!this.#Tt){const e=this.#At();this.#et.appendChild(e)}if(this.#at=document.createElement("div"),this.#at.className=G("carousel-cards"),this.#et.appendChild(this.#ht),this.#et.appendChild(this.#at),this.#ot.length>1){const e=this.#Ut();this.#et.appendChild(e)}return this.#Lt(),this.#It=!this.#$t&&this.#xt.matches,this.#Nt(),this.#$t||(this.#Ct=this.#Rt.bind(this),this.#xt.addEventListener("change",this.#Ct)),this.#et}#Nt(){if(this.#Ot(),this.#yt=this.#st,this.#It)this.#Mt();else{this.#Dt(),this.#Pt(),this.#_t(0,!1),this.#zt(this.#st),this.#Ft(this.#st);const e=this.#ot[this.#st];e&&this.#vt!==this.#st&&(this.#vt=this.#st,this.#ke.emit("offerView",{offer:e,index:this.#st}))}}#Rt(){const e=this.#xt.matches;e!==this.#It&&(this.#It=e,this.#Nt())}#Ot(){this.#ut&&(document.removeEventListener("keydown",this.#ut),this.#ut=null),this.#at&&this.#pt&&(this.#at.removeEventListener("scroll",this.#pt),this.#pt=null),this.#at&&this.#mt&&(this.#at.removeEventListener("scrollend",this.#mt),this.#mt=null),null!==this.#ft&&(cancelAnimationFrame(this.#ft),this.#ft=null),this.#bt&&(clearTimeout(this.#bt),this.#bt=null),this.#kt&&(this.#kt.disconnect(),this.#kt=null)}#Mt(){this.#at&&(this.#kt=new IntersectionObserver(e=>{let t=null;for(const n of e){if(!n.isIntersecting)continue;const e=n.target.getAttribute("data-index");if(null===e)continue;const i=Number(e);(!t||n.intersectionRatio>t.ratio)&&(t={index:i,ratio:n.intersectionRatio})}if(!t)return;const{index:n}=t;if(n===this.#Et)return;this.#Et=n,this.#st=n,n!==this.#yt&&(this.#yt=n,this.#ke.emit("scroll",{axis:"vertical",position:n}));const i=this.#ot[n];i&&(this.#ke.emit("offerView",{offer:i,index:n}),o.debug(`Offer ${n+1} entered view`))},{root:this.#at,threshold:.5}),this.#at.querySelectorAll(`.${G("carousel-item")}`).forEach(e=>this.#kt?.observe(e)))}on(e,t){return this.#ke.on(e,t)}getCurrentIndex(){return this.#st}getTotalOffers(){return this.#ot.length}next(){const e=Math.min(this.#st+1,this.#ot.length-1);e!==this.#st&&(this.#st=e,this.#zt(e),this.#_t(e),this.#Bt(`Showing offer ${e+1} of ${this.#ot.length}`),o.debug(`Navigated to offer ${e+1}`))}previous(){const e=Math.max(this.#st-1,0);e!==this.#st&&(this.#st=e,this.#zt(e),this.#_t(e),this.#Bt(`Showing offer ${e+1} of ${this.#ot.length}`),o.debug(`Navigated to offer ${e+1}`))}#Lt(){if(!this.#at)return;const e=this.#at;this.#ot.forEach((t,n)=>{const i=new Z,r=i.render(t),o=document.createElement("div");o.className=G("carousel-item"),o.setAttribute("data-index",n.toString()),o.appendChild(r),i.on("claim",()=>{this.#ke.emit("offerClaim",{offer:t,index:n})}),this.#ct.push(i),e.appendChild(o)}),requestAnimationFrame(()=>{e.classList.add(G("cards-ready"))})}#_t(e,t=!0){if(!this.#at)return;const n=this.#at.querySelectorAll(`.${G("carousel-item")}`)[e];n&&n.scrollIntoView({behavior:t?"smooth":"auto",block:"nearest",inline:"center"})}#zt(e){this.#Vt(),this.#Ht(e)}#Ft(e){this.#at&&(this.#gt=e,this.#at.querySelectorAll(`.${G("carousel-item")}`).forEach((t,n)=>{n===e?t.classList.add(G("active")):t.classList.remove(G("active"))}))}#At(){const e=document.createElement("div");e.className=G("carousel-header");const t=document.createElement("h2");t.className=G("carousel-title"),t.id=G("modal-title"),t.textContent=this.#wt?.customHeadline??this.#wt?.titleText??"Unlock 10 free articles – choose an offer below";const n=document.createElement("p");return n.className=G("carousel-subtitle"),n.textContent=this.#qt(this.#st),this.#lt=n,e.appendChild(t),e.appendChild(n),e}#qt(e){const t=this.#wt?.customSubheadline??this.#wt?.subtitleText??"";if(!this.#$t)return t;const n=this.#ot[e]?.perk?.trim();return n||t}#Ht(e){if(!this.#$t||!this.#lt)return;const t=this.#qt(e);this.#lt.textContent!==t&&(this.#lt.textContent=t)}#Ut(){const e=document.createElement("div");e.className=G("carousel-navigation");const t=document.createElement("button");t.className=G("nav-button"),t.setAttribute("aria-label","Previous offer"),t.innerHTML=this.#$t?'\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round">\n <path d="M14.5 5.5L8 12l6.5 6.5" />\n </svg>\n ':'\n <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" style="transform: rotate(90deg);">\n <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />\n </svg>\n ',t.addEventListener("click",()=>this.previous());const n=document.createElement("button");return n.className=G("nav-button"),n.setAttribute("aria-label","Next offer"),n.innerHTML=this.#$t?'\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round">\n <path d="M9.5 5.5L16 12l-6.5 6.5" />\n </svg>\n ':'\n <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" style="transform: rotate(270deg);">\n <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />\n </svg>\n ',n.addEventListener("click",()=>this.next()),e.appendChild(t),e.appendChild(n),this.#dt=e,this.#Vt(t,n),e}#Vt(e,t){if(!this.#dt)return;const n=this.#dt.querySelectorAll(`.${G("nav-button")}`),i=e??n[0],r=t??n[1];i&&(i.disabled=0===this.#st,i.classList.toggle(G("disabled"),0===this.#st)),r&&(r.disabled=this.#st===this.#ot.length-1,r.classList.toggle(G("disabled"),this.#st===this.#ot.length-1))}#Dt(){this.#ut=this.#Kt.bind(this),document.addEventListener("keydown",this.#ut)}#Kt(e){if(this.#et)switch(e.key){case"ArrowLeft":e.preventDefault(),this.previous();break;case"ArrowRight":e.preventDefault(),this.next()}}#Pt(){if(!this.#at)return;const e=this.#at,t="onscrollend"in e;this.#pt=()=>{null===this.#ft&&(this.#ft=window.requestAnimationFrame(()=>{this.#ft=null,this.#jt()})),t||(this.#bt&&clearTimeout(this.#bt),this.#bt=window.setTimeout(()=>this.#Gt(),120))},e.addEventListener("scroll",this.#pt),t&&(this.#mt=()=>this.#Gt(),e.addEventListener("scrollend",this.#mt))}#Yt(){if(!this.#at)return this.#st;const e=this.#at,t=e.querySelectorAll(`.${G("carousel-item")}`),n=e.getBoundingClientRect(),i=n.left+n.width/2;let r=0,o=1/0;return t.forEach((e,t)=>{const n=e.getBoundingClientRect(),s=Math.abs(i-(n.left+n.width/2));s<o&&(o=s,r=t)}),r}#jt(){if(!this.#at)return;const e=this.#Yt();e!==this.#gt&&this.#Ft(e)}#Gt(){this.#bt&&(clearTimeout(this.#bt),this.#bt=null);const e=this.#Yt();e!==this.#st&&(this.#st=e,this.#zt(e),o.debug(`Gesture settled on offer ${e+1}`)),this.#Ft(e),e!==this.#yt&&(this.#yt=e,this.#ke.emit("scroll",{axis:"horizontal",position:e}));const t=this.#st;if(t===this.#vt)return;const n=this.#ot[t];n&&(this.#vt=t,this.#ke.emit("offerView",{offer:n,index:t}))}#Bt(e){this.#ht&&(this.#ht.textContent=e)}#St(){this.#ot.forEach(e=>{e.imageUrl&&((new Image).src=e.imageUrl,o.debug("Preloading offer image",{offerId:e.id,url:e.imageUrl})),e.logoUrl&&((new Image).src=e.logoUrl,o.debug("Preloading merchant logo",{offerId:e.id,url:e.logoUrl}))})}destroy(){this.#Ot(),this.#Ct&&(this.#xt.removeEventListener("change",this.#Ct),this.#Ct=null),this.#ct.forEach(e=>e.destroy()),this.#ct=[],this.#ke.removeAllListeners(),this.#et=null,this.#at=null,this.#lt=null,this.#dt=null,this.#ht=null}}const ee=["list","thankYou"],te=G("thankyou-title");function ne(e,t={}){const{header:n,footer:i,receipt:r,reward:s}=t,a=function(e,t){if(!t)return e?.subtitle;const n="string"==typeof t.amount?t.amount.trim():"";if(!n)return o.warn("Receipt is missing a non-empty `amount` — ignoring the receipt"),e?.subtitle;e?.subtitle&&o.warn("Both `receipt` and `header.subtitle` supplied — the receipt owns the subtitle slot; `header.subtitle` is ignored");const i="string"==typeof t.purpose?t.purpose.trim():"";return i?`You paid ${n} for ${i}`:`You paid ${n}`}(n,r),l=document.createElement("div");l.className=s?`${G("thankyou")} ${G("thankyou--reward")}`:G("thankyou");const d="none"!==n?.icon;if(d||n?.title||a){const e=document.createElement("div");if(e.className=G("thankyou-status"),d){const t=document.createElement("span");t.className=G("thankyou-check"),t.setAttribute("aria-hidden","true"),t.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5" /></svg>',e.appendChild(t)}if(n?.title){const t=document.createElement("h2");t.className=G("thankyou-title"),t.id=te,t.textContent=n.title,e.appendChild(t)}if(a){const t=document.createElement("p");t.className=G("thankyou-subtitle"),t.textContent=a,e.appendChild(t)}l.appendChild(e)}if(l.appendChild(e),i?.text){const e=document.createElement("p");e.className=G("thankyou-footer"),e.textContent=i.text,l.appendChild(e)}return l}const ie=e=>{const t=e?.trim();if(void 0!==t&&t.length>0)return t},re=(e,t)=>!(void 0!==e.when.layout&&e.when.layout!==t.layout||void 0!==e.when.mode&&e.when.mode!==t.mode),oe=[{context:"list · deferred",field:"appDisplayName",when:{layout:"list",mode:"deferred"},required:!0,resolve:e=>ie(e.display?.appDisplayName)??ie(e.remote.appName)},{context:"list · deferred",field:"trialPeriod",when:{layout:"list",mode:"deferred"},required:!0,resolve:e=>ie(e.display?.trialPeriod)??(e=>{const t=ie(e.entitlementValue);let n=ie(e.entitlementUnit);if(t&&n)return 1===Number(t)&&n.endsWith("s")&&(n=n.slice(0,-1)),`${t} ${n}`})(e.remote)},{context:"list · deferred",field:"logoUrl",when:{layout:"list",mode:"deferred"},required:!0,resolve:e=>ie(e.display?.logoUrl)??ie(e.remote.appIconUrl)},{context:"thankYou",field:"header.title",when:{layout:"thankYou"},resolve:e=>ie(e.header?.title)??"Payment Successful"},{context:"list · immediate",field:"header.title",when:{layout:"list",mode:"immediate"},resolve:e=>ie(e.header?.title)},{context:"list · immediate",field:"header.subtitle",when:{layout:"list",mode:"immediate"},resolve:e=>ie(e.header?.subtitle)},{context:"list · deferred",field:"header.title",when:{layout:"list",mode:"deferred"},resolve:(e,t)=>ie(e.header?.title)??(t.appDisplayName&&t.trialPeriod?se.s1Title(t.appDisplayName,t.trialPeriod):void 0)},{context:"list · deferred",field:"header.subtitle",when:{layout:"list",mode:"deferred"},resolve:e=>ie(e.header?.subtitle)??se.s1Subtitle()}],se={s1Title:(e,t)=>`Get ${e} free for ${t}`,s1Subtitle:()=>"Claim a partner offer to start",primerTitle:(e,t)=>t?`Amazing! Your ${t} free trial of ${e} is ready`:`Amazing! Your ${e} reward is ready`,primerSubtitle:e=>`Next you'll see the ${e} activation screen. No charge today.`,primerCta:(e,t)=>t?`Activate ${e} trial`:`Continue to ${e}`,s2Title:(e,t)=>t?`Congratulations! Your ${t} free trial is active.`:`Congratulations! Your ${e} plan is active.`,s2Subtitle:e=>`All ${e} features are now unlocked.`,s2HostDetail:e=>`Free until ${new Intl.DateTimeFormat(void 0,{month:"long",day:"numeric",year:"numeric"}).format(e)}`,s2Cta:()=>"Claim your reward"};function ae(e){const t={},n={},i=[];for(const r of oe){if(!re(r,e))continue;const o=r.resolve(e,t);if(void 0!==o)switch(r.field){case"header.title":n.title=o;break;case"header.subtitle":n.subtitle=o;break;default:t[r.field]=o}else r.required&&i.push(r.field)}return i.length>0?{status:"missing",missingFields:i}:{status:"ok",display:t,header:n}}function le(e,t,n){const i=document.createElement("div");i.className=`${t} ${G("brand-tile-placeholder")}`;const r=e.name?.trim().charAt(0);i.textContent=r?r.toUpperCase():"★";const o=Q(e.logoUrl);if(o){const r=document.createElement("img");r.className=t,r.alt=e.name?`${e.name} logo`:"Brand logo",r.width=n,r.height=n,r.decoding="async",r.addEventListener("load",()=>i.replaceWith(r)),r.src=o}return i}function de(e,t){const n=document.createElement("div");n.className=G("lockup"),n.setAttribute("aria-hidden","true");const i=le(e,`${G("lockup-tile")} ${G("lockup-tile-partner")}`,56),r=le(t,`${G("lockup-tile")} ${G("lockup-tile-host")}`,56);return n.appendChild(i),n.appendChild(r),n}function ce(e){const t=document.createElement("div");return t.className=G("ledger"),e.forEach(e=>t.appendChild(function(e){const t=document.createElement("div");t.className=G("ledger-row");const n=le(e.brand,G("ledger-logo"),44),i=document.createElement("div");i.className=G("ledger-text");const r=document.createElement("div");if(r.className=G("ledger-name"),r.textContent=e.name,i.appendChild(r),e.detail){const t=document.createElement("div");t.className=G("ledger-detail"),t.textContent=e.detail,i.appendChild(t)}const o=document.createElement("div");return o.className=`${G("ledger-status")} ${G(`ledger-status-${e.status.tone}`)}`,o.textContent=e.status.text,t.appendChild(n),t.appendChild(i),t.appendChild(o),t}(e))),t}function he(e,t,n){const i=document.createElement("button");return i.className=`encore-button-claim ${t}`,i.setAttribute("type","button"),i.textContent=e,i.addEventListener("click",n),i}const ue=e=>({...e.advertiserName&&{name:e.advertiserName},...(e.logoUrl??e.imageUrl)&&{logoUrl:e.logoUrl??e.imageUrl}}),pe=e=>({brand:ue(e),name:e.advertiserName??"Your reward",...e.reward&&{detail:e.reward},status:{text:"Pending",tone:"pending"}}),me=e=>{const t=document.createElement("h2");return t.className=G("redemption-title"),t.textContent=e,t},fe=e=>{const t=document.createElement("p");return t.className=G("redemption-subtitle"),t.textContent=e,t},ge={activation(e){const t=e.host??{},n=t.name??"your app",i=[{brand:{name:n,...t.logoUrl&&{logoUrl:t.logoUrl}},name:n,...t.detail&&{detail:t.detail},status:{text:t.status??"Active",tone:"active"}},pe(e)];return[de(ue(e),{name:n,...t.logoUrl&&{logoUrl:t.logoUrl}}),me(se.s2Title(n,t.trialPeriod)),fe(se.s2Subtitle(n)),ce(i)]},reward:e=>[me("Congratulations!"),fe(e.reward?`Now claim your ${e.reward}`:"Now claim your reward"),ce([pe(e)])]};class be{#et=null;#ke;#t;constructor(e){this.#t=e,this.#ke=new z}render(){const{advertiserName:e,buttonText:t}=this.#t,n=this.#t.variant??"reward",i=document.createElement("div");i.className=G("redemption-container");for(const e of ge[n](this.#t))i.appendChild(e);const r=document.createElement("p");r.className=G("redemption-note"),r.textContent=e?`You'll be sent to ${e}. No payment now, cancel anytime.`:"No payment now, cancel anytime.";const s=he(t??se.s2Cta(),G("redemption-cta"),()=>{o.debug("User tapped the redemption CTA"),this.#ke.emit("continue",void 0)});return i.appendChild(r),i.appendChild(s),this.#et=i,o.debug("Redemption screen rendered",{variant:n}),i}on(e,t){return this.#ke.on(e,t)}destroy(){this.#et&&(this.#et.remove(),this.#et=null),this.#ke.removeAllListeners(),o.debug("Redemption screen destroyed")}}class ve{#et=null;#ke;#t;constructor(e){this.#t=e,this.#ke=new z}render(){const{display:e,partner:t}=this.#t,n=e.appDisplayName??"your app",i=document.createElement("div");i.className=G("primer-container");const r=t.logoUrl??t.imageUrl,s=de({...t.name&&{name:t.name},...r&&{logoUrl:r}},{name:n,...e.logoUrl&&{logoUrl:e.logoUrl}}),a=document.createElement("h2");a.className=G("primer-title"),a.textContent=se.primerTitle(n,e.trialPeriod);const l=document.createElement("p");l.className=G("primer-subtitle"),l.textContent=se.primerSubtitle(n);const d=he(se.primerCta(n,e.trialPeriod),G("primer-cta"),()=>{o.debug("User tapped the primer CTA"),this.#ke.emit("continue",void 0)});return i.appendChild(s),i.appendChild(a),i.appendChild(l),i.appendChild(d),this.#et=i,o.debug("Primer screen rendered"),i}on(e,t){return this.#ke.on(e,t)}destroy(){this.#et&&(this.#et.remove(),this.#et=null),this.#ke.removeAllListeners(),o.debug("Primer screen destroyed")}}class ye{#Wt=null;#Qt=null;#Xt=!1;start(e){this.#Xt?o.warn("FocusDetector already active"):(this.#Wt=()=>{o.debug("Window focused"),e()},this.#Qt=()=>{"visible"===document.visibilityState&&(o.debug("Document became visible"),e())},window.addEventListener("focus",this.#Wt),document.addEventListener("visibilitychange",this.#Qt),this.#Xt=!0,o.debug("FocusDetector started"))}stop(){this.#Xt&&(this.#Wt&&(window.removeEventListener("focus",this.#Wt),this.#Wt=null),this.#Qt&&(document.removeEventListener("visibilitychange",this.#Qt),this.#Qt=null),this.#Xt=!1,o.debug("FocusDetector stopped"))}isActive(){return this.#Xt}}const we="user_id",xe="user_attributes";function Ie(e){const t=e?.values?.text;return!!t&&Object.keys(t).length>0}function Ce(...e){for(const t of e){const e=t?.trim();if(e)return e}}const ke=new class{#Zt;#s;#b=null;#T=null;#Jt=null;#en=null;#tn=null;#nn=null;#v=null;#in={};#rn=!1;#on={};#sn;#an={};#ln={};#dn={};#cn=new Set;#hn=new Set;#un=null;#pn=!1;#mn;#fn=null;constructor(){this.#Zt=new s,this.#s=new c,this.#mn=new l(this.#s)}configure(e){const t=this.#Zt.configure(e);if(t){this.#gn();const e=this.#Zt.getBaseURL(),t=this.#Zt.getAnalyticsBaseURL(),n=this.#Zt.getApiKey()??"";if(e){if(this.#b=new $(n,e),t&&(this.#T=new N(n,t,this.#s),!this.#tn)){const e=new _(n,t,this.#s);this.#tn=new D(this.#s,()=>this.#v,e),e.flush()}this.#v&&(this.#Jt=new u(this.#s,this.#b,this.#v),this.#en?.destroy(),this.#en=new x(this.#b,this.#v,this.#T??void 0)),this.#nn=new ye}if(o.info("Encore SDK initialized successfully",{version:E}),this.#T&&this.#v){const e="undefined"!=typeof window?window.location.hostname:"unknown";this.#T.track(function(e,t,n){return{name:"sdk_initialized",distinctId:e,properties:{sdkVersion:t,appBundleId:n,platform:"web"}}}(this.#v,E,e))}this.#b&&this.#v&&this.#b.initUser(this.#v).catch(e=>{o.warn("Failed to init user on backend (non-blocking)",e)}),this.#bn()}return t}#gn(){const e=this.#s.get(we);e?(this.#v=e,o.debug("Loaded existing user ID:",e)):(this.#v=w(),this.#s.set(we,this.#v),o.debug("Generated new user ID:",this.#v));const t=this.#s.get(xe);t&&(this.#in=t,o.debug("Loaded user attributes"))}#vn(){return!!this.#Zt.isConfigured()||(o.error("SDK not configured. Call Encore.configure() first."),!1)}identify(e,t){if(!this.#vn())return;if(!e||"string"!=typeof e)return void o.error("identify() requires a valid user ID string");const n=this.#v;if(this.#v=e,this.#s.set(we,e),t&&this.setUserAttributes(t),n!==e&&(o.info("User identified:",e),this.#Jt&&this.#Jt.setUserId(e),this.#en&&this.#en.setUserId(e),this.#b&&n&&this.#b.identifyUser(n,e,t?.email?{email:t.email}:void 0).catch(e=>{o.warn("Failed to identify user on backend (non-blocking)",e)}),this.#T)){const n="undefined"!=typeof window?window.location.hostname:"unknown";this.#T.track(function(e,t,n,i){return{name:"user_identified",distinctId:e,properties:{userId:t,appBundleId:n,...i&&{attributes:i}}}}(e,e,n,t))}}getCurrentUserId(){return this.#v}getAppAccountId(){return this.#v}reset(){this.#vn()&&(o.info("Resetting SDK"),this.#s.clear(),this.#mn.clear(),this.#v=w(),this.#s.set(we,this.#v),this.#in={},this.#on={},this.#an={},this.#ln={},this.#dn={},this.#cn.clear(),this.#un=null,this.#pn=!1,this.#Jt&&(this.#Jt.clearQueue(),this.#Jt.destroy(),this.#b&&this.#v&&(this.#Jt=new u(this.#s,this.#b,this.#v))),this.#en&&(this.#en.clearCache(),this.#en.destroy(),this.#b&&this.#v&&(this.#en=new x(this.#b,this.#v,this.#T??void 0))),this.#nn?.isActive()&&this.#nn.stop(),this.#fn?.(),this.#fn=null,o.debug("SDK reset complete",{newUserId:this.#v}))}getVersion(){return E}isConfigured(){return this.#Zt.isConfigured()}getConfiguration(){return this.#Zt.isConfigured()?{apiKey:this.#Zt.getApiKey()??"",environment:this.#Zt.getEnvironment()??"",baseURL:this.#Zt.getBaseURL()??"",analyticsBaseURL:this.#Zt.getAnalyticsBaseURL()??"",logLevel:this.#Zt.getConfig()?.logLevel??"none",redemptionMode:this.#Zt.getRedemptionMode()}:null}async getExperimentState(){return this.#Zt.isConfigured()&&this.#tn?this.#tn.getState():null}setLogLevel(e){o.setLogLevel(e)}async revokeEntitlements(){if(!this.#vn())return!1;if(!this.#b||!this.#v)return!1;try{return await this.#b.revokeEntitlements(this.#v)}catch(e){return o.error("Failed to revoke entitlements",e),!1}}setUserAttributes(e){this.#vn()&&(e&&"object"==typeof e?(this.#in={...this.#in,...e,custom:{...this.#in.custom,...e.custom}},this.#s.set(xe,this.#in),o.debug("User attributes updated",this.#in)):o.error("setUserAttributes() requires a valid UserAttributes object"))}getUserAttributes(){return{...this.#in}}async show(e,r,s){if(!this.#vn()){const n={type:"error",error:{code:t.INITIALIZATION_ERROR,message:"SDK not configured"}};return e?.onNotGranted?.(n),e?.onError?.(n.error),{status:"dismissed",reason:n}}const a=(l=r?.useCase,void 0===l||n.includes(l)?null:{code:t.INVALID_REQUEST,message:`useCase ${i(l)} is not a recognised Encore use case — nothing was presented. Supported values: ${n.map(e=>`'${e}'`).join(", ")}.`,details:{useCase:l}});var l;if(a){o.error(`[Encore SDK] Integration error: ${a.message}`),this.#yn("Unsupported use case",new Error(a.message));const t={type:"error",error:a};return e?.onNotGranted?.(t),e?.onError?.(a),{status:"dismissed",reason:t}}try{const t=await this.#wn(r,s,e?.receipt);return"claimed"===t.status&&"immediate"===this.#Zt.getRedemptionMode()?e?.onGranted?.(void 0):"dismissed"===t.status&&t.reason&&e?.onNotGranted?.(t.reason),t}catch(n){o.error("Error during show",n),this.#yn("Show flow error",n);const i={type:"error",error:{code:t.UNKNOWN_ERROR,message:n instanceof Error?n.message:"Unknown error"}};return e?.onError?.(i.error),e?.onNotGranted?.(i),{status:"dismissed",reason:i}}}async presentOffer(e,t){const n=await this.show(e,t);switch(n.status){case"unavailable":return{granted:!1,reason:"noOffersAvailable"};case"dismissed":return{granted:!1,...n.reason&&{reason:n.reason}};case"claimed":return"immediate"===this.#Zt.getRedemptionMode()?{granted:!0}:{granted:!1}}}async redeem(e){if(!this.#vn())return{status:"dismissed",reason:{type:"error",error:{code:t.INITIALIZATION_ERROR,message:"SDK not configured"}}};const n=this.#xn(e);if("list"!==n?.claimLayout||"post-action-reward"===n.useCase||e?.host?.name||await this.#In(),!n)return o.info("redeem(): no pending transaction to redeem"),{status:"none"};try{this.#rn||(j({}),this.#rn=!0);const t=new Y,i=await new Promise(i=>{let r=!1;const o=e=>{r||(r=!0,i(e))};t.on("close",()=>o({status:"dismissed"})),t.on("backdropClick",()=>{t.hide(),o({status:"dismissed"})}),t.on("hide",()=>o({status:"dismissed"})),t.show(),this.#Cn(t,n,e?.host,()=>{const e=this.#kn(),i=this.#En(n.useCase);this.#$n(n,e,{campaignId:n.offer.campaignId,...n.offer.advertiserName&&{advertiserName:n.offer.advertiserName},...i&&{variantId:i},...n.useCase&&{useCase:n.useCase}}),o({status:"redeemed"}),t.hide()})});return t.destroy(),i}catch(e){return o.error("Error during redeem",e),this.#yn("Redeem flow error",e),{status:"dismissed",reason:{type:"error",error:{code:t.UNKNOWN_ERROR,message:e instanceof Error?e.message:"Unknown error"}}}}}getPendingTransaction(e){return this.#vn()?e?this.#mn.getByPlacement(e):this.#mn.getLatest():null}isActive(e,t){return o.error("isActive() has been removed. Entitlement validation must be done server-side. See: https://docs.encorekit.com/server-side-validation"),!1}didGrant(e,t){this.#vn()&&(this.#Jt?t&&"string"==typeof t?this.#Jt.sendGrantSignal(t,e):o.error("didGrant() requires a valid transaction ID"):o.error("SignalManager not initialized"))}async waitForVerification(e,t){if(!this.#vn())return o.error("SDK not configured"),"failed";if(!this.#b)return o.error("API client not initialized"),"failed";if(!e)return o.error("waitForVerification() requires a transaction ID"),"failed";try{o.debug("Starting verification wait",{transactionId:e,timeout:t});const n={transactionId:e};void 0!==t&&(n.timeout=t);const i=new I(this.#b,n);return await i.poll()}catch(t){return o.error("Error during verification wait",{transactionId:e,error:t}),"failed"}}on(e,t){return o.error("on() event listener has been removed. Entitlement validation must be done server-side. See: https://docs.encorekit.com/server-side-validation"),()=>{}}observeEntitlement(e,t){return o.error("observeEntitlement() has been removed. Entitlement validation must be done server-side. See: https://docs.encorekit.com/server-side-validation"),{subscribe:e=>()=>{}}}placement(e,t){let n,i;return"string"==typeof e?(n=e,i=t):(n=`placement_${w().slice(0,8)}`,i=e),new C(n,async(e,t)=>this.show(t,e,n),i,e=>this.redeem({placement:e}))}#yn(e,t){if(!this.#b)return;const n=t instanceof Error?t.message:String(t),i=t instanceof Error?t.stack:void 0;this.#b.reportError({errorType:t instanceof Error?t.constructor.name:"UnknownError",message:n,context:e,severity:"error",...i&&{stackTrace:i},...this.#v&&{userId:this.#v}}).catch(()=>{}),this.#T&&this.#v&&this.#T.track(function(e,t,n,i){return{name:"sdk_error",distinctId:e,properties:{errorType:t,errorDescription:n,...i&&{context:i}}}}(this.#v,t instanceof Error?t.constructor.name:"UnknownError",n,e))}async#Tn(e,t){if("churn-intervention"!==t)return null;const n=this.#tn?await this.#tn.getCohort():"notEnrolled";return this.#tn?.logExposure(n),"control"!==n?null:(this.#T&&this.#v&&this.#T.track(g(this.#v,"experiment_control",e,this.#sn,t)),o.debug("NCL control cohort — ghost trigger, no UI shown"),{status:"unavailable"})}#Sn(e){if(e.ui?.values?.text){const t=e.ui.values.text;this.#on={...t},o.debug("Remote config loaded",t)}const t=e.entitlements?.native,n=e.ui?.values?.media;this.#an={...t?.value&&{entitlementValue:String(t.value)},...t?.unit&&{entitlementUnit:t.unit},...t?.durationDays&&{trialDurationDays:t.durationDays},...n?.appstoreIcon&&{appIconUrl:n.appstoreIcon}},this.#sn=e.ui?.variantId??void 0;const i=e.useCases??{},r={};for(const e of Object.keys(this.#dn)){const t=this.#ln[e];t&&(r[e]=t)}this.#ln={...i,...r},this.#tn?.setConfig(e.experiments?.ncl),this.#pn=!0}#An(e){if(this.#cn.has(e))return null;const t=this.#ln[e];return t&&Ie(t)?{variantId:t.variantId??void 0,values:t.values}:null}async#Un(e){this.#un||this.#bn(),await this.#In();return this.#An(e)||(await this.#Ln(e),this.#An(e))}#Ln(e){const t=this.#dn[e];if(t)return t;if(!this.#b||!this.#v)return Promise.resolve();this.#Nn(e);const n=this.#v,i=this.#b.fetchConfig(n,E,e).then(t=>{if(this.#v!==n)return;const i=t.ui,r=i?.useCase===e,o=i?.values;r&&Ie({values:o})?(this.#cn.delete(e),this.#ln[e]={variantId:i.variantId??null,variantName:i.variantName??null,template:i.template??null,values:o}):(this.#cn.add(e),delete this.#ln[e])}).catch(t=>{o.warn(`Failed to fetch config for use case '${e}' (non-blocking)`,t),delete this.#dn[e]});return this.#dn[e]=i,i}#Nn(e){this.#hn.has(e)||(this.#hn.add(e),o.warn(`[INTEGRATION] Use case '${e}' was not prefetched, so it is being fetched on demand — this first presentation will be slower, then it is cached. Nothing has failed. To make it instant, ask Encore to enable '${e}' for this app.`))}#En(e){return e&&"churn-intervention"!==e?this.#An(e)?.variantId:this.#sn}#bn(){if(!this.#b||!this.#v)return;const e=Date.now(),t=this.#v;this.#un=this.#b.fetchConfig(t,E).then(n=>{this.#v===t&&(this.#Sn(n),this.#Rn("remote",Date.now()-e))}).catch(t=>{o.warn("Failed to fetch remote config (non-blocking)",t),this.#Rn("fallback",Date.now()-e),this.#un=null})}#Rn(e,t){var n;this.#T&&this.#v&&this.#T.track({name:"sdk_sdui_config_loaded",distinctId:this.#v,properties:{...(n={...this.#sn&&{variantId:this.#sn},loadSource:e,loadDurationMs:t}).variantId&&{variantId:n.variantId},loadSource:n.loadSource,loadDurationMs:n.loadDurationMs,...n.presentationId&&{presentationId:n.presentationId}}})}async#In(){if(!this.#un)return;let e;await Promise.race([this.#un,new Promise(t=>{e=setTimeout(t,2500)})]),clearTimeout(e)}#On(e,t,n){return{layout:e,mode:t,...n?.header&&{header:n.header},...n?.display&&{display:n.display},remote:{...this.#on.appName&&{appName:this.#on.appName},...this.#an}}}async#Mn(e,n,i){let r=ae(this.#On(e,n,i));if("ok"===r.status)return{ok:!0,display:r.display,header:r.header};if(this.#un||this.#bn(),await this.#In(),r=ae(this.#On(e,n,i)),"ok"===r.status)return{ok:!0,display:r.display,header:r.header};if(this.#pn&&"missing"===r.status){const e={appDisplayName:"appDisplayName (pass display.appDisplayName, or set the app name in the Encore portal)",trialPeriod:"trialPeriod (pass display.trialPeriod, or set the app entitlement value + unit in the Encore portal)",logoUrl:"logoUrl (pass display.logoUrl, or set the app icon in the Encore portal)"},n=`Deferred mode requires host display values that did not resolve: ${r.missingFields.map(t=>e[t]).join("; ")}.`;return o.error(`[Encore SDK] Integration error: ${n}`),{ok:!1,result:{status:"dismissed",reason:{type:"error",error:{code:t.CONFIGURATION_ERROR,message:n}}}}}const s="Could not load the remote display configuration (network). The next show() call will retry.";return o.error(`[Encore SDK] ${s}`),{ok:!1,result:{status:"dismissed",reason:{type:"error",error:{code:t.NETWORK_ERROR,message:s}}}}}#Dn(e,t,n,i){if(!this.#T||!this.#v)return;const r=this.#En(i),o={transactionId:t.id,campaignId:t.offer.campaignId,presentationId:n,...r&&{variantId:r},...i&&{useCase:i}};this.#T.track("continue"===e?function(e,t){const{useCase:n,...i}=t;return{name:"sdk_offer_activation_continue",distinctId:e,properties:{...i,...f(n)}}}(this.#v,o):function(e,t){const{useCase:n,...i}=t;return{name:"sdk_offer_activation_dismissed",distinctId:e,properties:{...i,...f(n)}}}(this.#v,o))}#Pn(e,t,n,i){const r=new ve({display:n,partner:{...t.offer.advertiserName&&{name:t.offer.advertiserName},...t.offer.logoUrl&&{logoUrl:t.offer.logoUrl},...t.offer.imageUrl&&{imageUrl:t.offer.imageUrl}}});r.on("continue",()=>{r.destroy(),i()}),e.setContent(r.render()),o.debug("Primer screen displayed",{transactionId:t.id})}async#wn(e,n,i){const r=e?.useCase??"churn-intervention";let s=null;if("churn-intervention"!==r&&(s=await this.#Un(r),!s))return o.info(`[Encore SDK] No template for use case '${r}' on this app — no-op`),this.#T&&this.#v&&this.#T.track(g(this.#v,"no_template",w(),void 0,r)),{status:"unavailable"};if(!this.#en||!this.#b)return o.error("Managers not initialized"),{status:"dismissed",reason:{type:"error",error:{code:t.INITIALIZATION_ERROR,message:"Managers not initialized"}}};const a=(l=e?.layout,void 0===l?"list":"string"==typeof l&&ee.includes(l)?l:(o.warn("Unknown placement layout — falling back to 'list'",{layout:l}),"list"));var l;const d=e?.redemptionMode??this.#Zt.getRedemptionMode();let c,h;if("thankYou"===a&&"deferred"===d&&o.warn("thankYou + deferred: the claim will NOT redirect — you own calling redeem() after your flow completes. Most post-purchase surfaces want redemptionMode 'immediate'."),"deferred"===d){const t=await this.#Mn(a,d,e);if(!t.ok)return t.result;c=t.display,h=t.header}else{const t=ae(this.#On(a,d,e));c="ok"===t.status?t.display:{},h="ok"===t.status?t.header:{}}const u=this.#En(r),p=w();this.#T&&this.#v&&this.#T.track(function(e,t,n,i){return{name:"sdk_offer_presentation_triggered",distinctId:e,properties:{presentationId:t,...n&&{variantId:n},...f(i)}}}(this.#v,p,u,r));const m=await this.#Tn(p,r);if(m)return m;try{const l=e?.appearance,m={...l?.accentColor&&{primaryColor:l.accentColor},...l?.backgroundColor&&{backgroundColor:l.backgroundColor},...l?.textColor&&{textColor:l.textColor}};this.#rn?l&&function(e){const t=document.documentElement;e.primaryColor&&t.style.setProperty("--encore-primary-color",e.primaryColor),e.backgroundColor&&t.style.setProperty("--encore-bg-color",e.backgroundColor),e.textColor&&t.style.setProperty("--encore-text-color",e.textColor),e.borderRadius&&t.style.setProperty("--encore-border-radius",e.borderRadius),e.fontFamily&&t.style.setProperty("--encore-font-family",e.fontFamily),o.debug("Theme variables updated",e)}(m):(j(m),this.#rn=!0),o.debug("Fetching offers");const{offers:b}=await this.#en.fetchOffers(this.#in,!0,n);if(0===b.length)return o.info("No offers available"),this.#T&&this.#v&&this.#T.track(g(this.#v,"no_offer_available",p,u,r)),{status:"unavailable"};const x="thankYou"===e?.layout?e:void 0,I="thankYou"===a?x?.offerContext:h,C={...this.#on,...I?.title&&{titleText:I.title,customHeadline:I.title},...I?.subtitle&&{subtitleText:I.subtitle,customSubheadline:I.subtitle}},k=this.#_n(C),E=this.#en.beginPresentation(u,r),$=new Map;b.forEach(e=>{$.set(e.id,w())});const T=e=>{let t=$.get(e);return t||(t=w(),$.set(e,t)),t};this.#T&&this.#v&&this.#T.track(function(e,t,n,i,r){return{name:"sdk_offer_presentation_success",distinctId:e,properties:{presentationId:t,offerCount:n,...i&&{variantId:i},...f(r)}}}(this.#v,E,b.length,u,r));const S=e=>({campaignId:e.campaignId,...e.creativeId&&{creativeId:e.creativeId},...null!=e.merchantName&&{advertiserName:e.merchantName},impressionId:T(e.id),offerIndex:b.indexOf(e),totalOffers:b.length,presentationId:E,...u&&{variantId:u},...r&&{useCase:r}}),A=new Y,U=new J(b,C,"post-action-reward"===r?{mode:"paged",hideHeader:!0}:"thankYou"===a?{mode:"paged"}:void 0),L=await new Promise(l=>{let p=!1;const m=e=>{p||(p=!0,l(e))};let f=null;U.on("offerClaim",e=>{const i=e.offer;o.debug("User claimed offer",{offerId:i.id});const s=S(i);this.#zn(e=>function(e,t){return{name:"sdk_offer_claim_tapped",distinctId:e,properties:v(t)}}(e,s)),this.#zn(e=>y(e,{...u&&{variantId:u},actionType:"claimOffer",presentationId:E,currentState:"carousel"}));const l="immediate"===d?this.#kn():null;(async()=>{try{const e=T(i.id),t=b.indexOf(i),o=await this.#Fn(i,n,e,t,b.length,k,a,r);"immediate"===d?(this.#$n(o,l,s),this.#en?.endPresentation(void 0,r),A.hide(),m({status:"claimed"})):"list"!==a||"post-action-reward"===r?(f=o,this.#en?.endPresentation(void 0,r),m({status:"claimed"}),A.hide()):(f=o,this.#en?.endPresentation(void 0,r),this.#Pn(A,o,c,()=>{this.#Dn("continue",o,E,r),m({status:"claimed"}),A.hide()}))}catch(e){o.error("Error creating transaction",{offerId:i.id,error:e}),this.#yn("Transaction creation error",e),f||this.#zn(t=>function(e,t,n){return{name:"sdk_offer_claim_failed",distinctId:e,properties:{...v(t),reason:n.reason,...n.errorDescription&&{errorDescription:n.errorDescription},...n.transactionId&&{transactionId:n.transactionId}}}}(t,s,{reason:this.#en?"transaction_start_failed":"manager_unavailable",errorDescription:e instanceof Error?e.message:"Transaction failed"})),this.#Bn(l),this.#en?.endPresentation("error",r),A.hide(),m(f?{status:"claimed"}:{status:"dismissed",reason:{type:"error",error:{code:t.UNKNOWN_ERROR,message:e instanceof Error?e.message:"Transaction failed"}}})}})()}),U.on("complete",e=>{e.claimed||(o.debug("User declined last offer"),this.#en?.endPresentation("userDeclinedLastOffer",r),U.destroy(),A.hide(),m({status:"dismissed",reason:"userDeclinedLastOffer"}))}),A.on("close",()=>{if(this.#zn(e=>y(e,{...u&&{variantId:u},actionType:"close",presentationId:E,currentState:f?"primer":"carousel"})),f)return o.debug("User dismissed the primer"),p||this.#Dn("dismissed",f,E,r),U.destroy(),void m({status:"claimed"});o.debug("User closed modal"),this.#en?.endPresentation("userClosedModal",r),U.destroy(),m({status:"dismissed",reason:"userClosedModal"})}),A.on("backdropClick",()=>{if(f)return o.debug("User dismissed the primer (backdrop)"),p||this.#Dn("dismissed",f,E,r),U.destroy(),A.hide(),void m({status:"claimed"});o.debug("User clicked outside modal"),this.#en?.endPresentation("userClickedOutside",r),U.destroy(),A.hide(),m({status:"dismissed",reason:"userClickedOutside"})}),U.on("offerView",e=>{const{offer:t}=e,n=T(t.id),i=b.indexOf(t);this.#en&&this.#en.trackOfferPresented(t,n,i,b.length,r)}),U.on("scroll",e=>{this.#zn(t=>function(e,t){return{name:"sdk_sdui_scroll",distinctId:e,properties:{...t.variantId&&{variantId:t.variantId},scrollAxis:t.scrollAxis,scrollPosition:t.scrollPosition,presentationId:t.presentationId}}}(t,{...u&&{variantId:u},scrollAxis:e.axis,scrollPosition:e.position,presentationId:E}))}),A.on("hide",()=>{if(f)return p||this.#Dn("dismissed",f,E,r),void m({status:"claimed"});m({status:"dismissed",reason:"userClosedModal"})}),A.show();const g=U.render();let w,C;if("post-action-reward"===r&&s){const e=s.values?.text,t=Ce(I?.title,e?.rewardHeadline)??"Congratulations!";C=t;const n=Ce(I?.subtitle,e?.rewardSubheadline);w=ne(g,{reward:!0,header:{title:t,...n&&{subtitle:n}}})}else"thankYou"===a?(C=h.title,w=ne(g,{header:{...e?.header,...void 0!==h.title&&{title:h.title}},...x?.footer&&{footer:x.footer},...i&&{receipt:i}})):w=g;A.setContent(w),C&&A.setLabelledBy(te)});return A.destroy(),U.destroy(),L}catch(e){return o.error("Error in offer flow",e),this.#yn("Offer flow error",e),{status:"dismissed",reason:{type:"error",error:{code:t.UNKNOWN_ERROR,message:e instanceof Error?e.message:"Unknown error"}}}}}async#Fn(e,t,n,i,r,s,a,l){if(!this.#en)throw new Error("OfferManager not initialized");const d=await this.#en.startTransaction(e.campaignId,l);this.#en.trackOfferClaimed(e,n,d,i,r,l);const c=Date.now(),h={id:d,appAccountId:this.#v??"",...t?{placementId:t}:{},offer:{id:e.id,campaignId:e.campaignId,advertiserUrl:e.advertiserUrl,...e.trackingParameters&&{trackingParameters:e.trackingParameters},...e.merchantName&&{advertiserName:e.merchantName},...s&&{reward:s},...e.imageUrl&&{imageUrl:e.imageUrl},...e.logoUrl&&{logoUrl:e.logoUrl}},status:"claimed",createdAt:new Date(c).toISOString(),expiresAt:new Date(c+6048e5).toISOString(),...a&&{claimLayout:a},...l&&"churn-intervention"!==l&&{useCase:l}};return this.#mn.save(h),o.debug("Transaction created",{transactionId:h.id,placement:t}),h}#$n(e,t,n){const i=this.#Vn(e.offer.advertiserUrl,e.offer.trackingParameters,e.id),r=this.#Hn(i);n&&this.#zn(e=>function(e,t,n){return{name:"sdk_offer_webview_attempting_open",distinctId:e,properties:{...v(t),url:n.url,urlHost:n.urlHost}}}(e,n,{url:i,urlHost:r}));const s=this.#qn(t,i);return s||o.warn("Failed to open advertiser URL (popup blocked?)"),n&&(s?(this.#zn(e=>function(e,t,n){return{name:"sdk_offer_webview_opened",distinctId:e,properties:{...v(t),url:n.url,urlHost:n.urlHost,openedAt:n.openedAt}}}(e,n,{url:i,urlHost:r,openedAt:(new Date).toISOString()})),this.#Kn(n,e.id)):this.#zn(e=>function(e,t,n){return{name:"sdk_offer_webview_load_failed",distinctId:e,properties:{...v(t),url:n.url,urlHost:n.urlHost,loadSuccess:!1,reason:n.reason}}}(e,n,{url:i,urlHost:r,reason:"popup_blocked"}))),"churn-intervention"===(e.useCase??"churn-intervention")&&this.didGrant("provisional",e.id),this.#mn.markRedeemed(e.id),s}#Hn(e){try{return new URL(e).hostname}catch{return""}}#Kn(e,t){if("undefined"==typeof window||"undefined"==typeof document)return;const n=this.#v;if(this.#T&&n)try{this.#fn?.();const i=Date.now();let r=!1;const o=()=>{window.removeEventListener("focus",s),document.removeEventListener("visibilitychange",s),this.#fn===o&&(this.#fn=null)},s=()=>{if(r||"visible"!==document.visibilityState)return;r=!0,o();const s=(Date.now()-i)/1e3;this.#T?.track(function(e,t,n){return{name:"sdk_offer_claim_returned",distinctId:e,properties:{...v(t),...n.transactionId&&{transactionId:n.transactionId}}}}(n,e,{transactionId:t})),this.#T?.track(function(e,t,n){return{name:"sdk_offer_webview_dismissed",distinctId:e,properties:{...v(t),timeSpentSeconds:n.timeSpentSeconds}}}(n,e,{timeSpentSeconds:s}))};window.addEventListener("focus",s),document.addEventListener("visibilitychange",s),this.#fn=o}catch{}}#zn(e){const t=this.#v;this.#T&&t&&this.#T.track(e(t))}#Vn(e,t,n){try{const i=new URL(e);t&&Object.entries(t).forEach(([e,t])=>{i.searchParams.append(e,String(t))});let r=i.toString();return r=r.replace(/TRANSACTION_ID/g,n),o.debug("Built final advertiser URL",{original:e,final:r,transactionId:n}),r}catch(t){return o.error("Failed to build advertiser URL",{url:e,error:t}),e.replace(/TRANSACTION_ID/g,n)}}#kn(){try{return window.open("","_blank")}catch(e){return o.error("Failed to open blank advertiser window",{error:e}),null}}#qn(e,t){try{if(e)return e.location.href=t,e.focus(),!0;const n=window.open(t,"_blank","noopener,noreferrer");return!!n&&(n.focus(),!0)}catch(e){return o.error("Failed to navigate advertiser URL",{url:t,error:e}),!1}}#Bn(e){try{e?.close()}catch(e){o.error("Failed to close advertiser window",{error:e})}}#_n(e){const t=e.creditClaimedTitleText?.trim();if(t)return t;const n=e.entitlementValue?.trim(),i=e.entitlementUnit?.trim();if(n&&i){const t=e.appName?.trim();return t?`${n} ${i} of ${t}`:`${n} ${i}`}}#xn(e){return e?.placement?this.#mn.getByPlacement(e.placement):this.#mn.getLatest()}#Cn(e,t,n,i){try{const{reward:r,advertiserName:s,logoUrl:a,imageUrl:l}=t.offer,d="post-action-reward"===t.useCase||"list"!==t.claimLayout?"reward":"activation";let c={};if("activation"===d){const e=ae(this.#On("list","deferred",{display:{...n?.name&&{appDisplayName:n.name},...n?.logoUrl&&{logoUrl:n.logoUrl}}})),t="ok"===e.status?e.display:void 0,i=n?.name??t?.appDisplayName,r=this.#an.trialDurationDays,o=r?se.s2HostDetail(new Date(Date.now()+24*r*60*60*1e3)):void 0,s=n?.logoUrl??t?.logoUrl;i&&(c={host:{name:i,...t?.trialPeriod&&{trialPeriod:t.trialPeriod},...n?.status&&{status:n.status},...o&&{detail:o},...s&&{logoUrl:s}}})}const h=new be({variant:"activation"===d&&c.host?"activation":"reward",...r&&{reward:r},...s&&{advertiserName:s},...a&&{logoUrl:a},...l&&{imageUrl:l},...c});h.on("continue",()=>{h.destroy(),i()}),e.setContent(h.render()),o.debug("Redemption screen displayed",{transactionId:t.id})}catch(e){o.error("Failed to show redemption screen",e),i()}}};e.Encore=ke,e.default=ke,Object.defineProperty(e,"__esModule",{value:!0})});
2
2
  //# sourceMappingURL=encore.min.js.map