{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/player/AdBreakOrchestrator.cjs","../../src/player/AdBreakOrchestrator.ts","../../src/utils/mqttConfig.ts","../../src/utils/mqttClient.ts","../../src/utils/vastEnvironmentSignals.ts","../../src/utils/tracking.ts","../../src/player/PlayerControls.ts"],"names":["__create","Object","create","__defProp","defineProperty","__getOwnPropDesc","__getOwnPropNames","getOwnPropertyDescriptor","getOwnPropertyNames","__getProtoOf","getPrototypeOf","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","from","to","except","desc","key","call","__toESM","mod","isNodeMode","__esModule","value","__toCommonJS","AdBreakOrchestrator_exports","AdBreakOrchestrator","module","exports","DEFAULT_MQTT_CONFIG","enabled","brokerAddress","brokerPort","wsPort","username","password","topicPrefix","mqttConfig","buildMQTTBrokerUrl","brokerUrl","buildPlayerTopic","licenseKey","channel","import_mqtt","require","LOG","client","status","initMQTTClient","url","isMQTTEnabled","clientId","Math","random","toString","slice","mqtt","connect","keepalive","clean","reconnectPeriod","connectTimeout","queueQoSZero","err","console","warn","on","info","message","ensureMQTTClient","publishMQTT","topic","payload","publish","JSON","stringify","qos","PLAYER_ID_STORAGE_KEY","DEVICE_ID_STORAGE_KEYS","test","isMacroPlaceholder","inferDeviceIdType","navigator","ua","userAgent","resolveNativeDeviceId","bridgeSignals","readNativeBridgeSignals","platformDevice","readPlatformNativeDeviceId","deviceId","readStoredDeviceId","isZeroedAdId","getOrCreateStoredUuid","storageKey","window","existing","readStoredString","id","createUuid","localStorage","setItem","crypto","randomUUID","replace","char","floor","nibble","adinfo","webapis","tifa","getTIFA","lat","isLATEnabled","deviceIdType","limitAdTracking","webOSDev","lgudid","LGUDID","roku","Roku","rida","getPublisherUniqueId","ZEROED_AD_ID_PATTERNS","Set","t","trim","has","toLowerCase","candidates","__STORMCLOUD_CTV_AD_INFO__","__STORMCLOUD_CTV__","stormcloudCtv","AiryTV","Android","webOS","tizen","amazon","filter","Boolean","source","contentUrl","readBridgeValue","appId","appName","readBridgeBoolean","keys","result","normalized","normalizeBoolean","getItem","cachedBrowserId","buildAnalyticsContext","config","context","debugAdTiming","isLiveStream","inputStreamType","getClientInfo","screen","platform","vendor","maxTouchPoints","memory","deviceMemory","hardwareConcurrency","screenInfo","width","height","availWidth","availHeight","orientation","type","pixelDepth","deviceType","brand","os","model","isSmartTV","isAndroid","isWebView","isWebApp","includes","webosMatch","match","tizenMatch","tvMatch","androidModelMatch","outerHeight","outerWidth","matchMedia","matches","standalone","angle","substring","doNotTrack","visibilityState","buildPlayerMetricEvent","domain","location","hostname","origin","path","pathname","language","languages","join","cookieEnabled","referrer","document","encodedData","utf8","buffer","i","hashBuffer","hash","fallbackHash","timestamp","subtle","digest","Uint8Array","TextEncoder","encode","input","length","charCodeAt","Array","map","b","padStart","abs","Date","now","padEnd","resolvePlayerIdentitySeed","clientInfo","nativeDeviceId","persistedId","getBrowserID","sha256Hex","canPublish","flags","log","playerId","player_id","device_type","ad_loaded","adLoaded","ad_detect","adDetect","capture_at","captureAt","input_stream_type","publishTracking","body","sendAdLoadedTracking","adLoadedInfo","sendHeartbeat","sendAdImpressionTracking","adImpressionInfo","metricEvent","events","ad_played_count","ad_url","adUrl","error","heartbeatData","getAdAudioVolume","adPlayer","getOriginalMutedState","getOriginalVolume","host","timing","preloadPool","adConfig","cueManager","placeholder","inAdBreak","adDetectSentForCurrentBreak","continuousFetchingActive","continuousFetchLoopRunning","adPodQueue","maxPlaceholderDurationMs","pendingAdBreak","debug","analyticsContext","optimizedPodsEnabled","optimizedPods","attachImaEventListeners","notePodAdStarted","index","total","adIndex","currentAdIndex","showAds","isShowingPlaceholder","toISOString","catch","recordPlayedAdDuration","remaining","getRemainingAdMs","getAdPlayer","consecutiveFailures","adRequestQueue","queuedUrls","hasTimeToStartAnotherAd","tryNextAvailableAdWithRateLimit","handleAdPodComplete","errorMessage","errorPayload","errorCode","code","vastErrorCode","cause","innerError","causeMessage","String","handleAdFailure","clearAdFailsafeTimer","clearAdRequestWatchdog","activeAdRequestToken","resetGamNoFillCounter","expectedAdBreakDurationMs","getAdBreakEndWallClockMs","scheduleAdStopAtBreakBoundary","shouldShowPlaceholder","newAdPlayer","initialize","updateOriginalMutedState","setAdVolume","hidePlaceholder","video","muted","recreateAdController","preservedMutedState","preservedVolumeState","destroy","createAdPlayer","continueLiveStreamDuringAds","setAdPlayer","show","showPlaceholder","clearPendingAdBreak","clearTimeout","prefetchTimerId","pendingScte35CueKey","podAssignedByPrefetch","handleAdStart","_marker","scheduled","tags","baseVastUrl","adBreakDurationMs","mode","currentMuted","currentVolume","podPlayed","firstAdUrl","usePreloadedAd","preloadedController","preloaded","firstAdUrlArray","preservedMuted","preservedVolume","adVolume","fallbackPreloaded","ap","fallbackError","findCurrentOrNextBreak","currentTime","consumedVmapBreakIds","add","getAdBreakKey","selectVastTagsForBreak","apiVastTagUrl","durationSeconds","durationMs","failedVastUrls","clear","temporaryFailureUrls","totalAdRequestsInBreak","beginNewAdPod","volume","adBreakPlayedDurationMs","totalAdsInBreak","startOptimizedPod","getPreloadedAd","vastUrl","adController","vastUrls","shift","ensureLoadedAdFitsBudget","rejectLoadedAdForDuration","startContinuousFetching","resume","active","isLoopRunning","preloadPoolLoop","play","markAdStarted","enforceGlobalRateLimit","lastAdRequestTime","requestAds","startPreloadPool","swapToPreloadedAdPlayer"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YACIA,SAAWC,OAAOC,MAAM;QACxBC,YAAYF,OAAOG,cAAc;IACrC,EAAIC,eAAAA,CACJ,EAAIC,UADmBL,OAAOM,GACNN,OAAOO,cADuB,KACJ;IAClD,EAAIC,EAAAA,aAAeR,OAAOS,cAAc;QACpCC,IAAAA,OAAAA,EAAeV,KAAAA,EAAOW,EAAAA,OAAS,CAACC,cAAc;QAC9CC,IAAAA,MAAAA,CAAW,kBAACC,QAAQC;YACtB,EAAK,EAAIC,OAAAA,CAAQD,IACfb,EAAAA,KAAAA,GAAUY,QAAQE,MAAM,GAAA,KAAA,aAAA,KAAA,oBAAA,KAAA,KAAA,IAAA;cAAEC,EAAAA,GAAKF,GAAG,CAACC,KAAK,IAAA,YAAA,QAAA,CAAA,mBAAA,OAAA;gBAAEE,OAAAA;oBAAAA,GAAY,OAAA;oBAAA,cAAA;oBAAA,iBAAA;gBAAA;YAAK;QAC/D;IACA,EAAIC,eAAAA,GACF,IAAIC,CADY,OACJ,CAAA,OAAOA,MADFC,IAAID,MAAME,QAAQC,aACvB,SAAOH,KAAG,MAAM,YAAY,OAAOA,SAAS,YAAY;cAC7D,kCAAA,2BAAA;;;kBAAA,IAAII,MAAJ;kBACH,IAAI,CAACd,aAAae,IAAI,CAACJ,IAAIG,QAAQA,QAAQF,QACzCpB,UAAUmB,IAAIG,KAAK;oBAAEP,KAAK,SAALA;+BAAWG,EAAAA,EAAI,CAACI,IAAI;;sBAAEN,YAAY,CAAEK,CAAAA,OAAOnB,WAAAA,IAAAA,CAAAA,CAAiBgB,KAAAA,CAAMI,IAAG,KAAMD,KAAKL,UAAU,2BAAA,IAAA,CAAA,MAAA,sBAAA,GAAA,CAAA,EAAA,WAAA;gBAAC;;cAFpH,QAAK,WAAA,CAAWb,EAAAA,CAAAA,MAAAA,IAAAA,GAAAA,EAAkBe,SAAAA,iBAA7B,SAAA,6BAAA,QAAA,yBAAA;;YAAA;cAAA,CAAA,WAAA,aAAA;;;qBAAA,6BAAA;wBAAA,iBAAA;;;wBAAA;8BAAA;;;;MAGP,MAAA,CAAA;QACA,kCAAA,2BAAA;;UAAA,MAAA,YAAOC,+BAAP,IAAOA,KAAP,6BAAA,QAAA,yBAAA,iCAAOA;YAAP,IAAOA,SAAP;YACF,IAAA,aAAA,gBAAA,QAAA;gBAAA;gBAAA;gBAAA;gBAAA;aAAA,KAAA,KAAA;YACIK,IAAAA,IAAU,IAAA,aAACC,GAAAA,EAAKC,MAAAA;gBAAAA,KAAYd;gBAAAA;gBAAAA;gBAAAA;aAAAA,KAAAA,KAAAA;iBAAYA,SAASa,OAAO,OAAO5B,EAAAA,OAASS,CAAAA;gBAAAA;gBAAamB;gBAAAA,EAAQ,CAAC,GAAGR;aACnG,KAAA,KAAA,4DAAsE;YACtE,IAAA,WAAA,gBAAA,QAAA,wBAAiE;gBACjE,oEAAsE;gBACtE,mEAAqE;gBACrES,YAAc,CAACD,OAAO,CAACA,IAAIE,UAAU,GAAG3B,UAAUY,QAAQ,WAAW;kBAAEgB,OAAOH;kBAAKT,YAAY;gBAAK,GAAKJ,QACzGa;;gBAEEI,aAAe,sBAACJ;qBAAQR,YAAYjB,UAAU,CAAC,GAAG,cAAc;kBAAE4B,OAAO;gBAAK,EAAIH;;YAEtF,IAAA,CAAA,cAAA,CAAA,YAAoC,CAAA,SAAA,CAAA,SAAA;YC7BpCK,IAAAA,eAAAA,SAAA,CAAA,MAAA,QAAA;gBAAAnB,GAAAmB,6BAAA;gBAAAC,mBAAA,SAAAA;yBAAAA;;aAAA,KAAA;YAAAC,GAAAC,CAAAA,IAAA,GAAAJ,WAAAA,EAAAC,gBAAAA,QAAAA;gBDoCA,oBAA0B;gBEtBbI,oBAAkC;gBAC7CC,OAAS;gBACTC,aAAe;aACfC,WAAY;YACZC,OAAAA,CAAQ,kBACRC,KAAU,QAAA;gBAAA,YAAA;YAAA,IAAA,CAAA,GACVC,KAAU,GAAA;gBAAA,OAAA;YAAA,IAAA,CAAA,GACVC,QAAa,EAAA;gBAAA,SAAA;YAAA,IAAA,CAAA,GACR,WAAA;gBAAA,UAAA;YAAA,IAAA,CAAA,GACP,YAAA,eAAA;gBAAA,cAAA;YAAA,IAAA,CAAA,GAEaC,QAAyB,IAAA,eAAKR,IAAAA,OAAAA;gBAAAA,iBAAAA;YAAAA,IAAAA,CAAAA;UAOzC,OAAOQ,WAAWP,OAAA;;QFflB;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IEgBF,OAAA,CAAA;AAEO,SAASQ;IACd,IAAID,CAAAA,UAAWE,MAAAA,GAAA,EAAW,CAAA,EAAA,IAAOF,WAAWE,SAAA;QAC5C,IAAO,8BAAP,2BAAA;;UAAA,MAAA,YAA4CF,yBAA5C,SAAA,6BAAA,QAAA,yBAAA,iCAA4CA,CAA5BA,WAAWN,aAAa,EAAA,KAAqB,OAAjBM,WAAWJ,MAAM,EAAA;YAA7D,IAAO,MAAP;YACF,IAAA,QAAA,mBAAA,6BAAA,MAAA,CAAA,IAAA;YAEO,IAAA,CAASO,MAAAA,UAAAA,CACdC,UAAA,CAAA,CACAC,MAAAA,CAAA;gBAEA,KAAO,EAAA,CAA6BD,OAA1BJ,WAAWD,WAAW,EAAA,KAAkBM,OAAdD,YAAU,KAAW,OAAPC;YACpD;YFcA,IAAA,OAAA,UAAA,CAA0B,WAAA;gBG3D1BC,IAAAA,QAAiBxB,QAAAyB,QAAA,SAAA;oBAUXC,EAAM,EAAA,SAAA,MAAA,IAAA,CAAA;oBAIRC,IAAAA,CAA4B,MAAA,WAAA,YAAA,QAAA;wBAC5BC,GAAqB,IAAA;oBAmBlB,CAASC;gBACd,EAAIF,eAAAA,CAEJ,EAAMG,CAFQ,CAACC,IAEHZ,aAFoB;YAGhCS,SAAS;UAET,IAAMI,WAAW,iBAAuD,OAAtCC,KAAKC,MAAA,GAASC,QAAA,CAAS,IAAIC,KAAA,CAAM,GAAG;;QDHtE;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;MCKA,IAAI,CAAA,KAAA;QACFT,SAASH,YAAAa,OAAAA,CAAKC,OAAA,CAAQR,KAAK;YACzBE,UAAAA,KAAAA,MAAAA,EAAAA,IAAAA;;;4BACAjB,EAAUG,qCAAVH,0FAAUG,OAAWH,QAAA;gBAArBA;kBACAC,MAAAA,mBAAAA,6BAAAA,IAAUE,EAAAA,CAAAA,IAAAA,EAAWF,QAAA;kBACrBuB,WAAW,iBAAA;oBACXC,OAAO,GAAA,MAAA;sBACPC,CAAAA,gBAAiB;oBACjBC,gBAAgB;oBAChBC,GAAAA,UAAAA,CAAc,WAAA;kBAChB,EAAA;oBACF,IAASC,CAAAA,GAAK,KAAA,iBAAA,MAAA,IAAA,CAAA;oBACZhB,IAAAA,KAAS,KAAA,MAAA;wBACTiB,MAAQC,CAAAA,GAAA,CAAK,GAAM,OAAHpB,KAAG,sBAAqBkB;oBACxC;gBACF,EAAA,eAAA,CAEAjB,KAAOoB,EAAA,CAAG,WAAW;gBACnBnB,SAAS;cACTiB,QAAQG,IAAA,CAAK,GAAuBlB,OAApBJ,KAAG,kBAAoB,OAAHI;;;;;;;;;;;;;;;MACtC,KAAA,KAAA;IAEAH,OAAOoB,EAAA,CAAG,aAAa;QACrBnB,CAAAA,QAAS;2CACTiB;;4BAAQ,CAAK,GAAM,OAAHnB,KAAG,mIAAA;gBAAnBmB,GAAQG;YACV,IAAA,QAAA,iBAAA;YAEArB,IAAAA,GAAOoB,EAAA,CAAG,CAAA,UAAW;kBACnBnB,KAAAA,IAAS;gBACTiB,QAAQC,IAAA,CAAK,GAAM,OAAHpB,KAAG;UACrB;;;;;;;;;;;;;;;MAEAC,KAAAA,EAAOoB,EAAA,CAAG,SAAS,SAACH;QAClBhB,SAAS;QACTiB,CAAAA,OAAQC,IAAA,CAAK,GAAM,EAAA,GAAA,EAAHpB,KAAG,YAAWkB,IAAIK,OAAO;MAC3C,EAAA,OAAA,WAAA,aAAA;QAEAtB,OAAOoB,EAAA,CAAG,EAAA,OAAS;UACjB,IAAInB,WAAW,aAAa;cAC1BA,SAAS;YACX;YAAA,SAAA,uBAAA,OAAA,YAAA,cAAA,2CAAA,qBAAA,OAAA,CAAA;QACF,OAAA,SAAA,KAAA;IACF,EAAA,eAAA;QAEO,KAASsB,EAAAA,KAAAA;MACd,IAAInB,mBAAmB,CAACJ,QAAQ;QAC9BE;IACF,KAAA,iBAAA,KAAA;IACF,IAAA,OAAA,UAAA,WAAA;QAEO,KAASsB,EAAAA,UACdC,KAAA,EACAC,OAAA;MAEA,IAAI,CAACtB,iBAAiB;UACpB,KAAA,EAAO,QAAA,UAAA;QACT,OAAA,UAAA;MAEAmB;MAEA,EAAA,EAAI,CAACvB,IAAAA,IAAQ,MAAA,UAAA;YACX,KAAO,QAAA,MAAA,WAAA;QACT,IAAA,eAAA,OAAA,eAAA,UAAA,eAAA,OAAA;YAEA,EAAI,KAAA;YACFA,OAAO2B,OAAA,CAAQF,OAAOG,KAAKC,SAAA,CAAUH,UAAU;gBAAEI,KAAKvC,MAAAA,KAAWuC,EAAAA,CAAA,cAAA,WAAA,eAAA,MAAA;cAAI,KAAA;YACrE,OAAO;MACT,EAAA,OAASb,KAAK;UACZC,CAAAA,KAAAA,EAAQC,IAAA,CAAK,GAA4BM,OAAzB1B,KAAG,uBAA2B,OAAL0B,OAAK,MAAKR;QACnD,OAAO;AAEX,wBAAA;AHgBA,IAAA,kBAAA,gBAAsC;AI9G/B,IAAMc,KAAAA,mBAAwB,GAAA,MAAA;IA0BrC,EAAMC,EAAAA,UAAAA,CAAAA,UAAyB;MAC7B,EAAA,OAAA,aAAA,EAAA;QACA,QAAA,aAAA,GAAA;MACA;MACA,EAAA,OAAA,YAAA,KAAA,KAAA,GAAA;QACA,QAAA,eAAA,GAAA,OAAA,YAAA,GAAA,SAAA;MACA;MACA,KAAA;IACA;IACA,KAAA;QAsEI,KAAQC,IACR,UACA,UACA,UACA,qBAAA,UACA,UA0IN,GAASC,MAAmB,UAM5B,4BAAA,gBA6BE;MAvPA,EAAA,KAAA,UAAA,SAAA;IACF,IAAA,WAAA,UAAA,QAAA;IA8DO,IAAA,CAASC,QAAAA,UAAAA,MAAAA,IAAAA;MACd,EAAI,OAAOC,UAAAA,IAAc,MAAA,OAAa,OAAA,IAAA;QACpC,OAAO,EAAA,GAAA,OAAA,YAAA,IAAA;MACT,EAAA,sBAAA,UAAA,mBAAA,IAAA;MAEA,EAAMC,KAAKD,QAAAA,EAAUE,SAAA;QACrB,IAAI,CAAA,GAAA,UAAA,oBAAA,8BAAA,QAAQL,CAAA,CAAKI,GAAAA,EAAK,OAAO;QAC7B,IAAI,EAAA,GAAA,WAAA,oBAAA,+BAAA,SAAA,KAAiBJ,CAAAA,GAAA,CAAKI,KAAK,OAAO;QACtC,IAAI,MAAA,GAAA,WAAA,oBAAA,+BAAA,SAAA,IAAoBJ,IAAA,CAAKI,CAAAA,IAAK,OAAO;QACzC,IAAI,OAAA,GAAA,WAAA,oBAAA,+BAAA,SAAA,KAAsBJ,IAAA,CAAKI,CAAAA,IAAK,OAAO;QAC3C,IAAI,SAAA,EAAA,WAAA,oBAAA,gCAAA,sBAAA,SAAA,WAAA,cAAA,0CAAA,oBAAA,IAAA,EAAoCJ,GAAAA,EAAA,CAAKI,KAAK,OAAO;QACzD,IAAI,MAAA,GAAA,WAAA,oBAAA,+BAAA,SAAA,KAAqBJ,IAAA,CAAKI,KAAK,OAAO;MAC1C,IAAI,oBAAoBJ,IAAA,CAAKI,KAAK,OAAO;MACzC,EAAA,KAAO,KAAA,GAAA;IACT,IAAA,QAAA;IAaO,IAAA,GAASE,EAAAA;MACd,EAAA,EAAMC,MAAAA,UAAgBC;MACtB,EAAA,EAAMC,UAAAA,OAAiBC;MACvB,EAAA,EAAMC,UAAAA,CACJJ,cAAcI,QAAA,IACdC,wBACAH,eAAeE,QAAA;MAEjB,EAAA,EAAI,CAACA,SAAAA,GAAYV,mBAAmBU,aAAaE,aAAaF,WAAW;UACvE,OAAO,EAAA,GAAA;MACT,EAAA,GAAA,QAAA,CAAA,UAAA;QAEA,OAAOA,CAAAA;QACT,KAAA;QAEO,KAASG,OAAAA,eAAsBC,UAAA;QACpC,IAAI,OAAOC,EAAAA,SAAW,aAAa;YACjC,KAAO,KAAA,GAAA,GAAA,KAAA,CAAA;QACT,QAAA,aAAA,SAAA,OAAA,UAAA,CAAA,EAAA,IAAA;MAEA,IAAMC,CAAAA,IAAAA,GAAAA,GAAWC,KAAAA,CAAAA,UAAAA,CAAiBH;QAClC,IAAIE,IAAAA,MAAU;YACZ,CAAA,MAAOA;QACT,YAAA;QAEA,IAAME,KAAKC,IAAAA;QACX,IAAI,aAAA,GAAA,KAAA,CAAA;cACFJ,QAAAA,GAAAA,KAAAA,CAAAA,+BAAAA,aAAAA;aAAAA,GAAAA,aAAAA,MAAAA,GAAO,OAAPA,KAAOK,KAAAA,CAAAA,EAAAA,EAAAA,KAAA,gBAAA,IAAPL,KAAAA,sCAAAA,qBAAqBM,OAAA,CAAQP,YAAYI;MAC3C,EAAA,GAAA,IAAA,GAAA,KAAQ,CAAC,EAAA,CAAA,YAAA;QACT,OAAOA,CAAAA;QACT,KAAA;QAeA,KAASC,OAAAA;QACP,IAAI,OAAOG,EAAAA,SAAW,eAAe,OAAOA,OAAOC,UAAA,KAAe,YAAY;UAC5E,CAAA,IAAA,EAAOD,CAAAA,MAAOC,EAAAA,CAAAA,OAAA,KAAA,GAAA,QAAA,CAAA,UAAA;QAChB,QAAA;QAEA,KAAA,EAAO,uCAAuCC,OAAA,CAAQ,SAAS,SAACC;YAC9D,IAAMlF,IAAAA,IAAQ6B,KAAKsD,KAAA,CAAMtD,KAAKC,MAAA,KAAW;YACzC,IAAMsD,KAAAA,IAASF,SAAS,MAAMlF,QAASA,QAAQ,IAAO;UACtD,CAAA,IAAA,EAAOoF,CAAAA,MAAOrD,EAAAA,CAAAA,KAAA,CAAS,QAAA,CAAA,GAAA,QAAA,CAAA,WAAA,OAAA,QAAA,CAAA,OAAA,GAAA;QACzB,QAAA;QACF,KAAA;QAEA,KAASmC,OAAAA;QAKP,IAAI,OAAOM,EAAAA,SAAW,aAAa,OAAO,CAAC;MAE3C,IAAI,CAAA,IAAA,GAAA,QAAA,CAAA,cAAA,CAAA,GAAA,QAAA,CAAA,cAAA,GAAA,QAAA,CAAA,KAAA,GAAA;gBACcA;YAAhB,CAAA,GAAMa,UAAUb,kBAAAA,OAAec,OAAA,cAAfd,sCAAAA,gBAAwBa,MAAA;YACxC,IAAIA,IAAAA,IAAQ;gBACV,IAAME,CAAAA,MACJ,OAAOF,OAAOG,OAAA,KAAY,aAAaH,OAAOG,OAAA,KAAYH,OAAOE,IAAA;cACnE,CAAA,GAAI,OAAOA,CAAAA,CAAAA,OAAS,KAAA,GAAA,IAAYA,IAAAA,CAAAA,GAAQ,CAAC9B,MAAAA,aAAmB8B,OAAO;oBACjE,IAAME,MACJ,OAAOJ,OAAOK,YAAA,KAAiB,aAC3BL,OAAOK,YAAA,KACP,KAAA;oBACN,OAAO;wBACLvB,UAAUoB;wBACVI,cAAc;qBACV,KAAA,CAAA,CAAOF,QAAQ,GAAA,SAAY;wBAAEG,iBAAiBH;oBAAI,IAAI,CAAC;gBAE/D,IAAA;YACF,SAAA;MACF,EAAA,eAAQ,CAAC;MAET,EAAA,EAAI,CAAA,QAAA,CAAA,YAAA;YACF,IAAMI,IAAAA,OAAYrB,OAAeqB,QAAA;YACjC,CAAA,GAAIA,UAAU;oBAINA,CAAAA,SAAAA,IAAAA,CAAAA,MAAAA,WAAAA;gBAHN,IAAMC,GAAAA,CAAAA,KACJ,OAAOD,EAAAA,CAAAA,MAASE,MAAA,KAAW,EAAA,KAAA,GAAA,GACvBF,KAAAA,CAAAA,GAASE,MAAA,MACTF,CAAAA,GAAAA,QAAAA,CAAAA,MAAAA,GAAAA,GAAAA,GAASC,MAAA,cAATD,8BAAAA,mBAAmBA,SAASE,MAAA;kBAClC,IAAI,GAAA,IAAOD,WAAW,YAAYA,QAAQ;sBACxC,EAAA,EAAIzB,aAAayB,SAAS;0BACxB,IAAA,GAAO,SAAA,eAAA;4BAAEH,cAAc;0BAAUC,MAAAA,GAAAA,KAAAA,CAAAA,EAAiB;wBAAK,SAAA,iBAAA,CAAA,EAAA,EAAA;sBACzD,eAAA,CAAA,EAAA;oBACA,IAAI,CAACnC,mBAAmBqC,SAAS;sBAC/B,OAAO;0BAAE3B,CAAAA,IAAAA,CAAAA,IAAU2B,CAAAA;4BAAQH,cAAc;4BAAUC,iBAAiB;wBAAM;oBAC5E,EAAA,cAAA,GAAA,KAAA,OAAA,IAAA,CAAA,KAAA;kBACF,OAAA;YACF;MACF,EAAA,eAAQ,CAAC;MAET,EAAA,CAAA,CAAI,YAAA,CAAA,aAAA,CAAA,SAAA,IAAA,CAAA,KAAA;YACF,GAAA,CAAMI,OAAQxB,CAAAA,MAAeyB,IAAA,EAAA;cAC7B,GAAA,CAAID,MAAM;kBACR,IAAME,GAAAA,IACJ,OAAOF,KAAKG,oBAAA,KAAyB,aACjCH,KAAKG,oBAAA,KACLH,KAAKE,IAAA;gBACX,GAAA,CAAI,EAAA,KAAOA,GAAAA,CAAAA,KAAS,KAAA,CAAA,MAAYA,GAAAA,IAAAA,CAAQ,CAACzC,IAAAA,eAAmByC,OAAO;sBACjE,OAAO;0BAAE/B,UAAU+B;0BAAMP,OAAAA,GAAAA,IAAc,SAAA;wBAAQC,MAAAA,CAAAA,UAAiB;sBAAM;kBACxE,OAAA;YACF;MACF,EAAA,eAAQ,CAAC;MAET,EAAA,KAAO,CAAC,IAAA,WAAA;QACV,IAAA,OAAA,QAAA,CAAA,aAAA,GAAA,QAAA,CAAA,WAAA,QAAA;QAEMQ,IAAAA,OAAAA,QAAAA,CAAAA,IAA6C,MAAA,OAAA,CAAA,EAAA,IAAIC,IAAI;QACzD,IAAA,OAAA,QAAA,CAAA,cAAA,GAAA,QAAA,CAAA,QAAA,QAAA;MACA;IACD,YAAA,uBAAA,IAAA,CAAA;IAED,IAAA,EAAA,UAAA,oBAAA,8BAAA,QAAS5C,WAAAA,GAAmBzD,GAAAA,GAAA,EAAA,EAAA,WAAA,oBAAA,+BAAA,SAAA,UAAA,MAAA,GAAA;QAC1B,IAAMsG,IAAItG,IAAAA,EAAMuG,IAAA;MAChB,OAAO,CAACD,KACN,qCAAqC9C,IAAA,CAAK8C,MAC1C,oDAAoD9C,IAAA,CAAK8C,MACzDF,sBAAsBI,GAAA,CAAIF,EAAEG,WAAA;IAChC,WAAA,OAAA,UAAA,CAAA,8BAAA,OAAA,IAAA,OAAA,SAAA,CAAA,UAAA,KAAA,QAAA,EAAA,iBAAA,OAAA,MAAA,cAAA,sCAAA,6BAAA,eAAA,WAAA,cAAA,iDAAA,2BAAA,KAAA,MAAA,KAAA;IAEA,OAASpC,aAAarE,KAAA;QACpB,OAAOoG,sBAAsBI,GAAA,CAAIxG,MAAMuG,IAAA,GAAOE,WAAA;QAChD,IAAA;QAEA,KAASzC,EAAAA,SAAAA,GAAAA,SAAAA,CAAAA,GAAAA,MAAAA;oBAQP,IAAI,OAAOQ,WAAW,aAAa;uBACjC,OAAO,CAAC;QACV;QAEA,IAAMkC,aAAa;YAChBlC,OAAemC,0BAAA;YACfnC,IAAAA,GAAeoC,IAAAA,QAAAA,CAAAA,KAAA,GAAA;YACfpC,IAAAA,GAAeqC,IAAAA,QAAAA,CAAA,MAAA;YACfrC,EAAAA,KAAesC,EAAAA,IAAA,IAAA,CAAA,QAAA;YACftC,OAAeuC,OAAA;oBACfvC,OAAewC,KAAA;sBACfxC,OAAeyC,KAAA;YACfzC,IAAAA,GAAe0C,MAAA;8BAClB,CAAEC,MAAA,CAAOC;YAET,UAAA,wBAAA,2BAAA;;YAAA,MAAA,EAAA,QAAA,IAAqBV,IAAAA,2BAArB,SAAA,6BAAA,QAAA,yBAAA,iCAAiC;gBAAjC,GAAA,EAAA,uBAAA,UAAA,SAAA,cAAA,2CAAA,CAAWW,oBAAX,IAAA,CAAA,SAAA;gBACE,IAAMC,GAAAA,UACJC,aAAAA,GAAgBF,QAAQ;oBAAC,UAAA,UAAA,IAAA;oBAAc,OAAA,QAAA;oBAAO,KAAA,SAAA,eAAA;kBAAY;aAAe,KACzE,KAAA;SACF,CAAMG,EAAN,MACED,CAAAA,KAAAA,UAAgBF,QAAQ;;YAAC,sBAAkC,SAGvB,MAAM,MAAkB,CAAK,MAM/D,uBAUJ,KAEA,IAAM1B,QAKF,WACD,KAAKjC,KACR,IAAMkC;;;;oCA5BqB,QAAA,iBAAA,iBAAA,OAAA,MAAA,cAAA,qCAAA,eAAA,MAAA,CAAA;;;;;;;;;;;;;;4BAAiB,GAAA,MAAA,CAAA,MAAA,CAAA,WAAA,IAAA;4BAAA;4BAAA;4BAAA;;;;;2BAA+B,IAAA,CACzE,KAAA,UAAA,aAAA;4BACF,IAAM6B,MAAAA,IACJF,cAAAA,EAAgBF,IAAAA,CAAAA,GAAQ;8BAAC;+BAAW,SAAA,mBAAA;iCAAM,IAAA,WAAA,KAAA,MAAA;4BAAkB,CAAA,GAAK,EAAA,CAAA,IAAA,KAAA,MAAA,EAAA,IAAA;8BACnE,IAAMlD,CAAAA,EAAAA,GAAAA,KACJoD,UAAAA,CAAAA,KAAgBF,QAAQ;gCACtB;gCACA,MAAA;8BACA;oBACA;;wBAAA,OAAA,MAAA,CAAA,MAAA,CAAA,WAAA;;;iCAAA;;;2BACA,GAAA,IAAA,CAAA,IAAA,WAAA,aAAA,GAAA,CAAA,SAAA;mCAAA,EAAA,QAAA,CAAA,IAAA,QAAA,CAAA,GAAA;2BAAA,IAAA,CAAA;;;;8BAEA,EAAA,SACA;;;;;;2BAID,KAAK,KAAA;6BAER,CAAI,CAACC,CAAAA,KAAAA,MAAAA,GAAc,CAACnD,EAAAA,EAAAA,KAAAA,GAAY,CAACqD,SAAS,CAACC,SAAS;+BAE9C9B,MAAAA,QACJ4B,EAAAA,CAAAA,aAAgBF,QAAQ;mCACtB,KAAA,CAAA,IAAA,OAAA;oCACA,EAAA;kCACA;mCACA,KAAA,GAAA,CAAA,MAAA,QAAA,CAAA,IAAA,QAAA,CAAA,GAAA;gCACI3D,KAAAA,GAAAA,GAAAA,QAAAA,CAAAA,IAAAA,QAAAA,CAAAA,IAAAA;6BACFkC,KAAAA,MAAAA,GAAAA,GAAkB8B,KAAAA,CAAAA,IAAAA,QAAkBL,CAAAA,CAAAA,GAAAA,GAAQ,CAAA,QAAA,CAAA,IAAA;;;+BAChD,SAAA,YAAA,MAAA,EAAA,MAAA,CAAA,IAAA;;;;oBACA;;gBACA,mBAAA,UAAA;gBACA,SAAA;eACD,SAAA;eAED,OAAO,GAAA,EACDC,KADC,QACY;kBAAEA,YAAAA;YAAW,IAAI,CAAC,GAC/BE,EAAAA,MAAQ,gBAAA;kBAAEA,GAAAA,IAAAA;eAAM,IAAI,CAAC,GACrBC,KAAU,OAAVA,MAAU;kBAAEA,SAAAA;aAAQ,IAAI,CAAC,GACzBtD,KAAW,OAAXA,KAAAA,EAAW,OAAA,CAAA;gBAAEA,UAAAA;SAAS,CAAI,CAAC,CAAL,EACtBA,QAAAA,IAAYwB,MAAAA,SAAe;;;;;kCAAEA,OAAAA,OAAAA;;;6BAAa,IAAI,CAAC,GAC/CxB,YAAYyB,mBAAmB,OAAO;;kCAAEA,iBAAAA;oBAAqB;;wBAAA,UAAA,0BAAA;;;8BAAL,IAAI,CAAC,GAAA;0BAErE;;wBAAA;;;;;;QAhDA,CAAA,WAAA,UAAA;UAAA,CAAA,QAAA,mBAAA;;;;4BAkDQ,kBAIR,CAAA,gBAAA,CAAWlG,qDAJX,GAAO,CAAC;;;;;;6BAlDR,IAAA,yBAAA;4BAAA,QAAA,CAAA,CAAA,QAAA,aAAA;;;kCAAA;;;wBAAA,aAAA;;;kCAAA;;;;oBAkDA,aAAQ,mBAAA,MAAA,SAAA,cAAA,8BAAA,mBAAA,aAAA,GAAA,IAAA,OAAA,WAAA;oBACV;;wBAAA;4BAEA,KAAS6H,MAAAA,UAAgBF,MAAA,EAAaM,IAAA;gCACpC,SAAA,WAAA,UAAA,IAAA,2BAAA;;gCAAA,KAAA,GAAA,kBAAA,MAAA,OAAkBA,CAAAA,cAAlB,6BAAA,kBAAkBA,oBAAlB,SAAA,6BAAA,QAAA,yBAAA,iCAAwB;oCAAxB,CAAA,GAAA,kBAAA,MAAWjI,CAAX,OAAA,cAAA,6BAAA,kBAAA;oCACE,IAAMM,QAAQqH,mBAAAA,6BAAAA,MAAA,CAAS3H,IAAG;gCAC1B,GAAA,CAAI,OAAOM,OAAAA,GAAU;4BAAA,UAAYA,OAAO,EAAA,QAAA,eAAA;wBAAA,IAAA,CAAA;;;;gBAExC;;YACA,IAAI,OAAOA,EAAAA,QAAU,EAAA,EAAA,OAAA,CAAY,CAAA,IAAA;kBAC/B,IAAI;sBACF,IAAM4H,OAAAA,EAAS5H,MAAML,IAAA,CAAK0H,SAAAA;oBAC1B,IAAI,OAAOO,WAAW,YAAYA,QAAQ;kBACxC,OAAOA;wCAAAA,UAAAA,EAAAA,YAAAA;YAAAA,SATf;;;;;oBASeA,UAAAA,oEAAAA,CAAAA;;;;;;;;;;;8BAEX,EAAA,MAAA,SAAQ,CAAC,EAAA,SAAA;kCACX,IAAA;8BACF,SAAA,aAAA,SAAA;;;;;;;;;;oBAbA;wBAAA,IAAA,KAAA;;;;;;;;;;;;;;;mCAuBE;;;;;;kCAvBF,EAAA,aAAA;;;;;;;;;;;;;;;;;;;oBAcA,OAAO,KAAA,IAAA,YAAA,aAAA;oBACT,gBAAA,YAAA,eAAA;wBAEA,GAASF,GAAAA,eAAkBL,MAAA,EAAaM,IAAA;4BACtC,kCAAA,2BAAA;;gCAAA,MAAA,WAAA,CAAkBA,yBAAlB,SAAA,6BAAA,QAAA,yBAAA,iCAAwB;kCAAxB,IAAWjI,EAAAA,IAAX,aAAA,KAAA;kCACE,IAAMM,MAAAA,EAAQqH,eAAAA,IAAAA,KAAAA,wBAAAA,MAAA,CAAS3H,IAAG;gCAC1B,IAAMmI,aAAaC,iBAAiB9H;;gCAElC,OAAO6H;;;;;;;4BAET,IAAI,CAAA,MAAO7H,KACT,IAAI,CADe,YAAY,oDAE7B,IAAM4H,SAASE,iBAAiB9H,MAAML,IAAA,CAAK0H;;;;;;;;;;;wBAG3C;;UACF,EAAA;wCAAA,IAAQ,CAAC,KAAA;YAAA,SAAA,sBAZb;;;;;oBAYa,UAAA,oEAAA,CAAA,GAAA,QAAA,oEAAA,CAAA;8BACX,MAAA,aAAA;;;;;;;;;;;;;;;;;wBAbF,YAAA,YAAA,aAAA;;;;;;oBAAA;;;;;;;;;;;;qBAAA,6BAAA;;;;kBAAA,EAAA,qBAAA,KAAA,IAAA,SAAA,iBAAA;sBAAA;;;iCAeO,EAAA,EAAA,EAAA,MAAA,EAAA,WAAA,EAAA,QAAA,EAAA,UAAA,EAAA,WAAA;;QACT,IAAA,CAAA,SAAA,GAAA;QAEA,IAAA,CAASjD,OAAAA,GAAAA;YACP,CAAA,oBAAA,GAAA,UAAA,2BAAA;;YAAA,CAAA,OAAA,YAAkBb,KAAAA,GAAAA,mCAAlB,SAAA,6BAAA,QAAA,yBAAA,iCAA0C;gBAA1C,IAAW7D,MAAX,aAAA,GAAA;gBACE,IAAMM,OAAAA,CAAQ0E,EAAAA,EAAAA,aAAiBhF;gBAC/B,IAAIM,GAAAA,GAAAA,CAAO,CAAA;oBACT,OAAOA,UAAAA,GAAAA;gBACT,WAAA,GAAA;YACF,CAAA,IAAA,GAAA;;YALA,CAAA,WAAA,GAAA;YAAA,CAAA,QAAA,GAAA;;;;;;2BAAA;;;;;;2BAAA;;kCAAA,WAAA,IAAA,CAAA,IAAA,CAAA,MAAA;;;;YAOF,KAAA;iBAAA;;cADE,OAAO,KAAA,oDAAA;YACT;gBAEA,KAAS0E,EAAAA,CAAAA,CAAAA,IAAAA,CAAAA,IAAAA,CAAAA,GAAiBhF,GAAA,CAAA,aAAA;cACxB,IAAI,OAAO8E,WAAW,aAAa;;;;kCACjC,OAAO,KAAA;;gBACT,IAAA,WAAA,IAAA,CAAA,IAAA,CAAA,WAAA;gBAEA,IAAI,KAAA,EAAA,CAAA,kBAAA,SAAA;2BACYA,KAAAA,CAAAA,gBAAAA;sBAAd,EAAA,EAAMxE,IAAAA,KAAAA,CAAQwE,CAAAA,sBAAAA,OAAOK,YAAA,cAAPL,2CAAAA,qBAAqBuD,OAAA,CAAQrI;;wBAC3C,OAAOM,CAAAA,GAAAA,CACT,IADkB,KAAA,CACV,qBAAA,CAAA,oBAAA,8BAAA,QAAA,KAAA,KAAA,OAAA,QAAA,KAAA,GAAA,IAAA,KAAA,KAAA,eAAA,oBAAA,8BAAA,QAAA,KAAA,uCAAA;oBAER;gBACF;gBAUA,KAAS8H,IAAAA,EAAAA,CAAAA,UAAiB9H,KAAA,MAAA;oBACxB,EAAI,OAAOA,UAAU,MACnB,KAD8B,CAC9B,EAAOA,EAAAA,CAAAA,MAAAA,CAAAA,UAAAA,EACT;wBAAA,QAAA;wBAAA,SAAA,MAAA,MAAA,CAAA,cAAA;wBAAA,WAAA,aAAA,GAAA,IAAA,OAAA,WAAA;oBAAA,GACI,MAAA,EAAOA,UAAU,IAAA,EACnB,IAD6B,CAC7B,CAAA,CAAOA,UAAU,CACnB;oBACA,EAAI,IAAA,IAAOA,EAAAA,CAAAA,OAAU,UAAU,KAAA,CAAA,MAAA,IAAA,CAAA,WAAA;sBAC7B,EAAM6H,YAAAA,CAAa7H,KAAAA,EAAMyG,IAAAA,CAAAA,MAAA,UAAA;sBACzB,IAAA,CAAIoB,KAAAA,CAAAA,SAAe,OAAOA,GAAAA,GAAAA,SAAe,UAAUA,eAAe,OAAO;0BACvE,IAAA,IAAO,CAAA,EAAA;wBACT,QAAA,GAAA,CACA,CAAIA,eAAe,OAAOA,eAAe,WAAWA,eAAe,MAAM,EAAA,OAAA,WAAA,mBAAA,OAAA,MAAA,cAAA,CAAA,MAAA,KACvE,IAAA,IAAO,UAAA,CAAA,MAAA,GAAA,IAAA;4BAAA,YAAA,qBAAA,MAAA,cAAA;wBAAA,IAAA,CAAA;oBAEX;oBACA,IAAA,CAAO,KAAA,CAAA,QAAA,EAAA;wBACT,IAAA,MAAA,MAAA,CAAA,uBAAA,IAAA;4BJ/DA,MAAA,SAAwB,sBAAA;wBKxWpBG,OAAAA,OAAiC;4BAErBC,MAAAA,gBACdC,GAAAA,GAAA;wBAEMC,UAAkC,CAAC;oBACzC,EAAID,OAAOE,aAAA,EAAe;oBACxBD,QAAQC,aAAA,GAAgB;gBAC1B,SAAA,EAAA,CAAA,YAAA,SAAA;oBACA,EAAIF,EAAAA,KAAOG,UAAAA,EAAA,KAAiB,KAAA,GAAW;sBACrCF,EAAAA,MAAQG,QAAAA,OAAA,GAAkBJ,OAAOG,YAAA,GAAe,SAAS;wBAC3D,IAAA,YAAA,aAAA,IAAA,IAAA,aAAA,SAAA,IAAA;wBACA,GAAOF,CAAAA,gBAAAA,aAAAA,aAAAA;wBACT,IAAA,UAAA,aAAA,OAAA,IAAA,aAAA,YAAA,IAAA;wBAEO,CAASI,GAAAA,QAAAA,aAAAA,KAAAA,IAAAA,aAAAA,UAAAA,IAAAA,aAAAA,KAAAA;wBASLC,SACCA,MAAAA,GACIA,UACCA,QACCA,OADDA,GACCA,QAAAA,MAAAA,EAAAA,KAAAA,KACFA,UAwHVhE,SAA6BA,UAO/BA,4BAAAA,gBAsBWb;wBAlKPC,IAAAA,CAAKD,UAAUE,MAAAA,GAAA,eAAA,SAAA,kBAAA,WAAA;4BACf4E,SAAW9E,OAAAA,EAAU8E,QAAA,YAAA,OAAA,eAAA;wBACrBC,SAAS/E,UAAU+E,MAAA,IAAU;wBAC7BC,IAAAA,OAAAA,MAAiBhF,UAAUgF,cAAA,IAAkB;4BAC7CC,IAAAA,CAAUjF,UAAkBkF,IAAAA,OAAAA,CAAA,IAAgB,KAAA,WAAA,QAAA,MAAA,OAAA,IAAA,OAAA;4BAC5CC,gBAAAA,GAAsBnF,UAAUmF,GAAAA,IAAA,GAAAA,CAAuB;wBAEvDC,aAAa;sBACjBC,KAAA,GAAOR,UAAAA,oBAAAA,8BAAAA,QAAQQ,KAAA;sBACfC,MAAA,GAAQT,EAAAA,CAAAA,QAAAA,MAAAA,cAAAA,gBAAAA,eAAAA,SAAQS,MAAA;sBAChBC,IAAAA,OAAA,GAAYV,KAAAA,MAAAA,oBAAAA,+BAAAA,SAAQU,UAAA;oBACpBC,WAAA,GAAaX,WAAAA,oBAAAA,+BAAAA,SAAQW,WAAA;oBACrBC,KAAAA,EAAAA,CAAAA,KAAcZ,EAAAA,UAAAA,CAAAA,oBAAAA,gCAAAA,sBAAAA,SAAQY,WAAA,cAARZ,0CAAAA,oBAA6Ba,IAAA,KAAQ;sBACnDC,IAAAA,MAAAA,CAAA,GAAYd,WAAAA,MAAAA,cAAAA,+BAAAA,SAAQc,UAAA;oBACtB,MAAA,MAAA,CAAA,sBAAA;oBAEA,EAAIC,IAAAA,MAAAA,CAAAA,GAAqD,iBAAA,GAAA;oBACzD,EAAIC,IAAAA,KAAQ,EAAA,GAAA;oBACZ,EAAIC,IAAAA,EAAK,IAAA,CAAA,qBAAA;oBACT,EAAIC,EAAAA,MAAAA,CAAQ,QAAA,IAAA,MAAA,MAAA,CAAA,yBAAA,IAAA,QAAA,MAAA,MAAA,CAAA,wBAAA,MAAA,MAAA;wBACRC,MAAAA,MAAAA,CAAY,6BAAA;wBACZC,IAAAA,MAAAA,GAAY,EAAA,EAAA;4BACZC,QAAAA,EAAY,CAAA,CAAA;wBACZC,WAAW;oBAEf,EAAIlG,GAAGmG,QAAA,CAAS,UAAU;sBACxBP,IAAAA,IAAAA,CAAQ,WAAA,GAAA,WAAA,CAAA,iBAAA,MAAA,IAAA,CAAA,WAAA;sBACRC,EAAAA,GAAK,GAAA,oBAAA,EAAA;wBACLE,IAAAA,MAAAA,GAAY,EAAA,EAAA;4BACZJ,QAAAA,GAAa,CAAA;wBACb,IAAMS,aAAapG,GAAGqG,KAAA,CAAM;wBAC5BP,MAAAA,GAAQM,CAAAA,CAAAA,WAAa,GAAA,MAAsB,OAAbA,EAAAA,QAAA,CAAW,EAAE,IAAK;wBAClD,GAAA,GAAA,EAAWpG,GAAGmG,QAAA,CAAS,MAAA,GAAA,CAAU;sBAC/BP,QAAQ;oBACRC,KAAK;oBACLE,KAAAA,EAAAA,CAAAA,IAAY,cAAA;sBACZJ,aAAa,UACb,IAAMW,EAAAA,IAAAA,CAAAA,MAAAA,CAAatG,GAAGqG,KAAA,CAAM,CAAA,EAC5B;wBAAA,EAAME,MAAAA,IAAUvG;wBAAGqG,KAAA,CAAM,GAAA,MAAA,MAAA,CAAA,cAAA;wBAA+B,WAAA,CAAa,YAAA,GAAA,IAAA,OAAA,WAAA;oBAAA,GACrEP,MAAAA,GAAQQ,aACJ,EAEN,GAAA,EAAA,CAAA,CAFgCC,AAErBvG,GAAGmG,IAFCG,IAED,CAAS,CACrBV,IAHa,CAAW,EAAE,CAGlB,CAHkB,EAEO,GAFI,OAAPW,SAAU5D,IAAA,KACpC;sBAGJkD,EAAAA,CAAAA,EAAK,IAAA,IAAA,CAAA,KAAA,CAAA,KAAA,EAAA;wBACLE,MAAAA,IAAAA,CAAAA,EAAY,GAAA,CAAA,KAAA,GAAA;wBACZJ,IAAAA,MAAAA,IAAa,CAAA,EAAA;4BACf,CAAA,IAAW3F,GAAGmG,GAAAA,CAAAA,IAAA,CAAS,YAAYnG,GAAGmG,QAAA,CAAS,UAAU;wBACvDP,QAAQ;sBACRC,KAAK;sBACLE,IAAAA,MAAAA,CAAAA,EAAY,kBAAA;sBACZJ,IAAAA,MAAAA,CAAAA,GAAa,mBAAA;oBACf,MAAA,IACE3F,EAAAA,CAAGmG,QAAA,CAAS,WAAA,GACXnG,CAAAA,GAAGmG,QAAA,CAAS,WAAWrB,OAAOqB,QAAA,CAAS,OAAM,GAC9C;sBACAP,IAAAA,KAAQ,EAAA,GAAA;sBACRC,EAAAA,CAAAA,EAAK,IAAA,SAAA,EAAA;wBACLE,YAAY;sBACZJ,aAAa;oBACf,MAAA,IACE3F,EAAAA,CAAGmG,QAAA,CAAS,UAAA,GAAA,CACXnG,CAAAA,GAAGmG,QAAA,CAAS,cAAcnG,GAAGmG,QAAA,CAAS,KAAI,GAC3C;sBACAP,IAAAA,KAAQ,CAAA,CAAA,sBAAA,CAAA,MAAA,IAAA,CAAA,WAAA;sBACRC,EAAAA,CAAK,WAAA,MAAA,MAAA,CAAA,gBAAA;sBACLE,EAAAA,MAAAA,KAAY,EAAA;wBACZJ,QAAAA,GAAAA,CACF,CADe,GACJ3F,GAAGmG,QAAA,CAAS,YAAYnG,GAAGmG,QAAA,CAAS,UAAU,IAAA,OAAA,WAAA,mBAAA,OAAA,MAAA,cAAA,CAAA,MAAA,GACvDP,MAAAA,CAAQ,aAAA,CAAA,MAAA,GAAA,IAAA;4BAAA,YAAA,qBAAA,MAAA,cAAA;wBAAA,IAAA,CAAA;sBAERG,YAAY;sBACZJ,EAAAA,MAAAA,MAAa,CAAA,uBAAA,IAAA;wBACf,GAAA,GAAA,EAAW3F,GAAGmG,QAAA,CAAS,YAAY,KAAA;sBACjCP,KAAAA,GAAQ;wBACRC,MAAK,mBAAA;sBACLE,YAAY;oBACZJ,aAAa;cACf;;;;8BAEA,IAAI3F,GAAGmG,QAAA,CAAS,YAAY;oBAC1BH,IAAAA,CAAAA,KAAAA,EAAY;sBACZH,KAAK,CAAA,GAAA,CAAA;oBACLF,aAAa,SAAS/F,IAAA,CAAKI,MAAM,WAAW;oBAE5C,EACEA,GAAGmG,QAAA,CAAS,UAAA,IACXpB,CAAAA,SAAAA,IAAAA,IAAAA,CAAAA,CAAmB,KAClB/E,CAAAA,EAAGmG,QAAA,CAAS,gBACZnG,GAAGmG,QAAA,CAAS,SAAQ,GACtB;sBACAR,SAAAA,IAAa,CAAA,IAAA,CAAA,WAAA;wBACbI,YAAY,SAAA,UAAA;0BACZH,EAAAA,CAAAA,KAAQA,EAAAA,QAAU,YAAY,eAAeA;wBAC/C,QAAA,GAAA,CAAA;sBAEA,IAAMY,oBAAoBxG,GAAGqG,KAAA,CAAM;sBACnC,EAAA,CAAA,CAAIG,UAAAA,CAAAA,IAAAA,MAAqBA,iBAAA,CAAkB,EAAC,EAAG;0BAC7CV,GAAAA,KAAQU,UAAAA,OAAA,CAAkB,EAAC;sBAC7B,EAAA,CAAA,oBAAA,GAAA;gBACF;gBAEA,IAAI,iBAAmB5G,IAAA,CAAKI,KAAK,IAAA,qBAAA;oBAC/B6F,GAAK,oBAAA,SAAA,iBAAA;oBACLF,UAAAA,GAAa;sBACbC,EAAAA,MAAQ;wBACR,IAAI7F,KAAAA,KAAUgF,EAAAA,YAAA,GAAiB,KAAK,OAAOnF,IAAA,CAAKI,KAAK;4BACnD2F,CAAAA,IAAAA,CAAAA,KAAAA,CAAAA,CAAa,IAAA,GAAA;sBACf,OAAA,OAAA;wBACF,IAAA,IAAA,CAAA,KAAA,EAAA;4BAEKK,QAAAA,IAAa,CAACD,aAAa,CAAC,SAASnG,IAAA,CAAKI,KAAK,6BAAA;wBAClD,IAAIA,GAAGmG,QAAA,CAAS,YAAY;0BAC1BN,KAAK;wBACLF,aAAa;oBACf,KAAA,IAAW3F,GAAGmG,QAAA,CAAS,SAAA,CAAU,CAAC,EAAA,CAAA,IAAA,CAAA,CAASvG,IAAA,CAAKI,KAAK,sBAAA;sBACnD6F,KAAK,OAAA,IAAA,CAAA,IAAA,CAAA,cAAA,CAAA;wBACLF,CAAAA,CAAAA,WAAa,CAAA;wBACb,IAAIZ,UAAAA,OAAiB,GAAGY,aAAa;oBACvC,OAAA,CAAA,GAAW3F,GAAGmG,QAAA,CAAS,SAAA,CAAU,qBAAA;wBAC/BN,KAAK,eAAA;wBACLF,aAAa,MAAA;sBACf,EAAA,CAAA,WAAA,CAAA,IAAA;oBACF,YAAA,eAAA;oBAEA,EAAIC,EAAAA,CAAAA,OAAU,WAAW,EAAA,GAAA;oBACvB,IAAId,OAAOqB,QAAA,CAAS,aAAanG,GAAGmG,QAAA,CAAS,WAAWP,QAAQ;kBAChE,IAAId,OAAOqB,QAAA,CAAS,UAAUP,QAAQ;;;;kCACtC,IAAId,OAAOqB,QAAA,CAAS,cAAcnG,GAAGmG,QAAA,CAAS,QAAQP,QAAQ;gBAChE,IAAA,IAAA,CAAA,eAAA,IAAA,MAAA;oBAEAK,UAAY,GAAA,IAAA,CAAA,eAAuBrG,IAAA,CAAKI;oBAExC,EAAIY,EAAAA,CAAAA,SAAAA,MAAAA,GAAAA,KAAAA,MAAAA,8BAAAA,QAAQ6F,WAAA,MAAgB,KAAK7F,EAAAA,WAAAA,oBAAAA,+BAAAA,SAAQ8F,UAAA,MAAe,GAAG;oBACzDT,YAAY;gBACd,IAAA,CAAA,cAAA,GAAA;gBAEAC,IAAAA,CAAAA,MACEtF,IAAAA,CAAAA,EAAO+F,UAAA,CAAW,MAAA,GAAA,KAAA,gBAA8BC,OAAA,IAC/ChG,OAAOb,SAAA,CAAkB8G,UAAA,KAAe,QACzCjG,EAAAA,iBAAAA,OAAOgE,MAAA,cAAPhE,sCAAAA,6BAAAA,eAAe4E,WAAA,cAAf5E,iDAAAA,2BAA4BkG,KAAA,MAAU,KAAA;gBAExC,IAAA,CAAA,EAAO,MAAA,CAAA,qBAAA,GAAA;kBACLlB,OAAAA;;;;yBACAC,IAAAA,wDAAAA;kBACAC,OAAOA,KAAAA,IAAS9F,GAAG+G,SAAA,CAAU,GAAG,MAAM;kBACtCpB,YAAAA,gDAAAA;qBACAI,WAAAA,GAAAA,OAAAA;;wBACAC,WAMA/F,MACA6E,QAAAA,KASAkC,mBAEAC,0EAqB2B9F,wCAsB7B,yCAkBA,IAAIiD,OAkBS8C,uCAqCbhI,qBAmDE;;;;gCAzLA8G,SAAAA,GAAAA,IAAAA,CAAAA,QAAAA,CAAAA,sBAAAA,GACAC,EAAAA,CAAAA,IAAAA,CAAAA,GAAAA,EAAAA,CAAAA,WAAAA,GAAAA;oCAEAkB,QAAQvG,GAAAA,IAAOwG,QAAA,CAASC,QAAA;sCACxBC,EAAAA,CAAAA,KAAQ1G,GAAAA,CAAAA,GAAOwG,QAAA,CAASE,MAAA,EAAA,CAAA,GAAA,CAAA,IAAA,CAAA,QAAA,CAAA,aAAA,CAAA;oCACxBC,MAAM3G,OAAOwG,QAAA,CAASI,QAAA;gCACtBvH,OAAAA,EAAWD,EAAAA,CAAAA,QAAAA,CAAAA,sBAAAA,CAAAA;oCAEX6E,QAAAA,EAAAA,GAAAA,MAAAA,GAAAA,KAAAA,IAAAA,CAAAA,EAAAA,EAAAA;sCACAD,QAAQO,IAAAA,IAAAA,CAAAA,EAAAA;oCACRD,GAAAA,IAAAA,IAAAA,CAAAA,QAAAA,CAAAA,aAAAA,EAAAA;sCACAD,YAAAA,EAAcD,EAAAA,CAAAA,QAAAA,CAAAA,aAAAA;oCACdD,GAAAA,aAAAA;sCACA0C,EAAAA,CAAAA,OAAU1H,UAAU0H,EAAAA,MAAA;sCACpBC,WAAW3H,EAAAA,uBAAAA,UAAU2H,SAAA,cAAV3H,2CAAAA,qBAAqB4H,IAAA,CAAK,SAAQ;;;oCAC7CC,eAAe7H,UAAU6H,aAAA;gCACzBZ,UAAYjH,UAAUiH,QAAAA,EAAA,IAAc,SAAA,IAAA,OAAA,QAAA,eAAA,GAAA,MAAA,sBAAA,gCAAA,UAAA,UAAA;oCACpCa,IAAAA,CAAAA,KAAUC,EAAAA,OAASD,QAAA;oCACnBZ,OAAAA,IAAAA,CAAAA,CAAiBa,GAAAA,CAAAA,KAASb,CAAAA,CAAAA,YAAAA,CAAA,EAAA,SAAA;oCAC5B,QAAA,GAAA,CACF,sBAAA,OAAA,MAAA,2BAAA,OAAA,mBAAA;;wCAGuC9F,GAAAA,CAAAA,YAI7B4G,EAAAA,CAAAA,KAAAA,KAIIC,MACAC,QACGC,GAMLC,oBAWNC,MACKF,IACD5G,MAKF+G,cACAC,WACApK;;;;qDApCF,CAAA,GAAA,GAAA,CAAOiD,WAAW,iBAAeA,iBAAAA,OAAOoH,MAAA,cAAPpH,qCAAAA,eAAeqH,MAAA,CAAA,GAAhD;;;;;;;;;;;;gDAEA,UAAA,GAAA;;oDAAMrH,OAAOoH,MAAA,CAAOC,MAAA,CAAO,WAAW,IAAIC;wDAAY;wDAAG,iBAAA,IAAA,QAAA,qBAAA,MAAA;0DAAG,eAAA,GAAA;;;;;;;;uEAA5D,aAGA,IAAI,OAAOC,YACTX,IADyB,MAE3B,IADgB,GADwB,AAEjC,CADaW,cAAcC,MAAA,CAAOC;;;;kDAGjCX,SAAS,IAAIQ,WAAWT,KAAKa,MAAM;oDACzC,IAASX,IAAI,GAAGA,IAAIF,KAAKa,MAAA,EAAQX,IAAK;;;sDACpCD,MAAA,CAAOC,EAAC,GAAIF,KAAKc,UAAA,CAAWZ;kDAC9B;oDACAH,cAAcE;8CAChB;;;iDAEyB9G,MAAOoH,MAAA,CAAOC,MAAA,CAAO,WAAWT;;gDAAnDI,aAAa;kDACnB,SAAA,OAAA;;sDAAOY,MAAMrN,IAAA,CAAK,IAAI+M,WAAWN,aAC9Ba,GAAA,CAAI,SAACC;iEAAMA,EAAE9K,QAAA,CAAS,AACtBwJ,IAD0BuB,AAC1B,CAAK,GAAA,IADqB,CAAS,GAAG;;;;8FAGzCrK,QAAQC,IAAA,CACN;;;;;;kDAKFsJ,GAAAA,CAAAA,GAAO,WAAA,CAAA,KAAA;kDACX,EAAA,EAASF,KAAI,GAAGA,KAAIU,MAAMC,MAAA,EAAQX,KAAK;uDAC/B5G,OAAOsH,MAAME,UAAA,CAAWZ,8BAAAA,OAAAA,IAAAA,CAAAA,cAAAA,CAAAA,MAAAA,GAAAA,GAAAA;sDAC9BE,OAAA,AAAQA,CAAAA,QAAQ,CAAA,IAAKA,OAAO9G;sDAC5B8G,MAAAA,CAAOA,OAAOA;gDAChB;kDAEMC,EAAAA,aAAepK,KAAKkL,GAAA,CAAIf,MAAMjK,QAAA,CAAS,IAAI+K,QAAA,CAAS,GAAG;oDACvDZ,YAAYc,KAAKC,GAAA,GAAMlL,QAAA,CAAS,IAAI+K,QAAA,CAAS,IAAI;kDACjDhL,SAASD,KAAKC,MAAA,GAASC,QAAA,CAAS,IAAI4I,SAAA,CAAU,GAAG,IAAImC,QAAA,CAAS,IAAI;sDAExE,IAAA,CAAA,IAAA,CAAA,gBAAA,CAAA,aAAA;;sDAAQb,CAAAA,eAAeC,YAAYpK,MAAA,EAAQoL,MAAA,CAAO,IAAI;;;;wCACxD,QAAA,IAAA,CAAA;;oCAEA,GAASC,CAAAA,CAAAA,mBAAAA,KAA0BC,UAAA;oCACjC,EAAMC,iBAAiBvJ;;;gCACvB,IAAIuJ,gBAAgB;oCAClB,IAAA,CAAA,EAAO,GAAA,EAAA,KAAwB,OAAdA;oCACnB,QAAA,GAAA,CAAA,sCAAA;gCAEA,IAAMC,cAAchJ,sBAAsBhB;;;;;;;;;qCAExC,CAAA,kBAA+B,OAAXgK,YAAAA,GAApB,KAAO;;;;qCACT,CAAA,IAAA,CAAA,MAAA,CAAA,wBAAA,CAAA,sBAAA;;;;gCAEA,CAAO,GAAA,CAAA,MAAA,CAAA,IAAyC,OAA1BnK,KAAKC,SAAA,CAAUgK;gCACvC,IAAA,CAAA,uBAAA,CAAA;gCAEsBG;;oCAAAA,IAAAA,CAAAA,CAAaH,UAAA,oBAAA;;;gCAAbG;;;;;;;kDAChBvF,SAAiB,iBAAA;gDACnB;;oDAAOA,CAAAA,CAAAA;;4CACT,gBAAA;4CAEkB,EAAA,EAAA;;gDAAMwF,UAAUL,0BAA0BC;;;4CAA5DpF,gBAAAA,CAAAA,CAAkB;4CAClB,SAAA,CAAA,MAAA,EAAA;;gDAAOA;;;;gCACT;;oCAAA,oBAAA,IAAA;;;gCAAA;;gCAQA,CAASyF,GAAAA,IAAAA,CAAAA,GAAWvM,GAAAA,CAAAA,MAAA,mBAAA,IAAA,QAAA,IAAA,CAAA,MAAA,CAAA,wBAAA,MAAA,MAAA;oCAClB,CAAOkG,GAAAA,CAAAA,IAAQzF,EAAAA,CAAAA,gBAAmBT,aAAAA;gCACpC;gCAEe4J,WAAAA,eAAAA,IAAAA;;;;;;;;;;;;;;;;;;;;;4CACb3C,UAAAA,SAGMiF,MAAAA,CAAAA,MAAa7E,IAAAA,0CAHe,CAAC,GACnCmF,QAAAA,oEAA2B,CAAC;4CAItBtF;oCAAAA,SAAgB,CAAC,CAACD,QAAQC;oCAAAA,WAAA,aAAA,GAAA,IAAA,OAAA,WAAA;gCAAA,aAChC,IAAIA,OAAAA,QAAe,QACjB3F,QAAQkL,GAAA,CAAI,IACd,oCADsDxK,KAAKC,SAAA,CAAUgK,YAAY,MAAM;4CAGtE,EAAA,EAAA;;gDAAMG,aAAaH;;;;;;0CAA9BQ,WAAW,IAAA;0CAEjB,IAAIxF,cAAAA,CAAe;;;4CACjB3F,QAAQkL,GAAA,CAAI,gBAAA,sBAAsCC;;;;0CACpD;;;;4CAIA,mBAAA,GAAA;;gDAAO,KAAA,CAAA,MAAA,EAAA;sDACLC,WAAWD,IAAAA,CAAAA;oDACXE,aAAaV,WAAW7D,UAAA;;;mDACxBE,IAAI2D,WAAW3D,EAAA,CAAGhD,WAAA;;;;oDAClBsH,KAAAA,IAAA,GAAWL,kBAAAA,MAAMM,QAAA,cAANN,6BAAAA,kBAAkB;oDAC7BO,SAAA,GAAWP,SAAAA,IAAAA,KAAAA,GAAAA,GAAMQ,CAAAA,CAAAA,MAAA,CAAA,aAANR,WAAAA,MAAAA,MAAAA,MAAAA,kBAAkB;sDAC7BS,YAAYC,WAAAA;mDACRjG,QAAQG,eAAA,GACR;+CAAE+F,YAAAA,IAAAA,GAAmBlG,QAAQG,eAAA;iDAAgB,IAC7C,CAAC;;;;;;;;;;oCAET,QAAA,IAAA,CAAA,+CAAA;;qCAESgG,CAAAA,cACPpN,EADOoN,QACP,EACAnN,OAAA,EACAoN,IAAA;;;;gCAEAzL,oBAAAA,IAAAA,CAAAA,WAAAA,CAAAA,cAAAA;qCACAC,IAAY9B,eAAZ8B,EAA6B7B,YAAYC,UAAUoN;;;;gCACrD,IAAA,IAAA,CAAA,KAAA,EAAA;oCA2CsBC,QAAAA,GAAAA,CAAAA;8DACpBtN,UAAA,EACAuN,YAAA;;;;;;;;;;;;;;;;;;;;;sCACAtG,UAAAA,oEAAkC,CAAC;;;;;;;;;;;qCAGjC,IAAA,CAAA,IAAA,CAAA,WAAA;;;;;;;4CAAMuG,aAAAA,CAAcxN,YAAYiH,SAAS;gDACvC6F,UAAU,eAAA,IAAA,QAAA,IAAA,CAAA,MAAA,CAAA,wBAAA,MAAA,MAAA;kDACVI,WAAWK,aAAavC,GAAAA,MAAA;4CAC1B;;;wCAHA;;;;;;;;;;;;;;;;;;;;;;;gCAUJ;;gCAEA,GAAsByC,CAAAA,CAAAA,uBAAAA,CAAAA;;;gEACpBzN,QAAAA,EAAA,EACA0N,gBAAA;;;;;;;;;;;;;;;;;;6BACAzG;;;;uCAAAA,IAAAA,OAAAA,EAAAA,eAAAA,EAAAA,YAAAA,EAAAA,aAAAA,eAAkC,CAAC;;oCAEnC,IAAI,CAACsF,WAAWvM,aAAa;;;;;;;;;;;;;;;;;gDAGP,SAAA;;8CAAM4J,CAAAA,EAAAA,IAAAA,CAAAA,MAAAA,CAAAA,QAAuB3C,SAAS,IAAA;wDACxDiG,QAAAA,GAAWQ,iBAAiB1C,SAAA;oDAC9B,cAAA,CAAA;;;;;;;;;;;;;;;8CAFM2C,cAAc,CAAA,GAAA,KAAA,GAAA;;;4CAIpBP,CAAAA,UAAAA,CAAAA,IAAgBpN,YAAY,aAAa2N;;;;8CACzCP,gBAAgBpN,IAAAA,QAAY,eAAe;kDACzC4N,MAAA,YAAA,KAAA,cAAA;;;qDACE;;0DACEjB,WAAWgB,YAAYhB,SAAA;uFACvBkB,aAAAA,IAAiB,qIAAA;0DACjBC,QAAQJ,iBAAiBK,KAAA;;;yDACzBd,YAAYS,iBAAiB1C,SAAA;;sDAC/B;;gDAEJ;;;8CACOgD;oCAAAA,SAAAA;oCAAAA,WAAAA,aAAAA,GAAAA,IAAAA,OAAAA,WAAAA;gCAAAA,eACPzM,QAAQyM,CAAAA,IAAA,CACN,iEACAA;;;;;;;;;;;;;;gCAGN,IAAA,aAAA,GAAA;;oCAEA,CAAsBR,GAAAA,CAAAA,uBAAAA,CAAAA;oEACpBxN,UAAA;0CACAiH,EAAAA,CAAAA,MACAuF,KAAAA,CAAAA,CAKQyB,eAECD,CAAAA;;;;;;;;;8CART/G,UAAAA,UAAAA,0DAAkC,CAAC,GACnCuF,QAAAA,oEAA2B,CAAC;8CAE5B,IAAI,CAACD,WAAWvM,EAAAA,WAAa;;;;;;;;;;;8CAGL,WAAA;;;;;;;;;;;;;;;;;8BAAhBiO,CAAAA,IAAAA,CAAAA,IAAAA,CAAAA,KAAgB,MAAA;8BACtBb,OAAAA,SAAgBpN,YAAY,SAAA,IAAaiO;;;;;;gCAClCD,IAAAA,wBAAAA,CAAAA,gBAAAA;gCACPzM,IAAAA,IAAQyM,EAAAA,GAAA,CAAM,oDAAoDA;;;;;;;;;;;;;;wBAEtE,QAAA,GAAA,CAAA;;oBL2OA,yBAA+B;gBM/oBxB,KAASE,iBAAiBC,QAAA;gBAC/B,IAAA,GAAOA,CAAAA,CAAAA,KAAAA,EAASC,qBAAA,KAA0B,IAAID,SAASE,iBAAA;oBACzD,QAAA,GAAA,CAAA;gBNkpBA,gCAAoC;gBCznBvBpP,IAAAA,CAAAA,0BAAAA,GAAAA,EAAN;yBAAMA,eAAAA,CAAAA,IA6BTqP,IAAA,EACAC,MAAA,EACAC,WAAA,EACAC,QAAA,EACAC,UAAA,EACAC,WAAA;0CAlCS1P;;;;4BACX,IAAA,CAAA2P,SAAA,GAAY,GAAA,WAAA;;sCACZ;;;;;;;;;gDAEA,CAAAC,WAAAA,KAAAA,GAAAA,CAEA,IAAA,EAAQC,CAFR,GAAuC,EAE/BA,CAAAA,iBAAA,GAAoC,KAAA,IAC5C,IAAA,EAAQC,MAAAA,CAAAA,mBAAA,GAA6B,MAAA;kDAErC,EAAA,EAAA,CAAAC,UAAA,GAAuB,GAAC,MAAA,CAAA,0BAAA,IAAA;oDACxB,IAAA,CAAiBC,KAAAA,KAAAA,EAAAA,aAAA,GAAmC;wDAEpD,EAAA,CAAAC,KAAAA,GAAAA,CAAAA,IAAA,GAMW,mDAAA,OAAA,MAAA,MAAA,CAAA,yBAAA,IAAA,eAAA,OAAA,MAAA,MAAA,CAAA,4BAAA,IAAA;oDAkBT,IAAA,CAAKZ,IAAA,GAAOA;;;wDACZ,IAAA,CAAKC,MAAA,GAASA;;kDACd,IAAA,CAAKC,WAAA,GAAcA;kDACnB,EAAA,EAAA,CAAKC,GAAAA,MAAA,CAAA,EAAWA,aAAAA,IAAAA;oDAChB,IAAA,CAAKC,KAAAA,KAAAA,CAAA,CAAA,EAAaA;wDAClB,EAAA,CAAKC,KAAAA,GAAAA,CAAAA,EAAA,GAAcA;;;;;;;sDAGTQ,IAAAA,EAAAA,IAAAA,CAAAA,mBAAAA,IAAAA,MAAAA,MAAAA,CAAAA,sBAAAA,EAAAA;8DAAZ,KAAA,EAAA;8DACE,EAAA,GAAA,CAAA,AAAO,CAAC,CAAC,IAAA,CAAKb,IAAA,CAAKtH,MAAA,CAAOE,aAAA,qBAAA,OAAA,MAAA,MAAA,CAAA,mBAAA,EAAA;wDAC5B;;;;;;sDAEYkI,IAAAA,EAAAA,IAAAA,CAAAA,sBAAAA,IAAAA,MAAAA,MAAAA,CAAAA,0BAAAA,EAAAA;8DAAZ,KAAA,EAAA;8DACE,EAAA,GAAA,CAAA,AAAOrI,sBAAsB,IAAA,CAAKuH,IAAA,CAAKtH,MAAM,aAAA,OAAA,MAAA,MAAA,CAAA,0BAAA,EAAA;wDAC/C;;;;;;gDAMYqI,KAAAA,UAAAA;8DANZ,AAMA,YAAA,CAAA,MAAA,IAAA,YAAA,gCANA;;;;oDAAA,MAAA,KAAA,EAAA,WAAA;sDAAA,MAAA,GAAA,CAAA,sCAAA,OAAA,MAAA,aAAA,CAAA,CAAA,MAAA,EAAA;oDAMA;;;sDACE,EAAA,KAAO,CAAC,CAAC,CAAA,SAAA,EAAA,CAAKf,IAAA,CAAKtH;+DAAAA,EAAA,CAAOsI,QAAAA,KAAA,IAAA;;;;;;;wDAC5B;;;;wDAEAC,KAAAA,MAAAA,MAAAA,MAAAA,CAAAA,cAAAA,CAAAA,GAAAA,CAAAA,aAAAA,MAAAA,MAAAA,CAAAA,eAAAA,CAAAA,SAAAA;;;;;;yDAAAA,OAAAA,SAAAA,CAAAA;+DAAAA,WAAAA,SAAAA;;;;;;;;;;2DAEEpB,IAAAA,EAAAA,GAAS1M,EAAA,CAAG,kBAAkB,SAACM;0DAC7B,MAAKwM,MAAA,CAAOiB,CAAAA,qBAAAA,MAAAA,MAAA,QAAA;wDAAA;;gEACZ,IAAI,MAAKL,KAAA,EAAO;oEACd5N,QAAQkL,GAAA,CD9FhB,AC+FU;oDAAA,yBAAwB1K,CAAAA,oBAAAA,8BAAAA,QAAS0N,KAAA,KAAS,OAAO1N,QAAQ0N,KAAA,GAAQ,IAAI,KAAG,KAAyB,eAArB1N,oBAAAA,8BAAAA,QAAS2N,KAAA,uCAAS;0DAGpG;2DACAvB,SAAS1M,CAAAA,CAAA,CAAG,EAAA,CAAA,kBAAqB;wDAC/BgM,UAAAA,MAAAA,MAAAA,CAAAA,GACE,MAAKa,IAAA,CAAKtH,KAAAA,CAAA,CAAOhH,CAAAA,IAAAA,KAAA,EACjB,CAAA,CAAA,MAAA,KAAA,GAAA,CAAA,GAAA,MAAA,MAAA,CAAA,mBAAA,GAAA,OAAA;;;gEAAEmG,SAAAA,OAAQ;+DAAA,WAAA,SAAA;;;;;;;;;;oDAAOwJ,SAAS,MAAKpB,MAAA,CAAOqB,cAAA;gCAvE5C,EAAA,CAAAC,KAAAA,EAAA,EAAA,CAAmB,MAAA,CAAA,YAAA;;;qCACnB,CAAA,CAAA,CAAAC,EAAAA,CAAAA,iBAAA,GAAgC,IAAA,IAAA,IAAA,CAAA,SAAA,IAAA,UAAA,IAAA,CAAA,MAAA,CAAA,YAAA;;;;;;;;;;;;;;;;;;;;oDAsE4B9E,WAAW,GAAA,UAAA,GAAA,IAAIc,OAAOiE,WAAA;gDAAc,GAC1F,MAAKX,gBAAA,EACLY,KAAA,CAAM,YAAO;kDAEf,MAAKzB,GACL,GADK,CAAO0B,AACNC,YAAY,MAAK3B,IADX,CAAuB,CACZ,CAAO4B,IADU7B,IAAA,CAAK8B,QACf,GADe,IACf,IAAA,CAAA,cAAA,CAAA,MAAA,EAAA,kBAE9B,MAAK7B,CAAAA,CAAAA,IAAA,CAAO8B,CAAAA,GAAAA,IAAAA;wCAAAA,SAAA,GAAsB,qBAAA,IAAA,CAAA,cAAA;oCAAA,IAAA,CAAA;oDAGhC9O,QAAQkL,GAAA,CACN,wEAAmG,OAA3ByD,WAAS,mBAA4C,OAA1B,MAAKI,cAAA,CAAe/E,MAAM,GAC7H,MAAK+E,cAAA,CAAe/E,MAAA,GAAS,IAAI;;;;;;0CAAEgF,YAAa,qBAAG,MAAKD,cAAc;;;;;kDAAE,IAAI,CAAC;gCAEjF,aAAA,GAAA;gCAEA,IAAI,MAAK1B,KAAAA,GAAAA,CAAA,EAAW;oCAClB,CAAA,GAAI,MAAKL,MAAA,CAAOiC,uBAAA,IAA2B;wCACzC,MAAKC,+BAAA;sCACP,OAAO;wCACL,MAAKC,mBAAA;kCACP;;;wBAIF,IAAIC,eAAe;qCAHnB,gDAAA;0BACF,EAAA;0BACAxC,SAAS1M,EAAA,CAAG,YAAY,SAACmP,mBAAAA;;;iDAiBnBD,eACF,UACF,sBAEApP,QAAQyM,QAEV;;;;gDApBE,GAAA,CAAI4C,CAAAA,aAAc;sDAChB,IAAMC,YAAYD,aAAaE,IAAA,IAAQF,aAAaC,SAAA,IAAa;;;oDACjE,IAAME,gBAAgBH,aAAaG,aAAA;8CACnC,IAAMpP,CAAAA,CAAAA,QAAUiP,IAAAA,SAAajP,OAAA,IAAWiP,aAAaD,YAAA,IAAgB;oDACrE,IAAMK,QAAQJ,GAAAA,IAAAA,IAAAA,CAAAA,CAAaI,KAAA,CAAA,GAASJ,aAAaK,MAAAA,EAAAA,EAAA,IAAcL,aAAa5C,KAAA;sDAE5E2C,eAAe,qBAAmChP,OAAdkP,WAAS,MAAY,OAAPlP;uDAElD,IAAIoP,iBAAiBA,kBAAkB,SAASA,EAA6B,OAA7BA,IAAAA,CAAAA,MAAAA,CAAAA,KAAkBF,WAAW,GAAA,EAAA;0DAC3EF,gBAAgB,sBAAmC,OAAbI,eAAa;sDACrD,MAAA;sDAEA,IAAIC,OAAO;;;wDACT,IAAME,eAAe,OAAOF,UAAU,WAAWA,QAASA,MAAMrP,OAAA,IAAWwP,OAAOH;oDAClFL,KAAAA,GAAAA,CAAAA,GAAAA,EAAgB,EAAA,CAAA,MAAA,CAAA,MAA4B,OAAZO,MAAAA;+CAClC,KAAA,GAAA,CAAA,IAAA,CAAA,MAAA,CAAA,aAAA,GAAA,mBAAA,IAAA,CAAA,MAAA,CAAA,YAAA;uDACF,IAAA,CAAA,MAAA,CAAA,sBAAA,GAAA,CAAA,IAAA,CAAA,MAAA,CAAA,mBAAA,GAAA,IAAA,eAAA,CAAA;uDAEQ,CAAM,IAAA,GAAA,KAAA,EAAcP,EAAAA,CAAAA,MAAAA,CAAAA,IAAcC,aAAAA,GAAgB;iDAC1D,MAAKQ,MAAAA,UAAA,UAAA;;;;2CACP,uBAAA;0CACAjD,IAAAA,EAAAA,GAAS1M,EAAA,CAAG,iBAAiB;gDAC3B,KAAK8M,MAAA,CAAO8C,oBAAA,aAAA,OAAA,UAAA,qCAAA,OAAA,IAAA,CAAA,MAAA,CAAA,mBAAA,EAAA;8CACZ,MAAK9C,MAAA,CAAO+C,sBAAA;;;4CACZ,IAAA,SAAA,CAAK/C,MAAA,CAAOgD;+CAAAA,WAAAA,KAAA,GAAuB,CAAA;;;;;;;gDAEnC,GAAA,GAAKhD,CAAAA,KAAA,CAAOiD,IAAAA,IAAAA,CAAAA,MAAAA,CAAAA,KAAA,OAAA,EAAA;kDAEZ,IAAI,MAAK5C,SAAA,IAAa,MAAKL,MAAA,CAAOkD,yBAAA,IAA6B,QAAQ,MAAKlD,MAAA,CAAOmD,wBAAA,MAA8B,MAAM;;;oDACrH,MAAKnD,MAAA,CAAOoD,6BAAA;;;iDACZ,IAAI,MAAKxC,CAAAA,IAAA,EAAO;;;;0CACd5N,QAAQkL,GAAA,CAAI;;;;iCACd;;oBAAA,aAAA,iEAAA;;6CAgCF,MA+BF,IAAImF,cA2BJC,SACAA,EA5B6B1D,CA2BjB2D,SA3B2B,AA4B3BC,CADA;;;;gDAzFV,GAAA,EAAA;kDAEA,MAAKzD,IAAA,CAAK8B,WAAA,GAAc4B,WAAA,CAAY9D,iBAAiB,MAAKI,IAAA,CAAK8B,WAAA;;;gDAE/D,IAAI,MAAKN,oBAAA,EAAsB;8CAC7B,EAAA,CAAA,CAAI,KAAA,CAAKX,KAAA,EAAO,SAAA;wDACd5N,QAAQkL,GAAA,CAAI,IAAA,IAAA;sDACd;uDACA,MAAK6B,IAAA,CAAK8B,WAAA,GAAc6B,eAAA,MAAA,OAAA,WAAA;sDACxB,MAAKnC,oBAAA,GAAuB;kDAC9B,UAAA;8CACF;;;4CACA3B,SAAS1M,EAAA,CAAG,kBAAkB;gDAC5BgM,eAAAA,IAAAA,MACE,MAAKa,IAAA,CAAKtH,MAAA,CAAOhH,UAAA,EACjB;sDAAEmG,QAAQ;wDAAOwJ,SAAS,MAAKpB,MAAA,CAAOqB,cAAA;sDAAgB5E,WAAA,AAAW,aAAA,GAAA,IAAIc,OAAOiE,WAAA;kDAAc,GAC1F,MAAKX,CAAAA,eAAA,EACLY,KAAA,CAAM,YAAO;kDAEf,IAAI,CAAC,MAAK1B,IAAA,CAAK4D,KAAA,CAAMC,KAAA,EAAO;;;oDAC1B,MAAK7D,IAAA,CAAK4D,KAAA,CAAMC,KAAA,GAAQ;oDACxB,IAAI,MAAKhD,KAAA,EAAO,EAAA,IAAA,CAAA,MAAA,CAAA,sBAAA,EAAA;0DACd5N,QAAQkL,GAAA,CAAI;uDACd,kDAAA,OAAA,IAAA,CAAA,MAAA,CAAA,mBAAA,EAAA;kDACF;kDAEA,MAAK8B,IAAAA,EAAA,CAAO8C,oBAAA;kDACZ,MAAK9C,MAAA,CAAO+C,sBAAA;;;gDACZ,MAAK/C,MAAA,CAAOgD,oBAAA,GAAuB;gDACnC,MAAK1B,OAAA,GAAU,IAAA;4CAEf,EAAI,CAAC,CAAA,CAAA,IAAKjB,OAAAA,CAAAA,CAAA,EAAW,WAAA;gEACnB;;;;8CACF,EAAA;gDAEA,MAAKL,MAAA,CAAO8B,mBAAA,GAAsB;8CAElC,MAAK9B,MAAA,CAAO0B,sBAAA,CAAuB,MAAK3B,IAAA,CAAK8B,WAAA;8CAC7C,IAAMF,YAAY,GAElB,GAFuB3B,CAEnB,CAAA,IAFmB,CAAO4B,AAErBhB,KAAA,EAAO,SAFc;kDAG5B5N;oCAAAA,OAAQkL,EAAAA,CAAA,CACN,QAAA,OAAA;oCAAA,WAAA,aAAA,GAAA,IAAA,KAAiF,EAAA,IAA3ByD,OAAAA;gCAAAA,CAAS,mBAA4C,GACxEK,CAAAA,GAD8C,MAAKD,EACtC,OAAwB,IAAI,CADU,AACT,CADwB/E,MAAM,EACxD,CAAnD,CAEJ,IAF4D+E,CAAnDA,aAAiE,CAAjE,CAAe/E,MAAA,GAAS,IAAI;;;;;;;;;iDAKnC,CAAA,KAAKkF,mBAAAA,CAAAA,UAAAA,CAAA,WAAA;;;;0CACP,CAAA,CAAA,KAAO,oBAAA,CAAA,UAAA,YAAA;;;4CACL,MAAKC,mBAAA,GAAA;;;;0CACP;;;;oCAEJ,CAAA,uBAAA,CAAA,UAAA,YAAA;;;;;;;;oCAEA0B,CAAAA,IAAAA,EAAAA,CAAAA,aAAAA;2CAAAA,IAAAA,CAAAA,IAAAA,qBAAAA,IAAAA,QAAAA,IAAAA,CAAAA,MAAAA,CAAAA,wBAAAA,MAAAA,MAAAA;0CACE,IAAI,CAAA,CAAA,EAAA,CAAKjD,KAAA,EAAO,mBAAA;4CACd5N,QAAQkL,GAAA,CAAI;wCACd,MAAA,CAAA,iBAAA;wCAEA,GAAA,CAAMmF,mBAAAA,GAAAA,EAAwB,IAAA,CAAKhD,SAAA,IAAa,IAAA,CAAKiB,OAAA;wCACrD,IAAM1B,WAAW,IAAA,CAAKG,IAAA,CAAK8B,WAAA;;;;;4CAEzB,EAAA,EAAI,IAAA,CAAKjB,KAAA,EAAO;kDACd5N,QAAQkL,GAAA,CAAI,sCAAA;4CACd;;;;;;+CAGA,IAAA,CAAKqD,KAAAA,CAAAA,MAAAA,GAAAA,CAAAA,KAAA,GAAuB;;;;oCAC9B,QAAA,IAAA,CAAA,cAAA,CAAA,KAAA;0CAEA,IAAMuC,EAAAA,oBAAsBlE,SAASC,qBAAA;;;;0CACrC,IAAMkE,CAAAA,CAAAA,cAAAA,CAAAA,GAAAA,CAAAA,EAAuBnE,SAASE,CAAAA,gBAAA;4CAEtC,CAAA,GAAIF,EAAAA,EAAAA,MAAU,EAAA,GAAA,CAAA;gDACZ,IAAI;;;;kDACFA,SAASoE,OAAA;kDACT,IAAA,CAAKjE,IAAA,CAAK4D,GAAAA,CAAAA,CAAA,CAAMC,KAAA,GAAQ,EAAA;gDAC1B,EAAA,EAAA,KAASnE,GAAAA,GAAAA,CAAO;oDACd,IAAI,IAAA,CAAKmB,KAAA,EAAO;;;;sDACd5N,QAAQC,IAAA,CAAK,8DAA8DwM;kDAC7E;gDACF,4DAAA,OAAA,IAAA,CAAA,MAAA,CAAA,cAAA,GAAA,GAAA,MAAA,OAAA,IAAA,CAAA,cAAA,CAAA,MAAA,EAAA;0CACF;;;;;;;;;;;sCAGA,EAAA,CAAA,CAAM6D,WAAAA,CAAAA,EAAc,IAAA,CAAKvD,IAAA,CAAKkE,cAAA,CAAeC;;;;wCAC7C,GAAA,CAAA,CAAKnE,IAAA,CAAKoE,WAAA,CAAYb,CAAAA,GAAAA;;;;;;;+CAGVE,OAAAA,OAAAA,UAAA,CAAyBM,qBAAqBC;wCAC1D,CAAA,GAAA,CAAK/C,CAAAA,EAAAA,QAAAA,GAAAA,CAAAA,QAAA,iCAAA;kDAEDqC,QAAAA,CAAAA,SAAuB,0BAA3B,IAAIA;;;;;;wCACF,CAAA,GAAA,CAAKjD,WAAA,CAAYgE,IAAA,WAAA;;;;0CACjBd,YAAYe,eAAA;;;;wCAEd,UAAA,QAAA,CAAA,yBAAA,CAAA,aAAA,QAAA,CAAA,gBAAA;sCACF,EAAA,CAAA,MAAA,CAAA,mBAAA;;;;;;;;;;;;;6CAEAC,SAAAA;;;;0CAEIC,GAAAA,UAAa,IAAA,CAAKC,eAAe;kDACjC,IAAA,CAAKA,YAAAA,GAAA,CAAA,EAAkB,KAAA,MAAA,cAAA,YAAA,GAAA;;;;0CACzB,IAAA,EAAA;4CACA,GAAA,CAAA,AAAK7D,cAAA,GAAiB,uCAAA,OAAA,aAAA,GAAA,KAAA,OAAA,YAAA;0CACtB,IAAA,CAAKR,UAAA,CAAWsE,mBAAA,GAAsB,KAAA;;;wCACtC,IAAA,CAAKvE,GAAAA,SAAAA,IAAA,CAASwE;+CAAAA,WAAAA,GAAA,GAAwB,GAAA;;;;;;;oCACxC,IAAA,CAAA,kBAAA,CAAA,aAAA;;;;;;;;wCAMMC,CAAAA,CAAAA,GAAAA,iBAAAA,IAAAA,YAAAA,KAAAA;iDANN,AAMA,oBAAA,8CANA;wCAAA,kBAAA;0CAAA,EAAA,CAAA,KAAA,EAAA,QAAA,GAAA,CAAA,4CAAA;0CAMA,SAAMA,SAAAA,KAAcC,OAAA;;;;;;;sCACZC,WAMAC,MAEFC,aAWEC,mBAMEC,MAmBFC,cACAC,eAkBEC,WAcJC,YACAC,gBACAC,qBAEEC,WA4BEC,iBA0BE7F,UACA8F,gBACAC,iBA+BAC,UAGAhG,WAoCAgG,WAGDnG,OAMCoG,mBAwBIC,IAQAF,WAICG;;;;;;;;6CA5PTlB,YAAY,IAAA,CAAK3E,QAAA,CAAS8F,sBAAA,CAC9B,IAAA,CAAKjG,IAAA,CAAK4D,KAAA,CAAMsC,WAAA,GAAc;;iFAoE9B;;;;sDAlEF,IAAIpB,MAAAA,KAAW;gEACb,IAAA,CAAK3E,QAAA,CAASgG,oBAAA,CAAqBC,GAAA,CAAI,IAAA,CAAKjG,QAAA,CAASkG,aAAA,CAAcvB;;;4DACrE,EAAA,CAAA,GAAA,CAAA,aAAA;8DACMC,EAAAA,CAAAA,IAAO,IAAA,CAAK5E,QAAA,CAASmG,sBAAA,CAAuBxB,WAAAA;8DAIlD,IAAIC,QAAQA,KAAK9H,MAAA,GAAS,KAAK8H,IAAA,CAAK,EAAC,EAAG;gEACtCC,cAAcD,IAAA,CAAK,EAAC;4DACtB,GAAA,CAAA,GAAA,IAAW,IAAA,CAAK5E,CAAAA,OAAA,CAASoG,aAAA,EAAe;kEACtCvB,cAAc,IAAA,CAAK7E,QAAA,CAASoG,aAAA,eAAA;8DAC9B,OAAO;gEACL,IAAA,CAAKhC,mBAAA;gEACL,GAAA,IAAA,IAAA,CAAA,MAAA,CAAA,sBAAA,EAAA;;;4DACF;4DAEMU,GAAAA,IAAAA,aACJJ,QAAQ2B,eAAA,IAAmB,OACvB3B,QAAQ2B,eAAA,GAAkB,MAC1B1B,sBAAAA,gCAAAA,UAAW2B,UAAA;8DAEjB,EAAA,CAAA,CAAI,IAAA,CAAK5F,KAAA,EAAO;kEACRqE,OAAO,IAAA,CAAKlF,IAAA,CAAKtH,MAAA,CAAOG,YAAA,GAAe,SAAS;gEACtD5F,QAAQkL,GAAA,CACN,sBAAoD8G,OAA9BC,MAAI,2BAA2C,OAAjBD,mBAAiB;4DAEzE;sDAEA,IAAA,CAAKhF,CAAAA,CAAAA,IAAA,CAAOyG,cAAA,CAAeC,CAAAA,IAAA;4DAC3B,IAAA,CAAK1G,EAAAA,IAAA,CAAO2G,oBAAA,CAAqBD,KAAA;4DACjC,IAAA,CAAK1G,CAAAA,CAAAA,IAAA,CAAO8B,mBAAA,GAAsB;;;;;;;;;;;wDAElC,IAAA,CAAKP,SAAAA,WAAA,GAAuB;;;;0DAC5B,GAAA,CAAA,CAAKvB,CAAAA,KAAA,CAAO4G,EAAAA,oBAAA,GAAyB;oDAErC,IAAI,EAAA,EAAA,CAAK1G,QAAA,CAASwE,qBAAA,EAAuB;;;4DACvC,IAAA,CAAKxE,QAAA,CAASwE,qBAAA,GAAwB;;;;0DACxC,OAAO,gBACL,IAAA,CAAKxE,QAAA,CAAS2G,aAAA;;2CAChB,EAAA;oCAAA,WAAA,aAAA,GAAA,IAAA,OAAA,WAAA;gCAAA,2BAEM3B,eAAe,IAAA,CAAKnF,GACpBoF,CADoB,CAAKxB,KAAA,CAAMC,KAAA,GACf,IAAA,AACtB,CAD2B7D,GAC3B,CAD2B,AACtBA,CAD2B4D,GAC3B,CAAK9B,CADsB,CAAMiF,MAAA,GAC5B,GAActD,wBAAA,CAAyB0B,cAAcC;0DAE/D,IAAA,CAAK9E,GAAAA,MAAA,GAAY;0DACjB,IAAA,CAAKL,KAAAA,CAAA,CAAO+G,GAAAA,cAAAA,MAAA,GAA0B;;;0DACtC,IAAA,CAAK/G,MAAA,CAAOqB,GAAAA,CAAAA,KAAAA,KAAA,GAAiB;4DAC7B,IAAA,CAAKrB,MAAA,CAAOgH,CAAAA,CAAAA,aAAA,GAAkB;4DAC9B,CAAA,GAAA,CAAKvG,UAAA;4DACL,IAAA,CAAKa,OAAA,GAAU;0DAEf,IACE,IAAA,CAAKtB,MAAA,CAAOkD,yBAAA,IAA6B,QACzC8B,qBAAqB,MACrB;;;;;;;;;wDAEF,QAAA,CAAA;;;2DAEI,IAAA,CAAKlE,oBAAA,EAAL;;;;;;;wDACgB;;4DAAM,GAAA,CAAA,CAAKmG,CAAAA,gBAAA,CAC3BlC,aACAC,mBACAE,cACAC;;;;;;;;wDAJIC,KAAAA,CAAAA,MAAY,2BAAA;oDAMlB,EAAIA,EAAAA,CAAAA,QAAW,GAAA,CAAA,cAAA;gFACb;;;;;;;;;;;;;oDACF,qBAAA,CAAA,kBAAA,YAAA,GAAA;sDACA,IAAI,IAAA,CAAKxE,KAAA,EAAO,GAAA,CAAA,kBAAA,YAAA;0DACd5N,QAAQkL,GAAA,CAAI;oDACd;;;oDAIEoH,WAAAA,GAAAA,GAAiB;8CAGfE,EAAAA,CAAAA,SAAY,EAAA,EAAA,CAAKvF,WAAA,CAAYiH,cAAA;;;kDACnC,IAAI1B,WAAW;;;;wDACbH,CAAAA,YAAaG,UAAU2B,OAAA;wDACvB5B,iBAAAA,IAAAA,CAAsBC,OAAAA,GAAU4B,CAAAA,CAAAA,MAAAA,CAAAA,GAAA,qBAAA,MAAA,MAAA;0DAChC9B,iBAAiB,EAAA;wDAEjBvG,qBACE,IAAA,CAAKgB,IAAA,CAAKtH,MAAA,CAAOhH,UAAA,EACjB;4DAAEmG,MAAAA,EAAQ;4DAAOuP,SAAS9B;;;;qDAAY5I,WAAA,AAAW,aAAA,GAAA,IAAIc,OAAOiE,WAAA;wDAAc,GAC1E,CAAA,CAAA,EAAA,CAAKX,gBAAA,EACLY,KAAA,CAAM,YAAO,OAAA;;;;;;4DAGf,MAAA,CAAA,YAAA;0DACF,OAAA,GAAA,CAAW,GAAA,CAAA,CAAKd,WAAAA,GAAA,EAAA,EAAkB,CAAA,GAAA,CAAKA,cAAA,CAAe0G,QAAA,CAASrK,MAAA,GAAS,GAAG;8DACzE,CAAA,CAAA,EAAA,CAAK+E,cAAA,GAAkB,qBAAG,IAAA,CAAKpB,cAAA,CAAe0G,QAAQ;4DACtDhC,aAAa,IAAA,CAAKtD,cAAA,CAAeuF,KAAA;8DAEjC,CAAA,GAAI,CAAA,GAAA,CAAK1G,KAAA,EAAO;kEACd5N,QAAQkL,GAAA,CAAI,yDAAuF,OAA9B,IAAA,CAAK6D,cAAA,CAAe/E,MAAA,GAAS,GAAC;4DACrG;4DAEA,IAAA,CAAKsH,mBAAA;wDACP,OAAO,KAAA,KAAA,cAAA;8DACL,IAAI,EAAA,EAAA,CAAK1D,KAAA,EAAO;gEACd5N,QAAQkL,GAAA,CAAI;4DACd;;;;;;;uDAGAmH,aAAaI,eAAA,CAAgB,EAAC;sGAC9B,IAAA,CAAK1D,KAAAA,UAAA;0DACP,GAAA,CAAA,iCAAA;qDAEA,IAAI,CAACsD,WAAAA,CAAY,cAAA;mFACf,IAAI,IAAA,CAAKzE,KAAA,EAAO;;;;gEACd5N,QAAQC,IAAA,CAAK;;;;;;;;;0DAEf,IAAA,CAAKkP,UAAAA,CAAAA,QAAA,WAAA,YAAA,GAAA;4DACL,aAAA,CAAA,mBAAA,YAAA;;;sDACF,YAAA;sDAEA,IAAI,IAAA,CAAKvB,CAAAA,IAAA,EAAO;0DACd5N,EAAAA,CAAAA,KAAQkL,GAAA,CAAI,UAAA,YAAA,iBAAsCmH;sDACpD,SAAA,GAAA;;;;;;;;;;;;;;;;;;;;;;;;+DAIQ,CAAC,IAAA,CAAKrF,MAAA,CAAOuH,wBAAA,CAAyBhC,sBAAtC;;;;0DACF,IAAA,CAAKvF,GAAAA,GAAA,CAAOwH,yBAAA,CAA0BjC;0DACtC,IAAA,CAAKkC,CAAAA,sBAAA,CAAwB1C;0DAC7B,UAAA,KAAA,cAAA;;8DAAM,IAAA,CAAK7C,+BAAA;;;;;;;;8CAAX;;;;;iDACA,oCAAA;;;;;;;;;sDAGItC,CAAAA,CAAAA,SAAW,IAAA,CAAKG,EAAAA,EAAA,CAAK8B,WAAA;sDACrB6D,EAAAA,CAAAA,cAAiB9F,SAASC,CAAAA,EAAAA,kBAAA;4DAC1B8F,GAAAA,IAAAA,WAAkB/F,SAASE,iBAAA;8DACjCF,CAAAA,CAAAA,OAASoE,OAAA;8DACT,IAAA,CAAKjE,IAAA,CAAK4D,KAAA,CAAMC,KAAA,GAAQ;8DACxB,IAAA,CAAK7D,IAAA,CAAKoE,WAAA,CAAYoB;;;4DACtBA,oBAAoB/B,wBAAA,CAAyBkC,gBAAgBC;4DAE7D,IAAA,CAAK3E,EAAAA,IAAAA,IAAAA,CAAAA,MAAAA,CAAAA,KAAA,iBAAA,EAAA;8DAEL,CAAA,CAAA,EAAI,IAAA,CAAKJ,KAAA,EAAO;kEACd5N,QAAQkL,GAAA,CAAI;8DACd;;;4DAEAqH,oBAAoBmC,MAAA;4DAEpB,IAAA,CAAK1H,MAAA,CAAO8B,mBAAA,GAAsB;8DAClC,IAAA,CAAK2F,uBAAA,CAAwB1C;8DAE7B,IAAI,CAAC,IAAA,CAAK9E,WAAA,CAAY0H,MAAA,EAAQ;;;gEAC5B,IAAA,CAAK1H,WAAA,CAAY0H,MAAA,GAAS;4DAC5B;6DACA,IAAI,CAAC,IAAA,CAAK1H,WAAA,CAAY2H,WAAe,OAAfA,GAAA,EAAe,KAAA;gEACnC,IAAA,CAAK3H,WAAA,CAAY4H,eAAA,CAAgB9C;4DACnC;4DAEA,WAAA;;0DAAMQ,QAAAA,YAAoBuC,IAAA;;;;;;;;;;;;;;;;0DAA1B;;;0DACA,IAAA,CAAK9H,IAAAA,EAAA,CAAO+H,CAAAA,IAAAA,CAAAA,MAAAA,CAAA,sBAAA,EAAA;4DAEZ,GAAA,CAAI,IAAA,CAAK/H,MAAA,CAAOkD,yBAAA,IAA6B,QAAQ,IAAA,CAAKlD,MAAA,CAAOmD,wBAAA,MAA8B,MAAM;gEACnG,IAAA,CAAKnD,MAAA,CAAOoD,6BAAA;;;;0DACd;0DAEMwC,WAAWV,GAAAA,IAAAA,QAAe,IAAIC;4DACpCI,GAAAA,CAAAA,gBAAoB9B,WAAA,CAAYmC;;;;;;;;;;;;;;;;;wDAE1BhG,YAAW,IAAA,CAAKG,IAAA,CAAK8B,WAAA;;;;wDAC3B,MAAA,CAAA,GAAA,CAAA,YAAA;;;;;;;;4DAAM,IAAA,CAAK7B,MAAA,CAAOgI,sBAAA;;;;;;;;;;;;;oDAClB,CAAA,CAAA,EAAA,CAAKhI,MAAA,CAAOiI,iBAAA,GAAoB1K,KAAKC,GAAA;;;;sDACrC,SAAA,GAAA;;;;;;;oDAAMoC,CAAAA,OAAAA,EAASsI,UAAA,CAAW7C;;;;;;;;;;;oDAA1B;;;;0DAGIzN,CAAAA,OAAQ,kBAAA,CAAA,aAAA,QAAA,CAAA,gBAAA;4DAAOuP,OAAAA,EAAS9B;0DAAY5I,WAAA,AAAW,aAAA,GAAA,IAAIc,OAAOiE,WAAA;;;oDAAc,GAC1E,IAAA,CAAKX,YAAAA,IAAA,EACLY,KAAA,CAAM,YAAO;;;;;;;;;4DAGbzO,QAAQkL,GAAA,CAAI;;;;;;;;;;;;;4DAIZ,IAAA,CAAK8B,MAAA,CAAOwH,yBAAA,CAA0B5H;;;;;;8CACtC,IAAA,CAAKiE,oBAAA;;;;0CACL,IAAA,CAAK4D,uBAAA,CAAwB1C,OAAAA;0CAC7B;;mDAAM,IAAA,CAAK7C,+BAAA;;;;4CAAX;4CACA;;;;4CAGF,CAAA,EAAA,CAAA,CAAKlC,MAAA,CAAO8B,mBAAA,GAAsB;8CAClC,IAAA,CAAK2F,QAAAA,eAAA,CAAwB1C;8CAE7B,EAAA,EAAI,CAAC,IAAA,CAAK9E,WAAA,CAAY0H,MAAA,EAAQ;gDAC5B,IAAA,CAAK1H,WAAA,CAAYkI,gBAAA,CAAiBpD;4CACpC;4CAEA;;gDAAMnF,IAAAA,GAAAA,GAASkI,EAAAA,EAAA;;;4CAAf;4CACA,GAAA,CAAA,CAAK9H,GAAAA,CAAAA,EAAA,CAAO+H,aAAA,wBAEZ,IAAI,IAAA,CAAK/H,MAAA,CAAOkD,yBAAA,IAA6B,QAAQ,IAAA,CAAKlD,MAAA,CAAOmD,wBAAA,MAA8B,MAAM;8CACnG,IAAA,CAAKnD,MAAA,CAAOoD,EAAAA,qBAAAA,MAAA;0CACd,IAAA,CAAA,WAAA,GAAA,iBAAA;4CAEMwC,EAAAA,UAAWV,KAAAA,UAAe,IAAIC;8CACpCvF,UAAS6D,WAAA,CAAYmC;;;;;;;;gDAEhBnG;kDACP,IAAI,IAAA,CAAKmB,IACP5N,CADO,EAAO,KACNC,IAAA,CAAK,+BAA+CwM,OAA/C,IAAA,CAAA,IAAA,CAAA,EAA+CA,GAAAA,CAAAA,WAAAA,CAAAA,OAAAA,CAAAA,IAAAA,QAAAA,OAAAA,YAAAA,OAAAA,CAAAA,IAAAA;qDAG1D,CAAC6F,gBAAD;;;;;gDACIO,GAAAA,CAAAA,gBAAoB,IAAA,CAAK5F,WAAA,CAAYiH,cAAA;4JACvCrB,CAAAA,kBAAAA;;;8CACF,IAAI,IAAA,CAAKjF,KAAA,EAAO;gDACd5N,CAAAA,EAAAA,KAAQkL,GAAA,CAAI;;wJACd,GAAA,CAAA;;;;;;;;;;;mDAGM,CAAC,IAAA,CAAK8B,MAAA,CAAOuH,wBAAA,CAAyB1B,kBAAkBuB,YAAY,GAApE;;;4CACF,IAAA,CAAKpH,MAAA,CAAOwH,yBAAA,CAA0B3B,kBAAkBuB,YAAY;4CACpE,IAAA,CAAKK,EAAAA,IAAAA,IAAAA,CAAAA,MAAAA,CAAAA,KAAA,CAAwB1C,gBAAAA,EAAAA;8CAC7B,CAAA,CAAA,wDAAA,OAAA,IAAA,CAAA,MAAA,CAAA,mBAAA,EAAA;;kDAAM,IAAA,CAAK7C,+BAAA;;;8CAAX,UAAA;4CACA;;;;0CAGF,IAAA,CAAKkG,uBAAA,CAAwBvC,kBAAkBuB,YAAY;;;;;gCAG3D,IAAA,CAAKK,uBAAA,CAAwB1C;gCAE7B,IAAI,CAAC,IAAA,CAAK9E,WAAA,CAAY0H,MAAA,EAAQ;2DAC5B,IAAA,CAAK1H,WAAA,CAAY0H,MAAA,GAAS;qCAC1B,IAAA,CAAK1H,WAAA,CAAY4H,eAAA,CAAgB9C","sourcesContent":["\"use strict\";\nvar __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n  for (var name in all)\n    __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n  if (from && typeof from === \"object\" || typeof from === \"function\") {\n    for (let key of __getOwnPropNames(from))\n      if (!__hasOwnProp.call(to, key) && key !== except)\n        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n  }\n  return to;\n};\nvar __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n  // If the importer is in node compatibility mode or this is not an ESM\n  // file that has been converted to a CommonJS file using a Babel-\n  // compatible transform (i.e. \"__esModule\" has not been set), then set\n  // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n  isNodeMode || !mod || !mod.__esModule ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target,\n  mod\n));\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/player/AdBreakOrchestrator.ts\nvar AdBreakOrchestrator_exports = {};\n__export(AdBreakOrchestrator_exports, {\n  AdBreakOrchestrator: () => AdBreakOrchestrator\n});\nmodule.exports = __toCommonJS(AdBreakOrchestrator_exports);\n\n// src/utils/mqttConfig.ts\nvar DEFAULT_MQTT_CONFIG = {\n  enabled: true,\n  brokerAddress: \"vecbae77.ala.us-east-1.emqxsl.com\",\n  brokerPort: 8883,\n  wsPort: 8084,\n  username: \"for-sonifi\",\n  password: \"sonifi-mqtt\",\n  topicPrefix: \"adstorm/players\",\n  qos: 1\n};\nvar mqttConfig = { ...DEFAULT_MQTT_CONFIG };\nfunction isMQTTEnabled() {\n  return mqttConfig.enabled;\n}\nfunction buildMQTTBrokerUrl() {\n  if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;\n  return `wss://${mqttConfig.brokerAddress}:${mqttConfig.wsPort}/mqtt`;\n}\nfunction buildPlayerTopic(licenseKey, channel) {\n  return `${mqttConfig.topicPrefix}/${licenseKey}/${channel}`;\n}\n\n// src/utils/mqttClient.ts\nvar import_mqtt = __toESM(require(\"mqtt\"), 1);\nvar LOG = \"[StormcloudVideoPlayer][MQTT]\";\nvar client = null;\nvar status = \"disconnected\";\nfunction initMQTTClient() {\n  if (client || !isMQTTEnabled()) return;\n  const url = buildMQTTBrokerUrl();\n  status = \"connecting\";\n  const clientId = `stormcloud-vp-${Math.random().toString(36).slice(2, 9)}`;\n  try {\n    client = import_mqtt.default.connect(url, {\n      clientId,\n      username: mqttConfig.username,\n      password: mqttConfig.password,\n      keepalive: 60,\n      clean: true,\n      reconnectPeriod: 5e3,\n      connectTimeout: 1e4,\n      queueQoSZero: false\n    });\n  } catch (err) {\n    status = \"error\";\n    console.warn(`${LOG} connect() threw:`, err);\n    return;\n  }\n  client.on(\"connect\", () => {\n    status = \"connected\";\n    console.info(`${LOG} connected to ${url}`);\n  });\n  client.on(\"reconnect\", () => {\n    status = \"connecting\";\n    console.info(`${LOG} reconnecting\\u2026`);\n  });\n  client.on(\"offline\", () => {\n    status = \"disconnected\";\n    console.warn(`${LOG} offline`);\n  });\n  client.on(\"error\", (err) => {\n    status = \"error\";\n    console.warn(`${LOG} error:`, err.message);\n  });\n  client.on(\"close\", () => {\n    if (status === \"connected\") {\n      status = \"disconnected\";\n    }\n  });\n}\nfunction ensureMQTTClient() {\n  if (isMQTTEnabled() && !client) {\n    initMQTTClient();\n  }\n}\nfunction publishMQTT(topic, payload) {\n  if (!isMQTTEnabled()) {\n    return false;\n  }\n  ensureMQTTClient();\n  if (!client) {\n    return false;\n  }\n  try {\n    client.publish(topic, JSON.stringify(payload), { qos: mqttConfig.qos });\n    return true;\n  } catch (err) {\n    console.warn(`${LOG} publish failed on ${topic}:`, err);\n    return false;\n  }\n}\n\n// src/utils/vastEnvironmentSignals.ts\nvar PLAYER_ID_STORAGE_KEY = \"stormcloud.player.id\";\nvar DEVICE_ID_STORAGE_KEYS = [\n  \"rdid\",\n  \"ifa\",\n  \"advertisingId\",\n  \"advertising_id\",\n  \"deviceAdvertisingId\",\n  \"aaid\",\n  \"adid\",\n  \"rida\",\n  \"tifa\",\n  \"lgudid\"\n];\nfunction inferDeviceIdType() {\n  if (typeof navigator === \"undefined\") {\n    return void 0;\n  }\n  const ua = navigator.userAgent;\n  if (/Roku/i.test(ua)) return \"rida\";\n  if (/Tizen|Samsung/i.test(ua)) return \"tifa\";\n  if (/AppleTV|Apple TV/i.test(ua)) return \"tvOS\";\n  if (/AFT|Fire TV|Amazon/i.test(ua)) return \"afai\";\n  if (/Web0S|webOS|LG Browser|LGSTB|LGE/i.test(ua)) return \"lgudid\";\n  if (/Android|Google TV/i.test(ua)) return \"aaid\";\n  if (/iPhone|iPad|iPod/i.test(ua)) return \"idfa\";\n  return void 0;\n}\nfunction resolveNativeDeviceId() {\n  const bridgeSignals = readNativeBridgeSignals();\n  const platformDevice = readPlatformNativeDeviceId();\n  const deviceId = bridgeSignals.deviceId || readStoredDeviceId() || platformDevice.deviceId;\n  if (!deviceId || isMacroPlaceholder(deviceId) || isZeroedAdId(deviceId)) {\n    return void 0;\n  }\n  return deviceId;\n}\nfunction getOrCreateStoredUuid(storageKey) {\n  if (typeof window === \"undefined\") {\n    return void 0;\n  }\n  const existing = readStoredString(storageKey);\n  if (existing) {\n    return existing;\n  }\n  const id = createUuid();\n  try {\n    window.localStorage?.setItem(storageKey, id);\n  } catch {\n  }\n  return id;\n}\nfunction createUuid() {\n  if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n    return crypto.randomUUID();\n  }\n  return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (char) => {\n    const value = Math.floor(Math.random() * 16);\n    const nibble = char === \"x\" ? value : value & 3 | 8;\n    return nibble.toString(16);\n  });\n}\nfunction readPlatformNativeDeviceId() {\n  if (typeof window === \"undefined\") return {};\n  try {\n    const adinfo = window.webapis?.adinfo;\n    if (adinfo) {\n      const tifa = typeof adinfo.getTIFA === \"function\" ? adinfo.getTIFA() : adinfo.tifa;\n      if (typeof tifa === \"string\" && tifa && !isMacroPlaceholder(tifa)) {\n        const lat = typeof adinfo.isLATEnabled === \"function\" ? adinfo.isLATEnabled() : void 0;\n        return {\n          deviceId: tifa,\n          deviceIdType: \"tifa\",\n          ...typeof lat === \"boolean\" ? { limitAdTracking: lat } : {}\n        };\n      }\n    }\n  } catch {\n  }\n  try {\n    const webOSDev = window.webOSDev;\n    if (webOSDev) {\n      const lgudid = typeof webOSDev.LGUDID === \"function\" ? webOSDev.LGUDID() : webOSDev.lgudid ?? webOSDev.LGUDID;\n      if (typeof lgudid === \"string\" && lgudid) {\n        if (isZeroedAdId(lgudid)) {\n          return { deviceIdType: \"lgudid\", limitAdTracking: true };\n        }\n        if (!isMacroPlaceholder(lgudid)) {\n          return { deviceId: lgudid, deviceIdType: \"lgudid\", limitAdTracking: false };\n        }\n      }\n    }\n  } catch {\n  }\n  try {\n    const roku = window.Roku;\n    if (roku) {\n      const rida = typeof roku.getPublisherUniqueId === \"function\" ? roku.getPublisherUniqueId() : roku.rida;\n      if (typeof rida === \"string\" && rida && !isMacroPlaceholder(rida)) {\n        return { deviceId: rida, deviceIdType: \"rida\", limitAdTracking: false };\n      }\n    }\n  } catch {\n  }\n  return {};\n}\nvar ZEROED_AD_ID_PATTERNS = /* @__PURE__ */ new Set([\n  \"38400000-8cf0-11bd-b23e-10b96e40000d\",\n  \"00000000-0000-0000-0000-000000000000\"\n]);\nfunction isMacroPlaceholder(value) {\n  const t = value.trim();\n  return !t || /^(\\[[^\\]]*\\]|\\{[^}]*\\}|%%[^%]*%%)$/.test(t) || /^(unknown|null|undefined|none|n\\/a|\\$\\{[^}]+\\})$/i.test(t) || ZEROED_AD_ID_PATTERNS.has(t.toLowerCase());\n}\nfunction isZeroedAdId(value) {\n  return ZEROED_AD_ID_PATTERNS.has(value.trim().toLowerCase());\n}\nfunction readNativeBridgeSignals() {\n  if (typeof window === \"undefined\") {\n    return {};\n  }\n  const candidates = [\n    window.__STORMCLOUD_CTV_AD_INFO__,\n    window.__STORMCLOUD_CTV__,\n    window.stormcloudCtv,\n    window.AiryTV,\n    window.Android,\n    window.webOS,\n    window.tizen,\n    window.amazon\n  ].filter(Boolean);\n  for (const source of candidates) {\n    const contentUrl = readBridgeValue(source, [\"contentUrl\", \"url\", \"videoUrl\", \"canonicalUrl\"]) || void 0;\n    const appId = readBridgeValue(source, [\"appId\", \"msid\", \"applicationId\", \"packageName\"]) || void 0;\n    const appName = readBridgeValue(source, [\"appName\", \"an\", \"applicationName\"]) || void 0;\n    const deviceId = readBridgeValue(source, [\n      \"rdid\",\n      \"ifa\",\n      \"advertisingId\",\n      \"adId\",\n      \"deviceId\",\n      \"lgudid\",\n      \"LGUDID\",\n      \"tifa\",\n      \"rida\",\n      \"afai\",\n      \"aaid\"\n    ]) || void 0;\n    if (!contentUrl && !deviceId && !appId && !appName) continue;\n    const deviceIdType = readBridgeValue(source, [\n      \"idtype\",\n      \"deviceIdType\",\n      \"advertisingIdType\",\n      \"idType\"\n    ]) || inferDeviceIdType();\n    const limitAdTracking = readBridgeBoolean(source, [\n      \"is_lat\",\n      \"limitAdTracking\",\n      \"limitedAdTracking\",\n      \"lat\"\n    ]);\n    return {\n      ...contentUrl ? { contentUrl } : {},\n      ...appId ? { appId } : {},\n      ...appName ? { appName } : {},\n      ...deviceId ? { deviceId } : {},\n      ...deviceId && deviceIdType ? { deviceIdType } : {},\n      ...deviceId && limitAdTracking != null ? { limitAdTracking } : {}\n    };\n  }\n  return {};\n}\nfunction readBridgeValue(source, keys) {\n  for (const key of keys) {\n    const value = source?.[key];\n    if (typeof value === \"string\" && value) {\n      return value;\n    }\n    if (typeof value === \"function\") {\n      try {\n        const result = value.call(source);\n        if (typeof result === \"string\" && result) {\n          return result;\n        }\n      } catch {\n      }\n    }\n  }\n  return void 0;\n}\nfunction readBridgeBoolean(source, keys) {\n  for (const key of keys) {\n    const value = source?.[key];\n    const normalized = normalizeBoolean(value);\n    if (normalized != null) {\n      return normalized;\n    }\n    if (typeof value === \"function\") {\n      try {\n        const result = normalizeBoolean(value.call(source));\n        if (result != null) {\n          return result;\n        }\n      } catch {\n      }\n    }\n  }\n  return void 0;\n}\nfunction readStoredDeviceId() {\n  for (const key of DEVICE_ID_STORAGE_KEYS) {\n    const value = readStoredString(key);\n    if (value) {\n      return value;\n    }\n  }\n  return void 0;\n}\nfunction readStoredString(key) {\n  if (typeof window === \"undefined\") {\n    return void 0;\n  }\n  try {\n    const value = window.localStorage?.getItem(key);\n    return value || void 0;\n  } catch {\n    return void 0;\n  }\n}\nfunction normalizeBoolean(value) {\n  if (typeof value === \"boolean\") {\n    return value;\n  }\n  if (typeof value === \"number\") {\n    return value === 1;\n  }\n  if (typeof value === \"string\") {\n    const normalized = value.toLowerCase();\n    if (normalized === \"1\" || normalized === \"true\" || normalized === \"yes\") {\n      return true;\n    }\n    if (normalized === \"0\" || normalized === \"false\" || normalized === \"no\") {\n      return false;\n    }\n  }\n  return void 0;\n}\n\n// src/utils/tracking.ts\nvar cachedBrowserId = null;\nfunction buildAnalyticsContext(config) {\n  const context = {};\n  if (config.debugAdTiming) {\n    context.debugAdTiming = true;\n  }\n  if (config.isLiveStream !== void 0) {\n    context.inputStreamType = config.isLiveStream ? \"live\" : \"vod\";\n  }\n  return context;\n}\nfunction getClientInfo() {\n  const ua = navigator.userAgent;\n  const platform = navigator.platform;\n  const vendor = navigator.vendor || \"\";\n  const maxTouchPoints = navigator.maxTouchPoints || 0;\n  const memory = navigator.deviceMemory || null;\n  const hardwareConcurrency = navigator.hardwareConcurrency || 1;\n  const screenInfo = {\n    width: screen?.width,\n    height: screen?.height,\n    availWidth: screen?.availWidth,\n    availHeight: screen?.availHeight,\n    orientation: screen?.orientation?.type || \"\",\n    pixelDepth: screen?.pixelDepth\n  };\n  let deviceType = \"desktop\";\n  let brand = \"Unknown\";\n  let os = \"Unknown\";\n  let model = \"\";\n  let isSmartTV = false;\n  let isAndroid = false;\n  let isWebView = false;\n  let isWebApp = false;\n  if (ua.includes(\"Web0S\")) {\n    brand = \"LG\";\n    os = \"webOS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n    const webosMatch = ua.match(/Web0S\\/([^\\s]+)/);\n    model = webosMatch ? `webOS ${webosMatch[1]}` : \"webOS TV\";\n  } else if (ua.includes(\"Tizen\")) {\n    brand = \"Samsung\";\n    os = \"Tizen\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n    const tizenMatch = ua.match(/Tizen\\/([^\\s]+)/);\n    const tvMatch = ua.match(/(?:Smart-TV|SMART-TV|TV)/i) ? \"Smart TV\" : \"\";\n    model = tizenMatch ? `Tizen ${tizenMatch[1]} ${tvMatch}`.trim() : \"Tizen TV\";\n  } else if (ua.includes(\"Philips\")) {\n    brand = \"Philips\";\n    os = \"Saphi\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"Sharp\") || ua.includes(\"AQUOS\")) {\n    brand = \"Sharp\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"Android\") && (ua.includes(\"Sony\") || vendor.includes(\"Sony\"))) {\n    brand = \"Sony\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"Android\") && (ua.includes(\"NetCast\") || ua.includes(\"LG\"))) {\n    brand = \"LG\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\" Roku\") || ua.includes(\"Roku/\")) {\n    brand = \"Roku\";\n    os = \"Roku OS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"AppleTV\")) {\n    brand = \"Apple\";\n    os = \"tvOS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  }\n  if (ua.includes(\"Android\")) {\n    isAndroid = true;\n    os = \"Android\";\n    deviceType = /Mobile/.test(ua) ? \"mobile\" : \"tablet\";\n    if (ua.includes(\"Android\") && (maxTouchPoints === 0 || ua.includes(\"Google TV\") || ua.includes(\"XiaoMi\"))) {\n      deviceType = \"tv\";\n      isSmartTV = true;\n      brand = brand === \"Unknown\" ? \"Android TV\" : brand;\n    }\n    const androidModelMatch = ua.match(/\\(([^)]*Android[^)]*)\\)/);\n    if (androidModelMatch && androidModelMatch[1]) {\n      model = androidModelMatch[1];\n    }\n  }\n  if (/iPad|iPhone|iPod/.test(ua)) {\n    os = \"iOS\";\n    deviceType = \"mobile\";\n    brand = \"Apple\";\n    if (navigator.maxTouchPoints > 1 && /iPad/.test(ua)) {\n      deviceType = \"tablet\";\n    }\n  }\n  if (!isAndroid && !isSmartTV && !/Mobile/.test(ua)) {\n    if (ua.includes(\"Windows\")) {\n      os = \"Windows\";\n      deviceType = \"desktop\";\n    } else if (ua.includes(\"Mac\") && !/iPhone/.test(ua)) {\n      os = \"macOS\";\n      deviceType = \"desktop\";\n      if (maxTouchPoints > 1) deviceType = \"tablet\";\n    } else if (ua.includes(\"Linux\")) {\n      os = \"Linux\";\n      deviceType = \"desktop\";\n    }\n  }\n  if (brand === \"Unknown\") {\n    if (vendor.includes(\"Google\") || ua.includes(\"Chrome\")) brand = \"Google\";\n    if (vendor.includes(\"Apple\")) brand = \"Apple\";\n    if (vendor.includes(\"Samsung\") || ua.includes(\"SM-\")) brand = \"Samsung\";\n  }\n  isWebView = /wv|WebView|Linux; U;/.test(ua);\n  if (window?.outerHeight === 0 && window?.outerWidth === 0) {\n    isWebView = true;\n  }\n  isWebApp = window.matchMedia(\"(display-mode: standalone)\").matches || window.navigator.standalone === true || window.screen?.orientation?.angle !== void 0;\n  return {\n    brand,\n    os,\n    model: model || ua.substring(0, 50) + \"...\",\n    deviceType,\n    isSmartTV,\n    isAndroid,\n    isWebView,\n    isWebApp,\n    domain: window.location.hostname,\n    origin: window.location.origin,\n    path: window.location.pathname,\n    userAgent: ua,\n    vendor,\n    platform,\n    screen: screenInfo,\n    hardwareConcurrency,\n    deviceMemory: memory,\n    maxTouchPoints,\n    language: navigator.language,\n    languages: navigator.languages?.join(\",\") || \"\",\n    cookieEnabled: navigator.cookieEnabled,\n    doNotTrack: navigator.doNotTrack || \"\",\n    referrer: document.referrer,\n    visibilityState: document.visibilityState\n  };\n}\nasync function sha256Hex(input) {\n  if (typeof crypto !== \"undefined\" && crypto.subtle?.digest) {\n    try {\n      await crypto.subtle.digest(\"SHA-256\", new Uint8Array([1, 2, 3]));\n      let encodedData;\n      if (typeof TextEncoder !== \"undefined\") {\n        encodedData = new TextEncoder().encode(input);\n      } else {\n        const utf8 = unescape(encodeURIComponent(input));\n        const buffer = new Uint8Array(utf8.length);\n        for (let i = 0; i < utf8.length; i++) {\n          buffer[i] = utf8.charCodeAt(i);\n        }\n        encodedData = buffer;\n      }\n      const hashBuffer = await crypto.subtle.digest(\"SHA-256\", encodedData);\n      return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n    } catch {\n      console.warn(\n        \"[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash\"\n      );\n    }\n  }\n  let hash = 0;\n  for (let i = 0; i < input.length; i++) {\n    const char = input.charCodeAt(i);\n    hash = (hash << 5) - hash + char;\n    hash = hash & hash;\n  }\n  const fallbackHash = Math.abs(hash).toString(16).padStart(8, \"0\");\n  const timestamp = Date.now().toString(16).padStart(12, \"0\");\n  const random = Math.random().toString(16).substring(2, 14).padStart(12, \"0\");\n  return (fallbackHash + timestamp + random).padEnd(64, \"0\");\n}\nfunction resolvePlayerIdentitySeed(clientInfo) {\n  const nativeDeviceId = resolveNativeDeviceId();\n  if (nativeDeviceId) {\n    return `device:${nativeDeviceId}`;\n  }\n  const persistedId = getOrCreateStoredUuid(PLAYER_ID_STORAGE_KEY);\n  if (persistedId) {\n    return `persisted:${persistedId}`;\n  }\n  return `fingerprint:${JSON.stringify(clientInfo)}`;\n}\nasync function getBrowserID(clientInfo) {\n  if (cachedBrowserId) {\n    return cachedBrowserId;\n  }\n  cachedBrowserId = await sha256Hex(resolvePlayerIdentitySeed(clientInfo));\n  return cachedBrowserId;\n}\nfunction canPublish(licenseKey) {\n  return Boolean(isMQTTEnabled() && licenseKey);\n}\nasync function buildPlayerMetricEvent(context = {}, flags = {}) {\n  const clientInfo = getClientInfo();\n  const debugAdTiming = !!context.debugAdTiming;\n  if (debugAdTiming) {\n    console.log(\"[StormcloudVideoPlayer] clientInfo: \", JSON.stringify(clientInfo, null, 2));\n  }\n  const playerId = await getBrowserID(clientInfo);\n  if (debugAdTiming) {\n    console.log(\"[StormcloudVideoPlayer] playerId: \", playerId);\n  }\n  const captureAt = flags.captureAt ?? (/* @__PURE__ */ new Date()).toISOString();\n  return {\n    player_id: playerId,\n    device_type: clientInfo.deviceType,\n    os: clientInfo.os.toLowerCase(),\n    ad_loaded: flags.adLoaded ?? false,\n    ad_detect: flags.adDetect ?? false,\n    capture_at: captureAt,\n    ...context.inputStreamType ? { input_stream_type: context.inputStreamType } : {}\n  };\n}\nfunction publishTracking(licenseKey, channel, body) {\n  ensureMQTTClient();\n  publishMQTT(buildPlayerTopic(licenseKey, channel), body);\n}\nasync function sendAdLoadedTracking(licenseKey, adLoadedInfo, context = {}) {\n  try {\n    await sendHeartbeat(licenseKey, context, {\n      adLoaded: true,\n      captureAt: adLoadedInfo.timestamp\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending ad loaded tracking:\",\n      error\n    );\n  }\n}\nasync function sendAdImpressionTracking(licenseKey, adImpressionInfo, context = {}) {\n  if (!canPublish(licenseKey)) return;\n  try {\n    const metricEvent = await buildPlayerMetricEvent(context, {\n      captureAt: adImpressionInfo.timestamp\n    });\n    publishTracking(licenseKey, \"heartbeat\", metricEvent);\n    publishTracking(licenseKey, \"impressions\", {\n      events: [\n        {\n          player_id: metricEvent.player_id,\n          ad_played_count: 1,\n          ad_url: adImpressionInfo.adUrl,\n          capture_at: adImpressionInfo.timestamp\n        }\n      ]\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending ad impression tracking:\",\n      error\n    );\n  }\n}\nasync function sendHeartbeat(licenseKey, context = {}, flags = {}) {\n  if (!canPublish(licenseKey)) return;\n  try {\n    const heartbeatData = await buildPlayerMetricEvent(context, flags);\n    publishTracking(licenseKey, \"heartbeat\", heartbeatData);\n  } catch (error) {\n    console.error(\"[StormcloudVideoPlayer] Error sending heartbeat:\", error);\n  }\n}\n\n// src/player/PlayerControls.ts\nfunction getAdAudioVolume(adPlayer) {\n  return adPlayer.getOriginalMutedState() ? 0 : adPlayer.getOriginalVolume();\n}\n\n// src/player/AdBreakOrchestrator.ts\nvar AdBreakOrchestrator = class {\n  constructor(host, timing, preloadPool, adConfig, cueManager, placeholder) {\n    this.inAdBreak = false;\n    this.showAds = false;\n    this.isShowingPlaceholder = false;\n    this.adDetectSentForCurrentBreak = false;\n    this.continuousFetchingActive = false;\n    this.continuousFetchLoopRunning = false;\n    this.adRequestQueue = [];\n    this.adPodQueue = [];\n    this.maxPlaceholderDurationMs = 5e3;\n    this.pendingAdBreak = null;\n    this.host = host;\n    this.timing = timing;\n    this.preloadPool = preloadPool;\n    this.adConfig = adConfig;\n    this.cueManager = cueManager;\n    this.placeholder = placeholder;\n  }\n  get debug() {\n    return !!this.host.config.debugAdTiming;\n  }\n  get analyticsContext() {\n    return buildAnalyticsContext(this.host.config);\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  IMA event listeners\n  // ───────────────────────────────────────────────────────────────\n  get optimizedPodsEnabled() {\n    return !!this.host.config.optimizedPods;\n  }\n  attachImaEventListeners() {\n    const adPlayer = this.host.getAdPlayer();\n    adPlayer.on(\"pod_ad_started\", (payload) => {\n      this.timing.notePodAdStarted();\n      if (this.debug) {\n        console.log(\n          `[POD] Playing pod ad ${payload?.index != null ? payload.index + 1 : \"?\"}/${payload?.total ?? \"?\"}`\n        );\n      }\n    });\n    adPlayer.on(\"all_ads_completed\", () => {\n      sendAdImpressionTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", adIndex: this.timing.currentAdIndex, timestamp: (/* @__PURE__ */ new Date()).toISOString() },\n        this.analyticsContext\n      ).catch(() => {\n      });\n      this.timing.recordPlayedAdDuration(this.host.getAdPlayer());\n      const remaining = this.timing.getRemainingAdMs();\n      this.timing.consecutiveFailures = 0;\n      if (this.debug) {\n        console.log(\n          `[StormcloudVideoPlayer] all_ads_completed event received - remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`,\n          this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n        );\n      }\n      if (this.inAdBreak) {\n        if (this.timing.hasTimeToStartAnotherAd()) {\n          this.tryNextAvailableAdWithRateLimit();\n        } else {\n          this.handleAdPodComplete();\n        }\n      }\n    });\n    adPlayer.on(\"ad_error\", (errorPayload) => {\n      let errorMessage = \"Ad playback failed\";\n      if (errorPayload) {\n        const errorCode = errorPayload.code || errorPayload.errorCode || \"unknown\";\n        const vastErrorCode = errorPayload.vastErrorCode;\n        const message = errorPayload.message || errorPayload.errorMessage || \"Unknown error\";\n        const cause = errorPayload.cause || errorPayload.innerError || errorPayload.error;\n        errorMessage = `Ad error: AdError ${errorCode}: ${message}`;\n        if (vastErrorCode && vastErrorCode !== \"N/A\" && vastErrorCode !== errorCode) {\n          errorMessage += ` (VAST Error Code: ${vastErrorCode})`;\n        }\n        if (cause) {\n          const causeMessage = typeof cause === \"string\" ? cause : cause.message || String(cause);\n          errorMessage += `. Caused by: ${causeMessage}`;\n        }\n      }\n      console.error(\"[AD-ERROR]\", errorMessage, errorPayload || \"\");\n      this.handleAdFailure();\n    });\n    adPlayer.on(\"content_pause\", () => {\n      this.timing.clearAdFailsafeTimer();\n      this.timing.clearAdRequestWatchdog();\n      this.timing.activeAdRequestToken = null;\n      this.showAds = true;\n      this.timing.resetGamNoFillCounter();\n      if (this.inAdBreak && this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n        this.timing.scheduleAdStopAtBreakBoundary();\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Starting ad break timer on content_pause (first ad starting)\");\n        }\n      }\n      this.host.getAdPlayer().setAdVolume(getAdAudioVolume(this.host.getAdPlayer()));\n      if (this.isShowingPlaceholder) {\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Hiding placeholder - new ads starting\");\n        }\n        this.host.getAdPlayer().hidePlaceholder();\n        this.isShowingPlaceholder = false;\n      }\n    });\n    adPlayer.on(\"content_resume\", () => {\n      sendAdImpressionTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", adIndex: this.timing.currentAdIndex, timestamp: (/* @__PURE__ */ new Date()).toISOString() },\n        this.analyticsContext\n      ).catch(() => {\n      });\n      if (!this.host.video.muted) {\n        this.host.video.muted = true;\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Muted video on content_resume (ad ended, gap between ads)\");\n        }\n      }\n      this.timing.clearAdFailsafeTimer();\n      this.timing.clearAdRequestWatchdog();\n      this.timing.activeAdRequestToken = null;\n      this.showAds = false;\n      if (!this.inAdBreak) {\n        return;\n      }\n      this.timing.consecutiveFailures = 0;\n      this.timing.recordPlayedAdDuration(this.host.getAdPlayer());\n      const remaining = this.timing.getRemainingAdMs();\n      if (this.debug) {\n        console.log(\n          `[CONTINUOUS-FETCH] content_resume event: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`,\n          this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n        );\n      }\n      if (this.timing.hasTimeToStartAnotherAd()) {\n        this.tryNextAvailableAdWithRateLimit();\n      } else {\n        this.handleAdPodComplete();\n      }\n    });\n  }\n  recreateAdController() {\n    if (this.debug) {\n      console.log(\"[StormcloudVideoPlayer] Recreating AdController for new ad\");\n    }\n    const shouldShowPlaceholder = this.inAdBreak && this.showAds;\n    const adPlayer = this.host.getAdPlayer();\n    if (shouldShowPlaceholder && adPlayer) {\n      if (this.debug) {\n        console.log(\"[StormcloudVideoPlayer] Showing placeholder before destroying old AdController\");\n      }\n      this.placeholder.show();\n      adPlayer.showPlaceholder();\n      this.isShowingPlaceholder = true;\n    }\n    const preservedMutedState = adPlayer.getOriginalMutedState();\n    const preservedVolumeState = adPlayer.getOriginalVolume();\n    if (adPlayer) {\n      try {\n        adPlayer.destroy();\n        this.host.video.muted = true;\n      } catch (error) {\n        if (this.debug) {\n          console.warn(\"[StormcloudVideoPlayer] Error destroying old AdController:\", error);\n        }\n      }\n    }\n    const continueLiveStreamDuringAds = this.host.shouldContinueLiveStreamDuringAds();\n    const newAdPlayer = this.host.createAdPlayer(continueLiveStreamDuringAds);\n    this.host.setAdPlayer(newAdPlayer);\n    newAdPlayer.initialize();\n    newAdPlayer.updateOriginalMutedState(preservedMutedState, preservedVolumeState);\n    this.attachImaEventListeners();\n    if (shouldShowPlaceholder) {\n      this.placeholder.show();\n      newAdPlayer.showPlaceholder();\n      this.isShowingPlaceholder = true;\n    }\n  }\n  clearPendingAdBreak() {\n    if (this.prefetchTimerId != null) {\n      clearTimeout(this.prefetchTimerId);\n      this.prefetchTimerId = void 0;\n    }\n    this.pendingAdBreak = null;\n    this.cueManager.pendingScte35CueKey = void 0;\n    this.adConfig.podAssignedByPrefetch = false;\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Ad break start\n  // ───────────────────────────────────────────────────────────────\n  async handleAdStart(_marker) {\n    const scheduled = this.adConfig.findCurrentOrNextBreak(\n      this.host.video.currentTime * 1e3\n    );\n    if (scheduled) {\n      this.adConfig.consumedVmapBreakIds.add(this.adConfig.getAdBreakKey(scheduled));\n    }\n    const tags = this.adConfig.selectVastTagsForBreak(scheduled);\n    let baseVastUrl;\n    if (tags && tags.length > 0 && tags[0]) {\n      baseVastUrl = tags[0];\n    } else if (this.adConfig.apiVastTagUrl) {\n      baseVastUrl = this.adConfig.apiVastTagUrl;\n    } else {\n      this.clearPendingAdBreak();\n      return;\n    }\n    const adBreakDurationMs = _marker.durationSeconds != null ? _marker.durationSeconds * 1e3 : scheduled?.durationMs;\n    if (this.debug) {\n      const mode = this.host.config.isLiveStream ? \"LIVE\" : \"VOD\";\n      console.log(\n        `[CONTINUOUS-FETCH] ${mode} MODE: Target duration=${adBreakDurationMs}ms`\n      );\n    }\n    this.timing.failedVastUrls.clear();\n    this.timing.temporaryFailureUrls.clear();\n    this.timing.consecutiveFailures = 0;\n    this.continuousFetchingActive = true;\n    this.isShowingPlaceholder = false;\n    this.timing.totalAdRequestsInBreak = 0;\n    if (this.adConfig.podAssignedByPrefetch) {\n      this.adConfig.podAssignedByPrefetch = false;\n    } else {\n      this.adConfig.beginNewAdPod();\n    }\n    const currentMuted = this.host.video.muted;\n    const currentVolume = this.host.video.volume;\n    this.host.getAdPlayer().updateOriginalMutedState(currentMuted, currentVolume);\n    this.inAdBreak = true;\n    this.timing.adBreakPlayedDurationMs = 0;\n    this.timing.currentAdIndex = 0;\n    this.timing.totalAdsInBreak = 0;\n    this.adPodQueue = [];\n    this.showAds = false;\n    if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {\n      this.timing.expectedAdBreakDurationMs = adBreakDurationMs;\n    }\n    if (this.optimizedPodsEnabled) {\n      const podPlayed = await this.startOptimizedPod(\n        baseVastUrl,\n        adBreakDurationMs,\n        currentMuted,\n        currentVolume\n      );\n      if (podPlayed) {\n        return;\n      }\n      if (this.debug) {\n        console.log(\"[POD] Optimized pod unavailable, falling back to sequential single-ad path\");\n      }\n    }\n    let firstAdUrl;\n    let usePreloadedAd = false;\n    let preloadedController;\n    const preloaded = this.preloadPool.getPreloadedAd();\n    if (preloaded) {\n      firstAdUrl = preloaded.vastUrl;\n      preloadedController = preloaded.adController;\n      usePreloadedAd = true;\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: firstAdUrl, timestamp: (/* @__PURE__ */ new Date()).toISOString() },\n        this.analyticsContext\n      ).catch(() => {\n      });\n      if (this.debug) {\n        console.log(\"[CONTINUOUS-FETCH] Using preloaded ad from pool (preloaded in advance, ready immediately!)\");\n      }\n    } else if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {\n      this.adRequestQueue = [...this.pendingAdBreak.vastUrls];\n      firstAdUrl = this.adRequestQueue.shift();\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Pool empty, using prefetched URLs (${this.adRequestQueue.length + 1} available)`);\n      }\n      this.clearPendingAdBreak();\n    } else {\n      if (this.debug) {\n        console.log(\"[CONTINUOUS-FETCH] Generating new ad URLs...\");\n      }\n      const firstAdUrlArray = this.host.generateVastUrls(baseVastUrl, 1);\n      firstAdUrl = firstAdUrlArray[0];\n      this.adRequestQueue = [];\n    }\n    if (!firstAdUrl) {\n      if (this.debug) {\n        console.warn(\"[CONTINUOUS-FETCH] Failed to get first ad URL\");\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n    if (this.debug) {\n      console.log(\"[CONTINUOUS-FETCH] First VAST URL:\", firstAdUrl);\n    }\n    try {\n      if (usePreloadedAd && preloadedController) {\n        if (!this.timing.ensureLoadedAdFitsBudget(preloadedController)) {\n          this.timing.rejectLoadedAdForDuration(preloadedController);\n          this.startContinuousFetching(baseVastUrl);\n          await this.tryNextAvailableAdWithRateLimit();\n          return;\n        }\n        const adPlayer = this.host.getAdPlayer();\n        const preservedMuted = adPlayer.getOriginalMutedState();\n        const preservedVolume = adPlayer.getOriginalVolume();\n        adPlayer.destroy();\n        this.host.video.muted = true;\n        this.host.setAdPlayer(preloadedController);\n        preloadedController.updateOriginalMutedState(preservedMuted, preservedVolume);\n        this.attachImaEventListeners();\n        if (this.debug) {\n          console.log(\"[CONTINUOUS-FETCH] Using preloaded ad, resuming and starting playback immediately (no request delay!)\");\n        }\n        preloadedController.resume();\n        this.timing.consecutiveFailures = 0;\n        this.startContinuousFetching(baseVastUrl);\n        if (!this.preloadPool.active) {\n          this.preloadPool.active = true;\n        }\n        if (!this.preloadPool.isLoopRunning) {\n          this.preloadPool.preloadPoolLoop(baseVastUrl);\n        }\n        await preloadedController.play();\n        this.timing.markAdStarted();\n        if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n          this.timing.scheduleAdStopAtBreakBoundary();\n        }\n        const adVolume = currentMuted ? 0 : currentVolume;\n        preloadedController.setAdVolume(adVolume);\n      } else {\n        const adPlayer = this.host.getAdPlayer();\n        await this.timing.enforceGlobalRateLimit();\n        this.timing.lastAdRequestTime = Date.now();\n        await adPlayer.requestAds(firstAdUrl);\n        sendAdLoadedTracking(\n          this.host.config.licenseKey,\n          { source: \"hls\", vastUrl: firstAdUrl, timestamp: (/* @__PURE__ */ new Date()).toISOString() },\n          this.analyticsContext\n        ).catch(() => {\n        });\n        if (this.debug) {\n          console.log(\"[CONTINUOUS-FETCH] First ad request successful, starting playback\");\n        }\n        if (!this.timing.ensureLoadedAdFitsBudget(adPlayer)) {\n          this.timing.rejectLoadedAdForDuration(adPlayer);\n          this.recreateAdController();\n          this.startContinuousFetching(baseVastUrl);\n          await this.tryNextAvailableAdWithRateLimit();\n          return;\n        }\n        this.timing.consecutiveFailures = 0;\n        this.startContinuousFetching(baseVastUrl);\n        if (!this.preloadPool.active) {\n          this.preloadPool.startPreloadPool(baseVastUrl, []);\n        }\n        await adPlayer.play();\n        this.timing.markAdStarted();\n        if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n          this.timing.scheduleAdStopAtBreakBoundary();\n        }\n        const adVolume = currentMuted ? 0 : currentVolume;\n        adPlayer.setAdVolume(adVolume);\n      }\n    } catch (error) {\n      if (this.debug) {\n        console.warn(\"[CONTINUOUS-FETCH] First ad request failed:\", error);\n      }\n      if (!usePreloadedAd) {\n        const fallbackPreloaded = this.preloadPool.getPreloadedAd();\n        if (fallbackPreloaded) {\n          if (this.debug) {\n            console.log(\"[CONTINUOUS-FETCH] First ad failed, using preloaded fallback\");\n          }\n          try {\n            if (!this.timing.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) {\n              this.timing.rejectLoadedAdForDuration(fallbackPreloaded.adController);\n              this.startContinuousFetching(baseVastUrl);\n              await this.tryNextAvailableAdWithRateLimit();\n              return;\n            }\n            this.swapToPreloadedAdPlayer(fallbackPreloaded.adController);\n            this.timing.consecutiveFailures = 0;\n            this.startContinuousFetching(baseVastUrl);\n            if (!this.preloadPool.active) {\n              this.preloadPool.active = true;\n              this.preloadPool.preloadPoolLoop(baseVastUrl);\n            }\n            const ap = this.host.getAdPlayer();\n            await ap.play();\n            this.timing.markAdStarted();\n            if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n              this.timing.scheduleAdStopAtBreakBoundary();\n            }\n            const adVolume = currentMuted ? 0 : currentVolume;\n            ap.setAdVolume(adVolume);\n            return;\n          } catch (fallbackError) {\n            if (this.debug) {\n              console.warn(\"[CONTINUOUS-FETCH] Preloaded fallback also failed:\", fallbackError);\n            }\n          }\n        }\n      }\n      if (this.timing.isTemporaryAdError(error)) {\n        this.timing.temporaryFailureUrls.set(firstAdUrl, Date.now());\n        if (this.debug) {\n          console.log(\"[CONTINUOUS-FETCH] First ad had temporary error (no-fill) - will try alternatives\");\n        }\n      } else {\n        this.timing.failedVastUrls.add(firstAdUrl);\n        if (this.debug) {\n          console.log(\"[CONTINUOUS-FETCH] First ad had permanent error - URL blacklisted\");\n        }\n      }\n      this.timing.consecutiveFailures++;\n      this.startContinuousFetching(baseVastUrl);\n      await this.tryNextAvailableAdWithRateLimit();\n    }\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Optimized pod (single multi-ad request)\n  // ───────────────────────────────────────────────────────────────\n  async startOptimizedPod(baseVastUrl, breakDurationMs, currentMuted, currentVolume) {\n    let podUrl;\n    if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {\n      podUrl = this.pendingAdBreak.vastUrls[0];\n      this.clearPendingAdBreak();\n    } else {\n      podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);\n    }\n    if (!podUrl) {\n      return false;\n    }\n    this.continuousFetchingActive = false;\n    this.recreateAdController();\n    const adPlayer = this.host.getAdPlayer();\n    const requestToken = ++this.timing.adRequestTokenCounter;\n    this.timing.activeAdRequestToken = requestToken;\n    this.timing.startAdRequestWatchdog(requestToken);\n    try {\n      await this.timing.enforceGlobalRateLimit();\n      this.timing.lastAdRequestTime = Date.now();\n      await adPlayer.requestAds(podUrl);\n      this.timing.clearAdRequestWatchdog();\n      if (this.timing.activeAdRequestToken !== requestToken) {\n        return true;\n      }\n      const podCount = adPlayer.getPodAdCount?.() ?? 1;\n      if (podCount < 1) {\n        return false;\n      }\n      if (this.debug) {\n        console.log(\n          podCount === 1 ? \"[POD] Response contained a single ad; playing it and enabling sequential top-up\" : `[POD] Optimized pod returned ${podCount} ads; playing pod in one controller`\n        );\n      }\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: podUrl, timestamp: (/* @__PURE__ */ new Date()).toISOString() },\n        this.analyticsContext\n      ).catch(() => {\n      });\n      this.timing.startAdFailsafeTimer(requestToken);\n      await adPlayer.play();\n      this.timing.markAdStarted();\n      if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n        this.timing.scheduleAdStopAtBreakBoundary();\n      }\n      adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);\n      this.timing.consecutiveFailures = 0;\n      if (podCount === 1) {\n        this.continuousFetchingActive = true;\n        this.startContinuousFetching(baseVastUrl);\n        if (!this.preloadPool.active) {\n          this.preloadPool.startPreloadPool(baseVastUrl, []);\n        }\n      }\n      return true;\n    } catch (error) {\n      this.timing.clearAdRequestWatchdog();\n      this.timing.clearAdFailsafeTimer();\n      if (this.timing.activeAdRequestToken === requestToken) {\n        this.timing.activeAdRequestToken = null;\n      }\n      if (this.debug) {\n        console.warn(\"[POD] Optimized pod request/play failed:\", error);\n      }\n      if (this.timing.isTemporaryAdError(error)) {\n        this.timing.temporaryFailureUrls.set(podUrl, Date.now());\n      }\n      this.continuousFetchingActive = true;\n      this.recreateAdController();\n      return false;\n    }\n  }\n  swapToPreloadedAdPlayer(preloadedController) {\n    const adPlayer = this.host.getAdPlayer();\n    const preservedMuted = adPlayer.getOriginalMutedState();\n    const preservedVolume = adPlayer.getOriginalVolume();\n    adPlayer.destroy();\n    this.host.video.muted = true;\n    this.host.setAdPlayer(preloadedController);\n    this.attachImaEventListeners();\n    preloadedController.updateOriginalMutedState(preservedMuted, preservedVolume);\n    preloadedController.resume();\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Continuous fetching\n  // ───────────────────────────────────────────────────────────────\n  startContinuousFetching(baseVastUrl) {\n    if (!this.continuousFetchingActive) {\n      return;\n    }\n    if (this.continuousFetchLoopRunning) {\n      if (this.debug) {\n        console.log(\"[CONTINUOUS-FETCH] Fetch loop already running, skipping duplicate start\");\n      }\n      return;\n    }\n    if (this.debug) {\n      console.log(\"[CONTINUOUS-FETCH] Starting continuous ad fetching loop\");\n    }\n    this.continuousFetchLoopRunning = true;\n    this.continuousFetchLoop(baseVastUrl);\n  }\n  async continuousFetchLoop(baseVastUrl) {\n    const epoch = this.timing.adBreakEpoch;\n    while (this.continuousFetchingActive && this.inAdBreak && epoch === this.timing.adBreakEpoch) {\n      const remaining = Math.min(\n        this.timing.getWallClockRemainingAdMs(),\n        this.timing.getDurationBudgetRemainingMs()\n      );\n      if (remaining <= this.timing.getMinRemainingMsToStartAd()) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Not enough ad break time left (wall=${this.timing.getWallClockRemainingAdMs()}ms, budget=${this.timing.getDurationBudgetRemainingMs()}ms), stopping URL generation`);\n        }\n        break;\n      }\n      if (this.timing.isGamInCooldown()) {\n        if (this.debug) {\n          console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, stopping URL generation\");\n        }\n        break;\n      }\n      if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), stopping URL generation`);\n        }\n        break;\n      }\n      if (this.timing.totalAdRequestsInBreak >= this.timing.maxTotalAdRequestsPerBreak) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Maximum ad requests reached (${this.timing.maxTotalAdRequestsPerBreak}), stopping`);\n        }\n        break;\n      }\n      const maxQueueSize = 5;\n      if (this.adRequestQueue.length >= maxQueueSize) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] URL queue full (${this.adRequestQueue.length}), waiting...`);\n        }\n        await new Promise((resolve) => setTimeout(resolve, 1e3));\n        continue;\n      }\n      const newAdUrl = this.host.generateVastUrls(baseVastUrl, 1)[0];\n      if (!newAdUrl || this.timing.failedVastUrls.has(newAdUrl) || this.timing.isUrlInCooldown(newAdUrl)) {\n        await new Promise((resolve) => setTimeout(resolve, 500));\n        continue;\n      }\n      if (this.debug) {\n        const queuedUrlsPreview = [...this.adRequestQueue, newAdUrl];\n        console.log(\n          `[CONTINUOUS-FETCH] Queued VAST URL (queue size: ${queuedUrlsPreview.length})`,\n          { queuedUrls: queuedUrlsPreview }\n        );\n      }\n      this.adRequestQueue.push(newAdUrl);\n      const generationDelay = this.timing.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, this.timing.consecutiveFailures), 5e3) : 500;\n      await new Promise((resolve) => setTimeout(resolve, generationDelay));\n    }\n    this.continuousFetchLoopRunning = false;\n    if (this.debug) {\n      console.log(\n        `[CONTINUOUS-FETCH] URL generation loop ended (queued: ${this.adRequestQueue.length})`,\n        this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n      );\n    }\n  }\n  stopContinuousFetching() {\n    this.continuousFetchingActive = false;\n    this.continuousFetchLoopRunning = false;\n    this.placeholder.hide();\n    if (this.debug) {\n      console.log(\"[CONTINUOUS-FETCH] Stopping continuous ad fetching\");\n    }\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Ad iteration\n  // ───────────────────────────────────────────────────────────────\n  async tryNextAvailableAdWithRateLimit() {\n    if (!this.inAdBreak) {\n      return;\n    }\n    const epoch = this.timing.adBreakEpoch;\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), ending ad break gracefully`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n    const backoffMultiplier = Math.pow(2, this.timing.consecutiveFailures);\n    const backoffDelay = Math.min(this.timing.backoffBaseMs * backoffMultiplier, this.timing.maxBackoffMs);\n    const effectiveMinInterval = this.timing.minAdRequestIntervalMs + (this.timing.consecutiveFailures > 0 ? backoffDelay : 0);\n    const timeSinceLastRequest = Date.now() - this.timing.lastAdRequestTime;\n    if (timeSinceLastRequest < effectiveMinInterval) {\n      const waitTime = effectiveMinInterval - timeSinceLastRequest;\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Rate limiting: waiting ${waitTime}ms before next request (backoff: ${this.timing.consecutiveFailures} failures)`);\n      }\n      await new Promise((resolve) => setTimeout(resolve, waitTime));\n    }\n    if (!this.inAdBreak || epoch !== this.timing.adBreakEpoch) {\n      return;\n    }\n    return this.tryNextAvailableAd(0);\n  }\n  async tryNextAvailableAd(retryCount = 0) {\n    if (!this.inAdBreak) {\n      return;\n    }\n    const remaining = this.timing.getRemainingAdMs();\n    if (!this.timing.hasTimeToStartAnotherAd()) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Not enough time remaining (${remaining}ms) to start another ad, ending ad break`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) {\n        console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, ending ad break gracefully\");\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), ending ad break`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n    this.timing.cleanupTemporaryFailures();\n    const preloaded = this.preloadPool.getPreloadedAd();\n    if (preloaded) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Using preloaded ad from pool`);\n      }\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: preloaded.vastUrl, timestamp: (/* @__PURE__ */ new Date()).toISOString() },\n        this.analyticsContext\n      ).catch(() => {\n      });\n      try {\n        if (!this.timing.ensureLoadedAdFitsBudget(preloaded.adController)) {\n          this.timing.rejectLoadedAdForDuration(preloaded.adController);\n          await this.tryNextAvailableAdWithRateLimit();\n          return;\n        }\n        this.swapToPreloadedAdPlayer(preloaded.adController);\n        const ap = this.host.getAdPlayer();\n        await ap.play();\n        this.timing.markAdStarted();\n        if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n          this.timing.scheduleAdStopAtBreakBoundary();\n        }\n        ap.setAdVolume(getAdAudioVolume(ap));\n        this.timing.consecutiveFailures = 0;\n        return;\n      } catch (error) {\n        if (this.debug) {\n          console.warn(\"[CONTINUOUS-FETCH] Preloaded ad failed to play:\", error);\n        }\n      }\n    }\n    while (this.adRequestQueue.length > 0) {\n      const nextAdUrl = this.adRequestQueue.shift();\n      if (!nextAdUrl) continue;\n      if (this.timing.failedVastUrls.has(nextAdUrl)) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping permanently-failed URL, trying next...\");\n        continue;\n      }\n      if (this.timing.isUrlInCooldown(nextAdUrl)) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping URL in cooldown (recent no-fill), trying next...\");\n        continue;\n      }\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Requesting next ad via HLS ad player (${this.timing.currentAdIndex + 1}, ${this.adRequestQueue.length} remaining in queue)`);\n      }\n      try {\n        await this.playSingleAd(nextAdUrl);\n        this.timing.consecutiveFailures = 0;\n      } catch (error) {\n        const errorMessage = error.message;\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Ad request failed:\", errorMessage);\n        if (errorMessage.includes(\"ad_duration_exceeds_budget\")) {\n          await this.tryNextAvailableAdWithRateLimit();\n          return;\n        }\n        if (!errorMessage.includes(\"permanently failed\") && !errorMessage.includes(\"in cooldown\")) {\n          this.timing.consecutiveFailures++;\n        }\n        await this.tryNextAvailableAdWithRateLimit();\n      }\n      return;\n    }\n    const maxRetries = 3;\n    if (this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Queue empty, waiting for URLs... (${retryCount + 1}/${maxRetries})`);\n      }\n      await new Promise((resolve) => setTimeout(resolve, 500));\n      await this.tryNextAvailableAd(retryCount + 1);\n      return;\n    }\n    if (!this.isShowingPlaceholder && remaining > 1e3) {\n      this.showPlaceholderAndWaitForAds();\n    } else {\n      if (this.debug) console.log(\"[CONTINUOUS-FETCH] No more ads available, ending ad break\");\n      this.handleAdPodComplete();\n    }\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Single ad playback\n  // ───────────────────────────────────────────────────────────────\n  async playSingleAd(vastTagUrl) {\n    const adPlayer = this.host.getAdPlayer();\n    if (adPlayer.isAdPlaying()) return;\n    if (this.timing.failedVastUrls.has(vastTagUrl)) {\n      if (this.debug) console.warn(\"[AD-ERROR] Skipping permanently-failed VAST URL:\", vastTagUrl);\n      throw new Error(\"VAST URL permanently failed\");\n    }\n    if (this.timing.isUrlInCooldown(vastTagUrl)) {\n      if (this.debug) console.warn(\"[AD-ERROR] Skipping URL in cooldown (recent no-fill):\", vastTagUrl);\n      throw new Error(\"VAST URL in cooldown after no-fill\");\n    }\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.warn(\"[AD-ERROR] Too many consecutive failures, not requesting more ads\");\n      throw new Error(\"Too many consecutive failures\");\n    }\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) console.warn(\"[CIRCUIT-BREAKER] GAM in cooldown, skipping ad request\");\n      throw new Error(\"GAM in cooldown\");\n    }\n    this.recreateAdController();\n    const requestToken = ++this.timing.adRequestTokenCounter;\n    this.timing.activeAdRequestToken = requestToken;\n    this.timing.startAdRequestWatchdog(requestToken);\n    try {\n      await this.timing.enforceGlobalRateLimit();\n      this.timing.lastAdRequestTime = Date.now();\n      const ap = this.host.getAdPlayer();\n      await ap.requestAds(vastTagUrl);\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: vastTagUrl, timestamp: (/* @__PURE__ */ new Date()).toISOString() },\n        this.analyticsContext\n      ).catch(() => {\n      });\n      this.timing.clearAdRequestWatchdog();\n      if (this.timing.activeAdRequestToken !== requestToken) return;\n      if (!this.timing.ensureLoadedAdFitsBudget(ap)) {\n        this.timing.rejectLoadedAdForDuration(ap);\n        this.recreateAdController();\n        throw new Error(\"ad_duration_exceeds_budget\");\n      }\n      try {\n        this.timing.startAdFailsafeTimer(requestToken);\n        await ap.play();\n        this.timing.markAdStarted();\n        if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n          this.timing.scheduleAdStopAtBreakBoundary();\n        }\n        ap.setAdVolume(getAdAudioVolume(ap));\n        this.timing.consecutiveFailures = 0;\n        this.timing.temporaryFailureUrls.delete(vastTagUrl);\n      } catch (playError) {\n        if (this.debug) console.error(\"[AD-ERROR] Failed to play ad:\", playError);\n        const preloadedFallback = this.preloadPool.getPreloadedAd();\n        if (preloadedFallback) {\n          if (this.debug) console.log(\"[AD-ERROR] Play failed, trying preloaded fallback ad\");\n          try {\n            if (!this.timing.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {\n              this.timing.rejectLoadedAdForDuration(preloadedFallback.adController);\n              throw playError;\n            }\n            this.timing.clearAdFailsafeTimer();\n            this.swapToPreloadedAdPlayer(preloadedFallback.adController);\n            this.timing.consecutiveFailures = 0;\n            const fbAp = this.host.getAdPlayer();\n            await fbAp.play();\n            this.timing.markAdStarted();\n            if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n              this.timing.scheduleAdStopAtBreakBoundary();\n            }\n            fbAp.setAdVolume(getAdAudioVolume(fbAp));\n            return;\n          } catch (fallbackError) {\n            if (this.debug) console.warn(\"[AD-ERROR] Preloaded fallback also failed:\", fallbackError);\n          }\n        }\n        if (this.timing.isTemporaryAdError(playError)) {\n          this.timing.temporaryFailureUrls.set(vastTagUrl, Date.now());\n          if (this.debug) console.log(\"[AD-ERROR] Temporary play error - URL can be retried after cooldown\");\n        } else {\n          this.timing.failedVastUrls.add(vastTagUrl);\n          if (this.debug) console.log(\"[AD-ERROR] Permanent play error - URL blacklisted\");\n        }\n        this.timing.clearAdFailsafeTimer();\n        if (this.timing.activeAdRequestToken === requestToken) {\n          this.timing.activeAdRequestToken = null;\n        }\n        throw playError;\n      }\n    } catch (error) {\n      const errorMessage = error?.message || \"\";\n      if (this.debug) console.error(\"[AD-ERROR] Ad request failed:\", errorMessage);\n      const preloadedFallback = this.preloadPool.getPreloadedAd();\n      if (preloadedFallback) {\n        if (this.debug) console.log(\"[AD-ERROR] Request failed, trying preloaded fallback ad\");\n        try {\n          if (!this.timing.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {\n            this.timing.rejectLoadedAdForDuration(preloadedFallback.adController);\n            throw error;\n          }\n          this.timing.clearAdRequestWatchdog();\n          this.timing.clearAdFailsafeTimer();\n          this.swapToPreloadedAdPlayer(preloadedFallback.adController);\n          this.timing.consecutiveFailures = 0;\n          const fbAp = this.host.getAdPlayer();\n          await fbAp.play();\n          this.timing.markAdStarted();\n          fbAp.setAdVolume(getAdAudioVolume(fbAp));\n          return;\n        } catch (fallbackError) {\n          if (this.debug) console.warn(\"[AD-ERROR] Preloaded fallback also failed:\", fallbackError);\n        }\n      }\n      if (this.timing.isTemporaryAdError(error)) {\n        this.timing.temporaryFailureUrls.set(vastTagUrl, Date.now());\n        if (this.debug) console.log(\"[AD-ERROR] Temporary error (no-fill/timeout) - URL can be retried after cooldown\");\n      } else {\n        this.timing.failedVastUrls.add(vastTagUrl);\n        if (this.debug) console.log(\"[AD-ERROR] Permanent error - URL blacklisted:\", errorMessage);\n      }\n      this.timing.clearAdRequestWatchdog();\n      this.timing.clearAdFailsafeTimer();\n      if (this.timing.activeAdRequestToken === requestToken) {\n        this.timing.activeAdRequestToken = null;\n      }\n      throw error;\n    }\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Placeholder wait\n  // ───────────────────────────────────────────────────────────────\n  async showPlaceholderAndWaitForAds() {\n    const remaining = this.timing.getRemainingAdMs();\n    const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, skipping placeholder wait\");\n      this.handleAdPodComplete();\n      return;\n    }\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping placeholder - too many consecutive failures\");\n      this.handleAdPodComplete();\n      return;\n    }\n    if (waitTime < 1e3) {\n      this.handleAdPodComplete();\n      return;\n    }\n    if (this.debug) {\n      console.log(`[CONTINUOUS-FETCH] Showing placeholder for ${waitTime}ms while waiting for ad URLs`);\n    }\n    this.isShowingPlaceholder = true;\n    this.host.getAdPlayer().showPlaceholder();\n    const checkInterval = 300;\n    const maxChecks = Math.floor(waitTime / checkInterval);\n    for (let i = 0; i < maxChecks; i++) {\n      await new Promise((resolve) => setTimeout(resolve, checkInterval));\n      if (!this.inAdBreak) return;\n      if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Too many failures during placeholder wait\");\n        break;\n      }\n      if (!this.timing.hasTimeToStartAnotherAd()) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Not enough time remaining during placeholder wait, ending ad break\");\n        break;\n      }\n      while (this.adRequestQueue.length > 0) {\n        if (!this.timing.hasTimeToStartAnotherAd()) break;\n        const nextAdUrl = this.adRequestQueue.shift();\n        if (!nextAdUrl) continue;\n        if (this.timing.failedVastUrls.has(nextAdUrl)) continue;\n        if (this.timing.isUrlInCooldown(nextAdUrl)) continue;\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Ad URL available, requesting via HLS ad player\");\n        this.isShowingPlaceholder = false;\n        this.host.getAdPlayer().hidePlaceholder();\n        try {\n          await this.playSingleAd(nextAdUrl);\n          this.timing.consecutiveFailures = 0;\n        } catch (error) {\n          const errorMessage = error.message;\n          if (errorMessage.includes(\"ad_duration_exceeds_budget\")) {\n            await this.tryNextAvailableAdWithRateLimit();\n            return;\n          }\n          if (!errorMessage.includes(\"permanently failed\") && !errorMessage.includes(\"in cooldown\")) {\n            this.timing.consecutiveFailures++;\n          }\n          await this.tryNextAvailableAdWithRateLimit();\n        }\n        return;\n      }\n    }\n    if (this.debug) console.log(\"[CONTINUOUS-FETCH] Placeholder timeout, ending ad break\");\n    this.isShowingPlaceholder = false;\n    this.host.getAdPlayer().hidePlaceholder();\n    this.handleAdPodComplete();\n  }\n  // ───────────────────────────────────────────────────────────────\n  //  Ad break completion\n  // ───────────────────────────────────────────────────────────────\n  handleAdPodComplete() {\n    if (this.debug) {\n      console.log(\"[StormcloudVideoPlayer] Ad pod complete - cleaning up\");\n    }\n    this.timing.resetForBreakEnd();\n    this.stopContinuousFetching();\n    this.preloadPool.stop();\n    this.clearPendingAdBreak();\n    if (this.isShowingPlaceholder) {\n      this.host.getAdPlayer().hidePlaceholder();\n      this.isShowingPlaceholder = false;\n    }\n    this.adRequestQueue = [];\n    this.inAdBreak = false;\n    this.adDetectSentForCurrentBreak = false;\n    this.cueManager.activeScte35BreakKey = void 0;\n    this.cueManager.scheduledScte35BreakKey = void 0;\n    this.adPodQueue = [];\n    this.showAds = false;\n    this.host.getAdPlayer().stop().catch(() => {\n    });\n    const restoredMuted = this.host.getAdPlayer().getOriginalMutedState();\n    const restoredVolume = this.host.getAdPlayer().getOriginalVolume();\n    if (this.host.video.muted !== restoredMuted) {\n      this.host.video.muted = restoredMuted;\n    }\n    if (Math.abs(this.host.video.volume - restoredVolume) > 0.01) {\n      this.host.video.volume = restoredVolume;\n    }\n    if (this.host.shouldContinueLiveStreamDuringAds()) {\n      const liveSyncPos = this.host.getLiveSyncPosition();\n      if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {\n        if (this.debug) {\n          console.log(\n            `[StormcloudVideoPlayer] Live break end \\u2014 snapping to live edge: ${this.host.video.currentTime.toFixed(2)}s \\u2192 ${liveSyncPos.toFixed(2)}s`\n          );\n        }\n        this.host.video.currentTime = liveSyncPos;\n      }\n      if (this.host.video.paused) {\n        if (this.debug) console.log(\"[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback\");\n        this.host.video.play()?.catch(() => {\n        });\n      } else {\n        if (this.debug) console.log(\"[StormcloudVideoPlayer] Content video already playing in live mode after ads\");\n      }\n    } else if (this.host.video.paused) {\n      this.host.video.play()?.catch(() => {\n      });\n    }\n  }\n  handleAdFailure() {\n    this.timing.consecutiveFailures++;\n    this.timing.recordGamNoFill();\n    const remaining = this.timing.getRemainingAdMs();\n    if (this.debug) {\n      console.log(\n        `[CONTINUOUS-FETCH] Ad failure: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}, consecutiveFailures=${this.timing.consecutiveFailures}`,\n        this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n      );\n    }\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.log(`[CONTINUOUS-FETCH] Max consecutive failures reached (${this.timing.consecutiveFailures}), ending ad break gracefully`);\n      this.handleAdPodComplete();\n      return;\n    }\n    if (this.timing.hasTimeToStartAnotherAd()) {\n      this.tryNextAvailableAdWithRateLimit();\n    } else {\n      if (this.debug) console.log(\"[AD-ERROR] Ad failed and not enough time remaining for another ad. Failed URLs:\", this.timing.failedVastUrls.size);\n      this.handleAdPodComplete();\n    }\n  }\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n  AdBreakOrchestrator\n});\n","import type {\n  StormcloudVideoPlayerConfig,\n  Scte35Marker,\n  AdController,\n} from \"../types\";\nimport {\n  buildAnalyticsContext,\n  sendAdLoadedTracking,\n  sendAdImpressionTracking,\n} from \"../utils/tracking\";\nimport type { AdTimingService } from \"./AdTimingService\";\nimport type { AdPreloadPool } from \"./AdPreloadPool\";\nimport type { AdConfigManager } from \"./AdConfigManager\";\nimport type { Scte35CueManager } from \"./Scte35CueManager\";\nimport type { PlaceholderLayer } from \"./PlaceholderLayer\";\nimport { getAdAudioVolume } from \"./PlayerControls\";\n\nexport interface AdBreakOrchestratorHost {\n  readonly config: StormcloudVideoPlayerConfig;\n  readonly video: HTMLVideoElement;\n  getAdPlayer(): AdController;\n  setAdPlayer(player: AdController): void;\n  createAdPlayer(continueLiveStreamDuringAds: boolean): AdController;\n  shouldContinueLiveStreamDuringAds(): boolean;\n  getLiveSyncPosition(): number | undefined;\n  generateVastUrls(baseUrl: string, count: number): string[];\n  generatePodVastUrl(baseUrl: string, breakDurationMs?: number): string;\n}\n\nexport class AdBreakOrchestrator {\n  inAdBreak = false;\n  showAds: boolean = false;\n  isShowingPlaceholder: boolean = false;\n  adDetectSentForCurrentBreak: boolean = false;\n\n  private continuousFetchingActive: boolean = false;\n  private continuousFetchLoopRunning = false;\n  adRequestQueue: string[] = [];\n  adPodQueue: string[] = [];\n  private readonly maxPlaceholderDurationMs: number = 5000;\n\n  pendingAdBreak: {\n    marker: Scte35Marker;\n    detectedAtFragmentSn?: number;\n    vastUrls: string[];\n    isFetching: boolean;\n    fetchStartTime?: number;\n  } | null = null;\n  prefetchTimerId: number | undefined;\n\n  private readonly host: AdBreakOrchestratorHost;\n  private readonly timing: AdTimingService;\n  private readonly preloadPool: AdPreloadPool;\n  private readonly adConfig: AdConfigManager;\n  private readonly cueManager: Scte35CueManager;\n  private readonly placeholder: PlaceholderLayer;\n\n  constructor(\n    host: AdBreakOrchestratorHost,\n    timing: AdTimingService,\n    preloadPool: AdPreloadPool,\n    adConfig: AdConfigManager,\n    cueManager: Scte35CueManager,\n    placeholder: PlaceholderLayer\n  ) {\n    this.host = host;\n    this.timing = timing;\n    this.preloadPool = preloadPool;\n    this.adConfig = adConfig;\n    this.cueManager = cueManager;\n    this.placeholder = placeholder;\n  }\n\n  private get debug(): boolean {\n    return !!this.host.config.debugAdTiming;\n  }\n\n  private get analyticsContext() {\n    return buildAnalyticsContext(this.host.config);\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  IMA event listeners\n  // ───────────────────────────────────────────────────────────────\n\n  private get optimizedPodsEnabled(): boolean {\n    return !!this.host.config.optimizedPods;\n  }\n\n  attachImaEventListeners(): void {\n    const adPlayer = this.host.getAdPlayer();\n    adPlayer.on(\"pod_ad_started\", (payload?: any) => {\n      this.timing.notePodAdStarted();\n      if (this.debug) {\n        console.log(\n          `[POD] Playing pod ad ${payload?.index != null ? payload.index + 1 : \"?\"}/${payload?.total ?? \"?\"}`\n        );\n      }\n    });\n    adPlayer.on(\"all_ads_completed\", () => {\n      sendAdImpressionTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", adIndex: this.timing.currentAdIndex, timestamp: new Date().toISOString() },\n        this.analyticsContext\n      ).catch(() => {});\n\n      this.timing.recordPlayedAdDuration(this.host.getAdPlayer());\n      const remaining = this.timing.getRemainingAdMs();\n\n      this.timing.consecutiveFailures = 0;\n\n      if (this.debug) {\n        console.log(\n          `[StormcloudVideoPlayer] all_ads_completed event received - remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`,\n          this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n        );\n      }\n\n      if (this.inAdBreak) {\n        if (this.timing.hasTimeToStartAnotherAd()) {\n          this.tryNextAvailableAdWithRateLimit();\n        } else {\n          this.handleAdPodComplete();\n        }\n      }\n    });\n    adPlayer.on(\"ad_error\", (errorPayload?: any) => {\n      let errorMessage = \"Ad playback failed\";\n\n      if (errorPayload) {\n        const errorCode = errorPayload.code || errorPayload.errorCode || \"unknown\";\n        const vastErrorCode = errorPayload.vastErrorCode;\n        const message = errorPayload.message || errorPayload.errorMessage || \"Unknown error\";\n        const cause = errorPayload.cause || errorPayload.innerError || errorPayload.error;\n\n        errorMessage = `Ad error: AdError ${errorCode}: ${message}`;\n\n        if (vastErrorCode && vastErrorCode !== \"N/A\" && vastErrorCode !== errorCode) {\n          errorMessage += ` (VAST Error Code: ${vastErrorCode})`;\n        }\n\n        if (cause) {\n          const causeMessage = typeof cause === \"string\" ? cause : (cause.message || String(cause));\n          errorMessage += `. Caused by: ${causeMessage}`;\n        }\n      }\n\n      console.error(\"[AD-ERROR]\", errorMessage, errorPayload || \"\");\n      this.handleAdFailure();\n    });\n    adPlayer.on(\"content_pause\", () => {\n      this.timing.clearAdFailsafeTimer();\n      this.timing.clearAdRequestWatchdog();\n      this.timing.activeAdRequestToken = null;\n      this.showAds = true;\n      this.timing.resetGamNoFillCounter();\n\n      if (this.inAdBreak && this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n        this.timing.scheduleAdStopAtBreakBoundary();\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Starting ad break timer on content_pause (first ad starting)\");\n        }\n      }\n\n      this.host.getAdPlayer().setAdVolume(getAdAudioVolume(this.host.getAdPlayer()));\n\n      if (this.isShowingPlaceholder) {\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Hiding placeholder - new ads starting\");\n        }\n        this.host.getAdPlayer().hidePlaceholder();\n        this.isShowingPlaceholder = false;\n      }\n    });\n    adPlayer.on(\"content_resume\", () => {\n      sendAdImpressionTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", adIndex: this.timing.currentAdIndex, timestamp: new Date().toISOString() },\n        this.analyticsContext\n      ).catch(() => {});\n\n      if (!this.host.video.muted) {\n        this.host.video.muted = true;\n        if (this.debug) {\n          console.log(\"[StormcloudVideoPlayer] Muted video on content_resume (ad ended, gap between ads)\");\n        }\n      }\n\n      this.timing.clearAdFailsafeTimer();\n      this.timing.clearAdRequestWatchdog();\n      this.timing.activeAdRequestToken = null;\n      this.showAds = false;\n\n      if (!this.inAdBreak) {\n        return;\n      }\n\n      this.timing.consecutiveFailures = 0;\n\n      this.timing.recordPlayedAdDuration(this.host.getAdPlayer());\n      const remaining = this.timing.getRemainingAdMs();\n\n      if (this.debug) {\n        console.log(\n          `[CONTINUOUS-FETCH] content_resume event: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}`,\n          this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n        );\n      }\n\n      if (this.timing.hasTimeToStartAnotherAd()) {\n        this.tryNextAvailableAdWithRateLimit();\n      } else {\n        this.handleAdPodComplete();\n      }\n    });\n  }\n\n  recreateAdController(): void {\n    if (this.debug) {\n      console.log(\"[StormcloudVideoPlayer] Recreating AdController for new ad\");\n    }\n\n    const shouldShowPlaceholder = this.inAdBreak && this.showAds;\n    const adPlayer = this.host.getAdPlayer();\n    if (shouldShowPlaceholder && adPlayer) {\n      if (this.debug) {\n        console.log(\"[StormcloudVideoPlayer] Showing placeholder before destroying old AdController\");\n      }\n      this.placeholder.show();\n      adPlayer.showPlaceholder();\n      this.isShowingPlaceholder = true;\n    }\n\n    const preservedMutedState = adPlayer.getOriginalMutedState();\n    const preservedVolumeState = adPlayer.getOriginalVolume();\n\n    if (adPlayer) {\n      try {\n        adPlayer.destroy();\n        this.host.video.muted = true;\n      } catch (error) {\n        if (this.debug) {\n          console.warn(\"[StormcloudVideoPlayer] Error destroying old AdController:\", error);\n        }\n      }\n    }\n\n    const continueLiveStreamDuringAds = this.host.shouldContinueLiveStreamDuringAds();\n    const newAdPlayer = this.host.createAdPlayer(continueLiveStreamDuringAds);\n    this.host.setAdPlayer(newAdPlayer);\n\n    newAdPlayer.initialize();\n    newAdPlayer.updateOriginalMutedState(preservedMutedState, preservedVolumeState);\n    this.attachImaEventListeners();\n\n    if (shouldShowPlaceholder) {\n      this.placeholder.show();\n      newAdPlayer.showPlaceholder();\n      this.isShowingPlaceholder = true;\n    }\n  }\n\n  clearPendingAdBreak(): void {\n    if (this.prefetchTimerId != null) {\n      clearTimeout(this.prefetchTimerId);\n      this.prefetchTimerId = undefined;\n    }\n    this.pendingAdBreak = null;\n    this.cueManager.pendingScte35CueKey = undefined;\n    this.adConfig.podAssignedByPrefetch = false;\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Ad break start\n  // ───────────────────────────────────────────────────────────────\n\n  async handleAdStart(_marker: Scte35Marker): Promise<void> {\n    const scheduled = this.adConfig.findCurrentOrNextBreak(\n      this.host.video.currentTime * 1000\n    );\n    if (scheduled) {\n      this.adConfig.consumedVmapBreakIds.add(this.adConfig.getAdBreakKey(scheduled));\n    }\n    const tags = this.adConfig.selectVastTagsForBreak(scheduled);\n\n    let baseVastUrl: string;\n\n    if (tags && tags.length > 0 && tags[0]) {\n      baseVastUrl = tags[0];\n    } else if (this.adConfig.apiVastTagUrl) {\n      baseVastUrl = this.adConfig.apiVastTagUrl;\n    } else {\n      this.clearPendingAdBreak();\n      return;\n    }\n\n    const adBreakDurationMs =\n      _marker.durationSeconds != null\n        ? _marker.durationSeconds * 1000\n        : scheduled?.durationMs;\n\n    if (this.debug) {\n      const mode = this.host.config.isLiveStream ? \"LIVE\" : \"VOD\";\n      console.log(\n        `[CONTINUOUS-FETCH] ${mode} MODE: Target duration=${adBreakDurationMs}ms`\n      );\n    }\n\n    this.timing.failedVastUrls.clear();\n    this.timing.temporaryFailureUrls.clear();\n    this.timing.consecutiveFailures = 0;\n    this.continuousFetchingActive = true;\n    this.isShowingPlaceholder = false;\n    this.timing.totalAdRequestsInBreak = 0;\n\n    if (this.adConfig.podAssignedByPrefetch) {\n      this.adConfig.podAssignedByPrefetch = false;\n    } else {\n      this.adConfig.beginNewAdPod();\n    }\n\n    const currentMuted = this.host.video.muted;\n    const currentVolume = this.host.video.volume;\n    this.host.getAdPlayer().updateOriginalMutedState(currentMuted, currentVolume);\n\n    this.inAdBreak = true;\n    this.timing.adBreakPlayedDurationMs = 0;\n    this.timing.currentAdIndex = 0;\n    this.timing.totalAdsInBreak = 0;\n    this.adPodQueue = [];\n    this.showAds = false;\n\n    if (\n      this.timing.expectedAdBreakDurationMs == null &&\n      adBreakDurationMs != null\n    ) {\n      this.timing.expectedAdBreakDurationMs = adBreakDurationMs;\n    }\n\n    if (this.optimizedPodsEnabled) {\n      const podPlayed = await this.startOptimizedPod(\n        baseVastUrl,\n        adBreakDurationMs,\n        currentMuted,\n        currentVolume\n      );\n      if (podPlayed) {\n        return;\n      }\n      if (this.debug) {\n        console.log(\"[POD] Optimized pod unavailable, falling back to sequential single-ad path\");\n      }\n    }\n\n    let firstAdUrl: string | undefined;\n    let usePreloadedAd = false;\n    let preloadedController: AdController | undefined;\n\n    const preloaded = this.preloadPool.getPreloadedAd();\n    if (preloaded) {\n      firstAdUrl = preloaded.vastUrl;\n      preloadedController = preloaded.adController;\n      usePreloadedAd = true;\n\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: firstAdUrl, timestamp: new Date().toISOString() },\n        this.analyticsContext\n      ).catch(() => {});\n      if (this.debug) {\n        console.log(\"[CONTINUOUS-FETCH] Using preloaded ad from pool (preloaded in advance, ready immediately!)\");\n      }\n    } else if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {\n      this.adRequestQueue = [...this.pendingAdBreak.vastUrls];\n      firstAdUrl = this.adRequestQueue.shift();\n\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Pool empty, using prefetched URLs (${this.adRequestQueue.length + 1} available)`);\n      }\n\n      this.clearPendingAdBreak();\n    } else {\n      if (this.debug) {\n        console.log(\"[CONTINUOUS-FETCH] Generating new ad URLs...\");\n      }\n\n      const firstAdUrlArray = this.host.generateVastUrls(baseVastUrl, 1);\n      firstAdUrl = firstAdUrlArray[0];\n      this.adRequestQueue = [];\n    }\n\n    if (!firstAdUrl) {\n      if (this.debug) {\n        console.warn(\"[CONTINUOUS-FETCH] Failed to get first ad URL\");\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (this.debug) {\n      console.log(\"[CONTINUOUS-FETCH] First VAST URL:\", firstAdUrl);\n    }\n\n    try {\n      if (usePreloadedAd && preloadedController) {\n        if (!this.timing.ensureLoadedAdFitsBudget(preloadedController)) {\n          this.timing.rejectLoadedAdForDuration(preloadedController);\n          this.startContinuousFetching(baseVastUrl);\n          await this.tryNextAvailableAdWithRateLimit();\n          return;\n        }\n\n        const adPlayer = this.host.getAdPlayer();\n        const preservedMuted = adPlayer.getOriginalMutedState();\n        const preservedVolume = adPlayer.getOriginalVolume();\n        adPlayer.destroy();\n        this.host.video.muted = true;\n        this.host.setAdPlayer(preloadedController);\n        preloadedController.updateOriginalMutedState(preservedMuted, preservedVolume);\n\n        this.attachImaEventListeners();\n\n        if (this.debug) {\n          console.log(\"[CONTINUOUS-FETCH] Using preloaded ad, resuming and starting playback immediately (no request delay!)\");\n        }\n\n        preloadedController.resume();\n\n        this.timing.consecutiveFailures = 0;\n        this.startContinuousFetching(baseVastUrl);\n\n        if (!this.preloadPool.active) {\n          this.preloadPool.active = true;\n        }\n        if (!this.preloadPool.isLoopRunning) {\n          this.preloadPool.preloadPoolLoop(baseVastUrl);\n        }\n\n        await preloadedController.play();\n        this.timing.markAdStarted();\n\n        if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n          this.timing.scheduleAdStopAtBreakBoundary();\n        }\n\n        const adVolume = currentMuted ? 0 : currentVolume;\n        preloadedController.setAdVolume(adVolume);\n      } else {\n        const adPlayer = this.host.getAdPlayer();\n        await this.timing.enforceGlobalRateLimit();\n        this.timing.lastAdRequestTime = Date.now();\n        await adPlayer.requestAds(firstAdUrl);\n        sendAdLoadedTracking(\n          this.host.config.licenseKey,\n          { source: \"hls\", vastUrl: firstAdUrl, timestamp: new Date().toISOString() },\n          this.analyticsContext\n        ).catch(() => {});\n\n        if (this.debug) {\n          console.log(\"[CONTINUOUS-FETCH] First ad request successful, starting playback\");\n        }\n\n        if (!this.timing.ensureLoadedAdFitsBudget(adPlayer)) {\n          this.timing.rejectLoadedAdForDuration(adPlayer);\n          this.recreateAdController();\n          this.startContinuousFetching(baseVastUrl);\n          await this.tryNextAvailableAdWithRateLimit();\n          return;\n        }\n\n        this.timing.consecutiveFailures = 0;\n        this.startContinuousFetching(baseVastUrl);\n\n        if (!this.preloadPool.active) {\n          this.preloadPool.startPreloadPool(baseVastUrl, []);\n        }\n\n        await adPlayer.play();\n        this.timing.markAdStarted();\n\n        if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n          this.timing.scheduleAdStopAtBreakBoundary();\n        }\n\n        const adVolume = currentMuted ? 0 : currentVolume;\n        adPlayer.setAdVolume(adVolume);\n      }\n    } catch (error) {\n      if (this.debug) {\n        console.warn(\"[CONTINUOUS-FETCH] First ad request failed:\", error);\n      }\n\n      if (!usePreloadedAd) {\n        const fallbackPreloaded = this.preloadPool.getPreloadedAd();\n        if (fallbackPreloaded) {\n          if (this.debug) {\n            console.log(\"[CONTINUOUS-FETCH] First ad failed, using preloaded fallback\");\n          }\n\n          try {\n            if (!this.timing.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) {\n              this.timing.rejectLoadedAdForDuration(fallbackPreloaded.adController);\n              this.startContinuousFetching(baseVastUrl);\n              await this.tryNextAvailableAdWithRateLimit();\n              return;\n            }\n\n            this.swapToPreloadedAdPlayer(fallbackPreloaded.adController);\n\n            this.timing.consecutiveFailures = 0;\n            this.startContinuousFetching(baseVastUrl);\n\n            if (!this.preloadPool.active) {\n              this.preloadPool.active = true;\n              this.preloadPool.preloadPoolLoop(baseVastUrl);\n            }\n\n            const ap = this.host.getAdPlayer();\n            await ap.play();\n            this.timing.markAdStarted();\n\n            if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n              this.timing.scheduleAdStopAtBreakBoundary();\n            }\n\n            const adVolume = currentMuted ? 0 : currentVolume;\n            ap.setAdVolume(adVolume);\n\n            return;\n          } catch (fallbackError) {\n            if (this.debug) {\n              console.warn(\"[CONTINUOUS-FETCH] Preloaded fallback also failed:\", fallbackError);\n            }\n          }\n        }\n      }\n\n      if (this.timing.isTemporaryAdError(error as Error)) {\n        this.timing.temporaryFailureUrls.set(firstAdUrl, Date.now());\n        if (this.debug) {\n          console.log(\"[CONTINUOUS-FETCH] First ad had temporary error (no-fill) - will try alternatives\");\n        }\n      } else {\n        this.timing.failedVastUrls.add(firstAdUrl);\n        if (this.debug) {\n          console.log(\"[CONTINUOUS-FETCH] First ad had permanent error - URL blacklisted\");\n        }\n      }\n      this.timing.consecutiveFailures++;\n\n      this.startContinuousFetching(baseVastUrl);\n      await this.tryNextAvailableAdWithRateLimit();\n    }\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Optimized pod (single multi-ad request)\n  // ───────────────────────────────────────────────────────────────\n\n  private async startOptimizedPod(\n    baseVastUrl: string,\n    breakDurationMs: number | undefined,\n    currentMuted: boolean,\n    currentVolume: number\n  ): Promise<boolean> {\n    let podUrl: string | undefined;\n    if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {\n      podUrl = this.pendingAdBreak.vastUrls[0];\n      this.clearPendingAdBreak();\n    } else {\n      podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);\n    }\n\n    if (!podUrl) {\n      return false;\n    }\n\n    this.continuousFetchingActive = false;\n\n    this.recreateAdController();\n\n    const adPlayer = this.host.getAdPlayer();\n    const requestToken = ++this.timing.adRequestTokenCounter;\n    this.timing.activeAdRequestToken = requestToken;\n    this.timing.startAdRequestWatchdog(requestToken);\n\n    try {\n      await this.timing.enforceGlobalRateLimit();\n      this.timing.lastAdRequestTime = Date.now();\n      await adPlayer.requestAds(podUrl);\n      this.timing.clearAdRequestWatchdog();\n\n      if (this.timing.activeAdRequestToken !== requestToken) {\n        return true;\n      }\n\n      const podCount = adPlayer.getPodAdCount?.() ?? 1;\n      if (podCount < 1) {\n        return false;\n      }\n\n      if (this.debug) {\n        console.log(\n          podCount === 1\n            ? \"[POD] Response contained a single ad; playing it and enabling sequential top-up\"\n            : `[POD] Optimized pod returned ${podCount} ads; playing pod in one controller`\n        );\n      }\n\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: podUrl, timestamp: new Date().toISOString() },\n        this.analyticsContext\n      ).catch(() => {});\n\n      this.timing.startAdFailsafeTimer(requestToken);\n      await adPlayer.play();\n      this.timing.markAdStarted();\n\n      if (\n        this.timing.expectedAdBreakDurationMs != null &&\n        this.timing.getAdBreakEndWallClockMs() == null\n      ) {\n        this.timing.scheduleAdStopAtBreakBoundary();\n      }\n\n      adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);\n      this.timing.consecutiveFailures = 0;\n\n      if (podCount === 1) {\n        this.continuousFetchingActive = true;\n        this.startContinuousFetching(baseVastUrl);\n        if (!this.preloadPool.active) {\n          this.preloadPool.startPreloadPool(baseVastUrl, []);\n        }\n      }\n\n      return true;\n    } catch (error) {\n      this.timing.clearAdRequestWatchdog();\n      this.timing.clearAdFailsafeTimer();\n      if (this.timing.activeAdRequestToken === requestToken) {\n        this.timing.activeAdRequestToken = null;\n      }\n      if (this.debug) {\n        console.warn(\"[POD] Optimized pod request/play failed:\", error);\n      }\n\n      if (this.timing.isTemporaryAdError(error as Error)) {\n        this.timing.temporaryFailureUrls.set(podUrl, Date.now());\n      }\n\n      this.continuousFetchingActive = true;\n      this.recreateAdController();\n      return false;\n    }\n  }\n\n  private swapToPreloadedAdPlayer(preloadedController: AdController): void {\n    const adPlayer = this.host.getAdPlayer();\n    const preservedMuted = adPlayer.getOriginalMutedState();\n    const preservedVolume = adPlayer.getOriginalVolume();\n    adPlayer.destroy();\n    this.host.video.muted = true;\n    this.host.setAdPlayer(preloadedController);\n    this.attachImaEventListeners();\n    preloadedController.updateOriginalMutedState(preservedMuted, preservedVolume);\n    preloadedController.resume();\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Continuous fetching\n  // ───────────────────────────────────────────────────────────────\n\n  private startContinuousFetching(baseVastUrl: string): void {\n    if (!this.continuousFetchingActive) {\n      return;\n    }\n\n    if (this.continuousFetchLoopRunning) {\n      if (this.debug) {\n        console.log(\"[CONTINUOUS-FETCH] Fetch loop already running, skipping duplicate start\");\n      }\n      return;\n    }\n\n    if (this.debug) {\n      console.log(\"[CONTINUOUS-FETCH] Starting continuous ad fetching loop\");\n    }\n\n    this.continuousFetchLoopRunning = true;\n    this.continuousFetchLoop(baseVastUrl);\n  }\n\n  private async continuousFetchLoop(baseVastUrl: string): Promise<void> {\n    const epoch = this.timing.adBreakEpoch;\n    while (\n      this.continuousFetchingActive &&\n      this.inAdBreak &&\n      epoch === this.timing.adBreakEpoch\n    ) {\n      const remaining = Math.min(\n        this.timing.getWallClockRemainingAdMs(),\n        this.timing.getDurationBudgetRemainingMs()\n      );\n\n      if (remaining <= this.timing.getMinRemainingMsToStartAd()) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Not enough ad break time left (wall=${this.timing.getWallClockRemainingAdMs()}ms, budget=${this.timing.getDurationBudgetRemainingMs()}ms), stopping URL generation`);\n        }\n        break;\n      }\n\n      if (this.timing.isGamInCooldown()) {\n        if (this.debug) {\n          console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, stopping URL generation\");\n        }\n        break;\n      }\n\n      if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), stopping URL generation`);\n        }\n        break;\n      }\n\n      if (this.timing.totalAdRequestsInBreak >= this.timing.maxTotalAdRequestsPerBreak) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] Maximum ad requests reached (${this.timing.maxTotalAdRequestsPerBreak}), stopping`);\n        }\n        break;\n      }\n\n      const maxQueueSize = 5;\n      if (this.adRequestQueue.length >= maxQueueSize) {\n        if (this.debug) {\n          console.log(`[CONTINUOUS-FETCH] URL queue full (${this.adRequestQueue.length}), waiting...`);\n        }\n        await new Promise(resolve => setTimeout(resolve, 1000));\n        continue;\n      }\n\n      const newAdUrl = this.host.generateVastUrls(baseVastUrl, 1)[0];\n\n      if (!newAdUrl || this.timing.failedVastUrls.has(newAdUrl) || this.timing.isUrlInCooldown(newAdUrl)) {\n        await new Promise(resolve => setTimeout(resolve, 500));\n        continue;\n      }\n\n      if (this.debug) {\n        const queuedUrlsPreview = [...this.adRequestQueue, newAdUrl];\n        console.log(\n          `[CONTINUOUS-FETCH] Queued VAST URL (queue size: ${queuedUrlsPreview.length})`,\n          { queuedUrls: queuedUrlsPreview }\n        );\n      }\n\n      this.adRequestQueue.push(newAdUrl);\n\n      const generationDelay = this.timing.consecutiveFailures > 0\n        ? Math.min(1000 * Math.pow(2, this.timing.consecutiveFailures), 5000)\n        : 500;\n      await new Promise(resolve => setTimeout(resolve, generationDelay));\n    }\n\n    this.continuousFetchLoopRunning = false;\n\n    if (this.debug) {\n      console.log(\n        `[CONTINUOUS-FETCH] URL generation loop ended (queued: ${this.adRequestQueue.length})`,\n        this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n      );\n    }\n  }\n\n  stopContinuousFetching(): void {\n    this.continuousFetchingActive = false;\n    this.continuousFetchLoopRunning = false;\n\n    this.placeholder.hide();\n\n    if (this.debug) {\n      console.log(\"[CONTINUOUS-FETCH] Stopping continuous ad fetching\");\n    }\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Ad iteration\n  // ───────────────────────────────────────────────────────────────\n\n  async tryNextAvailableAdWithRateLimit(): Promise<void> {\n    if (!this.inAdBreak) {\n      return;\n    }\n\n    const epoch = this.timing.adBreakEpoch;\n\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), ending ad break gracefully`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n\n    const backoffMultiplier = Math.pow(2, this.timing.consecutiveFailures);\n    const backoffDelay = Math.min(this.timing.backoffBaseMs * backoffMultiplier, this.timing.maxBackoffMs);\n    const effectiveMinInterval = this.timing.minAdRequestIntervalMs + (this.timing.consecutiveFailures > 0 ? backoffDelay : 0);\n\n    const timeSinceLastRequest = Date.now() - this.timing.lastAdRequestTime;\n    if (timeSinceLastRequest < effectiveMinInterval) {\n      const waitTime = effectiveMinInterval - timeSinceLastRequest;\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Rate limiting: waiting ${waitTime}ms before next request (backoff: ${this.timing.consecutiveFailures} failures)`);\n      }\n      await new Promise(resolve => setTimeout(resolve, waitTime));\n    }\n\n    if (!this.inAdBreak || epoch !== this.timing.adBreakEpoch) {\n      return;\n    }\n\n    return this.tryNextAvailableAd(0);\n  }\n\n  private async tryNextAvailableAd(retryCount: number = 0): Promise<void> {\n    if (!this.inAdBreak) {\n      return;\n    }\n\n    const remaining = this.timing.getRemainingAdMs();\n\n    if (!this.timing.hasTimeToStartAnotherAd()) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Not enough time remaining (${remaining}ms) to start another ad, ending ad break`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) {\n        console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, ending ad break gracefully\");\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Too many consecutive failures (${this.timing.consecutiveFailures}), ending ad break`);\n      }\n      this.handleAdPodComplete();\n      return;\n    }\n\n    this.timing.cleanupTemporaryFailures();\n\n    const preloaded = this.preloadPool.getPreloadedAd();\n    if (preloaded) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Using preloaded ad from pool`);\n      }\n\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: preloaded.vastUrl, timestamp: new Date().toISOString() },\n        this.analyticsContext\n      ).catch(() => {});\n\n      try {\n        if (!this.timing.ensureLoadedAdFitsBudget(preloaded.adController)) {\n          this.timing.rejectLoadedAdForDuration(preloaded.adController);\n          await this.tryNextAvailableAdWithRateLimit();\n          return;\n        }\n\n        this.swapToPreloadedAdPlayer(preloaded.adController);\n\n        const ap = this.host.getAdPlayer();\n        await ap.play();\n        this.timing.markAdStarted();\n\n        if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n          this.timing.scheduleAdStopAtBreakBoundary();\n        }\n\n        ap.setAdVolume(getAdAudioVolume(ap));\n\n        this.timing.consecutiveFailures = 0;\n        return;\n      } catch (error) {\n        if (this.debug) {\n          console.warn(\"[CONTINUOUS-FETCH] Preloaded ad failed to play:\", error);\n        }\n      }\n    }\n\n    while (this.adRequestQueue.length > 0) {\n      const nextAdUrl = this.adRequestQueue.shift();\n\n      if (!nextAdUrl) continue;\n\n      if (this.timing.failedVastUrls.has(nextAdUrl)) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping permanently-failed URL, trying next...\");\n        continue;\n      }\n\n      if (this.timing.isUrlInCooldown(nextAdUrl)) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping URL in cooldown (recent no-fill), trying next...\");\n        continue;\n      }\n\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Requesting next ad via HLS ad player (${this.timing.currentAdIndex + 1}, ${this.adRequestQueue.length} remaining in queue)`);\n      }\n\n      try {\n        await this.playSingleAd(nextAdUrl);\n        this.timing.consecutiveFailures = 0;\n      } catch (error) {\n        const errorMessage = (error as Error).message;\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Ad request failed:\", errorMessage);\n\n        if (errorMessage.includes(\"ad_duration_exceeds_budget\")) {\n          await this.tryNextAvailableAdWithRateLimit();\n          return;\n        }\n\n        if (!errorMessage.includes(\"permanently failed\") && !errorMessage.includes(\"in cooldown\")) {\n          this.timing.consecutiveFailures++;\n        }\n\n        await this.tryNextAvailableAdWithRateLimit();\n      }\n      return;\n    }\n\n    const maxRetries = 3;\n    if (this.continuousFetchingActive && retryCount < maxRetries && remaining > 2000) {\n      if (this.debug) {\n        console.log(`[CONTINUOUS-FETCH] Queue empty, waiting for URLs... (${retryCount + 1}/${maxRetries})`);\n      }\n      await new Promise(resolve => setTimeout(resolve, 500));\n      await this.tryNextAvailableAd(retryCount + 1);\n      return;\n    }\n\n    if (!this.isShowingPlaceholder && remaining > 1000) {\n      this.showPlaceholderAndWaitForAds();\n    } else {\n      if (this.debug) console.log(\"[CONTINUOUS-FETCH] No more ads available, ending ad break\");\n      this.handleAdPodComplete();\n    }\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Single ad playback\n  // ───────────────────────────────────────────────────────────────\n\n  async playSingleAd(vastTagUrl: string): Promise<void> {\n    const adPlayer = this.host.getAdPlayer();\n    if (adPlayer.isAdPlaying()) return;\n\n    if (this.timing.failedVastUrls.has(vastTagUrl)) {\n      if (this.debug) console.warn(\"[AD-ERROR] Skipping permanently-failed VAST URL:\", vastTagUrl);\n      throw new Error(\"VAST URL permanently failed\");\n    }\n\n    if (this.timing.isUrlInCooldown(vastTagUrl)) {\n      if (this.debug) console.warn(\"[AD-ERROR] Skipping URL in cooldown (recent no-fill):\", vastTagUrl);\n      throw new Error(\"VAST URL in cooldown after no-fill\");\n    }\n\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.warn(\"[AD-ERROR] Too many consecutive failures, not requesting more ads\");\n      throw new Error(\"Too many consecutive failures\");\n    }\n\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) console.warn(\"[CIRCUIT-BREAKER] GAM in cooldown, skipping ad request\");\n      throw new Error(\"GAM in cooldown\");\n    }\n\n    this.recreateAdController();\n\n    const requestToken = ++this.timing.adRequestTokenCounter;\n    this.timing.activeAdRequestToken = requestToken;\n    this.timing.startAdRequestWatchdog(requestToken);\n\n    try {\n      await this.timing.enforceGlobalRateLimit();\n      this.timing.lastAdRequestTime = Date.now();\n      const ap = this.host.getAdPlayer();\n      await ap.requestAds(vastTagUrl);\n      sendAdLoadedTracking(\n        this.host.config.licenseKey,\n        { source: \"hls\", vastUrl: vastTagUrl, timestamp: new Date().toISOString() },\n        this.analyticsContext\n      ).catch(() => {});\n\n      this.timing.clearAdRequestWatchdog();\n\n      if (this.timing.activeAdRequestToken !== requestToken) return;\n\n      if (!this.timing.ensureLoadedAdFitsBudget(ap)) {\n        this.timing.rejectLoadedAdForDuration(ap);\n        this.recreateAdController();\n        throw new Error(\"ad_duration_exceeds_budget\");\n      }\n\n      try {\n        this.timing.startAdFailsafeTimer(requestToken);\n        await ap.play();\n        this.timing.markAdStarted();\n\n        if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n          this.timing.scheduleAdStopAtBreakBoundary();\n        }\n\n        ap.setAdVolume(getAdAudioVolume(ap));\n        this.timing.consecutiveFailures = 0;\n        this.timing.temporaryFailureUrls.delete(vastTagUrl);\n      } catch (playError) {\n        if (this.debug) console.error(\"[AD-ERROR] Failed to play ad:\", playError);\n\n        const preloadedFallback = this.preloadPool.getPreloadedAd();\n        if (preloadedFallback) {\n          if (this.debug) console.log(\"[AD-ERROR] Play failed, trying preloaded fallback ad\");\n          try {\n            if (!this.timing.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {\n              this.timing.rejectLoadedAdForDuration(preloadedFallback.adController);\n              throw playError;\n            }\n            this.timing.clearAdFailsafeTimer();\n            this.swapToPreloadedAdPlayer(preloadedFallback.adController);\n            this.timing.consecutiveFailures = 0;\n            const fbAp = this.host.getAdPlayer();\n            await fbAp.play();\n            this.timing.markAdStarted();\n            if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {\n              this.timing.scheduleAdStopAtBreakBoundary();\n            }\n            fbAp.setAdVolume(getAdAudioVolume(fbAp));\n            return;\n          } catch (fallbackError) {\n            if (this.debug) console.warn(\"[AD-ERROR] Preloaded fallback also failed:\", fallbackError);\n          }\n        }\n\n        if (this.timing.isTemporaryAdError(playError as Error)) {\n          this.timing.temporaryFailureUrls.set(vastTagUrl, Date.now());\n          if (this.debug) console.log(\"[AD-ERROR] Temporary play error - URL can be retried after cooldown\");\n        } else {\n          this.timing.failedVastUrls.add(vastTagUrl);\n          if (this.debug) console.log(\"[AD-ERROR] Permanent play error - URL blacklisted\");\n        }\n\n        this.timing.clearAdFailsafeTimer();\n        if (this.timing.activeAdRequestToken === requestToken) {\n          this.timing.activeAdRequestToken = null;\n        }\n        throw playError;\n      }\n    } catch (error) {\n      const errorMessage = (error as Error)?.message || \"\";\n      if (this.debug) console.error(\"[AD-ERROR] Ad request failed:\", errorMessage);\n\n      const preloadedFallback = this.preloadPool.getPreloadedAd();\n      if (preloadedFallback) {\n        if (this.debug) console.log(\"[AD-ERROR] Request failed, trying preloaded fallback ad\");\n        try {\n          if (!this.timing.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {\n            this.timing.rejectLoadedAdForDuration(preloadedFallback.adController);\n            throw error;\n          }\n          this.timing.clearAdRequestWatchdog();\n          this.timing.clearAdFailsafeTimer();\n          this.swapToPreloadedAdPlayer(preloadedFallback.adController);\n          this.timing.consecutiveFailures = 0;\n          const fbAp = this.host.getAdPlayer();\n          await fbAp.play();\n          this.timing.markAdStarted();\n          fbAp.setAdVolume(getAdAudioVolume(fbAp));\n          return;\n        } catch (fallbackError) {\n          if (this.debug) console.warn(\"[AD-ERROR] Preloaded fallback also failed:\", fallbackError);\n        }\n      }\n\n      if (this.timing.isTemporaryAdError(error as Error)) {\n        this.timing.temporaryFailureUrls.set(vastTagUrl, Date.now());\n        if (this.debug) console.log(\"[AD-ERROR] Temporary error (no-fill/timeout) - URL can be retried after cooldown\");\n      } else {\n        this.timing.failedVastUrls.add(vastTagUrl);\n        if (this.debug) console.log(\"[AD-ERROR] Permanent error - URL blacklisted:\", errorMessage);\n      }\n\n      this.timing.clearAdRequestWatchdog();\n      this.timing.clearAdFailsafeTimer();\n      if (this.timing.activeAdRequestToken === requestToken) {\n        this.timing.activeAdRequestToken = null;\n      }\n      throw error;\n    }\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Placeholder wait\n  // ───────────────────────────────────────────────────────────────\n\n  private async showPlaceholderAndWaitForAds(): Promise<void> {\n    const remaining = this.timing.getRemainingAdMs();\n    const waitTime = Math.min(this.maxPlaceholderDurationMs, remaining);\n\n    if (this.timing.isGamInCooldown()) {\n      if (this.debug) console.log(\"[CIRCUIT-BREAKER] GAM in cooldown, skipping placeholder wait\");\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.log(\"[CONTINUOUS-FETCH] Skipping placeholder - too many consecutive failures\");\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (waitTime < 1000) {\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (this.debug) {\n      console.log(`[CONTINUOUS-FETCH] Showing placeholder for ${waitTime}ms while waiting for ad URLs`);\n    }\n\n    this.isShowingPlaceholder = true;\n    this.host.getAdPlayer().showPlaceholder();\n\n    const checkInterval = 300;\n    const maxChecks = Math.floor(waitTime / checkInterval);\n\n    for (let i = 0; i < maxChecks; i++) {\n      await new Promise(resolve => setTimeout(resolve, checkInterval));\n\n      if (!this.inAdBreak) return;\n\n      if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Too many failures during placeholder wait\");\n        break;\n      }\n\n      if (!this.timing.hasTimeToStartAnotherAd()) {\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Not enough time remaining during placeholder wait, ending ad break\");\n        break;\n      }\n\n      while (this.adRequestQueue.length > 0) {\n        if (!this.timing.hasTimeToStartAnotherAd()) break;\n\n        const nextAdUrl = this.adRequestQueue.shift();\n        if (!nextAdUrl) continue;\n        if (this.timing.failedVastUrls.has(nextAdUrl)) continue;\n        if (this.timing.isUrlInCooldown(nextAdUrl)) continue;\n\n        if (this.debug) console.log(\"[CONTINUOUS-FETCH] Ad URL available, requesting via HLS ad player\");\n\n        this.isShowingPlaceholder = false;\n        this.host.getAdPlayer().hidePlaceholder();\n\n        try {\n          await this.playSingleAd(nextAdUrl);\n          this.timing.consecutiveFailures = 0;\n        } catch (error) {\n          const errorMessage = (error as Error).message;\n          if (errorMessage.includes(\"ad_duration_exceeds_budget\")) {\n            await this.tryNextAvailableAdWithRateLimit();\n            return;\n          }\n          if (!errorMessage.includes(\"permanently failed\") && !errorMessage.includes(\"in cooldown\")) {\n            this.timing.consecutiveFailures++;\n          }\n          await this.tryNextAvailableAdWithRateLimit();\n        }\n        return;\n      }\n    }\n\n    if (this.debug) console.log(\"[CONTINUOUS-FETCH] Placeholder timeout, ending ad break\");\n\n    this.isShowingPlaceholder = false;\n    this.host.getAdPlayer().hidePlaceholder();\n    this.handleAdPodComplete();\n  }\n\n  // ───────────────────────────────────────────────────────────────\n  //  Ad break completion\n  // ───────────────────────────────────────────────────────────────\n\n  handleAdPodComplete(): void {\n    if (this.debug) {\n      console.log(\"[StormcloudVideoPlayer] Ad pod complete - cleaning up\");\n    }\n\n    this.timing.resetForBreakEnd();\n\n    this.stopContinuousFetching();\n    this.preloadPool.stop();\n    this.clearPendingAdBreak();\n\n    if (this.isShowingPlaceholder) {\n      this.host.getAdPlayer().hidePlaceholder();\n      this.isShowingPlaceholder = false;\n    }\n\n    this.adRequestQueue = [];\n\n    this.inAdBreak = false;\n    this.adDetectSentForCurrentBreak = false;\n    this.cueManager.activeScte35BreakKey = undefined;\n    this.cueManager.scheduledScte35BreakKey = undefined;\n    this.adPodQueue = [];\n    this.showAds = false;\n\n    this.host.getAdPlayer().stop().catch(() => {});\n\n    const restoredMuted = this.host.getAdPlayer().getOriginalMutedState();\n    const restoredVolume = this.host.getAdPlayer().getOriginalVolume();\n\n    if (this.host.video.muted !== restoredMuted) {\n      this.host.video.muted = restoredMuted;\n    }\n    if (Math.abs(this.host.video.volume - restoredVolume) > 0.01) {\n      this.host.video.volume = restoredVolume;\n    }\n\n    if (this.host.shouldContinueLiveStreamDuringAds()) {\n      const liveSyncPos = this.host.getLiveSyncPosition();\n      if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {\n        if (this.debug) {\n          console.log(\n            `[StormcloudVideoPlayer] Live break end — snapping to live edge: ${this.host.video.currentTime.toFixed(2)}s → ${liveSyncPos.toFixed(2)}s`\n          );\n        }\n        this.host.video.currentTime = liveSyncPos;\n      }\n\n      if (this.host.video.paused) {\n        if (this.debug) console.log(\"[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback\");\n        this.host.video.play()?.catch(() => {});\n      } else {\n        if (this.debug) console.log(\"[StormcloudVideoPlayer] Content video already playing in live mode after ads\");\n      }\n    } else if (this.host.video.paused) {\n      this.host.video.play()?.catch(() => {});\n    }\n  }\n\n  handleAdFailure(): void {\n    this.timing.consecutiveFailures++;\n    this.timing.recordGamNoFill();\n    const remaining = this.timing.getRemainingAdMs();\n\n    if (this.debug) {\n      console.log(\n        `[CONTINUOUS-FETCH] Ad failure: remaining=${remaining}ms, queued ads=${this.adRequestQueue.length}, consecutiveFailures=${this.timing.consecutiveFailures}`,\n        this.adRequestQueue.length > 0 ? { queuedUrls: [...this.adRequestQueue] } : {}\n      );\n    }\n\n    if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {\n      if (this.debug) console.log(`[CONTINUOUS-FETCH] Max consecutive failures reached (${this.timing.consecutiveFailures}), ending ad break gracefully`);\n      this.handleAdPodComplete();\n      return;\n    }\n\n    if (this.timing.hasTimeToStartAnotherAd()) {\n      this.tryNextAvailableAdWithRateLimit();\n    } else {\n      if (this.debug) console.log(\"[AD-ERROR] Ad failed and not enough time remaining for another ad. Failed URLs:\", this.timing.failedVastUrls.size);\n      this.handleAdPodComplete();\n    }\n  }\n}\n","export const MQTT_CA_CERT_FILE = \"src/certs/emqxsl-ca.crt\";\n\nexport type MQTTConfig = {\n  enabled: boolean;\n  brokerAddress: string;\n  brokerPort: number;\n  wsPort: number;\n  username: string;\n  password: string;\n  topicPrefix: string;\n  qos: 0 | 1 | 2;\n  brokerUrl?: string;\n};\n\nexport const DEFAULT_MQTT_CONFIG: MQTTConfig = {\n  enabled: true,\n  brokerAddress: \"vecbae77.ala.us-east-1.emqxsl.com\",\n  brokerPort: 8883,\n  wsPort: 8084,\n  username: \"for-sonifi\",\n  password: \"sonifi-mqtt\",\n  topicPrefix: \"adstorm/players\",\n  qos: 1,\n};\n\nexport const mqttConfig: MQTTConfig = { ...DEFAULT_MQTT_CONFIG };\n\nexport function applyMQTTConfig(overrides: Partial<MQTTConfig>): void {\n  Object.assign(mqttConfig, overrides);\n}\n\nexport function isMQTTEnabled(): boolean {\n  return mqttConfig.enabled;\n}\n\nexport function buildMQTTBrokerUrl(): string {\n  if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;\n  return `wss://${mqttConfig.brokerAddress}:${mqttConfig.wsPort}/mqtt`;\n}\n\nexport function buildPlayerTopic(\n  licenseKey: string,\n  channel: \"metrics\" | \"impressions\" | \"heartbeat\"\n): string {\n  return `${mqttConfig.topicPrefix}/${licenseKey}/${channel}`;\n}\n","import mqtt from \"mqtt\";\nimport type { MqttClient } from \"mqtt\";\nimport {\n  applyMQTTConfig,\n  buildMQTTBrokerUrl,\n  isMQTTEnabled,\n  mqttConfig,\n} from \"./mqttConfig\";\nimport type { MQTTConfig } from \"./mqttConfig\";\n\nconst LOG = \"[StormcloudVideoPlayer][MQTT]\";\n\nexport type MQTTStatus = \"disconnected\" | \"connecting\" | \"connected\" | \"error\";\n\nlet client: MqttClient | null = null;\nlet status: MQTTStatus = \"disconnected\";\n\nexport function getMQTTStatus(): MQTTStatus {\n  return status;\n}\n\nexport function isMQTTConnected(): boolean {\n  return status === \"connected\" && client !== null && client.connected;\n}\n\nexport function isMQTTConfigured(): boolean {\n  return isMQTTEnabled();\n}\n\nexport function configureMQTT(overrides: Partial<MQTTConfig>): void {\n  applyMQTTConfig(overrides);\n  disconnectMQTT();\n}\n\nexport function initMQTTClient(): void {\n  if (client || !isMQTTEnabled()) return;\n\n  const url = buildMQTTBrokerUrl();\n  status = \"connecting\";\n\n  const clientId = `stormcloud-vp-${Math.random().toString(36).slice(2, 9)}`;\n\n  try {\n    client = mqtt.connect(url, {\n      clientId,\n      username: mqttConfig.username,\n      password: mqttConfig.password,\n      keepalive: 60,\n      clean: true,\n      reconnectPeriod: 5000,\n      connectTimeout: 10_000,\n      queueQoSZero: false,\n    });\n  } catch (err) {\n    status = \"error\";\n    console.warn(`${LOG} connect() threw:`, err);\n    return;\n  }\n\n  client.on(\"connect\", () => {\n    status = \"connected\";\n    console.info(`${LOG} connected to ${url}`);\n  });\n\n  client.on(\"reconnect\", () => {\n    status = \"connecting\";\n    console.info(`${LOG} reconnecting…`);\n  });\n\n  client.on(\"offline\", () => {\n    status = \"disconnected\";\n    console.warn(`${LOG} offline`);\n  });\n\n  client.on(\"error\", (err) => {\n    status = \"error\";\n    console.warn(`${LOG} error:`, err.message);\n  });\n\n  client.on(\"close\", () => {\n    if (status === \"connected\") {\n      status = \"disconnected\";\n    }\n  });\n}\n\nexport function ensureMQTTClient(): void {\n  if (isMQTTEnabled() && !client) {\n    initMQTTClient();\n  }\n}\n\nexport function publishMQTT(\n  topic: string,\n  payload: Record<string, unknown>\n): boolean {\n  if (!isMQTTEnabled()) {\n    return false;\n  }\n\n  ensureMQTTClient();\n\n  if (!client) {\n    return false;\n  }\n\n  try {\n    client.publish(topic, JSON.stringify(payload), { qos: mqttConfig.qos });\n    return true;\n  } catch (err) {\n    console.warn(`${LOG} publish failed on ${topic}:`, err);\n    return false;\n  }\n}\n\nexport function disconnectMQTT(): void {\n  if (client) {\n    client.end(true);\n    client = null;\n    status = \"disconnected\";\n  }\n}\n","export interface VastEnvironmentSignals {\n  isCtv: boolean;\n  contentUrl?: string;\n  appId?: string;\n  appName?: string;\n  sessionId?: string;\n  deviceId?: string;\n  deviceIdType?: string;\n  limitAdTracking?: boolean;\n  deviceTypeHint?: number;\n}\n\nconst AIRY_ANDROID_APP_ID = \"com.freeairytv.android\";\nconst AIRY_WEBOS_APP_ID    = \"com.freeairytv.webos\";\nconst AIRY_TIZEN_APP_ID    = \"com.freeairytv.samsung\";\nconst AIRY_ROKU_APP_ID     = \"com.freeairytv.roku\";\nconst AIRY_APP_NAME = \"AiryTV Movies & TV\";\nconst AIRY_DEFAULT_CONTENT_URL = \"https://live.airy.tv\";\nconst CTV_SESSION_STORAGE_KEY = \"stormcloud.ctv.sid\";\nexport const PLAYER_ID_STORAGE_KEY = \"stormcloud.player.id\";\n\nconst LG_TEST_APP_ID      = \"com.hulu.plus\";\nconst LG_TEST_APP_NAME    = \"Hulu\";\nconst LG_TEST_CONTENT_URL = \"https://www.hulu.com/live-tv\";\n\nlet _lgTestDeviceId: string | undefined;\nfunction getLgTestDeviceId(): string {\n  if (!_lgTestDeviceId) _lgTestDeviceId = createUuid();\n  return _lgTestDeviceId;\n}\n\nfunction isLgWebOS(): boolean {\n  if (typeof navigator === \"undefined\") return false;\n  return /Web0S|webOS|LG Browser|LGSTB|LGE/i.test(navigator.userAgent);\n}\n\nfunction getDefaultAppId(): string {\n  if (typeof navigator === \"undefined\") return AIRY_ANDROID_APP_ID;\n  const ua = navigator.userAgent;\n  if (/Web0S|webOS|LG Browser|LGSTB|LGE/i.test(ua)) return AIRY_WEBOS_APP_ID;\n  if (/Tizen|Samsung/i.test(ua))                       return AIRY_TIZEN_APP_ID;\n  if (/Roku/i.test(ua))                                return AIRY_ROKU_APP_ID;\n  return AIRY_ANDROID_APP_ID;\n}\n\nconst DEVICE_ID_STORAGE_KEYS = [\n  \"rdid\",\n  \"ifa\",\n  \"advertisingId\",\n  \"advertising_id\",\n  \"deviceAdvertisingId\",\n  \"aaid\",\n  \"adid\",\n  \"rida\",\n  \"tifa\",\n  \"lgudid\",\n];\n\nconst ANDROID_ID_TYPES = new Set([\"aaid\", \"adid\"]);\n\nexport function resolveVastEnvironmentSignals(isCtv: boolean): VastEnvironmentSignals {\n  const bridgeSignals  = readNativeBridgeSignals();\n  const platformDevice = readPlatformNativeDeviceId();\n  const lg = isCtv && isLgWebOS();\n\n  const deviceId =\n    bridgeSignals.deviceId  ||\n    readStoredDeviceId()    ||\n    platformDevice.deviceId ||\n    (lg && platformDevice.limitAdTracking !== true ? getLgTestDeviceId() : undefined);\n  const deviceIdType =\n    bridgeSignals.deviceIdType  ||\n    readStoredString(\"deviceIdType\") ||\n    readStoredString(\"idtype\") ||\n    platformDevice.deviceIdType ||\n    inferDeviceIdType() ||\n    (lg ? \"lgudid\" : undefined);\n  const platformLat = bridgeSignals.deviceId\n    ? bridgeSignals.limitAdTracking\n    : platformDevice.limitAdTracking;\n  const limitAdTracking =\n    deviceId != null\n      ? platformLat ?? readStoredLimitAdTracking() ?? false\n      : undefined;\n\n  if (isCtv) {\n    return {\n      isCtv: true,\n      contentUrl:\n        bridgeSignals.contentUrl ||\n        readStoredString(\"contentUrl\") ||\n        (lg ? LG_TEST_CONTENT_URL : AIRY_DEFAULT_CONTENT_URL),\n      appId:\n        bridgeSignals.appId ||\n        readStoredString(\"appId\") ||\n        readStoredString(\"msid\") ||\n        (lg ? LG_TEST_APP_ID : getDefaultAppId()),\n      appName:\n        bridgeSignals.appName ||\n        readStoredString(\"appName\") ||\n        readStoredString(\"an\") ||\n        (lg ? LG_TEST_APP_NAME : AIRY_APP_NAME),\n      sessionId: getOrCreateCtvSessionId(),\n      ...(deviceId ? { deviceId } : {}),\n      ...(deviceIdType ? { deviceIdType } : {}),\n      ...(limitAdTracking != null ? { limitAdTracking } : {}),\n      deviceTypeHint: 5,\n    };\n  }\n\n  return {\n    isCtv: false,\n    ...(deviceId ? { deviceId } : {}),\n    ...(deviceIdType ? { deviceIdType } : {}),\n    ...(limitAdTracking != null ? { limitAdTracking } : {}),\n  };\n}\n\nexport function inferDeviceIdType(): string | undefined {\n  if (typeof navigator === \"undefined\") {\n    return undefined;\n  }\n\n  const ua = navigator.userAgent;\n  if (/Roku/i.test(ua)) return \"rida\";\n  if (/Tizen|Samsung/i.test(ua)) return \"tifa\";\n  if (/AppleTV|Apple TV/i.test(ua)) return \"tvOS\";\n  if (/AFT|Fire TV|Amazon/i.test(ua)) return \"afai\";\n  if (/Web0S|webOS|LG Browser|LGSTB|LGE/i.test(ua)) return \"lgudid\";\n  if (/Android|Google TV/i.test(ua)) return \"aaid\";\n  if (/iPhone|iPad|iPod/i.test(ua)) return \"idfa\";\n  return undefined;\n}\n\nexport function isEquivalentDeviceIdType(\n  tagType: string | undefined,\n  runtimeType: string | undefined\n): boolean {\n  if (!tagType || !runtimeType) return false;\n  const tag = tagType.toLowerCase();\n  const runtime = runtimeType.toLowerCase();\n  if (tag === runtime) return true;\n  return ANDROID_ID_TYPES.has(tag) && ANDROID_ID_TYPES.has(runtime);\n}\n\nexport function resolveNativeDeviceId(): string | undefined {\n  const bridgeSignals = readNativeBridgeSignals();\n  const platformDevice = readPlatformNativeDeviceId();\n  const deviceId =\n    bridgeSignals.deviceId ||\n    readStoredDeviceId() ||\n    platformDevice.deviceId;\n\n  if (!deviceId || isMacroPlaceholder(deviceId) || isZeroedAdId(deviceId)) {\n    return undefined;\n  }\n\n  return deviceId;\n}\n\nexport function getOrCreateStoredUuid(storageKey: string): string | undefined {\n  if (typeof window === \"undefined\") {\n    return undefined;\n  }\n\n  const existing = readStoredString(storageKey);\n  if (existing) {\n    return existing;\n  }\n\n  const id = createUuid();\n  try {\n    window.localStorage?.setItem(storageKey, id);\n  } catch {}\n  return id;\n}\n\nfunction getOrCreateCtvSessionId(): string {\n  const existing = readStoredString(CTV_SESSION_STORAGE_KEY);\n  if (existing) {\n    return existing;\n  }\n\n  const sessionId = createUuid();\n  try {\n    window.localStorage?.setItem(CTV_SESSION_STORAGE_KEY, sessionId);\n  } catch {}\n  return sessionId;\n}\n\nfunction createUuid(): string {\n  if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n    return crypto.randomUUID();\n  }\n\n  return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (char) => {\n    const value = Math.floor(Math.random() * 16);\n    const nibble = char === \"x\" ? value : (value & 0x3) | 0x8;\n    return nibble.toString(16);\n  });\n}\n\nfunction readPlatformNativeDeviceId(): {\n  deviceId?: string;\n  deviceIdType?: string;\n  limitAdTracking?: boolean;\n} {\n  if (typeof window === \"undefined\") return {};\n\n  try {\n    const adinfo = (window as any).webapis?.adinfo;\n    if (adinfo) {\n      const tifa: unknown =\n        typeof adinfo.getTIFA === \"function\" ? adinfo.getTIFA() : adinfo.tifa;\n      if (typeof tifa === \"string\" && tifa && !isMacroPlaceholder(tifa)) {\n        const lat: unknown =\n          typeof adinfo.isLATEnabled === \"function\"\n            ? adinfo.isLATEnabled()\n            : undefined;\n        return {\n          deviceId: tifa,\n          deviceIdType: \"tifa\",\n          ...(typeof lat === \"boolean\" ? { limitAdTracking: lat } : {}),\n        };\n      }\n    }\n  } catch {}\n\n  try {\n    const webOSDev = (window as any).webOSDev;\n    if (webOSDev) {\n      const lgudid: unknown =\n        typeof webOSDev.LGUDID === \"function\"\n          ? webOSDev.LGUDID()\n          : webOSDev.lgudid ?? webOSDev.LGUDID;\n      if (typeof lgudid === \"string\" && lgudid) {\n        if (isZeroedAdId(lgudid)) {\n          return { deviceIdType: \"lgudid\", limitAdTracking: true };\n        }\n        if (!isMacroPlaceholder(lgudid)) {\n          return { deviceId: lgudid, deviceIdType: \"lgudid\", limitAdTracking: false };\n        }\n      }\n    }\n  } catch {}\n\n  try {\n    const roku = (window as any).Roku;\n    if (roku) {\n      const rida: unknown =\n        typeof roku.getPublisherUniqueId === \"function\"\n          ? roku.getPublisherUniqueId()\n          : roku.rida;\n      if (typeof rida === \"string\" && rida && !isMacroPlaceholder(rida)) {\n        return { deviceId: rida, deviceIdType: \"rida\", limitAdTracking: false };\n      }\n    }\n  } catch {}\n\n  return {};\n}\n\nconst ZEROED_AD_ID_PATTERNS: ReadonlySet<string> = new Set([\n  \"38400000-8cf0-11bd-b23e-10b96e40000d\",\n  \"00000000-0000-0000-0000-000000000000\",\n]);\n\nfunction isMacroPlaceholder(value: string): boolean {\n  const t = value.trim();\n  return !t ||\n    /^(\\[[^\\]]*\\]|\\{[^}]*\\}|%%[^%]*%%)$/.test(t) ||\n    /^(unknown|null|undefined|none|n\\/a|\\$\\{[^}]+\\})$/i.test(t) ||\n    ZEROED_AD_ID_PATTERNS.has(t.toLowerCase());\n}\n\nfunction isZeroedAdId(value: string): boolean {\n  return ZEROED_AD_ID_PATTERNS.has(value.trim().toLowerCase());\n}\n\nfunction readNativeBridgeSignals(): {\n  contentUrl?: string;\n  appId?: string;\n  appName?: string;\n  deviceId?: string;\n  deviceIdType?: string;\n  limitAdTracking?: boolean;\n} {\n  if (typeof window === \"undefined\") {\n    return {};\n  }\n\n  const candidates = [\n    (window as any).__STORMCLOUD_CTV_AD_INFO__,\n    (window as any).__STORMCLOUD_CTV__,\n    (window as any).stormcloudCtv,\n    (window as any).AiryTV,\n    (window as any).Android,\n    (window as any).webOS,\n    (window as any).tizen,\n    (window as any).amazon,\n  ].filter(Boolean);\n\n  for (const source of candidates) {\n    const contentUrl =\n      readBridgeValue(source, [\"contentUrl\", \"url\", \"videoUrl\", \"canonicalUrl\"]) ||\n      undefined;\n    const appId =\n      readBridgeValue(source, [\"appId\", \"msid\", \"applicationId\", \"packageName\"]) ||\n      undefined;\n    const appName =\n      readBridgeValue(source, [\"appName\", \"an\", \"applicationName\"]) || undefined;\n    const deviceId =\n      readBridgeValue(source, [\n        \"rdid\",\n        \"ifa\",\n        \"advertisingId\",\n        \"adId\",\n        \"deviceId\",\n        \"lgudid\",\n        \"LGUDID\",\n        \"tifa\",\n        \"rida\",\n        \"afai\",\n        \"aaid\",\n      ]) || undefined;\n\n    if (!contentUrl && !deviceId && !appId && !appName) continue;\n\n    const deviceIdType =\n      readBridgeValue(source, [\n        \"idtype\",\n        \"deviceIdType\",\n        \"advertisingIdType\",\n        \"idType\",\n      ]) || inferDeviceIdType();\n    const limitAdTracking = readBridgeBoolean(source, [\n      \"is_lat\",\n      \"limitAdTracking\",\n      \"limitedAdTracking\",\n      \"lat\",\n    ]);\n\n    return {\n      ...(contentUrl ? { contentUrl } : {}),\n      ...(appId ? { appId } : {}),\n      ...(appName ? { appName } : {}),\n      ...(deviceId ? { deviceId } : {}),\n      ...(deviceId && deviceIdType ? { deviceIdType } : {}),\n      ...(deviceId && limitAdTracking != null ? { limitAdTracking } : {}),\n    };\n  }\n\n  return {};\n}\n\nfunction readBridgeValue(source: any, keys: string[]): string | undefined {\n  for (const key of keys) {\n    const value = source?.[key];\n    if (typeof value === \"string\" && value) {\n      return value;\n    }\n    if (typeof value === \"function\") {\n      try {\n        const result = value.call(source);\n        if (typeof result === \"string\" && result) {\n          return result;\n        }\n      } catch {}\n    }\n  }\n  return undefined;\n}\n\nfunction readBridgeBoolean(source: any, keys: string[]): boolean | undefined {\n  for (const key of keys) {\n    const value = source?.[key];\n    const normalized = normalizeBoolean(value);\n    if (normalized != null) {\n      return normalized;\n    }\n    if (typeof value === \"function\") {\n      try {\n        const result = normalizeBoolean(value.call(source));\n        if (result != null) {\n          return result;\n        }\n      } catch {}\n    }\n  }\n  return undefined;\n}\n\nfunction readStoredDeviceId(): string | undefined {\n  for (const key of DEVICE_ID_STORAGE_KEYS) {\n    const value = readStoredString(key);\n    if (value) {\n      return value;\n    }\n  }\n  return undefined;\n}\n\nfunction readStoredString(key: string): string | undefined {\n  if (typeof window === \"undefined\") {\n    return undefined;\n  }\n\n  try {\n    const value = window.localStorage?.getItem(key);\n    return value || undefined;\n  } catch {\n    return undefined;\n  }\n}\n\nfunction readStoredLimitAdTracking(): boolean | undefined {\n  return normalizeBoolean(\n    readStoredString(\"limitAdTracking\") ||\n      readStoredString(\"limitedAdTracking\") ||\n      readStoredString(\"is_lat\")\n  );\n}\n\nfunction normalizeBoolean(value: unknown): boolean | undefined {\n  if (typeof value === \"boolean\") {\n    return value;\n  }\n  if (typeof value === \"number\") {\n    return value === 1;\n  }\n  if (typeof value === \"string\") {\n    const normalized = value.toLowerCase();\n    if (normalized === \"1\" || normalized === \"true\" || normalized === \"yes\") {\n      return true;\n    }\n    if (normalized === \"0\" || normalized === \"false\" || normalized === \"no\") {\n      return false;\n    }\n  }\n  return undefined;\n}\n","import type {\n  ClientInfo,\n  AdDetectInfo,\n  AdLoadedInfo,\n  AdImpressionInfo,\n  PlayerAnalyticsContext,\n  StormcloudVideoPlayerConfig,\n} from \"../types\";\n\nimport { buildPlayerTopic, isMQTTEnabled } from \"./mqttConfig\";\nimport { ensureMQTTClient, publishMQTT } from \"./mqttClient\";\nimport {\n  getOrCreateStoredUuid,\n  PLAYER_ID_STORAGE_KEY,\n  resolveNativeDeviceId,\n} from \"./vastEnvironmentSignals\";\n\nlet cachedBrowserId: string | null = null;\n\nexport function buildAnalyticsContext(\n  config: Pick<StormcloudVideoPlayerConfig, \"debugAdTiming\" | \"isLiveStream\">\n): PlayerAnalyticsContext {\n  const context: PlayerAnalyticsContext = {};\n  if (config.debugAdTiming) {\n    context.debugAdTiming = true;\n  }\n  if (config.isLiveStream !== undefined) {\n    context.inputStreamType = config.isLiveStream ? \"live\" : \"vod\";\n  }\n  return context;\n}\n\nexport function getClientInfo(): ClientInfo {\n  const ua = navigator.userAgent;\n  const platform = navigator.platform;\n  const vendor = navigator.vendor || \"\";\n  const maxTouchPoints = navigator.maxTouchPoints || 0;\n  const memory = (navigator as any).deviceMemory || null;\n  const hardwareConcurrency = navigator.hardwareConcurrency || 1;\n\n  const screenInfo = {\n    width: screen?.width,\n    height: screen?.height,\n    availWidth: screen?.availWidth,\n    availHeight: screen?.availHeight,\n    orientation: (screen?.orientation as any)?.type || \"\",\n    pixelDepth: screen?.pixelDepth,\n  };\n\n  let deviceType: \"tv\" | \"mobile\" | \"tablet\" | \"desktop\" = \"desktop\";\n  let brand = \"Unknown\";\n  let os = \"Unknown\";\n  let model = \"\";\n  let isSmartTV = false;\n  let isAndroid = false;\n  let isWebView = false;\n  let isWebApp = false;\n\n  if (ua.includes(\"Web0S\")) {\n    brand = \"LG\";\n    os = \"webOS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n    const webosMatch = ua.match(/Web0S\\/([^\\s]+)/);\n    model = webosMatch ? `webOS ${webosMatch[1]}` : \"webOS TV\";\n  } else if (ua.includes(\"Tizen\")) {\n    brand = \"Samsung\";\n    os = \"Tizen\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n    const tizenMatch = ua.match(/Tizen\\/([^\\s]+)/);\n    const tvMatch = ua.match(/(?:Smart-TV|SMART-TV|TV)/i) ? \"Smart TV\" : \"\";\n    model = tizenMatch\n      ? `Tizen ${tizenMatch[1]} ${tvMatch}`.trim()\n      : \"Tizen TV\";\n  } else if (ua.includes(\"Philips\")) {\n    brand = \"Philips\";\n    os = \"Saphi\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"Sharp\") || ua.includes(\"AQUOS\")) {\n    brand = \"Sharp\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (\n    ua.includes(\"Android\") &&\n    (ua.includes(\"Sony\") || vendor.includes(\"Sony\"))\n  ) {\n    brand = \"Sony\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (\n    ua.includes(\"Android\") &&\n    (ua.includes(\"NetCast\") || ua.includes(\"LG\"))\n  ) {\n    brand = \"LG\";\n    os = \"Android TV\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\" Roku\") || ua.includes(\"Roku/\")) {\n    brand = \"Roku\";\n    os = \"Roku OS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  } else if (ua.includes(\"AppleTV\")) {\n    brand = \"Apple\";\n    os = \"tvOS\";\n    isSmartTV = true;\n    deviceType = \"tv\";\n  }\n\n  if (ua.includes(\"Android\")) {\n    isAndroid = true;\n    os = \"Android\";\n    deviceType = /Mobile/.test(ua) ? \"mobile\" : \"tablet\";\n\n    if (\n      ua.includes(\"Android\") &&\n      (maxTouchPoints === 0 ||\n        ua.includes(\"Google TV\") ||\n        ua.includes(\"XiaoMi\"))\n    ) {\n      deviceType = \"tv\";\n      isSmartTV = true;\n      brand = brand === \"Unknown\" ? \"Android TV\" : brand;\n    }\n\n    const androidModelMatch = ua.match(/\\(([^)]*Android[^)]*)\\)/);\n    if (androidModelMatch && androidModelMatch[1]) {\n      model = androidModelMatch[1];\n    }\n  }\n\n  if (/iPad|iPhone|iPod/.test(ua)) {\n    os = \"iOS\";\n    deviceType = \"mobile\";\n    brand = \"Apple\";\n    if (navigator.maxTouchPoints > 1 && /iPad/.test(ua)) {\n      deviceType = \"tablet\";\n    }\n  }\n\n  if (!isAndroid && !isSmartTV && !/Mobile/.test(ua)) {\n    if (ua.includes(\"Windows\")) {\n      os = \"Windows\";\n      deviceType = \"desktop\";\n    } else if (ua.includes(\"Mac\") && !/iPhone/.test(ua)) {\n      os = \"macOS\";\n      deviceType = \"desktop\";\n      if (maxTouchPoints > 1) deviceType = \"tablet\";\n    } else if (ua.includes(\"Linux\")) {\n      os = \"Linux\";\n      deviceType = \"desktop\";\n    }\n  }\n\n  if (brand === \"Unknown\") {\n    if (vendor.includes(\"Google\") || ua.includes(\"Chrome\")) brand = \"Google\";\n    if (vendor.includes(\"Apple\")) brand = \"Apple\";\n    if (vendor.includes(\"Samsung\") || ua.includes(\"SM-\")) brand = \"Samsung\";\n  }\n\n  isWebView = /wv|WebView|Linux; U;/.test(ua);\n\n  if (window?.outerHeight === 0 && window?.outerWidth === 0) {\n    isWebView = true;\n  }\n\n  isWebApp =\n    window.matchMedia(\"(display-mode: standalone)\").matches ||\n    (window.navigator as any).standalone === true ||\n    window.screen?.orientation?.angle !== undefined;\n\n  return {\n    brand,\n    os,\n    model: model || ua.substring(0, 50) + \"...\",\n    deviceType,\n    isSmartTV,\n    isAndroid,\n    isWebView,\n    isWebApp,\n    domain: window.location.hostname,\n    origin: window.location.origin,\n    path: window.location.pathname,\n    userAgent: ua,\n    vendor,\n    platform,\n    screen: screenInfo,\n    hardwareConcurrency,\n    deviceMemory: memory,\n    maxTouchPoints,\n    language: navigator.language,\n    languages: navigator.languages?.join(\",\") || \"\",\n    cookieEnabled: navigator.cookieEnabled,\n    doNotTrack: navigator.doNotTrack || \"\",\n    referrer: document.referrer,\n    visibilityState: document.visibilityState,\n  };\n}\n\nasync function sha256Hex(input: string): Promise<string> {\n  if (typeof crypto !== \"undefined\" && crypto.subtle?.digest) {\n    try {\n      await crypto.subtle.digest(\"SHA-256\", new Uint8Array([1, 2, 3]));\n\n      let encodedData: BufferSource;\n      if (typeof TextEncoder !== \"undefined\") {\n        encodedData = new TextEncoder().encode(input);\n      } else {\n        const utf8 = unescape(encodeURIComponent(input));\n        const buffer = new Uint8Array(utf8.length);\n        for (let i = 0; i < utf8.length; i++) {\n          buffer[i] = utf8.charCodeAt(i);\n        }\n        encodedData = buffer;\n      }\n\n      const hashBuffer = await crypto.subtle.digest(\"SHA-256\", encodedData);\n      return Array.from(new Uint8Array(hashBuffer))\n        .map((b) => b.toString(16).padStart(2, \"0\"))\n        .join(\"\");\n    } catch {\n      console.warn(\n        \"[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash\"\n      );\n    }\n  }\n\n  let hash = 0;\n  for (let i = 0; i < input.length; i++) {\n    const char = input.charCodeAt(i);\n    hash = (hash << 5) - hash + char;\n    hash = hash & hash;\n  }\n\n  const fallbackHash = Math.abs(hash).toString(16).padStart(8, \"0\");\n  const timestamp = Date.now().toString(16).padStart(12, \"0\");\n  const random = Math.random().toString(16).substring(2, 14).padStart(12, \"0\");\n\n  return (fallbackHash + timestamp + random).padEnd(64, \"0\");\n}\n\nfunction resolvePlayerIdentitySeed(clientInfo: ClientInfo): string {\n  const nativeDeviceId = resolveNativeDeviceId();\n  if (nativeDeviceId) {\n    return `device:${nativeDeviceId}`;\n  }\n\n  const persistedId = getOrCreateStoredUuid(PLAYER_ID_STORAGE_KEY);\n  if (persistedId) {\n    return `persisted:${persistedId}`;\n  }\n\n  return `fingerprint:${JSON.stringify(clientInfo)}`;\n}\n\nexport async function getBrowserID(clientInfo: ClientInfo): Promise<string> {\n  if (cachedBrowserId) {\n    return cachedBrowserId;\n  }\n\n  cachedBrowserId = await sha256Hex(resolvePlayerIdentitySeed(clientInfo));\n  return cachedBrowserId;\n}\n\ntype PlayerMetricFlags = {\n  adLoaded?: boolean;\n  adDetect?: boolean;\n  captureAt?: string;\n};\n\nfunction canPublish(licenseKey: string | undefined): licenseKey is string {\n  return Boolean(isMQTTEnabled() && licenseKey);\n}\n\nasync function buildPlayerMetricEvent(\n  context: PlayerAnalyticsContext = {},\n  flags: PlayerMetricFlags = {}\n): Promise<Record<string, unknown>> {\n  const clientInfo = getClientInfo();\n\n  const debugAdTiming = !!context.debugAdTiming;\n  if (debugAdTiming) {\n    console.log(\"[StormcloudVideoPlayer] clientInfo: \", JSON.stringify(clientInfo, null, 2));\n  }\n\n  const playerId = await getBrowserID(clientInfo);\n\n  if (debugAdTiming) {\n    console.log(\"[StormcloudVideoPlayer] playerId: \", playerId);\n  }\n\n  const captureAt = flags.captureAt ?? new Date().toISOString();\n\n  return {\n    player_id: playerId,\n    device_type: clientInfo.deviceType,\n    os: clientInfo.os.toLowerCase(),\n    ad_loaded: flags.adLoaded ?? false,\n    ad_detect: flags.adDetect ?? false,\n    capture_at: captureAt,\n    ...(context.inputStreamType\n      ? { input_stream_type: context.inputStreamType }\n      : {}),\n  };\n}\n\nfunction publishTracking(\n  licenseKey: string,\n  channel: \"metrics\" | \"impressions\" | \"heartbeat\",\n  body: Record<string, unknown>\n): void {\n  ensureMQTTClient();\n  publishMQTT(buildPlayerTopic(licenseKey, channel), body);\n}\n\nexport async function sendInitialTracking(\n  licenseKey?: string,\n  context: PlayerAnalyticsContext = {}\n): Promise<void> {\n  if (!canPublish(licenseKey)) return;\n\n  try {\n    const metricEvent = await buildPlayerMetricEvent(context, {\n      adLoaded: false,\n      adDetect: false,\n    });\n\n    publishTracking(licenseKey, \"metrics\", {\n      events: [metricEvent],\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending initial tracking data:\",\n      error\n    );\n  }\n}\n\nexport async function sendAdDetectTracking(\n  licenseKey: string | undefined,\n  adDetectInfo: AdDetectInfo,\n  context: PlayerAnalyticsContext = {}\n): Promise<void> {\n  try {\n    await sendHeartbeat(licenseKey, context, {\n      adDetect: true,\n      captureAt: adDetectInfo.timestamp,\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending ad detect tracking:\",\n      error\n    );\n  }\n}\n\nexport async function sendAdLoadedTracking(\n  licenseKey: string | undefined,\n  adLoadedInfo: AdLoadedInfo,\n  context: PlayerAnalyticsContext = {}\n): Promise<void> {\n  try {\n    await sendHeartbeat(licenseKey, context, {\n      adLoaded: true,\n      captureAt: adLoadedInfo.timestamp,\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending ad loaded tracking:\",\n      error\n    );\n  }\n}\n\nexport async function sendAdImpressionTracking(\n  licenseKey: string | undefined,\n  adImpressionInfo: AdImpressionInfo,\n  context: PlayerAnalyticsContext = {}\n): Promise<void> {\n  if (!canPublish(licenseKey)) return;\n\n  try {\n    const metricEvent = await buildPlayerMetricEvent(context, {\n      captureAt: adImpressionInfo.timestamp,\n    });\n\n    publishTracking(licenseKey, \"heartbeat\", metricEvent);\n    publishTracking(licenseKey, \"impressions\", {\n      events: [\n        {\n          player_id: metricEvent.player_id,\n          ad_played_count: 1,\n          ad_url: adImpressionInfo.adUrl,\n          capture_at: adImpressionInfo.timestamp,\n        },\n      ],\n    });\n  } catch (error) {\n    console.error(\n      \"[StormcloudVideoPlayer] Error sending ad impression tracking:\",\n      error\n    );\n  }\n}\n\nexport async function sendHeartbeat(\n  licenseKey?: string,\n  context: PlayerAnalyticsContext = {},\n  flags: PlayerMetricFlags = {}\n): Promise<void> {\n  if (!canPublish(licenseKey)) return;\n\n  try {\n    const heartbeatData = await buildPlayerMetricEvent(context, flags);\n    publishTracking(licenseKey, \"heartbeat\", heartbeatData);\n  } catch (error) {\n    console.error(\"[StormcloudVideoPlayer] Error sending heartbeat:\", error);\n  }\n}\n","import type { AdController } from \"../types\";\n\nexport function getAdAudioVolume(adPlayer: AdController): number {\n  return adPlayer.getOriginalMutedState() ? 0 : adPlayer.getOriginalVolume();\n}\n\nexport function toggleMute(\n  video: HTMLVideoElement,\n  adPlayer: AdController,\n  debug: boolean\n): void {\n  if (adPlayer.isAdPlaying()) {\n    const newMutedState = !adPlayer.getOriginalMutedState();\n    const restoreVolume = adPlayer.getOriginalVolume() || 1;\n\n    adPlayer.updateOriginalMutedState(newMutedState, video.volume);\n    adPlayer.setAdVolume(newMutedState ? 0 : restoreVolume);\n\n    if (debug) {\n      console.log(\n        \"[StormcloudVideoPlayer] Mute toggle during ad - immediately applied:\",\n        newMutedState\n      );\n    }\n  } else {\n    video.muted = !video.muted;\n    adPlayer.updateOriginalMutedState(video.muted, video.volume);\n    if (debug) {\n      console.log(\"[StormcloudVideoPlayer] Muted:\", video.muted);\n    }\n  }\n}\n\nexport function toggleFullscreen(\n  video: HTMLVideoElement,\n  debug: boolean\n): Promise<void> {\n  return new Promise((resolve, reject) => {\n    if (!document.fullscreenElement) {\n      const container = video.parentElement;\n      if (!container) {\n        reject(new Error(\"No parent container found for fullscreen\"));\n        return;\n      }\n      container\n        .requestFullscreen()\n        .then(() => {\n          if (debug) {\n            console.log(\"[StormcloudVideoPlayer] Entered fullscreen\");\n          }\n          resolve();\n        })\n        .catch((err) => {\n          if (debug) {\n            console.error(\"[StormcloudVideoPlayer] Fullscreen error:\", err);\n          }\n          reject(err);\n        });\n    } else {\n      document\n        .exitFullscreen()\n        .then(() => {\n          if (debug) {\n            console.log(\"[StormcloudVideoPlayer] Exited fullscreen\");\n          }\n          resolve();\n        })\n        .catch((err) => {\n          if (debug) {\n            console.error(\n              \"[StormcloudVideoPlayer] Exit fullscreen error:\",\n              err\n            );\n          }\n          reject(err);\n        });\n    }\n  });\n}\n\nexport function isMuted(\n  video: HTMLVideoElement,\n  adPlayer: AdController\n): boolean {\n  if (adPlayer.isAdPlaying()) {\n    return adPlayer.getOriginalMutedState();\n  }\n  return video.muted;\n}\n\nexport function setMuted(\n  video: HTMLVideoElement,\n  adPlayer: AdController,\n  muted: boolean,\n  debug: boolean\n): void {\n  const adPlaying = adPlayer.isAdPlaying();\n\n  if (adPlaying && muted === video.muted) {\n    if (debug) {\n      console.log(\n        \"[StormcloudVideoPlayer] setMuted reflective update during ad ignored\",\n        { muted }\n      );\n    }\n    return;\n  }\n\n  video.muted = muted;\n\n  if (adPlaying) {\n    adPlayer.updateOriginalMutedState(muted, video.volume);\n    adPlayer.setAdVolume(muted ? 0 : 1);\n    if (debug) {\n      console.log(\"[StormcloudVideoPlayer] setMuted applied during ad\", {\n        muted,\n      });\n    }\n    return;\n  }\n\n  adPlayer.updateOriginalMutedState(muted, video.volume);\n  if (debug) {\n    console.log(\"[StormcloudVideoPlayer] setMuted called:\", muted);\n  }\n}\n\nexport function setVolume(\n  video: HTMLVideoElement,\n  adPlayer: AdController,\n  volume: number,\n  debug: boolean\n): void {\n  const clampedVolume = Math.max(0, Math.min(1, volume));\n  const adPlaying = adPlayer.isAdPlaying();\n\n  if (adPlaying) {\n    adPlayer.setAdVolume(clampedVolume);\n    adPlayer.updateOriginalMutedState(clampedVolume === 0, clampedVolume);\n    if (debug) {\n      console.log(\"[StormcloudVideoPlayer] setVolume applied during ad\", {\n        volume: clampedVolume,\n      });\n    }\n  } else {\n    video.volume = clampedVolume;\n    video.muted = clampedVolume === 0;\n    adPlayer.updateOriginalMutedState(clampedVolume === 0, clampedVolume);\n    if (debug) {\n      console.log(\"[StormcloudVideoPlayer] setVolume called:\", clampedVolume);\n    }\n  }\n}\n\nexport function getVolume(\n  video: HTMLVideoElement,\n  adPlayer: AdController\n): number {\n  const adPlaying = adPlayer.isAdPlaying();\n  if (adPlaying) {\n    return adPlayer.getAdVolume();\n  }\n  return video.volume;\n}\n\nexport function isFullscreen(): boolean {\n  return !!document.fullscreenElement;\n}\n\nexport function resizePlayer(\n  video: HTMLVideoElement,\n  adPlayer: AdController,\n  debug: boolean\n): void {\n  if (debug) {\n    console.log(\"[StormcloudVideoPlayer] Resizing player\");\n  }\n\n  if (adPlayer && adPlayer.isAdPlaying()) {\n    const width = video.clientWidth || 640;\n    const height = video.clientHeight || 480;\n\n    if (debug) {\n      console.log(\n        `[StormcloudVideoPlayer] Resizing ads manager to ${width}x${height}`\n      );\n    }\n\n    adPlayer.resize(width, height);\n  }\n}\n"]}