{"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":["__copyProps","__create","Object","create","__defProp","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__getProtoOf","getPrototypeOf","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","to","from","except","desc","key","call","__toESM","mod","isNodeMode","__esModule","value","__toCommonJS","AdBreakOrchestrator_exports","AdBreakOrchestrator","exports","DEFAULT_MQTT_CONFIG","enabled","brokerAddress","brokerPort","wsPort","username","password","topicPrefix","qos","mqttConfig","isMQTTEnabled","buildMQTTBrokerUrl","brokerUrl","buildPlayerTopic","licenseKey","channel","import_mqtt","require","LOG","client","status","initMQTTClient","url","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","PLAYER_ID_STORAGE_KEY","DEVICE_ID_STORAGE_KEYS","inferDeviceIdType","navigator","ua","userAgent","test","resolveNativeDeviceId","bridgeSignals","readNativeBridgeSignals","platformDevice","readPlatformNativeDeviceId","deviceId","readStoredDeviceId","isMacroPlaceholder","isZeroedAdId","getOrCreateStoredUuid","storageKey","window","existing","readPersistentId","writePersistentId","id","createUuid","readStoredString","readCookie","localStorage","setItem","writeCookie","PERSISTENT_ID_COOKIE_MAX_AGE","document","prefix","encodeURIComponent","parts","cookie","split","rida","part","entry","trim","startsWith","decodeURIComponent","length","bytes","crypto","randomUUID","Uint8Array","getRandomValues","seed","Date","now","floor","performanceNow","i","hex","padStart","performance","adinfo","webapis","tifa","getTIFA","lat","isLATEnabled","deviceIdType","limitAdTracking","webOSDev","lgudid","LGUDID","roku","Roku","getPublisherUniqueId","ZEROED_AD_ID_PATTERNS","Set","t","has","toLowerCase","candidates","__STORMCLOUD_CTV_AD_INFO__","__STORMCLOUD_CTV__","stormcloudCtv","AiryTV","amazon","source","contentUrl","Android","tizen","filter","Boolean","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","language","buffer","utf8","charCodeAt","encodedData","sendHeartbeat","domain","location","hostname","origin","path","pathname","languages","cookieEnabled","join","referrer","visibilityState","sha256Hex","input","hashBuffer","hash","char","fallbackHash","timestamp","subtle","digest","TextEncoder","encode","unescape","Array","b","map","abs","substring","padEnd","nativeDeviceId","persistedId","clientInfo","getBrowserID","resolvePlayerIdentitySeed","canPublish","buildPlayerMetricEvent","flags","playerId","captureAt","log","toISOString","player_id","device_type","ad_loaded","adLoaded","ad_detect","adDetect","input_stream_type","publishTracking","body","sendAdLoadedTracking","adLoadedInfo","error","sendAdImpressionTracking","adImpressionInfo","metricEvent","events","ad_played_count","ad_url","capture_at","adUrl","heartbeatData","getAdAudioVolume","adPlayer","host","timing","preloadPool","adConfig","cueManager","placeholder","inAdBreak","showAds","isShowingPlaceholder","adDetectSentForCurrentBreak","continuousFetchingActive","continuousFetchLoopRunning","adRequestQueue","adPodQueue","maxPlaceholderDurationMs","pendingAdBreak","debug","analyticsContext","nextAdRequestUrl","baseVastUrl","remaining","min","getWallClockRemainingAdMs","getDurationBudgetRemainingMs","breakDurationMs","generatePodVastUrl","generateVastUrls","getOriginalMutedState","getOriginalVolume","attachImaEventListeners","getAdPlayer","notePodAdStarted","index","total","adIndex","currentAdIndex","catch","recordPlayedAdDuration","getRemainingAdMs","consecutiveFailures","queuedUrls","hasTimeToStartAnotherAd","tryNextAvailableAdWithRateLimit","handleAdPodComplete","errorPayload","errorMessage","errorCode","code","vastErrorCode","cause","innerError","showPlaceholder","causeMessage","String","handleAdFailure","clearAdFailsafeTimer","clearAdRequestWatchdog","activeAdRequestToken","resetGamNoFillCounter","expectedAdBreakDurationMs","getAdBreakEndWallClockMs","scheduleAdStopAtBreakBoundary","setAdVolume","hidePlaceholder","video","muted","recreateAdController","shouldShowPlaceholder","show","preservedMutedState","preservedVolumeState","destroy","continueLiveStreamDuringAds","shouldContinueLiveStreamDuringAds","newAdPlayer","createAdPlayer","setAdPlayer","initialize","updateOriginalMutedState","clearPendingAdBreak","prefetchTimerId","clearTimeout","currentMuted","vastUrl","currentVolume","firstAdUrl","pendingScte35CueKey","podAssignedByPrefetch","handleAdStart","_marker","scheduled","tags","adBreakDurationMs","mode","podPlayed","usePreloadedAd","preloadedController","preloaded","preservedMuted","preservedVolume","adVolume","fallbackPreloaded","ap","fallbackError","findCurrentOrNextBreak","currentTime","consumedVmapBreakIds","add","getAdBreakKey","selectVastTagsForBreak","apiVastTagUrl","durationSeconds","durationMs","failedVastUrls","clear","temporaryFailureUrls","totalAdRequestsInBreak","beginNewAdPod","volume","adBreakPlayedDurationMs","totalAdsInBreak","optimizedPodsEnabled","startOptimizedPod","getPreloadedAd","adController","vastUrls","shift","ensureLoadedAdFitsBudget","rejectLoadedAdForDuration","startContinuousFetching","resume","active","isLoopRunning","preloadPoolLoop","play","markAdStarted","lastAdRequestTime","requestAds","startPreloadPool"],"mappings":"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","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 = readPersistentId(storageKey);\n  if (existing) {\n    writePersistentId(storageKey, existing);\n    return existing;\n  }\n  const id = createUuid();\n  writePersistentId(storageKey, id);\n  return id;\n}\nfunction readPersistentId(storageKey) {\n  return readStoredString(storageKey) || readCookie(storageKey);\n}\nfunction writePersistentId(storageKey, value) {\n  try {\n    window.localStorage?.setItem(storageKey, value);\n  } catch {\n  }\n  writeCookie(storageKey, value);\n}\nvar PERSISTENT_ID_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5;\nfunction readCookie(name) {\n  if (typeof document === \"undefined\") {\n    return void 0;\n  }\n  try {\n    const prefix = encodeURIComponent(name) + \"=\";\n    const parts = document.cookie ? document.cookie.split(\";\") : [];\n    for (const part of parts) {\n      const entry = part.trim();\n      if (entry.startsWith(prefix)) {\n        return decodeURIComponent(entry.slice(prefix.length)) || void 0;\n      }\n    }\n  } catch {\n  }\n  return void 0;\n}\nfunction writeCookie(name, value) {\n  if (typeof document === \"undefined\") {\n    return;\n  }\n  try {\n    document.cookie = encodeURIComponent(name) + \"=\" + encodeURIComponent(value) + \";path=/;max-age=\" + PERSISTENT_ID_COOKIE_MAX_AGE + \";SameSite=Lax\";\n  } catch {\n  }\n}\nfunction createUuid() {\n  if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n    return crypto.randomUUID();\n  }\n  const bytes = new Uint8Array(16);\n  if (typeof crypto !== \"undefined\" && typeof crypto.getRandomValues === \"function\") {\n    crypto.getRandomValues(bytes);\n  } else {\n    let seed = Date.now() ^ Math.floor(performanceNow() * 1e3);\n    for (let i = 0; i < 16; i++) {\n      seed = seed * 1103515245 + 12345 & 2147483647;\n      bytes[i] = (seed ^ Math.floor(Math.random() * 256)) & 255;\n    }\n  }\n  bytes[6] = (bytes[6] ?? 0) & 15 | 64;\n  bytes[8] = (bytes[8] ?? 0) & 63 | 128;\n  let hex = \"\";\n  for (let i = 0; i < 16; i++) {\n    hex += (bytes[i] ?? 0).toString(16).padStart(2, \"0\");\n  }\n  return hex.slice(0, 8) + \"-\" + hex.slice(8, 12) + \"-\" + hex.slice(12, 16) + \"-\" + hex.slice(16, 20) + \"-\" + hex.slice(20, 32);\n}\nfunction performanceNow() {\n  try {\n    if (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n      return performance.now();\n    }\n  } catch {\n  }\n  return 0;\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 !== false;\n  }\n  nextAdRequestUrl(baseVastUrl) {\n    if (this.optimizedPodsEnabled) {\n      const remaining = Math.min(\n        this.timing.getWallClockRemainingAdMs(),\n        this.timing.getDurationBudgetRemainingMs()\n      );\n      const breakDurationMs = remaining > 0 ? remaining : void 0;\n      return this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);\n    }\n    return this.host.generateVastUrls(baseVastUrl, 1)[0];\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      firstAdUrl = this.nextAdRequestUrl(baseVastUrl);\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.optimizedPodsEnabled) {\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        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.optimizedPodsEnabled && !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.optimizedPodsEnabled && !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      }\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.nextAdRequestUrl(baseVastUrl);\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 !== false;\n  }\n\n  private nextAdRequestUrl(baseVastUrl: string): string | undefined {\n    if (this.optimizedPodsEnabled) {\n      const remaining = Math.min(\n        this.timing.getWallClockRemainingAdMs(),\n        this.timing.getDurationBudgetRemainingMs()\n      );\n      const breakDurationMs = remaining > 0 ? remaining : undefined;\n      return this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);\n    }\n    return this.host.generateVastUrls(baseVastUrl, 1)[0];\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      firstAdUrl = this.nextAdRequestUrl(baseVastUrl);\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.optimizedPodsEnabled) {\n          if (!this.preloadPool.active) {\n            this.preloadPool.active = true;\n          }\n          if (!this.preloadPool.isLoopRunning) {\n            this.preloadPool.preloadPoolLoop(baseVastUrl);\n          }\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.optimizedPodsEnabled && !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.optimizedPodsEnabled && !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      }\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.nextAdRequestUrl(baseVastUrl);\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 = readPersistentId(storageKey);\n  if (existing) {\n    writePersistentId(storageKey, existing);\n    return existing;\n  }\n\n  const id = createUuid();\n  writePersistentId(storageKey, id);\n  return id;\n}\n\nfunction getOrCreateCtvSessionId(): string {\n  const existing = readPersistentId(CTV_SESSION_STORAGE_KEY);\n  if (existing) {\n    writePersistentId(CTV_SESSION_STORAGE_KEY, existing);\n    return existing;\n  }\n\n  const sessionId = createUuid();\n  writePersistentId(CTV_SESSION_STORAGE_KEY, sessionId);\n  return sessionId;\n}\n\nfunction readPersistentId(storageKey: string): string | undefined {\n  return readStoredString(storageKey) || readCookie(storageKey);\n}\n\nfunction writePersistentId(storageKey: string, value: string): void {\n  try {\n    window.localStorage?.setItem(storageKey, value);\n  } catch {}\n  writeCookie(storageKey, value);\n}\n\nconst PERSISTENT_ID_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5; // ~5 years\n\nfunction readCookie(name: string): string | undefined {\n  if (typeof document === \"undefined\") {\n    return undefined;\n  }\n\n  try {\n    const prefix = encodeURIComponent(name) + \"=\";\n    const parts = document.cookie ? document.cookie.split(\";\") : [];\n    for (const part of parts) {\n      const entry = part.trim();\n      if (entry.startsWith(prefix)) {\n        return decodeURIComponent(entry.slice(prefix.length)) || undefined;\n      }\n    }\n  } catch {}\n\n  return undefined;\n}\n\nfunction writeCookie(name: string, value: string): void {\n  if (typeof document === \"undefined\") {\n    return;\n  }\n\n  try {\n    document.cookie =\n      encodeURIComponent(name) +\n      \"=\" +\n      encodeURIComponent(value) +\n      \";path=/;max-age=\" +\n      PERSISTENT_ID_COOKIE_MAX_AGE +\n      \";SameSite=Lax\";\n  } catch {}\n}\n\nfunction createUuid(): string {\n  if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n    return crypto.randomUUID();\n  }\n\n  const bytes = new Uint8Array(16);\n  if (typeof crypto !== \"undefined\" && typeof crypto.getRandomValues === \"function\") {\n    crypto.getRandomValues(bytes);\n  } else {\n    let seed = Date.now() ^ Math.floor(performanceNow() * 1e3);\n    for (let i = 0; i < 16; i++) {\n      seed = (seed * 1103515245 + 12345) & 0x7fffffff;\n      bytes[i] = (seed ^ Math.floor(Math.random() * 256)) & 0xff;\n    }\n  }\n\n  bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;\n  bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;\n\n  let hex = \"\";\n  for (let i = 0; i < 16; i++) {\n    hex += (bytes[i] ?? 0).toString(16).padStart(2, \"0\");\n  }\n\n  return (\n    hex.slice(0, 8) +\n    \"-\" +\n    hex.slice(8, 12) +\n    \"-\" +\n    hex.slice(12, 16) +\n    \"-\" +\n    hex.slice(16, 20) +\n    \"-\" +\n    hex.slice(20, 32)\n  );\n}\n\nfunction performanceNow(): number {\n  try {\n    if (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n      return performance.now();\n    }\n  } catch {}\n  return 0;\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"]}