@active-reach/web-sdk 1.43.0 → 1.43.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/dist/aegis-push.min.js +2 -0
  2. package/dist/aegis-push.min.js.map +1 -0
  3. package/dist/aegis-sw.js +1 -0
  4. package/dist/aegis.min.js +2 -0
  5. package/dist/aegis.min.js.map +1 -0
  6. package/dist/analytics-C-Go3Pm5.mjs +3509 -0
  7. package/dist/analytics-C-Go3Pm5.mjs.map +1 -0
  8. package/dist/cdn.d.ts +26 -0
  9. package/dist/cdn.d.ts.map +1 -0
  10. package/dist/chat/AegisChat.d.ts +169 -0
  11. package/dist/chat/AegisChat.d.ts.map +1 -0
  12. package/dist/chat/index.d.ts +3 -0
  13. package/dist/chat/index.d.ts.map +1 -0
  14. package/dist/core/analytics.d.ts +320 -0
  15. package/dist/core/analytics.d.ts.map +1 -0
  16. package/dist/core/bootstrap.d.ts +96 -0
  17. package/dist/core/bootstrap.d.ts.map +1 -0
  18. package/dist/core/prefetch-bundle-client.d.ts +175 -0
  19. package/dist/core/prefetch-bundle-client.d.ts.map +1 -0
  20. package/dist/core/queue.d.ts +35 -0
  21. package/dist/core/queue.d.ts.map +1 -0
  22. package/dist/core/rate-limiter.d.ts +45 -0
  23. package/dist/core/rate-limiter.d.ts.map +1 -0
  24. package/dist/core/sdk-config-poller.d.ts +59 -0
  25. package/dist/core/sdk-config-poller.d.ts.map +1 -0
  26. package/dist/core/session.d.ts +52 -0
  27. package/dist/core/session.d.ts.map +1 -0
  28. package/dist/core/transport.d.ts +50 -0
  29. package/dist/core/transport.d.ts.map +1 -0
  30. package/dist/core/user-namespace.d.ts +48 -0
  31. package/dist/core/user-namespace.d.ts.map +1 -0
  32. package/dist/ecommerce/index.d.ts +94 -0
  33. package/dist/ecommerce/index.d.ts.map +1 -0
  34. package/dist/ecommerce/types.d.ts +81 -0
  35. package/dist/ecommerce/types.d.ts.map +1 -0
  36. package/dist/governance/bloom-filter.d.ts +47 -0
  37. package/dist/governance/bloom-filter.d.ts.map +1 -0
  38. package/dist/governance/index.d.ts +8 -0
  39. package/dist/governance/index.d.ts.map +1 -0
  40. package/dist/governance/murmur3.d.ts +43 -0
  41. package/dist/governance/murmur3.d.ts.map +1 -0
  42. package/dist/governance/name-governor.d.ts +98 -0
  43. package/dist/governance/name-governor.d.ts.map +1 -0
  44. package/dist/governance/trait-governor.d.ts +63 -0
  45. package/dist/governance/trait-governor.d.ts.map +1 -0
  46. package/dist/inapp/AegisInAppManager.d.ts +894 -0
  47. package/dist/inapp/AegisInAppManager.d.ts.map +1 -0
  48. package/dist/inapp/actions.d.ts +91 -0
  49. package/dist/inapp/actions.d.ts.map +1 -0
  50. package/dist/inapp/devicePairing.d.ts +70 -0
  51. package/dist/inapp/devicePairing.d.ts.map +1 -0
  52. package/dist/inapp/eval-throttle.d.ts +33 -0
  53. package/dist/inapp/eval-throttle.d.ts.map +1 -0
  54. package/dist/inapp/font.d.ts +16 -0
  55. package/dist/inapp/font.d.ts.map +1 -0
  56. package/dist/inapp/index.d.ts +8 -0
  57. package/dist/inapp/index.d.ts.map +1 -0
  58. package/dist/inapp/lucide-svg.d.ts +46 -0
  59. package/dist/inapp/lucide-svg.d.ts.map +1 -0
  60. package/dist/inapp/renderPreview.d.ts +40 -0
  61. package/dist/inapp/renderPreview.d.ts.map +1 -0
  62. package/dist/inapp/renderers/active-web-chat.d.ts +45 -0
  63. package/dist/inapp/renderers/active-web-chat.d.ts.map +1 -0
  64. package/dist/inapp/renderers/card-style.d.ts +31 -0
  65. package/dist/inapp/renderers/card-style.d.ts.map +1 -0
  66. package/dist/inapp/renderers/card-template.d.ts +233 -0
  67. package/dist/inapp/renderers/card-template.d.ts.map +1 -0
  68. package/dist/inapp/renderers/carousel-cards.d.ts +15 -0
  69. package/dist/inapp/renderers/carousel-cards.d.ts.map +1 -0
  70. package/dist/inapp/renderers/coachmark-tour.d.ts +24 -0
  71. package/dist/inapp/renderers/coachmark-tour.d.ts.map +1 -0
  72. package/dist/inapp/renderers/games.d.ts +27 -0
  73. package/dist/inapp/renderers/games.d.ts.map +1 -0
  74. package/dist/inapp/renderers/hero.d.ts +23 -0
  75. package/dist/inapp/renderers/hero.d.ts.map +1 -0
  76. package/dist/inapp/renderers/index.d.ts +22 -0
  77. package/dist/inapp/renderers/index.d.ts.map +1 -0
  78. package/dist/inapp/renderers/interactive-contract.d.ts +48 -0
  79. package/dist/inapp/renderers/interactive-contract.d.ts.map +1 -0
  80. package/dist/inapp/renderers/product-recommendation.d.ts +37 -0
  81. package/dist/inapp/renderers/product-recommendation.d.ts.map +1 -0
  82. package/dist/inapp/renderers/progress-bar.d.ts +38 -0
  83. package/dist/inapp/renderers/progress-bar.d.ts.map +1 -0
  84. package/dist/inapp/renderers/progress-core.d.ts +80 -0
  85. package/dist/inapp/renderers/progress-core.d.ts.map +1 -0
  86. package/dist/inapp/renderers/reviews.d.ts +38 -0
  87. package/dist/inapp/renderers/reviews.d.ts.map +1 -0
  88. package/dist/inapp/renderers/scratch-engine.d.ts +66 -0
  89. package/dist/inapp/renderers/scratch-engine.d.ts.map +1 -0
  90. package/dist/inapp/renderers/sticky-bar.d.ts +14 -0
  91. package/dist/inapp/renderers/sticky-bar.d.ts.map +1 -0
  92. package/dist/inapp/renderers/stories.d.ts +31 -0
  93. package/dist/inapp/renderers/stories.d.ts.map +1 -0
  94. package/dist/inapp/renderers/types.d.ts +152 -0
  95. package/dist/inapp/renderers/types.d.ts.map +1 -0
  96. package/dist/inapp/renderers/video.d.ts +20 -0
  97. package/dist/inapp/renderers/video.d.ts.map +1 -0
  98. package/dist/inbox/AegisInbox.d.ts +106 -0
  99. package/dist/inbox/AegisInbox.d.ts.map +1 -0
  100. package/dist/inbox/index.d.ts +3 -0
  101. package/dist/inbox/index.d.ts.map +1 -0
  102. package/dist/index.d.ts +42 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +15478 -0
  105. package/dist/index.js.map +1 -0
  106. package/dist/integrations/react.d.ts +19 -0
  107. package/dist/integrations/react.d.ts.map +1 -0
  108. package/dist/loyalty/AegisLoyaltyManager.d.ts +120 -0
  109. package/dist/loyalty/AegisLoyaltyManager.d.ts.map +1 -0
  110. package/dist/loyalty/index.d.ts +3 -0
  111. package/dist/loyalty/index.d.ts.map +1 -0
  112. package/dist/plugins/registry.d.ts +13 -0
  113. package/dist/plugins/registry.d.ts.map +1 -0
  114. package/dist/push/AegisWebPush.d.ts +199 -0
  115. package/dist/push/AegisWebPush.d.ts.map +1 -0
  116. package/dist/push/AegisWebPush.js +542 -0
  117. package/dist/push/AegisWebPush.js.map +1 -0
  118. package/dist/push/cdn-push.d.ts +21 -0
  119. package/dist/push/cdn-push.d.ts.map +1 -0
  120. package/dist/react.js +70 -0
  121. package/dist/react.js.map +1 -0
  122. package/dist/runtime/AegisMessageRuntime.d.ts +276 -0
  123. package/dist/runtime/AegisMessageRuntime.d.ts.map +1 -0
  124. package/dist/runtime/index.d.ts +3 -0
  125. package/dist/runtime/index.d.ts.map +1 -0
  126. package/dist/snippet.min.js +1 -0
  127. package/dist/state/intent_ledger.d.ts +136 -0
  128. package/dist/state/intent_ledger.d.ts.map +1 -0
  129. package/dist/triggers/ContactScoresFetcher.d.ts +113 -0
  130. package/dist/triggers/ContactScoresFetcher.d.ts.map +1 -0
  131. package/dist/triggers/IntentRuleEvaluator.d.ts +213 -0
  132. package/dist/triggers/IntentRuleEvaluator.d.ts.map +1 -0
  133. package/dist/triggers/IntentSnapshotCollector.d.ts +83 -0
  134. package/dist/triggers/IntentSnapshotCollector.d.ts.map +1 -0
  135. package/dist/triggers/SelectorBinder.d.ts +118 -0
  136. package/dist/triggers/SelectorBinder.d.ts.map +1 -0
  137. package/dist/triggers/TriggerEngine.d.ts +296 -0
  138. package/dist/triggers/TriggerEngine.d.ts.map +1 -0
  139. package/dist/triggers/behaviouralTriggers.d.ts +44 -0
  140. package/dist/triggers/behaviouralTriggers.d.ts.map +1 -0
  141. package/dist/triggers/index.d.ts +11 -0
  142. package/dist/triggers/index.d.ts.map +1 -0
  143. package/dist/triggers/microIntentTriggers.d.ts +80 -0
  144. package/dist/triggers/microIntentTriggers.d.ts.map +1 -0
  145. package/dist/types/config.d.ts +77 -0
  146. package/dist/types/config.d.ts.map +1 -0
  147. package/dist/types/events.d.ts +147 -0
  148. package/dist/types/events.d.ts.map +1 -0
  149. package/dist/types/index.d.ts +5 -0
  150. package/dist/types/index.d.ts.map +1 -0
  151. package/dist/types/plugin.d.ts +21 -0
  152. package/dist/types/plugin.d.ts.map +1 -0
  153. package/dist/types/type-safe-events.d.ts +14 -0
  154. package/dist/types/type-safe-events.d.ts.map +1 -0
  155. package/dist/utils/canonical-identity.d.ts +29 -0
  156. package/dist/utils/canonical-identity.d.ts.map +1 -0
  157. package/dist/utils/consent.d.ts +40 -0
  158. package/dist/utils/consent.d.ts.map +1 -0
  159. package/dist/utils/debounce.d.ts +3 -0
  160. package/dist/utils/debounce.d.ts.map +1 -0
  161. package/dist/utils/device.d.ts +6 -0
  162. package/dist/utils/device.d.ts.map +1 -0
  163. package/dist/utils/identity.d.ts +27 -0
  164. package/dist/utils/identity.d.ts.map +1 -0
  165. package/dist/utils/index.d.ts +7 -0
  166. package/dist/utils/index.d.ts.map +1 -0
  167. package/dist/utils/logger.d.ts +15 -0
  168. package/dist/utils/logger.d.ts.map +1 -0
  169. package/dist/utils/meta-cookies.d.ts +65 -0
  170. package/dist/utils/meta-cookies.d.ts.map +1 -0
  171. package/dist/utils/redact-url.d.ts +56 -0
  172. package/dist/utils/redact-url.d.ts.map +1 -0
  173. package/dist/utils/storage.d.ts +20 -0
  174. package/dist/utils/storage.d.ts.map +1 -0
  175. package/dist/utils/url-parser.d.ts +61 -0
  176. package/dist/utils/url-parser.d.ts.map +1 -0
  177. package/dist/utils/uuid.d.ts +4 -0
  178. package/dist/utils/uuid.d.ts.map +1 -0
  179. package/dist/utils/visitor.d.ts +43 -0
  180. package/dist/utils/visitor.d.ts.map +1 -0
  181. package/dist/widgets/AegisWidgetManager.d.ts +209 -0
  182. package/dist/widgets/AegisWidgetManager.d.ts.map +1 -0
  183. package/dist/widgets/index.d.ts +3 -0
  184. package/dist/widgets/index.d.ts.map +1 -0
  185. package/package.json +2 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aegis.min.js","sources":["../src/types/config.ts","../src/utils/uuid.ts","../src/utils/logger.ts","../src/utils/identity.ts","../src/core/session.ts","../src/utils/meta-cookies.ts","../src/core/queue.ts","../src/core/transport.ts","../src/utils/storage.ts","../src/utils/redact-url.ts","../src/utils/url-parser.ts","../src/utils/device.ts","../src/plugins/registry.ts","../src/utils/consent.ts","../src/ecommerce/index.ts","../src/core/rate-limiter.ts","../src/governance/murmur3.ts","../src/governance/bloom-filter.ts","../src/governance/name-governor.ts","../src/governance/trait-governor.ts","../src/core/user-namespace.ts","../src/core/analytics.ts","../src/inapp/actions.ts","../src/inapp/lucide-svg.ts","../src/inapp/font.ts","../src/inapp/renderers/interactive-contract.ts","../src/inapp/renderers/progress-core.ts","../src/inapp/renderers/card-template.ts","../src/inapp/renderers/sticky-bar.ts","../src/inapp/renderers/progress-bar.ts","../src/inapp/renderers/coachmark-tour.ts","../src/inapp/renderers/stories.ts","../src/inapp/renderers/games.ts","../src/inapp/renderers/video.ts","../src/inapp/renderers/hero.ts","../src/chat/AegisChat.ts","../src/triggers/TriggerEngine.ts","../src/inapp/renderers/active-web-chat.ts","../src/inapp/renderers/reviews.ts","../src/inapp/renderers/scratch-engine.ts","../src/utils/visitor.ts","../src/triggers/behaviouralTriggers.ts","../src/inapp/AegisInAppManager.ts","../src/inapp/renderers/product-recommendation.ts","../src/inapp/renderers/carousel-cards.ts","../src/inapp/devicePairing.ts","../src/widgets/AegisWidgetManager.ts","../src/triggers/microIntentTriggers.ts","../src/triggers/IntentRuleEvaluator.ts","../src/triggers/ContactScoresFetcher.ts","../src/loyalty/AegisLoyaltyManager.ts","../src/runtime/AegisMessageRuntime.ts","../src/core/bootstrap.ts","../src/cdn.ts","../src/inapp/renderPreview.ts","../src/push/AegisWebPush.ts"],"sourcesContent":["export type CellRegion = 'us-east' | 'us-west' | 'eu-central' | 'ap-south' | 'ap-southeast';\n\nexport interface CellEndpoint {\n region: CellRegion;\n url: string;\n priority: number;\n healthy: boolean;\n}\n\nexport interface AegisConfig {\n write_key: string;\n\n workspace_id?: string;\n \n api_host?: string;\n \n cell_endpoints?: CellEndpoint[];\n \n preferred_region?: CellRegion;\n \n auto_region_detection?: boolean;\n \n batch_size?: number;\n \n batch_interval?: number;\n \n capture_utm?: boolean;\n \n capture_referrer?: boolean;\n \n auto_page_view?: boolean;\n \n session_timeout?: number;\n \n debug?: boolean;\n \n respect_dnt?: boolean;\n \n cross_domain_tracking?: boolean;\n \n cookie_domain?: string;\n \n secure_cookie?: boolean;\n \n enable_offline_mode?: boolean;\n \n max_offline_events?: number;\n \n retry_failed_requests?: boolean;\n \n max_retries?: number;\n \n retry_backoff_multiplier?: number;\n \n request_timeout?: number;\n\n /** Client-side rate limiter — burst (token bucket capacity). Default 100. */\n rate_limit_burst?: number;\n\n /** Client-side rate limiter — sustained tokens per second. Default 20. */\n rate_limit_per_second?: number;\n\n plugins?: string[];\n\n wait_for_consent?: boolean;\n\n default_consent?: {\n analytics?: boolean;\n marketing?: boolean;\n functional?: boolean;\n };\n\n enable_consent_mode?: boolean;\n\n integrate_onetrust?: boolean;\n\n integrate_cookiebot?: boolean;\n\n integrate_google_consent_mode?: boolean;\n\n /** Push notification configuration. When provided, AegisWebPush is initialized. */\n push?: {\n /** VAPID public key for Web Push subscription. */\n vapidPublicKey: string;\n /** Automatically prompt for push permission after SDK init. Default: false. */\n autoPrompt?: boolean;\n /** Delay in ms before showing auto-prompt. Default: 5000. */\n promptDelay?: number;\n /** Path to the service worker file. Default: '/aegis-sw.js'. */\n serviceWorkerPath?: string;\n /** Scope for the service worker registration. Default: '/'. Critical for e-commerce platforms. */\n serviceWorkerScope?: string;\n };\n}\n\nexport interface AegisConfigInternal extends Required<Omit<AegisConfig, 'api_host' | 'cell_endpoints' | 'preferred_region' | 'cookie_domain' | 'plugins' | 'default_consent' | 'workspace_id'>> {\n initialized: boolean;\n workspace_id: string | null;\n api_host: string | null;\n cell_endpoints: CellEndpoint[];\n preferred_region: CellRegion | null;\n cookie_domain: string | null;\n plugins: string[];\n active_cell: CellEndpoint | null;\n default_consent: {\n analytics: boolean;\n marketing: boolean;\n functional: boolean;\n };\n}\n\nexport const DEFAULT_CONFIG: Partial<AegisConfigInternal> = {\n workspace_id: null,\n batch_size: 10,\n batch_interval: 1000,\n capture_utm: true,\n capture_referrer: true,\n auto_page_view: false,\n session_timeout: 30,\n debug: false,\n respect_dnt: true,\n cross_domain_tracking: false,\n secure_cookie: true,\n enable_offline_mode: true,\n max_offline_events: 100,\n retry_failed_requests: true,\n max_retries: 3,\n retry_backoff_multiplier: 2,\n request_timeout: 5000,\n rate_limit_burst: 100,\n rate_limit_per_second: 20,\n auto_region_detection: true,\n wait_for_consent: false,\n enable_consent_mode: false,\n integrate_onetrust: false,\n integrate_cookiebot: false,\n integrate_google_consent_mode: false,\n default_consent: {\n analytics: false,\n marketing: false,\n functional: true,\n },\n initialized: false,\n api_host: null,\n cell_endpoints: [],\n preferred_region: null,\n cookie_domain: null,\n plugins: [],\n active_cell: null,\n};\n","export function generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nexport function generateSessionId(): string {\n return `sess_${Date.now()}_${generateUUID().slice(0, 8)}`;\n}\n\nexport function generateMessageId(): string {\n return `msg_${Date.now()}_${generateUUID().slice(0, 8)}`;\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nclass Logger {\n private enabled: boolean = false;\n private prefix: string = '[Active Reach SDK]';\n\n enable(): void {\n this.enabled = true;\n }\n\n disable(): void {\n this.enabled = false;\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n debug(message: string, ...args: any[]): void {\n if (!this.enabled) return;\n console.debug(`${this.prefix} ${message}`, ...args);\n }\n\n info(message: string, ...args: any[]): void {\n if (!this.enabled) return;\n console.info(`${this.prefix} ${message}`, ...args);\n }\n\n warn(message: string, ...args: any[]): void {\n console.warn(`${this.prefix} ${message}`, ...args);\n }\n\n error(message: string, ...args: any[]): void {\n console.error(`${this.prefix} ${message}`, ...args);\n }\n}\n\nexport const logger = new Logger();\n","import { generateUUID } from './uuid';\nimport { Storage } from './storage';\nimport { logger } from './logger';\n\nexport interface IdentityState {\n anonymousId: string;\n userId: string | null;\n traits: Record<string, any>;\n}\n\nexport class Identity {\n private storage: Storage;\n private anonymousId: string;\n private userId: string | null = null;\n private traits: Record<string, any> = {};\n\n constructor(storage: Storage) {\n this.storage = storage;\n this.anonymousId = this.getOrCreateAnonymousId();\n this.loadUserIdentity();\n }\n\n private getOrCreateAnonymousId(): string {\n let id = this.storage.get('anon_id');\n\n if (!id) {\n id = generateUUID();\n this.storage.set('anon_id', id, 365);\n logger.debug('Created new anonymous ID:', id);\n } else {\n logger.debug('Loaded existing anonymous ID:', id);\n }\n\n return id;\n }\n\n private loadUserIdentity(): void {\n const storedUserId = this.storage.get('user_id');\n const storedTraits = this.storage.get('user_traits');\n\n if (storedUserId) {\n this.userId = storedUserId;\n logger.debug('Loaded user ID:', storedUserId);\n }\n\n if (storedTraits) {\n try {\n this.traits = JSON.parse(storedTraits);\n logger.debug('Loaded user traits:', this.traits);\n } catch (error) {\n logger.warn('Failed to parse stored traits:', error);\n this.traits = {};\n }\n }\n }\n\n getAnonymousId(): string {\n return this.anonymousId;\n }\n\n setUserId(userId: string, traits?: Record<string, any>): void {\n this.userId = userId;\n this.storage.set('user_id', userId, 365);\n logger.info('User identified:', userId);\n\n if (traits) {\n this.setTraits(traits);\n }\n }\n\n getUserId(): string | null {\n return this.userId;\n }\n\n setTraits(traits: Record<string, any>): void {\n this.traits = { ...this.traits, ...traits };\n this.storage.set('user_traits', JSON.stringify(this.traits), 365);\n logger.debug('User traits updated:', this.traits);\n }\n\n getTraits(): Record<string, any> {\n return { ...this.traits };\n }\n\n reset(): void {\n this.userId = null;\n this.traits = {};\n this.anonymousId = generateUUID();\n\n this.storage.remove('user_id');\n this.storage.remove('user_traits');\n this.storage.set('anon_id', this.anonymousId, 365);\n\n logger.info('Identity reset, new anonymous ID:', this.anonymousId);\n }\n\n alias(newUserId: string): { previousId: string; newUserId: string } {\n const previousId = this.userId || this.anonymousId;\n\n this.setUserId(newUserId);\n\n logger.info('User aliased:', { previousId, newUserId });\n\n return { previousId, newUserId };\n }\n\n getState(): IdentityState {\n return {\n anonymousId: this.anonymousId,\n userId: this.userId,\n traits: this.getTraits(),\n };\n }\n}\n","import { Storage } from '../utils/storage';\nimport { generateSessionId } from '../utils/uuid';\nimport { logger } from '../utils/logger';\nimport { AdClickIDs, LinkAttribution } from '../utils/url-parser';\n\n/**\n * How long a landing's Aegis link attribution survives a closed session —\n * the same 30-day window `click_attribution_bridge_worker.ATTRIBUTION_WINDOW_DAYS`\n * uses to attribute a short-link click, so the browser never claims a send\n * the server would already have aged out.\n */\nconst LINK_ATTRIBUTION_DURABLE_DAYS = 30;\n\nexport interface SessionState {\n sessionId: string;\n startTime: number;\n lastActivityTime: number;\n eventCount: number;\n adClickIDs?: AdClickIDs;\n landingPage?: string;\n linkAttribution?: LinkAttribution;\n}\n\nexport class SessionManager {\n private storage: Storage;\n private sessionId: string;\n private sessionTimeout: number;\n private lastActivityTime: number;\n private sessionStartTime: number;\n private eventCount: number = 0;\n private checkInterval: number | null = null;\n private activityThrottle: number = 1000;\n private lastActivityUpdate: number = 0;\n private adClickIDs: AdClickIDs = {};\n private landingPage?: string;\n private linkAttribution: LinkAttribution = {};\n\n constructor(storage: Storage, sessionTimeoutMinutes: number = 30) {\n this.storage = storage;\n this.sessionTimeout = sessionTimeoutMinutes * 60 * 1000;\n this.lastActivityTime = Date.now();\n this.sessionStartTime = Date.now();\n\n const session = this.loadSession();\n if (session) {\n this.sessionId = session.sessionId;\n this.sessionStartTime = session.startTime;\n this.lastActivityTime = session.lastActivityTime;\n this.eventCount = session.eventCount;\n this.adClickIDs = session.adClickIDs || {};\n this.landingPage = session.landingPage;\n this.linkAttribution = session.linkAttribution || {};\n logger.debug('Session loaded:', session);\n } else {\n this.sessionId = this.createNewSession();\n }\n\n this.startActivityTracking();\n this.startExpiryCheck();\n }\n\n private loadSession(): SessionState | null {\n const sessionData = this.storage.get('session');\n\n if (!sessionData) {\n return null;\n }\n\n try {\n const session: SessionState = JSON.parse(sessionData);\n const elapsed = Date.now() - session.lastActivityTime;\n\n if (elapsed < this.sessionTimeout) {\n return session;\n } else {\n logger.debug('Session expired, creating new session');\n this.storage.remove('session');\n return null;\n }\n } catch (error) {\n logger.warn('Failed to parse session data:', error);\n this.storage.remove('session');\n return null;\n }\n }\n\n private createNewSession(): string {\n const newSessionId = generateSessionId();\n this.sessionStartTime = Date.now();\n this.lastActivityTime = Date.now();\n this.eventCount = 0;\n // Assign BEFORE persisting — persistSession reads `this.sessionId`,\n // and prior to this assignment that field is still uninitialized\n // (the constructor only assigns it from createNewSession's return\n // value AFTER this call returns). Pre-fix, the first cookie write\n // contained the session JSON minus its sessionId field\n // (JSON.stringify drops undefined keys), and the next instance\n // reading that cookie would adopt the broken state.\n this.sessionId = newSessionId;\n\n this.persistSession();\n logger.info('New session created:', newSessionId);\n\n return newSessionId;\n }\n\n private persistSession(): void {\n const sessionData: SessionState = {\n sessionId: this.sessionId,\n startTime: this.sessionStartTime,\n lastActivityTime: this.lastActivityTime,\n eventCount: this.eventCount,\n adClickIDs: this.adClickIDs,\n landingPage: this.landingPage,\n linkAttribution: this.linkAttribution,\n };\n\n this.storage.set('session', JSON.stringify(sessionData));\n }\n\n private startActivityTracking(): void {\n const events = ['click', 'scroll', 'keypress', 'mousemove', 'touchstart'];\n\n const activityHandler = () => this.onActivity();\n\n events.forEach((event) => {\n window.addEventListener(event, activityHandler, { passive: true });\n });\n\n window.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n this.onActivity();\n }\n });\n }\n\n private startExpiryCheck(): void {\n this.checkInterval = window.setInterval(() => {\n this.checkSessionExpiry();\n }, 60000);\n }\n\n private onActivity(): void {\n const now = Date.now();\n const timeSinceLastUpdate = now - this.lastActivityUpdate;\n\n if (timeSinceLastUpdate < this.activityThrottle) {\n return;\n }\n\n this.lastActivityUpdate = now;\n const timeSinceLastActivity = now - this.lastActivityTime;\n\n if (timeSinceLastActivity > this.sessionTimeout) {\n logger.info('Session expired due to inactivity, creating new session');\n this.sessionId = this.createNewSession();\n } else {\n this.lastActivityTime = now;\n this.persistSession();\n }\n }\n\n private checkSessionExpiry(): void {\n const elapsed = Date.now() - this.lastActivityTime;\n\n if (elapsed > this.sessionTimeout) {\n logger.info('Session expired during check, creating new session');\n this.sessionId = this.createNewSession();\n }\n }\n\n getSessionId(): string {\n return this.sessionId;\n }\n\n incrementEventCount(): void {\n this.eventCount++;\n this.lastActivityTime = Date.now();\n this.persistSession();\n }\n\n getSessionDuration(): number {\n return Date.now() - this.sessionStartTime;\n }\n\n getEventCount(): number {\n return this.eventCount;\n }\n\n getState(): SessionState {\n return {\n sessionId: this.sessionId,\n startTime: this.sessionStartTime,\n lastActivityTime: this.lastActivityTime,\n eventCount: this.eventCount,\n adClickIDs: this.adClickIDs,\n landingPage: this.landingPage,\n linkAttribution: this.linkAttribution,\n };\n }\n\n setAdClickIDs(adClickIDs: AdClickIDs, landingPage?: string): void {\n if (Object.keys(adClickIDs).length > 0) {\n this.adClickIDs = { ...this.adClickIDs, ...adClickIDs };\n if (landingPage) {\n this.landingPage = landingPage;\n }\n this.persistSession();\n // Also persist DURABLY (365d, like anon_id/user_id) — NOT only in the\n // 30-min session. An ad click must survive to a LATER-session login on\n // the same device: the visitor clicks anonymously, returns/logs in days\n // later, identify() re-injects these ids → the click is attributed. If\n // they lived only in the session they'd expire in 30 min of inactivity,\n // long before the platform attribution window (7-90d). Keyed durably so\n // getAdClickIDs() hydrates them on a fresh session.\n try {\n this.storage.set('ad_click_ids', JSON.stringify(this.adClickIDs), 365);\n if (this.landingPage) {\n this.storage.set('ad_landing_page', this.landingPage, 365);\n }\n } catch {\n // best-effort; the session copy still works in-session\n }\n logger.info('Ad click IDs stored (session + durable):', this.adClickIDs);\n }\n }\n\n getAdClickIDs(): AdClickIDs {\n // Hydrate from durable storage when the (30-min) session had none — this is\n // what lets a later-session login attribute an earlier anonymous ad click.\n if (Object.keys(this.adClickIDs).length === 0) {\n try {\n const durable = this.storage.get('ad_click_ids');\n if (durable) {\n const parsed = JSON.parse(durable) as AdClickIDs;\n if (parsed && Object.keys(parsed).length > 0) {\n this.adClickIDs = parsed;\n if (!this.landingPage) {\n this.landingPage = this.storage.get('ad_landing_page') || undefined;\n }\n }\n }\n } catch {\n // ignore parse/storage errors — fall back to the in-memory value\n }\n }\n return this.adClickIDs;\n }\n\n getLandingPage(): string | undefined {\n return this.landingPage;\n }\n\n /**\n * Record the landing's Aegis link attribution. LAST touch: a new `aegis_code`\n * replaces the previous set whole — never merged, because the fields are the\n * attributes of ONE link and a mix of two links describes neither.\n * Session + durable (30d, the server's short-link attribution window) so a\n * checkout in a later session can still name the send that brought them.\n */\n setLinkAttribution(attribution: LinkAttribution): void {\n if (!attribution.aegis_code) return;\n this.linkAttribution = { ...attribution };\n this.persistSession();\n try {\n this.storage.set(\n 'link_attribution',\n JSON.stringify(this.linkAttribution),\n LINK_ATTRIBUTION_DURABLE_DAYS,\n );\n } catch {\n // best-effort; the session copy still works in-session\n }\n }\n\n getLinkAttribution(): LinkAttribution {\n if (!this.linkAttribution.aegis_code) {\n try {\n const durable = this.storage.get('link_attribution');\n if (durable) {\n const parsed = JSON.parse(durable) as LinkAttribution;\n if (parsed && parsed.aegis_code) {\n this.linkAttribution = parsed;\n }\n }\n } catch {\n // ignore parse/storage errors — fall back to the in-memory value\n }\n }\n return this.linkAttribution;\n }\n\n destroy(): void {\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n }\n }\n}\n","/**\n * Meta Pixel companion cookies (_fbp, _fbc) — B1.2 of META_CAPI_PHASE1_TRACKER.\n *\n * We do NOT fire `fbq()` browser-side. We write the cookies Meta uses to\n * identify the same browser so server-side CAPI events (forwarded from\n * `meta_conversions_api_service.py`) carry the high-EMQ match keys\n * (fbp + fbc) without the cost of running a full Meta Pixel.\n *\n * Cookie spec (Meta docs, verified May 2026):\n * _fbp = fb.<subdomain_index>.<creation_ms>.<random>\n * _fbc = fb.<subdomain_index>.<click_ms>.<fbclid>\n * subdomain_index = number of dots in cookie domain\n * (example.com → 1, sub.example.com → 2)\n * random = 10-digit random integer\n * Lifetime: 90 days (Meta's default; longer doesn't increase EMQ)\n *\n * Reasoning for raw document.cookie writes (instead of utils/storage.ts):\n * The Storage helper prefixes every key with `aegis_` so writes go to\n * `aegis__fbp` / `aegis__fbc`. Meta only honors the exact names\n * `_fbp` and `_fbc`. We bypass the helper and write directly.\n *\n * Domain scope: written to the eTLD+1 (root domain) so a tenant who\n * later wires their own Meta Pixel via GTM sees the same cookies a\n * subdomain-rooted set would not.\n *\n * SSR-safe: every helper is no-op when `document` / `window` are\n * undefined. Callers don't need to guard.\n */\n\nconst FBP_COOKIE = '_fbp';\nconst FBC_COOKIE = '_fbc';\nconst COOKIE_LIFETIME_DAYS = 90;\n\nexport interface MetaCookies {\n fbp: string | null;\n fbc: string | null;\n fbclid: string | null;\n}\n\n/** Read a cookie value or null. SSR-safe. */\nfunction readCookie(name: string): string | null {\n if (typeof document === 'undefined') return null;\n try {\n const target = `${name}=`;\n const parts = document.cookie.split(';');\n for (let i = 0; i < parts.length; i++) {\n let c = parts[i];\n while (c.charAt(0) === ' ') c = c.substring(1);\n if (c.indexOf(target) === 0) {\n return decodeURIComponent(c.substring(target.length));\n }\n }\n } catch {\n /* swallow — cookie access can throw in cross-origin / sandboxed iframes */\n }\n return null;\n}\n\n/** Write a cookie at root-domain scope so cross-subdomain reads work. SSR-safe. */\nfunction writeCookie(name: string, value: string, days: number): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return;\n try {\n const expiry = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toUTCString();\n const domain = getRootDomain();\n const isHttps = window.location.protocol === 'https:';\n let cookieStr = `${name}=${encodeURIComponent(value)};expires=${expiry};path=/;SameSite=Lax`;\n if (isHttps) cookieStr += ';Secure';\n if (domain) cookieStr += `;domain=${domain}`;\n document.cookie = cookieStr;\n } catch {\n /* ignore — best effort */\n }\n}\n\n/**\n * Get eTLD+1 of current host. Returns null on localhost / IP / single-label\n * hosts so we fall through to host-only cookie scope (browser default).\n *\n * Heuristic, not PSL-perfect: produces `example.com` for `a.b.example.com`.\n * Edge cases (`co.uk` etc.) get the wrong answer, but Meta itself uses\n * the same heuristic when their own pixel writes _fbp, so we stay aligned.\n */\nfunction getRootDomain(): string | null {\n if (typeof window === 'undefined') return null;\n const host = window.location.hostname;\n if (!host || host === 'localhost') return null;\n // IP address — host-only cookie scope.\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(host)) return null;\n const parts = host.split('.');\n if (parts.length < 2) return null;\n return '.' + parts.slice(-2).join('.');\n}\n\n/**\n * Subdomain index per Meta spec — count of dots in the cookie domain.\n * Counts components, not literal dots:\n * example.com → 1\n * sub.example.com → 2\n * sub.sub.example.com → 3\n */\nfunction subdomainIndex(): number {\n if (typeof window === 'undefined') return 1;\n const host = window.location.hostname;\n if (!host || host === 'localhost' || /^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(host)) return 1;\n const parts = host.split('.');\n return Math.max(1, parts.length - 1);\n}\n\n/** 10-digit random integer (Meta's spec format). */\nfunction randomToken(): string {\n // Math.random() * 1e10 produces a 10-digit-ish integer; pad-left to be safe.\n const n = Math.floor(Math.random() * 1e10);\n return n.toString().padStart(10, '0');\n}\n\n/**\n * Read `_fbp` from cookies, write a fresh one if absent.\n * Returns the resulting `_fbp` value or null if cookies unavailable.\n */\nexport function ensureFbp(): string | null {\n const existing = readCookie(FBP_COOKIE);\n if (existing) return existing;\n const fbp = `fb.${subdomainIndex()}.${Date.now()}.${randomToken()}`;\n writeCookie(FBP_COOKIE, fbp, COOKIE_LIFETIME_DAYS);\n return fbp;\n}\n\n/**\n * Read `_fbc` from cookies. If absent and the URL carries `fbclid`, write\n * a fresh `_fbc` per Meta's spec. Returns the resulting value or null.\n *\n * Note: `_fbc` has no value to write without an `fbclid` source — unlike\n * `_fbp` which is browser-identity-only and can be self-generated.\n */\nexport function ensureFbc(): string | null {\n const existing = readCookie(FBC_COOKIE);\n if (existing) return existing;\n const fbclid = readFbclidFromUrl();\n if (!fbclid) return null;\n const fbc = `fb.${subdomainIndex()}.${Date.now()}.${fbclid}`;\n writeCookie(FBC_COOKIE, fbc, COOKIE_LIFETIME_DAYS);\n return fbc;\n}\n\n/** Pull `fbclid` from `?fbclid=...` query string. Null if absent or SSR. */\nexport function readFbclidFromUrl(): string | null {\n if (typeof window === 'undefined') return null;\n try {\n return new URLSearchParams(window.location.search).get('fbclid');\n } catch {\n return null;\n }\n}\n\n/**\n * Single call that returns `{fbp, fbc, fbclid}` for injection into every\n * event's `properties` (which lands in ClickHouse `in_app_events.metadata`,\n * which the cell-side CAPI service reads directly).\n *\n * Side effects: ensures both cookies are present before returning. Idempotent\n * — safe to call on every event capture.\n */\nexport function captureMetaCookies(): MetaCookies {\n return {\n fbp: ensureFbp(),\n fbc: ensureFbc(),\n fbclid: readFbclidFromUrl(),\n };\n}\n\n/**\n * A copy of `event` safe to hand to a logger: `properties.fbc` / `properties.fbp`\n * masked. They are Meta cookie values — match keys for CAPI only, and must\n * stay out of every log line (ACS tracking tracker §V.3). The event itself is\n * never mutated; only the logged copy is.\n */\nexport function redactMetaCookies<T>(event: T): T {\n const props = (event as { properties?: Record<string, unknown> } | null)?.properties;\n if (!props || (props.fbc === undefined && props.fbp === undefined)) return event;\n const masked: Record<string, unknown> = { ...props };\n if (masked.fbc !== undefined) masked.fbc = '[redacted]';\n if (masked.fbp !== undefined) masked.fbp = '[redacted]';\n return { ...(event as object), properties: masked } as T;\n}\n","import { AegisEvent } from '../types/events';\nimport { Transport } from './transport';\nimport { Storage } from '../utils/storage';\nimport { logger } from '../utils/logger';\nimport { redactMetaCookies } from '../utils/meta-cookies';\n\nexport interface QueueConfig {\n batchSize: number;\n batchInterval: number;\n enableOfflineMode: boolean;\n maxOfflineEvents: number;\n}\n\nexport class EventQueue {\n private buffer: AegisEvent[] = [];\n private config: QueueConfig;\n private transport: Transport;\n private storage: Storage;\n private flushInterval: number | null = null;\n private isFlushing: boolean = false;\n private offlineQueue: AegisEvent[] = [];\n private isOnline: boolean = navigator.onLine;\n\n constructor(config: QueueConfig, transport: Transport, storage: Storage) {\n this.config = config;\n this.transport = transport;\n this.storage = storage;\n\n if (config.enableOfflineMode) {\n this.loadOfflineQueue();\n this.setupOnlineListener();\n }\n\n this.startFlushTimer();\n this.setupUnloadHandlers();\n }\n\n private loadOfflineQueue(): void {\n const offlineData = this.storage.get('offline_queue');\n\n if (!offlineData) {\n return;\n }\n\n try {\n const events = JSON.parse(offlineData) as AegisEvent[];\n\n if (Array.isArray(events) && events.length > 0) {\n this.offlineQueue = events.slice(0, this.config.maxOfflineEvents);\n logger.info(`Loaded ${this.offlineQueue.length} offline events`);\n\n if (this.isOnline) {\n this.flushOfflineQueue();\n }\n }\n } catch (error) {\n logger.warn('Failed to load offline queue:', error);\n this.storage.remove('offline_queue');\n }\n }\n\n private saveOfflineQueue(): void {\n if (this.offlineQueue.length === 0) {\n this.storage.remove('offline_queue');\n return;\n }\n\n try {\n const data = JSON.stringify(this.offlineQueue);\n this.storage.set('offline_queue', data);\n logger.debug(`Saved ${this.offlineQueue.length} events to offline queue`);\n } catch (error) {\n logger.warn('Failed to save offline queue:', error);\n }\n }\n\n private setupOnlineListener(): void {\n window.addEventListener('online', () => {\n logger.info('Connection restored');\n this.isOnline = true;\n this.flushOfflineQueue();\n this.flush();\n });\n\n window.addEventListener('offline', () => {\n logger.warn('Connection lost, entering offline mode');\n this.isOnline = false;\n });\n }\n\n private async flushOfflineQueue(): Promise<void> {\n if (this.offlineQueue.length === 0) {\n return;\n }\n\n logger.info(`Flushing ${this.offlineQueue.length} offline events`);\n\n const events = [...this.offlineQueue];\n this.offlineQueue = [];\n this.storage.remove('offline_queue');\n\n const result = await this.transport.send(events);\n\n if (!result.success) {\n logger.warn('Failed to send offline events, re-queueing');\n this.offlineQueue = [...events, ...this.offlineQueue].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n } else {\n logger.info('Offline events sent successfully');\n }\n }\n\n push(event: AegisEvent): void {\n if (!this.isOnline && this.config.enableOfflineMode) {\n this.addToOfflineQueue(event);\n return;\n }\n\n this.buffer.push(event);\n logger.debug('Event queued:', event.type, redactMetaCookies(event));\n\n if (this.buffer.length >= this.config.batchSize) {\n this.flush();\n }\n }\n\n private addToOfflineQueue(event: AegisEvent): void {\n if (this.offlineQueue.length >= this.config.maxOfflineEvents) {\n logger.warn('Offline queue full, dropping oldest event');\n this.offlineQueue.shift();\n }\n\n this.offlineQueue.push(event);\n this.saveOfflineQueue();\n logger.debug('Event added to offline queue');\n }\n\n async flush(): Promise<void> {\n if (this.isFlushing) {\n logger.debug('Flush already in progress, skipping');\n return;\n }\n\n if (this.buffer.length === 0) {\n return;\n }\n\n this.isFlushing = true;\n\n const events = [...this.buffer];\n this.buffer = [];\n\n logger.info(`Flushing ${events.length} events`);\n\n try {\n const result = await this.transport.send(events);\n\n if (!result.success) {\n logger.error('Failed to send events:', result.error);\n\n if (this.config.enableOfflineMode) {\n this.offlineQueue = [...this.offlineQueue, ...events].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n logger.info('Events saved to offline queue');\n } else {\n this.buffer.unshift(...events);\n logger.warn('Events re-queued for retry');\n }\n } else {\n logger.info('Events sent successfully');\n }\n } catch (error) {\n logger.error('Flush error:', error);\n\n if (this.config.enableOfflineMode) {\n this.offlineQueue = [...this.offlineQueue, ...events].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n }\n } finally {\n this.isFlushing = false;\n }\n }\n\n private startFlushTimer(): void {\n if (this.flushInterval) {\n clearInterval(this.flushInterval);\n }\n\n this.flushInterval = window.setInterval(() => {\n this.flush();\n }, this.config.batchInterval);\n\n logger.debug(\n `Flush timer started with interval: ${this.config.batchInterval}ms`\n );\n }\n\n private setupUnloadHandlers(): void {\n const flushBeforeUnload = () => {\n if (this.buffer.length > 0 || this.offlineQueue.length > 0) {\n this.persistBufferToOfflineQueue();\n this.flush();\n }\n };\n\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n flushBeforeUnload();\n }\n });\n\n window.addEventListener('beforeunload', flushBeforeUnload);\n\n window.addEventListener('pagehide', flushBeforeUnload);\n }\n\n private persistBufferToOfflineQueue(): void {\n if (this.buffer.length === 0) {\n return;\n }\n\n if (this.config.enableOfflineMode) {\n this.offlineQueue = [...this.offlineQueue, ...this.buffer].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n logger.debug('Buffer persisted to offline queue before unload');\n }\n }\n\n getQueueSize(): number {\n return this.buffer.length;\n }\n\n getOfflineQueueSize(): number {\n return this.offlineQueue.length;\n }\n\n clear(): void {\n this.buffer = [];\n this.offlineQueue = [];\n this.storage.remove('offline_queue');\n logger.info('Queue cleared');\n }\n\n destroy(): void {\n if (this.flushInterval) {\n clearInterval(this.flushInterval);\n this.flushInterval = null;\n }\n\n this.flush();\n }\n}\n","import { AegisEvent, BatchPayload, BatchResponse } from '../types/events';\nimport { CellEndpoint, CellRegion } from '../types/config';\nimport { logger } from '../utils/logger';\nimport { Storage } from '../utils/storage';\n\nexport interface TransportConfig {\n writeKey: string;\n apiHost: string | null;\n cellEndpoints: CellEndpoint[];\n preferredRegion: CellRegion | null;\n autoRegionDetection: boolean;\n requestTimeout: number;\n retryFailedRequests: boolean;\n maxRetries: number;\n retryBackoffMultiplier: number;\n}\n\nexport interface SendResult {\n success: boolean;\n response?: BatchResponse;\n error?: Error;\n endpoint?: string;\n}\n\nexport class Transport {\n private config: TransportConfig;\n private storage: Storage;\n private activeCell: CellEndpoint | null = null;\n private healthCheckInterval: number | null = null;\n private regionLatencyMap: Map<string, number> = new Map();\n\n constructor(config: TransportConfig, storage: Storage) {\n this.config = config;\n this.storage = storage;\n\n if (config.cellEndpoints.length > 0) {\n this.initializeCellularArchitecture();\n }\n }\n\n private async initializeCellularArchitecture(): Promise<void> {\n const cachedCell = this.loadCachedCell();\n\n if (cachedCell && this.isCellHealthy(cachedCell)) {\n this.activeCell = cachedCell;\n logger.info('Using cached cell:', cachedCell);\n } else {\n await this.selectOptimalCell();\n }\n\n this.startHealthChecks();\n }\n\n private loadCachedCell(): CellEndpoint | null {\n const cached = this.storage.get('active_cell');\n\n if (!cached) {\n return null;\n }\n\n try {\n return JSON.parse(cached) as CellEndpoint;\n } catch (error) {\n logger.warn('Failed to parse cached cell:', error);\n return null;\n }\n }\n\n private saveCachedCell(cell: CellEndpoint): void {\n this.storage.set('active_cell', JSON.stringify(cell), 7);\n }\n\n private isCellHealthy(cell: CellEndpoint): boolean {\n return (\n cell.healthy &&\n this.config.cellEndpoints.some(\n (c) => c.region === cell.region && c.url === cell.url\n )\n );\n }\n\n private async selectOptimalCell(): Promise<void> {\n logger.info('Selecting optimal cell...');\n\n if (this.config.preferredRegion) {\n const preferredCell = this.config.cellEndpoints.find(\n (cell) => cell.region === this.config.preferredRegion && cell.healthy\n );\n\n if (preferredCell) {\n this.activeCell = preferredCell;\n this.saveCachedCell(preferredCell);\n logger.info('Using preferred region cell:', preferredCell);\n return;\n }\n }\n\n if (this.config.autoRegionDetection) {\n await this.detectOptimalRegion();\n } else {\n this.selectCellByPriority();\n }\n }\n\n private async detectOptimalRegion(): Promise<void> {\n const healthyCells = this.config.cellEndpoints.filter((cell) => cell.healthy);\n\n if (healthyCells.length === 0) {\n logger.error('No healthy cells available');\n return;\n }\n\n const latencyPromises = healthyCells.map(async (cell) => {\n const latency = await this.measureLatency(cell);\n this.regionLatencyMap.set(cell.region, latency);\n return { cell, latency };\n });\n\n const results = await Promise.all(latencyPromises);\n results.sort((a, b) => a.latency - b.latency);\n\n const optimalCell = results[0].cell;\n this.activeCell = optimalCell;\n this.saveCachedCell(optimalCell);\n\n logger.info('Optimal cell selected:', {\n region: optimalCell.region,\n latency: results[0].latency,\n });\n }\n\n private async measureLatency(cell: CellEndpoint): Promise<number> {\n const startTime = performance.now();\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 2000);\n\n const response = await fetch(`${cell.url}/health`, {\n method: 'GET',\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return performance.now() - startTime;\n } else {\n return Infinity;\n }\n } catch (error) {\n logger.warn(`Health check failed for ${cell.region}:`, error);\n return Infinity;\n }\n }\n\n private selectCellByPriority(): void {\n const sortedCells = [...this.config.cellEndpoints]\n .filter((cell) => cell.healthy)\n .sort((a, b) => a.priority - b.priority);\n\n if (sortedCells.length > 0) {\n this.activeCell = sortedCells[0];\n this.saveCachedCell(sortedCells[0]);\n logger.info('Cell selected by priority:', sortedCells[0]);\n } else {\n logger.error('No healthy cells available');\n }\n }\n\n private startHealthChecks(): void {\n this.healthCheckInterval = window.setInterval(() => {\n this.performHealthChecks();\n }, 60000);\n }\n\n private async performHealthChecks(): Promise<void> {\n const healthPromises = this.config.cellEndpoints.map(async (cell) => {\n const isHealthy = await this.checkCellHealth(cell);\n cell.healthy = isHealthy;\n return { cell, isHealthy };\n });\n\n const results = await Promise.all(healthPromises);\n\n if (this.activeCell && !this.activeCell.healthy) {\n logger.warn('Active cell unhealthy, selecting new cell');\n await this.selectOptimalCell();\n }\n\n logger.debug('Health check results:', results);\n }\n\n private async checkCellHealth(cell: CellEndpoint): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 3000);\n\n const response = await fetch(`${cell.url}/health`, {\n method: 'GET',\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n return response.ok;\n } catch (error) {\n return false;\n }\n }\n\n async send(events: AegisEvent[]): Promise<SendResult> {\n if (events.length === 0) {\n return { success: true };\n }\n\n const endpoint = this.getActiveEndpoint();\n const payload = this.buildPayload(events);\n\n logger.debug('Sending batch:', { endpoint, eventCount: events.length });\n\n if (document.visibilityState === 'hidden' && typeof navigator.sendBeacon === 'function') {\n const result = this.sendViaBeacon(endpoint, payload);\n return Promise.resolve(result);\n } else {\n return this.sendViaFetch(endpoint, payload);\n }\n }\n\n private getActiveEndpoint(): string {\n if (this.activeCell) {\n return `${this.activeCell.url}/v1/batch`;\n }\n\n if (this.config.apiHost) {\n return `${this.config.apiHost}/v1/batch`;\n }\n\n throw new Error('No active endpoint available');\n }\n\n private buildPayload(events: AegisEvent[]): BatchPayload {\n return {\n batch: events,\n sentAt: new Date().toISOString(),\n writeKey: this.config.writeKey,\n context: this.activeCell\n ? {\n cell: {\n region: this.activeCell.region,\n endpoint: this.activeCell.url,\n },\n }\n : undefined,\n };\n }\n\n private sendViaBeacon(endpoint: string, payload: BatchPayload): SendResult | Promise<SendResult> {\n try {\n const blob = new Blob([JSON.stringify(payload)], {\n type: 'application/json',\n });\n\n const sent = navigator.sendBeacon(endpoint, blob);\n\n if (sent) {\n logger.debug('Batch sent via Beacon');\n return { success: true, endpoint };\n } else {\n logger.warn('Beacon send failed, falling back to fetch');\n return this.sendViaFetch(endpoint, payload);\n }\n } catch (error) {\n logger.error('Beacon send error:', error);\n return {\n success: false,\n error: error as Error,\n endpoint,\n };\n }\n }\n\n private async sendViaFetch(\n endpoint: string,\n payload: BatchPayload,\n attempt: number = 1\n ): Promise<SendResult> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n this.config.requestTimeout\n );\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.writeKey}`,\n },\n body: JSON.stringify(payload),\n keepalive: true,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n const result: BatchResponse = await response.json();\n logger.debug('Batch sent successfully:', result);\n return { success: true, response: result, endpoint };\n } else {\n const errorText = await response.text();\n const error = new Error(\n `HTTP ${response.status}: ${errorText || response.statusText}`\n );\n\n logger.error('Batch send failed:', error);\n\n if (this.shouldRetry(response.status, attempt)) {\n return this.retryRequest(endpoint, payload, attempt);\n }\n\n return { success: false, error, endpoint };\n }\n } catch (error) {\n logger.error('Fetch error:', error);\n\n if (this.shouldRetry(0, attempt)) {\n return this.retryRequest(endpoint, payload, attempt);\n }\n\n return {\n success: false,\n error: error as Error,\n endpoint,\n };\n }\n }\n\n private shouldRetry(statusCode: number, attempt: number): boolean {\n if (!this.config.retryFailedRequests) {\n return false;\n }\n\n if (attempt >= this.config.maxRetries) {\n return false;\n }\n\n if (statusCode === 0) {\n return true;\n }\n\n return statusCode >= 500 || statusCode === 429;\n }\n\n private async retryRequest(\n endpoint: string,\n payload: BatchPayload,\n attempt: number\n ): Promise<SendResult> {\n const delay =\n Math.pow(this.config.retryBackoffMultiplier, attempt) * 1000;\n\n logger.info(`Retrying request in ${delay}ms (attempt ${attempt + 1})`);\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n return this.sendViaFetch(endpoint, payload, attempt + 1);\n }\n\n getActiveCell(): CellEndpoint | null {\n return this.activeCell;\n }\n\n getRegionLatencies(): Map<string, number> {\n return new Map(this.regionLatencyMap);\n }\n\n destroy(): void {\n if (this.healthCheckInterval) {\n clearInterval(this.healthCheckInterval);\n this.healthCheckInterval = null;\n }\n }\n}\n","const STORAGE_PREFIX = 'aegis_';\n\nexport interface StorageOptions {\n cookieDomain?: string | null;\n secureCookie?: boolean;\n crossDomain?: boolean;\n}\n\nexport class Storage {\n private useLocalStorage: boolean;\n private options: StorageOptions;\n\n constructor(options: StorageOptions = {}) {\n this.options = options;\n this.useLocalStorage = this.isLocalStorageAvailable();\n }\n\n private isLocalStorageAvailable(): boolean {\n try {\n const test = '__aegis_test__';\n localStorage.setItem(test, test);\n localStorage.removeItem(test);\n return true;\n } catch {\n return false;\n }\n }\n\n set(key: string, value: string, expiryDays: number = 365): void {\n const fullKey = STORAGE_PREFIX + key;\n\n if (this.useLocalStorage) {\n try {\n const item = {\n value,\n expiry: Date.now() + expiryDays * 24 * 60 * 60 * 1000,\n };\n localStorage.setItem(fullKey, JSON.stringify(item));\n } catch (error) {\n console.warn('[Active Reach Storage] localStorage.setItem failed:', error);\n }\n }\n\n this.setCookie(fullKey, value, expiryDays);\n }\n\n get(key: string): string | null {\n const fullKey = STORAGE_PREFIX + key;\n\n if (this.useLocalStorage) {\n try {\n const itemStr = localStorage.getItem(fullKey);\n if (itemStr) {\n const item = JSON.parse(itemStr);\n if (Date.now() < item.expiry) {\n return item.value;\n } else {\n localStorage.removeItem(fullKey);\n }\n }\n } catch (error) {\n console.warn('[Active Reach Storage] localStorage.getItem failed:', error);\n }\n }\n\n return this.getCookie(fullKey);\n }\n\n remove(key: string): void {\n const fullKey = STORAGE_PREFIX + key;\n\n if (this.useLocalStorage) {\n try {\n localStorage.removeItem(fullKey);\n } catch (error) {\n console.warn('[Active Reach Storage] localStorage.removeItem failed:', error);\n }\n }\n\n this.deleteCookie(fullKey);\n }\n\n clear(): void {\n if (this.useLocalStorage) {\n try {\n const keys = Object.keys(localStorage);\n keys.forEach((key) => {\n if (key.startsWith(STORAGE_PREFIX)) {\n localStorage.removeItem(key);\n }\n });\n } catch (error) {\n console.warn('[Active Reach Storage] localStorage.clear failed:', error);\n }\n }\n\n const cookies = document.cookie.split(';');\n cookies.forEach((cookie) => {\n const key = cookie.split('=')[0].trim();\n if (key.startsWith(STORAGE_PREFIX)) {\n this.deleteCookie(key);\n }\n });\n }\n\n private setCookie(name: string, value: string, days: number): void {\n try {\n const date = new Date();\n date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);\n const expires = `expires=${date.toUTCString()}`;\n \n let cookieStr = `${name}=${value};${expires};path=/`;\n \n if (this.options.secureCookie && window.location.protocol === 'https:') {\n cookieStr += ';Secure';\n }\n \n cookieStr += ';SameSite=Lax';\n \n if (this.options.cookieDomain) {\n cookieStr += `;domain=${this.options.cookieDomain}`;\n } else if (this.options.crossDomain) {\n const domain = this.getRootDomain();\n if (domain) {\n cookieStr += `;domain=${domain}`;\n }\n }\n \n document.cookie = cookieStr;\n } catch (error) {\n console.warn('[Active Reach Storage] setCookie failed:', error);\n }\n }\n\n private getCookie(name: string): string | null {\n try {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n \n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) === ' ') {\n c = c.substring(1, c.length);\n }\n if (c.indexOf(nameEQ) === 0) {\n return c.substring(nameEQ.length, c.length);\n }\n }\n } catch (error) {\n console.warn('[Active Reach Storage] getCookie failed:', error);\n }\n \n return null;\n }\n\n private deleteCookie(name: string): void {\n try {\n let cookieStr = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/`;\n \n if (this.options.cookieDomain) {\n cookieStr += `;domain=${this.options.cookieDomain}`;\n } else if (this.options.crossDomain) {\n const domain = this.getRootDomain();\n if (domain) {\n cookieStr += `;domain=${domain}`;\n }\n }\n \n document.cookie = cookieStr;\n } catch (error) {\n console.warn('[Active Reach Storage] deleteCookie failed:', error);\n }\n }\n\n private getRootDomain(): string | null {\n try {\n const hostname = window.location.hostname;\n \n if (/^(\\d{1,3}\\.){3}\\d{1,3}$/.test(hostname)) {\n return null;\n }\n \n if (hostname === 'localhost') {\n return null;\n }\n \n const parts = hostname.split('.');\n \n if (parts.length <= 2) {\n return `.${hostname}`;\n }\n \n return `.${parts.slice(-2).join('.')}`;\n } catch (error) {\n console.warn('[Active Reach Storage] getRootDomain failed:', error);\n return null;\n }\n }\n}\n","/**\n * URL credential redaction — the browser-SDK layer (Aman 2026-09-13).\n *\n * A landing URL can carry a credential: `k` (one order's view key), `t` (the\n * server-verified landing token), `code` / `session_code` (claim codes),\n * `aegis_pair` (a device-pairing code) and `u` (a contact token). The SDK must\n * never SEND one of those values — not in a page object, an event property, a\n * beacon or a query string to our own API. What it sends instead is the key\n * with its value replaced by `REDACTED`, so the shape of the URL (and the fact\n * that a credential was present) survives while the secret does not.\n *\n * The server twin is `libs/data-governance/data_governance/credential_params.py`\n * (`CREDENTIAL_URL_PARAMS`, `REDACTED`). The two lists MUST stay identical; a\n * parity guard compares them. Change both in the same commit or neither.\n *\n * Deliberately NOT in the list: `aegis_code`. It is the attribution join key\n * (the short link's own code), not a credential — redacting it would sever\n * the link from the order it brought in.\n *\n * Reading is untouched. The SDK's own inbound param readers (`aegis_code`,\n * gclid / fbclid, utm, `aegis_pair` for pairing) still read `location.search`\n * directly; only what leaves the browser is redacted.\n *\n * Why a targeted regex and not `new URL()`: a URL round-trip re-encodes and\n * reorders the rest of the string, so the value we send would no longer be\n * the page the visitor was on. The regex touches only the credential values.\n *\n * What it does not see: a percent-encoded key (`%6B=` for `k`), a `;`\n * separator, or a key whose case differs (`K=`). The server list has the same\n * shape, so both layers agree on what counts as a match.\n */\n\nexport const CREDENTIAL_URL_PARAMS = [\n 't',\n 'k',\n 'code',\n 'session_code',\n 'aegis_pair',\n 'u',\n // The Shopify CarrierService callback token (2026-09-18). Server-to-server\n // today, so the SDK never sees one — it is here because the two lists are\n // ONE rule, and a list that is only correct for today's callers is not.\n 'carrier_token',\n] as const;\n\nexport const REDACTED = 'REDACTED';\n\n// Byte-identical to the server's rule (credential_params.py):\n// - a key counts ONLY when introduced by `?` or `&` — `utm_t=` is not `t=`,\n// and prose such as \"k=3 cakes\" (even at the start of a string) is untouched;\n// - the value stops at `& # whitespace \" ' < >`, so a URL inside JSON, HTML\n// or a sentence keeps its closing quote / bracket / following words (the\n// first cut used `[^&#]*` and swallowed `\"}` out of `{\"href\":\"…?k=9\"}`);\n// - an EMPTY value is left alone — there is nothing to hide.\n// Shared cases: libs/data-governance/tests/fixtures/credential_param_cases.json.\nconst CREDENTIAL_PARAM_PATTERN = new RegExp(\n `([?&])(${CREDENTIAL_URL_PARAMS.join('|')})=([^&#\\\\s\"'<>]+)`,\n 'g',\n);\n\n/**\n * Replace the value of every credential query param with `REDACTED`.\n * Idempotent, fragment-preserving, and it never throws: input it cannot\n * handle is returned unchanged.\n */\nexport function redactUrl(value: string): string {\n if (typeof value !== 'string' || value.length === 0) return value;\n try {\n return value.replace(CREDENTIAL_PARAM_PATTERN, `$1$2=${REDACTED}`);\n } catch {\n return value;\n }\n}\n\n/**\n * Walk plain objects / arrays and apply `redactUrl` to every string — the TS\n * twin of the server's `redact_credentials_deep` (same depth cap, 12).\n *\n * Added for the SERVER consumers of `channel.delivery.status_{cell}` (BK-87,\n * 2026-09-13): that topic is a dual-route that never passes the governance\n * engine, and its click/receipt payloads carry URLs (`metadata.original_url`,\n * `referrer`, provider echoes). analytics-consumer and event-ingress's\n * delivery-status bridge import THIS module by relative path, so there is one\n * TS rule, not a copy. Redacting per string value (before serialisation) keeps\n * JSON valid even when a value itself holds escaped JSON.\n *\n * Only plain objects and arrays are walked; anything else (Date, Buffer, class\n * instances) is returned as is.\n */\nexport function redactCredentialsDeep<T>(value: T, depth = 0): T {\n if (depth > 12) return value;\n if (typeof value === 'string') return redactUrl(value) as unknown as T;\n if (Array.isArray(value)) {\n return value.map((v: unknown) => redactCredentialsDeep(v, depth + 1)) as unknown as T;\n }\n if (value !== null && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype) {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = redactCredentialsDeep(v, depth + 1);\n }\n return out as T;\n }\n return value;\n}\n","import { redactUrl } from './redact-url';\n\nexport interface UTMParams {\n source?: string;\n medium?: string;\n campaign?: string;\n term?: string;\n content?: string;\n name?: string;\n}\n\nexport function parseUTMParameters(url?: string): UTMParams {\n try {\n const searchParams = url\n ? new URL(url).searchParams\n : new URLSearchParams(window.location.search);\n\n const utm: UTMParams = {};\n\n const utmKeys: Array<keyof UTMParams> = [\n 'source',\n 'medium',\n 'campaign',\n 'term',\n 'content',\n 'name',\n ];\n\n utmKeys.forEach((key) => {\n const value = searchParams.get(`utm_${key}`);\n if (value) {\n utm[key] = value;\n }\n });\n\n return utm;\n } catch (error) {\n console.warn('[Active Reach URL Parser] parseUTMParameters failed:', error);\n return {};\n }\n}\n\nexport function parseQueryParameters(url?: string): Record<string, string> {\n try {\n const searchParams = url\n ? new URL(url).searchParams\n : new URLSearchParams(window.location.search);\n\n const params: Record<string, string> = {};\n\n searchParams.forEach((value, key) => {\n params[key] = value;\n });\n\n return params;\n } catch (error) {\n console.warn('[Active Reach URL Parser] parseQueryParameters failed:', error);\n return {};\n }\n}\n\n/**\n * The page's URL for REPORTING — so it is credential-redacted (`redactUrl`),\n * the canonical `<link>` included. No caller in the SDK today; redacted so a\n * future one cannot send a raw `?k=` / `?t=` by reaching for this helper.\n */\nexport function getCanonicalURL(): string {\n try {\n const canonical = document.querySelector('link[rel=\"canonical\"]');\n if (canonical && canonical.getAttribute('href')) {\n return redactUrl(canonical.getAttribute('href')!);\n }\n \n return redactUrl(window.location.href);\n } catch (error) {\n console.warn('[Active Reach URL Parser] getCanonicalURL failed:', error);\n return redactUrl(window.location.href);\n }\n}\n\nexport function stripQueryParameters(url: string): string {\n try {\n const urlObj = new URL(url);\n return `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`;\n } catch (error) {\n console.warn('[Active Reach URL Parser] stripQueryParameters failed:', error);\n return url;\n }\n}\n\nexport interface AdClickIDs {\n gclid?: string;\n /** Google click id for iOS app→web traffic (Apple ATT). NOT a gclid. */\n gbraid?: string;\n /** Google click id for iOS web→app traffic (Apple ATT). NOT a gclid. */\n wbraid?: string;\n fbclid?: string;\n msclkid?: string;\n ctwa_clid?: string;\n ttclid?: string;\n li_fat_id?: string;\n}\n\nexport function parseAdClickIDs(url?: string): AdClickIDs {\n try {\n const searchParams = url\n ? new URL(url).searchParams\n : new URLSearchParams(window.location.search);\n\n const adClickIDs: AdClickIDs = {};\n\n const clickIDKeys: Array<keyof AdClickIDs> = [\n 'gclid',\n 'gbraid',\n 'wbraid',\n 'fbclid',\n 'msclkid',\n 'ctwa_clid',\n 'ttclid',\n 'li_fat_id',\n ];\n\n clickIDKeys.forEach((key) => {\n const value = searchParams.get(key);\n if (value) {\n adClickIDs[key] = value;\n }\n });\n\n return adClickIDs;\n } catch (error) {\n console.warn('[Active Reach URL Parser] parseAdClickIDs failed:', error);\n return {};\n }\n}\n\nexport function hasAdClickIDs(adClickIDs: AdClickIDs): boolean {\n return Object.keys(adClickIDs).length > 0;\n}\n\n/**\n * Which Aegis send brought the visitor — the `aegis_*` params the short-link\n * redirect stamps from the LINK row (`link_shortener` `aegis_params`).\n *\n * `aegis_code` is the join key (the short link's own code); the send is then\n * `aegis_campaign` (campaign sends only) OR `aegis_journey` (journey sends —\n * order confirmation, delivery, win-back). Present only when the link row has\n * a value, so never assume `aegis_campaign` exists.\n *\n * Deliberately NOT read here:\n * - `utm_*` — the tenant's own / third-party analytics; never identifies an\n * Aegis send (a tenant-set utm can sit on our own link).\n * - `k` — a credential for ONE order. Never read, copied or logged.\n * - `t` — server-verified landing token; never parsed client-side.\n * - `u` — consumed at the redirect, never forwarded.\n */\nexport interface LinkAttribution {\n aegis_code?: string;\n aegis_campaign?: string;\n aegis_journey?: string;\n aegis_playbook?: string;\n aegis_button?: string;\n}\n\nconst LINK_ATTRIBUTION_KEYS: Array<keyof LinkAttribution> = [\n 'aegis_code',\n 'aegis_campaign',\n 'aegis_journey',\n 'aegis_playbook',\n 'aegis_button',\n];\n\n/**\n * Parse the landing URL's Aegis link attribution. Returns `{}` unless\n * `aegis_code` is present — the rest of the set only means something as the\n * attributes OF that code.\n */\nexport function parseLinkAttribution(url?: string): LinkAttribution {\n try {\n const searchParams = url\n ? new URL(url).searchParams\n : new URLSearchParams(window.location.search);\n\n if (!searchParams.get('aegis_code')) return {};\n\n const attribution: LinkAttribution = {};\n LINK_ATTRIBUTION_KEYS.forEach((key) => {\n const value = searchParams.get(key);\n if (value) {\n attribution[key] = value;\n }\n });\n return attribution;\n } catch (error) {\n console.warn('[Active Reach URL Parser] parseLinkAttribution failed:', error);\n return {};\n }\n}\n","import { EventContext } from '../types/events';\nimport { parseUTMParameters } from './url-parser';\nimport { redactUrl } from './redact-url';\nimport { AegisConfigInternal } from '../types/config';\nimport type { SessionManager } from '../core/session';\n\nexport function buildContext(config: AegisConfigInternal, session?: SessionManager): EventContext {\n const utm = config.capture_utm ? parseUTMParameters() : undefined;\n const device = detectDevice();\n const os = detectOS();\n const browser = detectBrowser();\n const network = detectNetworkInfo();\n\n const adClickIDs = session?.getAdClickIDs();\n // Redacted again on the way out, not only at capture: the landing page is\n // persisted for 365 days (`ad_landing_page`), so a value stored by an SDK\n // build that predates redaction would otherwise keep being sent.\n const storedLandingPage = session?.getLandingPage();\n const landingPage = storedLandingPage ? redactUrl(storedLandingPage) : undefined;\n\n return {\n library: {\n name: '@active-reach/web-sdk',\n version: '1.0.0',\n },\n // Credential params (`k`, `t`, `code`, ...) keep their key and lose their\n // value here, where the URL enters the page object. The referrer is\n // redacted too: the previous page can be our own order/landing link.\n page: {\n path: window.location.pathname,\n referrer: config.capture_referrer ? redactUrl(document.referrer) : '',\n search: redactUrl(window.location.search),\n title: document.title,\n url: redactUrl(window.location.href),\n hash: window.location.hash,\n },\n userAgent: navigator.userAgent,\n locale: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n screen: {\n width: window.screen.width,\n height: window.screen.height,\n density: window.devicePixelRatio || 1,\n },\n viewport: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n ...(utm && Object.keys(utm).length > 0 && { campaign: utm }),\n ...(adClickIDs && Object.keys(adClickIDs).length > 0 && { adClickIDs }),\n ...(landingPage && { landingPage }),\n ...(network && { network }),\n ...(device && { device }),\n ...(os && { os }),\n ...(browser && { browser }),\n ...(config.active_cell && {\n cell: {\n region: config.active_cell.region,\n endpoint: config.active_cell.url,\n },\n }),\n };\n}\n\nfunction detectDevice(): EventContext['device'] {\n const ua = navigator.userAgent;\n\n if (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\n return { type: 'tablet' };\n }\n\n if (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\n return { type: 'mobile' };\n }\n\n return { type: 'desktop' };\n}\n\nfunction detectOS(): EventContext['os'] {\n const ua = navigator.userAgent;\n\n if (/Windows NT 10/i.test(ua)) return { name: 'Windows', version: '10' };\n if (/Windows NT 6.3/i.test(ua)) return { name: 'Windows', version: '8.1' };\n if (/Windows NT 6.2/i.test(ua)) return { name: 'Windows', version: '8' };\n if (/Windows NT 6.1/i.test(ua)) return { name: 'Windows', version: '7' };\n if (/Windows/i.test(ua)) return { name: 'Windows', version: 'Unknown' };\n\n if (/Mac OS X (\\d+)[._](\\d+)/.test(ua)) {\n const match = ua.match(/Mac OS X (\\d+)[._](\\d+)/);\n return { name: 'macOS', version: `${match![1]}.${match![2]}` };\n }\n if (/Mac/i.test(ua)) return { name: 'macOS', version: 'Unknown' };\n\n if (/Android (\\d+\\.\\d+)/.test(ua)) {\n const match = ua.match(/Android (\\d+\\.\\d+)/);\n return { name: 'Android', version: match![1] };\n }\n if (/Android/i.test(ua)) return { name: 'Android', version: 'Unknown' };\n\n if (/iPhone OS (\\d+)_(\\d+)/.test(ua)) {\n const match = ua.match(/iPhone OS (\\d+)_(\\d+)/);\n return { name: 'iOS', version: `${match![1]}.${match![2]}` };\n }\n if (/iPad.*OS (\\d+)_(\\d+)/.test(ua)) {\n const match = ua.match(/iPad.*OS (\\d+)_(\\d+)/);\n return { name: 'iOS', version: `${match![1]}.${match![2]}` };\n }\n if (/iPhone|iPad/i.test(ua)) return { name: 'iOS', version: 'Unknown' };\n\n if (/Linux/i.test(ua)) return { name: 'Linux', version: 'Unknown' };\n\n return { name: 'Unknown', version: 'Unknown' };\n}\n\nfunction detectBrowser(): EventContext['browser'] {\n const ua = navigator.userAgent;\n\n if (/Edg\\/(\\d+\\.\\d+)/.test(ua)) {\n const match = ua.match(/Edg\\/(\\d+\\.\\d+)/);\n return { name: 'Edge', version: match![1] };\n }\n\n if (/Chrome\\/(\\d+\\.\\d+)/.test(ua) && !/Edg/.test(ua)) {\n const match = ua.match(/Chrome\\/(\\d+\\.\\d+)/);\n return { name: 'Chrome', version: match![1] };\n }\n\n if (/Firefox\\/(\\d+\\.\\d+)/.test(ua)) {\n const match = ua.match(/Firefox\\/(\\d+\\.\\d+)/);\n return { name: 'Firefox', version: match![1] };\n }\n\n if (/Safari\\/(\\d+\\.\\d+)/.test(ua) && !/Chrome/.test(ua)) {\n const match = ua.match(/Version\\/(\\d+\\.\\d+)/);\n return { name: 'Safari', version: match ? match[1] : 'Unknown' };\n }\n\n if (/MSIE (\\d+\\.\\d+)/.test(ua) || /Trident\\//.test(ua)) {\n const match = ua.match(/MSIE (\\d+\\.\\d+)/) || ua.match(/rv:(\\d+\\.\\d+)/);\n return { name: 'Internet Explorer', version: match ? match[1] : 'Unknown' };\n }\n\n return { name: 'Unknown', version: 'Unknown' };\n}\n\nfunction detectNetworkInfo(): EventContext['network'] | undefined {\n try {\n const connection =\n (navigator as any).connection ||\n (navigator as any).mozConnection ||\n (navigator as any).webkitConnection;\n\n if (connection) {\n return {\n effectiveType: connection.effectiveType,\n downlink: connection.downlink,\n rtt: connection.rtt,\n };\n }\n } catch (error) {\n console.warn('[Active Reach Device] detectNetworkInfo failed:', error);\n }\n\n return undefined;\n}\n\nexport function isBot(): boolean {\n const ua = navigator.userAgent.toLowerCase();\n const botPatterns = [\n 'bot',\n 'crawl',\n 'spider',\n 'slurp',\n 'mediapartners',\n 'googlebot',\n 'bingbot',\n 'yahoo',\n 'duckduckbot',\n 'baiduspider',\n 'yandex',\n 'facebookexternalhit',\n 'linkedinbot',\n 'twitterbot',\n 'slackbot',\n 'telegrambot',\n 'whatsapp',\n 'pingdom',\n 'uptimerobot',\n ];\n\n return botPatterns.some((pattern) => ua.includes(pattern));\n}\n","import { Plugin } from '../types/plugin';\nimport { logger } from '../utils/logger';\n\nexport class PluginRegistry {\n private plugins: Map<string, Plugin> = new Map();\n\n register(plugin: Plugin): void {\n if (this.plugins.has(plugin.name)) {\n logger.warn(`Plugin \"${plugin.name}\" already registered, skipping`);\n return;\n }\n\n this.plugins.set(plugin.name, plugin);\n logger.info(`Plugin registered: ${plugin.name} v${plugin.version}`);\n }\n\n unregister(pluginName: string): void {\n const plugin = this.plugins.get(pluginName);\n\n if (!plugin) {\n logger.warn(`Plugin \"${pluginName}\" not found`);\n return;\n }\n\n if (plugin.destroy) {\n try {\n plugin.destroy();\n } catch (error) {\n logger.error(`Error destroying plugin \"${pluginName}\":`, error);\n }\n }\n\n this.plugins.delete(pluginName);\n logger.info(`Plugin unregistered: ${pluginName}`);\n }\n\n get(pluginName: string): Plugin | undefined {\n return this.plugins.get(pluginName);\n }\n\n getAll(): Plugin[] {\n return Array.from(this.plugins.values());\n }\n\n async executeHook<T = any>(\n hookName: keyof Plugin,\n ...args: any[]\n ): Promise<T | undefined> {\n for (const plugin of this.plugins.values()) {\n const hook = plugin[hookName];\n\n if (typeof hook === 'function') {\n try {\n const result = await (hook as Function).apply(plugin, args);\n\n if (result !== undefined) {\n return result as T;\n }\n } catch (error) {\n logger.error(\n `Error executing ${String(hookName)} in plugin \"${plugin.name}\":`,\n error\n );\n\n if (plugin.onError) {\n try {\n plugin.onError(error as Error, { hookName, args });\n } catch (onErrorError) {\n logger.error(\n `Error in onError handler for plugin \"${plugin.name}\":`,\n onErrorError\n );\n }\n }\n }\n }\n }\n\n return undefined;\n }\n\n async executeHookChain<T = any>(\n hookName: keyof Plugin,\n initialValue: T,\n ...additionalArgs: any[]\n ): Promise<T> {\n let value = initialValue;\n\n for (const plugin of this.plugins.values()) {\n const hook = plugin[hookName];\n\n if (typeof hook === 'function') {\n try {\n const result = await (hook as Function).apply(plugin, [\n value,\n ...additionalArgs,\n ]);\n\n if (result !== undefined && result !== null) {\n value = result;\n }\n } catch (error) {\n logger.error(\n `Error executing ${String(hookName)} in plugin \"${plugin.name}\":`,\n error\n );\n\n if (plugin.onError) {\n try {\n plugin.onError(error as Error, { hookName, value, additionalArgs });\n } catch (onErrorError) {\n logger.error(\n `Error in onError handler for plugin \"${plugin.name}\":`,\n onErrorError\n );\n }\n }\n }\n }\n }\n\n return value;\n }\n\n async executeHookParallel<T = any>(\n hookName: keyof Plugin,\n ...args: any[]\n ): Promise<T[]> {\n const promises: Promise<T>[] = [];\n\n for (const plugin of this.plugins.values()) {\n const hook = plugin[hookName];\n\n if (typeof hook === 'function') {\n const promise = (async () => {\n try {\n return await (hook as Function).apply(plugin, args);\n } catch (error) {\n logger.error(\n `Error executing ${String(hookName)} in plugin \"${plugin.name}\":`,\n error\n );\n\n if (plugin.onError) {\n try {\n plugin.onError(error as Error, { hookName, args });\n } catch (onErrorError) {\n logger.error(\n `Error in onError handler for plugin \"${plugin.name}\":`,\n onErrorError\n );\n }\n }\n\n return undefined;\n }\n })();\n\n promises.push(promise);\n }\n }\n\n const results = await Promise.all(promises);\n return results.filter((r) => r !== undefined) as T[];\n }\n\n clear(): void {\n for (const plugin of this.plugins.values()) {\n if (plugin.destroy) {\n try {\n plugin.destroy();\n } catch (error) {\n logger.error(`Error destroying plugin \"${plugin.name}\":`, error);\n }\n }\n }\n\n this.plugins.clear();\n logger.info('All plugins cleared');\n }\n}\n","import { Storage } from './storage';\nimport { logger } from './logger';\n\nexport type ConsentCategory =\n | 'analytics'\n | 'marketing'\n | 'functional'\n | 'necessary';\n\nexport interface ConsentPreferences {\n analytics: boolean;\n marketing: boolean;\n functional: boolean;\n necessary: boolean;\n}\n\nexport type ConsentStatus = 'granted' | 'denied' | 'pending';\n\nexport interface ConsentManagerConfig {\n defaultConsent?: Partial<ConsentPreferences>;\n waitForConsent?: boolean;\n consentStorageKey?: string;\n}\n\nexport class ConsentManager {\n private storage: Storage;\n private config: Required<ConsentManagerConfig>;\n private preferences: ConsentPreferences;\n private listeners: Array<(preferences: ConsentPreferences) => void> = [];\n\n constructor(storage: Storage, config: ConsentManagerConfig = {}) {\n this.storage = storage;\n this.config = {\n defaultConsent: config.defaultConsent || {},\n waitForConsent: config.waitForConsent ?? false,\n consentStorageKey: config.consentStorageKey || 'aegis_consent',\n };\n\n this.preferences = this.loadPreferences();\n }\n\n private loadPreferences(): ConsentPreferences {\n const stored = this.storage.get(this.config.consentStorageKey);\n\n if (stored) {\n try {\n const parsed = JSON.parse(stored);\n logger.info('Loaded consent preferences:', parsed);\n return this.mergeWithDefaults(parsed);\n } catch (error) {\n logger.warn('Failed to parse consent preferences:', error);\n }\n }\n\n return this.getDefaultPreferences();\n }\n\n private getDefaultPreferences(): ConsentPreferences {\n return {\n analytics: this.config.defaultConsent.analytics ?? false,\n marketing: this.config.defaultConsent.marketing ?? false,\n functional: this.config.defaultConsent.functional ?? true,\n necessary: true,\n };\n }\n\n private mergeWithDefaults(\n preferences: Partial<ConsentPreferences>\n ): ConsentPreferences {\n return {\n necessary: true,\n analytics: preferences.analytics ?? this.config.defaultConsent.analytics ?? false,\n marketing: preferences.marketing ?? this.config.defaultConsent.marketing ?? false,\n functional: preferences.functional ?? this.config.defaultConsent.functional ?? true,\n };\n }\n\n private savePreferences(): void {\n try {\n this.storage.set(\n this.config.consentStorageKey,\n JSON.stringify(this.preferences),\n 365\n );\n logger.info('Saved consent preferences:', this.preferences);\n } catch (error) {\n logger.error('Failed to save consent preferences:', error);\n }\n }\n\n setConsent(preferences: Partial<ConsentPreferences>): void {\n const updated = { ...this.preferences, ...preferences };\n updated.necessary = true;\n\n this.preferences = updated;\n this.savePreferences();\n this.notifyListeners();\n\n logger.info('Consent updated:', this.preferences);\n }\n\n grantAll(): void {\n this.setConsent({\n analytics: true,\n marketing: true,\n functional: true,\n necessary: true,\n });\n }\n\n denyAll(): void {\n this.setConsent({\n analytics: false,\n marketing: false,\n functional: false,\n necessary: true,\n });\n }\n\n hasConsent(category: ConsentCategory): boolean {\n return this.preferences[category];\n }\n\n getPreferences(): ConsentPreferences {\n return { ...this.preferences };\n }\n\n getStatus(category: ConsentCategory): ConsentStatus {\n if (this.hasConsent(category)) {\n return 'granted';\n }\n\n const stored = this.storage.get(this.config.consentStorageKey);\n if (!stored && this.config.waitForConsent) {\n return 'pending';\n }\n\n return 'denied';\n }\n\n isAnalyticsEnabled(): boolean {\n return this.hasConsent('analytics');\n }\n\n isMarketingEnabled(): boolean {\n return this.hasConsent('marketing');\n }\n\n onChange(callback: (preferences: ConsentPreferences) => void): () => void {\n this.listeners.push(callback);\n\n return () => {\n const index = this.listeners.indexOf(callback);\n if (index > -1) {\n this.listeners.splice(index, 1);\n }\n };\n }\n\n private notifyListeners(): void {\n this.listeners.forEach((listener) => {\n try {\n listener(this.getPreferences());\n } catch (error) {\n logger.error('Consent listener error:', error);\n }\n });\n }\n\n reset(): void {\n this.storage.remove(this.config.consentStorageKey);\n this.preferences = this.getDefaultPreferences();\n this.notifyListeners();\n logger.info('Consent preferences reset');\n }\n\n integrateOneTrust(): void {\n if (typeof window === 'undefined') return;\n\n const oneTrust = (window as any).OneTrust;\n if (!oneTrust) {\n logger.warn('OneTrust not detected');\n return;\n }\n\n const syncWithOneTrust = () => {\n const activeGroups = (window as any).OnetrustActiveGroups || '';\n\n this.setConsent({\n necessary: true,\n functional: activeGroups.includes('C0003'),\n analytics: activeGroups.includes('C0002'),\n marketing: activeGroups.includes('C0004'),\n });\n\n logger.info('Synced with OneTrust consent');\n };\n\n if ((window as any).OptanonWrapper) {\n const originalWrapper = (window as any).OptanonWrapper;\n (window as any).OptanonWrapper = function () {\n originalWrapper();\n syncWithOneTrust();\n };\n } else {\n (window as any).OptanonWrapper = syncWithOneTrust;\n }\n\n syncWithOneTrust();\n }\n\n integrateCookiebot(): void {\n if (typeof window === 'undefined') return;\n\n const cookiebot = (window as any).Cookiebot;\n if (!cookiebot) {\n logger.warn('Cookiebot not detected');\n return;\n }\n\n const syncWithCookiebot = () => {\n const consent = cookiebot.consent || {};\n\n this.setConsent({\n necessary: true,\n functional: consent.preferences || false,\n analytics: consent.statistics || false,\n marketing: consent.marketing || false,\n });\n\n logger.info('Synced with Cookiebot consent');\n };\n\n window.addEventListener('CookiebotOnAccept', syncWithCookiebot);\n window.addEventListener('CookiebotOnDecline', syncWithCookiebot);\n\n if (cookiebot.consent) {\n syncWithCookiebot();\n }\n }\n\n integrateGoogleConsentMode(): void {\n if (typeof window === 'undefined') return;\n\n // Consent Mode v2 is driven through the gtag command queue, which is just\n // `window.dataLayer`. The Google tag (gtag.js) loads async, so SDK init can\n // win the race and find no `gtag` yet. The old behaviour bailed with a\n // \"Google Tag Manager not detected\" warning — misleading (the tag usually\n // loads a tick later) and lossy (the consent signal was dropped). Instead\n // we define the canonical gtag stub so our consent commands queue on\n // `dataLayer` and gtag.js replays them when it boots. This is exactly\n // Google's documented bootstrap snippet. Callers gate this integration on a\n // Google tag actually being configured (see storefront provider), so we\n // never spin up an inert dataLayer for tenants with no Google tag.\n const w = window as unknown as {\n gtag?: (...args: unknown[]) => void;\n dataLayer?: unknown[];\n };\n if (typeof w.gtag !== 'function') {\n w.dataLayer = w.dataLayer ?? [];\n w.gtag = (...args: unknown[]) => {\n w.dataLayer?.push(args);\n };\n logger.debug('Google tag not yet loaded — queuing consent via dataLayer');\n }\n const gtag = w.gtag;\n\n const updateGoogleConsent = () => {\n gtag('consent', 'update', {\n ad_storage: this.preferences.marketing ? 'granted' : 'denied',\n analytics_storage: this.preferences.analytics ? 'granted' : 'denied',\n ad_user_data: this.preferences.marketing ? 'granted' : 'denied',\n ad_personalization: this.preferences.marketing ? 'granted' : 'denied',\n functionality_storage: this.preferences.functional ? 'granted' : 'denied',\n personalization_storage: this.preferences.functional ? 'granted' : 'denied',\n security_storage: 'granted',\n });\n\n logger.debug('Updated Google Consent Mode');\n };\n\n this.onChange(updateGoogleConsent);\n updateGoogleConsent();\n }\n}\n","/**\n * E-Commerce helper methods for the Active Reach SDK.\n *\n * Provides standardized e-commerce event tracking that maps to the\n * Active Reach Intelligence data governance pipeline.\n *\n * Event names follow the canonical mapper (libs/gateway-core/src/canonical-event.ts).\n * The dotted-vs-flat split is intentional, not accidental: HIGH-VALUE lifecycle\n * moments are namespaced `domain.action` (transaction.*, engagement.*); raw\n * funnel telemetry that maps 1:1 to a Meta-standard event stays flat snake_case\n * (its name is a frozen contract with the Meta CAPI/pixel maps). Don't \"tidy\"\n * either side — see the convention block in canonical-event.ts.\n * product_viewed → product_viewed (flat)\n * product_list_viewed → product_list_viewed (flat)\n * product_clicked → engagement.product_click (dotted)\n * cart_item_added → cart_item_added (flat)\n * cart_item_removed → cart_item_removed (flat)\n * cart_viewed → cart_viewed (flat)\n * checkout_started → checkout_started (flat)\n * order_completed → transaction.purchase (dotted)\n * order_refunded → transaction.refund (dotted)\n * coupon_applied → coupon_applied (flat)\n * search_performed → engagement.search (dotted)\n * wishlist_item_added → wishlist_item_added (flat)\n * product_waitlisted → product_waitlisted (back-in-stock waitlist opt-in;\n * consumed by product_event_trigger_service\n * _get_waitlisted_contacts; fires catalog.back_in_stock\n * journey trigger on the next ON_STOCK flip)\n * promotion_viewed → promotion_viewed\n * promotion_clicked → promotion_clicked\n */\n\nimport type { Aegis } from '../core/analytics';\nimport type {\n EcommerceProduct,\n EcommerceCart,\n EcommerceCheckout,\n EcommerceOrder,\n EcommerceCoupon,\n EcommerceSearch,\n EcommerceProductList,\n EcommerceWishlist,\n EcommerceWaitlist,\n EcommercePromotion,\n} from './types';\n\nfunction sumItems(products: EcommerceProduct[]): number {\n return products.reduce((sum, p) => sum + (p.quantity ?? 1), 0);\n}\n\n// Cart abandonment detection is SERVER-SIDE. The SDK fires the canonical\n// e-commerce events (`cart_item_added`, `cart_item_removed`, `cart_viewed`,\n// `transaction.purchase`); a Celery beat task on cell-plane scans\n// `aegis.events_with_ttl` every 5 minutes for shoppers with a\n// `cart_item_added` >30 min old and no subsequent purchase, emitting\n// `cart_abandoned` to the `behavioral-events_{cell_id}` Kafka topic.\n//\n// The SPA storefront has no persistent server-side cart object, so cart\n// identity is keyed off the shopper themselves: `COALESCE(user_id,\n// anonymous_id)`. Provider-issued cart tokens (Shopify/Woo/Magento pixel\n// bridge via Widget Manager) are surfaced on the emitted event when\n// present but are not required for detection.\n//\n// We deliberately do NOT run a JS timer on the client — mobile browsers\n// throttle background timers to ~1Hz and they're unreliable for any\n// abandonment threshold beyond a few seconds. Industry alignment: WebEngage,\n// MoEngage, CleverTap, Bitespeed, Klaviyo all detect server-side.\n//\n// See: `apps/cell-plane/app/workers/storefront_cart_abandonment_scanner.py`\n\nfunction mapProducts(products: EcommerceProduct[]) {\n return products.map((p) => ({\n product_id: p.product_id,\n sku: p.sku,\n name: p.name,\n price: p.price,\n quantity: p.quantity ?? 1,\n currency: p.currency,\n category: p.category,\n brand: p.brand,\n variant_id: p.variant_id,\n variant_label: p.variant_label,\n position: p.position,\n }));\n}\n\nexport class EcommerceTracker {\n constructor(private aegis: Aegis) {}\n\n // -- Cart model (CEP-canonical: the cart is DERIVED from the ecommerce events\n // the host already fires, so on-page cart-reactive surfaces — the\n // Free-Delivery progress card, the progress-bar widget, rule evaluation —\n // all light up without a separate `setCartState` call). `cartViewed` is\n // authoritative (full snapshot); add/remove apply best-effort deltas. The\n // sink (wired by `runtime.bindEcommerce`) forwards every change to the ONE\n // cart store. See docs/architecture/CART_STORE_UNIFICATION_TRACKER.md.\n private _cart: { total: number; itemCount: number; currency: string } = {\n total: 0,\n itemCount: 0,\n currency: 'INR',\n };\n private _cartSink: ((c: { total: number; itemCount: number; currency: string }) => void) | null = null;\n\n /** Register a sink that receives the derived cart on every cart event.\n * Wired once by `AegisMessageRuntime.bindEcommerce(...)`. Emits the current\n * snapshot immediately so a late binding still gets the latest cart. */\n onCartChange(sink: (c: { total: number; itemCount: number; currency: string }) => void): void {\n this._cartSink = sink;\n sink({ ...this._cart });\n }\n\n private _emitCart(): void {\n if (this._cart.total < 0) this._cart.total = 0;\n if (this._cart.itemCount < 0) this._cart.itemCount = 0;\n this._cartSink?.({ ...this._cart });\n }\n\n // -- Product Discovery --\n\n productViewed(product: EcommerceProduct): void {\n this.aegis.track('product_viewed', {\n product_id: product.product_id,\n sku: product.sku,\n name: product.name,\n price: product.price,\n currency: product.currency ?? 'INR',\n category: product.category,\n brand: product.brand,\n variant_id: product.variant_id,\n variant_label: product.variant_label,\n image_url: product.image_url,\n url: product.url,\n });\n }\n\n productListViewed(list: EcommerceProductList): void {\n const products = mapProducts(list.products);\n this.aegis.track('product_list_viewed', {\n list_id: list.list_id,\n list_name: list.list_name,\n category: list.category,\n products,\n // `items` as well as `products` — a THIRD spelling of the same fact was\n // one spelling too many. Every product reader in the platform looks for\n // `items` (or `line_items`, the Shopify shape); this event alone said\n // `products`, so a list view showed N products and contributed nothing to\n // any of them. On ST that was 4 views recorded as `not measured`, and it\n // is the same defect that made every Shopify order invisible.\n //\n // `products` is kept so nothing already reading it breaks. mapProducts\n // already emits product_id / sku / name / price / quantity, which is\n // exactly the canonical item shape — no second mapping to drift.\n items: products,\n });\n }\n\n productClicked(product: EcommerceProduct, source?: { list_id?: string; position?: number; section?: string }): void {\n this.aegis.track('product_clicked', {\n product_id: product.product_id,\n sku: product.sku,\n name: product.name,\n price: product.price,\n currency: product.currency ?? 'INR',\n category: product.category,\n brand: product.brand,\n variant_id: product.variant_id,\n list_id: source?.list_id,\n position: source?.position ?? product.position,\n section: source?.section,\n });\n }\n\n productImpressed(product: EcommerceProduct, source?: { list_id?: string; position?: number; section?: string }): void {\n this.aegis.track('product_impression', {\n product_id: product.product_id,\n sku: product.sku,\n name: product.name,\n price: product.price,\n currency: product.currency ?? 'INR',\n category: product.category,\n list_id: source?.list_id,\n position: source?.position ?? product.position,\n section: source?.section,\n });\n }\n\n categoryFiltered(category: string, options?: { previous_category?: string; result_count?: number }): void {\n this.aegis.track('category_filtered', {\n category,\n previous_category: options?.previous_category,\n result_count: options?.result_count,\n });\n }\n\n searchPerformed(search: EcommerceSearch): void {\n this.aegis.track('search_performed', {\n query: search.query,\n results_count: search.results_count,\n filters: search.filters,\n });\n }\n\n // -- Cart --\n\n addToCart(product: EcommerceProduct): void {\n const qty = product.quantity ?? 1;\n this.aegis.track('cart_item_added', {\n product_id: product.product_id,\n sku: product.sku,\n name: product.name,\n price: product.price,\n quantity: qty,\n currency: product.currency ?? 'INR',\n category: product.category,\n brand: product.brand,\n variant_id: product.variant_id,\n variant_label: product.variant_label,\n });\n // Best-effort delta — `cartViewed` corrects to the authoritative snapshot.\n this._cart.total += (product.price || 0) * qty;\n this._cart.itemCount += qty;\n if (product.currency) this._cart.currency = product.currency;\n this._emitCart();\n }\n\n removeFromCart(product: EcommerceProduct): void {\n const qty = product.quantity ?? 1;\n this.aegis.track('cart_item_removed', {\n product_id: product.product_id,\n sku: product.sku,\n name: product.name,\n price: product.price,\n quantity: qty,\n currency: product.currency ?? 'INR',\n variant_id: product.variant_id,\n });\n this._cart.total -= (product.price || 0) * qty;\n this._cart.itemCount -= qty;\n this._emitCart();\n }\n\n cartViewed(cart: EcommerceCart): void {\n this.aegis.track('cart_viewed', {\n cart_id: cart.cart_id,\n value: cart.value,\n currency: cart.currency ?? 'INR',\n num_items: sumItems(cart.products),\n products: mapProducts(cart.products),\n });\n // Authoritative snapshot — overwrites the running deltas.\n this._cart = {\n total: cart.value || 0,\n itemCount: sumItems(cart.products),\n currency: cart.currency ?? this._cart.currency ?? 'INR',\n };\n this._emitCart();\n }\n\n // -- Checkout --\n\n checkoutStarted(checkout: EcommerceCheckout): void {\n this.aegis.track('checkout_started', {\n checkout_id: checkout.checkout_id,\n value: checkout.value,\n currency: checkout.currency ?? 'INR',\n num_items: sumItems(checkout.products),\n coupon: checkout.coupon,\n shipping: checkout.shipping,\n tax: checkout.tax,\n products: mapProducts(checkout.products),\n });\n }\n\n checkoutStep(step: number, options?: Record<string, unknown>): void {\n this.aegis.track('checkout_step', {\n step,\n ...options,\n });\n }\n\n checkoutOption(option: {\n step?: number;\n payment_method?: string;\n shipping_method?: string;\n option_id?: string;\n option_label?: string;\n }): void {\n this.aegis.track('checkout_option', { ...option });\n }\n\n // -- Order --\n\n orderCompleted(order: EcommerceOrder): void {\n this.aegis.track('order_completed', {\n order_id: order.order_id,\n value: order.value,\n revenue: order.revenue ?? order.value,\n currency: order.currency ?? 'INR',\n num_items: sumItems(order.products),\n coupon: order.coupon,\n shipping: order.shipping,\n tax: order.tax,\n discount: order.discount,\n payment_method: order.payment_method,\n products: mapProducts(order.products),\n });\n }\n\n orderRefunded(orderId: string, value?: number, products?: EcommerceProduct[]): void {\n this.aegis.track('order_refunded', {\n order_id: orderId,\n value,\n products: products ? mapProducts(products) : undefined,\n });\n }\n\n orderShipped(\n orderId: string,\n tracking?: { carrier?: string; tracking_code?: string; tracking_url?: string },\n ): void {\n this.aegis.track('order_shipped', {\n order_id: orderId,\n carrier: tracking?.carrier,\n tracking_code: tracking?.tracking_code,\n tracking_url: tracking?.tracking_url,\n });\n }\n\n // -- Coupons --\n\n couponApplied(coupon: EcommerceCoupon): void {\n this.aegis.track('coupon_applied', { ...coupon });\n }\n\n couponRemoved(coupon: EcommerceCoupon): void {\n this.aegis.track('coupon_removed', { ...coupon });\n }\n\n couponDenied(coupon: EcommerceCoupon, reason?: string): void {\n this.aegis.track('coupon_denied', { ...coupon, reason });\n }\n\n // -- Wishlist --\n\n wishlistItemAdded(wishlist: EcommerceWishlist): void {\n this.aegis.track('wishlist_item_added', {\n wishlist_id: wishlist.wishlist_id,\n wishlist_name: wishlist.wishlist_name,\n product_id: wishlist.product.product_id,\n sku: wishlist.product.sku,\n name: wishlist.product.name,\n price: wishlist.product.price,\n variant_id: wishlist.product.variant_id,\n });\n }\n\n productRemovedFromWishlist(wishlist: EcommerceWishlist): void {\n this.aegis.track('wishlist_item_removed', {\n wishlist_id: wishlist.wishlist_id,\n wishlist_name: wishlist.wishlist_name,\n product_id: wishlist.product.product_id,\n sku: wishlist.product.sku,\n name: wishlist.product.name,\n price: wishlist.product.price,\n variant_id: wishlist.product.variant_id,\n });\n }\n\n // -- Back-in-stock waitlist --\n //\n // Server-side substrate: contact_events row keyed on\n // (organization_id, contact_id, event_name='product_waitlisted',\n // event_properties['product_id']). Resolved by\n // product_event_trigger_service._get_waitlisted_contacts and fanned out via\n // catalog.back_in_stock journey trigger when stock_event_handler_worker\n // detects the SKU flipping back into stock.\n productWaitlisted(waitlist: EcommerceWaitlist): void {\n this.aegis.track('product_waitlisted', {\n product_id: waitlist.product.product_id,\n sku: waitlist.product.sku,\n variant_id: waitlist.product.variant_id,\n name: waitlist.product.name,\n price: waitlist.product.price,\n channels: waitlist.channels,\n });\n }\n\n // -- Promotions --\n\n promotionViewed(promo: EcommercePromotion): void {\n this.aegis.track('promotion_viewed', { ...promo });\n }\n\n promotionClicked(promo: EcommercePromotion): void {\n this.aegis.track('promotion_clicked', { ...promo });\n }\n}\n\nexport type {\n EcommerceProduct,\n EcommerceCart,\n EcommerceCheckout,\n EcommerceOrder,\n EcommerceCoupon,\n EcommerceSearch,\n EcommerceProductList,\n EcommerceWishlist,\n EcommerceWaitlist,\n EcommercePromotion,\n} from './types';\n","/**\n * Token bucket rate limiter for client-side event throttling.\n *\n * Limits per session: burst (default 100) + sustained (default 20/sec).\n * Drops events with a console warning once a burst sustains. Drops are\n * emitted as a single coalesced `aegis.client.rate_limited` meta-event\n * per second so we don't flood ingress with drop notifications.\n *\n * Defaults are deliberately generous — the goal is to prevent runaway\n * loops (a buggy app firing 10k events/sec) without affecting normal use.\n */\n\nimport { logger } from '../utils/logger';\n\nexport interface RateLimiterOptions {\n /** Max tokens (burst size). Default 100. */\n burstCapacity?: number;\n /** Sustained rate per second. Default 20. */\n refillPerSecond?: number;\n /** Window in ms over which dropped events are coalesced. Default 1000. */\n dropCoalesceWindowMs?: number;\n /** Optional callback fired with the count of dropped events per coalesce window. */\n onDropBatch?: (droppedCount: number, sampleEventName: string | null) => void;\n}\n\nexport class RateLimiter {\n private tokens: number;\n private readonly capacity: number;\n private readonly refillPerSecond: number;\n private lastRefillMs: number;\n\n private droppedThisWindow = 0;\n private firstDroppedName: string | null = null;\n private windowFlushTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly windowMs: number;\n private readonly onDropBatch?: (droppedCount: number, sampleEventName: string | null) => void;\n\n constructor(options: RateLimiterOptions = {}) {\n this.capacity = Math.max(1, options.burstCapacity ?? 100);\n this.refillPerSecond = Math.max(1, options.refillPerSecond ?? 20);\n this.windowMs = options.dropCoalesceWindowMs ?? 1000;\n this.onDropBatch = options.onDropBatch;\n this.tokens = this.capacity;\n this.lastRefillMs = Date.now();\n }\n\n /**\n * Try to consume 1 token. Returns true if allowed, false if throttled.\n * Throttled events are coalesced into a single onDropBatch callback per window.\n */\n tryConsume(eventName: string): boolean {\n this.refill();\n\n if (this.tokens >= 1) {\n this.tokens -= 1;\n return true;\n }\n\n this.droppedThisWindow += 1;\n if (this.firstDroppedName === null) {\n this.firstDroppedName = eventName;\n logger.warn(\n `[Aegis] Rate limit reached — dropping event \"${eventName}\" and any further events for ${this.windowMs}ms. Burst=${this.capacity}, refill=${this.refillPerSecond}/s.`,\n );\n }\n\n if (!this.windowFlushTimer) {\n this.windowFlushTimer = setTimeout(() => this.flushWindow(), this.windowMs);\n }\n\n return false;\n }\n\n /** Diagnostics — current token count (rounded down). */\n getAvailableTokens(): number {\n this.refill();\n return Math.floor(this.tokens);\n }\n\n /** Cleanup. Idempotent. */\n destroy(): void {\n if (this.windowFlushTimer) {\n clearTimeout(this.windowFlushTimer);\n this.windowFlushTimer = null;\n }\n this.flushWindow();\n }\n\n // ---------------------------------------------------------------------\n\n private refill(): void {\n const now = Date.now();\n const elapsedSec = (now - this.lastRefillMs) / 1000;\n if (elapsedSec <= 0) return;\n\n const newTokens = elapsedSec * this.refillPerSecond;\n this.tokens = Math.min(this.capacity, this.tokens + newTokens);\n this.lastRefillMs = now;\n }\n\n private flushWindow(): void {\n if (this.windowFlushTimer) {\n clearTimeout(this.windowFlushTimer);\n this.windowFlushTimer = null;\n }\n if (this.droppedThisWindow > 0 && this.onDropBatch) {\n try {\n this.onDropBatch(this.droppedThisWindow, this.firstDroppedName);\n } catch (err) {\n logger.warn('[Active Reach] RateLimiter.onDropBatch threw:', err);\n }\n }\n this.droppedThisWindow = 0;\n this.firstDroppedName = null;\n }\n}\n","/**\n * MurmurHash3 x86-32 — deterministic, byte-identical with Python `mmh3.hash`.\n *\n * We hand-roll rather than pull a dep because (a) our bundle-budget is\n * aggressive for storefront first-paint, and (b) this is the authoritative\n * cross-language hash for the event-governance bloom filter — any behavior\n * drift between Python and JS produces silent FP-rate spikes in prod.\n *\n * Contract:\n * • Input is a JS string. It is UTF-8 encoded via TextEncoder before hashing.\n * • Output is an unsigned 32-bit integer (0 .. 2^32-1).\n * • Matches `mmh3.hash(s.encode('utf-8'), seed=seed, signed=False)` in\n * Python exactly — verified by libs/shared-types/bloom-test-vectors.json\n * in CI.\n *\n * Reference: Austin Appleby, MurmurHash3 (public domain).\n * https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp\n *\n * Implementation notes:\n * • All arithmetic on 32-bit ints. JS does bitwise ops on 32-bit ints natively;\n * `Math.imul` gives us correct 32-bit multiplication without overflow.\n * • We use `>>> 0` to coerce to unsigned after every step that could produce\n * a negative or >32-bit value.\n * • UTF-8 encoding matters — a naive `charCodeAt` loop would mis-hash any\n * string with non-ASCII characters (e.g. emoji, accented chars) and the\n * Python side would disagree. We always encode via TextEncoder first.\n */\n\nconst C1 = 0xcc9e2d51;\nconst C2 = 0x1b873593;\n\n/**\n * Compute MurmurHash3 x86-32 of a UTF-8 encoded string.\n *\n * @param input The string to hash.\n * @param seed 32-bit unsigned seed.\n * @returns Unsigned 32-bit integer hash.\n */\nexport function murmurhash3_x86_32(input: string, seed: number = 0): number {\n // Encode to UTF-8 bytes — matches Python's `s.encode('utf-8')`.\n const bytes = new TextEncoder().encode(input);\n return murmurhash3_bytes(bytes, seed);\n}\n\n/**\n * Compute MurmurHash3 x86-32 over a byte buffer directly.\n *\n * Exposed for callers that already have UTF-8 bytes and want to skip the\n * encode step (e.g., internal bloom-hash paths).\n */\nexport function murmurhash3_bytes(bytes: Uint8Array, seed: number = 0): number {\n const len = bytes.length;\n const nBlocks = Math.floor(len / 4);\n\n let h1 = seed >>> 0;\n\n // Body — consume 4-byte blocks.\n for (let i = 0; i < nBlocks; i++) {\n const offset = i * 4;\n let k1 =\n bytes[offset] |\n (bytes[offset + 1] << 8) |\n (bytes[offset + 2] << 16) |\n (bytes[offset + 3] << 24);\n\n k1 = Math.imul(k1, C1);\n k1 = (k1 << 15) | (k1 >>> 17);\n k1 = Math.imul(k1, C2);\n\n h1 ^= k1;\n h1 = (h1 << 13) | (h1 >>> 19);\n h1 = (Math.imul(h1, 5) + 0xe6546b64) >>> 0;\n }\n\n // Tail — up to 3 trailing bytes.\n const tailStart = nBlocks * 4;\n let k1 = 0;\n const tailLen = len - tailStart;\n if (tailLen === 3) k1 ^= bytes[tailStart + 2] << 16;\n if (tailLen >= 2) k1 ^= bytes[tailStart + 1] << 8;\n if (tailLen >= 1) {\n k1 ^= bytes[tailStart];\n k1 = Math.imul(k1, C1);\n k1 = (k1 << 15) | (k1 >>> 17);\n k1 = Math.imul(k1, C2);\n h1 ^= k1;\n }\n\n // Finalization — avalanche (fmix32).\n h1 ^= len;\n h1 ^= h1 >>> 16;\n h1 = Math.imul(h1, 0x85ebca6b);\n h1 ^= h1 >>> 13;\n h1 = Math.imul(h1, 0xc2b2ae35);\n h1 ^= h1 >>> 16;\n\n return h1 >>> 0;\n}\n","/**\n * Bloom filter — wire-compatible with the Python builder in\n * apps/control-plane/app/services/event_governance_bloom.py\n *\n * This SDK-side variant is QUERY-ONLY. The filter is built server-side\n * from the org's registered event-name set, base64-encoded, and shipped\n * to the browser on `/v1/sdk/bootstrap`. The SDK reads it once, uses it\n * to gate `track()` calls, and discards it on the next bootstrap.\n *\n * Wire format:\n * • `m` bits of storage, represented as m/8 bytes, base64-encoded.\n * • Bit `i` is at byte `i >> 3`, bitmask `1 << (i & 7)` — LSB-first.\n * • `m` MUST be a power of two — allows `idx & (m-1)` modulo.\n * • `k` hash functions synthesized via Kirsch-Mitzenmacher from two\n * MurmurHash3 x86-32 hashes with seeds (seedA, seedB):\n * h_i(x) = (h1(x) + i * h2(x)) mod m\n * • Input strings are UTF-8 encoded (handled inside murmurhash3_x86_32).\n */\n\nimport { murmurhash3_x86_32 } from './murmur3';\n\n/** Decode a base64 string to a Uint8Array. Accepts web (atob) and node (Buffer). */\nfunction base64ToBytes(b64: string): Uint8Array {\n if (typeof atob !== 'undefined') {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n // Node.js fallback — used by tests and SSR integrations.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const B = (globalThis as any).Buffer;\n if (B && typeof B.from === 'function') {\n const buf: Uint8Array = B.from(b64, 'base64');\n // Copy into a plain Uint8Array so callers don't observe Buffer semantics.\n return new Uint8Array(buf);\n }\n throw new Error('No base64 decoder available (neither atob nor Buffer)');\n}\n\nexport interface BloomFilterParams {\n /** Bits in the filter (must be power of 2). */\n m: number;\n /** Number of hash functions (Kirsch-Mitzenmacher synthesized). */\n k: number;\n /** Seed for the first MurmurHash3 call. */\n seedA: number;\n /** Seed for the second MurmurHash3 call. */\n seedB: number;\n}\n\nexport class BloomFilter {\n private readonly buf: Uint8Array;\n private readonly mask: number;\n\n constructor(buf: Uint8Array, private readonly params: BloomFilterParams) {\n if ((params.m & (params.m - 1)) !== 0) {\n throw new Error(`Bloom filter m must be a power of 2, got ${params.m}`);\n }\n if (buf.length !== params.m >> 3) {\n throw new Error(\n `Bloom filter buffer size mismatch: expected ${params.m >> 3} bytes, got ${buf.length}`\n );\n }\n this.buf = buf;\n this.mask = params.m - 1;\n }\n\n /** Build from the wire format (base64 bytes + explicit params). */\n static fromBase64(bloomB64: string, params: BloomFilterParams): BloomFilter {\n const bytes = base64ToBytes(bloomB64);\n return new BloomFilter(bytes, params);\n }\n\n /**\n * Returns true if `name` is probably in the set — possibly with the\n * filter's configured false-positive rate. FALSE is always authoritative.\n *\n * FP here means: SDK thinks a name is already registered when it isn't.\n * That costs one wasted server round-trip (gateway does the exact check\n * and catches it) — strictly safer than a false-negative, which could\n * leak a novel name past the SDK.\n */\n has(name: string): boolean {\n const h1 = murmurhash3_x86_32(name, this.params.seedA);\n const h2 = murmurhash3_x86_32(name, this.params.seedB);\n\n for (let i = 0; i < this.params.k; i++) {\n // >>> 0 at each step to keep arithmetic in unsigned 32-bit land;\n // we can't just take & mask at the end because h1 + i*h2 could\n // overflow past 2^32 when i*h2 is large.\n const combined = (h1 + Math.imul(i, h2)) >>> 0;\n const idx = combined & this.mask;\n const bit = this.buf[idx >> 3] & (1 << (idx & 7));\n if (bit === 0) return false;\n }\n return true;\n }\n}\n","/**\n * NameGovernor — client-side event-name cap enforcement.\n *\n * The governor consumes an `EventGovernanceHint` from the bootstrap response\n * and decides whether to let a `track()` call proceed to the network or drop\n * it locally.\n *\n * Why this exists:\n * Without it, a page that calls `track('new_btn_' + Date.now())` in a\n * render loop would (a) pass the local rate-limiter if within burst, (b)\n * force the gateway to ask control-plane for a verdict on every unique\n * name, and (c) amplify the CP `/event-governance/check` load quadratically\n * in the number of tabs firing the same bug. The bloom filter gives the\n * SDK enough information to drop novel names locally once the org hits\n * its cap, collapsing the amplification to zero.\n *\n * Design constraints:\n * • FAIL-OPEN — missing hint (Enterprise org, Redis outage) lets every\n * name through. The gateway is still the authoritative cap.\n * • FALSE-POSITIVE-SAFE — if the bloom says \"known\" for a name that isn't\n * actually registered, we send to gateway and the gateway's exact check\n * catches it. FPs cost one round trip, not a cap bypass.\n * • LOCAL-MEMO — a novel name that has already charged `remainingNewNames`\n * in this tab must NOT charge again on subsequent calls within the same\n * hint TTL. Without this, `track('new_btn_click')` fired 50 times would\n * drain 50 from the counter and block every OTHER legitimate novel name.\n * See `localNovelNames`.\n *\n * See docs/architecture/RFC_2026_04_SDK_GOVERNANCE_HINTS.md §3.3.\n */\n\nimport { BloomFilter } from './bloom-filter';\n\n/** Shape of the hint delivered by `/v1/sdk/bootstrap`. */\nexport interface EventGovernanceHint {\n bloom_algo: string;\n seed_a: number;\n seed_b: number;\n k: number;\n m: number;\n bloom_b64: string;\n remaining_new_names: number | null;\n /**\n * When true, the server is in its 7-day soft-cap grace window: it accepts\n * novel event names past the cap. SDK must NOT drop locally in this mode —\n * doing so would enforce harder than the server. See\n * apps/control-plane/app/schemas/event_governance_hint.py for the canonical\n * contract.\n */\n grace_active?: boolean;\n ttl_seconds: number;\n}\n\nexport interface DropReport {\n /** Map of event_name → count of local drops since last drain. */\n events: Record<string, number>;\n /** Total events dropped across all names (sum of values). */\n total: number;\n /** When the report window started (ms since epoch). */\n since: number;\n}\n\n/** Matches the `bloom_algo` tag shipped by the current control-plane build. */\nconst SUPPORTED_ALGO = 'mmh3_x86_32_km';\n\n/**\n * Best-effort console.warn — coalesced so a runaway render loop doesn't\n * flood the dev console. We only warn on the FIRST distinct dropped name;\n * subsequent names silently increment the telemetry counter.\n */\nfunction warnOncePerSession(message: string): void {\n if (typeof console === 'undefined' || typeof console.warn !== 'function') return;\n console.warn(message);\n}\n\nexport class NameGovernor {\n private bloom: BloomFilter | null = null;\n private remainingNewNames: number = Infinity;\n private graceActive: boolean = false;\n\n /**\n * Names this SDK instance has seen AS NOVEL and already charged against\n * `remainingNewNames`. Reset on every `ingestHint()` so we don't leak\n * accounting across hint refreshes.\n */\n private localNovelNames: Set<string> = new Set();\n\n /** Coalesced telemetry — flushed to the transport via drainDropReport(). */\n private droppedSinceLastReport: Map<string, number> = new Map();\n private reportWindowStart: number = Date.now();\n private hasWarnedThisSession = false;\n\n /**\n * Ingest a freshly-bootstrapped hint. Call on every successful bootstrap.\n * Passing `null` disables governance (fail-open).\n */\n ingestHint(hint: EventGovernanceHint | null): void {\n if (!hint || hint.bloom_algo !== SUPPORTED_ALGO) {\n // Unknown algo — a newer CP ships a version this SDK can't decode.\n // Silently disable governance; gateway remains authoritative.\n this.bloom = null;\n this.remainingNewNames = Infinity;\n this.graceActive = false;\n this.localNovelNames.clear();\n return;\n }\n\n try {\n this.bloom = BloomFilter.fromBase64(hint.bloom_b64, {\n m: hint.m,\n k: hint.k,\n seedA: hint.seed_a,\n seedB: hint.seed_b,\n });\n } catch {\n // Malformed hint — fail-open. Logged by the caller.\n this.bloom = null;\n }\n\n this.remainingNewNames = hint.remaining_new_names ?? Infinity;\n this.graceActive = hint.grace_active === true;\n this.localNovelNames.clear();\n }\n\n /**\n * Decide whether a `track()` call should proceed.\n *\n * Returns true = send to network (rate-limiter still runs after).\n * Returns false = drop locally; caller should return early.\n */\n shouldSend(eventName: string): boolean {\n // No hint = unlimited plan or fail-open. Send everything.\n if (!this.bloom) return true;\n\n // 7-day soft-cap grace window is active — server accepts novel names\n // past the cap, so SDK must not enforce harder. Fail-open to gateway.\n if (this.graceActive) return true;\n\n // Known name — send.\n if (this.bloom.has(eventName)) return true;\n\n // Already charged in this session — send (gateway has it cached too).\n if (this.localNovelNames.has(eventName)) return true;\n\n // Novel name, within headroom — charge once, send.\n if (this.remainingNewNames > 0) {\n this.localNovelNames.add(eventName);\n this.remainingNewNames -= 1;\n return true;\n }\n\n // Novel name, over the cap — drop locally, record for telemetry.\n const prev = this.droppedSinceLastReport.get(eventName) ?? 0;\n this.droppedSinceLastReport.set(eventName, prev + 1);\n\n // Only warn once per session, only on the first distinct dropped name —\n // a runaway loop will fill droppedSinceLastReport silently.\n if (!this.hasWarnedThisSession) {\n this.hasWarnedThisSession = true;\n warnOncePerSession(\n `[aegis] Event-name cap reached — \"${eventName}\" dropped locally. ` +\n `Upgrade your plan or remove dynamically-generated event names.`\n );\n }\n return false;\n }\n\n /**\n * Snapshot + reset the dropped-names counter. Called by the telemetry\n * beacon on batch flush so the gateway gets visibility into client-side\n * drops for ops dashboards.\n */\n drainDropReport(): DropReport | null {\n if (this.droppedSinceLastReport.size === 0) return null;\n\n const events: Record<string, number> = {};\n let total = 0;\n for (const [name, count] of this.droppedSinceLastReport) {\n events[name] = count;\n total += count;\n }\n\n const since = this.reportWindowStart;\n this.droppedSinceLastReport.clear();\n this.reportWindowStart = Date.now();\n\n return { events, total, since };\n }\n\n // Test-only accessors — not part of public API but easier than reflection.\n /** @internal */\n _debugState(): {\n hasBloom: boolean;\n remaining: number;\n localNovel: number;\n graceActive: boolean;\n } {\n return {\n hasBloom: this.bloom !== null,\n remaining: this.remainingNewNames,\n localNovel: this.localNovelNames.size,\n graceActive: this.graceActive,\n };\n }\n}\n","/**\n * TraitGovernor — client-side guards for trait writes (P10.5).\n *\n * Runs the same 5 ingestion guards client-side that the cell-plane backend\n * runs at the trait-write chokepoint (`record_attribute_keys`), so the SDK\n * can warn developers about problematic keys/values BEFORE network send.\n *\n * Why two chokepoints:\n * - **Backend** (`record_attribute_keys` → DLQ): authoritative; rejects\n * payloads that bypass the SDK (direct HTTP, CSV import, integration\n * workers). Operators see rejections via /Validation tab.\n * - **SDK** (this file): developer-facing; warns in console.warn at dev\n * time so the issue is caught BEFORE the bad data hits production. We\n * normalize what we can (key casing, value truncation, date format)\n * and warn-then-drop what we can't (reserved prefixes).\n *\n * \"First-3 per verdict per session\" — we warn at most 3 times for each\n * verdict per `(workspace_id, verdict)` key in any given page load. After\n * that, silent — the developer got the message; further warnings would\n * just spam the console and obscure other issues.\n *\n * Canonical contract: the verdict codes here MUST match\n * `data_governance.ingestion_guards.GuardResult.verdict` codes + the\n * `ingestion_dlq.verdict` DB CHECK + the FE `IngestionDLQVerdict` union.\n * Real-env smoke asserts BE ↔ DB drift; TS narrowing catches FE drift;\n * this file completes the four-surface drift protection.\n */\n\nexport type TraitVerdict =\n | 'bad_key_format'\n | 'value_too_long'\n | 'bad_date_format'\n | 'name_too_long' // not used here (event-name guard); kept in union for symmetry\n | 'reserved_prefix';\n\nexport interface TraitDrop {\n /** Original key as the developer wrote it. */\n originalKey: string;\n /** What the guard decided. */\n verdict: TraitVerdict;\n /** Human-readable reason — same text we'd log to ingestion_dlq.reason. */\n reason: string;\n}\n\nexport interface TraitResult {\n /**\n * Sanitized traits to actually send. Reserved-prefix keys are absent;\n * camelCase keys are rewritten to snake_case; long values truncated;\n * ISO dates → epoch ms.\n */\n sanitized: Record<string, unknown>;\n /** Keys that were dropped or modified (one row per change). */\n drops: TraitDrop[];\n}\n\n// Reserved namespace prefixes — mirrors\n// data_governance.ingestion_guards.DEFAULT_RESERVED_PREFIXES.\nconst RESERVED_PREFIXES: readonly string[] = [\n 'system.',\n 'user.',\n 'loyalty.',\n 'review.',\n 'cart.',\n 'checkout.',\n 'product.',\n 'pos.',\n 'bill.',\n 'feedback.',\n 'chat.',\n 'delivery.',\n 'event.',\n '$',\n '_',\n];\n\n// PII not detected here — that's done server-side by the canonical\n// is_likely_pii helper. SDK side handles only normalization + format\n// rejections that benefit from early warning.\n\nconst KEY_SOFT_VALUE_CAP = 512; // truncate\nconst KEY_HARD_VALUE_CAP = 10_000; // reject (DoS protection)\n\nconst CAMEL_BOUNDARY_1 = /(.)([A-Z][a-z]+)/g;\nconst CAMEL_BOUNDARY_2 = /([a-z0-9])([A-Z])/g;\nconst NON_SNAKE = /[\\s\\-.]+/g;\nconst DUPE_UNDERSCORE = /_+/g;\nconst ISO_8601 = /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/;\n\nconst DATE_KEY_HINTS = new Set([\n 'at', 'on', 'date', 'time', 'timestamp', 'dob', 'birthday',\n 'joined', 'expired', 'expires', 'created', 'updated', 'started', 'ended',\n]);\n\n/**\n * Normalize camelCase / PascalCase / dash-or-space-separated keys to\n * snake_case. Returns `null` if the key reduces to empty (caller treats\n * as `bad_key_format`).\n */\nfunction normalizeKey(key: string): string | null {\n if (!key) return null;\n const stage1 = key.replace(CAMEL_BOUNDARY_1, '$1_$2').replace(CAMEL_BOUNDARY_2, '$1_$2');\n const stage2 = stage1.replace(NON_SNAKE, '_').toLowerCase();\n const result = stage2.replace(DUPE_UNDERSCORE, '_').replace(/^_+|_+$/g, '');\n return result || null;\n}\n\nfunction startsWithReserved(key: string): string | null {\n const lower = key.toLowerCase();\n for (const prefix of RESERVED_PREFIXES) {\n if (lower.startsWith(prefix)) return prefix;\n }\n return null;\n}\n\nfunction looksLikeDateKey(key: string): boolean {\n for (const part of key.toLowerCase().split(/[_\\-.\\s]+/)) {\n if (DATE_KEY_HINTS.has(part)) return true;\n }\n return false;\n}\n\n/**\n * ISO-8601 / epoch string → epoch ms (number). Returns null on parse\n * failure. Numeric inputs assumed epoch (seconds if <1e11, else ms).\n */\nfunction parseDateValue(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value < 1e11 ? Math.floor(value * 1000) : Math.floor(value);\n }\n if (typeof value === 'string' && value) {\n const s = value.trim();\n if (ISO_8601.test(s)) {\n const ms = Date.parse(s);\n if (!Number.isNaN(ms)) return ms;\n }\n const asNum = Number(s);\n if (Number.isFinite(asNum)) {\n return asNum < 1e11 ? Math.floor(asNum * 1000) : Math.floor(asNum);\n }\n }\n return null;\n}\n\nfunction warnOnConsole(message: string): void {\n if (typeof console === 'undefined' || typeof console.warn !== 'function') return;\n console.warn(message);\n}\n\nexport class TraitGovernor {\n /** Cap warnings at 3 per (workspace_id, verdict) per session. */\n private static readonly WARN_CAP = 3;\n /** workspace_id || '__no_workspace__' → verdict → count. */\n private warnCounts: Map<string, Map<TraitVerdict, number>> = new Map();\n\n /**\n * Apply all guards to a traits object. Returns the sanitized payload\n * to send + a list of drops/modifications.\n *\n * Pass `workspace_id` so warning rate-limits scope per workspace; an\n * agency user switching workspaces won't have their warning budget\n * consumed cross-workspace.\n */\n process(\n traits: Record<string, unknown> | null | undefined,\n workspace_id?: string | null,\n ): TraitResult {\n const sanitized: Record<string, unknown> = {};\n const drops: TraitDrop[] = [];\n if (!traits || typeof traits !== 'object') {\n return { sanitized, drops };\n }\n\n for (const rawKey of Object.keys(traits)) {\n // 1. Reserved-prefix check runs on the ORIGINAL key, BEFORE\n // normalization. Otherwise `system.foo` would normalize to\n // `system_foo` (the `.` collapses to `_`) and bypass the\n // reserved check. This is the same order-of-operations bug\n // we caught at smoke time on the backend (2026-05-13).\n const prefix = startsWithReserved(rawKey);\n if (prefix !== null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'reserved_prefix',\n reason: `key uses reserved namespace ${JSON.stringify(prefix)}`,\n });\n continue;\n }\n\n // 2. Normalize key casing.\n const normalizedKey = normalizeKey(rawKey);\n if (normalizedKey === null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'bad_key_format',\n reason: `key reduced to empty after normalization`,\n });\n continue;\n }\n\n // 3. Re-check reserved on normalized form (belt-and-braces).\n if (startsWithReserved(normalizedKey) !== null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'reserved_prefix',\n reason: `normalized key ${JSON.stringify(normalizedKey)} still uses a reserved namespace`,\n });\n continue;\n }\n\n // 4. Value-side guards.\n let value = traits[rawKey];\n\n // 4a. Truncate long strings (soft cap); reject huge strings (hard cap).\n if (typeof value === 'string') {\n if (value.length > KEY_HARD_VALUE_CAP) {\n drops.push({\n originalKey: rawKey,\n verdict: 'value_too_long',\n reason: `value length ${value.length} exceeds hard cap ${KEY_HARD_VALUE_CAP}`,\n });\n continue;\n }\n if (value.length > KEY_SOFT_VALUE_CAP) {\n drops.push({\n originalKey: rawKey,\n verdict: 'value_too_long',\n reason: `value truncated from ${value.length} to ${KEY_SOFT_VALUE_CAP} chars`,\n });\n value = value.slice(0, KEY_SOFT_VALUE_CAP);\n }\n }\n\n // 4b. Date normalization — only when the key hints at date semantics\n // AND the value looks string-shaped. Numeric values are assumed\n // epoch already. Free-form strings on non-date-keyed properties\n // pass through (caller meant a string).\n if (looksLikeDateKey(normalizedKey) && typeof value === 'string') {\n const parsed = parseDateValue(value);\n if (parsed === null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'bad_date_format',\n reason: `value ${JSON.stringify(value)} on date-keyed field ${JSON.stringify(normalizedKey)} didn't parse as ISO-8601 / epoch`,\n });\n continue;\n }\n value = parsed;\n }\n\n sanitized[normalizedKey] = value;\n }\n\n // Emit first-3-per-verdict warnings (silent past the cap).\n for (const drop of drops) {\n this.maybeWarn(workspace_id ?? null, drop);\n }\n\n return { sanitized, drops };\n }\n\n private maybeWarn(workspace_id: string | null, drop: TraitDrop): void {\n const ws = workspace_id ?? '__no_workspace__';\n let perVerdict = this.warnCounts.get(ws);\n if (!perVerdict) {\n perVerdict = new Map();\n this.warnCounts.set(ws, perVerdict);\n }\n const count = perVerdict.get(drop.verdict) ?? 0;\n if (count >= TraitGovernor.WARN_CAP) return;\n perVerdict.set(drop.verdict, count + 1);\n warnOnConsole(\n `[Active Reach SDK] trait ${drop.verdict}: ${drop.reason} ` +\n `(original key: ${JSON.stringify(drop.originalKey)}). ` +\n `Backend will reject; fix the SDK call to silence this warning.`,\n );\n }\n}\n","/**\n * UserNamespace — typed PII + per-channel opt-in setters that delegate to\n * `Aegis.identify()`. WebEngage-/CleverTap-parity ergonomics.\n *\n * Pre-login `setX` calls accumulate in `pendingTraits` and flush as one\n * identify() when `login()` fires — avoids polluting the contact graph\n * with anonymous-id-as-userId rows.\n *\n * See docs/architecture/SDK_USER_NAMESPACE_AND_SCREEN.md.\n */\n\nimport type { Aegis } from './analytics';\nimport { logger } from '../utils/logger';\n\nexport type OptInChannel =\n | 'email'\n | 'sms'\n | 'push'\n | 'webpush'\n | 'whatsapp'\n | 'rcs'\n | 'inapp';\n\nconst VALID_CHANNELS: ReadonlySet<OptInChannel> = new Set<OptInChannel>([\n 'email',\n 'sms',\n 'push',\n 'webpush',\n 'whatsapp',\n 'rcs',\n 'inapp',\n]);\n\n// Conservative RFC 5322 subset — same shape every CEP peer uses on the\n// browser side. Server-side validation is authoritative.\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n// E.164: leading '+' then 8–15 digits.\nconst PHONE_RE = /^\\+[1-9]\\d{7,14}$/;\n// SHA-256 hex string: exactly 64 hex chars.\nconst SHA256_HEX_RE = /^[a-f0-9]{64}$/i;\n// ISO date: YYYY-MM-DD (we don't accept full ISO datetime — birth dates\n// don't have a time component).\nconst ISO_DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nexport class UserNamespace {\n private readonly aegis: Aegis;\n private pendingTraits: Record<string, unknown> = {};\n\n constructor(aegis: Aegis) {\n this.aegis = aegis;\n }\n\n /**\n * Authoritative identity write. Flushes any pending pre-login traits\n * along with the supplied `traits` argument as a single identify call.\n */\n login(userId: string, traits?: Record<string, unknown>): void {\n if (typeof userId !== 'string' || userId.length === 0) {\n logger.warn('[user.login] userId must be a non-empty string');\n return;\n }\n const merged = { ...this.pendingTraits, ...(traits || {}) };\n this.pendingTraits = {};\n this.aegis.identify(userId, merged);\n }\n\n /**\n * Drops the current userId and any pending pre-login traits. Does NOT\n * fire a server-side logout event — that's a separate explicit `track`.\n */\n logout(): void {\n this.pendingTraits = {};\n this.aegis.reset();\n }\n\n setAttribute(key: string, value: unknown): void {\n if (typeof key !== 'string' || key.length === 0) {\n logger.warn('[user.setAttribute] key must be a non-empty string');\n return;\n }\n this.writeTraits({ [key]: value });\n }\n\n setAttributes(map: Record<string, unknown>): void {\n if (!map || typeof map !== 'object') {\n logger.warn('[user.setAttributes] map must be an object');\n return;\n }\n this.writeTraits(map);\n }\n\n setEmail(email: string): void {\n if (!EMAIL_RE.test(email)) {\n logger.warn('[user.setEmail] invalid email format');\n return;\n }\n this.writeTraits({ email: email.toLowerCase() });\n }\n\n setPhone(phone: string): void {\n if (!PHONE_RE.test(phone)) {\n logger.warn('[user.setPhone] phone must be E.164 (e.g. +15551234567)');\n return;\n }\n this.writeTraits({ phone });\n }\n\n setHashedEmail(sha256Hex: string): void {\n if (!SHA256_HEX_RE.test(sha256Hex)) {\n logger.warn('[user.setHashedEmail] expected 64-char hex SHA-256');\n return;\n }\n this.writeTraits({ email_sha256: sha256Hex.toLowerCase() });\n }\n\n setHashedPhone(sha256Hex: string): void {\n if (!SHA256_HEX_RE.test(sha256Hex)) {\n logger.warn('[user.setHashedPhone] expected 64-char hex SHA-256');\n return;\n }\n this.writeTraits({ phone_sha256: sha256Hex.toLowerCase() });\n }\n\n setBirthDate(iso: string): void {\n if (!ISO_DATE_RE.test(iso)) {\n logger.warn('[user.setBirthDate] expected YYYY-MM-DD');\n return;\n }\n this.writeTraits({ birth_date: iso });\n }\n\n setOptIn(channel: OptInChannel, granted: boolean): void {\n if (!VALID_CHANNELS.has(channel)) {\n logger.warn(`[user.setOptIn] unknown channel: ${channel}`);\n return;\n }\n if (typeof granted !== 'boolean') {\n logger.warn('[user.setOptIn] granted must be a boolean');\n return;\n }\n this.writeTraits({ [`opt_in_${channel}`]: granted });\n }\n\n /**\n * HMAC-signed identity token. Ingress-side verification not yet wired —\n * the trait is forwarded as-is and persisted on the contact record\n * until that lands.\n */\n setSecureToken(token: string): void {\n if (typeof token !== 'string' || token.length === 0) {\n logger.warn('[user.setSecureToken] token must be a non-empty string');\n return;\n }\n this.writeTraits({ _secure_token: token });\n }\n\n /**\n * Test/inspection hook — returns a copy of the pending traits buffer.\n * Used by the e2e smoke test to assert pre-login accumulation.\n */\n _getPendingTraits(): Record<string, unknown> {\n return { ...this.pendingTraits };\n }\n\n // ---------------------------------------------------------------------------\n\n private writeTraits(traits: Record<string, unknown>): void {\n const userId = this.aegis.getUserId();\n if (userId) {\n this.aegis.identify(userId, traits);\n return;\n }\n Object.assign(this.pendingTraits, traits);\n }\n}\n","import {\n AegisConfig,\n AegisConfigInternal,\n DEFAULT_CONFIG,\n CellEndpoint,\n} from '../types/config';\nimport {\n TrackEvent,\n IdentifyEvent,\n PageEvent,\n GroupEvent,\n AliasEvent,\n ScreenEvent,\n AegisEvent,\n} from '../types/events';\nimport { Plugin } from '../types/plugin';\nimport { Identity } from '../utils/identity';\nimport { SessionManager } from './session';\nimport { EventQueue } from './queue';\nimport { Transport } from './transport';\nimport { Storage } from '../utils/storage';\nimport { buildContext, isBot } from '../utils/device';\nimport { generateMessageId } from '../utils/uuid';\nimport { logger } from '../utils/logger';\nimport { PluginRegistry } from '../plugins/registry';\nimport { ConsentManager, ConsentPreferences, ConsentCategory } from '../utils/consent';\nimport { captureMetaCookies, redactMetaCookies } from '../utils/meta-cookies';\nimport {\n parseAdClickIDs,\n hasAdClickIDs,\n parseLinkAttribution,\n type LinkAttribution,\n} from '../utils/url-parser';\nimport { redactUrl } from '../utils/redact-url';\nimport { EcommerceTracker } from '../ecommerce';\nimport { RateLimiter } from './rate-limiter';\nimport { NameGovernor, EventGovernanceHint, TraitGovernor } from '../governance';\nimport { UserNamespace } from './user-namespace';\n\n/** One redeemable reward for the identified contact (from `contact_entitlements`). */\nexport interface EntitlementItem {\n id: string;\n /** coupon | discount | points | free_delivery | freebie */\n type: string;\n label: string | null;\n /** Real, redeemable code (coupon/discount) — apply at checkout. */\n coupon_code: string | null;\n /** Awarded points (points grants). */\n points: number | null;\n /** Outlet the grant is attributed to; null = org-wide. */\n location_id: string | null;\n /** ISO 8601 expiry, or null for no expiry. */\n expires_at: string | null;\n}\n\n/** Response of `aegis.getRewards()` / `GET /api/v1/sdk/entitlements`. */\nexport interface RewardsResponse {\n contact_id: string;\n loyalty: { points_balance: number; tier: string | null };\n entitlements: EntitlementItem[];\n}\n\nexport class Aegis {\n private config: AegisConfigInternal | null = null;\n private storage!: Storage;\n private identity!: Identity;\n /** Server-minted proof of the current contact (C1b). No setter is wired\n * from `identify()` today — a host with a runtime (AegisMessageRuntime)\n * calls `setContactToken()` alongside `runtime.updateContactId()`. */\n private contactToken?: string;\n private session: SessionManager | null = null;\n private queue: EventQueue | null = null;\n private transport: Transport | null = null;\n private plugins: PluginRegistry = new PluginRegistry();\n private initPromise: Promise<void> | null = null;\n private consent: ConsentManager | null = null;\n private _ecommerce: EcommerceTracker | null = null;\n private _user: UserNamespace | null = null;\n private rateLimiter: RateLimiter | null = null;\n private nameGovernor: NameGovernor = new NameGovernor();\n // P10.5 — client-side guards for trait writes. Mirrors the BE\n // chokepoint in `record_attribute_keys` so developers see console\n // warnings about reserved-prefix / bad-key / bad-date traits at\n // dev time, BEFORE the data hits production.\n private traitGovernor: TraitGovernor = new TraitGovernor();\n private _lastPageUrl: string | null = null;\n private _popstateHandler: (() => void) | null = null;\n private _originalPushState: typeof history.pushState | null = null;\n private _originalReplaceState: typeof history.replaceState | null = null;\n\n // B1.2 — last messageId per event name. Tenants who run their own Meta\n // Pixel via GTM call `aegis.lastEventId('Purchase')` to retrieve the id\n // we generated, then pass it as `eventID` to their `fbq('track', ...)`\n // call. Meta dedups on (pixel_id, event_name, event_id) so the\n // browser-fired Pixel event and our server-side CAPI event collapse to\n // one. See docs/integrations/META_PIXEL_COEXISTENCE.md.\n private _lastEventIds: Map<string, string> = new Map();\n\n // Plugin-handshake P1 — workspace_code allowlist for path-segment\n // detection. Ingested from the bootstrap response via\n // `ingestWorkspaceCodes()`. Empty set = no codes registered for this\n // org (single-outlet tenant); path-detection cascade is skipped.\n private _workspaceCodes: Set<string> = new Set();\n\n // Plugin-handshake P1 (Track E) — runtime workspace selection via\n // aegis.setWorkspace(). Persisted to sessionStorage for the page-load\n // session so SPA navigations + reloads on the same outlet inherit it.\n private _runtimeWorkspace: string | null = null;\n\n // P15.1 — runtime LOCATION selection (outlet code or id), via\n // aegis.setLocation(). Independent of _runtimeWorkspace under the\n // post-2026-05-26 hierarchy: workspace = brand-tenant, location =\n // outlet within a brand. Both can be set on the same SDK instance\n // (e.g. multi-brand agency dashboard navigating between brands +\n // selecting outlets within each).\n private _runtimeLocation: string | null = null;\n\n async init(\n writeKey: string,\n config?: Partial<AegisConfig>\n ): Promise<void> {\n if (this.initPromise) {\n return this.initPromise;\n }\n\n this.initPromise = this._init(writeKey, config);\n return this.initPromise;\n }\n\n private async _init(\n writeKey: string,\n config?: Partial<AegisConfig>\n ): Promise<void> {\n if (this.config?.initialized) {\n logger.warn('SDK already initialized');\n return;\n }\n\n if (this.shouldRespectDNT(config)) {\n logger.info('Do Not Track is enabled, SDK disabled');\n return;\n }\n\n if (isBot()) {\n logger.info('Bot detected, SDK disabled');\n return;\n }\n\n this.config = this.buildConfig(writeKey, config);\n\n if (this.config.debug) {\n logger.enable();\n }\n\n logger.info('Initializing Active Reach SDK...', this.config);\n\n this.storage = new Storage({\n cookieDomain: this.config.cookie_domain,\n secureCookie: this.config.secure_cookie,\n crossDomain: this.config.cross_domain_tracking,\n });\n\n this.identity = new Identity(this.storage);\n\n this.consent = new ConsentManager(this.storage, {\n defaultConsent: this.config.default_consent,\n waitForConsent: this.config.wait_for_consent,\n });\n\n if (this.config.integrate_onetrust) {\n this.consent.integrateOneTrust();\n }\n\n if (this.config.integrate_cookiebot) {\n this.consent.integrateCookiebot();\n }\n\n if (this.config.integrate_google_consent_mode) {\n this.consent.integrateGoogleConsentMode();\n }\n\n this.session = new SessionManager(\n this.storage,\n this.config.session_timeout\n );\n\n this.captureAdClickIDsOnInit();\n\n this.transport = new Transport(\n {\n writeKey: this.config.write_key,\n apiHost: this.config.api_host,\n cellEndpoints: this.config.cell_endpoints,\n preferredRegion: this.config.preferred_region,\n autoRegionDetection: this.config.auto_region_detection,\n requestTimeout: this.config.request_timeout,\n retryFailedRequests: this.config.retry_failed_requests,\n maxRetries: this.config.max_retries,\n retryBackoffMultiplier: this.config.retry_backoff_multiplier,\n },\n this.storage\n );\n\n this.config.active_cell = this.transport.getActiveCell();\n\n this.queue = new EventQueue(\n {\n batchSize: this.config.batch_size,\n batchInterval: this.config.batch_interval,\n enableOfflineMode: this.config.enable_offline_mode,\n maxOfflineEvents: this.config.max_offline_events,\n },\n this.transport,\n this.storage\n );\n\n this.rateLimiter = new RateLimiter({\n burstCapacity: this.config.rate_limit_burst,\n refillPerSecond: this.config.rate_limit_per_second,\n onDropBatch: (count, sampleName) => this.emitRateLimitMeta(count, sampleName),\n });\n\n await this.initializePlugins();\n\n if (this.config.auto_page_view) {\n this.page();\n this.startSPATracking();\n }\n\n logger.info('Active Reach SDK initialized successfully');\n }\n\n /**\n * SPA navigation tracking — monkey-patches pushState/replaceState and\n * listens for popstate to fire page() on every client-side route change.\n */\n private startSPATracking(): void {\n this._lastPageUrl = window.location.href;\n\n const onRouteChange = () => {\n // Debounce: only fire if URL actually changed\n const currentUrl = window.location.href;\n if (currentUrl === this._lastPageUrl) return;\n this._lastPageUrl = currentUrl;\n\n // Small delay to let the framework update document.title\n setTimeout(() => {\n // A host that mounts one SDK instance per page (Next.js route\n // components) navigates via pushState BEFORE the old page unmounts;\n // the old instance's patched pushState schedules this callback and is\n // then destroyed. It must not emit — the new page's instance fires its\n // own page view on init, so emitting here double-counts the view.\n if (!this._popstateHandler) return;\n this.page();\n }, 50);\n };\n\n // Listen for browser back/forward\n this._popstateHandler = onRouteChange;\n window.addEventListener('popstate', this._popstateHandler);\n\n // Monkey-patch pushState and replaceState\n this._originalPushState = history.pushState.bind(history);\n this._originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this._originalPushState!(...args);\n onRouteChange();\n };\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this._originalReplaceState!(...args);\n onRouteChange();\n };\n }\n\n private stopSPATracking(): void {\n if (this._popstateHandler) {\n window.removeEventListener('popstate', this._popstateHandler);\n this._popstateHandler = null;\n }\n\n if (this._originalPushState) {\n history.pushState = this._originalPushState;\n this._originalPushState = null;\n }\n\n if (this._originalReplaceState) {\n history.replaceState = this._originalReplaceState;\n this._originalReplaceState = null;\n }\n }\n\n private buildConfig(\n writeKey: string,\n config?: Partial<AegisConfig>\n ): AegisConfigInternal {\n return {\n ...DEFAULT_CONFIG,\n ...config,\n write_key: writeKey,\n initialized: true,\n } as AegisConfigInternal;\n }\n\n private shouldRespectDNT(config?: Partial<AegisConfig>): boolean {\n const respectDNT = config?.respect_dnt !== false;\n if (!respectDNT) return false;\n\n const dnt =\n navigator.doNotTrack ||\n (window as any).doNotTrack ||\n (navigator as any).msDoNotTrack;\n\n return dnt === '1' || dnt === 'yes';\n }\n\n private captureAdClickIDsOnInit(): void {\n if (!this.session) return;\n\n const adClickIDs = parseAdClickIDs();\n\n if (hasAdClickIDs(adClickIDs)) {\n // The landing page is persisted and rides every event's context as\n // `landingPage` — redact its credential params before it is stored.\n this.session.setAdClickIDs(adClickIDs, redactUrl(window.location.href));\n logger.info('Ad click IDs captured on page load:', adClickIDs);\n }\n\n // The Aegis short-link redirect stamps `aegis_code` (+ the link row's\n // attribution) on the landing URL. Captured once, here, beside the ad\n // click ids: it is the join key that names the send which brought this\n // visitor, and it has to outlive the landing URL to reach the order.\n const linkAttribution = parseLinkAttribution();\n if (linkAttribution.aegis_code) {\n this.session.setLinkAttribution(linkAttribution);\n }\n }\n\n private async initializePlugins(): Promise<void> {\n if (!this.config) return;\n\n for (const pluginName of this.config.plugins) {\n logger.debug(`Initializing plugin: ${pluginName}`);\n }\n\n await this.plugins.executeHook('init', this.config);\n }\n\n use(plugin: Plugin): void {\n this.plugins.register(plugin);\n\n if (this.config?.initialized && plugin.init) {\n Promise.resolve(plugin.init(this.config)).catch((error: any) => {\n logger.error(`Failed to initialize plugin \"${plugin.name}\":`, error);\n });\n }\n }\n\n /**\n * Symmetric counterpart to `use(plugin)`. Calls the plugin's `destroy()`\n * hook (if defined) and removes it from the registry so its hooks no\n * longer fire on subsequent events. Safe to call with a name that\n * isn't registered — logs a warning and returns.\n *\n * Primary use case: React components that register a plugin in\n * `useEffect` and need to unregister in the cleanup function so HMR /\n * provider remount doesn't leak listeners. See cashier-portal's\n * MetaPixelInjector for an example.\n */\n removePlugin(name: string): void {\n this.plugins.unregister(name);\n }\n\n /**\n * Plugin-handshake P1 — ingest the workspace_code allowlist from the\n * bootstrap response. Pass `result.workspaceCodes` from `bootstrap()`\n * here so the SDK can do path-segment workspace detection on\n * customer-facing pages like `/south/rewards`, `/ghatkopar/feedback`.\n *\n * Empty array is the right answer for single-outlet tenants — SDK\n * will skip the path cascade and fall through to query param /\n * setWorkspace() / gateway origin lookup.\n *\n * Safe to call before init() — the set is consulted at event-fire time.\n */\n ingestWorkspaceCodes(codes: string[] | null | undefined): void {\n this._workspaceCodes = new Set(\n (codes ?? []).filter((c) => typeof c === 'string' && c.length > 0),\n );\n logger.debug('Workspace codes ingested', { count: this._workspaceCodes.size });\n }\n\n /**\n * Plugin-handshake P1 (Track E) — explicitly set the workspace for\n * subsequent events. Used by:\n * - SPAs where workspace context changes via in-app routing\n * - Mobile / RN apps that have no URL\n * - Custom integrations that resolve workspace from their own state\n *\n * Pass null to clear (or call clearWorkspace()).\n *\n * Persisted to sessionStorage so SPA reloads / page transitions on the\n * same outlet inherit it without re-calling.\n */\n setWorkspace(codeOrId: string | null): void {\n this._runtimeWorkspace = codeOrId;\n if (typeof window !== 'undefined' && window.sessionStorage) {\n try {\n if (codeOrId) {\n window.sessionStorage.setItem('aegis_runtime_ws', codeOrId);\n } else {\n window.sessionStorage.removeItem('aegis_runtime_ws');\n }\n } catch {\n // Quota / private mode — non-fatal; in-memory value still applies.\n }\n }\n logger.debug('Runtime workspace set', { value: codeOrId });\n }\n\n /** Symmetric helper to clear the runtime workspace. */\n clearWorkspace(): void {\n this.setWorkspace(null);\n }\n\n // ── P15.1 — location (outlet) facade ─────────────────────────\n //\n // Under the post-2026-05-26 canonical hierarchy:\n // workspace_id = brand-tenant (one per brand)\n // location_id = outlet within a brand\n // The two are distinct dimensions. Both can be stamped on the\n // same event. These methods mirror the workspace facade but\n // resolve the outlet specifically.\n\n /**\n * P15.1 — ingest the outlet-code allowlist from bootstrap. The\n * SDK consults this set when reading the URL path segment to\n * decide whether `location.pathname.split('/')[1]` is a valid\n * outlet code vs a generic route.\n *\n * Empty array = single-outlet tenant; SDK skips the path-segment\n * cascade for location resolution.\n *\n * Safe to call before init(); the set is consulted at event-\n * fire time.\n */\n ingestLocationCodes(codes: string[] | null | undefined): void {\n // Shares the same underlying set as workspace codes — under the\n // new hierarchy the path segment IS the outlet code (was\n // workspace code pre-2026-05-26 when the dimensions collapsed).\n // The set is consulted by both `getPathWorkspaceCode()` and\n // `getPathLocationCode()` to avoid maintaining two copies of\n // the same allowlist.\n this._workspaceCodes = new Set(\n (codes ?? []).filter((c) => typeof c === 'string' && c.length > 0),\n );\n logger.debug('Location codes ingested', { count: this._workspaceCodes.size });\n }\n\n /**\n * P15.1 — explicitly set the active outlet for subsequent events.\n * Used by:\n * - Storefronts where outlet picker fires `aegis.setLocation()`\n * on selection (so events that follow carry that outlet's\n * location_id without the SDK having to re-parse the URL).\n * - SPAs where outlet context changes via in-app routing.\n * - Mobile/RN apps that have no URL.\n *\n * Pass null to clear (or call clearLocation()).\n *\n * Persisted to sessionStorage so SPA reloads + route transitions\n * on the same outlet inherit it without re-calling.\n */\n setLocation(codeOrId: string | null): void {\n this._runtimeLocation = codeOrId;\n if (typeof window !== 'undefined' && window.sessionStorage) {\n try {\n if (codeOrId) {\n window.sessionStorage.setItem('aegis_runtime_location', codeOrId);\n } else {\n window.sessionStorage.removeItem('aegis_runtime_location');\n }\n } catch {\n // Quota / private mode — non-fatal; in-memory value still applies.\n }\n }\n logger.debug('Runtime location set', { value: codeOrId });\n }\n\n /** Symmetric helper to clear the runtime location. */\n clearLocation(): void {\n this.setLocation(null);\n }\n\n /**\n * P15.1 — same logic as `getPathWorkspaceCode()` but expresses\n * the semantic correctly: under the new hierarchy, the first\n * path segment after the brand subdomain IS the outlet code.\n * Returns `undefined` when no outlet code matches the allowlist.\n */\n private getPathLocationCode(): string | undefined {\n // Same allowlist as workspace codes (single set, two names).\n return this.getPathWorkspaceCode();\n }\n\n /**\n * P15.1 — resolve the effective `location_id` for the next event.\n * Independent of `getEffectiveWorkspaceId()`: both run on every\n * event so workspace_id (brand) and location_id (outlet) land\n * on the same event when both are resolvable.\n *\n * Cascade (highest precedence first):\n * 1. `this.config.location_id` — explicit operator config.\n * 2. `?location=` query param (also accepts `?ws=` for back-\n * compat with storefronts that pass outlet via the old\n * param name — the value is the same; param naming only).\n * 3. URL path segment matching the outlet-code allowlist.\n * 4. Runtime `aegis.setLocation()` (in-memory).\n * 5. sessionStorage `aegis_runtime_location`.\n * 6. `undefined` — event-ingress treats as brand-tier\n * (no outlet attribution).\n */\n public getEffectiveLocationId(): string | undefined {\n // 1. Explicit operator config wins\n const configured = (this.config as { location_id?: string } | undefined)\n ?.location_id;\n if (configured) return configured;\n if (typeof window === 'undefined') return undefined;\n\n // 2. ?location= / ?ws= query param (both accepted)\n try {\n const search = new URLSearchParams(window.location.search);\n const loc = search.get('location') || search.get('ws');\n if (loc && loc.length > 0) return loc;\n } catch {\n /* malformed search — fall through */\n }\n\n // 3. URL path segment matching outlet-code allowlist\n const pathLoc = this.getPathLocationCode();\n if (pathLoc) return pathLoc;\n\n // 4. Runtime aegis.setLocation() value (in-memory)\n if (this._runtimeLocation) return this._runtimeLocation;\n\n // 5. sessionStorage fallback (set by prior setLocation() this session)\n try {\n if (window.sessionStorage) {\n const cached = window.sessionStorage.getItem('aegis_runtime_location');\n if (cached && cached.length > 0) return cached;\n }\n } catch {\n /* private mode — fall through */\n }\n\n // 6. No outlet resolvable — event is brand-tier\n return undefined;\n }\n\n /**\n * Inspect the URL's first path segment and return it if it's in the\n * org's workspace_code allowlist. Skips well-known non-workspace prefixes\n * (e.g. `b` for bill short codes; `s` is the storefront app slug).\n *\n * Returns undefined when:\n * - window is undefined (SSR / Node)\n * - path is empty / root\n * - first segment isn't in `_workspaceCodes` (e.g. /products, /cart)\n *\n * The returned value is a workspace CODE (slug like \"south\"), not a\n * UUID. The gateway normalizes code → UUID server-side via\n * workspace_subaccounts lookup with Redis cache.\n */\n private getPathWorkspaceCode(): string | undefined {\n if (typeof window === 'undefined') return undefined;\n if (this._workspaceCodes.size === 0) return undefined;\n try {\n const segments = window.location.pathname.split('/').filter(Boolean);\n if (segments.length === 0) return undefined;\n const first = segments[0].toLowerCase();\n return this._workspaceCodes.has(first) ? first : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Resolve the effective `workspace_id` for the next event.\n *\n * Cascade (highest precedence first):\n * 1. `this.config.workspace_id` — explicit operator config (headless\n * SDK usage, server-rendered apps, native shells).\n * 2. `?ws=` query param on the current URL — the P3 storefront URL\n * contract. The outlet picker writes this via `router.replace`.\n * 3. URL path segment (P1 Track A) — `/south/rewards` → \"south\" when\n * \"south\" is in the org's workspace_code allowlist.\n * 4. `aegis.setWorkspace()` runtime override (P1 Track E) — SPAs +\n * mobile + custom integrations.\n * 5. sessionStorage `aegis_runtime_ws` — persists setWorkspace across\n * reloads/SPA route changes.\n * 6. `undefined` — gateway falls back to `resolveByOrigin` lookup\n * against the property's allowed_origins.\n *\n * The returned value can be EITHER a UUID (from config / ?ws=) or a\n * workspace CODE slug (from path / setWorkspace). The gateway\n * normalizes slug → UUID server-side; analytics layer never needs to\n * worry about the difference.\n *\n * See docs/architecture/MULTI_OUTLET_URL_STRATEGY.md §2.3 and\n * docs/architecture/PLUGIN_HANDSHAKE_AUTOMATION.md §2.\n */\n /**\n * Public so peer SDK surfaces (AegisMessageRuntime / AegisInAppManager /\n * AegisWidgetManager) can plumb the same\n * resolved workspace into their own gateway POSTs. Each manager calls\n * the gateway on its own endpoint (`/v1/in-app/events`,\n * `/v1/widgets/track-event`); without this,\n * those events arrive at event-ingress with no workspace_id stamped\n * → impressions/clicks fall back to the org's primary workspace and\n * cross-outlet attribution breaks.\n */\n public getEffectiveWorkspaceId(): string | undefined {\n // 1. Explicit operator config wins\n const configured = this.config?.workspace_id;\n if (configured) return configured;\n if (typeof window === 'undefined') return undefined;\n\n // 2. ?ws= query param\n try {\n const ws = new URLSearchParams(window.location.search).get('ws');\n if (ws && ws.length > 0) return ws;\n } catch {\n /* malformed search — fall through */\n }\n\n // 3. URL path segment matching workspace_code allowlist\n const pathWs = this.getPathWorkspaceCode();\n if (pathWs) return pathWs;\n\n // 4. Runtime aegis.setWorkspace() value (in-memory)\n if (this._runtimeWorkspace) return this._runtimeWorkspace;\n\n // 5. sessionStorage fallback (set by prior setWorkspace() this session)\n try {\n if (window.sessionStorage) {\n const cached = window.sessionStorage.getItem('aegis_runtime_ws');\n if (cached && cached.length > 0) return cached;\n }\n } catch {\n /* private mode — fall through */\n }\n\n // 6. Gateway handles via resolveByOrigin\n return undefined;\n }\n\n track(eventName: string, properties?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n const messageId = generateMessageId();\n const event: TrackEvent = {\n type: 'track',\n event: eventName,\n properties: properties || {},\n messageId,\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n location_id: this.getEffectiveLocationId(),\n context: buildContext(this.config!, this.session!),\n };\n\n // Record so tenants who run their own Meta Pixel via GTM can pull the\n // event_id and pass it as `eventID` in fbq('track', ...). See B1.2.\n this._lastEventIds.set(eventName, messageId);\n\n this.captureEvent(event);\n }\n\n identify(userId: string, traits?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n // P10.5 — apply trait governance BEFORE setUserId + send. The BE\n // applies the same guards in `record_attribute_keys`, but doing it\n // client-side lets us warn the developer in console.warn and emit\n // sanitized values so what they see locally matches what gets stored.\n const wsForGovernor = this.getEffectiveWorkspaceId() || null;\n const { sanitized: governedTraits } = this.traitGovernor.process(traits, wsForGovernor);\n\n // C1b: a token is bound to ONE contact — identifying a different one\n // without a fresh `setContactToken()` call must not keep the old proof.\n if (this.identity.getUserId() !== userId) {\n this.contactToken = undefined;\n }\n this.identity.setUserId(userId, governedTraits);\n\n const event: IdentifyEvent = {\n type: 'identify',\n traits: governedTraits,\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: userId,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n location_id: this.getEffectiveLocationId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n page(name?: string, properties?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n const event: PageEvent = {\n type: 'page',\n name: name || document.title,\n properties: properties || {},\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n location_id: this.getEffectiveLocationId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n group(groupId: string, traits?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n // P10.5 — same trait governance as identify(); group traits ride\n // the same `record_attribute_keys` BE chokepoint.\n const wsForGovernor = this.getEffectiveWorkspaceId() || null;\n const { sanitized: governedTraits } = this.traitGovernor.process(traits, wsForGovernor);\n\n const event: GroupEvent = {\n type: 'group',\n groupId,\n traits: governedTraits,\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n alias(newUserId: string): void {\n if (!this.assertInitialized()) return;\n\n const { previousId } = this.identity.alias(newUserId);\n\n const event: AliasEvent = {\n type: 'alias',\n previousId,\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: newUserId,\n sessionId: this.session!.getSessionId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n /**\n * SPA-logical screen view. Distinct from `page()` (URL-bound) — fires\n * when the user enters a logical surface like cart / checkout / drawer\n * without a URL change. Gateway maps `screen` → `engagement.screen_view`.\n */\n screen(name: string, properties?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n if (typeof name !== 'string' || name.length === 0) {\n logger.warn('aegis.screen: name must be a non-empty string');\n return;\n }\n\n const event: ScreenEvent = {\n type: 'screen',\n name,\n properties: properties || {},\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n location_id: this.getEffectiveLocationId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n private async captureEvent(event: AegisEvent): Promise<void> {\n if (this.config?.enable_consent_mode && this.consent) {\n if (event.type === 'track' || event.type === 'page') {\n if (!this.consent.hasConsent('analytics')) {\n logger.debug('Event blocked: analytics consent not granted');\n return;\n }\n }\n }\n\n // Event-name governance — drops novel names once the org's unique-event\n // cap is exhausted. Meta events (aegis.client.*) bypass the governor so\n // the telemetry channel stays open even under a cap-exhaustion storm.\n // See governance/name-governor.ts.\n if (event.type === 'track') {\n const trackEvent = event as TrackEvent;\n const isMetaEvent =\n typeof trackEvent.event === 'string' &&\n trackEvent.event.startsWith('aegis.client.');\n if (!isMetaEvent && !this.nameGovernor.shouldSend(trackEvent.event)) {\n return;\n }\n }\n\n if (this.rateLimiter) {\n const eventLabel =\n event.type === 'track' && (event as TrackEvent).event\n ? (event as TrackEvent).event\n : event.type;\n // Meta-events bypass the rate limiter to avoid recursion / lost telemetry.\n const isMetaEvent =\n event.type === 'track' &&\n typeof (event as TrackEvent).event === 'string' &&\n (event as TrackEvent).event.startsWith('aegis.client.');\n if (!isMetaEvent && !this.rateLimiter.tryConsume(eventLabel)) {\n return;\n }\n }\n\n // Auto-inject UTM attribution from the URL context into event properties\n // under the CANONICAL keys (`utm_*`, data_governance.canonical_properties)\n // so downstream consumers receive it without manual mapping.\n //\n // `campaign_id` is NEVER written here. It is ONLY ever an Aegis campaign\n // id; this block used to put `utm.campaign` (free text a tenant or an ad\n // platform chose) into it, which corrupted every reader of that slot\n // (`uniqExact(campaign_id)` on /live among them). History is left as it is\n // — no backfill. The legacy `campaign_source/_medium/_content/_term`\n // spellings are no longer written; readers resolve `utm_*` first and still\n // read the old spelling on historical events.\n const ctx = (event as any).context;\n const props = (event as any).properties;\n if (ctx?.campaign && props) {\n const utm = ctx.campaign;\n if (utm.source && !props.utm_source) props.utm_source = utm.source;\n if (utm.medium && !props.utm_medium) props.utm_medium = utm.medium;\n if (utm.campaign && !props.utm_campaign) props.utm_campaign = utm.campaign;\n if (utm.content && !props.utm_content) props.utm_content = utm.content;\n if (utm.term && !props.utm_term) props.utm_term = utm.term;\n }\n if (ctx?.adClickIDs && props) {\n const clicks = ctx.adClickIDs;\n // The explicit per-platform keys — each its own fact. `gbraid` / `wbraid`\n // are Google's iOS (ATT) click ids and are NOT gclids.\n if (clicks.gclid && !props.gclid) props.gclid = clicks.gclid;\n if (clicks.gbraid && !props.gbraid) props.gbraid = clicks.gbraid;\n if (clicks.wbraid && !props.wbraid) props.wbraid = clicks.wbraid;\n if (clicks.fbclid && !props.fbclid) props.fbclid = clicks.fbclid;\n // `campaign_click_id` is the merged \"some click id\" — kept for its\n // existing readers, and deliberately NOT a spelling of any single id\n // (it can hold an fbclid).\n const clickId = clicks.gclid || clicks.fbclid || clicks.ctwa_clid || clicks.msclkid || clicks.ttclid;\n if (clickId && !props.campaign_click_id) props.campaign_click_id = clickId;\n }\n // Which Aegis send brought this visitor — `aegis_code` (the join key) and\n // the link row's attribution, captured from the landing URL. Never `utm_*`.\n if (props && this.session) {\n const link = this.session.getLinkAttribution();\n if (link.aegis_code) {\n for (const [key, value] of Object.entries(link)) {\n if (value && !props[key]) props[key] = value;\n }\n }\n }\n\n // B1.2 — Auto-inject Meta companion cookies (_fbp, _fbc, fbclid) into\n // event properties so the cell-side CAPI service can pass them as\n // user_data fields to Meta. Lifts EMQ from ~6-7 (PII-only) to ≥8.0\n // without firing fbq() browser-side. fbp is self-generated if absent;\n // fbc only set when an inbound fbclid landed in the URL.\n // See libs/web-sdk/src/utils/meta-cookies.ts for the spec.\n if (props) {\n const meta = captureMetaCookies();\n if (meta.fbp && !props.fbp) props.fbp = meta.fbp;\n if (meta.fbc && !props.fbc) props.fbc = meta.fbc;\n if (meta.fbclid && !props.fbclid) props.fbclid = meta.fbclid;\n }\n\n // `fbc` / `fbp` are Meta cookie values — match keys for CAPI, never log\n // content. The debug line prints the event with them masked.\n logger.debug('Capturing event:', redactMetaCookies(event));\n\n const transformedEvent = await this.plugins.executeHookChain(\n 'beforeEventCapture',\n event\n );\n\n if (!transformedEvent) {\n logger.debug('Event cancelled by plugin');\n return;\n }\n\n this.queue!.push(transformedEvent);\n this.session!.incrementEventCount();\n\n await this.plugins.executeHook('afterEventCapture', transformedEvent);\n }\n\n /**\n * Emit a coalesced meta-event reporting client-side rate-limit drops.\n * Bypasses the limiter (see captureEvent guard).\n */\n private emitRateLimitMeta(count: number, sampleEventName: string | null): void {\n if (!this.config?.initialized || !this.session || !this.identity) return;\n const event: TrackEvent = {\n type: 'track',\n event: 'aegis.client.rate_limited',\n properties: {\n dropped_count: count,\n sample_event_name: sampleEventName,\n burst_capacity: this.config.rate_limit_burst,\n refill_per_second: this.config.rate_limit_per_second,\n },\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session.getSessionId(),\n context: buildContext(this.config, this.session),\n };\n this.queue?.push(event);\n }\n\n reset(): void {\n if (!this.assertInitialized()) return;\n\n this.identity.reset();\n // Drop pending pre-login traits buffered on the user namespace —\n // a reset() either through `aegis.reset()` or `aegis.user.logout()`\n // should leave no residual identity state behind.\n this._user = null;\n logger.info('User identity reset');\n }\n\n /**\n * Ingest the event-governance hint returned from `/v1/sdk/bootstrap`.\n *\n * Callers (Shopify pixel, snippet, react integration) run `bootstrap()`\n * themselves and pass the resulting `eventGovernance` field here so the\n * SDK can self-throttle novel event names before they hit the gateway.\n *\n * Passing null/undefined disables governance (Enterprise plan / outage\n * fail-open). Safe to call before `init()` — the hint is stored and\n * applied as soon as init completes.\n */\n ingestGovernanceHint(hint: EventGovernanceHint | null | undefined): void {\n this.nameGovernor.ingestHint(hint ?? null);\n logger.debug('Governance hint ingested', hint ? {\n k: hint.k,\n m: hint.m,\n remaining: hint.remaining_new_names,\n } : 'disabled');\n }\n\n /**\n * Drain the client-side drop counter and emit a meta-event. Called\n * periodically by the queue flush path so ops dashboards see novel-name\n * amplification patterns in near-real-time.\n */\n private emitGovernanceDropMeta(): void {\n if (!this.config?.initialized || !this.session || !this.identity) return;\n const report = this.nameGovernor.drainDropReport();\n if (!report) return;\n\n const event: TrackEvent = {\n type: 'track',\n event: 'aegis.client.name_governor_dropped',\n properties: {\n dropped_count: report.total,\n distinct_names: Object.keys(report.events).length,\n // Cap the payload — a runaway loop could produce thousands of names;\n // we ship the top 10 for diagnostics and the total for counting.\n sample_names: Object.entries(report.events)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([name, count]) => ({ name, count })),\n window_start: report.since,\n window_end: Date.now(),\n },\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session.getSessionId(),\n context: buildContext(this.config, this.session),\n };\n this.queue?.push(event);\n }\n\n async flush(): Promise<void> {\n if (!this.assertInitialized()) return;\n\n // Emit any coalesced governance-drop telemetry BEFORE flushing so the\n // meta event rides in the same batch as the rest.\n this.emitGovernanceDropMeta();\n await this.queue!.flush();\n }\n\n getAnonymousId(): string | null {\n if (!this.identity) return null;\n return this.identity.getAnonymousId();\n }\n\n getUserId(): string | null {\n if (!this.identity) return null;\n return this.identity.getUserId();\n }\n\n /**\n * C1b: register the server-minted proof for the CURRENT contact (as\n * returned by `getUserId()`). A host wiring `AegisMessageRuntime` calls\n * this alongside `runtime.updateContactId(id, token)` so `getRewards()`\n * can also present `X-Contact-Token`. Pass `undefined` to clear it (e.g.\n * on logout, or when the id changes without a fresh token).\n */\n setContactToken(token?: string): void {\n this.contactToken = token;\n }\n\n /**\n * The Aegis short-link attribution captured from this visitor's landing URL\n * (`aegis_code` + the link row's `aegis_campaign` / `aegis_journey` /\n * `aegis_playbook` / `aegis_button`), or `{}` when they did not arrive\n * through one. Hosts forward `aegis_code` with an order so the order can be\n * joined to the send that brought the shopper. Never contains `utm_*`, `k`,\n * `t` or `u`.\n */\n getLinkAttribution(): LinkAttribution {\n if (!this.session) return {};\n return { ...this.session.getLinkAttribution() };\n }\n\n /**\n * Fetch the identified contact's REDEEMABLE rewards — coupon codes the\n * storefront can auto-apply at checkout + loyalty points balance/tier.\n * Mirrors `GET /api/v1/sdk/entitlements`. Returns `null` when the SDK is\n * uninitialised, the contact is anonymous, the endpoint returns 204 (nothing\n * to show), or on any network/HTTP error — callers degrade gracefully.\n *\n * Scoped to the identified contact (own grants only) and, when an outlet is\n * resolvable (`getEffectiveLocationId()`), to org-wide + that outlet's grants.\n */\n async getRewards(): Promise<RewardsResponse | null> {\n const config = this.config;\n if (!config || !config.api_host || !this.identity) return null;\n const contactId = this.identity.getUserId();\n if (!contactId) return null;\n const apiHost = config.api_host.replace(/\\/$/, '');\n\n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': config.write_key,\n 'X-Contact-ID': contactId,\n 'X-User-ID': contactId,\n };\n // C1b: only send the token with its own contact.\n if (this.contactToken) headers['X-Contact-Token'] = this.contactToken;\n const orgId = (config as { organization_id?: string }).organization_id;\n if (orgId) headers['X-Organization-ID'] = orgId;\n const location = this.getEffectiveLocationId();\n if (location) headers['X-Location-Id'] = location;\n\n try {\n const resp = await fetch(`${apiHost}/api/v1/sdk/entitlements`, {\n method: 'GET',\n headers,\n credentials: 'include',\n });\n if (resp.status === 204 || !resp.ok) return null;\n return (await resp.json()) as RewardsResponse;\n } catch (err) {\n logger.warn('getRewards() failed', err);\n return null;\n }\n }\n\n getSessionId(): string | null {\n if (!this.session) return null;\n return this.session.getSessionId();\n }\n\n /**\n * B1.2 — Tenant-Pixel coexistence handoff. Returns the messageId of the\n * most recent `track('<eventName>', ...)` call, or null if no event of\n * that name has been tracked since SDK init.\n *\n * Use this from a tenant's own Meta Pixel script (e.g. via GTM) to pass\n * the same event_id to `fbq('track', '<eventName>', payload, {eventID: id})`.\n * Meta dedups on (pixel_id, event_name, event_id) so the browser-fired\n * Pixel event and our server-side CAPI event collapse to one — preventing\n * double-counting.\n *\n * Event names are case-sensitive (\"Purchase\" ≠ \"purchase\") — Meta's dedup\n * is also case-sensitive, so be exact.\n *\n * See docs/integrations/META_PIXEL_COEXISTENCE.md.\n */\n lastEventId(eventName: string): string | null {\n return this._lastEventIds.get(eventName) ?? null;\n }\n\n debug(enable: boolean = true): void {\n if (enable) {\n logger.enable();\n } else {\n logger.disable();\n }\n\n if (this.config) {\n this.config.debug = enable;\n }\n }\n\n setCell(cellEndpoint: CellEndpoint): void {\n if (!this.config) {\n logger.warn('SDK not initialized');\n return;\n }\n\n const existingCell = this.config.cell_endpoints.find(\n (c) => c.region === cellEndpoint.region\n );\n\n if (existingCell) {\n Object.assign(existingCell, cellEndpoint);\n } else {\n this.config.cell_endpoints.push(cellEndpoint);\n }\n\n logger.info('Cell endpoint configured:', cellEndpoint);\n }\n\n getCellInfo() {\n if (!this.transport) return null;\n\n return {\n activeCell: this.transport.getActiveCell(),\n latencies: this.transport.getRegionLatencies(),\n };\n }\n\n setConsent(preferences: Partial<ConsentPreferences>): void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return;\n }\n\n this.consent.setConsent(preferences);\n logger.info('Consent preferences updated');\n }\n\n grantConsent(category?: ConsentCategory): void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return;\n }\n\n if (category) {\n this.consent.setConsent({ [category]: true });\n } else {\n this.consent.grantAll();\n }\n }\n\n denyConsent(category?: ConsentCategory): void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return;\n }\n\n if (category) {\n this.consent.setConsent({ [category]: false });\n } else {\n this.consent.denyAll();\n }\n }\n\n hasConsent(category: ConsentCategory): boolean {\n if (!this.consent) {\n return true;\n }\n\n return this.consent.hasConsent(category);\n }\n\n getConsentPreferences(): ConsentPreferences | null {\n if (!this.consent) {\n return null;\n }\n\n return this.consent.getPreferences();\n }\n\n onConsentChange(callback: (preferences: ConsentPreferences) => void): () => void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return () => {};\n }\n\n return this.consent.onChange(callback);\n }\n\n private assertInitialized(): boolean {\n if (!this.config?.initialized) {\n logger.warn('SDK not initialized. Call aegis.init() first.');\n return false;\n }\n return true;\n }\n\n get ecommerce(): EcommerceTracker {\n if (!this._ecommerce) {\n this._ecommerce = new EcommerceTracker(this);\n }\n return this._ecommerce;\n }\n\n get user(): UserNamespace {\n if (!this._user) {\n this._user = new UserNamespace(this);\n }\n return this._user;\n }\n\n destroy(): void {\n this.stopSPATracking();\n\n if (this.queue) {\n this.queue.destroy();\n }\n\n if (this.session) {\n this.session.destroy();\n }\n\n if (this.transport) {\n this.transport.destroy();\n }\n\n if (this.rateLimiter) {\n this.rateLimiter.destroy();\n this.rateLimiter = null;\n }\n\n this.plugins.clear();\n\n logger.info('SDK destroyed');\n }\n}\n","/**\n * Canonical in-app CTA action model (CANONICAL_ACTION_MODEL_TRACKER.md).\n *\n * Every CTA across every in-app format resolves to ONE typed `AegisAction`, and\n * is delivered to ONE host handler (`onAction`) — the CEP-standard shape\n * (CleverTap KV-callback + deeplink, WebEngage/MoEngage/Netcore equivalents),\n * replacing the drifted set of bespoke `aegis:*` DOM events. The legacy events\n * were removed in the Phase E.2 hard cutover — `onAction` is the only host seam.\n */\n\nexport type AegisAction =\n | { type: 'dismiss' }\n | { type: 'request_push' }\n | { type: 'navigate'; url: string }\n | { type: 'open_product'; sku?: string; productId?: string; url?: string; platformProductId?: string; platformUrl?: string }\n | { type: 'open_cart' }\n | { type: 'open_login' }\n | {\n type: 'add_to_cart';\n sku?: string;\n /** Internal Aegis product id — what ACS's add-to-cart handler resolves. */\n productId?: string;\n /**\n * Platform-native product/variant id (Shopify variant, Woo product id),\n * when the rec card carries it (catalog-sync C.2). A plugin's onAction uses\n * THIS for a native add — NOT the internal `productId`. Undefined → the\n * plugin degrades to product search.\n */\n platformProductId?: string;\n /**\n * Platform-native default VARIANT id (Shopify /cart/add.js needs a\n * variant, not a product). Fast path for a plugin's native add — no\n * /products/{handle}.js round-trip. Undefined → the plugin resolves\n * the variant from platformProductId / the handle URL, or degrades to\n * product search.\n */\n platformVariantId?: string;\n qty?: number;\n title?: string;\n price?: unknown;\n imageUrl?: string;\n url?: string;\n }\n | { type: 'custom'; name: string; payload: Record<string, unknown> };\n\n/** Extra card/campaign context that helps classify a raw action URL. */\nexport interface ActionMeta {\n product_id?: string;\n sku?: string;\n title?: string;\n price?: unknown;\n image_url?: string;\n /** Platform-native ids/urls from the catalog sync (C.2); used by plugins. */\n platform_product_id?: string;\n platform_url?: string;\n}\n\nconst PDP_SKU_RE = /\\/p\\/([^/?#]+)/;\n\n/**\n * THE SDK's product-page path — `/p/{sku}`, the SKU encoded exactly like the\n * server builders (Python `quote(sku, safe='')`, which also escapes `!'()*`).\n * CANONICAL_PRODUCT_IDENTITY_SPEC R1/R2: the SKU is the public handle and the\n * page is brand root. A platform adapter overrides it via `productUrl`.\n */\nexport function productPath(sku: string): string {\n const encoded = encodeURIComponent(sku).replace(\n /[!'()*]/g,\n (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n return `/p/${encoded}`;\n}\n\n/**\n * Map a raw CTA target (an `aegis://<verb>?…` action verb, a `/p/{sku}` PDP path,\n * an http(s) URL, or empty) + card metadata → one canonical `AegisAction`.\n * Single source of truth for \"what does this CTA do\". Used by both the card\n * renderer and the campaign-CTA dispatcher so the two add-to-cart paths converge.\n */\nexport function parseAction(actionUrl: string | undefined, meta?: ActionMeta): AegisAction {\n const url = (actionUrl ?? '').trim();\n const m = meta ?? {};\n\n // 1. aegis:// action verbs (in-app behaviours, no navigation).\n if (url.startsWith('aegis://')) {\n const rest = url.slice('aegis://'.length);\n const [verb, qs] = rest.split('?');\n const params = new URLSearchParams(qs || '');\n switch (verb) {\n case 'dismiss':\n return { type: 'dismiss' };\n case 'request-push':\n case 'request_push':\n return { type: 'request_push' };\n case 'cart-add':\n case 'add-to-cart':\n return {\n type: 'add_to_cart',\n sku: params.get('sku') || m.sku || undefined,\n productId: params.get('product_id') || m.product_id || undefined,\n platformProductId: m.platform_product_id,\n qty: Number(params.get('qty')) || 1,\n title: m.title,\n price: m.price,\n imageUrl: m.image_url,\n url: m.platform_url,\n };\n case 'open-cart':\n case 'cart':\n return { type: 'open_cart' };\n case 'open-login':\n case 'login':\n return { type: 'open_login' };\n case 'open-product':\n case 'product': {\n const sku = params.get('sku') || m.sku || undefined;\n return { type: 'open_product', sku, productId: m.product_id, url: sku ? productPath(sku) : undefined };\n }\n default:\n return { type: 'custom', name: verb || 'unknown', payload: Object.fromEntries(params) };\n }\n }\n\n // 2. A PDP path (`…/p/{sku}`) → open_product.\n const pdp = PDP_SKU_RE.exec(url);\n if (pdp) {\n return {\n type: 'open_product',\n sku: decodeURIComponent(pdp[1]),\n productId: m.product_id,\n url,\n platformProductId: m.platform_product_id,\n platformUrl: m.platform_url,\n };\n }\n\n // 3. Any other non-empty URL → navigate (deeplink/web).\n if (url) {\n return { type: 'navigate', url };\n }\n\n // 4. No URL but the card carries a product (a rail \"Add\") → add_to_cart.\n if (m.sku || m.product_id) {\n return {\n type: 'add_to_cart',\n sku: m.sku,\n productId: m.product_id,\n platformProductId: m.platform_product_id,\n qty: 1,\n title: m.title,\n price: m.price,\n imageUrl: m.image_url,\n url: m.platform_url,\n };\n }\n\n return { type: 'dismiss' };\n}\n\n/**\n * The canonical fallback URL for an action when no host handler claims it — so a\n * CTA on a host without an `onAction`/listener still does something sensible\n * (the gap that made the rail \"Add\" a dead button on 3rd-party). `productUrl`\n * lets a platform adapter map a sku → its PDP shape (Shopify `/products/{handle}`\n * etc.); defaults to the canonical `/p/{sku}`.\n */\nexport function fallbackUrlForAction(\n a: AegisAction,\n productUrl: (sku: string) => string = productPath,\n): string | undefined {\n switch (a.type) {\n case 'navigate':\n return a.url;\n case 'open_product':\n return a.url || (a.sku ? productUrl(a.sku) : undefined);\n case 'add_to_cart':\n // Can't add to a cart we don't control → land on the product page.\n return a.url || (a.sku ? productUrl(a.sku) : undefined);\n case 'open_cart':\n return '/cart';\n case 'open_login':\n return undefined; // host-only; no neutral fallback\n default:\n return undefined;\n }\n}\n","/**\n * lucide-svg.ts — inline lucide icons for the in-app HTML-string renderers.\n *\n * The web-sdk renderers build raw DOM/HTML and cannot import `lucide-react`\n * (no React, and we keep the CDN bundle lean). This helper emits the SAME\n * lucide glyphs as inline `<svg>` strings so injected in-app surfaces use the\n * platform icon set instead of emojis. See CLAUDE.md \"Icons\" +\n * docs/architecture/EMOJI_TO_LUCIDE_MIGRATION_TRACKER.md.\n *\n * Path data is copied verbatim from lucide-react (ISC). To add an icon, copy\n * its node array from `node_modules/lucide-react/dist/esm/icons/<name>.js`.\n */\n\ntype IconNode = Array<[string, Record<string, string | number>]>;\n\n// lucide default canvas: 24x24 viewBox, stroke-based, round caps/joins.\nconst ICONS: Record<string, IconNode> = {\n x: [\n ['path', { d: 'M18 6 6 18' }],\n ['path', { d: 'm6 6 12 12' }],\n ],\n check: [['path', { d: 'M20 6 9 17l-5-5' }]],\n info: [\n ['circle', { cx: '12', cy: '12', r: '10' }],\n ['path', { d: 'M12 16v-4' }],\n ['path', { d: 'M12 8h.01' }],\n ],\n 'triangle-alert': [\n ['path', { d: 'm21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3' }],\n ['path', { d: 'M12 9v4' }],\n ['path', { d: 'M12 17h.01' }],\n ],\n 'volume-2': [\n [\n 'path',\n {\n d: 'M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z',\n },\n ],\n ['path', { d: 'M16 9a5 5 0 0 1 0 6' }],\n ['path', { d: 'M19.364 18.364a9 9 0 0 0 0-12.728' }],\n ],\n 'volume-x': [\n [\n 'path',\n {\n d: 'M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z',\n },\n ],\n ['line', { x1: '22', x2: '16', y1: '9', y2: '15' }],\n ['line', { x1: '16', x2: '22', y1: '9', y2: '15' }],\n ],\n clock: [\n ['circle', { cx: '12', cy: '12', r: '10' }],\n ['polyline', { points: '12 6 12 12 16 14' }],\n ],\n play: [['polygon', { points: '6 3 20 12 6 21 6 3' }]],\n pause: [\n ['rect', { x: '14', y: '4', width: '4', height: '16', rx: '1' }],\n ['rect', { x: '6', y: '4', width: '4', height: '16', rx: '1' }],\n ],\n square: [['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2' }]],\n 'square-check': [\n ['path', { d: 'M21 10.5V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h12.5' }],\n ['path', { d: 'm9 11 3 3L22 4' }],\n ],\n truck: [\n ['path', { d: 'M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2' }],\n ['path', { d: 'M15 18H9' }],\n [\n 'path',\n {\n d: 'M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.624l-3.48-4.35A1 1 0 0 0 17.52 8H14',\n },\n ],\n ['circle', { cx: '17', cy: '18', r: '2' }],\n ['circle', { cx: '7', cy: '18', r: '2' }],\n ],\n 'circle-check': [\n ['circle', { cx: '12', cy: '12', r: '10' }],\n ['path', { d: 'm9 12 2 2 4-4' }],\n ],\n // ── Commerce / value-bar set (rich card editor picker) ───────────────────\n 'shopping-cart': [\n ['circle', { cx: '8', cy: '21', r: '1' }],\n ['circle', { cx: '19', cy: '21', r: '1' }],\n ['path', { d: 'M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12' }],\n ],\n 'shopping-bag': [\n ['path', { d: 'M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z' }],\n ['path', { d: 'M3 6h18' }],\n ['path', { d: 'M16 10a4 4 0 0 1-8 0' }],\n ],\n gift: [\n ['rect', { x: '3', y: '8', width: '18', height: '4', rx: '1' }],\n ['path', { d: 'M12 8v13' }],\n ['path', { d: 'M19 12v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-7' }],\n ['path', { d: 'M7.5 8a2.5 2.5 0 0 1 0-5A4.8 8 0 0 1 12 8a4.8 8 0 0 1 4.5-5 2.5 2.5 0 0 1 0 5' }],\n ],\n tag: [\n ['path', { d: 'M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z' }],\n ['circle', { cx: '7.5', cy: '7.5', r: '.5', fill: 'currentColor' }],\n ],\n percent: [\n ['line', { x1: '19', x2: '5', y1: '5', y2: '19' }],\n ['circle', { cx: '6.5', cy: '6.5', r: '2.5' }],\n ['circle', { cx: '17.5', cy: '17.5', r: '2.5' }],\n ],\n star: [\n ['path', { d: 'M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z' }],\n ],\n sparkles: [\n ['path', { d: 'M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z' }],\n ['path', { d: 'M20 3v4' }],\n ['path', { d: 'M22 5h-4' }],\n ],\n zap: [\n ['path', { d: 'M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z' }],\n ],\n heart: [\n ['path', { d: 'M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z' }],\n ],\n bell: [\n ['path', { d: 'M10.268 21a2 2 0 0 0 3.464 0' }],\n ['path', { d: 'M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326' }],\n ],\n lock: [\n ['rect', { width: '18', height: '11', x: '3', y: '11', rx: '2', ry: '2' }],\n ['path', { d: 'M7 11V7a5 5 0 0 1 10 0v4' }],\n ],\n 'badge-check': [\n ['path', { d: 'M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z' }],\n ['path', { d: 'm9 12 2 2 4-4' }],\n ],\n package: [\n ['path', { d: 'M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z' }],\n ['path', { d: 'M12 22V12' }],\n ['path', { d: 'm3.3 7 8.7 5 8.7-5' }],\n ['path', { d: 'm7.5 4.27 9 5.15' }],\n ],\n flame: [\n ['path', { d: 'M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z' }],\n ],\n crown: [\n ['path', { d: 'M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z' }],\n ['path', { d: 'M5 21h14' }],\n ],\n ticket: [\n ['path', { d: 'M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z' }],\n ['path', { d: 'M13 5v2' }],\n ['path', { d: 'M13 17v2' }],\n ['path', { d: 'M13 11v2' }],\n ],\n wallet: [\n ['path', { d: 'M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1' }],\n ['path', { d: 'M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4' }],\n ],\n coins: [\n ['circle', { cx: '8', cy: '8', r: '6' }],\n ['path', { d: 'M18.09 10.37A6 6 0 1 1 10.34 18' }],\n ['path', { d: 'M7 6h1v4' }],\n ['path', { d: 'm16.71 13.88.7.71-2.82 2.82' }],\n ],\n trophy: [\n ['path', { d: 'M6 9H4.5a2.5 2.5 0 0 1 0-5H6' }],\n ['path', { d: 'M18 9h1.5a2.5 2.5 0 0 0 0-5H18' }],\n ['path', { d: 'M4 22h16' }],\n ['path', { d: 'M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22' }],\n ['path', { d: 'M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22' }],\n ['path', { d: 'M18 2H6v7a6 6 0 0 0 12 0V2Z' }],\n ],\n timer: [\n ['line', { x1: '10', x2: '14', y1: '2', y2: '2' }],\n ['line', { x1: '12', x2: '15', y1: '14', y2: '11' }],\n ['circle', { cx: '12', cy: '14', r: '8' }],\n ],\n 'map-pin': [\n ['path', { d: 'M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0' }],\n ['circle', { cx: '12', cy: '10', r: '3' }],\n ],\n user: [\n ['path', { d: 'M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2' }],\n ['circle', { cx: '12', cy: '7', r: '4' }],\n ],\n 'thumbs-up': [\n ['path', { d: 'M7 10v12' }],\n ['path', { d: 'M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z' }],\n ],\n 'arrow-right': [\n ['path', { d: 'M5 12h14' }],\n ['path', { d: 'm12 5 7 7-7 7' }],\n ],\n 'chevron-right': [['path', { d: 'm9 18 6-6-6-6' }]],\n plus: [\n ['path', { d: 'M5 12h14' }],\n ['path', { d: 'M12 5v14' }],\n ],\n store: [\n ['path', { d: 'm2 7 4.41-4.41A2 2 0 0 1 7.83 2h8.34a2 2 0 0 1 1.42.59L22 7' }],\n ['path', { d: 'M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8' }],\n ['path', { d: 'M15 22v-4a2 2 0 0 0-2-2h-2a2 2 0 0 0-2 2v4' }],\n ['path', { d: 'M2 7h20' }],\n ['path', { d: 'M22 7v3a2 2 0 0 1-2 2a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 16 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 12 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 8 12a2.7 2.7 0 0 1-1.59-.63.7.7 0 0 0-.82 0A2.7 2.7 0 0 1 4 12a2 2 0 0 1-2-2V7' }],\n ],\n rocket: [\n ['path', { d: 'M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z' }],\n ['path', { d: 'm12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z' }],\n ['path', { d: 'M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0' }],\n ['path', { d: 'M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5' }],\n ],\n};\n\n/** Stable list of pickable glyph names — the compose IconPicker offers exactly\n * this set (the SDK can only render what's in ICONS). Adding a glyph above\n * auto-exposes it here. */\nexport const ICON_NAMES = Object.keys(ICONS);\n\nexport type LucideIconName = keyof typeof ICONS;\n\nexport interface LucideSvgOptions {\n /** Pixel size of the square icon (default 24). */\n size?: number;\n /** Stroke color — any CSS color (default 'currentColor'). */\n color?: string;\n /** Stroke width (default 2, matching the design system). */\n strokeWidth?: number;\n /** Extra class names on the <svg>. */\n className?: string;\n /** Accessible label; when omitted the icon is aria-hidden. */\n ariaLabel?: string;\n}\n\nfunction escapeAttr(value: string): string {\n return value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;');\n}\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\n/**\n * Builds an inline lucide icon as a real `SVGElement` via `createElementNS` —\n * the SECURE path for DOM renderers (no `innerHTML`/string parsing). Prefer this\n * over `lucideSvg` whenever you have a parent node to `appendChild` into.\n * Returns null for unknown names or outside a DOM (fail-soft).\n */\nexport function lucideSvgEl(\n name: LucideIconName | string,\n opts: LucideSvgOptions = {},\n): SVGSVGElement | null {\n const node = ICONS[name as LucideIconName];\n if (!node || typeof document === 'undefined') return null;\n const size = opts.size ?? 24;\n const svg = document.createElementNS(SVG_NS, 'svg');\n svg.setAttribute('width', String(size));\n svg.setAttribute('height', String(size));\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('fill', 'none');\n svg.setAttribute('stroke', opts.color ?? 'currentColor');\n svg.setAttribute('stroke-width', String(opts.strokeWidth ?? 2));\n svg.setAttribute('stroke-linecap', 'round');\n svg.setAttribute('stroke-linejoin', 'round');\n if (opts.className) svg.setAttribute('class', opts.className);\n if (opts.ariaLabel) {\n svg.setAttribute('role', 'img');\n svg.setAttribute('aria-label', opts.ariaLabel);\n } else {\n svg.setAttribute('aria-hidden', 'true');\n }\n for (const [tag, attrs] of node) {\n const child = document.createElementNS(SVG_NS, tag);\n for (const [k, v] of Object.entries(attrs)) child.setAttribute(k, String(v));\n svg.appendChild(child);\n }\n return svg;\n}\n\n/**\n * Returns an inline lucide `<svg>` string for use in HTML-string renderers.\n * Unknown names return an empty string (fail-soft — never break a surface).\n * For DOM renderers prefer `lucideSvgEl` (no innerHTML).\n */\nexport function lucideSvg(name: LucideIconName | string, opts: LucideSvgOptions = {}): string {\n const node = ICONS[name as LucideIconName];\n if (!node) return '';\n const size = opts.size ?? 24;\n const color = opts.color ?? 'currentColor';\n const strokeWidth = opts.strokeWidth ?? 2;\n const a11y = opts.ariaLabel\n ? `role=\"img\" aria-label=\"${escapeAttr(opts.ariaLabel)}\"`\n : 'aria-hidden=\"true\"';\n const cls = opts.className ? ` class=\"${escapeAttr(opts.className)}\"` : '';\n const children = node\n .map(([tag, attrs]) => {\n const attrStr = Object.entries(attrs)\n .map(([k, v]) => `${k}=\"${escapeAttr(String(v))}\"`)\n .join(' ');\n return `<${tag} ${attrStr}/>`;\n })\n .join('');\n return (\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size}\" height=\"${size}\" ` +\n `viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"${escapeAttr(color)}\" stroke-width=\"${strokeWidth}\" ` +\n `stroke-linecap=\"round\" stroke-linejoin=\"round\"${cls} ${a11y}>${children}</svg>`\n );\n}\n","/**\n * Shared in-app font default — Urbanist (a free, Google-hosted geometric sans\n * used in place of Zepto's commercial \"Gilroy\"). Used by every storefront-\n * facing in-app renderer (product cards, recommendation rails, carousels) so\n * the look is consistent on the ACS storefront AND on 3rd-party host pages that\n * embed the SDK and don't already ship the font.\n */\nexport const IN_APP_FONT_STACK =\n \"'Urbanist', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif\";\n\n/**\n * Lazy-load the Urbanist webfont once (idempotent). The storefront self-hosts\n * Urbanist via `--font-urbanist`, but a 3rd-party host won't — so any renderer\n * that applies {@link IN_APP_FONT_STACK} calls this to guarantee the glyphs are\n * available. No-op outside the browser.\n */\nexport function ensureInAppFont(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById('aegis-inapp-font-urbanist')) return;\n const link = document.createElement('link');\n link.id = 'aegis-inapp-font-urbanist';\n link.rel = 'stylesheet';\n link.href =\n 'https://fonts.googleapis.com/css2?family=Urbanist:wght@400;500;600;700;800&display=swap';\n document.head.appendChild(link);\n}\n","/**\n * Interactive widget contract — Zod-FREE mirror of the canonical registry in\n * `@aegis/shared-types/src/interactive-registry.ts` (Layer 1, SDUI unification).\n *\n * The web-SDK is a standalone, ZERO-dependency published package: it must NOT\n * import `zod`. The SDK only needs the contract TYPES + the response-type / band\n * constants to emit conforming payloads (validation runs author-time in the\n * editor and ingest-time in the backend, both against the Zod registry).\n *\n * The region between the sync sentinels below is kept identical to the contract\n * tokens of the Zod registry by `scripts/check-interactive-contract-sync.mjs`\n * (wired into `npm run lint`). Edit the registry → edit here, or the build fails.\n */\n\n// ===INTERACTIVE-CONTRACT-SYNC===\nexport type InteractiveWidgetType = 'progress' | 'rating' | 'poll' | 'nps' | 'countdown';\nexport type WidgetCapability = 'followup' | 'bands' | 'data_bound' | 'celebrate' | 'thanks';\nexport type NpsBand = 'detractor' | 'passive' | 'promoter';\n\n// Celebrate capability (frame-level) — mirror of CelebrateReactionEnum /\n// CelebrateScopeEnum / CelebrateConfigSchema. 'card' = anchored to the card\n// element; 'viewport' = full-viewport hero burst.\nexport type CelebrateReaction = 'celebrate' | 'affirm' | 'acknowledge';\nexport type CelebrateScope = 'card' | 'viewport';\nexport interface CelebrateConfig {\n celebrate_on_show?: boolean;\n celebrate_reaction?: CelebrateReaction;\n celebrate_scope?: CelebrateScope;\n celebrate_lottie?: string;\n}\n\nexport interface RatingPayload { stars: number; feedback_text?: string; properties?: Record<string, string | number | boolean> }\nexport interface PollPayload { option_index: number; option_label: string; properties?: Record<string, string | number | boolean> }\nexport interface NpsPayload { score: number; band?: NpsBand; reason_text?: string; properties?: Record<string, string | number | boolean> }\n\nexport const RESPONSE_TYPE = { rating: 'rating', poll: 'poll', nps: 'nps' } as const;\n// ===END-INTERACTIVE-CONTRACT-SYNC===\n\n/** Derive the NPS band from a score (mirror of registry npsBandFromScore). */\nexport function npsBandFromScore(score: number): NpsBand {\n if (score <= 6) return 'detractor';\n if (score <= 8) return 'passive';\n return 'promoter';\n}\n","/**\n * Progress-bar mechanic — the ONE computation shared by BOTH web-SDK progress\n * renderers: the standalone `progress_bar` widget (`progress-bar.ts`) and the\n * `progress` card element (`card-template.ts::buildProgressElement`). Before this\n * module each renderer re-implemented the pct / completion / remaining /\n * milestone math independently (drift waiting to happen).\n *\n * This is a BYTE-FOR-BYTE MIRROR of `@aegis/shared-types/src/progress.ts` (which\n * the dashboard compose preview imports). The SDK is a standalone published\n * package and cannot depend on the workspace `@aegis/shared-types` without\n * leaking it into the SDK's public `.d.ts`, so the core is duplicated here and\n * both copies are pinned to the same golden vectors (`progress-core.test.ts` +\n * `shared-types/src/progress.test.ts`). Change one, change both, or a test fails.\n *\n * Pure — no DOM, no SDK context — so it's trivially testable and identical\n * across runtime + preview.\n */\n\nexport type ProgressGoalType =\n | 'cart_total'\n | 'items_in_cart'\n | 'loyalty_points'\n | 'referrals';\n\nexport type ProgressMilestone = 'q25' | 'q50' | 'q75' | 'unlocked';\n\nexport interface ProgressComputeInput {\n goalType: ProgressGoalType;\n threshold: number;\n current: number;\n}\n\nexport interface ProgressState {\n pct: number;\n complete: boolean;\n remaining: number;\n milestonesReached: ProgressMilestone[];\n}\n\nexport function computeProgress(input: ProgressComputeInput): ProgressState {\n const threshold = input.threshold > 0 ? input.threshold : 0;\n const current =\n Number.isFinite(input.current) && input.current > 0 ? input.current : 0;\n const pct =\n threshold > 0 ? Math.max(0, Math.min(100, (current / threshold) * 100)) : 0;\n const complete = threshold > 0 && pct >= 100;\n const remaining = threshold > 0 ? Math.max(0, threshold - current) : 0;\n const milestonesReached: ProgressMilestone[] = [];\n if (threshold > 0) {\n if (pct >= 25) milestonesReached.push('q25');\n if (pct >= 50) milestonesReached.push('q50');\n if (pct >= 75) milestonesReached.push('q75');\n if (pct >= 100) milestonesReached.push('unlocked');\n }\n return { pct, complete, remaining, milestonesReached };\n}\n\nexport interface CardProgressLabelInput extends ProgressComputeInput {\n rewardText?: string;\n currencySymbol?: string;\n showPct?: boolean;\n}\n\nexport function cardProgressSubLabel(i: CardProgressLabelInput): {\n text: string;\n complete: boolean;\n} {\n const { complete, remaining, pct } = computeProgress(i);\n const threshold = i.threshold > 0 ? i.threshold : 0;\n if (complete) return { text: i.rewardText || 'Unlocked!', complete: true };\n if (threshold <= 0) return { text: i.rewardText || '', complete: false };\n const sym = i.goalType === 'cart_total' ? i.currencySymbol || '' : '';\n const pctStr = i.showPct ? ` · ${Math.round(pct)}%` : '';\n return {\n text: `Shop for ${sym}${remaining.toFixed(0)} more${pctStr}`,\n complete: false,\n };\n}\n\nexport function readGoalFromCart(\n goalType: ProgressGoalType,\n cart: { total?: number; itemCount?: number } | undefined | null,\n): number | undefined {\n if (!cart) return undefined;\n if (goalType === 'items_in_cart') return Number(cart.itemCount) || 0;\n if (goalType === 'cart_total') return Number(cart.total) || 0;\n return undefined;\n}\n\nexport interface CountdownState {\n totalMs: number;\n expired: boolean;\n days: number;\n hours: number;\n minutes: number;\n seconds: number;\n display: string;\n}\n\nexport function computeCountdown(targetMs: number, nowMs: number): CountdownState {\n const totalMs = Number.isFinite(targetMs) ? Math.max(0, targetMs - nowMs) : 0;\n const expired = !(targetMs > nowMs);\n const totalSec = Math.floor(totalMs / 1000);\n const days = Math.floor(totalSec / 86400);\n const hours = Math.floor((totalSec % 86400) / 3600);\n const minutes = Math.floor((totalSec % 3600) / 60);\n const seconds = totalSec % 60;\n const pad = (n: number): string => (n < 10 ? '0' + n : '' + n);\n const display = days > 0\n ? days + 'd ' + pad(hours) + ':' + pad(minutes) + ':' + pad(seconds)\n : pad(hours) + ':' + pad(minutes) + ':' + pad(seconds);\n return { totalMs, expired, days, hours, minutes, seconds, display };\n}\n\n// ── Flow engine (Class B) — accumulative session ledger (§16.3) ──\n// Per-step TELEMETRY is fire-and-forget (funnel drop-off); the RESPONSE RECORD\n// is buffered here and flushed ONCE at completion so a mid-flow abandon never\n// produces fragmented/orphan rows.\n\nexport interface FlowStepRecord {\n step_id: string;\n response_type?: string;\n payload?: Record<string, unknown>;\n}\n\nexport interface FlowAccumulator {\n recordStep(r: FlowStepRecord): void;\n steps(): FlowStepRecord[];\n consolidated(): { steps: FlowStepRecord[]; step_count: number; completed: boolean };\n markCompleted(): void;\n}\n\nexport function createFlowAccumulator(): FlowAccumulator {\n const buffer: FlowStepRecord[] = [];\n let completed = false;\n return {\n recordStep(r: FlowStepRecord): void {\n const idx = buffer.findIndex((b) => b.step_id === r.step_id);\n if (idx >= 0) buffer[idx] = r;\n else buffer.push(r);\n },\n steps(): FlowStepRecord[] {\n return buffer.slice();\n },\n consolidated(): { steps: FlowStepRecord[]; step_count: number; completed: boolean } {\n return { steps: buffer.slice(), step_count: buffer.length, completed };\n },\n markCompleted(): void {\n completed = true;\n },\n };\n}\n\n// ── Media engine (Class D) — playback state (§13 / P4) ──\nexport type PlaybackPhase = 'idle' | 'playing' | 'paused' | 'completed';\nexport interface PlaybackState {\n phase: PlaybackPhase;\n pct: number;\n positionSec: number;\n durationSec: number;\n reachedComplete: boolean;\n}\n\nexport function computePlaybackState(\n positionSec: number,\n durationSec: number,\n phase: PlaybackPhase,\n): PlaybackState {\n const dur = durationSec > 0 ? durationSec : 0;\n const pos = positionSec > 0 ? Math.min(positionSec, dur || positionSec) : 0;\n const pct = dur > 0 ? Math.max(0, Math.min(100, (pos / dur) * 100)) : 0;\n const reachedComplete = phase === 'completed' || (dur > 0 && pct >= 99);\n return { phase, pct, positionSec: pos, durationSec: dur, reachedComplete };\n}\n","/**\n * Card template interpreter — the SINGLE render path for in-app content cards\n * (`product_recommendation` overlay + inline rail, and `carousel_cards`).\n *\n * A card's LAYOUT is data, not SDK code: the campaign carries a declarative\n * `interactive_config.card_template` (an ordered element list + a frame), and\n * this module is the generic painter that walks it. New card looks ship by\n * authoring a template in the compose gallery — no SDK release. This is the\n * Plotline/Whatfix model (thin runtime + server-authored spec), bounded to a\n * fixed element vocabulary so it never becomes arbitrary HTML.\n *\n * Element vocabulary (data-bound implicitly by `type` — no `{{}}` grammar):\n * image → card.image_url / card.video_url\n * badge → static ribbon (element.text) on the image corner\n * price → card.metadata.price ?? card.body (\"₹61\") + optional compare-at strike\n * title → card.title\n * subtitle → card.body (merchandising selling line; carousel cards)\n * rating → card.metadata.avg_rating (+ review_count) — auto-hidden when 0\n * category → card.metadata.category (chip)\n * cta → card.cta_text / card.cta_url\n *\n * Resolution (single path, legacy-safe):\n * 1. global template = ic.card_template ?? synthesize(ic.card_style) ?? DEFAULT\n * 2. per-card overlay = card.style (CardLook, stamped per-SKU by the hydrator)\n * tweaks frame.accent/radius, the badge, element visibility + cta text\n * 3. paint frame + elements\n */\nimport { IN_APP_FONT_STACK, ensureInAppFont } from '../font';\nimport { lucideSvgEl } from '../lucide-svg';\nimport type { AegisAction } from '../actions';\nimport type { CardLook } from './card-style';\nimport { npsBandFromScore, type NpsPayload } from './interactive-contract';\nimport {\n cardProgressSubLabel,\n computeCountdown,\n computeProgress,\n readGoalFromCart,\n} from './progress-core';\nimport type { RenderContext } from './types';\n\nexport type CardElementType =\n | 'image'\n | 'badge'\n | 'price'\n | 'title'\n | 'subtitle'\n | 'rating'\n | 'category'\n | 'cta'\n // ── rich-card-editor vocabulary (P0) ──────────────────────────────────────\n | 'icon' // a lucide glyph (optionally in a tinted bubble)\n | 'text' // free text with fail-soft {token} interpolation\n | 'spacer' // vertical/horizontal gap\n | 'progress' // data-bound progress (cart_total/loyalty/…), reads getCartState\n | 'countdown' // display-only urgency timer counting down to countdown_target\n // ── interactive tier (C3) — carry a TYPED RESPONSE via ctx.submitResponse ────\n | 'rating_input' // tappable star rating → records 'rating' { stars }\n | 'poll' // single-choice poll → records 'poll' { option_index, option_label }\n | 'nps' // 0–10 scale → records 'nps' { score }\n | 'group' // nested container (own layout) → arbitrary composition\n | 'product_rating_list'; // repeater — one star row per order line-item (data-bound)\n\nexport interface CardElement {\n type: CardElementType;\n /** Per-element telemetry/heatmap key (reserved contract field — Part I.C). */\n id?: string;\n /** image: aspect ratio of the media box. */\n aspect?: '1/1' | '4/3' | '16/9';\n /** image/frame: corner radius (px). */\n radius?: number;\n /** price: 'pill' (filled chip) | 'plain'. cta: 'fill' | 'outline'. */\n variant?: string;\n /** price pill / badge background colour. text/icon: foreground colour. */\n color?: string;\n /** title/subtitle/text: max lines before clamp. */\n lines?: number;\n /** badge / cta: static label. */\n text?: string;\n /** cta: render overlaid on the image's bottom-right instead of in flow. */\n overlay?: boolean;\n /** cta: which canonical AegisAction the button fires (CANONICAL_ACTION_MODEL).\n * Absent ⇒ the surface default (cart rail → add_to_cart, else navigate). Lets\n * every card campaign re-point the CTA instead of hardcoding add-to-cart. */\n cta_action?: 'add_to_cart' | 'open_product' | 'open_cart' | 'open_login' | 'navigate' | 'custom';\n /** cta (cta_action='navigate'): target URL / deep link. Falls back to card.cta_url. */\n cta_url?: string;\n /** cta (cta_action='custom'): host-handled action name passed to onAction. */\n cta_custom_name?: string;\n // ── icon ──\n /** icon: lucide glyph name from the SDK's curated set (lucide-svg.ts). */\n icon_name?: string;\n /** icon/text: relative scale. */\n size?: 'sm' | 'md' | 'lg';\n /** icon: render inside a tinted circle. */\n bubble?: boolean;\n // ── text ──\n /** text: content, supports fail-soft {token} interpolation. */\n content?: string;\n /** text: font weight. */\n weight?: 'regular' | 'medium' | 'semibold' | 'bold';\n /** text: horizontal alignment. */\n align?: 'left' | 'center' | 'right';\n // ── progress ──\n goal_type?: 'cart_total' | 'items_in_cart' | 'loyalty_points' | 'referrals';\n threshold?: number;\n threshold_source?: 'fixed' | 'free_delivery_setting';\n reward_text?: string;\n /** progress: bar fill colour (defaults to accent). */\n fill?: string;\n show_pct?: boolean;\n currency_symbol?: string;\n /** progress: fire the shared celebrate reaction (Lottie + confetti) the first\n * time the goal is reached. Defaults ON — set false to keep it quiet. */\n celebrate_on_complete?: boolean;\n // ── countdown (display-only urgency timer; no response) ──\n /** ISO-8601 target the timer counts down to. Auto-hides if absent/invalid\n * (bounded-vocabulary rule — never fabricate a fake countdown). */\n countdown_target?: string;\n /** Lead-in label before the time (default \"Ends in\"). */\n countdown_label?: string;\n // ── interactive tier (C3) — rating_input / poll ──\n /** rating_input: number of stars (2–10, default 5). Records 'star_rating'. */\n rating_scale?: number;\n /** poll: single-choice options (≥2 to render; records 'quick_poll'). The\n * prompt/question reuses `content`. Auto-hides with fewer than 2 options. */\n poll_options?: string[];\n /** rating_input / poll: confirmation note shown AFTER the customer responds\n * (the \"thank-you\" state). Blank → a sensible default. Operator-controlled\n * post-response acknowledgement. */\n thanks_text?: string;\n // ── nps follow-up (P6.full — ported from the bespoke nps_survey renderer) ──\n /** nps: enable the band-conditional reason-capture step after the score. */\n nps_followup_enabled?: boolean;\n /** nps: per-band reason prompts (fallbacks applied when blank). */\n nps_follow_up_promoter?: string;\n nps_follow_up_passive?: string;\n nps_follow_up_detractor?: string;\n /** nps: quick-pick chips that fill the reason textarea. */\n nps_reason_chips?: string[];\n /** nps: score at/above which the contact is a promoter (default 9). */\n nps_promoter_threshold?: number;\n /** nps: promoter-only review/referral CTA shown on the thank-you takeover. */\n nps_promoter_url?: string;\n nps_promoter_cta?: string;\n // ── rating follow-up (P6.full — ported from the bespoke star_rating renderer) ──\n /** rating_input: enable the low-score reason-capture step. */\n rating_followup_enabled?: boolean;\n /** rating_input: stars at/below which the low-score follow-up fires (default ceil(80%)). */\n rating_followup_threshold?: number;\n rating_followup_prompt?: string;\n /** rating_input: high-score review/referral CTA. */\n rating_high_url?: string;\n rating_high_cta?: string;\n /**\n * rating_input: which lucide glyph the scale draws — 'star' (default) or\n * 'heart'. Authored in the dashboard's star-rating editor and persisted by\n * BOTH schemas (zod + pydantic), but unread here until 2026-08-13, so every\n * widget drew stars whatever the operator picked.\n *\n * Deliberately NOT an emoji option, despite the editor historically offering\n * one: this renderer is lucide-only by design rule, and an emoji glyph here\n * would be UI chrome. An 'emoji' value from an older config falls back to\n * 'star' rather than rendering nothing.\n */\n rating_icon?: 'star' | 'heart' | string;\n // ── product_rating_list (repeater, data-bound to the order's line items) ──\n /** Where the item array comes from. 'order' = the slot's data-rateable-items\n * (trusted Aegis surfaces) OR token→server-resolve (untrusted origins). The\n * ONLY value at launch — convention binding (PRODUCT_RATING_LIST_ELEMENT_PLAN\n * §3/§7). Items are NEVER authored on the element. */\n rating_source?: 'order';\n /** Show a product thumbnail per row (default true). Degrades to a tinted\n * monogram when the item has no image_url. */\n show_thumbnail?: boolean;\n /** Row density: 'card' (thumbnail + name + stars, default) | 'compact'. */\n rating_layout?: 'card' | 'compact';\n /** G6 — show the \"Add a written review\" disclosure (a textarea that submits as\n * review_text → external_reviews). Off by default (pure numeric capture). */\n allow_review_text?: boolean;\n /** G6 — show the photo/video picker (uploads to /v1/in-app/review-media →\n * media_urls[] → external_reviews.media + the loyalty photo/video bonus).\n * Off by default. Requires a host context that exposes apiHost + writeKey. */\n allow_review_media?: boolean;\n /** G6 — composer earn hint shown under the disclosure toggle (e.g.\n * \"Earn 100 pts · +50 photo · +75 video\"). Authored copy only — never a live\n * points calc; blank → no hint. */\n review_earn_hint?: string;\n // ── detractor arm (DETRACTOR_CARD_AND_RECOVERY_SPEC B1) — the negative-branch\n // post-tap state of product_rating_list. The page owns the moment: chips +\n // free-text captured inline, submitted as response_type='detractor_detail'.\n /** Headline of the detractor form (default \"Sorry we missed the mark.\"). */\n detractor_headline?: string;\n /** Prompt under the headline (default \"What went wrong?\"). */\n detractor_prompt?: string;\n /** Quick-pick reason chips (default Late / Quality / Wrong item). */\n detractor_chips?: string[];\n /** Submit button label (default \"Send to the team\"). */\n detractor_send_label?: string;\n /** Post-submit confirmation line (default \"Thank you — the team will\n * personally reach out.\"). */\n detractor_thanks?: string;\n // ── spacer ──\n height?: number;\n // ── group ──\n /** group: nested layout + children. */\n layout?: 'stack' | 'row';\n gap?: number;\n elements?: CardElement[];\n}\n\nexport interface CardFrame {\n bg?: string;\n border?: string;\n radius?: number | string;\n /** Box-shadow control: 'none' | false → flat (match flat host sections);\n * a CSS shadow string → custom; absent → legacy floating default. */\n shadow?: string | false;\n /** null/absent ⇒ fall back to --aegis-brand-accent (tenant brand). */\n accent?: string | null;\n // ── layout & sizing (P0/P1) ──\n /** 'stack' (default, column) | 'row' | 'grid'. */\n layout?: 'stack' | 'row' | 'grid';\n /** grid: column count. */\n columns?: number;\n /** flex/grid gap (px). */\n gap?: number;\n /** explicit width (px or CSS). Default: container-driven (responsive). */\n width?: number | string;\n min_width?: number;\n max_width?: number;\n /** explicit height (px). Default: content-driven (fits its elements). */\n height?: number;\n min_height?: number;\n /** Card font family. Absent ⇒ the default Urbanist stack (a free, license-\n * clean geometric sans in place of Zepto's commercial \"Gilroy\"); the SDK\n * lazy-loads Urbanist so it renders on ANY host page. The sentinel\n * `'inherit'` opts OUT — the card inherits the host page font instead. Any\n * other value is a CSS font stack (sanitized before use). */\n fontFamily?: string;\n // ── celebrate-on-show (Celebration-as-Campaign cutover) ──────────────────\n /** Fire the shared `playReaction` once when this card mounts (a hero/delight\n * moment), instead of only on a `progress` element completing. Mirrors\n * CelebrateConfigSchema. */\n celebrate_on_show?: boolean;\n /** Which shared reaction intent to play. Default 'celebrate'. */\n celebrate_reaction?: 'celebrate' | 'affirm' | 'acknowledge';\n /** 'card' (default) = anchored to the card; 'viewport' = full-page hero burst\n * (use with an `overlay` delivery surface). */\n celebrate_scope?: 'card' | 'viewport';\n /** Optional Lottie file override for the reaction. */\n celebrate_lottie?: string;\n}\n\nconst WEIGHT: Record<string, string> = {\n regular: '400',\n medium: '500',\n semibold: '600',\n bold: '700',\n};\n\nconst ICON_PX: Record<string, number> = { sm: 22, md: 30, lg: 40 };\nconst TEXT_PX: Record<string, number> = { sm: 11.5, md: 13, lg: 15 };\n\n/** Strip anything outside a safe CSS font-family grammar (letters, digits,\n * spaces, commas, quotes, hyphens) so an operator-authored value can never\n * break out of the inline `cssText`. Capped to a sane length. */\nfunction sanitizeFontFamily(v: string): string {\n return v.replace(/[^a-zA-Z0-9 ,\"'-]/g, '').slice(0, 120);\n}\n\n/** Resolve `frame.fontFamily` → the CSS value to apply. Absent ⇒ the shared\n * Urbanist default; the `'inherit'` sentinel ⇒ '' (inherit the host page);\n * anything else ⇒ the sanitized stack. */\nfunction resolveCardFont(ff: string | undefined): string {\n if (ff === 'inherit') return '';\n if (ff && ff.trim()) return sanitizeFontFamily(ff);\n return IN_APP_FONT_STACK;\n}\n\n/** Load the Urbanist webfont when the resolved card font is Urbanist (the\n * default / explicit pick) — no-op for a host/system stack. */\nfunction ensureCardFontLoaded(resolvedFont: string): void {\n if (/urbanist/i.test(resolvedFont)) ensureInAppFont();\n}\n\n/**\n * Fail-soft token interpolation for `text` elements. Reads `ctx.properties()`\n * (flat map), falling back to live cart for `cart.*`. Unknown/missing → ''.\n * Never throws, never evaluates — bounded `{a.b_c}` grammar only.\n */\nfunction interpolateTokens(s: string, ctx: RenderContext): string {\n if (!s || s.indexOf('{') === -1) return s;\n const props = (ctx.properties && ctx.properties()) || {};\n const cart = ctx.getCartState && ctx.getCartState();\n return s.replace(/\\{([a-z0-9_.]+)\\}/gi, (_m, key: string) => {\n let v: unknown = props[key];\n if (v == null) {\n if (key === 'cart.total') v = cart?.total;\n else if (key === 'cart.count') v = cart?.itemCount;\n }\n return v == null || v === '' ? '' : String(v);\n });\n}\n\nexport interface CardTemplate {\n id?: string;\n name?: string;\n /** (Phase B) library template this spec was cloned from — usage count only. */\n source_id?: string;\n frame?: CardFrame;\n elements?: CardElement[];\n}\n\n/** Card payload shape shared by both renderers (super-set). */\nexport interface TemplateCard {\n image_url?: string;\n video_url?: string;\n title?: string;\n body?: string;\n cta_text?: string;\n cta_url?: string;\n metadata?: Record<string, unknown>;\n /** Per-card CardLook override (global card_style merged with card_overrides[sku]). */\n style?: CardLook;\n}\n\nconst ASPECT: Record<string, string> = { '1/1': '1 / 1', '4/3': '4 / 3', '16/9': '16 / 9' };\n\n/** The built-in \"Clean\" layout — the runtime default when a campaign carries no\n * `card_template` and no legacy `card_style`. Mirrors the web gallery's Clean. */\nexport const DEFAULT_CARD_TEMPLATE: CardTemplate = {\n id: 'clean',\n frame: { bg: '#ffffff', border: '#f1f1f3', radius: 14 },\n elements: [\n { type: 'image', aspect: '1/1', radius: 10 },\n { type: 'price', variant: 'pill', color: '#059669' },\n { type: 'title', lines: 2 },\n { type: 'rating' },\n // Zepto/Blinkit-style ADD: a single OUTLINED pill (white fill, brand\n // border + brand text) overlaid on the bottom-right of the product image —\n // NOT a solid filled bar. `overlay:true` anchors it onto the image; the\n // outline variant paints the white/border look. The rail's tile-body click\n // still opens the product, so this one CTA owns add-to-cart.\n { type: 'cta', variant: 'outline', overlay: true },\n ],\n};\n\nfunction asLook(v: unknown): CardLook {\n return v && typeof v === 'object' ? (v as CardLook) : {};\n}\n\n/**\n * Legacy adapter — synthesize a template from a `card_style` (CardLook) so\n * pre-template campaigns render through the SAME interpreter (no parallel path).\n */\nfunction templateFromCardStyle(look: CardLook): CardTemplate {\n const elements: CardElement[] = [{ type: 'image', aspect: '1/1', radius: 10 }];\n if (look.badge) elements.push({ type: 'badge', text: look.badge, color: look.badge_color });\n if (!look.hide_price) elements.push({ type: 'price', variant: 'pill', color: '#059669' });\n elements.push({ type: 'title', lines: 2 });\n if (!look.hide_rating) elements.push({ type: 'rating' });\n if (!look.hide_cta) elements.push({ type: 'cta', variant: 'fill', text: look.cta_text });\n return {\n id: 'legacy',\n frame: {\n bg: look.background,\n border: look.border_color,\n radius: look.radius,\n accent: look.accent ?? null,\n },\n elements,\n };\n}\n\n/** Resolve the campaign-global template (template → legacy card_style → default). */\nexport function resolveGlobalTemplate(ic: Record<string, unknown>): CardTemplate {\n const t = ic.card_template;\n if (t && typeof t === 'object' && Array.isArray((t as CardTemplate).elements)) {\n return t as CardTemplate;\n }\n const legacy = ic.card_style;\n if (legacy && typeof legacy === 'object') {\n return templateFromCardStyle(asLook(legacy));\n }\n return DEFAULT_CARD_TEMPLATE;\n}\n\n/** Overlay a per-card CardLook (per-SKU override) onto the global template. */\nexport function applyPerCardOverride(tpl: CardTemplate, look: CardLook): CardTemplate {\n if (!look || Object.keys(look).length === 0) return tpl;\n const frame: CardFrame = { ...(tpl.frame || {}) };\n if (look.accent) frame.accent = look.accent;\n if (look.radius !== undefined) frame.radius = look.radius;\n\n let elements = (tpl.elements || []).slice();\n const drop = (type: CardElementType) => (elements = elements.filter((e) => e.type !== type));\n if (look.hide_price) drop('price');\n if (look.hide_cta) drop('cta');\n if (look.hide_rating) drop('rating');\n if (look.cta_text)\n elements = elements.map((e) => (e.type === 'cta' ? { ...e, text: look.cta_text } : e));\n // Per-card CTA ACTION override → stamp onto the cta element so buildCtaAction\n // resolves the card-specific action (parity with the template). Absent fields\n // inherit the template's element values.\n if (look.cta_action || look.cta_url || look.cta_custom_name) {\n elements = elements.map((e) =>\n e.type === 'cta'\n ? {\n ...e,\n ...(look.cta_action ? { cta_action: look.cta_action } : {}),\n ...(look.cta_url ? { cta_url: look.cta_url } : {}),\n ...(look.cta_custom_name ? { cta_custom_name: look.cta_custom_name } : {}),\n }\n : e\n );\n }\n // A per-SKU badge adds/updates the image-corner ribbon.\n if (look.badge) {\n const badgeEl: CardElement = { type: 'badge', text: look.badge, color: look.badge_color };\n elements = elements.some((e) => e.type === 'badge')\n ? elements.map((e) => (e.type === 'badge' ? badgeEl : e))\n : [elements[0], badgeEl, ...elements.slice(1)].filter(\n Boolean as unknown as (v: CardElement) => boolean\n );\n }\n return { ...tpl, frame, elements };\n}\n\n// ── element painters ────────────────────────────────────────────────────────\n\nfunction priceText(card: TemplateCard): string | undefined {\n const meta = card.metadata || {};\n const p = (meta as Record<string, unknown>).price;\n if (p !== undefined && p !== null) return String(p);\n if (card.body && /\\d/.test(card.body)) return card.body;\n return undefined;\n}\n\nfunction paintPrice(\n card: TemplateCard,\n el: CardElement,\n accent: string,\n ctx: RenderContext\n): HTMLElement | null {\n const price = priceText(card);\n if (price === undefined) return null;\n const meta = card.metadata || {};\n const compareAt =\n (meta as Record<string, unknown>).compare_at_price ??\n (meta as Record<string, unknown>).compare_at ??\n (meta as Record<string, unknown>).original_price;\n const row = document.createElement('div');\n row.style.cssText = 'display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;';\n const p = document.createElement('span');\n p.textContent = price;\n if (el.variant === 'plain') {\n p.style.cssText = `color:${accent};font-weight:700;font-size:13px;`;\n } else {\n const pillBg = el.color ? ctx.sanitizeColor(el.color) : '#059669';\n p.style.cssText = `background:${pillBg};color:#fff;font-weight:800;font-size:12px;padding:2px 7px;border-radius:6px;box-shadow:0 2px 6px rgba(0,0,0,0.30);`;\n }\n row.appendChild(p);\n const pNum = parseFloat(price.replace(/[^0-9.]/g, ''));\n const cNum =\n compareAt !== undefined ? parseFloat(String(compareAt).replace(/[^0-9.]/g, '')) : NaN;\n if (isFinite(cNum) && isFinite(pNum) && cNum > pNum) {\n const orig = document.createElement('span');\n orig.textContent = String(compareAt);\n orig.style.cssText = 'text-decoration:line-through;opacity:0.5;font-size:11px;';\n row.appendChild(orig);\n const off = document.createElement('span');\n off.textContent = `-${Math.round((1 - pNum / cNum) * 100)}%`;\n off.style.cssText =\n 'background:#ef444422;color:#ef4444;font-weight:700;font-size:10px;padding:1px 5px;border-radius:999px;';\n row.appendChild(off);\n }\n return row;\n}\n\nfunction paintRating(card: TemplateCard): HTMLElement | null {\n const meta = card.metadata || {};\n const avg = Number((meta as Record<string, unknown>).avg_rating ?? 0);\n if (!(avg > 0)) return null; // auto-hide — never fabricated\n const count = Number((meta as Record<string, unknown>).review_count ?? 0);\n const rr = document.createElement('div');\n rr.style.cssText = 'display:flex;align-items:center;gap:3px;font-size:11px;line-height:1;';\n // Filled lucide star (no emoji UI chrome — CLAUDE.md \"Icons\").\n const star = lucideSvgEl('star', { size: 12, color: '#059669' });\n if (star) star.setAttribute('fill', '#059669');\n const val = document.createElement('span');\n val.textContent = avg.toFixed(1);\n val.style.cssText = 'color:#059669;font-weight:700;';\n if (star) rr.appendChild(star);\n rr.appendChild(val);\n if (count > 0) {\n const c = document.createElement('span');\n c.textContent = `(${count})`;\n c.style.cssText = 'color:#94a3b8;';\n rr.appendChild(c);\n }\n return rr;\n}\n\n/** Pick readable text colour for a surface bg (luminance). White bg → dark text\n * (back-compat with the old hardcode); dark bg → white. */\nfunction contrastFg(bg: string): string {\n const raw = bg.replace('#', '');\n if (raw.length < 3) return '#1f2937';\n const h =\n raw.length === 3\n ? raw\n .split('')\n .map((c) => c + c)\n .join('')\n : raw.slice(0, 6);\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n if ([r, g, b].some((n) => Number.isNaN(n))) return '#1f2937';\n const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n return lum < 0.5 ? '#ffffff' : '#1f2937';\n}\n\n/**\n * `progress` card element — a data-bound hairline progress + human subline\n * (\"Shop for ₹X more\" / reward when complete). Reads live cart via\n * `ctx.getCartState()` and self-updates (1s poll). The card frame supplies the\n * surface; this is just the mechanic. Threshold comes from `el.threshold` or,\n * for `threshold_source:'free_delivery_setting'`, from `{cart.free_delivery_above}`\n * in `ctx.properties` (server-hydrated). Fail-soft: threshold 0 → reward text only.\n */\n/**\n * Display-only urgency countdown (C3 — the response-free slice). Ticks down to\n * `el.countdown_target` (ISO). Returns null to AUTO-HIDE when the target is\n * absent or invalid — the bounded-vocabulary rule (never paint a fake timer).\n * Self-clears its interval once detached from the DOM (overlay closed), so no\n * leak. No response/typed-data — that's the interactive tier (deferred to the\n * 4-SDK parity track). See IN_APP_FORMAT_LOCK_AND_COMPOSABLE_CARDS_TRACKER.md.\n */\nfunction buildCountdownElement(el: CardElement, fg: string, muted: string): HTMLElement | null {\n const targetStr = typeof el.countdown_target === 'string' ? el.countdown_target : '';\n const target = targetStr ? Date.parse(targetStr) : NaN;\n if (!targetStr || isNaN(target)) return null;\n\n const wrap = document.createElement('div');\n wrap.style.cssText =\n 'display:flex;align-items:baseline;gap:8px;font-variant-numeric:tabular-nums;margin:2px 0;';\n const label = document.createElement('span');\n label.textContent = (typeof el.countdown_label === 'string' && el.countdown_label) || 'Ends in';\n label.style.cssText = `font-size:12px;color:${muted};`;\n const time = document.createElement('span');\n time.style.cssText = `font-size:15px;font-weight:700;color:${fg};letter-spacing:0.2px;`;\n wrap.appendChild(label);\n wrap.appendChild(time);\n\n let started = false;\n let timer = 0;\n const tick = (): void => {\n // Self-clean once the card is gone (overlay closed) — avoids a leaked timer.\n if (started && !wrap.isConnected) {\n if (timer) clearInterval(timer);\n return;\n }\n // Shared data-bound core — same remaining-time math the dashboard preview\n // ticks with (computeCountdown), so the composer countdown matches runtime.\n const cd = computeCountdown(target, Date.now());\n time.textContent = cd.display;\n if (cd.expired && timer) clearInterval(timer);\n started = true;\n };\n tick();\n timer = setInterval(tick, 1000) as unknown as number;\n return wrap;\n}\n\n/**\n * Interactive tier (C3) — tappable star rating embedded in a card. Records a\n * typed 'star_rating' response via ctx.submitResponse (the same pipeline the\n * standalone star_rating widget + games use). Lucide stars (no emoji — design\n * rule). Single submission; the prompt reuses `el.content`.\n * See IN_APP_FORMAT_LOCK_AND_COMPOSABLE_CARDS_TRACKER.md (Part B / C3 interactive).\n */\n/** Rating thank-you state — high scorers can get a review/referral CTA. */\nfunction appendRatingThanks(\n wrap: HTMLElement,\n el: CardElement,\n ctx: RenderContext,\n fg: string,\n accent: string,\n isHigh: boolean\n): void {\n appendThanks(wrap, el, fg, 'Thanks for rating us!');\n const url =\n isHigh && typeof el.rating_high_url === 'string' ? ctx.sanitizeUrl(el.rating_high_url) : '';\n if (url) {\n const cta = document.createElement('button');\n cta.type = 'button';\n cta.textContent = el.rating_high_cta || 'Leave a review';\n cta.style.cssText = `margin:4px auto 0;display:block;padding:9px 18px;border:none;border-radius:999px;background:${accent};color:#fff;font-size:13px;font-weight:800;cursor:pointer;`;\n cta.addEventListener('click', () => {\n ctx.trackEvent(ctx.campaign.id, 'clicked', {\n meta: { element: 'rating_input', cta: 'high_review' },\n });\n // The public review ask is once per brand — record the tap-through\n // server-side (the server also withholds this button once recorded).\n void ctx.submitResponse?.('review_redirect', { cta: 'high_review' });\n if (ctx.navigate) ctx.navigate(url, 'cta');\n else window.location.href = url;\n });\n wrap.appendChild(cta);\n }\n}\n\n/** Rating low-score reason-capture step (ported from _renderRatingLowFollowup). */\nfunction renderRatingFollowupStep(\n wrap: HTMLElement,\n el: CardElement,\n ctx: RenderContext,\n stars: number,\n fg: string,\n accent: string\n): void {\n while (wrap.firstChild) wrap.removeChild(wrap.firstChild);\n const heading = document.createElement('div');\n heading.textContent = el.rating_followup_prompt || 'Sorry to hear that — what went wrong?';\n heading.style.cssText = `font-size:13px;font-weight:700;color:${fg};text-align:center;margin-bottom:8px;`;\n wrap.appendChild(heading);\n const ta = document.createElement('textarea');\n ta.rows = 3;\n ta.placeholder = 'Tell us more…';\n ta.style.cssText = `width:100%;box-sizing:border-box;padding:10px;border-radius:10px;border:1px solid color-mix(in srgb, ${accent} 30%, transparent);background:transparent;color:${fg};font-size:13px;resize:vertical;margin-bottom:10px;font-family:inherit;`;\n wrap.appendChild(ta);\n let done = false;\n const finish = (withComment: boolean): void => {\n if (done) return;\n done = true;\n const comment = withComment ? ta.value.trim() : '';\n void ctx.submitResponse?.('rating', comment ? { stars, comment } : { stars });\n ctx.playReaction?.(wrap, 'acknowledge');\n appendThanks(wrap, el, fg, 'Thanks — we’ll do better.');\n };\n const btnRow = document.createElement('div');\n btnRow.style.cssText = 'display:flex;gap:8px;justify-content:center;';\n const send = document.createElement('button');\n send.type = 'button';\n send.textContent = 'Send';\n send.style.cssText = `padding:9px 20px;border-radius:999px;border:none;background:${accent};color:#fff;font-weight:800;font-size:13px;cursor:pointer;`;\n send.addEventListener('click', () => finish(true));\n const skip = document.createElement('button');\n skip.type = 'button';\n skip.textContent = 'Skip';\n skip.style.cssText = `padding:9px 16px;border-radius:999px;border:1px solid color-mix(in srgb, ${accent} 30%, transparent);background:transparent;color:${fg};font-weight:600;font-size:13px;cursor:pointer;`;\n skip.addEventListener('click', () => finish(false));\n btnRow.appendChild(send);\n btnRow.appendChild(skip);\n wrap.appendChild(btnRow);\n ta.focus();\n}\n\nfunction buildRatingInputElement(el: CardElement, ctx: RenderContext, fg: string): HTMLElement {\n const accent = ctx.sanitizeColor((ctx.campaign.background_color as string) || '#4169e1');\n const wrap = document.createElement('div');\n wrap.style.cssText = 'display:flex;flex-direction:column;gap:6px;margin:2px 0;';\n if (el.content) {\n const q = document.createElement('div');\n q.textContent = el.content;\n q.style.cssText = `font-size:13px;font-weight:600;color:${fg};`;\n wrap.appendChild(q);\n }\n const scale =\n typeof el.rating_scale === 'number' && el.rating_scale >= 2 && el.rating_scale <= 10\n ? Math.floor(el.rating_scale)\n : 5;\n // Only 'heart' deviates; anything else (including a legacy 'emoji') draws\n // stars, so an unknown value degrades to the default instead of blank.\n const iconName = el.rating_icon === 'heart' ? 'heart' : 'star';\n const iconWord = iconName === 'heart' ? 'heart' : 'star';\n const row = document.createElement('div');\n row.style.cssText = 'display:flex;gap:6px;';\n const stars: SVGSVGElement[] = [];\n let submitted = false;\n const paint = (upto: number): void => {\n stars.forEach((s, idx) => {\n const on = idx < upto;\n s.setAttribute('stroke', on ? '#f59e0b' : '#d1d5db');\n s.setAttribute('fill', on ? '#f59e0b' : 'none');\n });\n };\n for (let i = 1; i <= scale; i++) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.setAttribute('aria-label', `${i} ${iconWord}${i > 1 ? 's' : ''}`);\n btn.style.cssText = 'cursor:pointer;border:none;background:none;padding:0;line-height:0;';\n const svg = lucideSvgEl(iconName, { size: 26, color: '#d1d5db' });\n if (svg) {\n btn.appendChild(svg);\n stars.push(svg);\n }\n btn.addEventListener('mouseenter', () => {\n if (!submitted) paint(i);\n });\n btn.addEventListener('mouseleave', () => {\n if (!submitted) paint(0);\n });\n btn.addEventListener('click', () => {\n if (submitted) return;\n submitted = true;\n paint(i);\n ctx.trackEvent(ctx.campaign.id, 'clicked', { meta: { element: 'rating_input', stars: i } });\n const lowThreshold =\n typeof el.rating_followup_threshold === 'number'\n ? el.rating_followup_threshold\n : Math.ceil(scale * 0.8);\n if (el.rating_followup_enabled === true && i <= lowThreshold) {\n // Low-score reason-capture step — submits AFTER send/skip (ported from\n // the bespoke star_rating low-score follow-up).\n renderRatingFollowupStep(wrap, el, ctx, i, fg, accent);\n } else {\n // Canonical capture — IDENTICAL to the standalone star_rating widget\n // (response_type 'rating', payload { stars }).\n void ctx.submitResponse?.('rating', { stars: i });\n ctx.playReaction?.(wrap, i >= Math.ceil(scale * 0.6) ? 'affirm' : 'acknowledge');\n appendRatingThanks(wrap, el, ctx, fg, accent, i >= Math.ceil(scale * 0.6));\n }\n });\n row.appendChild(btn);\n }\n wrap.appendChild(row);\n return wrap;\n}\n\n// ── product_rating_list (repeater, data-bound to the order's line items) ──────\n// One star row per SKU of the CURRENT order. Items are NEVER authored — they\n// come from runtime host context (the slot's data-rateable-items on trusted\n// Aegis surfaces, or token→server-resolve on untrusted origins). Submits the\n// per-SKU scores through the same /v1/in-app/responses fan-out the MVP used.\n// See PRODUCT_RATING_LIST_ELEMENT_PLAN.md.\n\ninterface RateableItem {\n product_id: string;\n name?: string | null;\n image_url?: string | null;\n}\n\n/** A tappable star row (its own score). Mirrors buildRatingInputElement's paint\n * pattern; calls `onPick(n)` so the parent can collect per-item scores. */\nfunction starRow(scale: number, onPick: (n: number) => void): HTMLElement {\n const row = document.createElement('div');\n row.style.cssText = 'display:flex;gap:2px;flex-shrink:0;';\n const stars: SVGSVGElement[] = [];\n let picked = 0;\n const paint = (upto: number): void => {\n stars.forEach((s, idx) => {\n const on = idx < upto;\n s.setAttribute('stroke', on ? '#f59e0b' : '#d1d5db');\n s.setAttribute('fill', on ? '#f59e0b' : 'none');\n });\n };\n for (let i = 1; i <= scale; i++) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.setAttribute('aria-label', `${i} star${i > 1 ? 's' : ''}`);\n btn.style.cssText = 'cursor:pointer;border:none;background:none;padding:1px;line-height:0;';\n const svg = lucideSvgEl('star', { size: 22, color: '#d1d5db' });\n if (svg) {\n btn.appendChild(svg);\n stars.push(svg);\n }\n btn.addEventListener('mouseenter', () => paint(i));\n btn.addEventListener('mouseleave', () => paint(picked));\n btn.addEventListener('click', () => {\n picked = i;\n paint(i);\n onPick(i);\n });\n row.appendChild(btn);\n }\n return row;\n}\n\n/** Per-item thumbnail — catalog image when present, else a tinted monogram. */\nfunction ratingThumb(it: RateableItem, accent: string): HTMLElement {\n const box = document.createElement('div');\n box.style.cssText =\n `width:40px;height:40px;flex-shrink:0;border-radius:8px;overflow:hidden;` +\n `background:${accent}1f;display:flex;align-items:center;justify-content:center;`;\n if (it.image_url) {\n const img = document.createElement('img');\n img.src = it.image_url;\n img.alt = it.name || '';\n img.loading = 'lazy';\n img.style.cssText = 'width:100%;height:100%;object-fit:cover;';\n box.appendChild(img);\n } else {\n const mono = document.createElement('span');\n mono.textContent = (it.name || '?').slice(0, 1).toUpperCase();\n mono.style.cssText = `font-size:16px;font-weight:700;color:${accent};`;\n box.appendChild(mono);\n }\n return box;\n}\n\nfunction buildProductRatingListElement(\n el: CardElement,\n ctx: RenderContext,\n fg: string\n): HTMLElement {\n const wrap = document.createElement('div');\n wrap.style.cssText = 'display:flex;flex-direction:column;gap:10px;margin:2px 0;';\n const slot = ctx.slotEl;\n if (!slot) return wrap; // no host context (overlay / dashboard preview) → collapse\n\n const accent = ctx.sanitizeColor(\n slot.getAttribute('data-accent') || (ctx.campaign.background_color as string) || '#4169e1'\n );\n const orderId = slot.getAttribute('data-order-id');\n const billCode = slot.getAttribute('data-bill-code');\n const token = slot.getAttribute('data-token');\n // The page's reviews page (which records the once-per-brand tap itself) wins;\n // the campaign's own `rating_high_url` goes straight to the platform, so a\n // tap on THAT is recorded here.\n const reviewsViaPage = Boolean(slot.getAttribute('data-reviews-url'));\n const reviewsUrl =\n slot.getAttribute('data-reviews-url') || (el.rating_high_url as string | undefined) || null;\n const scale =\n typeof el.rating_scale === 'number' && el.rating_scale >= 2 && el.rating_scale <= 10\n ? Math.floor(el.rating_scale)\n : 5;\n const showThumb = el.show_thumbnail !== false && el.rating_layout !== 'compact';\n const scores: Record<string, number> = {};\n // product_id → display name, captured at render so the submit can carry\n // `product_name` per item — the server's worst-item pick feeds the recovery\n // receipt's \"{{context.item_name}}\" and customer_survey_responses.\n const itemNames: Record<string, string> = {};\n\n // ── G6 review composer (optional text + photo/video) ──\n // Progressive disclosure from numeric capture to a full Judge.me-style review.\n // Authored on the element (allow_review_text / allow_review_media); collected\n // here and submitted ALONGSIDE the scores so the rating fan-out rolls them into\n // external_reviews.media + the loyalty photo/video bonus. Media needs a host\n // context (apiHost + writeKey + org) for the upload endpoint; without it the\n // picker is hidden and capture degrades to text/numeric.\n const allowText = el.allow_review_text === true;\n const canUpload = !!(ctx.apiHost && ctx.writeKey && ctx.organizationId);\n const allowMedia = el.allow_review_media === true && canUpload;\n let reviewText = '';\n const mediaUrls: Array<{ url: string; type: string }> = [];\n\n const fileToB64 = (file: File): Promise<string> =>\n new Promise((resolve, reject) => {\n const r = new FileReader();\n r.onload = () => {\n const s = String(r.result || '');\n resolve(s.includes(',') ? s.split(',')[1] : s);\n };\n r.onerror = () => reject(r.error);\n r.readAsDataURL(file);\n });\n\n const uploadOne = async (file: File): Promise<{ url: string; type: string } | null> => {\n if (!ctx.apiHost || !ctx.writeKey || !ctx.organizationId) return null;\n try {\n const data = await fileToB64(file);\n const res = await fetch(`${ctx.apiHost}/v1/in-app/review-media`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': ctx.writeKey,\n 'X-Organization-ID': ctx.organizationId,\n },\n body: JSON.stringify({ data, content_type: file.type }),\n });\n if (!res.ok) return null;\n const j = (await res.json()) as { url?: string; type?: string };\n return j.url ? { url: j.url, type: j.type === 'video' ? 'video' : 'image' } : null;\n } catch {\n return null;\n }\n };\n\n const buildComposer = (): HTMLElement => {\n const box = document.createElement('div');\n box.style.cssText = 'display:flex;flex-direction:column;gap:8px;margin-top:2px;';\n const toggle = document.createElement('button');\n toggle.type = 'button';\n toggle.textContent = allowMedia ? '+ Add a written review & photos' : '+ Add a written review';\n toggle.style.cssText =\n `align-self:flex-start;border:none;background:none;cursor:pointer;color:${accent};` +\n 'font-size:13px;font-weight:700;padding:2px 0;';\n const panel = document.createElement('div');\n panel.style.cssText = 'display:none;flex-direction:column;gap:8px;';\n toggle.addEventListener('click', () => {\n panel.style.display = 'flex';\n toggle.style.display = 'none';\n });\n // Earn hint — authored copy wins; when blank, auto-derive from the live\n // review earning rule (§9) so it never drifts from the configured points.\n const hint = document.createElement('div');\n hint.style.cssText = `font-size:11px;color:${fg};opacity:.65;`;\n if (typeof el.review_earn_hint === 'string' && el.review_earn_hint.trim()) {\n hint.textContent = el.review_earn_hint.trim();\n panel.appendChild(hint);\n } else if (ctx.apiHost && ctx.writeKey && ctx.organizationId) {\n panel.appendChild(hint); // filled async; stays empty (no layout shift) on miss\n // Per-brand rule: responses.py `get_review_earn_hint` reads the brand as\n // the `workspace_id` query param. Absent = org-level.\n const wsQs = ctx.workspaceId ? `?workspace_id=${encodeURIComponent(ctx.workspaceId)}` : '';\n const url = `${ctx.apiHost}/v1/in-app/review-earn-hint${wsQs}`;\n void fetch(url, {\n headers: { 'X-Aegis-Write-Key': ctx.writeKey, 'X-Organization-ID': ctx.organizationId },\n })\n .then((r) => (r.ok ? (r.json() as Promise<Record<string, unknown>>) : null))\n .then((d) => {\n if (!d || d.enabled !== true) return;\n const name = typeof d.points_name === 'string' ? d.points_name : 'points';\n const parts = [`Earn ${Number(d.base)} ${name}`];\n if (Number(d.photo_bonus) > 0) parts.push(`+${Number(d.photo_bonus)} photo`);\n if (Number(d.video_bonus) > 0) parts.push(`+${Number(d.video_bonus)} video`);\n hint.textContent = parts.join(' · ');\n })\n .catch(() => {\n /* hint is optional */\n });\n }\n if (allowText) {\n const ta = document.createElement('textarea');\n ta.placeholder = 'Tell us more (optional)';\n ta.rows = 3;\n ta.style.cssText =\n `width:100%;box-sizing:border-box;border:1px solid #e5e7eb;border-radius:10px;` +\n `padding:8px 10px;font-size:13px;color:${fg};font-family:inherit;resize:vertical;`;\n ta.addEventListener('input', () => {\n reviewText = ta.value;\n });\n panel.appendChild(ta);\n }\n if (allowMedia) {\n const strip = document.createElement('div');\n strip.style.cssText = 'display:flex;gap:8px;flex-wrap:wrap;align-items:center;';\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = 'image/*,video/*';\n input.multiple = true;\n input.style.cssText = 'display:none;';\n const addTile = document.createElement('button');\n addTile.type = 'button';\n addTile.setAttribute('aria-label', 'Add photo or video');\n addTile.style.cssText =\n `width:48px;height:48px;flex-shrink:0;border:1px dashed ${accent};border-radius:10px;` +\n `background:${accent}14;color:${accent};cursor:pointer;display:flex;align-items:center;justify-content:center;`;\n const plus = lucideSvgEl('plus', { size: 22, color: accent });\n if (plus) addTile.appendChild(plus);\n addTile.addEventListener('click', () => input.click());\n input.addEventListener('change', () => {\n const files = Array.from(input.files || []);\n input.value = '';\n files.forEach((f) => {\n if (mediaUrls.length >= 6) return;\n const thumb = document.createElement('div');\n thumb.style.cssText =\n 'width:48px;height:48px;flex-shrink:0;border-radius:10px;background:#f3f4f6;display:flex;' +\n 'align-items:center;justify-content:center;overflow:hidden;font-size:11px;color:#9ca3af;';\n thumb.textContent = '…';\n strip.insertBefore(thumb, addTile);\n void uploadOne(f).then((up) => {\n if (!up) {\n thumb.textContent = '!';\n return;\n }\n mediaUrls.push(up);\n thumb.textContent = '';\n if (up.type === 'video') {\n const vid = lucideSvgEl('play', { size: 20, color: '#6b7280' });\n if (vid) thumb.appendChild(vid);\n } else {\n const img = document.createElement('img');\n img.src = up.url;\n img.style.cssText = 'width:100%;height:100%;object-fit:cover;';\n thumb.appendChild(img);\n }\n });\n });\n });\n strip.appendChild(addTile);\n strip.appendChild(input);\n panel.appendChild(strip);\n }\n box.appendChild(toggle);\n box.appendChild(panel);\n return box;\n };\n\n const submitBtn = document.createElement('button');\n submitBtn.type = 'button';\n submitBtn.textContent = 'Submit ratings';\n submitBtn.style.cssText =\n `margin-top:2px;width:100%;border:0;border-radius:999px;background:${accent};color:#fff;` +\n 'font-size:14px;font-weight:700;padding:10px 16px;cursor:pointer;opacity:.5;pointer-events:none;';\n const refreshSubmit = (): void => {\n const has = Object.keys(scores).length > 0;\n submitBtn.style.opacity = has ? '1' : '.5';\n submitBtn.style.pointerEvents = has ? 'auto' : 'none';\n };\n\n // ── Detractor arm (spec B1) — negative branch replaces the plain thanks with\n // an inline capture form: reason chips + free text + \"Send to the team\".\n // Submits response_type='detractor_detail' through the SAME /v1/in-app/responses\n // fan-out; the WA follow-through (receipt vs ask) branches on whether this\n // detail was captured, so the customer is never asked the same question twice.\n const renderDetractorForm = (): void => {\n const box = document.createElement('div');\n box.style.cssText = 'display:flex;flex-direction:column;gap:10px;padding:4px 0;';\n\n const head = document.createElement('p');\n head.textContent =\n typeof el.detractor_headline === 'string' && el.detractor_headline.trim()\n ? el.detractor_headline\n : 'Sorry we missed the mark.';\n head.style.cssText = `margin:0;font-size:14px;font-weight:700;color:${fg};`;\n box.appendChild(head);\n\n const prompt = document.createElement('p');\n prompt.textContent =\n typeof el.detractor_prompt === 'string' && el.detractor_prompt.trim()\n ? el.detractor_prompt\n : 'What went wrong?';\n prompt.style.cssText = `margin:0;font-size:13px;color:${fg};opacity:.75;`;\n box.appendChild(prompt);\n\n const selected = new Set<string>();\n const chipLabels = (\n Array.isArray(el.detractor_chips) && el.detractor_chips.length\n ? el.detractor_chips.filter((c): c is string => typeof c === 'string' && !!c.trim())\n : ['Late', 'Quality', 'Wrong item']\n ).slice(0, 8);\n const chipRow = document.createElement('div');\n chipRow.style.cssText = 'display:flex;flex-wrap:wrap;gap:6px;';\n\n const ta = document.createElement('textarea');\n ta.placeholder = 'Tell us more (optional)';\n ta.rows = 3;\n ta.style.cssText =\n `width:100%;box-sizing:border-box;padding:10px;border-radius:10px;` +\n `border:1px solid color-mix(in srgb, ${accent} 30%, transparent);` +\n `background:transparent;color:${fg};font-size:13px;resize:vertical;font-family:inherit;`;\n\n const send = document.createElement('button');\n send.type = 'button';\n const sendLabel =\n typeof el.detractor_send_label === 'string' && el.detractor_send_label.trim()\n ? el.detractor_send_label\n : 'Send to the team';\n send.textContent = sendLabel;\n send.style.cssText =\n `width:100%;border:0;border-radius:999px;background:${accent};color:#fff;` +\n 'font-size:14px;font-weight:700;padding:10px 16px;cursor:pointer;opacity:.5;pointer-events:none;';\n const refreshSend = (): void => {\n const has = selected.size > 0 || !!ta.value.trim();\n send.style.opacity = has ? '1' : '.5';\n send.style.pointerEvents = has ? 'auto' : 'none';\n };\n ta.addEventListener('input', refreshSend);\n\n chipLabels.forEach((label) => {\n const chip = document.createElement('button');\n chip.type = 'button';\n chip.textContent = label;\n const paint = (): void => {\n const on = selected.has(label);\n chip.style.cssText =\n `border-radius:999px;padding:6px 12px;font-size:12px;font-weight:700;cursor:pointer;` +\n `border:1px solid color-mix(in srgb, ${accent} 35%, transparent);` +\n (on ? `background:${accent};color:#fff;` : `background:transparent;color:${fg};`);\n };\n chip.addEventListener('click', () => {\n if (selected.has(label)) selected.delete(label);\n else selected.add(label);\n paint();\n refreshSend();\n });\n paint();\n chipRow.appendChild(chip);\n });\n box.appendChild(chipRow);\n box.appendChild(ta);\n box.appendChild(send);\n\n const note = document.createElement('div');\n note.textContent = 'Your note goes straight to the team.';\n note.style.cssText = `font-size:11px;color:${fg};opacity:.6;text-align:center;`;\n box.appendChild(note);\n\n let sent = false;\n send.addEventListener('click', () => {\n if (sent) return;\n sent = true;\n send.style.opacity = '.6';\n send.style.pointerEvents = 'none';\n send.textContent = 'Sending…';\n ctx.trackEvent(ctx.campaign.id, 'clicked', {\n meta: { element: 'product_rating_list', detail: 'detractor_detail' },\n });\n Promise.resolve(\n ctx.submitResponse?.('detractor_detail', {\n chips: Array.from(selected),\n text: ta.value.trim(),\n order_id: orderId,\n bill_code: billCode,\n token,\n })\n )\n .then((res) => {\n if (res === null) throw new Error('detractor_detail rejected');\n box.innerHTML = '';\n const ok = document.createElement('p');\n ok.textContent =\n typeof el.detractor_thanks === 'string' && el.detractor_thanks.trim()\n ? el.detractor_thanks\n : 'Thank you — the team will personally reach out.';\n ok.style.cssText = `margin:0;font-size:13px;font-weight:700;color:${fg};text-align:center;padding:8px 0;`;\n box.appendChild(ok);\n ctx.playReaction?.(wrap, 'acknowledge');\n })\n .catch(() => {\n // failed → re-enable for retry; the capture is too valuable to drop\n sent = false;\n send.textContent = sendLabel;\n refreshSend();\n });\n });\n\n wrap.appendChild(box);\n };\n\n const done = (branch: string): void => {\n wrap.innerHTML = '';\n if (branch === 'negative') {\n renderDetractorForm();\n return;\n }\n const ack = document.createElement('div');\n ack.style.cssText = 'text-align:center;padding:8px 0;';\n const t = document.createElement('p');\n t.textContent =\n typeof el.thanks_text === 'string' && el.thanks_text.trim()\n ? el.thanks_text\n : 'Thanks for rating your items!';\n t.style.cssText = `margin:0;font-size:14px;font-weight:700;color:${fg};`;\n ack.appendChild(t);\n if (branch === 'positive' && reviewsUrl) {\n const a = document.createElement('a');\n a.href = ctx.sanitizeUrl(reviewsUrl) || '#';\n a.textContent = el.rating_high_cta || 'Share a review';\n a.style.cssText = `display:inline-block;margin-top:6px;font-size:12px;font-weight:700;color:${accent};text-decoration:none;`;\n if (!reviewsViaPage) {\n a.addEventListener('click', () => {\n void ctx.submitResponse?.('review_redirect', { cta: 'item_rating_review' });\n });\n }\n ack.appendChild(a);\n }\n wrap.appendChild(ack);\n ctx.playReaction?.(wrap, branch === 'positive' ? 'affirm' : 'acknowledge');\n };\n\n let submitting = false;\n submitBtn.addEventListener('click', () => {\n const rated = Object.keys(scores);\n if (submitting || rated.length === 0) return;\n submitting = true;\n submitBtn.disabled = true; // guard double-submit on slow links\n submitBtn.style.opacity = '.6';\n submitBtn.textContent = 'Saving…';\n ctx.trackEvent(ctx.campaign.id, 'clicked', {\n meta: { element: 'product_rating_list', items: rated.length },\n });\n Promise.resolve(\n ctx.submitResponse?.('rating', {\n items: rated.map((pid) => ({\n product_id: pid,\n score: scores[pid],\n ...(itemNames[pid] ? { product_name: itemNames[pid] } : {}),\n })),\n order_id: orderId,\n bill_code: billCode,\n token,\n ...(reviewText.trim() ? { review_text: reviewText.trim() } : {}),\n ...(mediaUrls.length ? { media_urls: mediaUrls } : {}),\n })\n )\n .then((res) => {\n // Band is SERVER-authoritative. A missing/failed band degrades to\n // 'passive' (plain thanks) — the quietest state. Never 'positive':\n // that shows the Google link to a customer whose band is unknown —\n // possibly the angriest one on their worst day (the fail-open-default\n // class; locked refinement 4, 2026-08-14).\n const branch =\n (res as { rating_result?: { branch?: string } } | null)?.rating_result?.branch ||\n 'passive';\n done(branch);\n })\n .catch(() => {\n // network error → re-enable for retry (the server write is idempotent\n // anyway — persist_item_ratings UPSERTs on (tenant, order, product)).\n submitting = false;\n submitBtn.disabled = false;\n submitBtn.style.opacity = '1';\n submitBtn.textContent = 'Submit ratings';\n });\n });\n\n const render = (items: RateableItem[]): void => {\n wrap.innerHTML = '';\n const clean = (items || []).filter((it) => it && it.product_id);\n if (clean.length === 0) {\n // No rateable items = nothing to ask (typically the order is ALREADY\n // rated — the host stops supplying items). Collapsing only the repeater\n // left a hollow shell (\"Rate your order\" + submit, zero rows) on every\n // revisit of the bill (found live 2026-08-15). Hide the WHOLE card:\n // deferred one frame because at build time `wrap` isn't attached yet.\n const hideCard = (): void => {\n const tile = wrap.closest('[data-aegis-reaction-anchor]') as HTMLElement | null;\n if (tile) tile.style.display = 'none';\n };\n if (typeof requestAnimationFrame === 'function') {\n requestAnimationFrame(hideCard);\n } else {\n setTimeout(hideCard, 0);\n }\n return;\n }\n const list = document.createElement('div');\n list.style.cssText = 'display:flex;flex-direction:column;gap:10px;';\n clean.forEach((it) => {\n const rowEl = document.createElement('div');\n rowEl.style.cssText = 'display:flex;align-items:center;gap:10px;';\n if (showThumb) rowEl.appendChild(ratingThumb(it, accent));\n if (it.name) itemNames[it.product_id] = it.name;\n const nameEl = document.createElement('span');\n nameEl.textContent = it.name || it.product_id;\n nameEl.style.cssText = `flex:1;min-width:0;font-size:13px;color:${fg};overflow:hidden;text-overflow:ellipsis;white-space:nowrap;`;\n rowEl.appendChild(nameEl);\n rowEl.appendChild(\n starRow(scale, (n) => {\n scores[it.product_id] = n;\n refreshSubmit();\n })\n );\n list.appendChild(rowEl);\n });\n wrap.appendChild(list);\n if (allowText || allowMedia) wrap.appendChild(buildComposer());\n wrap.appendChild(submitBtn);\n };\n\n // ── Item source (trust-gated, plan §7) ──\n const rawAttr = slot.getAttribute('data-rateable-items');\n if (rawAttr) {\n // Trusted Aegis surface (bill / ACS orders) — synchronous, our own SSR JSON.\n try {\n const parsed = JSON.parse(rawAttr) as unknown;\n render(Array.isArray(parsed) ? (parsed as RateableItem[]) : []);\n } catch {\n /* malformed → collapse */\n }\n } else if (token && ctx.apiHost && ctx.writeKey) {\n // Untrusted origin (Shopify / 3rd-party / native) — token→server-resolve\n // (P6 endpoint). Loader → fetch → render; ANY failure drops the loader and\n // collapses so a flaky network never leaves a hung spinner.\n const loader = document.createElement('div');\n loader.textContent = 'Loading your items…';\n loader.style.cssText = `font-size:13px;color:${fg};opacity:.6;padding:6px 0;`;\n wrap.appendChild(loader);\n const url = `${ctx.apiHost}/v1/in-app/rateable-items?token=${encodeURIComponent(token)}`;\n const ac = typeof AbortController !== 'undefined' ? new AbortController() : null;\n if (ac)\n setTimeout(() => {\n try {\n ac.abort();\n } catch {\n /* noop */\n }\n }, 6000);\n fetch(url, { headers: { 'X-Aegis-Write-Key': ctx.writeKey }, signal: ac?.signal })\n .then((r) => (r.ok ? (r.json() as Promise<{ items?: RateableItem[] }>) : null))\n .then((data) => {\n render(Array.isArray(data?.items) ? (data!.items as RateableItem[]) : []);\n })\n .catch(() => {\n wrap.innerHTML = ''; /* collapse */\n });\n }\n // else: no items + no token → empty wrap (collapse)\n\n return wrap;\n}\n\n/** Shared post-response acknowledgement (the \"thank-you\" state) for interactive\n * card elements. Operator-controlled via `el.thanks_text`; falls back to a\n * sensible default. Rendered once, after the customer responds. */\nfunction appendThanks(wrap: HTMLElement, el: CardElement, fg: string, fallback: string): void {\n const msg =\n typeof el.thanks_text === 'string' && el.thanks_text.trim() ? el.thanks_text : fallback;\n if (!msg) return;\n const note = document.createElement('div');\n note.textContent = msg;\n note.setAttribute('role', 'status');\n note.style.cssText = `font-size:12px;font-weight:600;color:${fg};opacity:0.85;margin-top:4px;`;\n wrap.appendChild(note);\n}\n\n/**\n * Interactive tier (C3) — single-choice poll embedded in a card. Records a typed\n * 'quick_poll' response via ctx.submitResponse. Auto-hides with <2 options\n * (bounded-vocabulary rule). The question reuses `el.content`.\n */\nfunction buildPollElement(\n el: CardElement,\n ctx: RenderContext,\n fg: string,\n accent: string\n): HTMLElement | null {\n const opts = Array.isArray(el.poll_options)\n ? el.poll_options.filter((o): o is string => typeof o === 'string' && o.trim().length > 0)\n : [];\n if (opts.length < 2) return null;\n const wrap = document.createElement('div');\n wrap.style.cssText = 'display:flex;flex-direction:column;gap:6px;margin:2px 0;';\n if (el.content) {\n const q = document.createElement('div');\n q.textContent = el.content;\n q.style.cssText = `font-size:13px;font-weight:600;color:${fg};`;\n wrap.appendChild(q);\n }\n let submitted = false;\n opts.forEach((label, idx) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = label;\n btn.style.cssText =\n `text-align:left;cursor:pointer;border:1px solid color-mix(in srgb, ${accent} 35%, transparent);` +\n `background:transparent;color:${fg};border-radius:10px;padding:9px 12px;font-size:13px;font-weight:600;` +\n 'transition:background .12s,border-color .12s;';\n btn.addEventListener('mouseenter', () => {\n if (!submitted) btn.style.background = `color-mix(in srgb, ${accent} 8%, transparent)`;\n });\n btn.addEventListener('mouseleave', () => {\n if (!submitted) btn.style.background = 'transparent';\n });\n btn.addEventListener('click', () => {\n if (submitted) return;\n submitted = true;\n Array.from(wrap.querySelectorAll('button')).forEach((b) => {\n (b as HTMLButtonElement).disabled = true;\n (b as HTMLButtonElement).style.opacity = '0.6';\n });\n btn.style.background = accent;\n btn.style.color = '#fff';\n btn.style.opacity = '1';\n ctx.trackEvent(ctx.campaign.id, 'clicked', {\n meta: { element: 'poll', option_index: idx, option_label: label },\n });\n // Canonical capture — IDENTICAL to the standalone quick_poll widget\n // (response_type 'poll', payload { option_index, option_label }).\n void ctx.submitResponse?.('poll', { option_index: idx, option_label: label });\n ctx.playReaction?.(wrap, 'affirm');\n appendThanks(wrap, el, fg, 'Thanks for your answer!');\n });\n wrap.appendChild(btn);\n });\n return wrap;\n}\n\n/** NPS thank-you state (ported from _renderNpsThankYou) — promoters can get an\n * optional review/referral CTA. Replaces the element's content in place. */\nfunction appendNpsThanks(\n wrap: HTMLElement,\n el: CardElement,\n ctx: RenderContext,\n fg: string,\n accent: string,\n isPromoter: boolean\n): void {\n while (wrap.firstChild) wrap.removeChild(wrap.firstChild);\n const note = document.createElement('div');\n note.setAttribute('role', 'status');\n note.textContent =\n (typeof el.thanks_text === 'string' && el.thanks_text.trim() ? el.thanks_text : '') ||\n (isPromoter\n ? 'Thank you! We’d love a public review.'\n : 'Thanks — your feedback helps us improve.');\n note.style.cssText = `font-size:13px;font-weight:700;color:${fg};text-align:center;margin:6px 0;`;\n wrap.appendChild(note);\n const url =\n isPromoter && typeof el.nps_promoter_url === 'string'\n ? ctx.sanitizeUrl(el.nps_promoter_url)\n : '';\n if (url) {\n const cta = document.createElement('button');\n cta.type = 'button';\n cta.textContent = el.nps_promoter_cta || 'Leave a review';\n cta.style.cssText = `margin:4px auto 0;display:block;padding:9px 18px;border:none;border-radius:999px;background:${accent};color:#fff;font-size:13px;font-weight:800;cursor:pointer;`;\n cta.addEventListener('click', () => {\n ctx.trackEvent(ctx.campaign.id, 'clicked', {\n meta: { element: 'nps', cta: 'promoter_review' },\n });\n // Once per brand — the same tap-through record as the rating button.\n void ctx.submitResponse?.('review_redirect', { cta: 'promoter_review' });\n if (ctx.navigate) ctx.navigate(url, 'cta');\n else window.location.href = url;\n });\n wrap.appendChild(cta);\n }\n}\n\n/** NPS band-conditional reason-capture step (ported from _renderNpsReasonFollowup):\n * per-band prompt + optional quick-pick chips + textarea + send/skip → submit. */\nfunction renderNpsReasonStep(\n wrap: HTMLElement,\n el: CardElement,\n ctx: RenderContext,\n score: number,\n fg: string,\n accent: string\n): void {\n const promoterThreshold =\n typeof el.nps_promoter_threshold === 'number' ? el.nps_promoter_threshold : 9;\n const band = npsBandFromScore(score);\n const prompt =\n score >= promoterThreshold\n ? el.nps_follow_up_promoter || 'What do you love most about us?'\n : score <= 6\n ? el.nps_follow_up_detractor || 'How can we improve?'\n : el.nps_follow_up_passive || 'What would make us a 10?';\n while (wrap.firstChild) wrap.removeChild(wrap.firstChild);\n const heading = document.createElement('div');\n heading.textContent = prompt;\n heading.style.cssText = `font-size:13px;font-weight:700;color:${fg};text-align:center;margin-bottom:8px;`;\n wrap.appendChild(heading);\n const ta = document.createElement('textarea');\n const chips = Array.isArray(el.nps_reason_chips) ? el.nps_reason_chips : [];\n if (chips.length) {\n const chipRow = document.createElement('div');\n chipRow.style.cssText =\n 'display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin-bottom:8px;';\n chips.slice(0, 8).forEach((c) => {\n const chip = document.createElement('button');\n chip.type = 'button';\n chip.textContent = c;\n chip.style.cssText = `padding:5px 11px;border-radius:999px;border:1px solid color-mix(in srgb, ${accent} 35%, transparent);background:transparent;color:${fg};font-size:12px;cursor:pointer;`;\n chip.addEventListener('click', () => {\n ta.value = ta.value ? `${ta.value}, ${c}` : c;\n ta.focus();\n });\n chipRow.appendChild(chip);\n });\n wrap.appendChild(chipRow);\n }\n ta.rows = 3;\n ta.placeholder = 'Share a little more…';\n ta.style.cssText = `width:100%;box-sizing:border-box;padding:10px;border-radius:10px;border:1px solid color-mix(in srgb, ${accent} 30%, transparent);background:transparent;color:${fg};font-size:13px;resize:vertical;margin-bottom:10px;font-family:inherit;`;\n wrap.appendChild(ta);\n let done = false;\n const finish = (withComment: boolean): void => {\n if (done) return;\n done = true;\n const comment = withComment ? ta.value.trim() : '';\n void ctx.submitResponse?.('nps', comment ? { score, band, comment } : { score, band });\n ctx.playReaction?.(\n wrap,\n score >= promoterThreshold ? 'affirm' : score <= 6 ? 'empathize' : 'acknowledge'\n );\n appendNpsThanks(wrap, el, ctx, fg, accent, score >= promoterThreshold);\n };\n const btnRow = document.createElement('div');\n btnRow.style.cssText = 'display:flex;gap:8px;justify-content:center;';\n const send = document.createElement('button');\n send.type = 'button';\n send.textContent = 'Send';\n send.style.cssText = `padding:9px 20px;border-radius:999px;border:none;background:${accent};color:#fff;font-weight:800;font-size:13px;cursor:pointer;`;\n send.addEventListener('click', () => finish(true));\n const skip = document.createElement('button');\n skip.type = 'button';\n skip.textContent = 'Skip';\n skip.style.cssText = `padding:9px 16px;border-radius:999px;border:1px solid color-mix(in srgb, ${accent} 30%, transparent);background:transparent;color:${fg};font-weight:600;font-size:13px;cursor:pointer;`;\n skip.addEventListener('click', () => finish(false));\n btnRow.appendChild(send);\n btnRow.appendChild(skip);\n wrap.appendChild(btnRow);\n ta.focus();\n}\n\n/**\n * Interactive tier (C3) — 0–10 NPS scale embedded in a card. Records a typed\n * 'nps' response { score } via ctx.submitResponse — IDENTICAL to the standalone\n * nps_survey widget, so it aggregates in the Responses tab + NPS score query +\n * fires the same reward grant. Question reuses `el.content`. With\n * `nps_followup_enabled` it runs the band-conditional reason step before thanks.\n */\nfunction buildNpsElement(\n el: CardElement,\n ctx: RenderContext,\n fg: string,\n accent: string,\n muted: string\n): HTMLElement {\n const wrap = document.createElement('div');\n wrap.style.cssText = 'display:flex;flex-direction:column;gap:6px;margin:2px 0;';\n if (el.content) {\n const q = document.createElement('div');\n q.textContent = el.content;\n q.style.cssText = `font-size:13px;font-weight:600;color:${fg};`;\n wrap.appendChild(q);\n }\n const row = document.createElement('div');\n row.style.cssText = 'display:flex;flex-wrap:wrap;gap:4px;';\n let submitted = false;\n for (let n = 0; n <= 10; n++) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = String(n);\n btn.setAttribute('aria-label', `Score ${n}`);\n btn.style.cssText =\n `cursor:pointer;min-width:26px;height:30px;border:1px solid color-mix(in srgb, ${accent} 30%, transparent);` +\n `background:transparent;color:${fg};border-radius:7px;font-size:12px;font-weight:700;transition:background .12s;`;\n btn.addEventListener('mouseenter', () => {\n if (!submitted) btn.style.background = `color-mix(in srgb, ${accent} 10%, transparent)`;\n });\n btn.addEventListener('mouseleave', () => {\n if (!submitted) btn.style.background = 'transparent';\n });\n btn.addEventListener('click', () => {\n if (submitted) return;\n submitted = true;\n Array.from(row.children).forEach((b) => {\n (b as HTMLButtonElement).disabled = true;\n (b as HTMLButtonElement).style.opacity = '0.55';\n });\n btn.style.background = accent;\n btn.style.color = '#fff';\n btn.style.opacity = '1';\n // Emit the canonical NpsPayload — band DERIVED via the shared contract\n // helper (server re-derives from score as source of truth; this denorm\n // keeps the band consistent with the registry classification).\n const npsPayload: NpsPayload = { score: n, band: npsBandFromScore(n) };\n ctx.trackEvent(ctx.campaign.id, 'clicked', {\n meta: { element: 'nps', score: n, band: npsPayload.band },\n });\n if (el.nps_followup_enabled === true) {\n // Band-conditional reason-capture step — submits AFTER send/skip (with the\n // optional comment), matching the bespoke nps_survey follow-up flow.\n renderNpsReasonStep(wrap, el, ctx, n, fg, accent);\n } else {\n void ctx.submitResponse?.('nps', { ...npsPayload });\n ctx.playReaction?.(wrap, n >= 9 ? 'affirm' : n >= 7 ? 'acknowledge' : 'empathize');\n appendThanks(wrap, el, fg, 'Thanks for your feedback!');\n }\n });\n row.appendChild(btn);\n }\n wrap.appendChild(row);\n const scale = document.createElement('div');\n scale.style.cssText = `display:flex;justify-content:space-between;font-size:10px;color:${muted};`;\n const lo = document.createElement('span');\n lo.textContent = 'Not likely';\n const hi = document.createElement('span');\n hi.textContent = 'Very likely';\n scale.appendChild(lo);\n scale.appendChild(hi);\n wrap.appendChild(scale);\n return wrap;\n}\n\nfunction buildProgressElement(\n el: CardElement,\n ctx: RenderContext,\n accent: string,\n muted: string\n): HTMLElement {\n const { trackEvent, campaign, sanitizeColor } = ctx;\n const goal = el.goal_type || 'cart_total';\n const moneyGoal = goal === 'cart_total';\n const fill = el.fill ? sanitizeColor(el.fill) : accent;\n const props = (): Record<string, unknown> => (ctx.properties && ctx.properties()) || {};\n const sym = (): string => {\n if (el.currency_symbol) return el.currency_symbol;\n const code = ctx.getCartState && ctx.getCartState()?.currency;\n const M: Record<string, string> = { INR: '₹', USD: '$', EUR: '€', GBP: '£' };\n return (code && M[code]) || '';\n };\n const threshold = (): number => {\n if (typeof el.threshold === 'number' && el.threshold > 0) return el.threshold;\n if (el.threshold_source === 'free_delivery_setting') {\n // The live quote FIRST. `freeDeliveryAbove` is the server's answer for\n // this cart's address — the matched area's line, not the brand's flat\n // one — pushed through `runtime.setCartState(...)` by the storefront\n // that already renders the same number on the bill.\n const fromCart = ctx.getCartState && ctx.getCartState()?.freeDeliveryAbove;\n if (typeof fromCart === 'number' && fromCart > 0) return fromCart;\n // Then the hydrated token, kept for a host app that sets card_context\n // itself. No server path in this repo writes it, which is why this\n // element rendered an empty bar for as long as it existed.\n const v = Number(props()['cart.free_delivery_above']);\n if (v > 0) return v;\n }\n return 0;\n };\n const readCur = (): number => {\n // Cart-backed goals come from the shared first-party reader (identical to the\n // standalone bar + the dashboard preview); loyalty/referrals come from props.\n const fromCart = readGoalFromCart(goal, ctx.getCartState && ctx.getCartState());\n if (fromCart !== undefined) return fromCart;\n const p = props();\n if (goal === 'loyalty_points') return Number(p['loyalty.points']) || 0;\n if (goal === 'referrals') return Number(p['referrals.count']) || 0;\n return 0;\n };\n // Is the cart actually populated? Used to tell \"shopper has items but is below\n // the threshold\" (a real witnessed-below state) apart from \"cart hasn't\n // hydrated yet\" (transient empty 0 on a fresh page). The latter must NOT count\n // as a witnessed below-threshold baseline — see `seenIncomplete` below.\n const cartItems = (): number => {\n const c = ctx.getCartState && ctx.getCartState();\n return c && typeof c.itemCount === 'number' ? c.itemCount : 0;\n };\n\n const wrap = document.createElement('div');\n wrap.style.cssText = 'display:flex;flex-direction:column;gap:3px;min-width:0;width:100%;';\n const sub = document.createElement('div');\n sub.style.cssText = `font-size:11.5px;line-height:1.2;color:${muted};white-space:nowrap;overflow:hidden;text-overflow:ellipsis;`;\n const shell = document.createElement('div');\n shell.style.cssText = `height:3px;border-radius:999px;background:color-mix(in srgb, ${fill} 16%, transparent);overflow:hidden;`;\n const bar = document.createElement('div');\n bar.style.cssText = `height:100%;border-radius:999px;background:${fill};width:0%;transition:width .4s cubic-bezier(.4,0,.2,1);`;\n shell.appendChild(bar);\n wrap.appendChild(sub);\n wrap.appendChild(shell);\n\n let firedUnlock = false;\n // Celebrate ONLY on a transition the customer witnesses — they were below the\n // threshold while THIS card was alive and then crossed it. Without this guard\n // the celebration also fires on mount-already-complete: first page load, or\n // closing a modal / navigating re-mounts the card with the cart already over\n // the threshold → confetti for something they didn't just unlock. `firedUnlock`\n // alone only de-dupes within a single mount, so every re-mount re-fired.\n // `seenIncomplete` gates it: mount-already-complete adopts the unlocked state\n // as the baseline (emerald label, NO confetti); a real crossing celebrates.\n let seenIncomplete = false;\n const update = (): void => {\n const th = threshold();\n const cur = readCur();\n // An UNMEASURED goal is not a goal of zero. `free_delivery_setting` has no\n // threshold until the server has quoted an address, so render nothing at\n // all rather than an empty track the shopper reads as \"you have made no\n // progress\" — the same rule the fee itself follows, where `null` never\n // renders as a price.\n if (th <= 0 && el.threshold_source === 'free_delivery_setting') {\n wrap.style.display = 'none';\n return;\n }\n wrap.style.display = 'flex';\n const { pct, complete } = computeProgress({ goalType: goal, threshold: th, current: cur });\n bar.style.width = `${pct}%`;\n const labeled = cardProgressSubLabel({\n goalType: goal,\n threshold: th,\n current: cur,\n rewardText: el.reward_text,\n currencySymbol: moneyGoal ? sym() : '',\n showPct: el.show_pct,\n });\n sub.textContent = labeled.text;\n if (complete) {\n sub.style.color = '#10b981'; // emerald-500 — the \"unlocked\" success tone\n sub.style.fontWeight = '700';\n if (!firedUnlock) {\n firedUnlock = true;\n // Only a WITNESSED crossing fires the unlock event + celebration.\n // Mount-already-complete (seenIncomplete still false) is silent — the\n // emerald \"unlocked\" label above already communicates the state.\n if (seenIncomplete) {\n trackEvent(campaign.id, 'clicked', { stepId: el.id || 'unlocked' });\n // Reward the moment: shared Lottie + confetti burst from the bar (the\n // SAME celebration system the games/forms use). Reduced-motion aware,\n // fired once per crossing. Opt out with `celebrate_on_complete: false`.\n if (el.celebrate_on_complete !== false) ctx.playReaction?.(wrap, 'celebrate');\n }\n }\n } else {\n sub.style.color = muted;\n sub.style.fontWeight = '';\n // Only count this as a WITNESSED below-threshold baseline when the shopper\n // is actually engaged — a non-empty cart (cart goals) or a non-zero metric\n // (loyalty/referrals). On a fresh page the cart hydrates empty(0) → its\n // restored value; treating that transient 0 as \"seen below\" made the\n // hydration look like a below→above crossing and fired confetti on EVERY\n // page load / remount with an already-full cart. Empty = not yet engaged.\n const engaged = goal === 'cart_total' || goal === 'items_in_cart' ? cartItems() > 0 : cur > 0;\n if (engaged) seenIncomplete = true;\n firedUnlock = false; // re-arm if the cart drops back below the threshold\n }\n };\n update();\n const timer = setInterval(update, 1000);\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => clearInterval(timer), { once: true });\n }\n return wrap;\n}\n\n/**\n * Resolve the canonical `AegisAction` a CTA button fires. The operator picks it\n * per-element via `el.cta_action`; when unset it falls back to the SURFACE\n * default so existing campaigns keep their behaviour (cart rail → add_to_cart,\n * navigate surfaces → navigate). This is the single place \"what does this CTA do\"\n * is decided for every card-based campaign (CANONICAL_ACTION_MODEL).\n */\nexport function buildCtaAction(\n el: CardElement,\n card: TemplateCard,\n meta: Record<string, unknown>,\n inline: boolean,\n sanitizeUrl: (u: string) => string | null\n): AegisAction {\n const kind = el.cta_action ?? (inline ? 'add_to_cart' : 'navigate');\n const safe = (u?: string) => (u ? sanitizeUrl(u) || undefined : undefined);\n const url = safe(el.cta_url || card.cta_url);\n const productId = meta.product_id as string | undefined;\n const sku = meta.sku as string | undefined;\n const platformProductId = meta.platform_product_id as string | undefined;\n const platformUrl = safe(meta.platform_url as string | undefined);\n // Default variant id (synced onto the card) so add_to_cart can add DIRECTLY\n // instead of round-tripping /products/{handle}.js to discover the variant.\n const platformVariantId = meta.platform_variant_id as string | undefined;\n switch (kind) {\n case 'open_cart':\n return { type: 'open_cart' };\n case 'open_login':\n return { type: 'open_login' };\n case 'open_product':\n return {\n type: 'open_product',\n productId,\n sku,\n url: platformUrl || url,\n platformProductId,\n platformUrl,\n };\n case 'navigate':\n return { type: 'navigate', url: url || '' };\n case 'custom':\n return { type: 'custom', name: el.cta_custom_name || 'cta', payload: { ...meta, url } };\n case 'add_to_cart':\n default:\n return {\n type: 'add_to_cart',\n productId,\n sku,\n platformProductId,\n platformVariantId,\n title: card.title,\n price: meta.price ?? card.body,\n imageUrl: card.image_url,\n url: platformUrl || url,\n qty: 1,\n };\n }\n}\n\n/**\n * Render one card tile from the resolved template. Shared by every card surface.\n *\n * `action` sets the SURFACE DEFAULT for the CTA + body-click (decoupled from\n * sizing, which follows `layout`). The per-element `cta_action` overrides the\n * CTA default; both resolve to a canonical `AegisAction` via `ctx.dispatchAction`\n * → the host's `onAction` handler.\n * 'cart' → CTA default `add_to_cart`, body click → `open_product`\n * (storefront recommendation RAIL).\n * 'navigate' → CTA default `navigate` (cta_url), body click → `navigate`\n * (overlay sheet + the merchandising carousel).\n */\nexport function renderCardFromTemplate(\n card: TemplateCard,\n opts: {\n template: CardTemplate;\n ctx: RenderContext;\n action: 'cart' | 'navigate';\n layout?: string;\n layoutAccent: string;\n }\n): HTMLElement {\n const { ctx, action, layout, layoutAccent } = opts;\n const inline = action === 'cart';\n const { campaign, trackEvent, sanitizeUrl, sanitizeColor } = ctx;\n const tpl = applyPerCardOverride(opts.template, asLook(card.style));\n const frame = tpl.frame || {};\n const meta = card.metadata || {};\n const accent =\n typeof frame.accent === 'string' && frame.accent ? sanitizeColor(frame.accent) : layoutAccent;\n const brand = `var(--aegis-brand-accent, ${accent})`;\n\n // Surface foreground — derived from frame.bg luminance so text/icons read on\n // ANY surface (charcoal value bar → light text; white product card → dark).\n // Back-compat: white bg → '#1f2937' (unchanged from the old hardcode).\n const fg = contrastFg(frame.bg ? sanitizeColor(frame.bg) : '#ffffff');\n const muted = fg === '#ffffff' ? 'rgba(255,255,255,0.72)' : '#6b7280';\n\n const tile = document.createElement('div');\n // Mark the tile as the celebration anchor so card-scoped reactions\n // (`playReaction`) center on the WHOLE card, not on the inner element that\n // fired them (e.g. the progress bar's column, which sits lower in a row card).\n tile.setAttribute('data-aegis-reaction-anchor', '');\n // Card font — Urbanist by default (lazy-loaded for any host), operator-\n // overridable via `frame.fontFamily`; '' ⇒ inherit the host page font. All\n // text elements below inherit this off the tile, so one declaration covers\n // title / body / price / CTA.\n const cardFont = resolveCardFont(frame.fontFamily);\n ensureCardFontLoaded(cardFont);\n const radius =\n frame.radius !== undefined ? `${parseInt(String(frame.radius), 10) || 12}px` : '12px';\n const fLayout = frame.layout; // template-declared layout (value bar / grid cell)\n const dir = fLayout === 'row' ? 'row' : 'column';\n const tileGap = frame.gap != null ? frame.gap : 6;\n // The rail's fixed 150px tile applies ONLY to legacy product cards that don't\n // declare their own layout (back-compat). A template WITH frame.layout is\n // container-driven / full-width (value bar) or grid-managed by the caller.\n const sizing = fLayout\n ? frame.width != null\n ? `width:${typeof frame.width === 'number' ? `${frame.width}px` : frame.width};`\n : 'flex:1 1 auto; width:100%;'\n : layout === 'grid' || layout === 'list'\n ? '' // grid cell / vertical-list row → full container width (no fixed 150px rail tile)\n : 'flex:0 0 150px; scroll-snap-align:start;';\n tile.style.cssText = `\n box-sizing:border-box; display:flex; flex-direction:${dir}; ${fLayout === 'row' ? 'align-items:center;' : ''} gap:${tileGap}px; position:relative;\n color:${fg};\n ${cardFont ? `font-family:${cardFont};` : ''}\n background:${frame.bg ? sanitizeColor(frame.bg) : '#ffffff'};\n border:1px solid ${frame.border ? sanitizeColor(frame.border) : 'transparent'};\n border-radius:${radius}; padding:8px;\n ${\n // frame.shadow (2026-08-15): 'none'/false → flat (host surfaces like the\n // bill page use flat bordered sections — the floating default made the\n // rating card visually shout); a string → custom box-shadow; absent →\n // legacy default. Config-driven: no SDK release needed per surface.\n frame.shadow === 'none' || frame.shadow === false\n ? ''\n : typeof frame.shadow === 'string' && frame.shadow.trim()\n ? `box-shadow:${frame.shadow.replace(/[;{}<>]/g, '')};`\n : fLayout === 'row'\n ? ''\n : 'box-shadow:0 4px 16px rgba(0,0,0,0.18);'\n }\n ${frame.max_width ? `max-width:${frame.max_width}px;` : ''}${frame.min_width ? `min-width:${frame.min_width}px;` : ''}\n ${frame.height ? `height:${frame.height}px;` : ''}${frame.min_height ? `min-height:${frame.min_height}px;` : ''}\n ${sizing}\n cursor:${card.cta_url || inline ? 'pointer' : 'default'}; transition:transform 0.15s ease;\n `;\n\n const elements =\n tpl.elements && tpl.elements.length ? tpl.elements : DEFAULT_CARD_TEMPLATE.elements!;\n let imageWrap: HTMLElement | null = null;\n\n // Recursive painter so `group` containers can nest arbitrary layouts.\n const paintInto = (container: HTMLElement, els: CardElement[]): void => {\n for (const el of els) {\n switch (el.type) {\n case 'image': {\n const wrap = document.createElement('div');\n wrap.style.cssText = `position:relative;width:100%;aspect-ratio:${ASPECT[el.aspect || '1/1'] || '1 / 1'};border-radius:${el.radius ?? 10}px;overflow:hidden;background:#f7f8fa;`;\n const safeVideo = card.video_url ? sanitizeUrl(card.video_url) : '';\n if (safeVideo) {\n const v = document.createElement('video');\n v.src = safeVideo;\n v.muted = true;\n v.loop = true;\n v.autoplay = true;\n v.playsInline = true;\n v.setAttribute('playsinline', '');\n v.style.cssText = 'width:100%;height:100%;object-fit:cover;display:block;';\n wrap.appendChild(v);\n } else if (card.image_url) {\n const safe = sanitizeUrl(card.image_url);\n if (safe) {\n const img = document.createElement('img');\n img.src = safe;\n img.alt = '';\n img.loading = 'lazy';\n img.style.cssText = 'width:100%;height:100%;object-fit:cover;display:block;';\n wrap.appendChild(img);\n }\n }\n imageWrap = wrap;\n container.appendChild(wrap);\n break;\n }\n case 'badge': {\n const text = el.text;\n if (!text) break;\n const badge = document.createElement('span');\n badge.textContent = text;\n const bg = el.color ? sanitizeColor(el.color) : brand;\n badge.style.cssText = `position:absolute;top:6px;left:6px;z-index:1;background:${bg};color:#fff;font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:0.03em;padding:3px 6px;border-radius:5px;`;\n (imageWrap || container).appendChild(badge);\n break;\n }\n case 'price': {\n const node = paintPrice(card, el, accent, ctx);\n if (node) container.appendChild(node);\n break;\n }\n case 'title': {\n if (!card.title) break;\n const t = document.createElement('div');\n t.textContent = card.title;\n const lines = el.lines && el.lines > 0 ? el.lines : 2;\n t.style.cssText = `font-size:12.5px;font-weight:600;line-height:1.3;text-align:${el.align ?? 'left'};color:${el.color ? sanitizeColor(el.color) : fg};display:-webkit-box;-webkit-line-clamp:${lines};-webkit-box-orient:vertical;overflow:hidden;`;\n container.appendChild(t);\n break;\n }\n case 'subtitle': {\n // Merchandising selling line (card.body). Used by carousel cards; skipped\n // for product cards (their templates carry `price`, not `subtitle`).\n if (!card.body) break;\n const s = document.createElement('div');\n s.textContent = card.body;\n const lines = el.lines && el.lines > 0 ? el.lines : 2;\n s.style.cssText = `font-size:11px;color:${el.color ? sanitizeColor(el.color) : muted};line-height:1.3;text-align:${el.align ?? 'left'};display:-webkit-box;-webkit-line-clamp:${lines};-webkit-box-orient:vertical;overflow:hidden;`;\n container.appendChild(s);\n break;\n }\n case 'rating': {\n const node = paintRating(card);\n if (node) container.appendChild(node);\n break;\n }\n case 'category': {\n const cat = (meta as Record<string, unknown>).category;\n if (typeof cat !== 'string' || !cat) break;\n const chip = document.createElement('span');\n chip.textContent = cat;\n chip.style.cssText =\n 'align-self:flex-start;background:#f3f4f6;color:#6b7280;font-size:10px;font-weight:500;padding:2px 7px;border-radius:6px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';\n container.appendChild(chip);\n break;\n }\n // ── rich-card-editor elements (P0) ──────────────────────────────────\n case 'icon': {\n const px = ICON_PX[el.size || 'md'] || 30;\n const node = document.createElement('div');\n const iconColor = el.color ? sanitizeColor(el.color) : fg;\n if (el.bubble) {\n const bubbleTint = el.color ? sanitizeColor(el.color) : brand;\n node.style.cssText = `flex:0 0 auto;width:${px}px;height:${px}px;border-radius:999px;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb, ${bubbleTint} 16%, transparent);`;\n } else {\n node.style.cssText = 'flex:0 0 auto;display:flex;align-items:center;';\n }\n const svg = lucideSvgEl(el.icon_name || 'circle-check', {\n size: el.bubble ? Math.round(px * 0.55) : Math.round(px * 0.7),\n color: iconColor,\n strokeWidth: 2,\n });\n if (svg) node.appendChild(svg);\n container.appendChild(node);\n break;\n }\n case 'text': {\n const t = document.createElement('div');\n const fs = TEXT_PX[el.size || 'md'] || 13;\n t.style.cssText =\n `font-size:${fs}px;font-weight:${WEIGHT[el.weight ?? 'medium']};` +\n `text-align:${el.align ?? 'left'};color:${el.color ? sanitizeColor(el.color) : fg};` +\n `line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;`;\n t.textContent = interpolateTokens(el.content ?? '', ctx);\n container.appendChild(t);\n break;\n }\n case 'spacer': {\n const sp = document.createElement('div');\n sp.style.height = `${el.height ?? 8}px`;\n container.appendChild(sp);\n break;\n }\n case 'progress': {\n container.appendChild(buildProgressElement(el, ctx, brand, muted));\n break;\n }\n case 'countdown': {\n const cd = buildCountdownElement(el, fg, muted);\n if (cd) container.appendChild(cd);\n break;\n }\n case 'rating_input': {\n container.appendChild(buildRatingInputElement(el, ctx, fg));\n break;\n }\n case 'poll': {\n const p = buildPollElement(el, ctx, fg, brand);\n if (p) container.appendChild(p);\n break;\n }\n case 'nps': {\n container.appendChild(buildNpsElement(el, ctx, fg, brand, muted));\n break;\n }\n case 'product_rating_list': {\n container.appendChild(buildProductRatingListElement(el, ctx, fg));\n break;\n }\n case 'group': {\n const g = document.createElement('div');\n const gdir = el.layout === 'row' ? 'row' : 'column';\n g.style.cssText =\n `display:flex;flex-direction:${gdir};${el.layout === 'row' ? 'align-items:center;' : ''}` +\n `gap:${el.gap ?? 4}px;min-width:0;${el.layout === 'row' ? '' : 'flex:1 1 auto;'}`;\n paintInto(g, el.elements || []);\n container.appendChild(g);\n break;\n }\n case 'cta': {\n const label = el.text || card.cta_text || (inline ? 'Add' : 'Shop');\n const cta = document.createElement('button');\n cta.className = 'aegis-cta';\n cta.textContent = label;\n const overlayOnImage = !!el.overlay && !!imageWrap;\n if (overlayOnImage) {\n // Zepto/Blinkit-style ADD pill on the image's bottom-right. Outline =\n // white fill + brand border/text (the canonical quick-commerce look);\n // uppercase + tracking so a lowercase \"Add\" label still reads as \"ADD\".\n cta.style.cssText =\n el.variant === 'outline'\n ? `position:absolute;right:6px;bottom:6px;background:#fff;color:${brand};border:1px solid ${brand};border-radius:9px;padding:5px 18px;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:0.04em;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,0.14);`\n : `position:absolute;right:6px;bottom:6px;background:${brand};color:#fff;border:none;border-radius:9px;padding:5px 18px;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:0.04em;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,0.14);`;\n } else {\n cta.style.cssText =\n el.variant === 'outline'\n ? `margin-top:${fLayout === 'row' ? '0' : 'auto'};background:transparent;color:${brand};border:1px solid ${brand};border-radius:999px;padding:6px 10px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;`\n : `margin-top:${fLayout === 'row' ? '0' : 'auto'};background:${brand};color:#fff;border:none;border-radius:999px;padding:7px 10px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;`;\n }\n cta.addEventListener('click', (e) => {\n e.stopPropagation();\n trackEvent(campaign.id, 'clicked', { stepId: el.id || 'add' });\n // Canonical CTA action — operator-chosen (`el.cta_action`) with a\n // surface default (cart rail → add_to_cart, else navigate). Funnels\n // through the manager's `dispatchAction` (host `onAction` → canonical\n // fallback navigation), so a CTA is never a dead no-op. `dispatchAction`\n // is undefined only outside the manager (dashboard preview), where CTAs\n // are intentionally inert. See CANONICAL_ACTION_MODEL_TRACKER.md.\n ctx.dispatchAction?.(\n buildCtaAction(el, card, meta as Record<string, unknown>, inline, sanitizeUrl)\n );\n });\n (overlayOnImage ? imageWrap! : container).appendChild(cta);\n break;\n }\n }\n }\n };\n paintInto(tile, elements);\n\n // Card-body click. Inline (rail) → `open_product` so the storefront routes\n // with its own slug; navigate surfaces → `navigate` the cta_url. Both funnel\n // through the canonical `dispatchAction` (host `onAction` → fallback). Inert in\n // dashboard preview (no `dispatchAction`).\n const open = (e: Event) => {\n e.stopPropagation();\n trackEvent(campaign.id, 'clicked');\n const bm = meta as Record<string, unknown>;\n const safe = (u?: string) => (u ? sanitizeUrl(u) || undefined : undefined);\n const act: AegisAction = inline\n ? {\n type: 'open_product',\n productId: bm.product_id as string | undefined,\n sku: bm.sku as string | undefined,\n url: safe(bm.platform_url as string | undefined) || safe(card.cta_url),\n platformProductId: bm.platform_product_id as string | undefined,\n platformUrl: safe(bm.platform_url as string | undefined),\n }\n : { type: 'navigate', url: safe(card.cta_url) || '' };\n ctx.dispatchAction?.(act);\n };\n if (inline || card.cta_url) tile.addEventListener('click', open);\n\n // ── celebrate-on-show (Celebration-as-Campaign cutover) ────────────────────\n // Fire the shared reaction once when this card mounts. `celebrate_scope`\n // decides the bounds: 'viewport' = full-page hero burst (overlay surfaces),\n // 'card' = anchored to the tile (micro-delight). One fire per render — the\n // campaign's frequency_cap (max_impressions/scope) gates how often the card\n // is delivered, so this never nags. Reduced-motion is handled inside\n // playReaction. Deferred a tick so the tile has DOM layout to anchor onto.\n if (frame.celebrate_on_show === true && ctx.playReaction) {\n const reaction = frame.celebrate_reaction || 'celebrate';\n const cscope = frame.celebrate_scope === 'viewport' ? 'viewport' : 'card';\n setTimeout(() => {\n ctx.playReaction?.(tile, reaction, frame.celebrate_lottie, cscope);\n // Transactional fallback dedup: a VIEWPORT (hero) campaign celebration\n // sets a fresh session marker so a host-app fallback for the SAME moment\n // (e.g. the storefront's order-confirmed LottieAffirm confetti) no-ops —\n // campaign celebration won. Card-scoped micro-delights don't set it\n // (nothing in the app duplicates them). See\n // CELEBRATION_AS_CAMPAIGN_CUTOVER_TRACKER.md §5.\n if (cscope === 'viewport') {\n try {\n if (typeof sessionStorage !== 'undefined') {\n sessionStorage.setItem('aegis:celebrated', String(Date.now()));\n }\n } catch {\n /* storage blocked — fallback simply fires too; acceptable */\n }\n }\n }, 0);\n }\n\n return tile;\n}\n","/**\n * StickyBar renderer — a pinned notification strip at the top or bottom\n * of the viewport. Used for free-shipping hints, flash-sale banners,\n * cart-reminder strips. Dismissible, optionally auto-hiding.\n *\n * interactive_config:\n * sticky_position: 'top' | 'bottom' (required)\n * sticky_bg_color?: string\n * sticky_dismissible?: boolean default true\n * sticky_auto_hide_ms?: number 0 = never\n */\n\nimport { lucideSvg } from '../lucide-svg';\nimport type { RenderContext } from './types';\n\n// Persist per-campaign dismissal across reloads so we don't re-show a\n// bar the user already closed. Scoped per campaign ID; storage key\n// survives navigation but is cleared when the user explicitly clears site data.\nconst DISMISS_STORAGE_PREFIX = 'aegis_sticky_dismissed:';\n\nexport function renderStickyBar(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeUrl, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const position = ic.sticky_position === 'top' ? 'top' : 'bottom';\n const dismissible = ic.sticky_dismissible !== false;\n const autoHide = Number(ic.sticky_auto_hide_ms) || 0;\n\n // Already dismissed this session? Skip silently — the prefetch bundle\n // keeps re-offering it, but the user's explicit close stands until TTL.\n try {\n if (\n typeof localStorage !== 'undefined' &&\n localStorage.getItem(DISMISS_STORAGE_PREFIX + campaign.id)\n ) {\n log(`sticky_bar ${campaign.id} suppressed — user dismissed earlier`);\n return;\n }\n } catch {\n // localStorage blocked — fall through and render\n }\n\n addAnimationStyles();\n\n const bg = sanitizeColor(\n (ic.sticky_bg_color as string) || (campaign.background_color as string) || '#4169e1',\n );\n const fg = sanitizeColor((campaign.text_color as string) || '#ffffff');\n\n const bar = document.createElement('div');\n bar.className = 'aegis-in-app-sticky-bar';\n bar.setAttribute('data-campaign-id', campaign.id);\n\n const positionCss =\n position === 'top'\n ? 'top: 0; left: 0; right: 0; animation: aegisSlideDown 0.3s ease-out;'\n : 'bottom: 0; left: 0; right: 0; animation: aegisSlideInFromBottom 0.3s ease-out;';\n\n bar.style.cssText = `\n position: fixed; ${positionCss}\n background: ${bg}; color: ${fg};\n padding: 10px 14px; z-index: 999998;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n display: flex; align-items: center; gap: 12px; justify-content: center;\n box-shadow: 0 ${position === 'top' ? '2px' : '-2px'} 8px rgba(0,0,0,0.08);\n `;\n\n const label = document.createElement('div');\n label.style.cssText = 'flex: 0 1 auto; font-size: 13px; font-weight: 500; text-align: center;';\n const strong = document.createElement('strong');\n strong.textContent = campaign.title;\n strong.style.cssText = 'margin-right: 6px; font-weight: 700;';\n label.appendChild(strong);\n label.appendChild(document.createTextNode(campaign.body));\n bar.appendChild(label);\n\n // CTA renders whenever LABELLED (was hidden unless BOTH set).\n const stickyCtaLabel = campaign.button_text || (campaign.action_url ? 'Shop now' : '');\n if (stickyCtaLabel) {\n const cta = document.createElement('button');\n cta.className = 'aegis-cta';\n cta.textContent = stickyCtaLabel;\n cta.style.cssText = `\n background: ${fg}; color: ${bg};\n border: none; padding: 6px 14px; border-radius: 999px;\n font-size: 12px; font-weight: 700; cursor: pointer; white-space: nowrap;\n `;\n cta.addEventListener('click', () => {\n trackEvent(campaign.id, 'clicked');\n if (campaign.action_url) {\n const safe = sanitizeUrl(campaign.action_url);\n if (safe) window.location.href = safe;\n }\n });\n bar.appendChild(cta);\n }\n\n let autoHideTimer: ReturnType<typeof setTimeout> | null = null;\n const remove = (persist: boolean) => {\n if (autoHideTimer) clearTimeout(autoHideTimer);\n if (persist) {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(DISMISS_STORAGE_PREFIX + campaign.id, '1');\n }\n } catch {\n // swallow — localStorage may be disabled\n }\n }\n bar.remove();\n };\n\n if (dismissible) {\n const close = document.createElement('button');\n close.innerHTML = lucideSvg('x', { size: 16 });\n close.setAttribute('aria-label', 'Dismiss');\n close.style.cssText = `\n background: transparent; border: none; color: inherit;\n font-size: 16px; cursor: pointer; padding: 0 4px; opacity: 0.75;\n `;\n close.addEventListener('click', () => {\n trackEvent(campaign.id, 'dismissed');\n remove(true);\n });\n bar.appendChild(close);\n }\n\n if (autoHide > 0) {\n autoHideTimer = setTimeout(() => remove(false), autoHide);\n }\n\n document.body.appendChild(bar);\n}\n","/**\n * ProgressBar renderer — a persistent bar that shows the user's progress\n * toward a reward threshold (free shipping, loyalty tier, referral bonus).\n *\n * Two sources of the current value:\n * 'client' — cheap: read from window.Shopify / WooCommerce / Magento\n * cart globals. Untrusted; fine for visual-only hints.\n * 'sse' — trusted: cell-plane pushes current_value via SSE to the\n * already-shipped realtime server. For cart-gated rewards\n * (must agree with server-side checkout guard), prefer SSE.\n *\n * For Phase 1 we implement CLIENT mode end-to-end. SSE mode reads\n * `window.__aegisProgressSSE[campaign.id]` if populated by a parent app\n * — higher-level integration (the SSE bridge) can push values there.\n *\n * interactive_config:\n * progress_goal_type: 'cart_total' | 'items_in_cart' | 'loyalty_points' | 'referrals'\n * progress_threshold: number\n * progress_reward_text?: string\n * progress_source?: 'client' | 'sse' default 'client'\n */\n\nimport { computeProgress, type ProgressGoalType } from './progress-core';\nimport type { RenderContext } from './types';\n\ntype GoalType = ProgressGoalType;\n\n/**\n * Build the progress-bar element + wire its live update loop. Shared by the\n * fixed overlay (`renderProgressBar`) and the inline slot variant\n * (`renderProgressBarInline`). Returns the element, or null when the campaign\n * is misconfigured (non-positive threshold). The caller positions/appends it:\n * - overlay → fixed bottom, appended to document.body\n * - inline → static, full-width, appended into a `[data-aegis-slot]` anchor\n * (the slot card supplies the chrome; the bar is just the mechanic)\n */\nfunction buildProgressBar(ctx: RenderContext, inline: boolean): HTMLElement | null {\n const { campaign, trackEvent, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const goalType = (ic.progress_goal_type as GoalType) || 'cart_total';\n const threshold = Number(ic.progress_threshold);\n if (!(threshold > 0)) {\n log('progress_bar requires a positive progress_threshold — skipping', 'warn');\n return null;\n }\n const rewardText =\n (ic.progress_reward_text as string) || (campaign.body as string) || 'Unlocked!';\n const source = ic.progress_source === 'sse' ? 'sse' : 'client';\n\n addAnimationStyles();\n\n // Surface-aware palette (matches the other widgets):\n // clean (default) — neutral WHITE card, dark text, BRAND-accent fill.\n // branded — BRAND-colour card, light text, WHITE fill.\n // The accent prefers the tenant brand colour the storefront publishes as the\n // `--aegis-brand-accent` CSS var (the SAME var the rec rail cards use), so the\n // bar matches the tenant primary instead of the default blue. Falls back to the\n // campaign's background_color, then blue (third-party embeds that don't set the\n // var keep their campaign colour — no regression).\n const brand = sanitizeColor((campaign.background_color as string) || '#4169e1');\n const accentVar = `var(--aegis-brand-accent, ${brand})`;\n const branded = (ic.surface_style as string) === 'branded';\n const bg = branded ? accentVar : '#ffffff';\n const fg = sanitizeColor((campaign.text_color as string) || (branded ? '#ffffff' : '#0f172a'));\n const fill = ic.progress_fill_color\n ? sanitizeColor(ic.progress_fill_color as string)\n : branded\n ? '#ffffff'\n : accentVar;\n // Track tint: a faint wash of the fill (branded uses a white wash on brand).\n // color-mix lets the wash derive from the CSS-var accent (a `${fill}22` hex\n // concat can't work once fill is a var()).\n const track = branded ? 'rgba(255,255,255,0.25)' : `color-mix(in srgb, ${fill} 14%, transparent)`;\n\n const bar = document.createElement('div');\n bar.className = 'aegis-in-app-progress-bar';\n bar.setAttribute('data-campaign-id', campaign.id);\n // Inline: no fixed positioning / shadow / slide-in — the host slot card owns\n // the chrome, so the bar fills the slot width and reads as part of the card.\n bar.style.cssText = inline\n ? `\n background: ${bg}; color: ${fg};\n padding: 10px 14px;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `\n : `\n position: fixed; left: 12px; right: 12px; bottom: 12px;\n max-width: 540px; margin: 0 auto;\n background: ${bg}; color: ${fg}; border-radius: 12px;\n padding: 10px 14px; z-index: 999997;\n box-shadow: 0 8px 20px rgba(0,0,0,0.08);\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideInFromBottom 0.3s ease-out;\n `;\n\n const label = document.createElement('div');\n label.style.cssText = 'display: flex; justify-content: space-between; font-size: 13px; font-weight: 600; margin-bottom: 6px;';\n\n const leading = document.createElement('span');\n leading.textContent = campaign.title;\n label.appendChild(leading);\n\n const numeric = document.createElement('span');\n numeric.style.cssText = 'opacity: 0.7; font-weight: 500;';\n label.appendChild(numeric);\n bar.appendChild(label);\n\n const shell = document.createElement('div');\n shell.style.cssText = `\n height: 8px; border-radius: 999px; background: ${track};\n overflow: hidden;\n `;\n const fillEl = document.createElement('div');\n fillEl.style.cssText = `\n position: relative; overflow: hidden;\n height: 100%; border-radius: 999px; background: ${fill};\n width: 0%; transition: width 0.4s cubic-bezier(.4,0,.2,1);\n `;\n // Animated shimmer sweep — makes the bar feel live as it fills.\n if (typeof document !== 'undefined' && !document.getElementById('aegis-progress-styles')) {\n const s = document.createElement('style');\n s.id = 'aegis-progress-styles';\n s.textContent = '@keyframes aegisProgShimmer{0%{transform:translateX(-100%)}60%,100%{transform:translateX(220%)}}';\n document.head.appendChild(s);\n }\n const shimmer = document.createElement('div');\n shimmer.style.cssText =\n 'position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.38), transparent); transform: translateX(-100%); animation: aegisProgShimmer 1.8s ease-in-out infinite;';\n fillEl.appendChild(shimmer);\n shell.appendChild(fillEl);\n bar.appendChild(shell);\n\n const footnote = document.createElement('div');\n footnote.style.cssText = 'font-size: 11.5px; opacity: 0.65; margin-top: 6px;';\n bar.appendChild(footnote);\n\n const readCurrentValue = (): number => {\n if (source === 'sse') {\n const hook = (window as unknown as {\n __aegisProgressSSE?: Record<string, number>;\n }).__aegisProgressSSE;\n return (hook && typeof hook[campaign.id] === 'number' ? hook[campaign.id] : 0) as number;\n }\n // First-party path — a headless storefront feeds live cart state into\n // PRIVATE SDK state via `runtime.setCartState(...)`; we read it here in\n // preference to the `window.*` globals below. This is how a Next.js\n // storefront (no `window.Shopify` / `window.aegis_cart`) drives the bar.\n if (goalType === 'cart_total' || goalType === 'items_in_cart') {\n const rc = ctx.getCartState?.();\n if (rc) {\n return goalType === 'items_in_cart' ? Number(rc.itemCount) || 0 : Number(rc.total) || 0;\n }\n }\n // client mode — read the platform cart globals the widget manager\n // already normalises (Shopify/Woo/Magento). We peek at the same\n // locations without pulling in AegisWidgetManager's 2k-line module.\n try {\n if (goalType === 'cart_total' || goalType === 'items_in_cart') {\n const win = window as unknown as {\n Shopify?: { checkout?: { total_price?: string | number } };\n aegis_cart?: { cart_total?: number; cart_items?: unknown[] };\n localStorage?: Storage;\n };\n if (win.Shopify?.checkout) {\n const v = parseFloat(String(win.Shopify.checkout.total_price || 0));\n return goalType === 'cart_total' ? v : 0;\n }\n if (win.aegis_cart) {\n if (goalType === 'items_in_cart') {\n return Array.isArray(win.aegis_cart.cart_items) ? win.aegis_cart.cart_items.length : 0;\n }\n return Number(win.aegis_cart.cart_total || 0);\n }\n // Magento cache fallback\n try {\n const cacheStr = window.localStorage?.getItem('mage-cache-storage');\n if (cacheStr) {\n const cache = JSON.parse(cacheStr) as { cart?: { subtotalAmount?: number; items?: unknown[] } };\n const cart = cache.cart;\n if (cart) {\n return goalType === 'cart_total'\n ? Number(cart.subtotalAmount || 0)\n : Array.isArray(cart.items)\n ? cart.items.length\n : 0;\n }\n }\n } catch {\n /* noop */\n }\n }\n // loyalty_points / referrals are expected to flow via SSE/push; in\n // client mode we cannot trust a local counter.\n return 0;\n } catch {\n return 0;\n }\n };\n\n // P3 — milestone-based step_id. Each 25%-quartile crossing fires its\n // own impression (`q25`, `q50`, `q75`) so the funnel-drop-off report\n // can show where users gave up before unlocking.\n let lastFiredUnlock = false;\n const milestonesFired = new Set<string>();\n const fireMilestone = (id: string) => {\n if (milestonesFired.has(id)) return;\n milestonesFired.add(id);\n trackEvent(campaign.id, 'impression', { stepId: id });\n };\n const update = () => {\n const cur = readCurrentValue();\n // Shared core — same pct / completion / remaining / milestone math the card\n // element and the dashboard preview use. This widget keeps its own footnote\n // copy (\"Add N more to unlock\") which differs from the card surface.\n const { pct, complete, remaining, milestonesReached } = computeProgress({\n goalType,\n threshold,\n current: cur,\n });\n fillEl.style.width = `${pct}%`;\n numeric.textContent = `${cur.toFixed(0)} / ${threshold.toFixed(0)} · ${Math.round(pct)}%`;\n // Hide the shimmer once complete (nothing left to fill).\n shimmer.style.display = complete ? 'none' : '';\n for (const m of milestonesReached) {\n if (m !== 'unlocked') fireMilestone(m);\n }\n if (complete) {\n footnote.textContent = rewardText;\n if (!lastFiredUnlock) {\n lastFiredUnlock = true;\n trackEvent(campaign.id, 'clicked', { stepId: 'unlocked' });\n }\n } else {\n footnote.textContent = `Add ${remaining.toFixed(0)} more to unlock: ${rewardText}`;\n }\n };\n\n update();\n\n // Client-mode cart state changes fire events on the storefront globals\n // (Shopify emits `cart:updated`, Woo emits its own). Poll fallback at\n // 1s — negligible overhead because every tick is synchronous.\n const pollTimer = setInterval(update, 1000);\n const cleanup = () => clearInterval(pollTimer);\n window.addEventListener('beforeunload', cleanup, { once: true });\n bar.addEventListener('remove', cleanup);\n\n return bar;\n}\n\n/**\n * Overlay progress bar — a fixed bar pinned to the bottom of the viewport.\n * Used by third-party storefront embeds (Shopify/Woo/Magento) that have no\n * native footer to host the mechanic.\n */\nexport function renderProgressBar(ctx: RenderContext): void {\n const bar = buildProgressBar(ctx, false);\n if (bar) document.body.appendChild(bar);\n}\n\n/**\n * Inline progress bar — renders the SAME live mechanic into a\n * `[data-aegis-slot]` anchor instead of a fixed overlay. This is how a\n * first-party storefront's value-bar footer is driven by a campaign: the host\n * owns the structural cart-summary card + a slot, and an operator-authored\n * `progress_bar` campaign fills the slot (replacing any host-rendered default).\n * Returns true when rendered so the slot dispatcher can track the impression.\n */\nexport function renderProgressBarInline(ctx: RenderContext, target: HTMLElement): boolean {\n const bar = buildProgressBar(ctx, true);\n if (!bar) return false;\n target.appendChild(bar);\n return true;\n}\n","/**\n * CoachmarkTour renderer — a guided, multi-step walkthrough anchored to\n * DOM elements via CSS selectors. One tooltip appears at a time with\n * next/back navigation; completion + skip are persisted per contact via\n * localStorage under the `resume_key`, so an abandoned tour resumes on\n * the next visit instead of restarting or re-bothering the user.\n *\n * interactive_config:\n * steps: Array<{\n * anchor_web?: string, // CSS selector for THIS platform\n * anchor_android?: string, // ignored on web\n * anchor_ios?: string, // ignored on web\n * title: string,\n * body: string,\n * placement?: 'top' | 'bottom' | 'left' | 'right',\n * cta_text?: string,\n * }>\n * resume_key: string,\n * allow_skip?: boolean,\n * show_progress_dots?: boolean,\n */\n\nimport type { RenderContext } from './types';\n\ntype Step = {\n anchor_web?: string;\n anchor_android?: string;\n anchor_ios?: string;\n title: string;\n body: string;\n placement?: 'top' | 'bottom' | 'left' | 'right';\n cta_text?: string;\n};\n\nconst RESUME_PREFIX = 'aegis_coachmark_progress:';\n\nfunction readResumeIdx(resumeKey: string): number {\n try {\n if (typeof localStorage === 'undefined') return 0;\n const raw = localStorage.getItem(RESUME_PREFIX + resumeKey);\n const n = raw ? parseInt(raw, 10) : 0;\n return Number.isFinite(n) && n >= 0 ? n : 0;\n } catch {\n return 0;\n }\n}\n\nfunction writeResumeIdx(resumeKey: string, idx: number): void {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(RESUME_PREFIX + resumeKey, String(idx));\n }\n } catch {\n /* noop */\n }\n}\n\nfunction clearResume(resumeKey: string): void {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem(RESUME_PREFIX + resumeKey);\n }\n } catch {\n /* noop */\n }\n}\n\nexport function renderCoachmarkTour(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const resumeKey = ic.resume_key as string | undefined;\n if (!resumeKey) {\n log('coachmark_tour requires interactive_config.resume_key — skipping', 'warn');\n return;\n }\n\n const steps = Array.isArray(ic.steps) ? (ic.steps as Step[]) : [];\n if (steps.length === 0) {\n log('coachmark_tour has no steps — skipping', 'warn');\n return;\n }\n\n const allowSkip = ic.allow_skip !== false;\n const showDots = ic.show_progress_dots !== false;\n\n addAnimationStyles();\n\n const bg = sanitizeColor((campaign.background_color as string) || '#0f172a');\n const fg = sanitizeColor((campaign.text_color as string) || '#ffffff');\n // Masked spotlight (vs the legacy ring). Spotlight punches a stronger,\n // configurable transparent hole around each step's anchor.\n const spotlight = (ic.highlight_style as string) === 'spotlight';\n const spotPad = typeof ic.spotlight_padding === 'number' ? (ic.spotlight_padding as number) : 6;\n const spotRadius = (ic.spotlight_shape as string) === 'circle' ? '50%' : '10px';\n const spotDim = typeof ic.spotlight_dim === 'number'\n ? Math.max(0, Math.min(95, ic.spotlight_dim as number)) / 100\n : (spotlight ? 0.64 : 0.4);\n // Backdrop colour: dark slate (default) · brand accent (bg) · black · custom.\n const dimMode = (ic.spotlight_dim_color as string) || 'dark';\n const dimBase = dimMode === 'black' ? '#000000'\n : dimMode === 'brand' ? bg\n : dimMode === 'custom' ? sanitizeColor((ic.spotlight_dim_custom as string) || '#0f172a')\n : '#0f172a';\n const dimAlpha = Math.round(Math.max(0, Math.min(1, spotDim)) * 255).toString(16).padStart(2, '0');\n const dimColor = /^#[0-9a-fA-F]{6}$/.test(dimBase) ? `${dimBase}${dimAlpha}` : `rgba(15,23,42,${spotDim})`;\n\n // Resume or start at 0.\n let current = readResumeIdx(resumeKey);\n if (current >= steps.length) {\n log(`coachmark_tour ${resumeKey} already complete — not re-showing`);\n return;\n }\n\n // Track the single overall impression once per mount.\n trackEvent(campaign.id, 'impression');\n\n let pointerEl: HTMLElement | null = null;\n let tipEl: HTMLElement | null = null;\n let highlightEl: HTMLElement | null = null;\n\n const cleanupOne = () => {\n pointerEl?.remove();\n tipEl?.remove();\n highlightEl?.remove();\n pointerEl = null;\n tipEl = null;\n highlightEl = null;\n };\n\n const finish = (opts: { skipped: boolean; lastStepIdx: number }) => {\n cleanupOne();\n const stepId = `step_${opts.lastStepIdx}`;\n if (opts.skipped) {\n trackEvent(campaign.id, 'dismissed', { stepId });\n } else {\n trackEvent(campaign.id, 'clicked', { stepId }); // completion\n // Finished the whole tour — timeline + funnel signal (no reward).\n void ctx.submitResponse?.('tour_complete', { steps: steps.length });\n }\n // Mark tour as complete so we never restart it for this contact.\n writeResumeIdx(resumeKey, steps.length);\n };\n\n const showStep = (idx: number) => {\n cleanupOne();\n const step = steps[idx];\n if (!step) {\n finish({ skipped: false, lastStepIdx: idx - 1 });\n return;\n }\n writeResumeIdx(resumeKey, idx);\n\n const selector = step.anchor_web;\n let anchor: Element | null = null;\n if (selector) {\n try {\n anchor = document.querySelector(selector);\n } catch {\n anchor = null;\n }\n }\n // Silent-anchor footgun: this step declared an anchor but it isn't on the\n // page, so the tip renders centered (below) instead of pointing at the\n // target — and the operator's impression metrics still look healthy. Stamp\n // `anchor_resolved: false` on the per-step impression and emit a dedicated\n // `anchor_unresolved` event so the misconfiguration is visible. On native\n // apps the same `anchor_*` key must be registered via registerInAppAnchor()\n // and adding a new one needs an app release.\n const anchorUnresolved = !!selector && !anchor;\n if (anchorUnresolved) {\n console.warn(\n `[AegisInApp] Coachmark step ${idx} anchor \"${selector}\" not found on this surface — rendering centered fallback.`,\n );\n trackEvent(campaign.id, 'anchor_unresolved', {\n stepId: `step_${idx}`,\n meta: { anchor_resolved: false, anchor_selector: selector, widget_type: 'coachmark_tour' },\n });\n }\n // P3 — per-step impression so the funnel-drop-off report can\n // distinguish \"made it to step N\" from \"made it to step N+1\".\n // Idx 0 still fires alongside the tour-level impression on mount\n // (line 100); downstream dedup on (campaign_id, step_id, contact_id)\n // collapses repeat fires within a step's display.\n trackEvent(campaign.id, 'impression', {\n stepId: `step_${idx}`,\n ...(anchorUnresolved ? { meta: { anchor_resolved: false } } : {}),\n });\n // A step whose anchor can't be resolved (the target isn't on this page\n // yet, or we're rendering in an isolated preview frame) still shows its\n // guidance — centered, over a dim scrim — instead of being silently\n // skipped. The tour stays interactive everywhere.\n const anchorRect = anchor ? anchor.getBoundingClientRect() : null;\n highlightEl = document.createElement('div');\n highlightEl.className = 'aegis-in-app-coachmark-highlight';\n if (anchorRect) {\n // Masked spotlight cutout around the anchor so the user's eye snaps to it.\n // spotlight = bright ring + accent glow + a strong configurable dim; ring\n // = the legacy thin ring + lighter dim.\n const shadow = spotlight\n ? `0 0 0 3px rgba(255,255,255,0.92), 0 0 0 7px ${bg}66, 0 0 0 9999px ${dimColor}`\n : `0 0 0 2px ${fg}, 0 0 0 9999px rgba(0,0,0,${spotDim})`;\n highlightEl.style.cssText = `\n position: fixed;\n top: ${anchorRect.top - spotPad}px; left: ${anchorRect.left - spotPad}px;\n width: ${anchorRect.width + spotPad * 2}px; height: ${anchorRect.height + spotPad * 2}px;\n border-radius: ${spotlight ? spotRadius : '10px'}; z-index: 999998;\n box-shadow: ${shadow};\n pointer-events: none;\n transition: all 0.2s ease;\n `;\n } else {\n // No anchor → a plain dim scrim behind the centered tip.\n highlightEl.style.cssText = `\n position: fixed; inset: 0; z-index: 999998;\n background: ${dimColor}; pointer-events: none;\n `;\n }\n document.body.appendChild(highlightEl);\n\n const placement = step.placement || 'bottom';\n tipEl = document.createElement('div');\n tipEl.className = 'aegis-in-app-coachmark-tip';\n tipEl.setAttribute('data-campaign-id', campaign.id);\n tipEl.style.cssText = `\n position: fixed; z-index: 999999;\n background: ${bg}; color: ${fg};\n padding: 12px 14px; border-radius: 12px;\n max-width: 260px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.25);\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisFadeIn 0.2s ease;\n `;\n\n const titleEl = document.createElement('div');\n titleEl.textContent = step.title;\n titleEl.style.cssText = 'font-weight: 700; font-size: 13px; margin-bottom: 4px;';\n tipEl.appendChild(titleEl);\n\n const bodyEl = document.createElement('div');\n bodyEl.textContent = step.body;\n bodyEl.style.cssText = 'font-size: 12.5px; line-height: 1.4; opacity: 0.9;';\n tipEl.appendChild(bodyEl);\n\n const controls = document.createElement('div');\n controls.style.cssText = 'display: flex; align-items: center; justify-content: space-between; margin-top: 10px; gap: 8px;';\n\n // Dots\n if (showDots) {\n const dots = document.createElement('div');\n dots.style.cssText = 'display: flex; gap: 4px;';\n steps.forEach((_, i) => {\n const d = document.createElement('span');\n d.style.cssText = `\n width: 5px; height: 5px; border-radius: 999px;\n background: ${fg}; opacity: ${i === idx ? '0.95' : '0.3'};\n `;\n dots.appendChild(d);\n });\n controls.appendChild(dots);\n } else {\n controls.appendChild(document.createElement('span'));\n }\n\n const buttons = document.createElement('div');\n buttons.style.cssText = 'display: flex; gap: 6px;';\n\n // Back — step navigation was forward-only (Next/Skip); a multi-step tour\n // needs to let the user return to a prior step.\n if (idx > 0) {\n const back = document.createElement('button');\n back.textContent = 'Back';\n back.style.cssText = `\n background: transparent; color: inherit; opacity: 0.7;\n border: none; font-size: 12px; cursor: pointer; padding: 6px 8px;\n `;\n back.addEventListener('click', () => showStep(idx - 1));\n buttons.appendChild(back);\n }\n\n if (allowSkip && idx < steps.length - 1) {\n const skip = document.createElement('button');\n skip.textContent = 'Skip';\n skip.style.cssText = `\n background: transparent; color: inherit; opacity: 0.7;\n border: none; font-size: 12px; cursor: pointer; padding: 6px 8px;\n `;\n skip.addEventListener('click', () => finish({ skipped: true, lastStepIdx: idx }));\n buttons.appendChild(skip);\n }\n\n const next = document.createElement('button');\n const isLast = idx === steps.length - 1;\n next.textContent = step.cta_text || (isLast ? 'Done' : 'Next');\n next.style.cssText = `\n background: ${fg}; color: ${bg};\n border: none; padding: 6px 12px; border-radius: 999px;\n font-size: 12px; font-weight: 700; cursor: pointer;\n `;\n next.addEventListener('click', () => {\n if (isLast) {\n finish({ skipped: false, lastStepIdx: idx });\n } else {\n showStep(idx + 1);\n }\n });\n buttons.appendChild(next);\n\n controls.appendChild(buttons);\n tipEl.appendChild(controls);\n document.body.appendChild(tipEl);\n\n // Position after insertion so we know the tip's own dimensions.\n const tipRect = tipEl.getBoundingClientRect();\n const margin = 12;\n let top: number;\n let left: number;\n if (anchorRect) {\n top = anchorRect.bottom + margin;\n left = anchorRect.left;\n if (placement === 'top') {\n top = anchorRect.top - tipRect.height - margin;\n } else if (placement === 'left') {\n top = anchorRect.top;\n left = anchorRect.left - tipRect.width - margin;\n } else if (placement === 'right') {\n top = anchorRect.top;\n left = anchorRect.right + margin;\n }\n } else {\n // Centered fallback (no anchor).\n top = (window.innerHeight - tipRect.height) / 2;\n left = (window.innerWidth - tipRect.width) / 2;\n }\n // Clamp to viewport.\n top = Math.max(8, Math.min(window.innerHeight - tipRect.height - 8, top));\n left = Math.max(8, Math.min(window.innerWidth - tipRect.width - 8, left));\n tipEl.style.top = `${top}px`;\n tipEl.style.left = `${left}px`;\n };\n\n showStep(current);\n\n // If the host page unloads mid-tour, leave the current index persisted so\n // the next session resumes from there. Nothing else to do — writeResumeIdx\n // was called in showStep.\n // Expose a cancel hook so app code can reset the tour from Dev Tools etc.\n (window as unknown as { aegisResetTour?: (key: string) => void }).aegisResetTour = (key: string) => clearResume(key);\n}\n","/**\n * Stories renderer — Instagram/Plotline-style stories, in two surfaces:\n *\n * 1. LAUNCHER BAR (the collapsed entry) — a horizontal row of story-GROUP\n * thumbnails. Each group is one bubble (circular gradient ring OR\n * rectangular cover card) with a title + cover; tapping it opens the\n * player and plays that group's FRAMES in sequence. Renders inline in a\n * `[data-aegis-slot]` anchor (the embedded widget, Plotline-parity).\n * 2. PLAYER — full-screen tap-through. tap RIGHT → next frame · tap LEFT →\n * previous · press-and-hold → pause · segmented progress fills per frame ·\n * ✕ / Esc closes · ←/→ navigate.\n *\n * Data shape (interactive_config):\n * stories: Array<{ id?, title?, cover_image_url?, frames: Frame[] }> // GROUPS\n * Frame: { image_url?, video_url?, title?, body?, cta_text?, cta_url?, cta_target?, duration_ms? }\n * stories_thumbnail_shape?: 'circle' | 'rectangle'\n * stories_ring_style?: 'brand' | 'instagram' | 'custom' // default brand\n * stories_ring_color?: string // for 'custom'\n * stories_autoplay?: boolean · stories_loop?: boolean · stories_show_progress?: boolean\n * stories_default_duration_ms?: number\n * surface_style?: 'clean' | 'branded' // branded → brand-fill launcher bar\n *\n * A flat `stories: Frame[]` (pre-groups shape) is tolerated — treated as one group.\n */\n\nimport { lucideSvg } from '../lucide-svg';\nimport type { RenderContext } from './types';\n\ntype Frame = {\n image_url?: string;\n video_url?: string;\n title?: string;\n body?: string;\n cta_text?: string;\n cta_url?: string;\n duration_ms?: number;\n};\n\ntype Group = {\n id?: string;\n title?: string;\n cover_image_url?: string;\n frames: Frame[];\n};\n\ntype StoriesConfig = {\n groups: Group[];\n thumbnailShape: 'circle' | 'rectangle';\n ringBg: string;\n ringBgLinear: string;\n branded: boolean;\n brand: string;\n autoplay: boolean;\n loop: boolean;\n showProgress: boolean;\n defaultDuration: number;\n reduceMotion: boolean;\n};\n\nconst MAX_GROUPS = 12;\nconst MAX_FRAMES = 20;\nconst DEFAULT_DURATION_MS = 5000;\nconst TAP_MAX_MS = 220;\n\n// Per-frame fallback gradients so a frame with no image still looks designed.\nconst FRAME_GRADIENTS = [\n 'linear-gradient(160deg, #6366f1, #a855f7)',\n 'linear-gradient(160deg, #ec4899, #f43f5e)',\n 'linear-gradient(160deg, #0ea5e9, #22d3ee)',\n 'linear-gradient(160deg, #f59e0b, #ef4444)',\n 'linear-gradient(160deg, #10b981, #14b8a6)',\n 'linear-gradient(160deg, #8b5cf6, #6366f1)',\n];\nconst frameGradient = (i: number): string => FRAME_GRADIENTS[i % FRAME_GRADIENTS.length];\n\n// The Instagram story ring (gradient sweep).\nconst RING_GRADIENT_IG =\n 'conic-gradient(from 0deg, #feda75, #fa7e1e, #d62976, #962fbf, #4f5bd5, #feda75)';\n// Brand / custom ring — a light+dark sweep of the chosen colour so the rotation\n// animation reads (a flat colour wouldn't appear to spin). color-mix is broadly\n// supported; if unsupported the browser falls back to the base colour stop.\nconst sheenRing = (c: string): string =>\n `conic-gradient(from 0deg, ${c}, color-mix(in srgb, ${c} 50%, #ffffff), ${c}, color-mix(in srgb, ${c} 72%, #000000), ${c})`;\n\n// LINEAR variants for RECTANGLE cards. A conic gradient is for the rotating\n// CIRCLE ring; rectangles must NOT rotate (a spinning square reads as broken),\n// so they use a linear gradient that shimmers via `background-position` instead.\nconst RING_GRADIENT_IG_LINEAR =\n 'linear-gradient(110deg, #feda75, #fa7e1e, #d62976, #962fbf, #4f5bd5, #feda75)';\nconst sheenRingLinear = (c: string): string =>\n `linear-gradient(110deg, ${c}, color-mix(in srgb, ${c} 50%, #ffffff), ${c}, color-mix(in srgb, ${c} 72%, #000000), ${c})`;\n\n// Normalise to GROUPS — accept the groups shape, or a flat frame array (legacy)\n// treated as a single group.\nfunction normalizeGroups(raw: unknown): Group[] {\n if (!Array.isArray(raw) || raw.length === 0) return [];\n const first = raw[0] as Record<string, unknown> | undefined;\n if (first && Array.isArray((first as { frames?: unknown }).frames)) {\n return (raw as Group[])\n .slice(0, MAX_GROUPS)\n .map((g) => ({ ...g, frames: (g.frames || []).slice(0, MAX_FRAMES) }))\n .filter((g) => g.frames.length > 0);\n }\n return [{ frames: (raw as Frame[]).slice(0, MAX_FRAMES) }];\n}\n\nfunction readConfig(ctx: RenderContext): StoriesConfig | null {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const groups = normalizeGroups(ic.stories);\n if (groups.length === 0) {\n ctx.log('stories rendered with zero groups — skipping', 'warn');\n return null;\n }\n const brand = ctx.sanitizeColor((ctx.campaign.background_color as string) || '#4169e1');\n const ringStyle = (ic.stories_ring_style as string) || 'brand';\n let ringBg: string;\n let ringBgLinear: string;\n if (ringStyle === 'instagram') {\n ringBg = RING_GRADIENT_IG;\n ringBgLinear = RING_GRADIENT_IG_LINEAR;\n } else if (ringStyle === 'custom') {\n const c = ctx.sanitizeColor((ic.stories_ring_color as string) || brand);\n ringBg = sheenRing(c);\n ringBgLinear = sheenRingLinear(c);\n } else {\n ringBg = sheenRing(brand);\n ringBgLinear = sheenRingLinear(brand);\n }\n\n return {\n groups,\n thumbnailShape: ic.stories_thumbnail_shape === 'rectangle' ? 'rectangle' : 'circle',\n ringBg,\n ringBgLinear,\n branded: (ic.surface_style as string) === 'branded',\n brand,\n autoplay: ic.stories_autoplay !== false,\n loop: ic.stories_loop === true,\n showProgress: ic.stories_show_progress !== false,\n defaultDuration: Number(ic.stories_default_duration_ms) || DEFAULT_DURATION_MS,\n reduceMotion:\n typeof window !== 'undefined' &&\n typeof window.matchMedia === 'function' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches,\n };\n}\n\n/** Overlay entry — a stories campaign delivered as an OVERLAY is a full-screen\n * takeover; open at the first group and auto-advance through every group. */\nexport function renderStories(ctx: RenderContext): void {\n const cfg = readConfig(ctx);\n if (!cfg) return;\n ctx.addAnimationStyles();\n injectStoryStyles();\n openViewer(ctx, cfg, 0, 0);\n}\n\n/** Slot/embedded entry — the inline launcher bar of group thumbnails. */\nexport function renderStoriesRings(ctx: RenderContext, target: HTMLElement | null): boolean {\n const cfg = readConfig(ctx);\n if (!cfg) return false;\n ctx.addAnimationStyles();\n injectStoryStyles();\n renderLauncherBar(ctx, cfg, target);\n return true;\n}\n\n// ── Launcher bar (collapsed entry) ─────────────────────────────────────────\nfunction renderLauncherBar(ctx: RenderContext, cfg: StoriesConfig, target: HTMLElement | null): void {\n const { campaign } = ctx;\n const floating = !target;\n const labelColor = cfg.branded ? '#ffffff' : '#334155';\n\n const bar = document.createElement('div');\n bar.className = 'aegis-in-app-stories-tray';\n bar.setAttribute('data-campaign-id', campaign.id);\n // Generous left padding so the first bubble never kisses the edge; scroll\n // padding keeps it clear when scrolled too.\n const surfaceBg = cfg.branded\n ? cfg.brand\n : floating\n ? 'rgba(255,255,255,0.96)'\n : 'transparent';\n bar.style.cssText = floating\n ? `\n position: fixed; left: 0; right: 0; bottom: 0; z-index: 99998;\n display: flex; gap: 16px; align-items: flex-start; overflow-x: auto;\n padding: 14px 18px 16px 20px; scroll-padding-left: 20px;\n background: ${surfaceBg}; backdrop-filter: blur(8px);\n box-shadow: 0 -4px 16px rgba(0,0,0,0.08); scrollbar-width: none;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideInFromBottom 0.3s ease-out;\n `\n : `\n display: flex; gap: 16px; align-items: flex-start; overflow-x: auto;\n padding: 12px 18px 12px 18px; scroll-padding-left: 18px;\n background: ${surfaceBg}; ${cfg.branded ? 'border-radius: 16px;' : ''}\n scrollbar-width: none;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n\n cfg.groups.forEach((g, i) => bar.appendChild(buildGroupBubble(ctx, cfg, g, i, labelColor)));\n\n if (floating) {\n const dismiss = document.createElement('button');\n dismiss.type = 'button';\n dismiss.innerHTML = lucideSvg('x', { size: 14 });\n dismiss.setAttribute('aria-label', 'Dismiss stories');\n dismiss.style.cssText = `\n flex: 0 0 auto; align-self: center; margin-left: auto;\n width: 28px; height: 28px; border: none; border-radius: 999px;\n background: ${cfg.branded ? 'rgba(255,255,255,0.18)' : 'rgba(0,0,0,0.06)'};\n color: ${cfg.branded ? '#fff' : '#475569'}; font-size: 14px; cursor: pointer;\n `;\n dismiss.addEventListener('click', () => {\n ctx.trackEvent(campaign.id, 'dismissed');\n bar.remove();\n });\n bar.appendChild(dismiss);\n }\n\n if (target) target.appendChild(bar);\n else document.body.appendChild(bar);\n}\n\nfunction groupCover(g: Group): { img: string | null; index: number } {\n if (g.cover_image_url) return { img: g.cover_image_url, index: 0 };\n const firstWithImg = g.frames.findIndex((f) => f.image_url);\n if (firstWithImg >= 0) return { img: g.frames[firstWithImg].image_url || null, index: firstWithImg };\n return { img: null, index: 0 };\n}\n\nfunction buildGroupBubble(\n ctx: RenderContext,\n cfg: StoriesConfig,\n g: Group,\n i: number,\n labelColor: string,\n): HTMLElement {\n const rectangle = cfg.thumbnailShape === 'rectangle';\n const title = g.title || g.frames[0]?.title || `Story ${i + 1}`;\n const cover = groupCover(g);\n const safeImg = cover.img ? ctx.sanitizeUrl(cover.img) : null;\n const grad = frameGradient(i);\n\n const wrap = document.createElement('button');\n wrap.type = 'button';\n wrap.setAttribute('aria-label', `Open ${title} (${g.frames.length} frames)`);\n wrap.style.cssText =\n 'flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 5px; border: none; background: transparent; cursor: pointer; padding: 0;';\n\n if (rectangle) {\n const cardWrap = document.createElement('div');\n // Rectangle cards must NOT rotate (a spinning square reads as broken). Use a\n // LINEAR gradient border that shimmers via background-position — a gentle\n // left↔right sheen, never a rotation. Static gradient when reduced-motion.\n cardWrap.style.cssText = `\n width: 92px; height: 124px; border-radius: 14px; padding: 2.5px;\n background: ${cfg.ringBgLinear}; background-size: 220% 100%; background-position: 0% 50%;\n ${cfg.reduceMotion ? '' : 'animation: aegisStoryShimmer 3s ease-in-out infinite;'}\n `;\n const card = document.createElement('div');\n card.style.cssText = `\n position: relative; width: 100%; height: 100%; border-radius: 12px; overflow: hidden;\n background: ${grad}; display: flex; align-items: flex-end;\n `;\n if (safeImg) {\n const img = document.createElement('img');\n img.src = safeImg; img.alt = '';\n img.style.cssText = 'position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;';\n card.appendChild(img);\n }\n const scrim = document.createElement('div');\n scrim.style.cssText = 'position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,0.6), rgba(0,0,0,0) 55%);';\n card.appendChild(scrim);\n const t = document.createElement('div');\n t.textContent = title;\n t.style.cssText = 'position: relative; z-index: 1; padding: 7px 8px; color: #fff; font-size: 11px; font-weight: 700; line-height: 1.2; text-shadow: 0 1px 4px rgba(0,0,0,0.5);';\n card.appendChild(t);\n cardWrap.appendChild(card);\n wrap.appendChild(cardWrap);\n wrap.addEventListener('click', () => openViewer(ctx, cfg, i, 0));\n return wrap;\n }\n\n const ringBox = document.createElement('div');\n ringBox.style.cssText = 'position: relative; width: 66px; height: 66px;';\n const ring = document.createElement('div');\n ring.style.cssText = `\n position: absolute; inset: 0; border-radius: 50%; background: ${cfg.ringBg};\n ${cfg.reduceMotion ? '' : 'animation: aegisStoryRing 4s linear infinite;'}\n `;\n ringBox.appendChild(ring);\n const gap = document.createElement('div');\n gap.style.cssText = `position: absolute; inset: 3px; border-radius: 50%; background: ${cfg.branded ? cfg.brand : '#fff'};`;\n ringBox.appendChild(gap);\n const thumb = document.createElement('div');\n thumb.style.cssText = `position: absolute; inset: 5px; border-radius: 50%; overflow: hidden; background: ${grad}; display: flex; align-items: center; justify-content: center;`;\n if (safeImg) {\n const img = document.createElement('img');\n img.src = safeImg; img.alt = '';\n img.style.cssText = 'width: 100%; height: 100%; object-fit: cover;';\n thumb.appendChild(img);\n } else {\n const initial = document.createElement('span');\n initial.textContent = title.trim().charAt(0).toUpperCase();\n initial.style.cssText = 'color: #fff; font-weight: 800; font-size: 22px; text-shadow: 0 1px 4px rgba(0,0,0,0.25);';\n thumb.appendChild(initial);\n }\n ringBox.appendChild(thumb);\n wrap.appendChild(ringBox);\n\n const label = document.createElement('span');\n label.textContent = title;\n label.style.cssText = `max-width: 70px; font-size: 11px; color: ${labelColor}; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;`;\n wrap.appendChild(label);\n\n wrap.addEventListener('click', () => openViewer(ctx, cfg, i, 0));\n return wrap;\n}\n\n// ── Player (full-screen) ───────────────────────────────────────────────────\n// Group-aware: plays the starting group's frames, then auto-advances to the\n// NEXT group (Instagram-style continuous playback) until the last group ends.\nfunction openViewer(ctx: RenderContext, cfg: StoriesConfig, startGroupIndex: number, startFrameIndex = 0): void {\n const { campaign, trackEvent, sanitizeUrl, log } = ctx;\n const { loop, autoplay, showProgress, defaultDuration, reduceMotion } = cfg;\n const groups = cfg.groups;\n if (groups.length === 0) return;\n let gIdx = Math.max(0, Math.min(startGroupIndex, groups.length - 1));\n let frames = groups[gIdx].frames;\n log(`stories viewer open @ group ${gIdx}`);\n\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-stories-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n overlay.setAttribute('role', 'dialog');\n overlay.setAttribute('aria-label', campaign.title || 'Stories');\n overlay.style.cssText = `\n position: fixed; inset: 0; z-index: 100000; background: #000;\n display: flex; align-items: center; justify-content: center;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisFadeIn 0.25s ease;\n `;\n\n const stage = document.createElement('div');\n stage.style.cssText = 'position: relative; width: 100%; height: 100%; max-width: 440px; max-height: 100vh; background: #111; overflow: hidden;';\n overlay.appendChild(stage);\n\n const frameLayer = document.createElement('div');\n frameLayer.style.cssText = 'position: absolute; inset: 0;';\n stage.appendChild(frameLayer);\n\n // Progress row is REBUILT per group (each group has its own frame count).\n const progressRow = document.createElement('div');\n progressRow.style.cssText = 'position: absolute; top: 0; left: 0; right: 0; z-index: 4; display: flex; gap: 4px; padding: 10px 10px 0;';\n stage.appendChild(progressRow);\n let bars: HTMLElement[] = [];\n const buildBars = (): void => {\n progressRow.replaceChildren();\n bars = [];\n if (!showProgress) return;\n frames.forEach(() => {\n const track = document.createElement('div');\n track.style.cssText = 'flex: 1; height: 2.5px; border-radius: 999px; background: rgba(255,255,255,0.32); overflow: hidden;';\n const fill = document.createElement('div');\n fill.style.cssText = 'width: 0%; height: 100%; background: #fff; border-radius: 999px;';\n track.appendChild(fill);\n progressRow.appendChild(track);\n bars.push(fill);\n });\n };\n\n const close = document.createElement('button');\n close.type = 'button';\n close.innerHTML = lucideSvg('x', { size: 18 });\n close.setAttribute('aria-label', 'Close');\n close.style.cssText = 'position: absolute; top: 12px; right: 10px; z-index: 5; width: 34px; height: 34px; border: none; border-radius: 999px; background: rgba(0,0,0,0.32); color: #fff; font-size: 18px; cursor: pointer; display: flex; align-items: center; justify-content: center; line-height: 1;';\n stage.appendChild(close);\n\n let idx = startFrameIndex;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let rafId: number | null = null;\n let frameStart = 0;\n let elapsedBeforePause = 0;\n let paused = false;\n let currentVideo: HTMLVideoElement | null = null;\n const seen = new Set<string>();\n\n const clearTimers = () => {\n if (timer) { clearTimeout(timer); timer = null; }\n if (rafId !== null) { cancelAnimationFrame(rafId); rafId = null; }\n };\n let completionFired = false;\n const teardown = (reason: 'dismissed' | 'completed') => {\n clearTimers();\n document.removeEventListener('keydown', onKey);\n if (reason === 'dismissed') {\n trackEvent(campaign.id, 'dismissed');\n } else if (!completionFired) {\n // Watched to the end — engagement-funnel + timeline signal (no reward).\n completionFired = true;\n void ctx.submitResponse?.('story_complete', { groups: cfg.groups.length });\n }\n overlay.remove();\n };\n const frameDuration = (i: number): number => {\n const d = Number(frames[i]?.duration_ms);\n return d >= 1000 ? d : defaultDuration;\n };\n const tickProgress = () => {\n if (!showProgress || paused) return;\n const dur = frameDuration(idx);\n const elapsed = elapsedBeforePause + (Date.now() - frameStart);\n const pct = Math.max(0, Math.min(100, (elapsed / dur) * 100));\n if (bars[idx]) bars[idx].style.width = `${pct}%`;\n if (pct < 100) rafId = requestAnimationFrame(tickProgress);\n };\n const scheduleAdvance = () => {\n if (!autoplay) return;\n const remaining = frameDuration(idx) - elapsedBeforePause;\n frameStart = Date.now();\n timer = setTimeout(() => advance(1), Math.max(0, remaining));\n if (showProgress && !reduceMotion) rafId = requestAnimationFrame(tickProgress);\n else if (showProgress && bars[idx]) bars[idx].style.width = '0%';\n };\n const paintBarsUpTo = (active: number) => {\n if (!showProgress) return;\n bars.forEach((b, i) => { b.style.width = i < active ? '100%' : '0%'; });\n };\n\n const renderFrame = (i: number): void => {\n const f = frames[i];\n frameLayer.replaceChildren();\n currentVideo = null;\n const safeVideo = f.video_url ? sanitizeUrl(f.video_url) : null;\n const safeImage = f.image_url ? sanitizeUrl(f.image_url) : null;\n if (safeVideo) {\n const vid = document.createElement('video');\n vid.src = safeVideo;\n vid.muted = true; vid.loop = true; vid.autoplay = true; vid.playsInline = true;\n vid.setAttribute('playsinline', '');\n vid.style.cssText = 'position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;';\n frameLayer.appendChild(vid);\n currentVideo = vid;\n void vid.play?.().catch(() => {});\n } else if (safeImage) {\n const img = document.createElement('img');\n img.src = safeImage; img.alt = f.title || '';\n img.style.cssText = 'position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;';\n frameLayer.appendChild(img);\n } else {\n const bgFill = document.createElement('div');\n bgFill.style.cssText = `position: absolute; inset: 0; background: ${cfg.branded ? cfg.brand : frameGradient(i)};`;\n frameLayer.appendChild(bgFill);\n }\n\n const scrim = document.createElement('div');\n scrim.style.cssText = 'position: absolute; inset: 0; pointer-events: none; background: linear-gradient(to top, rgba(0,0,0,0.72) 0%, rgba(0,0,0,0.18) 38%, rgba(0,0,0,0) 60%);';\n frameLayer.appendChild(scrim);\n\n const copy = document.createElement('div');\n copy.style.cssText = 'position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; padding: 20px 18px 30px; color: #fff; text-align: left;';\n if (f.title) {\n const t = document.createElement('div');\n t.textContent = f.title;\n t.style.cssText = \"font-family: 'Urbanist', system-ui, sans-serif; font-size: 21px; font-weight: 800; letter-spacing: -0.01em; line-height: 1.2; margin-bottom: 6px; text-shadow: 0 1px 8px rgba(0,0,0,0.4);\";\n copy.appendChild(t);\n }\n if (f.body) {\n const b = document.createElement('div');\n b.textContent = f.body;\n b.style.cssText = 'font-size: 14px; line-height: 1.45; opacity: 0.92; text-shadow: 0 1px 6px rgba(0,0,0,0.4);';\n copy.appendChild(b);\n }\n const ctaUrl = f.cta_url ? sanitizeUrl(f.cta_url) : null;\n const ctaLabel = f.cta_text || (ctaUrl ? 'Learn more' : '');\n if (ctaLabel) {\n const cta = document.createElement('button');\n cta.type = 'button';\n cta.className = 'aegis-cta';\n cta.textContent = ctaLabel;\n cta.style.cssText = 'margin-top: 14px; padding: 11px 22px; border: none; border-radius: 999px; background: #fff; color: #0f172a; font-size: 14px; font-weight: 700; cursor: pointer;';\n cta.addEventListener('pointerdown', (e) => e.stopPropagation());\n cta.addEventListener('click', (e) => {\n e.stopPropagation();\n trackEvent(campaign.id, 'clicked', { stepId: `g${gIdx}_f${i}` });\n if (ctaUrl) {\n clearTimers();\n if (ctx.navigate) ctx.navigate(ctaUrl, `g${gIdx}_f${i}`);\n else window.location.href = ctaUrl;\n }\n });\n copy.appendChild(cta);\n }\n frameLayer.appendChild(copy);\n\n const key = `${gIdx}_${i}`;\n if (!seen.has(key)) {\n seen.add(key);\n trackEvent(campaign.id, 'impression', { stepId: `g${gIdx}_f${i}` });\n }\n };\n\n const show = (i: number): void => {\n clearTimers();\n elapsedBeforePause = 0;\n paused = false;\n idx = i;\n paintBarsUpTo(i);\n renderFrame(i);\n scheduleAdvance();\n };\n\n // Move to another group (rebuilds the progress bars for its frame count).\n const loadGroup = (gi: number, startFrame: number): void => {\n gIdx = gi;\n frames = groups[gi].frames;\n buildBars();\n show(Math.max(0, Math.min(startFrame, frames.length - 1)));\n };\n\n function advance(dir: number): void {\n const next = idx + dir;\n if (next < 0) {\n // Back past the first frame → previous group's LAST frame (or stay).\n if (gIdx > 0) loadGroup(gIdx - 1, groups[gIdx - 1].frames.length - 1);\n else show(0);\n return;\n }\n if (next >= frames.length) {\n // Past the last frame → next GROUP; wrap to the first group if looping,\n // else the whole sequence is done.\n if (gIdx + 1 < groups.length) loadGroup(gIdx + 1, 0);\n else if (loop) { seen.clear(); loadGroup(0, 0); }\n else teardown('completed');\n return;\n }\n show(next);\n }\n\n const pause = () => {\n if (paused) return;\n paused = true;\n if (timer) { clearTimeout(timer); timer = null; }\n if (rafId !== null) { cancelAnimationFrame(rafId); rafId = null; }\n elapsedBeforePause += Date.now() - frameStart;\n currentVideo?.pause?.();\n };\n const resume = () => {\n if (!paused) return;\n paused = false;\n currentVideo?.play?.().catch(() => {});\n scheduleAdvance();\n };\n\n let pointerDownAt = 0;\n let pointerX = 0;\n stage.addEventListener('pointerdown', (e) => { pointerDownAt = Date.now(); pointerX = e.clientX; pause(); });\n stage.addEventListener('pointerup', () => {\n const held = Date.now() - pointerDownAt;\n if (held <= TAP_MAX_MS) {\n const rect = stage.getBoundingClientRect();\n advance(pointerX - rect.left < rect.width / 3 ? -1 : 1);\n } else resume();\n });\n stage.addEventListener('pointercancel', resume);\n stage.addEventListener('pointerleave', resume);\n\n close.addEventListener('pointerdown', (e) => e.stopPropagation());\n close.addEventListener('click', (e) => { e.stopPropagation(); teardown('dismissed'); });\n\n function onKey(e: KeyboardEvent): void {\n if (e.key === 'Escape') teardown('dismissed');\n else if (e.key === 'ArrowRight') advance(1);\n else if (e.key === 'ArrowLeft') advance(-1);\n }\n document.addEventListener('keydown', onKey);\n\n buildBars();\n document.body.appendChild(overlay);\n show(startFrameIndex);\n}\n\nfunction injectStoryStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById('aegis-stories-styles')) return;\n const style = document.createElement('style');\n style.id = 'aegis-stories-styles';\n style.textContent = `\n @keyframes aegisStoryRing { to { transform: rotate(360deg); } }\n @keyframes aegisStoryShimmer { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }\n .aegis-in-app-stories-tray::-webkit-scrollbar { display: none; }\n `;\n document.head.appendChild(style);\n}\n","/**\n * Gamification renderers — the 11 locked game mechanics (2026-06-14):\n * streak · milestone · challenge · achievements · leaderboard · check_in ·\n * stamp_card · mystery_box · tier_ladder · lucky_draw · slot_machine\n *\n * All are config-authored (operator sets the values / demo state via\n * interactive_config; the serve endpoint can later enrich with the customer's\n * real streak/stamps/tier). Each reuses campaign.title/body for the heading and\n * grants its reward via `ctx.submitResponse` on the key action (claim / play /\n * reveal) + the campaign CTA via `ctx.navigate`. Dispatched from\n * AegisInAppManager.renderInteractive (these are interactive sub_types).\n */\n\nimport { lucideSvgEl } from '../lucide-svg';\n// Earn-mechanic progress (milestone / challenge / stamp / tier) shares the ONE\n// data-bound progress core — same clamp + pct as the progress bar + the dashboard\n// preview, so a loyalty-progress widget can't drift from the canonical math.\nimport { computeProgress } from './progress-core';\nimport type { RenderContext } from './types';\n\n/** pct (0..100) toward an earn-mechanic goal via the shared data-bound core. */\nfunction earnPct(current: number, threshold: number): number {\n return computeProgress({ goalType: 'loyalty_points', threshold, current }).pct;\n}\n\nconst num = (v: unknown, d: number): number => { const n = Number(v); return Number.isFinite(n) ? n : d; };\nconst str = (v: unknown, d = ''): string => (typeof v === 'string' ? v : d);\nconst arr = <T = Record<string, unknown>>(v: unknown): T[] => (Array.isArray(v) ? (v as T[]) : []);\n\nfunction brandAccent(ctx: RenderContext): string {\n return ctx.sanitizeColor((ctx.campaign.background_color as string) || '#4169e1');\n}\n\ninterface GameShell {\n overlay: HTMLElement;\n card: HTMLElement;\n content: HTMLElement;\n accent: string;\n branded: boolean;\n dismiss: (track?: boolean) => void;\n ctaButton: (label: string, onClick?: () => void) => HTMLButtonElement | null;\n}\n\n// Shared centred-modal scaffold for every game (title/body header + close +\n// content area + optional accent CTA). Does NOT fire impression — the\n// renderInteractive dispatch does.\n/** One-time injection of game-specific keyframes (rattle/anticipation). The\n * shared celebration keyframes come from ctx.addAnimationStyles(). */\nfunction injectGameStyles(): void {\n if (typeof document === 'undefined' || document.getElementById('aegis-game-styles')) return;\n if (typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;\n const el = document.createElement('style');\n el.id = 'aegis-game-styles';\n el.textContent =\n '@keyframes aegisBoxRattle{0%,100%{transform:scale(1) rotate(0)}20%{transform:scale(1.05) rotate(-4deg)}40%{transform:scale(1.05) rotate(4deg)}60%{transform:scale(1.06) rotate(-3deg)}80%{transform:scale(1.06) rotate(3deg)}}' +\n // Stamp \"thunk\" — slams in oversized + rotated, settles to a hand-stamp tilt.\n '@keyframes aegisStampIn{0%{transform:scale(2.4) rotate(-20deg);opacity:0}55%{transform:scale(0.82) rotate(7deg);opacity:1}100%{transform:scale(1) rotate(-6deg);opacity:1}}' +\n // Badge unlock pop — springs up from nothing with a tiny overshoot.\n '@keyframes aegisBadgePop{0%{transform:scale(0) rotate(-12deg);opacity:0}70%{transform:scale(1.18) rotate(4deg);opacity:1}100%{transform:scale(1) rotate(0);opacity:1}}';\n document.head.appendChild(el);\n}\n\nfunction openGameModal(ctx: RenderContext, className: string, maxWidth = 360): GameShell {\n const { campaign, trackEvent, addAnimationStyles } = ctx;\n addAnimationStyles();\n injectGameStyles();\n const accent = brandAccent(ctx);\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const branded = (ic.surface_style as string) === 'branded';\n const bg = branded ? accent : '#ffffff';\n const fg = branded ? '#ffffff' : '#0f172a';\n\n const overlay = document.createElement('div');\n overlay.className = className;\n overlay.setAttribute('data-campaign-id', campaign.id);\n overlay.style.cssText =\n 'position:fixed;inset:0;z-index:100000;background:rgba(15,23,42,0.55);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;padding:16px;font-family:\"Urbanist\",system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;animation:aegisFadeIn 0.25s ease;';\n\n const card = document.createElement('div');\n card.style.cssText = `position:relative;width:100%;max-width:${maxWidth}px;max-height:88vh;overflow-y:auto;background:${bg};color:${fg};border-radius:20px;padding:24px 20px 20px;box-shadow:0 16px 48px rgba(0,0,0,0.22);animation:aegisScaleIn 0.3s ease;text-align:center;`;\n\n let removed = false;\n const dismiss = (track = false): void => {\n if (removed) return;\n removed = true;\n if (track) trackEvent(campaign.id, 'dismissed');\n overlay.style.animation = 'aegisFadeOut 0.2s ease';\n setTimeout(() => overlay.remove(), 180);\n };\n\n const close = document.createElement('button');\n close.type = 'button';\n // Safe SVG node (no innerHTML — XSS-safe by construction, matches card-template).\n const closeIcon = lucideSvgEl('x', { size: 15 });\n if (closeIcon) close.appendChild(closeIcon);\n close.setAttribute('aria-label', 'Close');\n close.style.cssText = `position:absolute;top:10px;right:10px;width:30px;height:30px;border:none;border-radius:999px;background:${branded ? 'rgba(255,255,255,0.18)' : 'rgba(0,0,0,0.05)'};color:${branded ? '#fff' : '#475569'};font-size:15px;cursor:pointer;z-index:1;`;\n close.addEventListener('click', () => dismiss(true));\n card.appendChild(close);\n overlay.addEventListener('click', (e) => { if (e.target === overlay) dismiss(true); });\n\n if (campaign.title) {\n const t = document.createElement('div');\n t.textContent = campaign.title;\n t.style.cssText = \"font-family:'Urbanist',system-ui,sans-serif;font-size:18px;font-weight:800;letter-spacing:-0.01em;margin:2px 24px 4px;line-height:1.25;\";\n card.appendChild(t);\n }\n if (campaign.body) {\n const b = document.createElement('div');\n b.textContent = campaign.body;\n b.style.cssText = 'font-size:13px;opacity:0.72;margin:0 6px 12px;line-height:1.4;';\n card.appendChild(b);\n }\n\n const content = document.createElement('div');\n card.appendChild(content);\n overlay.appendChild(card);\n document.body.appendChild(overlay);\n\n const ctaButton = (label: string, onClick?: () => void): HTMLButtonElement | null => {\n if (!label) return null;\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'aegis-cta';\n btn.textContent = label;\n btn.style.cssText = `margin-top:16px;width:100%;padding:12px 20px;border:none;border-radius:999px;background:${branded ? '#fff' : accent};color:${branded ? accent : '#fff'};font-size:14px;font-weight:700;cursor:pointer;`;\n btn.addEventListener('click', () => {\n trackEvent(campaign.id, 'clicked');\n if (onClick) onClick();\n const url = str(campaign.action_url);\n if (url) { if (ctx.navigate) ctx.navigate(url, 'cta'); else window.location.href = url; }\n });\n card.appendChild(btn);\n return btn;\n };\n\n return { overlay, card, content, accent, branded, dismiss, ctaButton };\n}\n\n// Small helpers for repeated bits. ALL DOM is built with textContent — NEVER\n// innerHTML — so operator config + customer data (leaderboard names, prizes,\n// merge-tags) can never inject markup/script (XSS-safe by construction).\nfunction gEl(tag: string, css: string, text?: string): HTMLElement {\n const e = document.createElement(tag);\n if (css) e.style.cssText = css;\n if (text != null) e.textContent = text;\n return e;\n}\n\n// Progress bar as a DOM node (no innerHTML).\nfunction fillBar(accent: string, pct: number, h = 8): HTMLElement {\n const track = gEl('div', `height:${h}px;border-radius:999px;background:rgba(148,163,184,0.25);overflow:hidden;margin:10px 0`);\n track.appendChild(gEl('div', `height:100%;width:${Math.max(0, Math.min(100, pct))}%;background:${accent};border-radius:999px;transition:width .5s ease`));\n return track;\n}\n\n// ── 1. Daily streak ─────────────────────────────────────────────────────────\n// The count is the customer's REAL visit streak, bound server-side from\n// `loyalty_streaks`; the server serves this game only while that streak is\n// alive and not yet saved today (Aman 2026-09-25). No count = no game: there is\n// no default, because \"5-day streak!\" is a claim about the customer.\n// The button cannot save a streak — only a purchase does — so its copy says\n// what the customer should do, never \"Keep my streak\".\nexport function renderStreak(ctx: RenderContext): void {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const count = num(ic.streak_count, 0);\n if (count <= 0) return;\n const s = openGameModal(ctx, 'aegis-in-app-streak');\n const goal = num(ic.streak_goal, count + 1);\n const reward = str(ic.streak_reward_text, 'Order today to keep it going.');\n const days = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];\n s.content.appendChild(gEl('div', 'font-size:44px;line-height:1', '🔥')); // eslint-disable-line no-restricted-syntax -- game content\n s.content.appendChild(gEl('div', 'font-size:22px;font-weight:800;margin:4px 0 2px', `${count}-day streak!`));\n const chips = gEl('div', 'display:flex;justify-content:center;gap:8px;margin:12px 0');\n for (let i = 0; i < 7; i++) {\n const chip = gEl('div', 'display:inline-flex;flex-direction:column;align-items:center;gap:3px;font-size:11px;opacity:0.8');\n chip.appendChild(gEl('span', '', days[i]));\n chip.appendChild(gEl('span', 'font-size:16px', i < count % 7 || (count >= 7 && i < 7) ? '✅' : i === count % 7 ? '🔥' : '⚪')); // eslint-disable-line no-restricted-syntax -- game content\n chips.appendChild(chip);\n }\n s.content.appendChild(chips);\n const note = gEl('div', 'font-size:13px;opacity:0.8');\n note.appendChild(document.createTextNode(reward + ' '));\n note.appendChild(gEl('b', '', `Day ${goal}`));\n note.appendChild(document.createTextNode(' unlocks a reward.'));\n s.content.appendChild(note);\n const hasUrl = Boolean(str(ctx.campaign.action_url));\n s.ctaButton(str(ic.streak_cta_text, hasUrl ? 'Order today' : 'Got it'), () => {\n ctx.haptic?.(20);\n ctx.playReaction?.(s.card, 'affirm');\n // Recorded as `streak_advanced` -> gamification.streak_prompt_tapped: an\n // engagement signal, not a streak day.\n void ctx.submitResponse?.('streak', { streak_count: count });\n if (!hasUrl) s.dismiss();\n });\n}\n\n// ── 2. Milestone tracker ────────────────────────────────────────────────────\nexport function renderMilestone(ctx: RenderContext): void {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const s = openGameModal(ctx, 'aegis-in-app-milestone');\n const unit = str(ic.milestone_unit, '');\n const cur = num(ic.milestone_current, 1200);\n const target = num(ic.milestone_target, 2000);\n const steps = arr<{ value: number; reward: string }>(ic.milestone_steps);\n const pct = earnPct(cur, target);\n s.content.appendChild(gEl('div', 'font-size:13px;opacity:0.8;margin-bottom:2px', `${unit}${cur} of ${unit}${target}`));\n s.content.appendChild(fillBar(s.accent, pct));\n s.content.appendChild(gEl('div', 'font-size:13px;font-weight:600', `${unit}${Math.max(0, target - cur)} to go`));\n if (steps.length) {\n const row = gEl('div', 'display:flex;justify-content:space-between;margin-top:6px;font-size:10px;opacity:0.7');\n steps.forEach((st) => {\n const cell = gEl('span', 'display:flex;flex-direction:column;text-align:center;max-width:64px');\n cell.appendChild(gEl('span', '', `${unit}${num(st.value, 0)}`));\n cell.appendChild(gEl('span', '', str(st.reward)));\n row.appendChild(cell);\n });\n s.content.appendChild(row);\n }\n s.ctaButton(str(ic.milestone_cta_text, 'Shop now'));\n}\n\n// ── 3. Challenges / quests ──────────────────────────────────────────────────\nexport function renderChallenge(ctx: RenderContext): void {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const s = openGameModal(ctx, 'aegis-in-app-challenge');\n const tasks = arr<{ label: string; points?: number; done?: boolean }>(ic.challenge_tasks);\n const done = tasks.filter((t) => t.done).length;\n const pct = tasks.length ? earnPct(done, tasks.length) : 0;\n if (str(ic.challenge_deadline_text)) s.content.appendChild(gEl('div', 'font-size:12px;opacity:0.65;margin-bottom:8px', str(ic.challenge_deadline_text)));\n tasks.forEach((t) => {\n const row = gEl('div', 'display:flex;align-items:center;gap:8px;padding:7px 0;border-bottom:1px solid rgba(148,163,184,0.18);font-size:13px;text-align:left');\n row.appendChild(gEl('span', '', t.done ? '☑' : '☐')); // eslint-disable-line no-restricted-syntax -- game content (quest checklist)\n row.appendChild(gEl('span', `flex:1;${t.done ? 'opacity:0.6' : ''}`, str(t.label)));\n if (t.points) row.appendChild(gEl('span', 'opacity:0.7;font-size:12px', `+${num(t.points, 0)}`));\n s.content.appendChild(row);\n });\n s.content.appendChild(fillBar(s.accent, pct));\n s.content.appendChild(gEl('div', 'font-size:13px;font-weight:600', `${done} of ${tasks.length} · ${str(ic.challenge_bonus_text, 'finish all to earn a bonus')}`));\n s.ctaButton(str(ic.challenge_cta_text, 'Continue'));\n}\n\n// ── 4. Badges / achievements ────────────────────────────────────────────────\nexport function renderAchievements(ctx: RenderContext): void {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const s = openGameModal(ctx, 'aegis-in-app-achievements', 380);\n const items = arr<{ label: string; icon?: string; unlocked?: boolean }>(ic.achievements_items);\n const got = items.filter((i) => i.unlocked).length;\n const reduce = typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n const grid = gEl('div', 'display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin:6px 0 10px');\n let unlockSeen = 0;\n items.forEach((i) => {\n const cell = gEl('div', `display:flex;flex-direction:column;align-items:center;gap:4px;padding:10px 6px;border-radius:12px;background:${i.unlocked ? s.accent + '1f' : 'rgba(148,163,184,0.12)'};${i.unlocked ? '' : 'opacity:0.55'}`);\n const icon = gEl('span', 'font-size:24px;display:inline-block', i.unlocked ? str(i.icon, '🏅') : '🔒'); // eslint-disable-line no-restricted-syntax -- game content (operator-overridable badge default + lock)\n cell.appendChild(icon);\n cell.appendChild(gEl('span', 'font-size:10.5px;line-height:1.15', str(i.label)));\n if (i.unlocked && !reduce) {\n icon.style.animation = `aegisBadgePop 0.5s cubic-bezier(0.2,1.4,0.4,1) ${unlockSeen * 0.08}s both`;\n unlockSeen++;\n }\n grid.appendChild(cell);\n });\n s.content.appendChild(grid);\n s.content.appendChild(gEl('div', 'font-size:13px;font-weight:600', `${got} of ${items.length} unlocked`));\n if (str(ic.achievements_next_text)) s.content.appendChild(gEl('div', 'font-size:12px;opacity:0.7;margin-top:2px', str(ic.achievements_next_text)));\n if (got > 0 && !reduce) { ctx.haptic?.(12); ctx.playReaction?.(s.card, 'affirm'); }\n s.ctaButton(str(ctx.campaign.button_text, ''));\n}\n\n// ── 5. Leaderboard ──────────────────────────────────────────────────────────\nexport function renderLeaderboard(ctx: RenderContext): void {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const s = openGameModal(ctx, 'aegis-in-app-leaderboard');\n const rows = arr<{ name: string; score: number; you?: boolean }>(ic.leaderboard_rows);\n const medal = ['🥇', '🥈', '🥉']; // eslint-disable-line no-restricted-syntax -- game content (leaderboard medals)\n if (str(ic.leaderboard_period_text)) s.content.appendChild(gEl('div', 'font-size:12px;opacity:0.65;margin-bottom:6px', str(ic.leaderboard_period_text)));\n rows.forEach((r, i) => {\n const row = gEl('div', `display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:10px;${r.you ? `background:${s.accent}14;font-weight:700` : ''};font-size:13px;text-align:left`);\n row.appendChild(gEl('span', 'width:22px', i < 3 ? medal[i] : String(i + 1)));\n // r.name is CUSTOMER data (contact first_name) — textContent makes it inert.\n row.appendChild(gEl('span', 'flex:1', str(r.name) + (r.you ? ' (You)' : '')));\n row.appendChild(gEl('span', '', num(r.score, 0).toLocaleString()));\n s.content.appendChild(row);\n });\n if (str(ic.leaderboard_gap_text)) s.content.appendChild(gEl('div', 'font-size:12.5px;opacity:0.8;margin-top:10px', str(ic.leaderboard_gap_text)));\n s.ctaButton(str(ctx.campaign.button_text, ''));\n}\n\n// ── 6. Daily check-in ───────────────────────────────────────────────────────\nexport function renderCheckIn(ctx: RenderContext): void {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const s = openGameModal(ctx, 'aegis-in-app-checkin', 380);\n const daysCfg = arr<{ label: string; reward: string; state?: string }>(ic.checkin_days);\n const strip = gEl('div', 'display:flex;gap:8px;overflow-x:auto;padding:6px 2px 10px;scrollbar-width:none');\n daysCfg.forEach((d) => {\n const hi = d.state === 'today';\n const cell = gEl('div', `flex:0 0 auto;width:60px;display:flex;flex-direction:column;align-items:center;gap:3px;padding:8px 4px;border-radius:12px;background:${hi ? s.accent + '1a' : 'rgba(148,163,184,0.12)'};border:1.5px solid ${hi ? s.accent : 'transparent'}`);\n cell.appendChild(gEl('span', 'font-size:11px;opacity:0.7', str(d.label)));\n cell.appendChild(gEl('span', 'font-size:18px', d.state === 'claimed' ? '✅' : hi ? '🎁' : '🔒')); // eslint-disable-line no-restricted-syntax -- game content (check-in day state)\n cell.appendChild(gEl('span', 'font-size:11px;font-weight:700', str(d.reward)));\n strip.appendChild(cell);\n });\n s.content.appendChild(strip);\n const today = daysCfg.find((d) => d.state === 'today');\n s.ctaButton(str(ic.checkin_cta_text, today ? `Claim ${str(today.label)} · ${str(today.reward)}` : 'Claim'), () => {\n ctx.haptic?.([0, 20, 15, 30]);\n ctx.playReaction?.(s.card, 'celebrate', 'gift.json');\n void ctx.submitResponse?.('check_in', { day: str(today?.label), reset: str(ic.checkin_reset) || 'daily' });\n });\n}\n\n// Shared lazy AudioContext for game SFX. A short percussive \"thunk\" for a stamp\n// landing — synth, no assets. Best-effort (autoplay-policy / no-WebAudio safe).\nlet _gameAudioCtx: AudioContext | null = null;\nfunction playStampThunk(): void {\n try {\n if (typeof window === 'undefined') return;\n const Ctx = window.AudioContext || (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Ctx) return;\n if (!_gameAudioCtx) _gameAudioCtx = new Ctx();\n const ac = _gameAudioCtx;\n if (ac.state === 'suspended') void ac.resume();\n const t = ac.currentTime;\n const osc = ac.createOscillator();\n const gain = ac.createGain();\n osc.type = 'triangle';\n osc.frequency.setValueAtTime(190, t);\n osc.frequency.exponentialRampToValueAtTime(68, t + 0.12);\n gain.gain.setValueAtTime(0.0001, t);\n gain.gain.exponentialRampToValueAtTime(0.22, t + 0.006);\n gain.gain.exponentialRampToValueAtTime(0.0001, t + 0.16);\n osc.connect(gain); gain.connect(ac.destination);\n osc.start(t); osc.stop(t + 0.18);\n } catch { /* no audio — silent */ }\n}\n\n// ── 7. Stamp / punch card ───────────────────────────────────────────────────\n// A REAL punch card: each earned slot receives an INK STAMP of the brand mark\n// (the tenant logo, tinted in the brand colour + a slight hand-stamp tilt), the\n// newest one slamming in with a \"thunk\". Empty slots are dashed wells; the final\n// slot is the reward. Falls back to a check glyph when no logo is configured.\nexport function renderStampCard(ctx: RenderContext): void {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const s = openGameModal(ctx, 'aegis-in-app-stamp', 380);\n const total = num(ic.stamp_total, 9);\n const filled = Math.min(total, num(ic.stamp_filled, 1));\n const reduce = typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n // Default to the brand logo (campaign image / background logo) — same default\n // as the scratch-card foil. Operators can override with stamp_logo_url. Only\n // sanitize a NON-EMPTY value: sanitizeUrl('') resolves to the page origin (a\n // truthy non-image URL) which would load + fail → the ✓ fallback stuck forever.\n const logoRaw = str(ic.stamp_logo_url) || str(ctx.campaign.image_url);\n const logo = logoRaw ? ctx.sanitizeUrl(logoRaw) : null;\n\n // Reward checkpoints by stamp position (1-based). Falls back to the single\n // completion reward at the last stamp, then to a plain \"free reward\" at the end.\n const rewards = arr<{ at?: number; label?: string; prize_value?: string; icon?: string }>(ic.stamp_rewards);\n const rewardAt = new Map<number, string>();\n const rewardIconAt = new Map<number, string>(); // the prize's own icon at this step\n if (rewards.length) {\n rewards.forEach((r) => {\n if (typeof r.at === 'number' && r.at >= 1 && r.at <= total) {\n rewardAt.set(r.at, str(r.label) || str(r.prize_value) || 'Reward');\n rewardIconAt.set(r.at, str(r.icon) || '🎁'); // eslint-disable-line no-restricted-syntax -- game content (operator-overridable prize icon default)\n }\n });\n } else {\n rewardAt.set(total, str((ic.stamp_reward as { label?: string } | undefined)?.label) || str(ic.stamp_reward_text) || 'Reward');\n rewardIconAt.set(total, '🎁'); // eslint-disable-line no-restricted-syntax -- game content (default prize icon)\n }\n\n // 3 BIG stamps per row. Each earned slot is a real GREEN INK stamp: the brand\n // mark reproduced as a single-colour emerald silhouette inside a green ring.\n const ink = '#10b981'; // emerald — same green as the Active Bill \"PAID\" stamp\n const cols = Math.min(total, 3);\n const grid = document.createElement('div');\n grid.style.cssText = `display:grid;grid-template-columns:repeat(${cols},1fr);gap:16px;margin:14px auto 16px;max-width:${cols * 110}px`;\n const logoHosts: HTMLElement[] = []; // filled-slot stamp areas to fill with the logo silhouette\n let stampOrder = 0; // stagger index across filled stamps (for the stamp-on-load)\n for (let i = 0; i < total; i++) {\n const pos = i + 1;\n const isReward = rewardAt.has(pos);\n const isFilled = i < filled;\n const slot = document.createElement('div');\n slot.title = isReward ? `Reward: ${rewardAt.get(pos)}` : '';\n slot.style.cssText = `position:relative;width:100%;aspect-ratio:1;border-radius:50%;display:flex;align-items:center;justify-content:center;overflow:visible;` +\n (isFilled\n ? `border:2.5px solid ${ink};background:${ink}14;${isReward ? `box-shadow:0 0 0 3px ${ink}55;` : ''}`\n : `border:2.5px dashed ${isReward ? ink + 'aa' : 'rgba(148,163,184,0.5)'};background:${isReward ? ink + '10' : 'transparent'};`);\n if (isFilled) {\n // The stamp area: circular, clips the logo silhouette; shows a ✓ until the\n // brand logo loads (and as the fallback if the URL is empty/blocked).\n const host = gEl('div', 'width:92%;height:92%;border-radius:50%;overflow:hidden;display:flex;align-items:center;justify-content:center;');\n host.appendChild(gEl('span', `font-size:32px;font-weight:900;color:${ink};transform:rotate(-6deg)`, '✓')); // eslint-disable-line no-restricted-syntax -- game content (stamp mark fallback)\n host.style.transform = 'rotate(-6deg) scale(1.02)';\n if (!reduce) {\n const delay = Math.min(stampOrder, 9) * 0.1;\n host.style.opacity = '0';\n host.style.animation = `aegisStampIn 0.45s cubic-bezier(0.2,1.3,0.4,1) ${delay}s both`;\n const order = stampOrder;\n window.setTimeout(() => { playStampThunk(); ctx.haptic?.(14); }, delay * 1000 + (order === 0 ? 80 : 0));\n }\n if (logo) logoHosts.push(host);\n stampOrder++;\n slot.appendChild(host);\n } else if (isReward) {\n // Unfilled reward slot → the PRIZE’s own icon (🎟/🏷/💎…), not a generic gift.\n slot.appendChild(gEl('span', 'font-size:30px', rewardIconAt.get(pos) || '🎁')); // eslint-disable-line no-restricted-syntax -- game content (prize icon default)\n }\n // Reward step → a prize badge so it reads as a milestone. When the slot is\n // STAMPED, the prize icon OVERLAYS the stamp (corner badge).\n if (isReward && isFilled) {\n slot.appendChild(gEl('span', 'position:absolute;top:-3px;right:-3px;font-size:17px;line-height:1;filter:drop-shadow(0 1px 1px rgba(0,0,0,0.25))', rewardIconAt.get(pos) || '🎁')); // eslint-disable-line no-restricted-syntax -- game content (prize icon default)\n }\n grid.appendChild(slot);\n }\n s.content.appendChild(grid);\n\n // Stamp every filled slot with the brand logo. NB: no `crossOrigin` — we only\n // DISPLAY the canvas, never read its pixels for OUTPUT, so it loads even from\n // hosts WITHOUT CORS headers. Forcing crossOrigin='anonymous' made the logo\n // fail to load on a no-CORS host → the ✓ fallback stuck forever (the bug).\n if (logo && logoHosts.length) {\n const im = new Image();\n im.onload = (): void => {\n // Downscale to a small master (cap the longest side) — the raw logo can be\n // multiple thousand px; cloning that into every slot is wasteful and the\n // stamp only renders ~90px. Keeps the preview snappy on re-render.\n const CAP = 160;\n const nw = im.naturalWidth || 96, nh = im.naturalHeight || 96;\n const scale = Math.min(1, CAP / Math.max(nw, nh));\n const w = Math.max(1, Math.round(nw * scale));\n const h = Math.max(1, Math.round(nh * scale));\n const master = document.createElement('canvas');\n master.width = w; master.height = h;\n const mctx = master.getContext('2d');\n if (!mctx) return;\n mctx.drawImage(im, 0, 0, w, h);\n // A TRANSPARENT logo → recolour to a clean emerald ink silhouette (the true\n // rubber-stamp look). An OPAQUE logo → keep it as-is, because a source-in\n // recolour would paint the whole rectangle a solid green blob. Detect via\n // corner-alpha; if the canvas is tainted (cross-origin, no CORS) the read\n // throws → fall back to a URL hint (the brand-kit birefnet variant is named\n // `..._transparent.png`). Either way the brand mark shows — never the ✓.\n let transparent = false;\n try {\n const corners: ReadonlyArray<readonly [number, number]> = [[0, 0], [w - 1, 0], [0, h - 1], [w - 1, h - 1]];\n transparent = corners.some(([x, y]) => mctx.getImageData(x, y, 1, 1).data[3] < 16);\n } catch { transparent = /transparent/i.test(logo || ''); }\n if (transparent) {\n mctx.globalCompositeOperation = 'source-in';\n mctx.fillStyle = ink;\n mctx.fillRect(0, 0, w, h);\n }\n logoHosts.forEach((host) => {\n const cv = document.createElement('canvas');\n cv.width = w; cv.height = h;\n cv.style.cssText = 'width:100%;height:100%;object-fit:contain;';\n const c = cv.getContext('2d');\n if (c) c.drawImage(master, 0, 0);\n host.replaceChildren(cv);\n });\n };\n im.onerror = (): void => { /* keep the check fallback */ };\n im.src = logo;\n }\n\n // Next reward checkpoint ahead of the customer.\n const nextReward = [...rewardAt.keys()].filter((p) => p > filled).sort((a, b) => a - b)[0];\n const progressTxt = nextReward\n ? `${nextReward - filled} more → ${rewardAt.get(nextReward)}`\n : 'Card complete!';\n const sub = gEl('div', 'font-size:13px;font-weight:600;margin-top:2px');\n sub.appendChild(fillBar(ink, earnPct(filled, total)));\n sub.appendChild(gEl('div', '', `${filled} of ${total}`));\n // The operator's reward text is the subtitle when set; else the computed hint.\n sub.appendChild(gEl('div', 'font-weight:400;opacity:0.82;margin-top:1px', str(ic.stamp_reward_text) || progressTxt));\n s.content.appendChild(sub);\n\n s.ctaButton(str(ic.stamp_cta_text, 'Order & earn'), () => { ctx.haptic?.(12); });\n\n // REVEAL — the serve binder injects `stamp_just_unlocked` when a milestone was\n // freshly granted this session. Celebrate it: gift-box Lottie + a banner that\n // the reward (and its coupon) is on the customer's account.\n const unlocked = ic.stamp_just_unlocked as { label?: string; icon?: string; coupon_code?: string; redeemable?: boolean } | undefined;\n if (unlocked && typeof unlocked === 'object') {\n const banner = gEl('div', `margin-top:12px;padding:11px 12px;border-radius:14px;background:${ink}14;border:1.5px solid ${ink}55;text-align:center`);\n banner.appendChild(gEl('div', 'font-size:24px', str(unlocked.icon) || '🎁')); // eslint-disable-line no-restricted-syntax -- game content (unlocked prize icon default)\n banner.appendChild(gEl('div', `font-size:13.5px;font-weight:800;color:${ink}`, `Unlocked: ${str(unlocked.label) || 'Reward'}`));\n banner.appendChild(gEl('div', 'font-size:11.5px;opacity:0.8;margin-top:1px',\n unlocked.coupon_code ? `Coupon ${String(unlocked.coupon_code)} added to your account` : 'Added to your account'));\n s.content.appendChild(banner);\n if (!reduce) { ctx.haptic?.([0, 30, 20, 40]); ctx.playReaction?.(s.card, 'celebrate', 'gift.json'); }\n }\n}\n\n// ── 8. Mystery box ──────────────────────────────────────────────────────────\nexport function renderMysteryBox(ctx: RenderContext): void {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const s = openGameModal(ctx, 'aegis-in-app-mystery');\n const boxes = arr<{ prize: string }>(ic.mystery_boxes);\n const list = boxes.length ? boxes : [{ prize: '10% OFF' }, { prize: 'Free shipping' }, { prize: '50 points' }];\n if (str(ic.mystery_subtitle)) {\n const sub = document.createElement('div'); sub.textContent = str(ic.mystery_subtitle);\n sub.style.cssText = 'font-size:12.5px;opacity:0.75;margin-bottom:8px'; s.content.appendChild(sub);\n }\n const row = document.createElement('div');\n row.style.cssText = 'display:flex;gap:12px;justify-content:center;margin:8px 0 4px';\n let opened = false;\n list.forEach((b, i) => {\n const box = document.createElement('button');\n box.type = 'button';\n box.style.cssText = `flex:1;max-width:96px;aspect-ratio:3/4;border:none;border-radius:14px;background:${s.accent}1a;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;cursor:pointer;font-size:13px;font-weight:700;color:#0f172a;transition:transform .15s`;\n box.appendChild(gEl('span', 'font-size:30px', '🎁')); // eslint-disable-line no-restricted-syntax -- game content (mystery box)\n box.appendChild(gEl('span', 'opacity:0.7', `Box ${String.fromCharCode(65 + i)}`));\n box.addEventListener('click', () => {\n if (opened) return;\n opened = true;\n ctx.haptic?.(12); // light tap\n // Anticipation: the chosen box rattles + others dim, then a brief suspense\n // pause before the reveal (dopamine ramps during the wait, not the prize).\n Array.from(row.children).forEach((c) => { if (c !== box) { (c as HTMLElement).style.opacity = '0.4'; (c as HTMLElement).style.transform = 'scale(0.94)'; } });\n box.style.animation = 'aegisBoxRattle 0.45s ease-in-out';\n // Server-authoritative: the SERVER picks win/lose + which prize from the\n // authored prize_pool (the box the user taps does NOT decide the reward).\n // We send the client prize as the no-pool fallback signal; if the campaign\n // has a prize_pool, the returned `prize` overrides the reveal.\n const submitP = ctx.submitResponse?.('mystery_box', { box_index: i, prize: str(b.prize) });\n const revealBox = async (): Promise<void> => {\n box.style.animation = '';\n let won = true;\n let prizeLabel = str(b.prize, 'You won!');\n try {\n const res = await submitP;\n const sp = res ? (res.prize as Record<string, unknown> | null) : null;\n if (sp) { won = sp.win !== false; if (sp.prize_label) prizeLabel = String(sp.prize_label); }\n } catch { /* network fail-soft → keep optimistic client prize */ }\n box.style.background = won ? `${s.accent}` : 'rgba(148,163,184,0.22)';\n box.style.color = won ? '#fff' : '#475569';\n box.style.transform = 'scale(1.08)';\n box.replaceChildren();\n box.appendChild(gEl('span', 'font-size:28px', won ? '🎉' : '🙈')); // eslint-disable-line no-restricted-syntax -- game content (mystery box reveal)\n box.appendChild(gEl('span', '', won ? prizeLabel : 'Not this time'));\n if (won) { ctx.haptic?.([0, 30, 20, 40]); ctx.playReaction?.(s.card, 'celebrate', 'gift.json'); }\n else { ctx.haptic?.(15); ctx.playReaction?.(s.card, 'empathize'); }\n };\n window.setTimeout(() => { void revealBox(); }, 520);\n });\n row.appendChild(box);\n });\n s.content.appendChild(row);\n s.ctaButton(str(ic.mystery_cta_text, ''));\n}\n\n// ── 9. Tier ladder ──────────────────────────────────────────────────────────\nexport function renderTierLadder(ctx: RenderContext): void {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const s = openGameModal(ctx, 'aegis-in-app-tier');\n const tiers = arr<{ name: string; icon?: string }>(ic.tiers);\n const list = tiers.length ? tiers : [{ name: 'Bronze', icon: '🥉' }, { name: 'Silver', icon: '🥈' }, { name: 'Gold', icon: '🥇' }]; // eslint-disable-line no-restricted-syntax -- game content (tier medals)\n const curIdx = Math.min(list.length - 1, num(ic.tier_current_index, 1));\n const curPts = num(ic.tier_current_points, 640);\n const nextPts = num(ic.tier_next_points, 1000);\n const pct = earnPct(curPts, nextPts);\n const perks = arr<string>(ic.tier_perks);\n const next = list[Math.min(list.length - 1, curIdx + 1)];\n const ladder = gEl('div', 'display:flex;align-items:flex-start;justify-content:center;margin:6px 0 12px');\n list.forEach((t, i) => {\n if (i > 0) ladder.appendChild(gEl('span', 'flex:1;height:2px;background:rgba(148,163,184,0.4);margin:12px 4px 0;align-self:flex-start'));\n const tier = gEl('div', `display:flex;flex-direction:column;align-items:center;gap:3px;${i === curIdx ? 'font-weight:800' : 'opacity:0.6'}`);\n tier.appendChild(gEl('span', 'font-size:24px', str(t.icon, '◆'))); // eslint-disable-line no-restricted-syntax -- game content (operator-overridable tier icon default)\n tier.appendChild(gEl('span', 'font-size:11px', str(t.name)));\n if (i === curIdx) tier.appendChild(gEl('span', `font-size:9px;color:${s.accent}`, '▲ you')); // eslint-disable-line no-restricted-syntax -- game content (current-tier marker)\n ladder.appendChild(tier);\n });\n s.content.appendChild(ladder);\n s.content.appendChild(fillBar(s.accent, pct));\n s.content.appendChild(gEl('div', 'font-size:13px;font-weight:600', `${Math.max(0, nextPts - curPts)} pts to ${str(next?.name, 'next tier')}`));\n if (perks.length) {\n const box = gEl('div', 'text-align:left;margin-top:10px;font-size:12.5px;opacity:0.85');\n box.appendChild(gEl('div', '', `${str(next?.name, 'Next')} unlocks:`));\n perks.forEach((p) => box.appendChild(gEl('div', '', `• ${str(p)}`)));\n s.content.appendChild(box);\n }\n s.ctaButton(str(ic.tier_cta_text, ''));\n}\n\n// ── 10. Lucky draw / raffle ─────────────────────────────────────────────────\nexport function renderLuckyDraw(ctx: RenderContext): void {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const s = openGameModal(ctx, 'aegis-in-app-draw');\n const entries = num(ic.draw_entries, 6);\n const actions = arr<{ label: string; entries?: number; done?: boolean }>(ic.draw_actions);\n if (str(ic.draw_deadline_text)) s.content.appendChild(gEl('div', 'font-size:12px;opacity:0.65;margin-bottom:6px', str(ic.draw_deadline_text)));\n s.content.appendChild(gEl('div', 'font-size:30px', '🎟️')); // eslint-disable-line no-restricted-syntax -- game content (lucky-draw ticket)\n s.content.appendChild(gEl('div', 'font-size:15px;font-weight:800;margin:2px 0 10px', `${entries} entries`));\n s.content.appendChild(gEl('div', 'font-size:12px;opacity:0.7;margin-bottom:4px;text-align:left', 'Earn more entries:'));\n actions.forEach((a) => {\n const row = gEl('div', 'display:flex;align-items:center;gap:8px;padding:7px 0;border-bottom:1px solid rgba(148,163,184,0.18);font-size:13px;text-align:left');\n row.appendChild(gEl('span', '', a.done ? '✅' : '☐')); // eslint-disable-line no-restricted-syntax -- game content (lucky-draw checklist)\n row.appendChild(gEl('span', `flex:1;${a.done ? 'opacity:0.6' : ''}`, str(a.label)));\n row.appendChild(gEl('span', 'opacity:0.7;font-size:12px', `+${num(a.entries, 1)}`));\n s.content.appendChild(row);\n });\n if (str(ic.draw_pool_text)) s.content.appendChild(gEl('div', 'font-size:12px;opacity:0.7;margin-top:8px', str(ic.draw_pool_text)));\n // Record this contact's entry weight in the raffle pool — the server-side\n // winner-selection job tallies the MAX entries per contact, so re-opening is\n // harmless. Seeing the draw enters you with your current entry count.\n void ctx.submitResponse?.('lucky_draw', { entries });\n s.ctaButton(str(ic.draw_cta_text, 'Get more entries'), () => { ctx.haptic?.(12); });\n}\n\n// ── 11. Slot machine ────────────────────────────────────────────────────────\n// A REAL slot machine: vertical reel STRIPS that roll past a masked window with\n// motion blur, decelerate with an ease-out + slight overshoot, stop staggered\n// L→R with an anticipation hold on the last reel, and a pull LEVER beside the\n// reels. The strip is pre-positioned to the server-decided outcome (the prize is\n// chosen server-side; the reels stop ON it). Reduced-motion → instant settle.\ninterface SlotReel { el: HTMLElement; startSpin: () => void; stop: (sym: string, done: () => void) => void; }\n\nfunction makeSlotReel(pool: string[], cell: number): SlotReel {\n const reduce = typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n const win = document.createElement('div');\n win.style.cssText = `position:relative;width:${cell}px;height:${cell}px;overflow:hidden;border-radius:10px;background:linear-gradient(180deg,#ffffff,#f1f5f9);box-shadow:inset 0 6px 8px -4px rgba(0,0,0,0.35),inset 0 -6px 8px -4px rgba(0,0,0,0.35)`;\n const period = pool.length * cell; // height of one full symbol cycle\n const STRIP = pool.length * 8; // enough repeats for a long roll\n const strip = document.createElement('div');\n strip.style.cssText = 'position:absolute;left:0;top:0;width:100%;will-change:transform';\n for (let i = 0; i < STRIP; i++) {\n const c = document.createElement('div');\n c.style.cssText = `height:${cell}px;display:flex;align-items:center;justify-content:center;font-size:${Math.round(cell * 0.55)}px`;\n c.textContent = pool[i % pool.length];\n strip.appendChild(c);\n }\n win.appendChild(strip);\n\n let pos = 0; // px scrolled (downward roll)\n let raf: number | null = null;\n const draw = () => { strip.style.transform = `translateY(${-(pos % period)}px)`; };\n draw();\n\n const startSpin = () => {\n if (reduce) return;\n strip.style.transition = '';\n strip.style.filter = 'blur(1.5px)';\n const tick = () => { pos += 34; draw(); raf = requestAnimationFrame(tick); };\n raf = requestAnimationFrame(tick);\n };\n\n const stop = (sym: string, done: () => void) => {\n if (raf !== null) { cancelAnimationFrame(raf); raf = null; }\n const residue = (Math.max(0, pool.indexOf(sym)) % pool.length) * cell;\n const cur = pos % period;\n if (reduce) {\n pos = residue; strip.style.filter = ''; draw(); done(); return;\n }\n const delta = ((residue - cur) % period + period) % period;\n const final = cur + 2 * period + delta; // ~2+ full spins, land on target\n strip.style.filter = '';\n strip.style.transition = 'transform 0.9s cubic-bezier(0.18,0.86,0.28,1.12)'; // ease-out + slight overshoot\n strip.style.transform = `translateY(${-final}px)`;\n const onEnd = () => {\n strip.removeEventListener('transitionend', onEnd);\n pos = residue; strip.style.transition = ''; draw(); // normalise back into range\n done();\n };\n strip.addEventListener('transitionend', onEnd);\n };\n\n return { el: win, startSpin, stop };\n}\n\nfunction makeSlotLever(accent: string, onPull: () => void): { el: HTMLElement; pull: () => void } {\n const wrap = document.createElement('div');\n wrap.style.cssText = 'position:relative;width:26px;height:96px;display:flex;justify-content:center;cursor:pointer;user-select:none';\n wrap.setAttribute('role', 'button');\n wrap.setAttribute('aria-label', 'Pull the lever');\n const track = document.createElement('div');\n track.style.cssText = 'position:absolute;top:10px;bottom:10px;width:6px;border-radius:3px;background:linear-gradient(180deg,#64748b,#334155)';\n const knob = document.createElement('div');\n knob.style.cssText = `position:absolute;top:0;width:26px;height:26px;border-radius:50%;background:radial-gradient(circle at 34% 30%,#ffffff,${accent});box-shadow:0 3px 7px rgba(0,0,0,0.45);z-index:1`;\n wrap.appendChild(track); wrap.appendChild(knob);\n const pull = () => {\n knob.style.transition = 'transform 0.12s ease-in';\n knob.style.transform = 'translateY(60px)';\n window.setTimeout(() => {\n knob.style.transition = 'transform 0.5s cubic-bezier(0.2,1.5,0.4,1)'; // springy return\n knob.style.transform = 'translateY(0)';\n }, 130);\n };\n wrap.addEventListener('click', onPull);\n return { el: wrap, pull };\n}\n\nexport function renderSlotMachine(ctx: RenderContext): void {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n const s = openGameModal(ctx, 'aegis-in-app-slot', 400);\n const symbols = arr<string>(ic.slot_symbols).filter((x) => typeof x === 'string' && x);\n const pool = symbols.length >= 3 ? symbols : ['🍒', '7️⃣', '🍋', '⭐', '🔔']; // eslint-disable-line no-restricted-syntax -- game reel\n const CELL = 68;\n const rnd = (n: number): number => Math.floor(Math.random() * n);\n\n // Machine: reels in a recessed cabinet + a win line + a lever on the right.\n const machine = document.createElement('div');\n machine.style.cssText = 'display:flex;align-items:center;justify-content:center;gap:12px;margin:12px 0 6px';\n const cabinet = document.createElement('div');\n cabinet.style.cssText = `position:relative;display:flex;gap:8px;padding:12px;border-radius:18px;background:linear-gradient(180deg,#1f2937,#0f172a);box-shadow:inset 0 3px 10px rgba(0,0,0,0.55),0 6px 16px rgba(0,0,0,0.25)`;\n const reels = [0, 1, 2].map(() => makeSlotReel(pool, CELL));\n reels.forEach((r) => cabinet.appendChild(r.el));\n const winLine = document.createElement('div');\n winLine.style.cssText = `position:absolute;left:8px;right:8px;top:50%;height:${CELL}px;transform:translateY(-50%);border:2.5px solid transparent;border-radius:12px;pointer-events:none;transition:border-color 0.3s,box-shadow 0.3s`;\n cabinet.appendChild(winLine);\n const lever = makeSlotLever(s.accent, () => { void spin(); });\n machine.appendChild(cabinet);\n machine.appendChild(lever.el);\n s.content.appendChild(machine);\n\n const result = document.createElement('div');\n result.style.cssText = 'font-size:13px;font-weight:700;min-height:18px;margin-top:8px';\n s.content.appendChild(result);\n\n let spinning = false;\n const playBtn = s.ctaButton(str(ic.slot_cta_text, 'PULL 🎰'), () => {}); // eslint-disable-line no-restricted-syntax -- game content (slot lever label)\n const fresh = playBtn ? (playBtn.cloneNode(true) as HTMLButtonElement) : null;\n if (playBtn && fresh) { playBtn.replaceWith(fresh); fresh.addEventListener('click', () => { void spin(); }); }\n\n const spin = async (): Promise<void> => {\n if (spinning) return;\n spinning = true;\n if (fresh) fresh.disabled = true;\n lever.pull();\n ctx.haptic?.(18);\n result.textContent = '';\n winLine.style.borderColor = 'transparent';\n winLine.style.boxShadow = 'none';\n reels.forEach((r) => r.startSpin());\n\n // Provisional client outcome (no-pool fallback) + ask the server to decide.\n const prov = [pool[rnd(pool.length)], pool[rnd(pool.length)], pool[rnd(pool.length)]];\n const provWin = prov[0] === prov[1] && prov[1] === prov[2];\n const submitP = ctx.submitResponse?.('slot_machine', { win: provWin, symbols: prov });\n\n let serverPrize: Record<string, unknown> | null = null;\n try {\n const res = await Promise.race([\n submitP ?? Promise.resolve(null),\n new Promise<null>((resolve) => window.setTimeout(() => resolve(null), 1400)),\n ]);\n serverPrize = res ? ((res as Record<string, unknown>).prize as Record<string, unknown> | null) : null;\n } catch { /* network fail-soft → provisional */ }\n\n const win = serverPrize ? serverPrize.win !== false : provWin;\n const label = serverPrize && serverPrize.prize_label ? String(serverPrize.prize_label) : str(ic.slot_win_text, 'You win!');\n let target: string[];\n if (serverPrize) {\n if (win) { const sym = pool[rnd(pool.length)]; target = [sym, sym, sym]; }\n else { target = [pool[rnd(pool.length)], pool[rnd(pool.length)], pool[rnd(pool.length)]]; if (target[0] === target[1] && target[1] === target[2]) target[2] = pool[(pool.indexOf(target[2]) + 1) % pool.length]; }\n } else { target = prov; }\n\n const finish = (): void => {\n result.textContent = win ? `🎉 ${label}` : 'So close — try again next time!'; // eslint-disable-line no-restricted-syntax -- game content (win banner)\n result.style.color = win ? s.accent : '#94a3b8';\n if (win) {\n winLine.style.borderColor = s.accent;\n winLine.style.boxShadow = `0 0 14px ${s.accent}aa, inset 0 0 14px ${s.accent}55`;\n ctx.haptic?.([0, 30, 20, 40]);\n ctx.playReaction?.(s.card, 'celebrate');\n } else {\n ctx.haptic?.(15);\n ctx.playReaction?.(s.card, 'empathize');\n }\n ctx.trackEvent(ctx.campaign.id, 'clicked', { stepId: win ? 'win' : 'lose' });\n spinning = false;\n };\n\n // Stop reels L→R after a minimum spin, with an anticipation hold before the last.\n const stopReel = (i: number): void => {\n reels[i].stop(target[i], () => {\n ctx.haptic?.(10);\n if (i < reels.length - 1) window.setTimeout(() => stopReel(i + 1), i === reels.length - 2 ? 560 : 320);\n else finish();\n });\n };\n window.setTimeout(() => stopReel(0), 620);\n };\n}\n\n// Dispatcher — maps a game sub_type to its renderer. Returns true if handled.\nconst GAME_RENDERERS: Record<string, (ctx: RenderContext) => void> = {\n streak: renderStreak,\n milestone: renderMilestone,\n challenge: renderChallenge,\n achievements: renderAchievements,\n leaderboard: renderLeaderboard,\n check_in: renderCheckIn,\n stamp_card: renderStampCard,\n mystery_box: renderMysteryBox,\n tier_ladder: renderTierLadder,\n lucky_draw: renderLuckyDraw,\n slot_machine: renderSlotMachine,\n};\n\nexport const GAME_SUB_TYPES = Object.keys(GAME_RENDERERS);\n\nexport function renderGame(ctx: RenderContext, subType: string): boolean {\n const fn = GAME_RENDERERS[subType];\n if (!fn) return false;\n fn(ctx);\n return true;\n}\n","/**\n * Standalone video hero (\"Video Banner\", 2026-06-14) — a prominent\n * muted-autoplay-loop video with an unmute pill, optional poster, and\n * title/body/CTA either OVERLAID on the video or BELOW it. Two surfaces:\n * - inline (renders into a `[data-aegis-slot]` anchor) — the Video Banner\n * - overlay (centred hero card) — the takeover\n * Distinct from `pip` (small floating corner) and `full_screen` (full-bleed\n * takeover). Reuses interactive_config.video_url + image_url (poster) + the\n * campaign title/body/button_text/action_url.\n *\n * interactive_config:\n * video_url (the muted-inline video) · video_aspect '16:9'|'9:16'|'1:1'|'4:5'\n * video_caption 'overlay'|'below' · video_autoplay (default true) · video_loop (default true)\n */\n\nimport { lucideSvg } from '../lucide-svg';\nimport type { RenderContext } from './types';\n\nconst ASPECT: Record<string, string> = { '16:9': '16 / 9', '9:16': '9 / 16', '1:1': '1 / 1', '4:5': '4 / 5' };\n\nfunction buildVideo(ctx: RenderContext, aspect: string, autoplay: boolean, loop: boolean): HTMLElement {\n const { campaign, sanitizeUrl } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const videoUrl = sanitizeUrl((ic.video_url as string) || (campaign as unknown as Record<string, unknown>).video_url as string || '');\n const poster = campaign.image_url ? sanitizeUrl(campaign.image_url) : null;\n\n const wrap = document.createElement('div');\n wrap.style.cssText = `position:relative;width:100%;aspect-ratio:${ASPECT[aspect] || ASPECT['16:9']};background:#0b1220;border-radius:14px;overflow:hidden;`;\n\n if (videoUrl) {\n const vid = document.createElement('video');\n vid.src = videoUrl;\n vid.muted = true;\n vid.loop = loop;\n vid.autoplay = autoplay;\n vid.playsInline = true;\n vid.setAttribute('playsinline', '');\n if (poster) vid.poster = poster;\n vid.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;object-fit:cover;';\n wrap.appendChild(vid);\n if (autoplay) void vid.play?.().catch(() => {});\n\n // Watch-completion — fired ONCE when the viewer reaches ~90% (works even\n // with loop=true, where 'ended' never fires). Engagement-funnel + timeline\n // signal (no reward). Threshold avoids counting a 1-second glance.\n let watchedFired = false;\n const onProgress = () => {\n if (watchedFired || !vid.duration || !isFinite(vid.duration)) return;\n if (vid.currentTime / vid.duration >= 0.9) {\n watchedFired = true;\n void ctx.submitResponse?.('video_complete', { duration_s: Math.round(vid.duration) });\n }\n };\n vid.addEventListener('timeupdate', onProgress);\n vid.addEventListener('ended', () => {\n if (!watchedFired) { watchedFired = true; void ctx.submitResponse?.('video_complete', { duration_s: Math.round(vid.duration || 0) }); }\n });\n\n // Unmute pill (browsers require muted autoplay; let the viewer turn sound on).\n const pill = document.createElement('button');\n pill.type = 'button';\n pill.innerHTML = lucideSvg('volume-x', { size: 14 });\n pill.setAttribute('aria-label', 'Unmute');\n pill.style.cssText = 'position:absolute;bottom:10px;right:10px;z-index:2;width:34px;height:34px;border:none;border-radius:999px;background:rgba(0,0,0,0.45);color:#fff;font-size:14px;cursor:pointer;';\n pill.addEventListener('click', (e) => {\n e.stopPropagation();\n vid.muted = !vid.muted;\n pill.innerHTML = lucideSvg(vid.muted ? 'volume-x' : 'volume-2', { size: 14 });\n pill.setAttribute('aria-label', vid.muted ? 'Unmute' : 'Mute');\n if (!vid.muted) void vid.play?.().catch(() => {});\n });\n wrap.appendChild(pill);\n } else if (poster) {\n const img = document.createElement('img');\n img.src = poster; img.alt = '';\n img.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;object-fit:cover;';\n wrap.appendChild(img);\n } else {\n wrap.style.background = 'linear-gradient(160deg,#1f2937,#0f172a)';\n }\n return wrap;\n}\n\nfunction caption(ctx: RenderContext, accent: string, onCard: boolean): HTMLElement | null {\n const { campaign } = ctx;\n if (!campaign.title && !campaign.body && !campaign.button_text && !campaign.action_url) return null;\n const box = document.createElement('div');\n box.style.cssText = onCard\n ? 'position:absolute;left:0;right:0;bottom:0;z-index:2;padding:18px 16px 16px;color:#fff;text-align:left;background:linear-gradient(to top,rgba(0,0,0,0.72),rgba(0,0,0,0) 80%);'\n : 'padding:14px 4px 2px;text-align:left;color:#0f172a;';\n if (campaign.title) {\n const t = document.createElement('div');\n t.textContent = campaign.title;\n t.style.cssText = `font-family:'Urbanist',system-ui,sans-serif;font-size:17px;font-weight:800;letter-spacing:-0.01em;line-height:1.2;${onCard ? 'text-shadow:0 1px 8px rgba(0,0,0,0.4);' : ''}`;\n box.appendChild(t);\n }\n if (campaign.body) {\n const b = document.createElement('div');\n b.textContent = campaign.body;\n b.style.cssText = `font-size:13px;line-height:1.45;margin-top:3px;${onCard ? 'opacity:0.92;text-shadow:0 1px 6px rgba(0,0,0,0.4);' : 'opacity:0.7;'}`;\n box.appendChild(b);\n }\n const label = campaign.button_text || (campaign.action_url ? 'Watch now' : '');\n if (label) {\n const cta = document.createElement('button');\n cta.type = 'button';\n cta.className = 'aegis-cta';\n cta.textContent = label;\n cta.style.cssText = `margin-top:12px;padding:10px 22px;border:none;border-radius:999px;background:${onCard ? '#fff' : accent};color:${onCard ? '#0f172a' : '#fff'};font-size:13px;font-weight:700;cursor:pointer;`;\n cta.addEventListener('click', (e) => {\n e.stopPropagation();\n ctx.trackEvent(campaign.id, 'clicked');\n const url = (campaign.action_url as string) || '';\n if (url) { if (ctx.navigate) ctx.navigate(url, 'cta'); else window.location.href = url; }\n });\n box.appendChild(cta);\n }\n return box;\n}\n\nfunction readOpts(ctx: RenderContext) {\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n return {\n accent: ctx.sanitizeColor((ctx.campaign.background_color as string) || '#4169e1'),\n aspect: (ic.video_aspect as string) || '16:9',\n overlayCaption: (ic.video_caption as string) !== 'below',\n autoplay: ic.video_autoplay !== false,\n loop: ic.video_loop !== false,\n };\n}\n\n/** Overlay entry — a centred video hero card. */\nexport function renderVideo(ctx: RenderContext): void {\n const { campaign, trackEvent, addAnimationStyles } = ctx;\n addAnimationStyles();\n const o = readOpts(ctx);\n\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-video-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n overlay.style.cssText = 'position:fixed;inset:0;z-index:100000;background:rgba(15,23,42,0.6);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;padding:16px;font-family:\"Urbanist\",system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;animation:aegisFadeIn 0.25s ease;';\n\n const card = document.createElement('div');\n card.style.cssText = `position:relative;width:100%;max-width:${o.aspect === '9:16' ? 320 : 460}px;background:#fff;border-radius:18px;overflow:hidden;box-shadow:0 16px 48px rgba(0,0,0,0.28);animation:aegisScaleIn 0.3s ease;`;\n\n const media = buildVideo(ctx, o.aspect, o.autoplay, o.loop);\n if (o.overlayCaption) { const c = caption(ctx, o.accent, true); if (c) media.appendChild(c); }\n card.appendChild(media);\n if (!o.overlayCaption) { const c = caption(ctx, o.accent, false); if (c) { c.style.padding = '14px 16px 16px'; card.appendChild(c); } }\n\n const close = document.createElement('button');\n close.type = 'button'; close.innerHTML = lucideSvg('x', { size: 15 }); close.setAttribute('aria-label', 'Close');\n close.style.cssText = 'position:absolute;top:10px;left:10px;z-index:3;width:32px;height:32px;border:none;border-radius:999px;background:rgba(0,0,0,0.4);color:#fff;font-size:15px;cursor:pointer;';\n close.addEventListener('click', () => { trackEvent(campaign.id, 'dismissed'); overlay.remove(); });\n card.appendChild(close);\n overlay.addEventListener('click', (e) => { if (e.target === overlay) { trackEvent(campaign.id, 'dismissed'); overlay.remove(); } });\n\n overlay.appendChild(card);\n document.body.appendChild(overlay);\n}\n\n/** Slot/embedded entry — the inline Video Banner. */\nexport function renderVideoInline(ctx: RenderContext, target: HTMLElement | null): boolean {\n if (!target) { renderVideo(ctx); return true; }\n ctx.addAnimationStyles();\n const o = readOpts(ctx);\n const banner = document.createElement('div');\n banner.className = 'aegis-in-app-video-banner';\n banner.setAttribute('data-campaign-id', ctx.campaign.id);\n banner.style.cssText = 'width:100%;font-family:\"Urbanist\",system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;';\n const media = buildVideo(ctx, o.aspect, o.autoplay, o.loop);\n if (o.overlayCaption) { const c = caption(ctx, o.accent, true); if (c) media.appendChild(c); }\n banner.appendChild(media);\n if (!o.overlayCaption) { const c = caption(ctx, o.accent, false); if (c) banner.appendChild(c); }\n target.appendChild(banner);\n return true;\n}\n","/**\n * Hero / banner renderer — the storefront hero, now a SELF-DESCRIBING in-app\n * campaign instead of a per-surface native component. The SDK injects it into a\n * plain `[data-aegis-slot=\"hero\"]` so the SAME campaign renders identically on\n * ACS, a 3rd-party website, the Bill page, etc. (ACS is the testbed.)\n *\n * Data shape (`interactive_config`):\n * cards: Array<{ media_url|image_url, media_type, headline|title,\n * subhead|body, cta_label|cta_text, cta_target|cta_url,\n * image_url_mobile? }>\n * chrome: { variant, aspect_ratio, radius, overlay, text_position,\n * cta_style, autoplay_ms, loop, accent } // how it DRAWS\n *\n * `chrome` is the portable \"look\" so the operator authors once and it renders\n * surface-framed everywhere. Ported from the cashier-portal <HeroBanner> visual.\n */\nimport type { RenderContext } from './types';\n\ninterface HeroCard {\n media_url?: string;\n image_url?: string;\n image_url_mobile?: string;\n video_url?: string; // CardsListEditor shape (image_url + optional video_url)\n media_type?: string; // 'image' | 'video' (backfill shape)\n headline?: string;\n title?: string;\n subhead?: string;\n body?: string;\n cta_label?: string;\n cta_text?: string;\n cta_target?: { type?: string; value?: string } | null;\n cta_url?: string;\n}\n\ninterface HeroChrome {\n variant?: string; // hero_fullbleed | announcement_bar | sticky_strip\n aspect_ratio?: string; // '16:9' | '4:3' | '21:9' | 'auto'\n radius?: string; // 'none' | 'lg' | 'xl' | '2xl'\n overlay?: string; // 'none' | 'bottom-scrim' | 'full-scrim'\n text_position?: string; // 'bottom-left' | 'center' | 'bottom-center'\n cta_style?: string; // 'pill' | 'underline' | 'none'\n autoplay_ms?: number;\n loop?: boolean;\n accent?: string; // 'brand' | <hex>\n}\n\nconst HEX = /^#[0-9a-fA-F]{3,8}$/;\nfunction safeColor(c: string | undefined, fallback: string): string {\n return c && HEX.test(c) ? c : fallback;\n}\nconst ASPECT: Record<string, string> = { '16:9': '16 / 9', '4:3': '4 / 3', '21:9': '21 / 9' };\n\n// Desktop-only hero overrides — injected once. Mobile keeps the rounded\n// corners + aspect-driven height; ≥768px goes full-bleed (square, 45–70vh).\nfunction _injectHeroResponsiveCss(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById('aegis-hero-style')) return;\n const style = document.createElement('style');\n style.id = 'aegis-hero-style';\n style.textContent =\n '.aegis-hero-root{border-radius:16px;}' +\n '@media (min-width:768px){.aegis-hero-root{border-radius:0;min-height:45vh;max-height:70vh;}}';\n document.head.appendChild(style);\n}\n\nfunction cardHeadline(c: HeroCard): string { return c.headline ?? c.title ?? ''; }\nfunction cardSub(c: HeroCard): string { return c.subhead ?? c.body ?? ''; }\nfunction cardCta(c: HeroCard): string { return c.cta_label ?? c.cta_text ?? ''; }\nfunction cardUrl(c: HeroCard): string {\n if (c.cta_url) return c.cta_url;\n const t = c.cta_target;\n if (t && t.value) return t.type === 'category' ? `?category=${encodeURIComponent(t.value)}` : t.value;\n return '';\n}\n\n/**\n * Render the hero into `target` (the slot element). Returns false (no-op) when\n * there are no cards so the caller can keep the slot's SSR default/skeleton.\n */\nexport function renderHeroInline(ctx: RenderContext, target: HTMLElement): boolean {\n const { campaign, sanitizeUrl, trackEvent } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const cards = (Array.isArray(ic.cards) ? ic.cards : []) as HeroCard[];\n if (cards.length === 0) return false;\n\n const chrome = (ic.chrome || {}) as HeroChrome;\n const accent = safeColor(\n chrome.accent && chrome.accent !== 'brand' ? chrome.accent : (campaign.background_color as string),\n '#4169e1',\n );\n const variant = chrome.variant ?? 'hero_fullbleed';\n\n // ── Announcement bar — thin one-line strip (no media bleed) ──────────\n if (variant === 'announcement_bar') {\n const c = cards[0];\n const bar = document.createElement('div');\n bar.style.cssText = `display:flex;align-items:center;justify-content:center;gap:10px;background:${accent};color:#fff;padding:8px 14px;font:600 13px/1.3 Inter,system-ui,sans-serif;text-align:center;`;\n bar.textContent = [cardHeadline(c), cardSub(c)].filter(Boolean).join(' · ');\n const url = sanitizeUrl(cardUrl(c));\n if (url && cardCta(c)) {\n const a = document.createElement('span');\n a.style.cssText = 'text-decoration:underline;cursor:pointer;white-space:nowrap;';\n a.textContent = cardCta(c);\n a.addEventListener('click', () => { trackEvent(campaign.id, 'clicked'); ctx.navigate?.(url); });\n bar.appendChild(a);\n }\n target.appendChild(bar);\n trackEvent(campaign.id, 'impression');\n return true;\n }\n\n // ── Hero (full-bleed carousel) — the ported <HeroBanner> visual ──────\n // Responsive look (DESKTOP-only changes; mobile keeps its rounded corners +\n // aspect-driven height, set inline below). The class drives the desktop\n // overrides via an injected media query (see _injectHeroResponsiveCss):\n // mobile → rounded 16px, height from aspect-ratio\n // ≥768px → square corners (full-bleed), min-height 45vh, max-height 70vh\n _injectHeroResponsiveCss();\n const root = document.createElement('div');\n root.className = 'aegis-hero-root';\n root.style.cssText = `position:relative;width:100%;overflow:hidden;box-shadow:0 1px 2px rgba(16,24,40,0.06);${chrome.aspect_ratio && ASPECT[chrome.aspect_ratio] ? `aspect-ratio:${ASPECT[chrome.aspect_ratio]};` : 'min-height:300px;'}`;\n root.setAttribute('data-campaign-id', campaign.id);\n\n const track = document.createElement('div');\n track.style.cssText = 'display:flex;height:100%;width:100%;transition:transform 0.5s ease;';\n root.appendChild(track);\n\n const overlayCss =\n chrome.overlay === 'none' ? ''\n : chrome.overlay === 'full-scrim' ? 'background:linear-gradient(0deg,rgba(0,0,0,0.55),rgba(0,0,0,0.25));'\n : 'background:linear-gradient(to top right,rgba(0,0,0,0.7),rgba(0,0,0,0.25) 45%,transparent);';\n const justify = chrome.text_position === 'center' ? 'center' : 'flex-end';\n const align = chrome.text_position === 'bottom-left' || !chrome.text_position ? 'flex-start' : 'center';\n\n cards.forEach((c, i) => {\n const slide = document.createElement('div');\n slide.style.cssText = 'position:relative;flex:0 0 100%;width:100%;height:100%;min-height:inherit;background:#f1f5f9;';\n // Handle BOTH card shapes: CardsListEditor ({image_url, video_url}) and the\n // banner backfill ({media_url, media_type}).\n const videoUrl = sanitizeUrl(c.video_url ?? '');\n const isVideo = !!videoUrl || c.media_type === 'video';\n const mediaUrl = isVideo\n ? (videoUrl || sanitizeUrl(c.media_url ?? ''))\n : sanitizeUrl(c.image_url ?? c.media_url ?? '');\n if (mediaUrl) {\n if (isVideo) {\n const v = document.createElement('video');\n v.src = mediaUrl; v.autoplay = true; v.muted = true; v.loop = true; v.playsInline = true;\n v.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;object-fit:cover;';\n slide.appendChild(v);\n } else {\n const img = document.createElement('div');\n img.style.cssText = `position:absolute;inset:0;background:url(\"${mediaUrl}\") center/cover no-repeat;`;\n slide.appendChild(img);\n }\n }\n if (overlayCss) {\n const scrim = document.createElement('div');\n scrim.style.cssText = `position:absolute;inset:0;${overlayCss}`;\n slide.appendChild(scrim);\n }\n const content = document.createElement('div');\n content.style.cssText = `position:absolute;inset:0;display:flex;flex-direction:column;justify-content:${justify};align-items:${align};gap:6px;padding:24px;text-align:${align === 'center' ? 'center' : 'left'};`;\n const h = cardHeadline(c);\n if (h) {\n const head = document.createElement('h2');\n head.textContent = h;\n head.style.cssText = 'margin:0;color:#fff;font:800 24px/1.2 Inter Tight,Inter,system-ui,sans-serif;letter-spacing:-0.02em;text-shadow:0 1px 8px rgba(0,0,0,0.35);max-width:90%;';\n content.appendChild(head);\n }\n const s = cardSub(c);\n if (s) {\n const sub = document.createElement('p');\n sub.textContent = s;\n sub.style.cssText = 'margin:0;color:rgba(255,255,255,0.92);font:500 14px/1.4 Inter,system-ui,sans-serif;text-shadow:0 1px 6px rgba(0,0,0,0.3);max-width:90%;';\n content.appendChild(sub);\n }\n const cta = cardCta(c);\n const url = sanitizeUrl(cardUrl(c));\n if (cta && chrome.cta_style !== 'none') {\n const btn = document.createElement('button');\n btn.textContent = cta;\n btn.style.cssText = chrome.cta_style === 'underline'\n ? `margin-top:6px;background:none;border:none;color:#fff;font:700 14px Inter,system-ui,sans-serif;text-decoration:underline;cursor:pointer;padding:0;`\n : `margin-top:8px;background:${accent};color:#fff;border:none;border-radius:999px;padding:9px 18px;font:700 13px Inter,system-ui,sans-serif;cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,0.2);`;\n btn.addEventListener('click', () => {\n trackEvent(campaign.id, 'clicked', { stepId: `card_${i}` });\n if (url) ctx.navigate?.(url);\n });\n content.appendChild(btn);\n }\n slide.appendChild(content);\n track.appendChild(slide);\n });\n\n const dotEls: HTMLSpanElement[] = [];\n let activeIdx = 0;\n const goto = (idx: number) => {\n activeIdx = ((idx % cards.length) + cards.length) % cards.length;\n track.style.transform = `translateX(-${activeIdx * 100}%)`;\n dotEls.forEach((d, i) => {\n d.style.width = i === activeIdx ? '18px' : '6px';\n d.style.background = i === activeIdx ? accent : 'rgba(255,255,255,0.7)';\n });\n };\n\n // Dots\n const dots = document.createElement('div');\n dots.style.cssText = 'position:absolute;bottom:8px;left:50%;transform:translateX(-50%);display:flex;gap:6px;z-index:2;';\n if (cards.length > 1) {\n cards.forEach((_, i) => {\n const dot = document.createElement('span');\n dot.style.cssText = 'width:6px;height:6px;border-radius:999px;background:rgba(255,255,255,0.7);transition:all 0.25s;cursor:pointer;';\n dot.addEventListener('click', () => goto(i));\n dotEls.push(dot);\n dots.appendChild(dot);\n });\n root.appendChild(dots);\n }\n\n target.appendChild(root);\n goto(0);\n\n // Autoplay\n if (cards.length > 1 && chrome.loop !== false) {\n const ms = typeof chrome.autoplay_ms === 'number' && chrome.autoplay_ms >= 1500 ? chrome.autoplay_ms : 4000;\n let timer = window.setInterval(() => goto(activeIdx + 1), ms);\n root.addEventListener('mouseenter', () => window.clearInterval(timer));\n root.addEventListener('mouseleave', () => { timer = window.setInterval(() => goto(activeIdx + 1), ms); });\n }\n\n trackEvent(campaign.id, 'impression');\n return true;\n}\n","/**\n * AegisChat — the website / in-app \"Shopping Assistant\" launcher (Track A).\n *\n * A floating bottom-right bubble that expands into a chat panel, letting a\n * customer converse with the SAME channel-neutral chat agent that powers\n * WhatsApp. This is pure transport + surface — it does NOT contain any agent\n * intelligence. Messages go to the gateway (`POST /v1/chat/message` →\n * `chatbot.inbound channel='web'`); replies arrive as a thin `chat.message`\n * SSE nudge (per-session, ticket-authed) that triggers a re-fetch of the\n * rendered thread from `GET /v1/chat/history`. REST polling is the fallback\n * when the panel is closed or SSE drops.\n *\n * Identity: true-anonymous visitors work — we send the SDK's `anonymous_id`\n * (first-party cookie) and the server resolves/creates a stable contact. The\n * server returns the opaque `contact_id`, which we echo on every subsequent\n * message so the server hits the indexed PK path (and the anon→known stitch\n * upgrades the contact when the customer later identifies).\n *\n * Rendering reuses the `web` payload the backend already emits\n * (`chat_response_renderer.py`): text + `{type:'product_carousel', products:[…]}`.\n */\n\nimport { lucideSvg } from '../inapp/lucide-svg';\nimport { Storage } from '../utils/storage';\n\nexport interface AegisChatConfig {\n writeKey: string;\n /** Gateway origin (the single public SDK surface). Falls back to same-origin. */\n apiHost?: string;\n organizationId?: string;\n /** Known contact id when the visitor is already identified; else anonymous. */\n contactId?: string;\n /** 'web' (browser) or 'in_app' (mobile webview). Opaque to the agent. */\n channel?: 'web' | 'in_app';\n debugMode?: boolean;\n /** Launcher header title shown to the customer. Default \"Shopping Assistant\";\n * the storefront passes \"Ask {Brand}\". */\n title?: string;\n /** First assistant greeting bubble + attention tooltip text. */\n greeting?: string;\n /** Accent colour for the bubble + customer bubbles. Defaults to brand. */\n accentColor?: string;\n /** Bubble icon style. 'sparkle' (default, signals AI) | 'chat' (plain bubble)\n * | 'logo' (renders `logoUrl`). No robot — brand-friendly by design. */\n icon?: 'sparkle' | 'chat' | 'logo';\n /** Image URL used when `icon: 'logo'` (brand logo / mascot). */\n logoUrl?: string;\n /** Corner the launcher docks to. */\n position?: 'bottom-right' | 'bottom-left';\n /** Surface style for the accent-filled chrome — the header AND the customer's\n * sent message bubbles. 'solid' (default) = full accent fill + white text.\n * 'tinted' = a soft accent wash over white + dark text (matches the\n * storefront's tinted header band). Campaign-driven (`interactive_config.chat_theme`). */\n surfaceStyle?: 'solid' | 'tinted';\n /** Chat agent persona key bound to this launcher (campaign-driven). Sent to\n * the backend on /v1/chat/message; honored server-side (Active Web Chat\n * Phase E). Absent = the tenant's default persona. */\n agentPersona?: string;\n /** Quick-reply chips offered under the greeting (campaign-driven). */\n quickReplies?: string[];\n /** Fired when the panel transitions closed→open — a chat session begins.\n * Used by the Active Web Chat renderer to report a campaign engagement. */\n onSessionStart?: () => void;\n /** Fired when the panel transitions open→closed — the session ends. */\n onSessionEnd?: () => void;\n /** Fired after a customer message is accepted by the server. */\n onMessageSent?: () => void;\n /** Presentation: 'bubble' (default, floating launcher) or 'nav' (headless —\n * no bubble; the panel mounts but opens only via openPanel() / the public\n * `aegis.chat.open()` API / a host trigger). Use 'nav' when the host owns\n * the entry point (e.g. a mobile bottom-nav \"Ask\" button). */\n displayMode?: 'bubble' | 'nav';\n}\n\ninterface ChatHistoryMessage {\n id: string;\n sender: 'customer' | 'assistant' | 'system';\n role: string;\n content: string;\n media_url?: string | null;\n media_type?: string | null;\n interactive?: Array<Record<string, unknown>> | null;\n created_at?: string | null;\n}\n\ninterface IngestResponse {\n accepted: boolean;\n contact_id: string;\n is_new_contact: boolean;\n message_id: string;\n channel: string;\n sse_ticket: string;\n sse_ticket_expires_at: number;\n}\n\nconst STYLE_ID = 'aegis-chat-styles';\nconst POLL_INTERVAL_MS = 5000;\n\n// ── Single live-launcher registry (canonical public open API) ────────────────\n// There is at most ONE launcher per page (the Phase-F strangler ensures only the\n// campaign path OR the legacy runtime path mounts, never both). Whichever mounts\n// self-registers here on initialize(), so `aegis.chat.open()` and host buttons\n// (e.g. the mobile bottom-nav \"Ask\") target the same instance without any caller\n// needing to hold a ref — and without a second registry.\nlet currentLauncher: AegisChat | null = null;\n/** The single mounted launcher, or null if chat isn't active on this page. */\nexport function getCurrentLauncher(): AegisChat | null {\n return currentLauncher;\n}\n/** Open the chat panel (no-op if no launcher is mounted). */\nexport function openChat(prefill?: string): void {\n currentLauncher?.openPanel(prefill);\n}\n/** Close the chat panel. */\nexport function closeChat(): void {\n currentLauncher?.closePanel();\n}\n/** Delay before the bubble pulses + the greeting tooltip appears (ms). */\nconst ATTENTION_DELAY_MS = 4500;\n\nexport class AegisChat {\n private readonly writeKey: string;\n private readonly organizationId?: string;\n private readonly apiHost: string;\n private contactId?: string;\n private readonly channel: 'web' | 'in_app';\n private readonly debugMode: boolean;\n private readonly title: string;\n private readonly greeting: string;\n private readonly accent: string;\n private readonly icon: 'sparkle' | 'chat' | 'logo';\n private readonly logoUrl?: string;\n private readonly position: 'bottom-right' | 'bottom-left';\n private readonly surfaceStyle: 'solid' | 'tinted';\n private readonly displayMode: 'bubble' | 'nav';\n private readonly agentPersona?: string;\n private readonly quickReplies?: string[];\n private readonly onSessionStart?: () => void;\n private readonly onSessionEnd?: () => void;\n private readonly onMessageSent?: () => void;\n\n private anonymousId?: string;\n /** Per-session SSE ticket from the last /message response. Authorizes both\n * the SSE subscription AND the contact-scoped /history read (the server\n * requires it — chat history is not readable by contact_id alone). */\n private sseTicket?: string;\n /** True once a resume attempt ran this page-load (one /session call max). */\n private sessionResumed = false;\n private open = false;\n private initialized = false;\n\n // DOM\n private root?: HTMLDivElement;\n private bubble?: HTMLButtonElement;\n private panel?: HTMLDivElement;\n private thread?: HTMLDivElement;\n private input?: HTMLTextAreaElement;\n private unread = 0;\n private badge?: HTMLSpanElement;\n\n // Transport\n private eventSource?: EventSource;\n private pollTimer?: ReturnType<typeof setInterval>;\n private renderedIds = new Set<string>();\n /** Contents of optimistically-rendered customer messages awaiting their\n * server echo — used to dedup the history refetch (double-message fix). */\n private pendingEcho: string[] = [];\n private typingEl?: HTMLDivElement;\n private greetingTip?: HTMLDivElement;\n private attentionTimer?: ReturnType<typeof setTimeout>;\n private hasInteracted = false;\n\n constructor(config: AegisChatConfig) {\n this.writeKey = config.writeKey;\n this.organizationId = config.organizationId;\n this.apiHost = (config.apiHost ?? '').replace(/\\/$/, '');\n this.contactId = config.contactId;\n this.channel = config.channel ?? 'web';\n this.debugMode = config.debugMode ?? false;\n this.title = config.title ?? 'Shopping Assistant';\n this.greeting = config.greeting ?? 'Hi! 👋 How can I help you today?'; // eslint-disable-line no-restricted-syntax -- chat greeting copy (message content)\n this.accent = config.accentColor ?? '#4169e1';\n this.surfaceStyle = config.surfaceStyle === 'tinted' ? 'tinted' : 'solid';\n this.icon = config.icon ?? 'sparkle';\n this.logoUrl = config.logoUrl;\n this.position = config.position ?? 'bottom-right';\n this.displayMode = config.displayMode ?? 'bubble';\n this.agentPersona = config.agentPersona;\n this.quickReplies = config.quickReplies;\n this.onSessionStart = config.onSessionStart;\n this.onSessionEnd = config.onSessionEnd;\n this.onMessageSent = config.onMessageSent;\n this.anonymousId = this.readAnonId();\n // Restore a prior contact so a returning visitor's thread loads on open\n // (history is contact + ticket scoped — see resumeSession()).\n this.contactId = this.contactId ?? this.readStoredContactId();\n }\n\n // ── Lifecycle ────────────────────────────────────────────────────────────\n\n initialize(): void {\n if (this.initialized || typeof document === 'undefined') return;\n this.initialized = true;\n this.injectStyles();\n this.mount();\n currentLauncher = this; // canonical single-instance registry for aegis.chat.open()\n // Attention pulse/tooltip is a bubble affordance — skip in headless 'nav' mode.\n if (this.displayMode !== 'nav') this.scheduleAttention();\n }\n\n /** Called by the runtime when the visitor identifies. */\n updateContactId(contactId: string): void {\n if (!contactId || contactId === this.contactId) return;\n this.contactId = contactId;\n }\n\n /** Programmatic open (used by the proactive TriggerEngine wiring). */\n openPanel(prefill?: string): void {\n if (!this.panel) this.initialize();\n if (this.open) return;\n this.open = true;\n this.hasInteracted = true;\n // Closed→open transition — a chat session begins (campaign engagement).\n try { this.onSessionStart?.(); } catch { /* host callback — never block UI */ }\n this.dismissAttention();\n this.panel?.classList.add('aegis-chat-panel--open');\n this.bubble?.classList.add('aegis-chat-bubble--hidden');\n this.clearUnread();\n if (prefill && this.input) this.input.value = prefill;\n // Restore a returning visitor's thread immediately on open: resumeSession()\n // mints a ticket for an existing contact (no message sent) and loads history.\n // Falls back to refreshHistory() when we already hold a ticket this session.\n void this.resumeSession();\n // SSE needs a per-session ticket, which arrives from resumeSession()/the\n // first /message — so on open we poll to bridge that gap. As soon as SSE\n // connects, `onopen` stops polling. While the panel is CLOSED there is\n // neither SSE nor polling — zero background load.\n this.startPolling();\n this.input?.focus();\n }\n\n closePanel(): void {\n const wasOpen = this.open;\n this.open = false;\n this.panel?.classList.remove('aegis-chat-panel--open');\n this.bubble?.classList.remove('aegis-chat-bubble--hidden');\n this.disconnectSSE();\n this.stopPolling();\n // Open→closed transition — the session ends. Guarded so a redundant\n // closePanel() (e.g. teardown after an already-closed panel) doesn't\n // double-report.\n if (wasOpen) {\n try { this.onSessionEnd?.(); } catch { /* host callback — never block UI */ }\n }\n }\n\n destroy(): void {\n this.disconnectSSE();\n this.stopPolling();\n this.root?.remove();\n this.initialized = false;\n if (currentLauncher === this) currentLauncher = null;\n }\n\n // ── Send ─────────────────────────────────────────────────────────────────\n\n private async send(text: string, media?: { url: string; type: string }): Promise<void> {\n const caption = text.trim();\n if (!caption && !media) return;\n // The server requires a non-empty message_text; for a media-only message\n // synthesize a short caption so the thread reads naturally.\n const message = caption || (media ? (media.type === 'image' ? 'Sent a photo 📷' : 'Sent a file 📎') : ''); // eslint-disable-line no-restricted-syntax -- chat message content (media placeholder)\n // Optimistic echo so the customer sees their message immediately. Track the\n // content so the history refetch reconciles (adopts) the server copy instead\n // of rendering it a second time.\n this.appendBubble({\n id: `local_${Date.now()}`, sender: 'customer', role: 'user', content: message,\n media_url: media?.url ?? null, media_type: media?.type ?? null,\n });\n this.pendingEcho.push(message);\n if (this.input) {\n this.input.value = '';\n this.input.style.height = 'auto';\n }\n // Assistant is working — show the typing indicator until its reply lands.\n this.showTyping();\n\n try {\n const res = await fetch(`${this.apiHost}/v1/chat/message`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'X-Aegis-Write-Key': this.writeKey },\n body: JSON.stringify({\n message_text: message,\n channel: this.channel,\n anonymous_id: this.anonymousId,\n contact_id: this.contactId,\n media_url: media?.url,\n media_type: media?.type,\n // Active Web Chat — binds the campaign-configured persona. Optional;\n // the backend falls back to the tenant default if unknown/absent\n // (honored server-side in Phase E). JSON.stringify drops it if absent.\n agent_persona: this.agentPersona,\n }),\n keepalive: true,\n });\n if (!res.ok) {\n this.log('send failed', res.status);\n this.appendSystem('Sorry — your message could not be delivered. Please try again.');\n return;\n }\n const data = (await res.json()) as IngestResponse;\n try { this.onMessageSent?.(); } catch { /* host callback — never block UI */ }\n // Echo the resolved contact id on subsequent sends (indexed PK path) and\n // (re)connect SSE with the fresh per-session ticket. The ticket also\n // authorizes the /history read, so retain it.\n this.contactId = data.contact_id;\n this.persistContactId(data.contact_id);\n this.sseTicket = data.sse_ticket;\n this.connectSSE(data.sse_ticket);\n // Now that we hold a ticket, pull the rendered thread (the optimistic echo\n // covered the gap until here).\n void this.refreshHistory();\n } catch (err) {\n this.log('send error', err);\n this.appendSystem('Sorry — your message could not be delivered. Please try again.');\n }\n }\n\n // ── Media attachments ─────────────────────────────────────────────────────\n\n /** Validate + upload a picked file, then send it as a media message. */\n private async handleFile(file: File): Promise<void> {\n const allowed = new Set(['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'application/pdf']);\n if (!allowed.has(file.type)) {\n this.appendSystem('That file type isn’t supported (images or PDF only).');\n return;\n }\n if (file.size > 8 * 1024 * 1024) {\n this.appendSystem('That file is too large (max 8 MB).');\n return;\n }\n try {\n const data = await this.fileToBase64(file);\n const res = await fetch(`${this.apiHost}/v1/chat/upload`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'X-Aegis-Write-Key': this.writeKey },\n body: JSON.stringify({ data, content_type: file.type, filename: file.name }),\n });\n if (!res.ok) {\n this.log('upload failed', res.status);\n this.appendSystem('Sorry — that attachment couldn’t be uploaded.');\n return;\n }\n const out = (await res.json()) as { media_url: string; media_type: string };\n // Caption = whatever the customer already typed (sent alongside the media).\n await this.send(this.input?.value ?? '', { url: out.media_url, type: out.media_type });\n } catch (err) {\n this.log('upload error', err);\n this.appendSystem('Sorry — that attachment couldn’t be uploaded.');\n }\n }\n\n /** Read a File as bare base64 (strip the `data:…;base64,` prefix). */\n private fileToBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = String(reader.result || '');\n const comma = result.indexOf(',');\n resolve(comma >= 0 ? result.slice(comma + 1) : result);\n };\n reader.onerror = () => reject(reader.error);\n reader.readAsDataURL(file);\n });\n }\n\n // ── Reply transport: SSE nudge + history refetch (poll fallback) ──────────\n\n private connectSSE(ticket?: string): void {\n // Hard guarantee: SSE only exists while the panel is open. Guards the\n // async race where a send()'s /message response resolves AFTER the user\n // closed the panel — without this, connectSSE would reopen a stream on a\n // closed panel and leak a connection.\n if (!this.open) return;\n if (typeof EventSource === 'undefined' || !this.contactId) return;\n // Mint-on-send: the ticket comes from the /message response. If we don't\n // have one yet (panel opened before first send), we rely on polling until\n // the first reply arrives and a ticket is issued.\n if (!ticket) return;\n this.disconnectSSE();\n const url = new URL('/v1/stream/realtime', this.apiHost || window.location.origin);\n url.searchParams.set('ticket', ticket);\n url.searchParams.set('mode', 'ephemeral');\n // Non-secret routing hint (SSE_PER_CELL_ROUTING_SPEC D1) — lets the gateway\n // proxy to the tenant's own cell's SSE server. The ticket stays the sole\n // credential; the SSE server asserts ticket.org === org.\n if (this.organizationId) url.searchParams.set('org', this.organizationId);\n try {\n this.eventSource = new EventSource(url.toString());\n this.eventSource.onopen = () => {\n // SSE is the live path — stop the REST-poll fallback so we don't run\n // both at once. Polling resumes only if the stream drops (onerror).\n this.stopPolling();\n this.log('SSE connected');\n };\n this.eventSource.addEventListener('chat.message', () => {\n void this.refreshHistory();\n });\n this.eventSource.onerror = () => {\n // Stream dropped — fall back to polling until the browser reconnects\n // (onopen will stop it again).\n this.startPolling();\n };\n } catch (err) {\n this.log('SSE connect failed', err);\n this.startPolling();\n }\n }\n\n private disconnectSSE(): void {\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = undefined;\n }\n }\n\n private startPolling(): void {\n // Same hard guarantee as connectSSE: no background fetch loop unless the\n // panel is open (covers an onerror firing after a close race).\n if (!this.open) return;\n if (this.pollTimer) return;\n this.pollTimer = setInterval(() => {\n void this.refreshHistory();\n }, POLL_INTERVAL_MS);\n }\n\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = undefined;\n }\n }\n\n private async refreshHistory(): Promise<void> {\n if (!this.contactId) return;\n // The server requires the per-session ticket to authorize this contact-\n // scoped read (history is not readable by contact_id alone). Until the first\n // send mints one, there is nothing to fetch — the optimistic echo + local\n // greeting cover the panel, and the post-send refreshHistory() backfills.\n if (!this.sseTicket) return;\n try {\n const url = new URL('/v1/chat/history', this.apiHost || window.location.origin);\n url.searchParams.set('contact_id', this.contactId);\n url.searchParams.set('ticket', this.sseTicket);\n url.searchParams.set('channel', this.channel);\n url.searchParams.set('limit', '50');\n const res = await fetch(url.toString(), {\n method: 'GET',\n headers: { 'X-Aegis-Write-Key': this.writeKey },\n });\n if (!res.ok) return;\n const data = (await res.json()) as { messages: ChatHistoryMessage[] };\n let appended = false;\n for (const msg of data.messages || []) {\n if (this.renderedIds.has(msg.id)) continue;\n // Reconcile the optimistic customer echo: a server customer-message that\n // matches a pending locally-rendered one is the SAME message with a real\n // id — adopt the id and skip the re-render (double-message fix).\n if (msg.sender === 'customer') {\n const idx = this.pendingEcho.indexOf(msg.content || '');\n if (idx !== -1) {\n this.pendingEcho.splice(idx, 1);\n if (msg.id) this.renderedIds.add(msg.id);\n continue;\n }\n }\n // An assistant reply has arrived — drop the typing indicator.\n if (msg.sender === 'assistant') this.hideTyping();\n this.appendBubble(msg);\n appended = true;\n if (msg.sender === 'assistant' && !this.open) this.bumpUnread();\n }\n if (appended) this.scrollToBottom();\n } catch (err) {\n this.log('history fetch error', err);\n }\n }\n\n // ── DOM ──────────────────────────────────────────────────────────────────\n\n /** Bubble glyph by config. Default 'sparkle' (signals AI, brand-friendly —\n * never a robot). 'logo' renders the brand image; falls back to sparkle. */\n private bubbleIconHtml(): string {\n const SPARKLE =\n '<svg width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">' +\n '<path d=\"M12 2.3l1.85 5.0 5.0 1.85-5.0 1.85L12 16l-1.85-5.0L5.15 9.15l5.0-1.85L12 2.3z\"/>' +\n '<path d=\"M18.7 14.3l.78 2.1 2.1.78-2.1.78-.78 2.1-.78-2.1-2.1-.78 2.1-.78.78-2.1z\" opacity=\"0.85\"/></svg>';\n const CHAT =\n '<svg width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">' +\n '<path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/></svg>';\n if (this.icon === 'chat') return CHAT;\n if (this.icon === 'logo' && this.logoUrl && /^https?:\\/\\//i.test(this.logoUrl)) {\n const safe = this.logoUrl.replace(/\"/g, '&quot;');\n return `<img class=\"aegis-chat-bubble-logo\" src=\"${safe}\" alt=\"\" />`;\n }\n return SPARKLE;\n }\n\n private mount(): void {\n const root = document.createElement('div');\n root.className = `aegis-chat-root aegis-chat-root--${this.position}`;\n root.style.setProperty('--aegis-chat-accent', this.accent);\n // Tinted theme: a soft accent wash + dark text for the accent-filled chrome\n // (header + customer bubbles). Solid (default) leaves the fill vars unset so\n // the CSS falls back to the full accent + white text.\n if (this.surfaceStyle === 'tinted') {\n root.style.setProperty('--aegis-chat-fill', 'color-mix(in srgb, var(--aegis-chat-accent,#4169e1) 16%, #ffffff)');\n root.style.setProperty('--aegis-chat-fill-fg', '#0f172a');\n }\n\n // Bubble\n const bubble = document.createElement('button');\n bubble.className = 'aegis-chat-bubble';\n bubble.setAttribute('aria-label', `Open ${this.title}`);\n bubble.innerHTML = this.bubbleIconHtml();\n const badge = document.createElement('span');\n badge.className = 'aegis-chat-badge';\n badge.style.display = 'none';\n bubble.appendChild(badge);\n bubble.addEventListener('click', () => this.openPanel());\n\n // Panel\n const panel = document.createElement('div');\n panel.className = 'aegis-chat-panel';\n panel.setAttribute('role', 'dialog');\n panel.setAttribute('aria-label', this.title);\n\n const header = document.createElement('div');\n header.className = 'aegis-chat-header';\n const titleEl = document.createElement('span');\n titleEl.className = 'aegis-chat-title';\n titleEl.textContent = this.title;\n const closeBtn = document.createElement('button');\n closeBtn.className = 'aegis-chat-close';\n closeBtn.setAttribute('aria-label', 'Close chat');\n closeBtn.innerHTML = lucideSvg('x', { size: 16 });\n closeBtn.addEventListener('click', () => this.closePanel());\n header.appendChild(titleEl);\n header.appendChild(closeBtn);\n\n const thread = document.createElement('div');\n thread.className = 'aegis-chat-thread';\n\n // Composer\n const composer = document.createElement('form');\n composer.className = 'aegis-chat-composer';\n const input = document.createElement('textarea');\n input.className = 'aegis-chat-input';\n input.rows = 1;\n input.placeholder = 'Type a message…';\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n void this.send(input.value);\n }\n });\n // Attach (media) — hidden file input + paperclip trigger.\n const fileInput = document.createElement('input');\n fileInput.type = 'file';\n fileInput.accept = 'image/*,application/pdf';\n fileInput.className = 'aegis-chat-fileinput';\n fileInput.addEventListener('change', () => {\n const f = fileInput.files && fileInput.files[0];\n if (f) void this.handleFile(f);\n fileInput.value = '';\n });\n const attachBtn = document.createElement('button');\n attachBtn.type = 'button';\n attachBtn.className = 'aegis-chat-attach';\n attachBtn.setAttribute('aria-label', 'Attach a file');\n attachBtn.innerHTML =\n '<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\"/></svg>';\n attachBtn.addEventListener('click', () => fileInput.click());\n\n const sendBtn = document.createElement('button');\n sendBtn.type = 'submit';\n sendBtn.className = 'aegis-chat-send';\n sendBtn.setAttribute('aria-label', 'Send message');\n sendBtn.innerHTML =\n '<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/></svg>';\n composer.addEventListener('submit', (e) => {\n e.preventDefault();\n void this.send(input.value);\n });\n composer.appendChild(attachBtn);\n composer.appendChild(input);\n composer.appendChild(sendBtn);\n composer.appendChild(fileInput);\n\n panel.appendChild(header);\n panel.appendChild(thread);\n panel.appendChild(composer);\n\n // One-time greeting tooltip (\"👋 Need help?\") — a high-intent affordance\n // that signals the assistant is live and ready, without a heavy Lottie\n // dependency. Shown by scheduleAttention(), dismissed on open.\n const tip = document.createElement('div');\n tip.className = 'aegis-chat-tip';\n const tipText = document.createElement('span');\n tipText.textContent = this.greeting || 'Need help finding something? Ask me 👋'; // eslint-disable-line no-restricted-syntax -- chat greeting copy (message content)\n const tipClose = document.createElement('button');\n tipClose.className = 'aegis-chat-tip-close';\n tipClose.setAttribute('aria-label', 'Dismiss');\n tipClose.innerHTML = lucideSvg('x', { size: 14 });\n tipClose.addEventListener('click', (e) => { e.stopPropagation(); this.dismissAttention(); });\n tip.appendChild(tipText);\n tip.appendChild(tipClose);\n tip.addEventListener('click', () => this.openPanel());\n\n root.appendChild(panel);\n // 'nav' mode is headless — the host owns the entry point (e.g. a bottom-nav\n // \"Ask\" button → openPanel()), so we mount the panel but NOT the bubble or\n // its attention tooltip. The panel itself starts hidden until openPanel().\n if (this.displayMode !== 'nav') {\n root.appendChild(tip);\n root.appendChild(bubble);\n }\n document.body.appendChild(root);\n\n this.greetingTip = tip;\n this.root = root;\n this.bubble = bubble;\n this.badge = badge;\n this.panel = panel;\n this.thread = thread;\n this.input = input;\n\n // Local greeting (server greeting also arrives via history on first turn).\n this.appendSystemGreeting(this.greeting);\n this.renderQuickReplies();\n }\n\n /** Campaign-configured quick-reply chips shown under the greeting. Tapping a\n * chip sends it as the customer's first message, then the row is removed. */\n private renderQuickReplies(): void {\n if (!this.thread || !this.quickReplies?.length) return;\n const row = document.createElement('div');\n row.className = 'aegis-chat-quickreplies';\n for (const label of this.quickReplies) {\n if (typeof label !== 'string' || !label.trim()) continue;\n const chip = document.createElement('button');\n chip.type = 'button';\n chip.className = 'aegis-chat-chip';\n chip.textContent = label;\n chip.addEventListener('click', () => {\n row.remove();\n void this.send(label);\n });\n row.appendChild(chip);\n }\n if (row.childElementCount > 0) this.thread.appendChild(row);\n }\n\n private appendBubble(msg: ChatHistoryMessage): void {\n if (!this.thread) return;\n if (msg.id) this.renderedIds.add(msg.id);\n\n const row = document.createElement('div');\n row.className = `aegis-chat-msg aegis-chat-msg--${msg.sender}`;\n\n // Media attachment (customer-sent or agent-sent).\n if (msg.media_url) {\n if (msg.media_type === 'image') {\n const img = document.createElement('img');\n img.className = 'aegis-chat-media-img';\n img.src = msg.media_url;\n img.alt = 'attachment';\n img.loading = 'lazy';\n const url = msg.media_url;\n img.addEventListener('click', () => window.open(url, '_blank', 'noopener'));\n row.appendChild(img);\n } else {\n const a = document.createElement('a');\n a.className = 'aegis-chat-media-file';\n a.href = msg.media_url;\n a.target = '_blank';\n a.rel = 'noopener noreferrer';\n a.textContent = '📎 Attachment'; // eslint-disable-line no-restricted-syntax -- chat message content (attachment link label)\n row.appendChild(a);\n }\n }\n\n if (msg.content) {\n const bubble = document.createElement('div');\n bubble.className = 'aegis-chat-bubbletext';\n bubble.textContent = msg.content;\n row.appendChild(bubble);\n }\n\n // Interactive payload: reuse the backend `web` render shape.\n if (msg.interactive && Array.isArray(msg.interactive)) {\n for (const inter of msg.interactive) {\n const el = this.renderInteractive(inter);\n if (el) row.appendChild(el);\n }\n }\n\n this.thread.appendChild(row);\n this.scrollToBottom();\n }\n\n /** Render a `web`-format interactive message. Currently product_carousel. */\n private renderInteractive(inter: Record<string, unknown>): HTMLElement | null {\n const type = inter?.type as string | undefined;\n if (type !== 'product_carousel') return null;\n const products = (inter.products as Array<Record<string, unknown>>) || [];\n if (!products.length) return null;\n\n const carousel = document.createElement('div');\n carousel.className = 'aegis-chat-carousel';\n for (const p of products) {\n const card = document.createElement('a');\n card.className = 'aegis-chat-card';\n const href = (p.cta_url as string) || '#';\n card.href = href;\n card.target = '_blank';\n card.rel = 'noopener noreferrer';\n\n if (p.image_url) {\n const img = document.createElement('img');\n img.className = 'aegis-chat-card-img';\n img.src = p.image_url as string;\n img.alt = (p.name as string) || '';\n img.loading = 'lazy';\n card.appendChild(img);\n }\n const name = document.createElement('div');\n name.className = 'aegis-chat-card-name';\n name.textContent = (p.name as string) || '';\n card.appendChild(name);\n\n if (p.price != null) {\n const price = document.createElement('div');\n price.className = 'aegis-chat-card-price';\n price.textContent = typeof p.price === 'number' ? String(p.price) : String(p.price);\n card.appendChild(price);\n }\n carousel.appendChild(card);\n }\n\n if (typeof inter.browse_all_url === 'string' && inter.browse_all_url) {\n const more = document.createElement('a');\n more.className = 'aegis-chat-card aegis-chat-card--more';\n more.href = inter.browse_all_url;\n more.target = '_blank';\n more.rel = 'noopener noreferrer';\n more.textContent = 'View all →';\n carousel.appendChild(more);\n }\n return carousel;\n }\n\n private appendSystem(textContent: string): void {\n this.appendBubble({ id: `sys_${Date.now()}`, sender: 'system', role: 'system', content: textContent });\n }\n\n private appendSystemGreeting(textContent: string): void {\n this.appendBubble({ id: `greeting_${Date.now()}`, sender: 'assistant', role: 'bot', content: textContent });\n }\n\n // ── Unread badge ─────────────────────────────────────────────────────────\n\n private bumpUnread(): void {\n this.unread += 1;\n if (this.badge) {\n this.badge.textContent = String(this.unread);\n this.badge.style.display = 'flex';\n }\n }\n\n private clearUnread(): void {\n this.unread = 0;\n if (this.badge) this.badge.style.display = 'none';\n }\n\n // ── helpers ──────────────────────────────────────────────────────────────\n\n private scrollToBottom(): void {\n if (this.thread) this.thread.scrollTop = this.thread.scrollHeight;\n }\n\n private readAnonId(): string | undefined {\n if (typeof document === 'undefined') return undefined;\n try {\n return new Storage().get('anon_id') ?? undefined;\n } catch {\n return undefined;\n }\n }\n\n private readStoredContactId(): string | undefined {\n if (typeof document === 'undefined') return undefined;\n try {\n return new Storage().get('aegis_chat_cid') ?? undefined;\n } catch {\n return undefined;\n }\n }\n\n private persistContactId(id: string): void {\n if (!id || typeof document === 'undefined') return;\n try {\n new Storage().set('aegis_chat_cid', id, 365);\n } catch {\n /* storage blocked — non-fatal */\n }\n }\n\n /** Restore a returning visitor's thread on open. Resolves an existing contact\n * + mints a per-session ticket WITHOUT sending a message (history is ticket-\n * gated). No-ops for brand-new visitors and once a ticket is already held. */\n private async resumeSession(): Promise<void> {\n if (this.sseTicket) {\n void this.refreshHistory();\n return;\n }\n if (this.sessionResumed) return;\n this.sessionResumed = true;\n if (!this.contactId && !this.anonymousId) return;\n try {\n const res = await fetch(`${this.apiHost}/v1/chat/session`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'X-Aegis-Write-Key': this.writeKey },\n body: JSON.stringify({\n anonymous_id: this.anonymousId,\n contact_id: this.contactId,\n channel: this.channel,\n }),\n });\n if (!res.ok) return;\n const data = (await res.json()) as {\n contact_id: string | null;\n sse_ticket?: string;\n has_history?: boolean;\n };\n if (!data.contact_id || !data.sse_ticket) return; // brand-new visitor\n this.contactId = data.contact_id;\n this.sseTicket = data.sse_ticket;\n this.persistContactId(data.contact_id);\n await this.refreshHistory();\n if (this.open) this.connectSSE(data.sse_ticket);\n } catch (err) {\n this.log('resume session failed', err);\n }\n }\n\n // ── Typing indicator ───────────────────────────────────────────────────────\n\n private showTyping(): void {\n if (!this.thread || this.typingEl) return;\n const row = document.createElement('div');\n row.className = 'aegis-chat-msg aegis-chat-msg--assistant aegis-chat-typing';\n row.innerHTML =\n '<div class=\"aegis-chat-bubbletext aegis-chat-typingdots\"><span></span><span></span><span></span></div>';\n this.thread.appendChild(row);\n this.typingEl = row;\n this.scrollToBottom();\n }\n\n private hideTyping(): void {\n if (this.typingEl) {\n this.typingEl.remove();\n this.typingEl = undefined;\n }\n }\n\n // ── Attention affordance (pulse + one-time greeting tooltip) ────────────────\n\n private scheduleAttention(): void {\n if (typeof window === 'undefined') return;\n // Let the page settle, then draw the eye to the bubble: a pulsing ring +\n // the greeting tooltip. Only if the visitor hasn't engaged yet.\n this.attentionTimer = setTimeout(() => {\n if (this.hasInteracted || this.open) return;\n this.bubble?.classList.add('aegis-chat-bubble--pulse');\n this.greetingTip?.classList.add('aegis-chat-tip--show');\n }, ATTENTION_DELAY_MS);\n }\n\n private dismissAttention(): void {\n if (this.attentionTimer) {\n clearTimeout(this.attentionTimer);\n this.attentionTimer = undefined;\n }\n this.bubble?.classList.remove('aegis-chat-bubble--pulse');\n this.greetingTip?.classList.remove('aegis-chat-tip--show');\n }\n\n private log(...args: unknown[]): void {\n if (this.debugMode) console.log('[AegisChat]', ...args);\n }\n\n private injectStyles(): void {\n if (typeof document === 'undefined' || document.getElementById(STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = CHAT_CSS;\n document.head.appendChild(style);\n }\n}\n\nconst CHAT_CSS = `\n.aegis-chat-root{position:fixed;bottom:20px;right:20px;z-index:2147483000;font-family:Inter,system-ui,-apple-system,sans-serif}\n.aegis-chat-root--bottom-left{left:20px;right:auto}\n.aegis-chat-root--bottom-left .aegis-chat-panel{left:0;right:auto}\n.aegis-chat-bubble-logo{width:34px;height:34px;border-radius:9999px;object-fit:cover}\n.aegis-chat-bubble{width:60px;height:60px;border-radius:9999px;border:none;background:var(--aegis-chat-accent,#4169e1);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 8px 24px rgba(0,0,0,.18);transition:transform .18s ease,box-shadow .18s ease;position:relative;animation:aegis-chat-in .4s cubic-bezier(.16,1,.3,1)}\n.aegis-chat-bubble:hover{transform:translateY(-2px) scale(1.04);box-shadow:0 12px 28px rgba(0,0,0,.22)}\n.aegis-chat-bubble--hidden{display:none}\n/* Attention pulse — an expanding accent ring that draws the eye to a live, ready assistant. */\n.aegis-chat-bubble--pulse::before{content:\"\";position:absolute;inset:0;border-radius:9999px;background:var(--aegis-chat-accent,#4169e1);z-index:-1;animation:aegis-chat-pulse 1.8s ease-out infinite}\n.aegis-chat-bubble--pulse{animation:aegis-chat-in .4s cubic-bezier(.16,1,.3,1),aegis-chat-nudge 1.8s ease-in-out 2}\n@keyframes aegis-chat-in{from{opacity:0;transform:scale(.6)}to{opacity:1;transform:scale(1)}}\n@keyframes aegis-chat-pulse{0%{transform:scale(1);opacity:.55}100%{transform:scale(1.9);opacity:0}}\n@keyframes aegis-chat-nudge{0%,100%{transform:translateY(0)}25%{transform:translateY(-5px)}50%{transform:translateY(0)}}\n/* One-time greeting tooltip */\n.aegis-chat-tip{position:absolute;bottom:14px;right:74px;max-width:230px;background:#fff;color:#0f172a;border:1px solid #e2e8f0;border-radius:14px;border-bottom-right-radius:4px;box-shadow:0 10px 30px rgba(0,0,0,.16);padding:10px 30px 10px 13px;font-size:13.5px;line-height:1.4;cursor:pointer;opacity:0;transform:translateX(8px) scale(.92);transform-origin:bottom right;pointer-events:none;transition:opacity .25s ease,transform .25s ease}\n.aegis-chat-tip--show{opacity:1;transform:translateX(0) scale(1);pointer-events:auto}\n.aegis-chat-tip-close{position:absolute;top:5px;right:6px;width:18px;height:18px;border:none;background:transparent;color:#94a3b8;font-size:12px;line-height:1;cursor:pointer;border-radius:9999px}\n.aegis-chat-tip-close:hover{background:#f1f5f9;color:#475569}\n@media (prefers-reduced-motion:reduce){.aegis-chat-bubble,.aegis-chat-bubble--pulse{animation:none}.aegis-chat-bubble--pulse::before{animation:none;display:none}}\n.aegis-chat-badge{position:absolute;top:-2px;right:-2px;min-width:20px;height:20px;padding:0 5px;border-radius:9999px;background:#ef4444;color:#fff;font-size:12px;font-weight:600;display:flex;align-items:center;justify-content:center}\n.aegis-chat-panel{position:absolute;bottom:0;right:0;width:380px;max-width:calc(100vw - 32px);height:560px;max-height:calc(100vh - 48px);background:#fff;border-radius:16px;box-shadow:0 16px 48px rgba(0,0,0,.24);display:none;flex-direction:column;overflow:hidden}\n.aegis-chat-panel--open{display:flex}\n/* Header + customer bubbles use the \"fill\" surface — accent solid by default, or\n a soft tint when surfaceStyle:'tinted' sets --aegis-chat-fill/-fg on the root. */\n.aegis-chat-header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;background:var(--aegis-chat-fill,var(--aegis-chat-accent,#4169e1));color:var(--aegis-chat-fill-fg,#fff)}\n.aegis-chat-title{font-weight:600;font-size:15px}\n.aegis-chat-close{background:transparent;border:none;color:var(--aegis-chat-fill-fg,#fff);font-size:16px;cursor:pointer;opacity:.85}\n.aegis-chat-close:hover{opacity:1}\n.aegis-chat-thread{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px;background:#f8fafc}\n.aegis-chat-msg{display:flex;flex-direction:column;max-width:85%}\n.aegis-chat-msg--customer{align-self:flex-end;align-items:flex-end}\n.aegis-chat-msg--assistant,.aegis-chat-msg--system{align-self:flex-start;align-items:flex-start}\n.aegis-chat-bubbletext{padding:9px 13px;border-radius:14px;font-size:14px;line-height:1.45;white-space:pre-wrap;word-break:break-word}\n.aegis-chat-msg--customer .aegis-chat-bubbletext{background:var(--aegis-chat-fill,var(--aegis-chat-accent,#4169e1));color:var(--aegis-chat-fill-fg,#fff);border-bottom-right-radius:4px}\n.aegis-chat-msg--assistant .aegis-chat-bubbletext{background:#fff;color:#0f172a;border:1px solid #e2e8f0;border-bottom-left-radius:4px}\n.aegis-chat-quickreplies{display:flex;flex-wrap:wrap;gap:6px;padding:2px 4px 6px;justify-content:flex-start}\n.aegis-chat-chip{appearance:none;cursor:pointer;border:1px solid var(--aegis-chat-accent,#4169e1);background:#fff;color:var(--aegis-chat-accent,#4169e1);border-radius:9999px;padding:6px 12px;font-size:13px;line-height:1;font-family:inherit;transition:background .15s,color .15s}\n.aegis-chat-chip:hover{background:var(--aegis-chat-accent,#4169e1);color:#fff}\n.aegis-chat-msg--system .aegis-chat-bubbletext{background:#fef3c7;color:#92400e;font-size:13px}\n.aegis-chat-typingdots{display:flex;gap:4px;align-items:center;padding:12px 14px}\n.aegis-chat-typingdots span{width:7px;height:7px;border-radius:9999px;background:#94a3b8;display:inline-block;animation:aegis-chat-typing 1.2s infinite ease-in-out}\n.aegis-chat-typingdots span:nth-child(2){animation-delay:.18s}\n.aegis-chat-typingdots span:nth-child(3){animation-delay:.36s}\n@keyframes aegis-chat-typing{0%,80%,100%{transform:translateY(0);opacity:.4}40%{transform:translateY(-4px);opacity:1}}\n.aegis-chat-carousel{display:flex;gap:10px;overflow-x:auto;padding:8px 0;max-width:320px;scroll-snap-type:x mandatory}\n.aegis-chat-card{flex:0 0 140px;scroll-snap-align:start;background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:8px;text-decoration:none;color:#0f172a;display:flex;flex-direction:column;gap:4px;transition:box-shadow .15s ease}\n.aegis-chat-card:hover{box-shadow:0 6px 16px rgba(0,0,0,.1)}\n.aegis-chat-card-img{width:100%;height:96px;object-fit:cover;border-radius:8px}\n.aegis-chat-card-name{font-size:13px;font-weight:500;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}\n.aegis-chat-card-price{font-size:13px;font-weight:600;color:var(--aegis-chat-accent,#4169e1)}\n.aegis-chat-card--more{align-items:center;justify-content:center;font-weight:600;color:var(--aegis-chat-accent,#4169e1)}\n.aegis-chat-composer{display:flex;align-items:flex-end;gap:8px;padding:12px;border-top:1px solid #e2e8f0;background:#fff}\n.aegis-chat-input{flex:1;resize:none;border:1px solid #e2e8f0;border-radius:12px;padding:9px 12px;font-size:14px;font-family:inherit;max-height:96px;outline:none}\n.aegis-chat-input:focus{border-color:var(--aegis-chat-accent,#4169e1)}\n.aegis-chat-send{width:40px;height:40px;border-radius:9999px;border:none;background:var(--aegis-chat-accent,#4169e1);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;flex:0 0 auto}\n.aegis-chat-attach{width:38px;height:38px;border-radius:9999px;border:none;background:transparent;color:#64748b;display:flex;align-items:center;justify-content:center;cursor:pointer;flex:0 0 auto;transition:background .15s ease,color .15s ease}\n.aegis-chat-attach:hover{background:#f1f5f9;color:var(--aegis-chat-accent,#4169e1)}\n.aegis-chat-fileinput{display:none}\n.aegis-chat-media-img{max-width:200px;max-height:200px;border-radius:12px;object-fit:cover;cursor:pointer;display:block}\n.aegis-chat-media-file{display:inline-flex;align-items:center;gap:6px;padding:8px 12px;border-radius:12px;background:#fff;border:1px solid #e2e8f0;font-size:13px;color:#0f172a;text-decoration:none;max-width:220px}\n.aegis-chat-msg--customer .aegis-chat-media-file{background:rgba(255,255,255,.18);border-color:transparent;color:#fff}\n/* Mobile: raise the bubble above the storefront's full-width cart bar so they\n never overlap; widen the panel and reduce the tooltip width. */\n@media (max-width:767px){\n .aegis-chat-root{bottom:84px;right:16px}\n /* Full-page chat on mobile — the open panel covers the whole viewport (incl.\n the bottom nav) like a native chat screen, instead of a floating card. */\n .aegis-chat-panel{position:fixed;inset:0;left:0;right:0;width:100vw;height:100vh;height:100dvh;max-width:none;max-height:none;border-radius:0}\n .aegis-chat-tip{max-width:200px}\n}\n`;\n","/**\n * TriggerEngine - Client-Side Trigger Evaluation\n * \n * Evaluates behavioral triggers that require real-time client state:\n * - Scroll depth (e.g., user scrolled >= 50%)\n * - Time on page (e.g., >= 30 seconds on current page)\n * - Exit intent (mouse leaving viewport toward browser controls)\n * - Inactivity (no user interaction for X seconds)\n * - Scroll velocity (mobile exit intent - fast upward scroll)\n * - Visibility change (tab/app switching)\n * - Back button (history navigation)\n * \n * Architecture:\n * - Server evaluates historical triggers (event counts, segments)\n * - Client evaluates behavioral triggers (scroll, time, exit intent)\n * - Zero latency - no server round trip needed\n * \n * Usage:\n * const trigger = new TriggerEngine();\n * trigger.on('scroll_depth_50', (data) => {\n * console.log('User scrolled to 50%', data);\n * });\n * trigger.start();\n *\n * Every `page_url` a trigger emits is credential-redacted (`redactUrl`): the\n * payload is handed to listeners that track it. The two `history.pushState`\n * calls read the raw URL on purpose — they re-push the page the visitor is\n * on, and a redacted URL there would rewrite the address bar.\n */\n\nimport { redactUrl } from '../utils/redact-url';\n\nexport interface TriggerRule {\n type: 'scroll_depth' | 'time_on_page' | 'exit_intent' | 'inactivity' | 'scroll_velocity' | 'visibility_change' | 'back_button';\n config: ScrollDepthConfig | TimeOnPageConfig | ExitIntentConfig | InactivityConfig | ScrollVelocityConfig | VisibilityChangeConfig | BackButtonConfig;\n}\n\nexport interface ScrollDepthConfig {\n depth_percent: number;\n}\n\nexport interface TimeOnPageConfig {\n seconds: number;\n}\n\nexport interface ExitIntentConfig {\n enabled: boolean;\n}\n\nexport interface InactivityConfig {\n idle_seconds: number;\n}\n\nexport interface ScrollVelocityConfig {\n threshold: number;\n minScrollPosition: number;\n cooldown: number;\n}\n\nexport interface VisibilityChangeConfig {\n enabled: boolean;\n}\n\nexport interface BackButtonConfig {\n enabled: boolean;\n}\n\n/**\n * Rage-click detection (Micro-Intent Engine P2 Task 1).\n *\n * Per-intent ring buffer: when ≥ `threshold` clicks land on the same\n * intent inside `windowMs`, the engine emits `rage_click` with the\n * intent name + current count + window. Frustration signal used by the\n * Stuck-at-Checkout pattern (e.g., rage-clicking the coupon input four\n * times in 800ms).\n *\n * Defaults match the plan:\n * threshold = 3 clicks\n * windowMs = 1000 ms\n *\n * TriggerEngine does NOT attach its own pointerdown listener — the\n * SelectorBinder (P1 Task 5) owns the global, delegated listener and\n * dispatches per-intent click events. The IntentSnapshotCollector\n * (P2 Task 5) wires `SelectorBinder.onIntent` to\n * `TriggerEngine.noteClick(intent)` so TriggerEngine sees a clean\n * intent-stamped stream instead of raw DOM events.\n */\nexport interface RageClickConfig {\n threshold: number;\n windowMs: number;\n}\n\n/** Payload emitted on the `rage_click` event. */\nexport interface RageClickData {\n intent: string;\n count: number;\n window_ms: number;\n}\n\n/**\n * Hover-dwell tracking (Micro-Intent Engine P2 Task 2).\n *\n * Tracks per-intent hover duration in milliseconds. `getHoverMs(intent)`\n * returns:\n * - if currently hovering the intent: ms since hover_start (live)\n * - else: ms duration of the most recent hover on that intent (sticky)\n * - if never hovered: 0\n *\n * The sticky behaviour is deliberate. The canonical use case\n * (MICRO_INTENT_ENGINE.md Story 1):\n * \"Priya hovers price for 1.8s, considers, moves cursor away.\n * Exit_intent fires. Rule checks price_hover_ms >= 1500 → true.\"\n * The rule evaluator queries `price_hover_ms` AFTER the hover ends. If\n * we reset to 0 on hover_end, the rule never matches the very pattern\n * the engine exists to detect.\n *\n * Threshold-based emission: when a hover crosses one of the registered\n * dwell targets (e.g. 1500ms), a `<intent>_hover_dwell_<ms>` event\n * fires. Useful for journeys that want event-style hooks; the\n * snapshot-style `getHoverMs` is the primary surface for the\n * compound-rule evaluator.\n */\nexport interface HoverDwellConfig {\n /** Dwell thresholds (ms) per intent. The engine fires\n * `<intent>_hover_dwell_<ms>` when an active hover crosses each\n * threshold. Defaults: price→[1500], cta→[1500]. */\n thresholdsByIntent?: Partial<Record<string, ReadonlyArray<number>>>;\n}\n\n/**\n * Mouse velocity toward the viewport top (Micro-Intent Engine P2 Task 3).\n *\n * Detects the early phase of exit-intent: the mouse moving rapidly\n * upward before it actually crosses out of the viewport. The signal is\n * a continuous px/ms value (positive = moving up), updated as samples\n * arrive. When velocity crosses `threshold`, a `mouse_velocity_to_top`\n * event fires (once per session-window cooldown to avoid spamming on a\n * single gesture).\n *\n * Sampling: callers feed positions via `noteMousePosition(x, y, ts)`.\n * The engine keeps a sliding `windowMs` ring buffer of samples and\n * computes velocity as the average dy / dt over samples in the window.\n * Per the plan: sampled at 100ms, 400ms window. That bound is\n * configurable.\n *\n * Why \"to top\" specifically: exit-intent fires only when y crosses 0;\n * we want the warning BEFORE that. A typical exit-intent gesture has\n * upward velocity well above any normal browsing motion.\n */\nexport interface MouseVelocityConfig {\n /** Threshold in px/ms (positive = moving up). Default 1.5 px/ms,\n * ≈ moving 600px in 400ms, which only happens during a deliberate\n * upward sweep. */\n threshold: number;\n /** Rolling-window length over which to compute velocity. */\n windowMs: number;\n /** Cooldown after a fire before the signal can fire again. */\n cooldownMs: number;\n}\n\nexport interface TriggerEvent {\n type: string;\n data: Record<string, any>;\n timestamp: number;\n}\n\ntype TriggerCallback = (event: TriggerEvent) => void;\n\nexport class TriggerEngine {\n private listeners: Map<string, Set<TriggerCallback>> = new Map();\n private isStarted = false;\n \n private scrollDepthTargets = new Set<number>();\n private scrollDepthReached = new Set<number>();\n \n private timeOnPageTargets = new Map<number, number>();\n private pageLoadTime?: number;\n \n private exitIntentEnabled = false;\n private exitIntentFired = false;\n \n private inactivityTargets = new Map<number, number>();\n private lastActivityTime = Date.now();\n private inactivityCheckInterval?: number;\n \n private scrollVelocityEnabled = false;\n private scrollVelocityFired = false;\n private scrollVelocityConfig: ScrollVelocityConfig = {\n threshold: 0.5,\n minScrollPosition: 100,\n cooldown: 5000\n };\n private lastScrollY = 0;\n private lastScrollTime = Date.now();\n private scrollVelocityCooldownTimer?: number;\n \n private visibilityChangeEnabled = false;\n\n private backButtonEnabled = false;\n private backButtonFired = false;\n\n // Rage-click state (P2 Task 1). Buffer indexed by intent name (price /\n // cta / coupon / checkout). Per-intent so rage-clicking the coupon\n // input doesn't get conflated with rapid CTA presses.\n private rageClickEnabled = false;\n private rageClickConfig: RageClickConfig = { threshold: 3, windowMs: 1000 };\n private rageClickBuffer = new Map<string, number[]>();\n /** Tracks whether a rage_click event has already fired for an intent\n * in the current burst. Reset when the buffer empties below threshold.\n * Prevents the same burst from firing the event N-2 times as each\n * click after threshold pushes the count higher. */\n private rageClickFiredInBurst = new Set<string>();\n\n // Hover-dwell state (P2 Task 2). Tracks live + sticky-last dwell per\n // intent. `hoverStartAt` undefined means no active hover.\n private hoverEnabled = false;\n private hoverStartAt = new Map<string, number>();\n private hoverLastMs = new Map<string, number>();\n private hoverThresholds = new Map<string, ReadonlyArray<number>>([\n ['price', [1500]],\n ['cta', [1500]],\n ]);\n /** Track which (intent, threshold) pairs have already fired in the\n * current hover so a single long hover doesn't double-fire as time\n * marches on. Reset on hover_start. */\n private hoverThresholdsFired = new Map<string, Set<number>>();\n /** Pending threshold timers, keyed by intent. Cleared on hover_end. */\n private hoverThresholdTimers = new Map<string, number[]>();\n\n // Mouse-velocity-to-top state (P2 Task 3).\n private mouseVelEnabled = false;\n private mouseVelConfig: MouseVelocityConfig = {\n threshold: 1.5,\n windowMs: 400,\n cooldownMs: 5000,\n };\n /** Ring buffer of (x, y, ts) samples within the rolling window. */\n private mouseSamples: Array<{ x: number; y: number; ts: number }> = [];\n /** Last computed velocity-to-top in px/ms (positive = moving up). */\n private mouseVelLast = 0;\n /** Epoch ms when the velocity event last fired — gates cooldown. */\n private mouseVelLastFiredAt = 0;\n\n constructor() {}\n \n on(eventType: string, callback: TriggerCallback): void {\n if (!this.listeners.has(eventType)) {\n this.listeners.set(eventType, new Set());\n }\n this.listeners.get(eventType)!.add(callback);\n }\n \n off(eventType: string, callback: TriggerCallback): void {\n const callbacks = this.listeners.get(eventType);\n if (callbacks) {\n callbacks.delete(callback);\n }\n }\n \n // A register* call after start() takes effect at once: in-app campaigns\n // (and so their triggers) arrive after the engine is already running.\n registerScrollDepth(depthPercent: number): void {\n const firstTarget = this.scrollDepthTargets.size === 0;\n this.scrollDepthTargets.add(depthPercent);\n if (!this.isStarted) return;\n if (firstTarget) {\n this.attachScrollListener();\n } else {\n this.handleScroll();\n }\n }\n \n registerTimeOnPage(seconds: number): void {\n if (!this.timeOnPageTargets.has(seconds)) {\n // Time on PAGE: once running, count from page load, not from registration.\n const elapsedMs = this.isStarted && this.pageLoadTime ? Date.now() - this.pageLoadTime : 0;\n const timerId = window.setTimeout(() => {\n this.emit(`time_on_page_${seconds}`, {\n seconds,\n page_url: redactUrl(window.location.href)\n });\n this.timeOnPageTargets.delete(seconds);\n }, Math.max(0, seconds * 1000 - elapsedMs));\n \n this.timeOnPageTargets.set(seconds, timerId);\n }\n }\n \n registerExitIntent(): void {\n if (this.isStarted && !this.exitIntentEnabled) {\n this.attachExitIntentListener();\n }\n this.exitIntentEnabled = true;\n }\n \n registerInactivity(idleSeconds: number): void {\n if (!this.inactivityTargets.has(idleSeconds)) {\n const timerId = window.setTimeout(() => {\n const idleTime = (Date.now() - this.lastActivityTime) / 1000;\n \n // Fire once, when the visitor has been idle long enough. An active\n // visitor at this moment keeps the target: the running engine's\n // per-second check fires it at the first real idle stretch.\n if (idleTime >= idleSeconds) {\n this.emit(`inactivity_${idleSeconds}`, {\n idle_seconds: idleSeconds,\n actual_idle_time: idleTime\n });\n this.inactivityTargets.delete(idleSeconds);\n }\n }, idleSeconds * 1000);\n \n this.inactivityTargets.set(idleSeconds, timerId);\n }\n }\n \n registerScrollVelocity(config?: Partial<ScrollVelocityConfig>): void {\n if (this.isStarted && !this.scrollVelocityEnabled) {\n this.attachScrollVelocityListener();\n }\n this.scrollVelocityEnabled = true;\n \n if (config) {\n this.scrollVelocityConfig = {\n threshold: config.threshold ?? this.scrollVelocityConfig.threshold,\n minScrollPosition: config.minScrollPosition ?? this.scrollVelocityConfig.minScrollPosition,\n cooldown: config.cooldown ?? this.scrollVelocityConfig.cooldown\n };\n }\n }\n \n registerVisibilityChange(): void {\n this.visibilityChangeEnabled = true;\n }\n \n registerBackButton(): void {\n this.backButtonEnabled = true;\n }\n\n /**\n * Enable rage-click detection (P2 Task 1).\n *\n * After calling this, feed click events via `noteClick(intent)`.\n * SelectorBinder (P1 Task 5) is the upstream source; the wiring lives\n * in IntentSnapshotCollector (P2 Task 5). Calling `noteClick` without\n * a prior `registerRageClick()` is a no-op so legacy callers don't\n * accidentally enable the feature.\n */\n registerRageClick(config?: Partial<RageClickConfig>): void {\n this.rageClickEnabled = true;\n if (config) {\n this.rageClickConfig = {\n threshold: config.threshold ?? this.rageClickConfig.threshold,\n windowMs: config.windowMs ?? this.rageClickConfig.windowMs,\n };\n }\n }\n\n /**\n * Feed a click event for rage-click detection. Returns the current\n * count within-window for the intent — useful for the snapshot\n * collector (P2 Task 5) which updates IntentRuleEvaluator with\n * `rage_click_count` continuously, not just at the burst threshold.\n *\n * Idempotent w.r.t. `registerRageClick` — when not enabled, this\n * returns 0 without buffering.\n */\n noteClick(intent: string, timestamp: number = Date.now()): number {\n if (!this.rageClickEnabled) return 0;\n const buf = this.rageClickBuffer.get(intent) ?? [];\n const cutoff = timestamp - this.rageClickConfig.windowMs;\n // Drop expired timestamps (clicks older than the window).\n // The buffer is append-only and timestamps are non-decreasing in\n // practice (Date.now is monotonic per device, modulo system-clock\n // adjustments), so an in-place trim from the head is correct.\n let trimStart = 0;\n while (trimStart < buf.length && buf[trimStart] < cutoff) trimStart++;\n const recent = trimStart === 0 ? buf : buf.slice(trimStart);\n recent.push(timestamp);\n this.rageClickBuffer.set(intent, recent);\n\n // Burst reset: when the recent count drops below threshold (because\n // older clicks aged out), the burst is over — re-arm the per-intent\n // fire-once latch so a fresh burst can fire again.\n if (recent.length < this.rageClickConfig.threshold) {\n this.rageClickFiredInBurst.delete(intent);\n }\n\n if (\n recent.length >= this.rageClickConfig.threshold &&\n !this.rageClickFiredInBurst.has(intent)\n ) {\n this.rageClickFiredInBurst.add(intent);\n this.emit('rage_click', {\n intent,\n count: recent.length,\n window_ms: this.rageClickConfig.windowMs,\n });\n }\n\n return recent.length;\n }\n\n /** Snapshot the current rage_click count for an intent. The\n * IntentSnapshotCollector (P2 Task 5) calls this when building the\n * `rage_click_count` signal value for the IntentRuleEvaluator. */\n getRageClickCount(intent: string): number {\n if (!this.rageClickEnabled) return 0;\n const buf = this.rageClickBuffer.get(intent);\n if (!buf) return 0;\n const cutoff = Date.now() - this.rageClickConfig.windowMs;\n // Lazy prune on read so the count reflects only currently-valid\n // clicks. Cheap because buffers are tiny (cap at threshold + 1 in\n // practice — older entries get dropped on every note).\n let live = 0;\n for (const ts of buf) {\n if (ts >= cutoff) live++;\n }\n return live;\n }\n\n /**\n * Enable hover-dwell tracking (P2 Task 2). After registration, feed\n * hover events via `noteHoverStart(intent, ts)` / `noteHoverEnd`.\n * SelectorBinder (P1 Task 5) is the upstream source; wiring lands in\n * P2 Task 5.\n */\n registerHoverDwell(config?: HoverDwellConfig): void {\n this.hoverEnabled = true;\n if (config?.thresholdsByIntent) {\n for (const [intent, thresholds] of Object.entries(config.thresholdsByIntent)) {\n if (thresholds) this.hoverThresholds.set(intent, thresholds);\n }\n }\n }\n\n /** Record the start of a hover on the named intent. Schedules\n * per-threshold timers that emit `<intent>_hover_dwell_<ms>` when\n * the active hover passes each registered threshold. */\n noteHoverStart(intent: string, timestamp: number = Date.now()): void {\n if (!this.hoverEnabled) return;\n // If a prior hover is still pending (no hover_end fired — possible\n // when the DOM node was unmounted mid-hover), close it out first to\n // keep `hoverLastMs` consistent.\n if (this.hoverStartAt.has(intent)) {\n this.noteHoverEnd(intent, timestamp);\n }\n this.hoverStartAt.set(intent, timestamp);\n this.hoverThresholdsFired.set(intent, new Set());\n\n // Schedule threshold timers. Uses the global setTimeout (works in\n // both browser and node, and vitest's fake-timers stub the global).\n // registerHoverDwell with no thresholds for an intent => no events,\n // only the snapshot value.\n const thresholds = this.hoverThresholds.get(intent);\n if (thresholds) {\n const timers: number[] = [];\n for (const ms of thresholds) {\n const timerId = setTimeout(() => {\n // Only fire if still hovering this intent — the event must\n // not fire after hover_end.\n if (!this.hoverStartAt.has(intent)) return;\n const firedSet = this.hoverThresholdsFired.get(intent);\n if (firedSet?.has(ms)) return;\n firedSet?.add(ms);\n this.emit(`${intent}_hover_dwell_${ms}`, {\n intent,\n threshold_ms: ms,\n actual_ms: Date.now() - timestamp,\n });\n }, ms) as unknown as number;\n timers.push(timerId);\n }\n this.hoverThresholdTimers.set(intent, timers);\n }\n }\n\n /** Record the end of a hover on the named intent. Updates the sticky\n * `lastHoverMs` so `getHoverMs` keeps returning the last-known\n * duration until the next hover_start. */\n noteHoverEnd(intent: string, timestamp: number = Date.now()): void {\n if (!this.hoverEnabled) return;\n const startAt = this.hoverStartAt.get(intent);\n if (startAt === undefined) return;\n const dwell = Math.max(0, timestamp - startAt);\n this.hoverLastMs.set(intent, dwell);\n this.hoverStartAt.delete(intent);\n // Clear pending threshold timers for this intent.\n const pending = this.hoverThresholdTimers.get(intent);\n if (pending) {\n for (const id of pending) clearTimeout(id);\n this.hoverThresholdTimers.delete(intent);\n }\n this.hoverThresholdsFired.delete(intent);\n }\n\n /**\n * Enable mouse-velocity-to-top tracking (P2 Task 3). After\n * registration, feed positions via `noteMousePosition(x, y, ts?)`.\n * IntentSnapshotCollector (P2 Task 5) wires window 'mousemove' to\n * this method, with throttling to ~100ms per the plan.\n */\n registerMouseVelocityToTop(config?: Partial<MouseVelocityConfig>): void {\n this.mouseVelEnabled = true;\n if (config) {\n this.mouseVelConfig = {\n threshold: config.threshold ?? this.mouseVelConfig.threshold,\n windowMs: config.windowMs ?? this.mouseVelConfig.windowMs,\n cooldownMs: config.cooldownMs ?? this.mouseVelConfig.cooldownMs,\n };\n }\n }\n\n /** Feed a mouse-position sample. Recomputes the rolling-window\n * velocity-to-top and fires `mouse_velocity_to_top` when the value\n * crosses the configured threshold (subject to cooldown). */\n noteMousePosition(x: number, y: number, timestamp: number = Date.now()): number {\n if (!this.mouseVelEnabled) return 0;\n const cutoff = timestamp - this.mouseVelConfig.windowMs;\n // Trim expired samples from the head; keep the buffer small.\n let trimStart = 0;\n while (trimStart < this.mouseSamples.length && this.mouseSamples[trimStart].ts < cutoff) {\n trimStart++;\n }\n if (trimStart > 0) this.mouseSamples = this.mouseSamples.slice(trimStart);\n this.mouseSamples.push({ x, y, ts: timestamp });\n\n // Velocity-to-top = -(dy / dt) over the oldest-to-newest pair in\n // the window. Positive value = moving up. With 2+ samples this is\n // the instantaneous slope across the window; with 1 sample we\n // return 0 (cannot compute velocity from a single point).\n if (this.mouseSamples.length < 2) {\n this.mouseVelLast = 0;\n return 0;\n }\n const oldest = this.mouseSamples[0];\n const newest = this.mouseSamples[this.mouseSamples.length - 1];\n const dt = newest.ts - oldest.ts;\n if (dt <= 0) {\n this.mouseVelLast = 0;\n return 0;\n }\n const velocity = -(newest.y - oldest.y) / dt; // px/ms; up = +\n this.mouseVelLast = velocity;\n\n // Threshold + cooldown firing. Initial state allows the first fire\n // — only subsequent fires within cooldownMs of the prior are gated.\n const cooldownOk =\n this.mouseVelLastFiredAt === 0 ||\n timestamp - this.mouseVelLastFiredAt >= this.mouseVelConfig.cooldownMs;\n if (velocity >= this.mouseVelConfig.threshold && cooldownOk) {\n this.mouseVelLastFiredAt = timestamp;\n this.emit('mouse_velocity_to_top', {\n velocity,\n threshold: this.mouseVelConfig.threshold,\n window_ms: this.mouseVelConfig.windowMs,\n samples: this.mouseSamples.length,\n });\n }\n return velocity;\n }\n\n /** Snapshot the current velocity-to-top in px/ms (positive = up).\n * Returns 0 when not enabled or insufficient samples. */\n getMouseVelocityToTop(): number {\n return this.mouseVelEnabled ? this.mouseVelLast : 0;\n }\n\n /**\n * Snapshot the hover dwell for an intent.\n * - Currently hovering: returns `Date.now() - hover_start_at`.\n * - Hovered before, not currently: returns the most recent hover's\n * duration (sticky — the rule evaluator queries this AFTER\n * mouseleave to see \"did Priya hover the price ≥ 1.5s?\").\n * - Never hovered: returns 0.\n */\n getHoverMs(intent: string): number {\n if (!this.hoverEnabled) return 0;\n const startAt = this.hoverStartAt.get(intent);\n if (startAt !== undefined) {\n return Math.max(0, Date.now() - startAt);\n }\n return this.hoverLastMs.get(intent) ?? 0;\n }\n\n start(): void {\n if (this.isStarted) {\n return;\n }\n \n this.pageLoadTime = Date.now();\n this.lastActivityTime = Date.now();\n this.lastScrollY = window.scrollY || 0;\n this.lastScrollTime = Date.now();\n \n if (this.scrollDepthTargets.size > 0) {\n this.attachScrollListener();\n }\n \n if (this.exitIntentEnabled) {\n this.attachExitIntentListener();\n }\n \n if (this.scrollVelocityEnabled) {\n this.attachScrollVelocityListener();\n }\n \n if (this.visibilityChangeEnabled) {\n this.attachVisibilityChangeListener();\n }\n \n if (this.backButtonEnabled) {\n this.attachBackButtonListener();\n }\n \n this.attachActivityListeners();\n \n this.startInactivityCheck();\n \n this.isStarted = true;\n }\n \n stop(): void {\n if (!this.isStarted) {\n return;\n }\n \n this.removeScrollListener();\n this.removeExitIntentListener();\n this.removeScrollVelocityListener();\n this.removeVisibilityChangeListener();\n this.removeBackButtonListener();\n this.removeActivityListeners();\n \n this.timeOnPageTargets.forEach(timerId => clearTimeout(timerId));\n this.timeOnPageTargets.clear();\n \n this.inactivityTargets.forEach(timerId => clearTimeout(timerId));\n this.inactivityTargets.clear();\n \n if (this.inactivityCheckInterval) {\n clearInterval(this.inactivityCheckInterval);\n this.inactivityCheckInterval = undefined;\n }\n \n if (this.scrollVelocityCooldownTimer) {\n clearTimeout(this.scrollVelocityCooldownTimer);\n this.scrollVelocityCooldownTimer = undefined;\n }\n \n this.isStarted = false;\n }\n \n reset(): void {\n this.scrollDepthReached.clear();\n this.exitIntentFired = false;\n this.scrollVelocityFired = false;\n this.backButtonFired = false;\n this.pageLoadTime = Date.now();\n this.lastActivityTime = Date.now();\n this.lastScrollY = window.scrollY || 0;\n this.lastScrollTime = Date.now();\n \n if (this.scrollVelocityCooldownTimer) {\n clearTimeout(this.scrollVelocityCooldownTimer);\n this.scrollVelocityCooldownTimer = undefined;\n }\n }\n \n private attachScrollListener(): void {\n window.addEventListener('scroll', this.handleScroll, { passive: true });\n this.handleScroll();\n }\n \n private removeScrollListener(): void {\n window.removeEventListener('scroll', this.handleScroll);\n }\n \n private handleScroll = (): void => {\n const scrollTop = window.pageYOffset || document.documentElement.scrollTop;\n const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;\n const scrollPercent = scrollHeight > 0 ? (scrollTop / scrollHeight) * 100 : 0;\n \n for (const targetDepth of this.scrollDepthTargets) {\n if (scrollPercent >= targetDepth && !this.scrollDepthReached.has(targetDepth)) {\n this.scrollDepthReached.add(targetDepth);\n \n this.emit(`scroll_depth_${targetDepth}`, {\n depth_percent: targetDepth,\n actual_percent: scrollPercent,\n scroll_top: scrollTop,\n scroll_height: scrollHeight\n });\n }\n }\n };\n \n private attachExitIntentListener(): void {\n document.addEventListener('mouseleave', this.handleExitIntent);\n }\n \n private removeExitIntentListener(): void {\n document.removeEventListener('mouseleave', this.handleExitIntent);\n }\n \n private handleExitIntent = (event: MouseEvent): void => {\n if (this.exitIntentFired) {\n return;\n }\n \n if (event.clientY < 10) {\n this.exitIntentFired = true;\n \n this.emit('exit_intent', {\n client_y: event.clientY,\n page_url: redactUrl(window.location.href),\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n }\n };\n \n private attachScrollVelocityListener(): void {\n window.addEventListener('scroll', this.handleScrollVelocity, { passive: true });\n }\n \n private removeScrollVelocityListener(): void {\n window.removeEventListener('scroll', this.handleScrollVelocity);\n }\n \n private handleScrollVelocity = (): void => {\n if (this.scrollVelocityFired) {\n return;\n }\n \n const currentY = window.scrollY || document.documentElement.scrollTop;\n const currentTime = Date.now();\n const timeDiff = currentTime - this.lastScrollTime;\n \n if (timeDiff > 100) {\n const distance = this.lastScrollY - currentY;\n const velocity = Math.abs(distance / timeDiff);\n \n if (\n distance > 0 &&\n velocity > this.scrollVelocityConfig.threshold &&\n currentY > this.scrollVelocityConfig.minScrollPosition\n ) {\n this.scrollVelocityFired = true;\n \n this.emit('mobile_exit_intent', {\n scroll_velocity: velocity,\n scroll_distance: distance,\n current_position: currentY,\n page_url: redactUrl(window.location.href),\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n \n this.scrollVelocityCooldownTimer = window.setTimeout(() => {\n this.scrollVelocityFired = false;\n }, this.scrollVelocityConfig.cooldown);\n }\n \n this.lastScrollY = currentY;\n this.lastScrollTime = currentTime;\n }\n };\n \n private attachVisibilityChangeListener(): void {\n document.addEventListener('visibilitychange', this.handleVisibilityChange);\n }\n \n private removeVisibilityChangeListener(): void {\n document.removeEventListener('visibilitychange', this.handleVisibilityChange);\n }\n \n private handleVisibilityChange = (): void => {\n if (document.hidden) {\n this.emit('visibility_hidden', {\n page_url: redactUrl(window.location.href),\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n } else {\n this.emit('visibility_visible', {\n page_url: redactUrl(window.location.href)\n });\n }\n };\n \n private attachBackButtonListener(): void {\n if (typeof window !== 'undefined' && window.history) {\n window.history.pushState(null, '', window.location.href);\n window.addEventListener('popstate', this.handleBackButton);\n }\n }\n \n private removeBackButtonListener(): void {\n window.removeEventListener('popstate', this.handleBackButton);\n }\n \n private handleBackButton = (): void => {\n if (this.backButtonFired) {\n return;\n }\n \n this.backButtonFired = true;\n \n window.history.pushState(null, '', window.location.href);\n \n this.emit('back_button', {\n page_url: redactUrl(window.location.href),\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n };\n \n private attachActivityListeners(): void {\n const events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];\n events.forEach(event => {\n document.addEventListener(event, this.handleActivity, { passive: true });\n });\n }\n \n private removeActivityListeners(): void {\n const events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];\n events.forEach(event => {\n document.removeEventListener(event, this.handleActivity);\n });\n }\n \n private handleActivity = (): void => {\n this.lastActivityTime = Date.now();\n };\n \n private startInactivityCheck(): void {\n this.inactivityCheckInterval = window.setInterval(() => {\n const idleTime = (Date.now() - this.lastActivityTime) / 1000;\n \n for (const [idleSeconds] of this.inactivityTargets) {\n if (idleTime >= idleSeconds) {\n this.emit(`inactivity_${idleSeconds}`, {\n idle_seconds: idleSeconds,\n actual_idle_time: idleTime\n });\n \n const timerId = this.inactivityTargets.get(idleSeconds);\n if (timerId) {\n clearTimeout(timerId);\n this.inactivityTargets.delete(idleSeconds);\n }\n }\n }\n }, 1000);\n }\n \n private emit(eventType: string, data: Record<string, any>): void {\n const event: TriggerEvent = {\n type: eventType,\n data,\n timestamp: Date.now()\n };\n \n const callbacks = this.listeners.get(eventType);\n if (callbacks) {\n callbacks.forEach(callback => {\n try {\n callback(event);\n } catch (error) {\n console.error(`Error in trigger callback for ${eventType}:`, error);\n }\n });\n }\n \n const wildcardCallbacks = this.listeners.get('*');\n if (wildcardCallbacks) {\n wildcardCallbacks.forEach(callback => {\n try {\n callback(event);\n } catch (error) {\n console.error(`Error in wildcard trigger callback:`, error);\n }\n });\n }\n }\n}\n","/**\n * Active Web Chat renderer — the \"Shopping Assistant\" chat launcher delivered\n * as a first-class in-app campaign widget (type `active_web_chat`).\n *\n * Unlike the preload-first renderers in this directory (pure DOM mutation, no\n * I/O), Active Web Chat is a *persistent launcher* backed by a stateful chat\n * engine. It does NOT reimplement chat transport — it instantiates the existing\n * `AegisChat` engine (chat/AegisChat.ts) and feeds it config from the campaign's\n * `interactive_config`. The chat SSE stays OFF until the customer opens the\n * panel and is torn down on close — that lifecycle lives entirely in AegisChat\n * (connectSSE/disconnectSSE are panel-gated); this renderer only wires the\n * campaign config + analytics bridge.\n *\n * Singleton per page: there is at most one chat launcher on screen. A repeat\n * delivery (SSE/poll refresh re-arming the same campaign) is a no-op.\n *\n * See docs/architecture/CHAT_WIDGET_AS_INAPP_CAMPAIGN_TRACKER.md.\n */\n\nimport { AegisChat, type AegisChatConfig } from '../../chat/AegisChat';\nimport { TriggerEngine } from '../../triggers/TriggerEngine';\n\nimport type { RenderContext } from './types';\n\n/** Inactivity window (seconds) that `cart_idle` proactive open maps to —\n * matches the legacy runtime's `inactivity_60` chat trigger. */\nconst PROACTIVE_IDLE_SECONDS = 60;\n\n/** SDK credentials the chat engine needs that the campaign payload doesn't\n * carry. Supplied by `AegisInAppManager` (which owns them). */\nexport interface ActiveWebChatCredentials {\n writeKey: string;\n apiHost: string;\n contactId?: string;\n channel?: 'web' | 'in_app';\n}\n\n/** Module-level singleton — at most one launcher per page. */\nlet activeInstance: AegisChat | null = null;\n/** Proactive-open trigger engine for the active launcher (Phase C). */\nlet activeTriggers: TriggerEngine | null = null;\n\nfunction str(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/**\n * Resolve the launcher presentation. Explicit `chat_display_mode` wins; when\n * unset we fall back to RESPONSIVE behaviour (parity with the legacy\n * store-config launcher): headless `'nav'` on phones — the host owns the entry\n * point (e.g. the storefront bottom-nav \"Ask\" tab) so the floating bubble\n * doesn't compete with the sticky cart bar — and `'bubble'` on desktop.\n */\nfunction resolveChatDisplayMode(raw: unknown): 'nav' | 'bubble' {\n if (raw === 'nav') return 'nav';\n if (raw === 'bubble') return 'bubble';\n const isMobile =\n typeof window !== 'undefined' &&\n window.matchMedia?.('(max-width: 767px)')?.matches === true;\n return isMobile ? 'nav' : 'bubble';\n}\n\n/**\n * Mount the Active Web Chat launcher for `ctx.campaign`. Returns the engine\n * instance (or the existing one if already mounted), or null in non-DOM\n * environments. The bubble is mounted immediately; the chat session (and its\n * SSE) is established only when the customer opens the panel.\n */\nexport function renderActiveWebChat(\n ctx: RenderContext,\n creds: ActiveWebChatCredentials,\n): AegisChat | null {\n if (typeof document === 'undefined') return null;\n // Persistent launcher: one per page. A re-delivery is a no-op (the open\n // session — if any — must not be torn down and re-created underneath the user).\n if (activeInstance) return activeInstance;\n\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n\n const accent = str(ic.chat_accent_color);\n const logo = str(ic.chat_logo_url);\n const icon = str(ic.chat_icon);\n\n const cfg: AegisChatConfig = {\n writeKey: creds.writeKey,\n apiHost: creds.apiHost,\n contactId: creds.contactId,\n channel: creds.channel ?? 'web',\n title: str(ic.chat_title),\n greeting: str(ic.chat_greeting),\n accentColor: accent ? ctx.sanitizeColor(accent) : undefined,\n icon: icon === 'sparkle' || icon === 'chat' || icon === 'logo' ? icon : undefined,\n logoUrl: logo ? ctx.sanitizeUrl(logo) ?? undefined : undefined,\n position: ic.chat_position === 'bottom-left' ? 'bottom-left' : undefined,\n surfaceStyle: ic.chat_theme === 'tinted' ? 'tinted' : undefined,\n // Explicit chat_display_mode wins; unset → responsive (phones = headless\n // 'nav' so the host's bottom-nav \"Ask\" tab is the entry; desktop = 'bubble').\n displayMode: resolveChatDisplayMode(ic.chat_display_mode),\n agentPersona: str(ic.chat_agent_persona),\n quickReplies: Array.isArray(ic.chat_quick_replies)\n ? (ic.chat_quick_replies as unknown[]).filter((q): q is string => typeof q === 'string')\n : undefined,\n // Bridge the engine's session lifecycle to campaign analytics. Per the\n // tracker's event mapping: panel open = engagement (`clicked`), panel close\n // = session end (`dismissed`), disambiguated by the `action` meta. The\n // bubble being visible is the `impression` — fired by the manager.\n onSessionStart: () =>\n ctx.trackEvent(ctx.campaign.id, 'clicked', { meta: { action: 'session_start' } }),\n onSessionEnd: () =>\n ctx.trackEvent(ctx.campaign.id, 'dismissed', { meta: { action: 'session_end' } }),\n onMessageSent: () =>\n ctx.trackEvent(ctx.campaign.id, 'clicked', { meta: { action: 'message_sent' } }),\n };\n\n const chat = new AegisChat(cfg);\n chat.initialize(); // mounts the bubble; NO chat SSE until the panel opens\n activeInstance = chat;\n\n // Phase C — proactive auto-open. Reuses the shared TriggerEngine (the same\n // primitive that powers chat proactive-open in AegisMessageRuntime). The\n // trigger ONLY opens the panel; openPanel() is a no-op if already open, and\n // exit-intent / inactivity each fire once, so this never nags.\n const proactive = (ic.chat_proactive || {}) as Record<string, unknown>;\n const wantExit = proactive.exit_intent === true;\n const wantIdle = proactive.cart_idle === true;\n if (wantExit || wantIdle) {\n const engine = new TriggerEngine();\n if (wantExit) {\n engine.registerExitIntent();\n engine.on('exit_intent', () => chat.openPanel());\n }\n if (wantIdle) {\n engine.registerInactivity(PROACTIVE_IDLE_SECONDS);\n engine.on(`inactivity_${PROACTIVE_IDLE_SECONDS}`, () => chat.openPanel());\n }\n engine.start();\n activeTriggers = engine;\n }\n\n return chat;\n}\n\n/** The single live launcher on the page, if mounted (campaign or legacy path). */\nexport function getActiveWebChat(): AegisChat | null {\n return activeInstance;\n}\n\n/** Canonical public open — used by host buttons (e.g. mobile bottom-nav \"Ask\")\n * and the `window.aegis.chat.open()` facade. Targets the ONE launcher instance;\n * no-op if no launcher is mounted (e.g. chat not enabled for this page). */\nexport function openActiveWebChat(prefill?: string): void {\n activeInstance?.openPanel(prefill);\n}\n\nexport function closeActiveWebChat(): void {\n activeInstance?.closePanel();\n}\n\n/** Tear down the launcher (manager teardown / page unload). */\nexport function disposeActiveWebChat(): void {\n activeTriggers?.stop();\n activeTriggers = null;\n activeInstance?.destroy();\n activeInstance = null;\n}\n","/**\n * Native review-wall renderer (Reviews Display & Integrity §3.5, mockup B′).\n *\n * The cross-surface PDP/storefront review widget. Unlike the preload-first\n * renderers in this directory, this one does REST reads at mount + on filter\n * change (the Active-Chat precedent for I/O-doing widgets) —\n * `GET {apiHost}/v1/reviews/public` via the gateway (writeKey-authed; tenant\n * resolved from the key). No SSE/socket (web-sdk is REST; review walls are a\n * browsing surface). Between fetches it is pure DOM, inline-styled in the\n * canonical Urbanist in-app font (IN_APP_FONT_STACK) — no external stylesheet,\n * works on any host incl. Shopify themes.\n *\n * Mounted into a `data-aegis-slot=\"reviews\"` anchor (widget_category='reviews',\n * sub_type='reviews', delivery_modes:['embedded_card']). Product read from the\n * slot's `data-product-id`. The SAME contract drives the native SDK + Shopify/\n * Woo/Magento embeds.\n *\n * Renders: a premium AI-summary card, AggregateRating + distribution panel, sort\n * + filter pills, and per card an avatar + verified-purchase badge (G1) + media\n * thumbs + an accent operator-reply card, plus `Review`/`AggregateRating`\n * JSON-LD (G4 SEO stars). The whole wall is centered (margin:auto).\n */\n\nimport type { RenderContext } from './types';\nimport { IN_APP_FONT_STACK, ensureInAppFont } from '../font';\n\n/** SDK credentials the review fetch needs (the campaign payload doesn't carry\n * them). Supplied by `AegisInAppManager`, same shape pattern as Active Chat. */\nexport interface ReviewsCredentials {\n writeKey: string;\n apiHost: string;\n}\n\ninterface ReviewItem {\n id: string;\n rating: number | null;\n review_text: string | null;\n verified_purchase: boolean;\n is_incentivized: boolean;\n media: Array<{ url: string; type?: string }>;\n operator_reply: { text: string } | null;\n reviewer_snapshot: { name?: string } | null;\n helpful_count: number;\n not_helpful_count: number;\n created_at: string | null;\n}\n\ninterface ReviewsAggregate {\n count: number;\n avg_rating: number | null;\n distribution: Record<string, number>;\n ai_summary?: { text: string; review_count: number } | null;\n}\n\ninterface ReviewsResponse {\n items: ReviewItem[];\n aggregate: ReviewsAggregate;\n}\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\nfunction el(tag: string, css?: string, text?: string): HTMLElement {\n const e = document.createElement(tag);\n if (css) e.style.cssText = css;\n if (text != null) e.textContent = text;\n return e;\n}\n\nfunction star(filled: boolean, size: number): SVGElement {\n const svg = document.createElementNS(SVG_NS, 'svg');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('width', String(size));\n svg.setAttribute('height', String(size));\n const p = document.createElementNS(SVG_NS, 'path');\n p.setAttribute(\n 'd',\n 'M12 2l2.9 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l7.1-1.01L12 2z',\n );\n p.setAttribute('fill', filled ? '#f59e0b' : '#e5e7eb');\n svg.appendChild(p);\n return svg;\n}\n\nfunction stars(rating: number, size = 15): HTMLElement {\n const wrap = el('span', 'display:inline-flex;gap:2px;vertical-align:middle;');\n wrap.setAttribute('aria-label', `${rating} out of 5`);\n for (let i = 1; i <= 5; i++) wrap.appendChild(star(i <= rating, size));\n return wrap;\n}\n\nfunction verifiedBadge(): HTMLElement {\n const b = el(\n 'span',\n 'display:inline-flex;align-items:center;gap:3px;font-size:12px;font-weight:600;color:#059669;',\n );\n const svg = document.createElementNS(SVG_NS, 'svg');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('width', '13');\n svg.setAttribute('height', '13');\n const p = document.createElementNS(SVG_NS, 'path');\n p.setAttribute('d', 'M9 12l2 2 4-4M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0z');\n p.setAttribute('fill', 'none');\n p.setAttribute('stroke', '#059669');\n p.setAttribute('stroke-width', '2');\n p.setAttribute('stroke-linecap', 'round');\n p.setAttribute('stroke-linejoin', 'round');\n svg.appendChild(p);\n b.appendChild(svg);\n b.appendChild(el('span', '', 'Verified purchase'));\n return b;\n}\n\n/** FTC/EU-Omnibus disclosure for reviews that earned a loyalty reward. Muted —\n * it's a transparency note, not a trust signal. Only rendered when the brand\n * opted into disclosure (the worker stamps `is_incentivized` only then). */\nfunction incentivizedBadge(): HTMLElement {\n const b = el(\n 'span',\n 'display:inline-flex;align-items:center;gap:3px;font-size:12px;font-weight:600;color:#6b7280;',\n );\n const svg = document.createElementNS(SVG_NS, 'svg');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('width', '13');\n svg.setAttribute('height', '13');\n const p = document.createElementNS(SVG_NS, 'path');\n // lucide \"gift\" glyph (rect + ribbon) simplified to one path.\n p.setAttribute('d', 'M20 12v9H4v-9M2 7h20v5H2zM12 7v14M12 7a2.5 2.5 0 0 0-2.5-2.5C8 4.5 7 6 7 7M12 7a2.5 2.5 0 0 1 2.5-2.5C16 4.5 17 6 17 7');\n p.setAttribute('fill', 'none');\n p.setAttribute('stroke', '#6b7280');\n p.setAttribute('stroke-width', '2');\n p.setAttribute('stroke-linecap', 'round');\n p.setAttribute('stroke-linejoin', 'round');\n svg.appendChild(p);\n b.appendChild(svg);\n b.appendChild(el('span', '', 'Incentivized'));\n return b;\n}\n\nfunction avatar(name: string, accent: string): HTMLElement {\n const initial = (name.trim()[0] || '?').toUpperCase();\n const a = el(\n 'span',\n `flex:0 0 auto;width:34px;height:34px;border-radius:999px;display:inline-flex;` +\n `align-items:center;justify-content:center;font-size:14px;font-weight:700;` +\n `color:${accent};background:${accent}1a;`,\n initial,\n );\n return a;\n}\n\nfunction thumbBtn(up: boolean, count: number, reviewId: string, creds: ReviewsCredentials): HTMLElement {\n const btn = el(\n 'button',\n 'display:inline-flex;align-items:center;gap:5px;font-size:13px;color:#6b7280;' +\n 'background:none;border:none;cursor:pointer;padding:4px 6px;border-radius:8px;font-family:inherit;',\n );\n const svg = document.createElementNS(SVG_NS, 'svg');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('width', '15');\n svg.setAttribute('height', '15');\n if (!up) svg.style.cssText = 'transform:rotate(180deg);';\n const p = document.createElementNS(SVG_NS, 'path');\n p.setAttribute(\n 'd',\n 'M2 21h4V9H2v12zM23 10a2 2 0 0 0-2-2h-6.31l.95-4.57.03-.32a1.5 1.5 0 0 0-.44-1.06L14.17 1 7.59 7.59A2 2 0 0 0 7 9v10a2 2 0 0 0 2 2h9a2 2 0 0 0 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-1z',\n );\n p.setAttribute('fill', 'currentColor');\n svg.appendChild(p);\n btn.appendChild(svg);\n const c = el('span', '', String(count));\n btn.appendChild(c);\n const key = `aegis_rv_voted_${reviewId}`;\n let voted = false;\n try { voted = typeof localStorage !== 'undefined' && localStorage.getItem(key) != null; } catch { /* */ }\n if (voted) btn.style.opacity = '0.5';\n btn.addEventListener('mouseenter', () => { if (!voted) btn.style.color = '#111827'; });\n btn.addEventListener('mouseleave', () => { btn.style.color = '#6b7280'; });\n btn.addEventListener('click', () => {\n try { if (localStorage.getItem(key) != null) return; localStorage.setItem(key, up ? 'up' : 'down'); } catch { /* */ }\n voted = true;\n c.textContent = String(count + 1);\n btn.style.opacity = '0.5';\n const url = new URL(`/v1/reviews/${reviewId}/helpful`, creds.apiHost);\n void fetch(url.toString(), {\n method: 'POST',\n headers: { 'X-Aegis-Write-Key': creds.writeKey, 'Content-Type': 'application/json' },\n body: JSON.stringify({ direction: up ? 'up' : 'down' }),\n }).catch(() => { /* soft signal */ });\n });\n return btn;\n}\n\nfunction injectJsonLd(target: HTMLElement, data: ReviewsResponse, productId: string | null): void {\n if (!data.aggregate.count || data.aggregate.avg_rating == null) return;\n const ld: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'Product',\n ...(productId ? { sku: productId } : {}),\n aggregateRating: {\n '@type': 'AggregateRating',\n ratingValue: data.aggregate.avg_rating,\n reviewCount: data.aggregate.count,\n },\n review: data.items.slice(0, 10).map((r) => ({\n '@type': 'Review',\n reviewRating: { '@type': 'Rating', ratingValue: r.rating ?? 0 },\n author: { '@type': 'Person', name: r.reviewer_snapshot?.name ?? 'Customer' },\n ...(r.review_text ? { reviewBody: r.review_text } : {}),\n })),\n };\n const script = document.createElement('script');\n script.type = 'application/ld+json';\n script.textContent = JSON.stringify(ld);\n target.appendChild(script);\n}\n\nconst SORTS: Array<{ key: string; label: string }> = [\n { key: 'recent', label: 'Most recent' },\n { key: 'helpful', label: 'Most helpful' },\n { key: 'rating', label: 'Highest rated' },\n];\n\ninterface WallState { sort: string; withPhotos: boolean }\n\nexport function renderReviewsInline(\n ctx: RenderContext,\n target: HTMLElement,\n creds: ReviewsCredentials,\n): boolean {\n if (typeof document === 'undefined' || !creds?.apiHost || !creds?.writeKey) return false;\n ensureInAppFont();\n\n const ic = (ctx.campaign.interactive_config || {}) as Record<string, unknown>;\n // The host-authored config attributes (data-product-id / data-brand-name /\n // data-accent) live on the [data-aegis-slot] element — but `target` is the\n // SDK-injected `[data-aegis-slot-rendered]` wrapper the manager appends INSIDE\n // that slot (see AegisInAppManager.renderIntoSlots). Resolve up to the slot so\n // we read the host's attributes, not the empty wrapper. Falls back to `target`\n // for callers that render directly into the configured element (preview).\n const cfg = (target.closest('[data-aegis-slot]') as HTMLElement | null) ?? target;\n // Accent: the host storefront passes its primary colour on the slot\n // (data-accent), so the wall matches the brand by default; falls back to the\n // campaign's background_color, then royal blue.\n const accent = ctx.sanitizeColor(\n cfg.getAttribute('data-accent') || (ctx.campaign.background_color as string) || '#4169e1',\n );\n // Reply-label brand: the host storefront knows its own name, so it passes it\n // on the slot (data-brand-name) — no API/cross-plane lookup. Take the part\n // before a \" | \" / \" · \" tagline separator so \"The Cake House | 100% Eggless\n // Bakery\" reads as \"The Cake House\". Falls back to campaign config, then a\n // neutral default.\n const rawBrand =\n (cfg.getAttribute('data-brand-name') || String(ic.brand_name || '')).trim();\n const brand = (rawBrand.split(/\\s[|·–-]\\s/)[0] || '').trim() || 'The store';\n\n const productId =\n cfg.getAttribute('data-product-id') ||\n (ctx.campaign as unknown as { product_id?: string }).product_id ||\n null;\n\n const state: WallState = { sort: 'recent', withPhotos: false };\n target.innerHTML = '';\n target.appendChild(el('div', `font-family:${IN_APP_FONT_STACK};font-size:13px;color:#9ca3af;padding:10px 0;`, 'Loading reviews…'));\n\n function buildUrl(): string {\n const url = new URL('/v1/reviews/public', creds.apiHost);\n if (productId) url.searchParams.set('product_id', productId);\n url.searchParams.set('sort', state.sort);\n if (state.withPhotos) url.searchParams.set('with_photos', 'true');\n url.searchParams.set('limit', '20');\n return url.toString();\n }\n function fetchReviews(): Promise<ReviewsResponse | null> {\n // no-store: never serve a browser-cached body — an early load (before the\n // AI summary / a new review) must not stick.\n return fetch(buildUrl(), { method: 'GET', cache: 'no-store', headers: { 'X-Aegis-Write-Key': creds.writeKey } })\n .then((r) => (r.ok ? (r.json() as Promise<ReviewsResponse>) : null))\n .catch(() => null);\n }\n\n let listContainer: HTMLElement | null = null;\n\n function reviewCard(r: ReviewItem): HTMLElement {\n const li = el('li', 'padding:22px 0;border-top:1px solid #f0f1f3;');\n\n const head = el('div', 'display:flex;align-items:center;gap:10px;');\n head.appendChild(avatar(r.reviewer_snapshot?.name ?? 'Customer', accent));\n const idn = el('div', 'display:flex;flex-direction:column;gap:2px;min-width:0;');\n const nameRow = el('div', 'display:flex;flex-wrap:wrap;align-items:center;gap:8px;');\n nameRow.appendChild(el('span', 'font-size:15px;font-weight:700;color:#111827;', r.reviewer_snapshot?.name ?? 'Customer'));\n if (r.verified_purchase) nameRow.appendChild(verifiedBadge());\n if (r.is_incentivized) nameRow.appendChild(incentivizedBadge());\n idn.appendChild(nameRow);\n idn.appendChild(stars(r.rating ?? 0, 14));\n head.appendChild(idn);\n if (r.created_at) {\n const d = new Date(r.created_at);\n if (!Number.isNaN(d.getTime())) {\n head.appendChild(el('span', 'margin-left:auto;font-size:12px;color:#9ca3af;white-space:nowrap;',\n d.toLocaleDateString(undefined, { day: 'numeric', month: 'short', year: 'numeric' })));\n }\n }\n li.appendChild(head);\n\n if (r.review_text) {\n li.appendChild(el('p', 'margin:12px 0 0;font-size:15px;line-height:1.6;color:#374151;', r.review_text));\n }\n\n if (r.media && r.media.length > 0) {\n const gallery = el('div', 'display:flex;flex-wrap:wrap;gap:8px;margin-top:12px;');\n for (const m of r.media.slice(0, 6)) {\n const safe = ctx.sanitizeUrl(m.url);\n if (!safe) continue;\n const img = document.createElement('img');\n img.src = safe; img.loading = 'lazy'; img.alt = '';\n img.style.cssText = 'width:72px;height:72px;object-fit:cover;border-radius:12px;border:1px solid #f0f1f3;';\n gallery.appendChild(img);\n }\n if (gallery.childElementCount) li.appendChild(gallery);\n }\n\n const foot = el('div', 'display:flex;align-items:center;gap:6px;margin-top:12px;');\n foot.appendChild(el('span', 'font-size:13px;color:#9ca3af;margin-right:2px;', 'Was this helpful?'));\n foot.appendChild(thumbBtn(true, r.helpful_count ?? 0, r.id, creds));\n foot.appendChild(thumbBtn(false, r.not_helpful_count ?? 0, r.id, creds));\n li.appendChild(foot);\n\n if (r.operator_reply?.text) {\n const reply = el(\n 'div',\n `margin-top:14px;margin-left:44px;border-left:3px solid ${accent};` +\n `background:${accent}0d;border-radius:0 12px 12px 0;padding:12px 14px;`,\n );\n const rlabel = el('div', 'display:flex;align-items:center;gap:6px;margin-bottom:5px;');\n rlabel.appendChild(el('span', `width:7px;height:7px;border-radius:999px;background:${accent};display:inline-block;`));\n rlabel.appendChild(el('span', `font-size:12px;font-weight:700;color:${accent};`, brand));\n rlabel.appendChild(el('span', 'font-size:12px;color:#9ca3af;', '· replied'));\n reply.appendChild(rlabel);\n reply.appendChild(el('p', 'margin:0;font-size:14px;line-height:1.55;color:#4b5563;', r.operator_reply.text));\n li.appendChild(reply);\n }\n return li;\n }\n\n function renderList(items: ReviewItem[]): void {\n if (!listContainer) return;\n listContainer.innerHTML = '';\n if (items.length === 0) {\n listContainer.appendChild(el('p', 'padding:20px 0;font-size:14px;color:#9ca3af;text-align:center;', 'No reviews match this filter.'));\n return;\n }\n const ul = el('ul', 'list-style:none;margin:0;padding:0;');\n for (const r of items) ul.appendChild(reviewCard(r));\n listContainer.appendChild(ul);\n }\n\n function pill(label: string, active: boolean, onClick: () => void): HTMLElement {\n const b = el(\n 'button',\n `font-size:13px;font-weight:600;padding:7px 16px;border-radius:999px;cursor:pointer;font-family:inherit;` +\n (active\n ? `background:${accent};color:#fff;border:1px solid ${accent};`\n : 'background:#fff;color:#374151;border:1px solid #e5e7eb;'),\n label,\n );\n b.addEventListener('click', onClick);\n return b;\n }\n\n function controls(rerender: () => void): HTMLElement {\n const bar = el('div', 'display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:18px 0 4px;');\n const sel = document.createElement('select');\n sel.style.cssText =\n `font-size:13px;font-weight:600;padding:7px 30px 7px 14px;border:1px solid #e5e7eb;border-radius:999px;` +\n `background:#fff;color:#374151;cursor:pointer;font-family:inherit;appearance:none;` +\n `background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E\");` +\n `background-repeat:no-repeat;background-position:right 12px center;`;\n for (const s of SORTS) {\n const o = document.createElement('option'); o.value = s.key; o.textContent = s.label; sel.appendChild(o);\n }\n sel.value = state.sort;\n sel.addEventListener('change', () => { state.sort = sel.value; rerender(); });\n bar.appendChild(sel);\n let photosBtn: HTMLElement | null = null;\n photosBtn = pill('With photos', state.withPhotos, () => {\n state.withPhotos = !state.withPhotos;\n if (photosBtn) {\n photosBtn.style.cssText = `font-size:13px;font-weight:600;padding:7px 16px;border-radius:999px;cursor:pointer;font-family:inherit;` +\n (state.withPhotos ? `background:${accent};color:#fff;border:1px solid ${accent};` : 'background:#fff;color:#374151;border:1px solid #e5e7eb;');\n }\n rerender();\n });\n bar.appendChild(photosBtn);\n return bar;\n }\n\n function distributionPanel(dist: Record<string, number>, total: number): HTMLElement {\n const box = el('div', 'flex:0 1 300px;width:300px;max-width:100%;display:flex;flex-direction:column;gap:7px;');\n for (let r = 5; r >= 1; r--) {\n const n = dist[String(r)] ?? 0;\n const pct = total > 0 ? Math.round((n / total) * 100) : 0;\n const row = el('div', 'display:flex;align-items:center;gap:10px;font-size:13px;color:#6b7280;');\n const lbl = el('span', 'width:30px;display:inline-flex;align-items:center;gap:3px;color:#374151;font-weight:600;', String(r));\n lbl.appendChild(star(true, 11));\n row.appendChild(lbl);\n const track = el('div', 'flex:1;height:8px;background:#f0f1f3;border-radius:999px;overflow:hidden;');\n track.appendChild(el('div', `width:${pct}%;height:100%;background:${accent};border-radius:999px;`));\n row.appendChild(track);\n row.appendChild(el('span', 'width:24px;text-align:right;font-variant-numeric:tabular-nums;', String(n)));\n box.appendChild(row);\n }\n return box;\n }\n\n function aiSummaryCard(summary: { text: string; review_count: number }): HTMLElement {\n const card = el(\n 'div',\n 'position:relative;border-radius:18px;padding:18px 20px;margin-bottom:24px;' +\n 'background:linear-gradient(135deg,#faf5ff 0%,#f5f3ff 100%);border:1px solid #ede9fe;',\n );\n const head = el('div', 'display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;');\n const left = el('div', 'display:flex;align-items:center;gap:7px;');\n const sp = document.createElementNS(SVG_NS, 'svg');\n sp.setAttribute('viewBox', '0 0 24 24'); sp.setAttribute('width', '16'); sp.setAttribute('height', '16');\n const spp = document.createElementNS(SVG_NS, 'path');\n spp.setAttribute('d', 'M12 3l1.6 4.4L18 9l-4.4 1.6L12 15l-1.6-4.4L6 9l4.4-1.6L12 3z');\n spp.setAttribute('fill', '#7c3aed'); sp.appendChild(spp);\n left.appendChild(sp);\n left.appendChild(el('span', 'font-size:12px;font-weight:800;letter-spacing:.07em;color:#6d28d9;text-transform:uppercase;', 'What customers say'));\n head.appendChild(left);\n head.appendChild(el('span', 'font-size:11px;font-weight:600;color:#a78bda;', 'AI-generated'));\n card.appendChild(head);\n card.appendChild(el('p', 'margin:0;font-size:15px;line-height:1.6;color:#3b2a63;', summary.text));\n card.appendChild(el('p', 'margin:8px 0 0;font-size:12px;color:#a78bda;text-align:right;', `from ${summary.review_count} reviews`));\n return card;\n }\n\n function renderWall(data: ReviewsResponse): void {\n target.innerHTML = '';\n const root = el(\n 'section',\n `font-family:${IN_APP_FONT_STACK};color:#111827;max-width:860px;margin:0 auto;` +\n '-webkit-font-smoothing:antialiased;',\n );\n root.setAttribute('data-aegis-reviews', 'true');\n\n const summary = data.aggregate.ai_summary;\n if (summary && summary.text) root.appendChild(aiSummaryCard(summary));\n\n if (data.aggregate.count === 0) {\n root.appendChild(el('p', 'font-size:15px;color:#9ca3af;padding:6px 0;', 'No reviews yet — be the first to share your experience.'));\n target.appendChild(root);\n return;\n }\n\n // Header: rating block pinned left, distribution panel pushed to the right\n // edge (space-between). On wrap (mobile) the panel stacks below, left-aligned.\n const header = el('div', 'display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:24px 40px;padding-bottom:4px;');\n const ratingBlock = el('div', 'display:flex;flex-direction:column;gap:4px;');\n const topRow = el('div', 'display:flex;align-items:center;gap:12px;');\n const avg = data.aggregate.avg_rating ?? 0;\n topRow.appendChild(stars(Math.round(avg), 20));\n topRow.appendChild(el('span', 'font-size:32px;font-weight:800;line-height:1;letter-spacing:-0.02em;', avg ? avg.toFixed(1) : '—'));\n ratingBlock.appendChild(topRow);\n ratingBlock.appendChild(el('span', 'font-size:13px;color:#6b7280;', `${data.aggregate.count} review${data.aggregate.count === 1 ? '' : 's'} · 100% verified`));\n header.appendChild(ratingBlock);\n header.appendChild(distributionPanel(data.aggregate.distribution, data.aggregate.count));\n root.appendChild(header);\n\n root.appendChild(controls(() => { void fetchReviews().then((d) => renderList(d?.items ?? [])); }));\n\n listContainer = el('div', 'margin-top:2px;');\n root.appendChild(listContainer);\n renderList(data.items);\n\n target.appendChild(root);\n injectJsonLd(target, data, productId);\n ctx.trackEvent(ctx.campaign.id, 'impression');\n }\n\n // Compact teaser (mockup B) — opt-in via interactive_config.display_style.\n const compact = ic.display_style === 'compact';\n function renderCompact(data: ReviewsResponse): void {\n target.innerHTML = '';\n if (data.aggregate.count === 0) return;\n const root = el(\n 'section',\n `font-family:${IN_APP_FONT_STACK};color:#111827;max-width:860px;margin:0 auto;` +\n 'border:1px solid #f0f1f3;border-radius:16px;padding:16px 18px;',\n );\n const line = el('div', 'display:flex;align-items:center;gap:8px;');\n const avg = data.aggregate.avg_rating ?? 0;\n line.appendChild(stars(Math.round(avg), 16));\n line.appendChild(el('span', 'font-size:16px;font-weight:700;', avg ? avg.toFixed(1) : '—'));\n line.appendChild(el('span', 'font-size:13px;color:#6b7280;', `(${data.aggregate.count})`));\n line.appendChild(verifiedBadge());\n root.appendChild(line);\n const top = data.items.find((i) => i.review_text);\n if (top && top.review_text) {\n root.appendChild(el('p', 'margin:10px 0 0;font-size:15px;color:#374151;line-height:1.5;', `“${top.review_text}”`));\n root.appendChild(el('span', 'font-size:13px;color:#9ca3af;', `— ${top.reviewer_snapshot?.name ?? 'Customer'}`));\n }\n const seeAll = el('button', `margin-top:12px;font-size:14px;font-weight:700;color:${accent};background:none;border:none;cursor:pointer;padding:0;font-family:inherit;`, 'See all reviews →');\n seeAll.addEventListener('click', () => renderWall(data));\n root.appendChild(seeAll);\n target.appendChild(root);\n ctx.trackEvent(ctx.campaign.id, 'impression');\n }\n\n void fetchReviews().then((data) => {\n if (!data || !Array.isArray(data.items)) { target.innerHTML = ''; return; }\n if (compact) renderCompact(data); else renderWall(data);\n });\n return true;\n}\n\n/**\n * Overlay variant (mockup C) — a slide-up bottom sheet containing the full\n * review wall. For a reviews campaign whose delivery_modes include\n * 'in_app_overlay' (typically client-triggered). Mounts the SAME\n * renderReviewsInline into the sheet body.\n */\nexport function renderReviewsOverlay(ctx: RenderContext, creds: ReviewsCredentials): void {\n if (typeof document === 'undefined' || !creds?.apiHost || !creds?.writeKey) return;\n ensureInAppFont();\n const overlay = document.createElement('div');\n overlay.setAttribute('data-campaign-id', ctx.campaign.id);\n overlay.style.cssText =\n 'position:fixed;inset:0;background:rgba(17,24,39,0.5);display:flex;align-items:flex-end;justify-content:center;z-index:99999;';\n const sheet = document.createElement('div');\n sheet.style.cssText =\n `background:#fff;color:#111827;max-width:680px;width:100%;max-height:84vh;overflow-y:auto;` +\n `border-radius:24px 24px 0 0;padding:18px 18px 28px;font-family:${IN_APP_FONT_STACK};box-shadow:0 -16px 40px rgba(0,0,0,0.14);`;\n sheet.appendChild(el('div', 'width:38px;height:4px;border-radius:999px;background:#e5e7eb;margin:0 auto 14px;'));\n const bar = el('div', 'display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;');\n bar.appendChild(el('div', 'font-weight:800;font-size:17px;', ctx.campaign.title || 'Customer reviews'));\n const close = el('button', 'background:none;border:none;cursor:pointer;padding:4px 8px;color:#6b7280;');\n const x = document.createElementNS(SVG_NS, 'svg');\n x.setAttribute('viewBox', '0 0 24 24'); x.setAttribute('width', '20'); x.setAttribute('height', '20');\n const xp = document.createElementNS(SVG_NS, 'path');\n xp.setAttribute('d', 'M18 6L6 18M6 6l12 12'); xp.setAttribute('stroke', 'currentColor');\n xp.setAttribute('stroke-width', '2'); xp.setAttribute('stroke-linecap', 'round'); x.appendChild(xp);\n close.appendChild(x); close.setAttribute('aria-label', 'Close');\n close.addEventListener('click', () => { ctx.trackEvent(ctx.campaign.id, 'dismissed'); overlay.remove(); });\n bar.appendChild(close);\n sheet.appendChild(bar);\n const body = el('div');\n const pid = (ctx.campaign as unknown as { product_id?: string }).product_id;\n if (pid) body.setAttribute('data-product-id', pid);\n sheet.appendChild(body);\n overlay.appendChild(sheet);\n overlay.addEventListener('click', (e) => { if (e.target === overlay) { ctx.trackEvent(ctx.campaign.id, 'dismissed'); overlay.remove(); } });\n document.body.appendChild(overlay);\n renderReviewsInline(ctx, body, creds);\n}\n","/**\n * The ONE scratch-off engine (REWARDS_WALLET_PARITY_SPEC Phase 2, Model B).\n *\n * A foil canvas the customer scratches away from a prize layer, with a taint-free\n * coverage grid, idle shimmer, dust flecks and tap-to-reveal. It is mounted by the\n * in-flow scratch card (`AegisInAppManager.renderScratchCardSlot`) AND by the\n * cashier-portal wallet teaser, so a card scratched in the flow and a card\n * scratched later from the wallet are the same object. It owns the foil and the\n * gesture and nothing else: what is underneath, the sound and the win rendering\n * are the caller's, and `onThreshold` is the single seam where the caller asks its\n * server to reveal.\n */\n\nexport interface ScratchFoilPalette {\n g0: string;\n g1: string;\n g2: string;\n ink: string;\n}\n\n/** Brushed-metal palette + contrast ink from a brand colour, or default silver. */\nexport function scratchFoilPalette(hex?: string): ScratchFoilPalette {\n const clean = (hex || '').trim().replace(/^#/, '');\n if (!/^[0-9a-fA-F]{6}$/.test(clean)) {\n return { g0: '#d7dbe2', g1: '#b4bac4', g2: '#c9cdd6', ink: '#6b7280' };\n }\n const r = parseInt(clean.slice(0, 2), 16);\n const g = parseInt(clean.slice(2, 4), 16);\n const b = parseInt(clean.slice(4, 6), 16);\n const toward = (amt: number): string => {\n const m = (c: number): number => Math.round(c + (255 - c) * amt);\n return `rgb(${m(r)}, ${m(g)}, ${m(b)})`;\n };\n const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n return {\n g0: toward(0.34),\n g1: `#${clean}`,\n g2: toward(0.18),\n ink: lum > 0.62 ? '#5b6270' : 'rgba(255,255,255,0.92)',\n };\n}\n\nexport interface ScratchSurfaceOptions {\n /** Layer shown under the foil (the prize, or a neutral teaser). */\n prizeLayer: HTMLElement;\n /** Fraction of the foil that must be cleared (0.1..1). Default 0.5. */\n thresholdPct?: number;\n brushRadius?: number;\n palette?: ScratchFoilPalette;\n /** Optional brand logo painted on the foil (CORS-enabled URL). */\n foilImageUrl?: string;\n idleShimmer?: boolean;\n /** A single tap (no drag) reveals with a burst. Default true. */\n tapToReveal?: boolean;\n /** Called on every second scratch stroke (sound / haptics hook). */\n onScratch?: () => void;\n /**\n * Fires ONCE when the threshold is crossed. Resolve `false` to say the reveal\n * did not happen (network / refusal): the foil is put back so the customer can\n * scratch again. Any other result keeps the card revealed.\n */\n onThreshold: () => Promise<boolean | void> | boolean | void;\n}\n\nexport interface ScratchSurface {\n stage: HTMLElement;\n canvas: HTMLCanvasElement;\n isRevealed(): boolean;\n /** Reveal without scratching (keyboard / assistive-tech path). */\n reveal(): void;\n destroy(): void;\n}\n\nconst KEYFRAMES_ID = 'aegis-scratch-kf';\n\nfunction ensureKeyframes(): void {\n if (typeof document === 'undefined' || document.getElementById(KEYFRAMES_ID)) return;\n const kf = document.createElement('style');\n kf.id = KEYFRAMES_ID;\n kf.textContent =\n '@keyframes aegisScratchSheen{0%{transform:translateX(-140%) skewX(-12deg)}100%{transform:translateX(260%) skewX(-12deg)}}' +\n '@keyframes aegisScratchPop{0%{opacity:0;transform:scale(0.8)}100%{opacity:1;transform:scale(1)}}' +\n '@keyframes aegisScratchDust{0%{opacity:0.9;transform:translate(0,0) scale(1)}100%{opacity:0;transform:translate(var(--dx),var(--dy)) scale(0.4)}}';\n document.head.appendChild(kf);\n}\n\n/**\n * Send a \"deal\" at most once, and only for an identified contact. An anonymous\n * card cannot be held in a wallet, so it keeps the single-call grant-at-threshold\n * path and never deals. Every call returns the SAME in-flight promise (null when\n * anonymous), so the reveal can wait for the deal and never overtake it.\n */\nexport function createDealOnce(\n isIdentified: () => boolean,\n send: () => Promise<unknown>,\n): () => Promise<unknown> | null {\n let pending: Promise<unknown> | null = null;\n return (): Promise<unknown> | null => {\n if (pending) return pending;\n if (!isIdentified()) return null;\n pending = Promise.resolve(send()).catch(() => null);\n return pending;\n };\n}\n\nexport function mountScratchSurface(host: HTMLElement, opts: ScratchSurfaceOptions): ScratchSurface {\n ensureKeyframes();\n const threshold = Math.min(1, Math.max(0.1, opts.thresholdPct ?? 0.5));\n const brush = Math.min(60, Math.max(8, opts.brushRadius ?? 22));\n const pal = opts.palette ?? scratchFoilPalette();\n const tapReveals = opts.tapToReveal !== false;\n\n let revealed = false;\n let scratching = false;\n let dragged = false;\n let moves = 0;\n let destroyed = false;\n\n const stage = document.createElement('div');\n stage.style.cssText =\n 'position: relative; width: 280px; max-width: 100%; aspect-ratio: 3 / 2; border-radius: 16px; overflow: hidden; box-shadow: 0 4px 14px rgba(0,0,0,0.12);';\n stage.appendChild(opts.prizeLayer);\n\n const canvas = document.createElement('canvas');\n canvas.width = 300;\n canvas.height = 200;\n canvas.style.cssText = 'position: absolute; inset: 0; width: 100%; height: 100%; cursor: grab; touch-action: none;';\n stage.appendChild(canvas);\n const ctx = canvas.getContext('2d');\n const CW = canvas.width;\n const CH = canvas.height;\n\n // Foil = brushed metal + EITHER one centred brand logo OR a sparkle motif.\n // Coverage uses the grid below (NOT getImageData), so a cross-origin logo\n // cannot taint the canvas.\n const paintFoil = (logo?: HTMLCanvasElement | null): void => {\n if (!ctx) return;\n ctx.globalCompositeOperation = 'source-over';\n const foil = ctx.createLinearGradient(0, 0, CW, CH);\n foil.addColorStop(0, pal.g0);\n foil.addColorStop(0.5, pal.g1);\n foil.addColorStop(1, pal.g2);\n ctx.fillStyle = foil;\n ctx.fillRect(0, 0, CW, CH);\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n if (logo) {\n ctx.globalAlpha = 0.6;\n ctx.drawImage(logo, (CW - logo.width) / 2, CH * 0.42 - logo.height / 2, logo.width, logo.height);\n ctx.globalAlpha = 1;\n ctx.fillStyle = pal.ink;\n ctx.font = '700 12px system-ui, sans-serif';\n ctx.fillText('SCRATCH HERE', CW / 2, CH * 0.84);\n } else {\n ctx.globalAlpha = 0.22;\n ctx.fillStyle = pal.ink;\n ctx.font = '12px system-ui, sans-serif';\n for (let gy = 18; gy < CH; gy += 30) {\n const off = ((gy / 30) | 0) % 2 ? 16 : 0;\n for (let gx = 16 + off; gx < CW; gx += 32) ctx.fillText('✦', gx, gy); // eslint-disable-line no-restricted-syntax -- game content (scratch foil sparkle texture)\n }\n ctx.globalAlpha = 1;\n ctx.fillStyle = pal.ink;\n ctx.font = '700 13px system-ui, sans-serif';\n ctx.fillText('SCRATCH HERE', CW / 2, CH / 2);\n }\n ctx.globalCompositeOperation = 'destination-out';\n };\n let logoCanvas: HTMLCanvasElement | null = null;\n if (ctx) {\n paintFoil(null);\n if (opts.foilImageUrl) {\n const im = new Image();\n im.crossOrigin = 'anonymous';\n im.onload = (): void => {\n if (revealed || destroyed || !ctx) return;\n const maxW = CW * 0.52;\n const maxH = CH * 0.42;\n const ar = im.width / im.height || 1;\n let lw = maxW;\n let lh = maxW / ar;\n if (lh > maxH) {\n lh = maxH;\n lw = maxH * ar;\n }\n const lc = document.createElement('canvas');\n lc.width = Math.max(1, Math.round(lw));\n lc.height = Math.max(1, Math.round(lh));\n const lctx = lc.getContext('2d');\n if (!lctx) return;\n lctx.drawImage(im, 0, 0, lc.width, lc.height);\n lctx.globalCompositeOperation = 'source-in';\n lctx.fillStyle = pal.ink;\n lctx.fillRect(0, 0, lc.width, lc.height);\n logoCanvas = lc;\n paintFoil(lc);\n };\n im.onerror = (): void => { /* keep the generated foil */ };\n im.src = opts.foilImageUrl;\n }\n }\n\n // Coverage tracker — grid cells flip as the brush passes.\n const GX = 30;\n const GY = 20;\n const TOTAL = GX * GY;\n let cells = new Uint8Array(TOTAL);\n let clearedCells = 0;\n const markCleared = (x: number, y: number): void => {\n const cw = CW / GX;\n const ch = CH / GY;\n const r = brush;\n const x0 = Math.max(0, Math.floor((x - r) / cw));\n const x1 = Math.min(GX - 1, Math.floor((x + r) / cw));\n const y0 = Math.max(0, Math.floor((y - r) / ch));\n const y1 = Math.min(GY - 1, Math.floor((y + r) / ch));\n for (let cy = y0; cy <= y1; cy++) {\n for (let cx = x0; cx <= x1; cx++) {\n const idx = cy * GX + cx;\n if (cells[idx]) continue;\n const dx = (cx + 0.5) * cw - x;\n const dy = (cy + 0.5) * ch - y;\n if (dx * dx + dy * dy <= r * r) {\n cells[idx] = 1;\n clearedCells++;\n }\n }\n }\n };\n\n let shimmer: HTMLElement | null = null;\n if (opts.idleShimmer !== false) {\n const clip = document.createElement('div');\n clip.style.cssText = 'position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 2;';\n const bar = document.createElement('div');\n bar.style.cssText =\n 'position: absolute; top: -20%; left: 0; width: 34%; height: 140%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.55), transparent); animation: aegisScratchSheen 2.6s ease-in-out infinite; animation-delay: 0.4s;';\n clip.appendChild(bar);\n shimmer = clip;\n stage.appendChild(clip);\n }\n const dust = document.createElement('div');\n dust.style.cssText = 'position: absolute; inset: 0; pointer-events: none; z-index: 3;';\n stage.appendChild(dust);\n\n const spawnDust = (cx: number, cy: number, n = 2): void => {\n const px = (cx / canvas.width) * 100;\n const py = (cy / canvas.height) * 100;\n for (let i = 0; i < n; i++) {\n const d = document.createElement('div');\n const dx = ((i * 53 + moves * 17) % 30) - 15;\n const dy = 8 + ((moves * 13 + i * 7) % 14);\n d.style.cssText = `position: absolute; left: ${px}%; top: ${py}%; width: 3px; height: 3px; border-radius: 50%; background: #b4bac4; --dx:${dx}px; --dy:${dy}px; animation: aegisScratchDust 0.5s ease-out forwards;`;\n dust.appendChild(d);\n window.setTimeout(() => d.remove(), 520);\n }\n };\n\n const rearm = (): void => {\n revealed = false;\n cells = new Uint8Array(TOTAL);\n clearedCells = 0;\n moves = 0;\n canvas.style.opacity = '1';\n paintFoil(logoCanvas);\n };\n\n const finish = (): void => {\n if (revealed || destroyed) return;\n revealed = true;\n try { shimmer?.remove(); } catch { /* noop */ }\n canvas.style.transition = 'opacity 0.4s ease';\n canvas.style.opacity = '0';\n let result: Promise<boolean | void> | boolean | void;\n try {\n result = opts.onThreshold();\n } catch {\n rearm();\n return;\n }\n void Promise.resolve(result).then(\n (ok) => { if (ok === false && !destroyed) rearm(); },\n () => { if (!destroyed) rearm(); },\n );\n };\n\n const scratchAt = (x: number, y: number): void => {\n if (revealed) return;\n if (!ctx) return finish();\n moves += 1;\n ctx.beginPath();\n ctx.arc(x, y, brush, 0, Math.PI * 2);\n ctx.fill();\n markCleared(x, y);\n if (moves % 2 === 0) {\n opts.onScratch?.();\n spawnDust(x, y);\n }\n if (clearedCells / TOTAL > threshold) finish();\n };\n const pos = (e: PointerEvent): { x: number; y: number } => {\n const r = canvas.getBoundingClientRect();\n return { x: ((e.clientX - r.left) / r.width) * canvas.width, y: ((e.clientY - r.top) / r.height) * canvas.height };\n };\n let downAt: { x: number; y: number } | null = null;\n const onDown = (e: PointerEvent): void => {\n if (revealed) return;\n scratching = true;\n dragged = false;\n try { canvas.setPointerCapture(e.pointerId); } catch { /* noop */ }\n const p = pos(e);\n downAt = p;\n scratchAt(p.x, p.y);\n };\n const onMove = (e: PointerEvent): void => {\n if (!scratching) return;\n const p = pos(e);\n if (downAt && Math.hypot(p.x - downAt.x, p.y - downAt.y) > 6) dragged = true;\n scratchAt(p.x, p.y);\n };\n const onUp = (): void => {\n const wasTap = scratching && !dragged && downAt;\n scratching = false;\n if (tapReveals && wasTap && !revealed && downAt) {\n spawnDust(downAt.x, downAt.y, 10); // the single-tap burst\n finish();\n }\n downAt = null;\n };\n const onCancel = (): void => { scratching = false; downAt = null; };\n canvas.addEventListener('pointerdown', onDown);\n canvas.addEventListener('pointermove', onMove);\n canvas.addEventListener('pointerup', onUp);\n canvas.addEventListener('pointercancel', onCancel);\n canvas.addEventListener('pointerleave', onCancel);\n\n host.appendChild(stage);\n\n return {\n stage,\n canvas,\n isRevealed: () => revealed,\n reveal: finish,\n destroy: (): void => {\n destroyed = true;\n canvas.removeEventListener('pointerdown', onDown);\n canvas.removeEventListener('pointermove', onMove);\n canvas.removeEventListener('pointerup', onUp);\n canvas.removeEventListener('pointercancel', onCancel);\n canvas.removeEventListener('pointerleave', onCancel);\n stage.remove();\n },\n };\n}\n\n/**\n * The server's verdict on a scratch reveal. The response has no top-level `won`:\n * a loss is `prize.win === false` (or a `no_prize` slice) with no reward, and a\n * win is `prize.win === true` or a granted reward. Reading only `won` /\n * `reward.granted` rendered every loss as a win with the sample prize label.\n * Returns `undefined` only when the response says neither.\n */\nexport function scratchOutcome(resp: Record<string, unknown>): boolean | undefined {\n const reward = resp.reward as Record<string, unknown> | null | undefined;\n const prize = resp.prize as Record<string, unknown> | null | undefined;\n if (resp.won === true || reward?.granted === true || prize?.win === true) return true;\n if (resp.won === false || prize?.win === false || prize?.prize_type === 'no_prize') return false;\n return undefined;\n}\n","/**\n * New-visitor verdict — the ONE reader and writer of the returning-visitor\n * marker (2026-09-13).\n *\n * `/v1/in-app/active` and `/v1/sdk/prefetch-bundle` both take `is_new_user`,\n * and the server drops any campaign whose `display_rules.show_to_new_users_only`\n * is set when it arrives false. The SDK used to write `aegis_returning_user=1`\n * at the top of `AegisInAppManager.initialize()` and read it afterwards, inside\n * the fetch — so the read always saw its own write, every fetch said\n * `is_new_user=false`, and a new-users-only campaign could never be served.\n *\n * The rule this module enforces: the marker is read ONCE, before anything\n * writes it, and the verdict is held for the whole browsing session.\n *\n * - `isNewVisitor()` resolves the verdict on first call and caches it in\n * memory (this page load) and in sessionStorage (later page loads in the\n * same tab session), so every fetch in a session sends the same value and\n * the verdict does not flip after the marker is written.\n * - `markReturningVisitor()` is the only writer. It resolves the verdict\n * first, so no call order can make the read see the write.\n *\n * Storage unavailable (SSR, blocked site data, a browser that throws on\n * access): the verdict is RETURNING (`false`). \"New\" is a positive claim that\n * unlocks first-visit-only messages; when the SDK cannot remember a visit it\n * cannot prove one never happened, and because the marker can never be saved,\n * answering \"new\" would re-show a first-visit message on every page of every\n * visit forever. `false` is also what the server assumes when the parameter is\n * absent, so an unknown visitor reads the same on both sides.\n */\n\nconst RETURNING_MARKER_KEY = 'aegis_returning_user';\nconst SESSION_VERDICT_KEY = 'aegis_new_visitor_session';\n\nlet cachedVerdict: boolean | null = null;\n\nfunction readSessionVerdict(): boolean | null {\n try {\n if (typeof sessionStorage === 'undefined') return null;\n const stored = sessionStorage.getItem(SESSION_VERDICT_KEY);\n if (stored === '1') return true;\n if (stored === '0') return false;\n return null;\n } catch {\n return null;\n }\n}\n\nfunction writeSessionVerdict(verdict: boolean): void {\n try {\n if (typeof sessionStorage === 'undefined') return;\n sessionStorage.setItem(SESSION_VERDICT_KEY, verdict ? '1' : '0');\n } catch {\n // sessionStorage blocked — the in-memory cache still holds this page load\n }\n}\n\nfunction readMarkerVerdict(): boolean {\n try {\n if (typeof localStorage === 'undefined') return false;\n return localStorage.getItem(RETURNING_MARKER_KEY) === null;\n } catch {\n return false;\n }\n}\n\n/**\n * Whether this browsing session belongs to a visitor the SDK has never seen\n * before. Stable for the whole session; see the module doc for the default\n * when storage is unavailable.\n */\nexport function isNewVisitor(): boolean {\n if (cachedVerdict !== null) return cachedVerdict;\n const sessionVerdict = readSessionVerdict();\n if (sessionVerdict !== null) {\n cachedVerdict = sessionVerdict;\n return cachedVerdict;\n }\n cachedVerdict = readMarkerVerdict();\n writeSessionVerdict(cachedVerdict);\n return cachedVerdict;\n}\n\n/**\n * Record that this visitor has now been seen, so the NEXT session reads as\n * returning. Resolves the current session's verdict before writing.\n */\nexport function markReturningVisitor(): void {\n isNewVisitor();\n try {\n if (typeof localStorage === 'undefined') return;\n localStorage.setItem(RETURNING_MARKER_KEY, '1');\n } catch {\n // storage blocked — nothing to remember; the verdict stays as resolved\n }\n}\n\n/** Test seam: forget the in-memory verdict, as a fresh page load would. */\nexport function resetVisitorVerdictCacheForTests(): void {\n cachedVerdict = null;\n}\n","/**\n * Behavioural in-app triggers — the builder's \"When\" options\n * (`display_rules.trigger_type`, served as `client_trigger`) that wait on a\n * visitor behaviour the TriggerEngine detects.\n *\n * ONE table maps each trigger to the client event it fires on, so the in-app\n * manager's matcher and the runtime's arming read the same names. Defaults\n * match the builder's (when-panel.tsx): 50 % scroll, 30 s on page, 30 s idle.\n *\n * `micro_intent` is NOT here: it is a compound rule the IntentRuleEvaluator\n * owns, not one engine event.\n */\n\nimport type { TriggerEngine } from './TriggerEngine';\n\nexport interface ClientTrigger {\n type: string;\n config?: Record<string, unknown>;\n}\n\nexport const BEHAVIOURAL_TRIGGER_TYPES = [\n 'exit_intent',\n 'scroll_depth',\n 'time_on_page',\n 'inactivity',\n] as const;\n\nexport const DEFAULT_SCROLL_DEPTH_PERCENT = 50;\nexport const DEFAULT_TIME_ON_PAGE_SECONDS = 30;\nexport const DEFAULT_IDLE_SECONDS = 30;\n\nfunction positiveNumber(raw: unknown, fallback: number): number {\n const n = Number(raw);\n return Number.isFinite(n) && n > 0 ? n : fallback;\n}\n\n/**\n * The client event a behavioural trigger fires on — the TriggerEngine's own\n * event name — or null when the trigger is not behavioural.\n */\nexport function behaviouralTriggerEvent(trigger: ClientTrigger | null | undefined): string | null {\n if (!trigger) return null;\n const cfg = trigger.config || {};\n switch (trigger.type) {\n case 'exit_intent':\n return 'exit_intent';\n case 'scroll_depth':\n return `scroll_depth_${positiveNumber(cfg.depth_percent, DEFAULT_SCROLL_DEPTH_PERCENT)}`;\n case 'time_on_page':\n return `time_on_page_${positiveNumber(cfg.seconds, DEFAULT_TIME_ON_PAGE_SECONDS)}`;\n case 'inactivity':\n return `inactivity_${positiveNumber(cfg.idle_seconds, DEFAULT_IDLE_SECONDS)}`;\n default:\n return null;\n }\n}\n\n/**\n * The inverse of `behaviouralTriggerEvent`: reads an engine event name\n * (`exit_intent`, `inactivity_60`, `time_on_page_30`, `scroll_depth_50`) back\n * into its trigger, for hosts that configure triggers by event name (the chat\n * launcher's `proactiveTriggers`). Null for any other name.\n */\nexport function triggerFromEngineEvent(eventName: string): ClientTrigger | null {\n if (eventName === 'exit_intent') return { type: 'exit_intent' };\n const match = /^(inactivity|time_on_page|scroll_depth)_(\\d+)$/.exec(eventName);\n if (!match) return null;\n const n = Number(match[2]);\n if (match[1] === 'inactivity') return { type: 'inactivity', config: { idle_seconds: n } };\n if (match[1] === 'time_on_page') return { type: 'time_on_page', config: { seconds: n } };\n return { type: 'scroll_depth', config: { depth_percent: n } };\n}\n\n/**\n * Arms the TriggerEngine for the behavioural triggers of `triggers` and calls\n * `fire(eventName)` when one happens. Returns the events it newly armed, so a\n * caller re-arming after each campaign refresh subscribes each event once.\n *\n * Exit intent: mouse leaving the top of the window on desktop; on mobile (no\n * mouse) a fast scroll back up, which the engine reports as\n * `mobile_exit_intent` and this forwards as `exit_intent`.\n */\nexport function armBehaviouralTriggers(\n engine: TriggerEngine,\n triggers: ReadonlyArray<ClientTrigger | null | undefined>,\n armed: Set<string>,\n fire: (eventName: string) => void,\n isMobile: boolean,\n): string[] {\n const newlyArmed: string[] = [];\n for (const trigger of triggers) {\n const eventName = behaviouralTriggerEvent(trigger);\n if (!eventName || armed.has(eventName)) continue;\n armed.add(eventName);\n newlyArmed.push(eventName);\n const cfg = trigger!.config || {};\n switch (trigger!.type) {\n case 'exit_intent':\n if (isMobile) {\n engine.registerScrollVelocity();\n engine.on('mobile_exit_intent', () => fire('exit_intent'));\n } else {\n engine.registerExitIntent();\n engine.on('exit_intent', () => fire('exit_intent'));\n }\n continue;\n case 'scroll_depth':\n engine.registerScrollDepth(positiveNumber(cfg.depth_percent, DEFAULT_SCROLL_DEPTH_PERCENT));\n break;\n case 'time_on_page':\n engine.registerTimeOnPage(positiveNumber(cfg.seconds, DEFAULT_TIME_ON_PAGE_SECONDS));\n break;\n case 'inactivity':\n engine.registerInactivity(positiveNumber(cfg.idle_seconds, DEFAULT_IDLE_SECONDS));\n break;\n }\n engine.on(eventName, () => fire(eventName));\n }\n return newlyArmed;\n}\n","/**\n * AegisInAppManager - Web SDK In-App Messaging Module\n *\n * HTTP polling architecture with client-side evaluation.\n *\n * Security:\n * - XSS Protection: Uses DOM APIs (createElement, textContent) instead of innerHTML\n * - URL Validation: Prevents javascript: protocol injection\n * - CSP Compatible: No inline styles in HTML strings\n *\n * Architecture (RFC 2026-04-16):\n * - Polls /v1/in-app/active on initialize + on identity change\n * - Caches campaigns locally in memory\n * - Evaluates trigger rules client-side\n * - Tracks events (impression, click, dismiss) asynchronously\n *\n * NOTE: Persistent SSE for downstream delivery is disabled by default\n * (enableSSE=false). Tenant dashboards that need realtime (inbox, chat,\n * live page) open SSE directly via Next.js API routes — not via this SDK.\n * End-user storefronts use polling + Web Push for background delivery.\n */\n\nimport type { RenderContext, CartState } from './renderers';\nimport {\n parseAction,\n fallbackUrlForAction,\n productPath,\n type AegisAction,\n type ActionMeta,\n} from './actions';\nimport {\n renderCarouselCards,\n renderCoachmarkTour,\n renderProductRecommendation,\n renderProductRecommendationInline,\n renderCardFromTemplate,\n resolveGlobalTemplate,\n type TemplateCard,\n renderProgressBar,\n renderProgressBarInline,\n renderStickyBar,\n renderStories,\n renderStoriesRings,\n renderGame,\n GAME_SUB_TYPES,\n renderVideo,\n renderVideoInline,\n renderHeroInline,\n renderActiveWebChat,\n disposeActiveWebChat,\n renderReviewsInline,\n renderReviewsOverlay,\n} from './renderers';\n// P3 flow engine — shared accumulator for the multi-step telemetry/response split.\nimport { createFlowAccumulator } from './renderers/progress-core';\nimport { ensureInAppFont } from './font';\nimport { lucideSvg } from './lucide-svg';\nimport type { AegisChat } from '../chat/AegisChat';\nimport { Storage } from '../utils/storage';\nimport { mountScratchSurface, scratchFoilPalette, createDealOnce, scratchOutcome } from './renderers/scratch-engine';\nimport { redactUrl } from '../utils/redact-url';\nimport { isNewVisitor, markReturningVisitor } from '../utils/visitor';\nimport { behaviouralTriggerEvent } from '../triggers/behaviouralTriggers';\n\n// Read the SDK's persisted anonymous_id from localStorage/cookie if available.\n// Returns undefined when called server-side (no document/window) or when no\n// id has been minted yet — caller falls through to leaving userId unset.\nfunction readAnonIdFromStorage(): string | undefined {\n if (typeof document === 'undefined') return undefined;\n try {\n const storage = new Storage();\n return storage.get('anon_id') ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface InAppCampaign {\n id: string;\n type:\n | 'modal'\n | 'banner'\n | 'tooltip'\n | 'full_screen'\n | 'half_interstitial'\n | 'alert'\n | 'pip'\n | 'video'\n // Stories — full-screen tap-through story/reel sequence (2026-06-14)\n | 'stories'\n // Preload-first display types (2026-04-22 §5.4 of the expansion plan)\n | 'carousel_cards'\n | 'sticky_bar'\n | 'progress_bar'\n | 'coachmark_tour'\n | 'product_recommendation'\n // Active Web Chat — persistent chat launcher backed by the AegisChat engine\n // (2026-06-15). See CHAT_WIDGET_AS_INAPP_CAMPAIGN_TRACKER.md.\n | 'active_web_chat'\n | 'custom_html';\n sub_type?: string;\n title: string;\n body: string;\n image_url?: string;\n video_url?: string;\n action_url?: string;\n button_text?: string;\n background_color?: string;\n text_color?: string;\n priority: number;\n expires_at?: string;\n frequency?: {\n max_impressions?: number;\n max_impressions_per_day?: number;\n cooldown_seconds?: number;\n // Conversion-aware suppression — added by Micro-Intent Engine P0a (2026-04-30).\n // Mirrors cell-plane FrequencyCap. When the host app calls\n // `notifyConversion(goalName)`, every armed campaign whose\n // `suppress_after_conversion_seconds` is set gets silenced for that\n // many seconds (session-scoped via sessionStorage). Prevents the\n // \"showing 10% off to a buyer who just paid full price\" failure mode.\n suppress_after_conversion_seconds?: number;\n suppress_after_dismiss_seconds?: number;\n scope?: 'session' | 'user' | 'user_sku';\n };\n interactive_config?: Record<string, unknown>;\n client_trigger?: {\n type: string;\n config?: Record<string, unknown>;\n };\n assigned_variant_id?: string;\n // F2 — campaign-delivery surface selection. When the array contains\n // 'embedded_card' AND the page has a `[data-aegis-slot=\"<widget_category>\"]`\n // anchor, the SDK renders this campaign INTO the slot (in addition to or\n // instead of an overlay; see `renderIntoSlots`). Defaults to\n // `['in_app_overlay']` server-side so legacy campaigns keep auto-overlay\n // behavior unchanged.\n delivery_modes?: Array<'in_app_overlay' | 'embedded_card' | 'standalone_page'>;\n // F3 — wide grouping discriminator used as the slot lookup key. A campaign\n // with `widget_category='feedback'` will fill the first\n // `[data-aegis-slot=\"feedback\"]` element on the page (if `embedded_card` is\n // among its delivery_modes).\n widget_category?: 'feedback' | 'loyalty' | 'promo' | 'commerce' | 'support' | 'custom' | 'onboarding' | 'hero' | 'announcement' | 'reorder' | 'reviews';\n // F2 — page-key for `standalone_page` delivery mode. Read by the Next.js\n // route handler in `apps/cashier-portal/src/app/s/[slug]/[workspace]/[page_key]`.\n // Matches the existing storefront vocabulary (`rewards`/`members`)\n // in apps/cashier-portal/src/app/s/[slug]/{rewards,members}/page.tsx.\n page_key?: 'bill' | 'rewards' | 'feedback' | 'reviews' | 'members';\n // Surface filter (In-App Surface Filter tracker P2). Canonical placement\n // keys this campaign may render on. NULL/undefined or [] = all surfaces\n // (legacy default). A non-empty array gates slot-fill: the SDK only renders\n // this campaign when the host page's declared surface (via aegis.page(name)\n // / aegis.screen(name)) is included. Mirrors the server-side filter at\n // /v1/in-app/active — this in-memory gate is defense-in-depth for stale\n // cached campaigns. The enum is server-locked (cell-plane Pydantic + alembic\n // CHECK); the SDK accepts any string the host declares.\n surface?: string[];\n // Dynamic Screens P2 — free-form screen names (operator-selected from\n // discovered traffic, e.g. 'storefront.cart') matched against the host's raw\n // `currentScreen`. Distinct from the 8-enum `surface`. NULL/empty = none.\n target_screens?: string[];\n // Set ONLY on a queued journey/campaign SEND (a `contact_in_app_queue` row);\n // null for catalog campaigns. It is the ack-routing discriminator: a queued\n // item's render/click/dismiss is ALSO acknowledged at\n // `/v1/in-app/widgets/{step}/ack`, which is what moves the row out of\n // `queued` so `/v1/in-app/active` stops re-serving it. See `trackEvent`.\n journey_step_execution_id?: string | null;\n}\n\n/** The queue-row acknowledgement each engagement event implies. `served` is\n * deliberately absent — it means the SDK received the payload, not that a\n * customer saw it — as are the diagnostic events. */\nconst JOURNEY_ACK_ACTION: Partial<Record<string, 'viewed' | 'clicked' | 'dismissed'>> = {\n impression: 'viewed',\n clicked: 'clicked',\n dismissed: 'dismissed',\n};\n\n/**\n * Payload for `campaign-click` lifecycle event. CANCELLABLE — return `false`\n * (or call `evt.preventDefault()`) from the handler to suppress the SDK's\n * default `window.location.href` navigation, e.g. to route via a SPA router.\n */\nexport interface CampaignClickEvent {\n campaign: InAppCampaign;\n /** Sanitized destination URL — already passed through `sanitizeUrl()`. */\n action_url: string;\n /** Best-effort button identifier — populated when the click came from a\n * named button in a multi-button campaign (modals, alerts). `cta` for the\n * primary CTA on single-button campaigns. */\n button_id?: string;\n /** Mutate via `evt.preventDefault()` to signal the SDK to skip its\n * default hard-navigation. Identical semantics to DOM CustomEvent. */\n preventDefault: () => void;\n defaultPrevented: boolean;\n}\n\n/** Payload for `campaign-dismiss` lifecycle event. */\nexport interface CampaignDismissEvent {\n campaign: InAppCampaign;\n /** Source of dismissal — close button, swipe, ESC key, programmatic. */\n source: 'close_button' | 'overlay' | 'esc' | 'auto_timeout' | 'programmatic';\n}\n\n/** Payload for `error` lifecycle event. Aggregates non-fatal SDK errors so\n * host apps can wire them into Sentry / Datadog RUM / similar. */\nexport interface AegisErrorEvent {\n message: string;\n error: unknown;\n /** Optional structured context — `{ event: 'campaign-click' }` for handler\n * throws, `{ campaign_id, stage }` for render path failures, etc. */\n context: Record<string, unknown>;\n}\n\n/**\n * Map of lifecycle event name → handler signature. Used by the typed\n * `on(event, handler)` overload below so TS correctly narrows the payload\n * type per event without a discriminated union at the call site.\n */\nexport interface InAppLifecycleEventMap {\n 'campaigns-loaded': (campaigns: InAppCampaign[]) => void;\n 'campaign-will-show': (campaign: InAppCampaign) => void | false;\n 'campaign-shown': (campaign: InAppCampaign) => void;\n 'campaign-click': (evt: CampaignClickEvent) => void | false;\n 'campaign-dismiss': (evt: CampaignDismissEvent) => void;\n 'error': (evt: AegisErrorEvent) => void;\n}\n\nexport interface AegisInAppConfig {\n writeKey: string;\n apiHost?: string;\n userId?: string;\n contactId?: string;\n /** Server-minted proof of `contactId` (S/C1). Without it, in-app answers are\n * kept as anonymous events and never written onto the contact's profile. */\n contactToken?: string;\n organizationId?: string;\n // Resolved by POST /v1/sdk/bootstrap — required in production. Campaigns\n // with a non-empty `target_properties` list are filtered against this on\n // the server, so passing the wrong id (or omitting it) narrows visible\n // campaigns to the property-agnostic ones only.\n propertyId?: string;\n debugMode?: boolean;\n enableSSE?: boolean;\n /**\n * Canonical CTA-action handler (CANONICAL_ACTION_MODEL_TRACKER.md). ONE host\n * callback receives every CTA action (add_to_cart / open_product / open_cart /\n * open_login / navigate / custom …) — the CEP-standard shape. Return `true`\n * when handled; otherwise the SDK applies the canonical fallback (navigate to\n * the product/cart page) so a CTA is never a dead no-op. Legacy `aegis:*` DOM\n * events still fire alongside this during the migration.\n */\n onAction?: (action: AegisAction) => boolean | void;\n /**\n * Platform-aware product-URL resolver (sku → PDP url). Lets a Shopify/Woo/\n * Magento adapter map to `/products/{handle}` etc. instead of the canonical\n * `/p/{sku}`. Defaults to `/p/{sku}` when unset.\n */\n productUrl?: (sku: string) => string;\n /**\n * Workspace cascade reader. Set this from the host SDK as\n * `getWorkspaceId: () => aegis.getEffectiveWorkspaceId()` so in-app\n * impressions/clicks/dismisses carry the same workspace context the\n * main analytics SDK sees (URL path slug, ?ws=, setWorkspace runtime).\n * Without it, in-app engagement events arrive at event-ingress with\n * no workspace_id stamped → cross-outlet attribution silently falls\n * back to the org's primary workspace.\n */\n getWorkspaceId?: () => string | undefined;\n /**\n * Live cart-state reader, set by the runtime from its imperative\n * `setCartState(...)`. Threaded into the renderer `RenderContext` so the\n * progress-bar renderer can read a first-party cart value instead of\n * `window.*` cart globals. Undefined for embeds that never feed a cart.\n */\n getCartState?: () => CartState | undefined;\n}\n\n/** Parse a TRUSTED, static SVG string (the SDK's own constant icon markup) into\n * a DOM node — so we never assign innerHTML on a live element. Returns null on\n * any parse error; callers fall back gracefully. NEVER pass campaign/customer\n * data here (it's only for hardcoded SDK icons). */\nfunction svgNode(svg: string): Node | null {\n try {\n const doc = new DOMParser().parseFromString(svg, 'image/svg+xml');\n const root = doc.documentElement;\n if (!root || root.getElementsByTagName('parsererror').length > 0) return null;\n return document.importNode(root, true);\n } catch {\n return null;\n }\n}\n\n\n/**\n * WHICH products a campaign put in front of the customer.\n *\n * An in-app card is a product SURFACE. On the ST tenant it rendered products\n * 4,256 times in thirty days and recorded which ones ZERO times, so every one\n * of those products read `Views 0` on the Products page — and `0` says nobody\n * wanted it, when the truth was that nobody measured it.\n *\n * The products are already in the bundle: the cell-plane stamps\n * `interactive_config.cards[]` at assembly time and each card's `metadata`\n * carries the identity the catalogue keyed it by. Nothing is fetched here;\n * this reads what the renderer already painted.\n *\n * DECLARED IDENTITY ONLY — never the card's title.\n *\n * The title fallback was in the first draft of this function and it was wrong.\n * `recommendation_hydration` stamps `metadata.product_id` / `metadata.sku` on\n * every card it builds from the catalogue, but it SKIPS hydration when cards\n * were authored by hand (`if cfg.get(\"cards\"): return`). ST's authored cards\n * carry only `price` in metadata, and their titles are:\n *\n * \"Unlock VIP\" \"View bill\" \"Rate your order\" \"Earn points\"\n * \"Featured product 1\" \"Celebrate your Special Moments\"\n *\n * A title fallback would have invented six products that do not exist, booked\n * impressions against them, and inflated the denominator of every conversion\n * rate on the Products page with cards that never showed a product. Fabricated\n * inventory is worse than a missing number, because a missing number announces\n * itself.\n *\n * A card with no declared identity therefore contributes NOTHING. That is not\n * a gap to close later with a guess — it is the correct answer for a card the\n * server never tied to a catalogue row.\n *\n * Returns undefined — never `[]` — when a campaign shows no identified\n * products, because an absent key means \"not a product surface\" while an empty\n * array would claim \"showed nothing\".\n */\n/**\n * Keyed on the cards ARRAY itself, so a re-render with a fresh bundle\n * recomputes and a repeat event on the same bundle does not. A WeakMap holds\n * no reference of its own: when the campaign is replaced the entry goes with\n * it, and there is nothing to invalidate or size-cap.\n *\n * Worth having because `trackEvent` is the chokepoint for every in-app event —\n * a 24-product carousel would otherwise re-walk its cards and re-stringify on\n * every impression, click and dismiss.\n */\nconst productIdentityCache = new WeakMap<object, string>();\n\nconst UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport function productIdentitiesFor(campaign: unknown): string | undefined {\n const ic = (campaign as { interactive_config?: Record<string, unknown> } | undefined)\n ?.interactive_config;\n const cards = ic && Array.isArray(ic.cards) ? (ic.cards as Record<string, unknown>[]) : [];\n if (cards.length === 0) return undefined;\n\n const cached = productIdentityCache.get(cards);\n if (cached !== undefined) return cached || undefined;\n\n const items: Array<{\n product_id?: string;\n sku?: string;\n platform_product_id?: string;\n name: string;\n quantity: number;\n }> = [];\n const seen = new Set<string>();\n // Same ceiling the renderer paints (MAX_PRODUCTS in product-recommendation).\n // A card beyond it was never on screen, so counting it would book an\n // impression for something nobody saw.\n for (const card of cards.slice(0, 24)) {\n const meta = (card?.metadata || {}) as Record<string, unknown>;\n // CANONICAL_PRODUCT_IDENTITY_SPEC R1: `product_id` is the catalogue UUID\n // and `sku` the public handle, each in its own field. The server resolves\n // a rail card's metadata to that pair; a non-UUID it could not place is\n // carried as `sku`, never as `product_id`, and a platform id stays in its\n // own field.\n const rawId = String(meta.product_id || '').trim();\n const rawSku = String(meta.sku || '').trim();\n const platformId = String(meta.platform_product_id || '').trim();\n const productId = UUID_SHAPE.test(rawId) ? rawId : '';\n const sku = rawSku && !UUID_SHAPE.test(rawSku) ? rawSku : rawId && !productId ? rawId : '';\n const identity = productId || sku || platformId;\n if (!identity || seen.has(identity)) continue;\n seen.add(identity);\n items.push({\n ...(productId ? { product_id: productId } : {}),\n ...(sku ? { sku } : {}),\n ...(!productId && !sku && platformId ? { platform_product_id: platformId } : {}),\n name: String((card as { title?: unknown })?.title || identity),\n quantity: 1,\n });\n }\n const encoded = items.length > 0 ? JSON.stringify(items) : '';\n productIdentityCache.set(cards, encoded);\n return encoded || undefined;\n}\n\n\nexport class AegisInAppManager {\n private writeKey: string;\n private apiHost: string;\n private userId?: string;\n private contactId?: string;\n private contactToken?: string;\n private organizationId?: string;\n private propertyId?: string;\n private debugMode: boolean;\n private enableSSE: boolean;\n private onActionHandler?: (action: AegisAction) => boolean | void;\n private productUrl: (sku: string) => string = productPath;\n private getWorkspaceId?: () => string | undefined;\n private getCartState?: () => CartState | undefined;\n\n private campaigns: InAppCampaign[] = [];\n private displayedCampaigns = new Set<string>();\n // Display-size multiplier for the campaign currently being rendered. Set in\n // displayCampaign from interactive_config.display_size; consumed canonically\n // by createOverlay (all overlay formats) + renderModal so the operator's size\n // choice is honored uniformly without per-renderer wiring.\n private _displaySizeMul = 1;\n // Conversion-aware suppression — campaign_id -> epoch_ms when suppression expires.\n // Populated by `notifyConversion()` and rehydrated from sessionStorage in\n // `refreshCampaigns()` so a navigation between pages preserves silence.\n private suppressedUntil = new Map<string, number>();\n private eventSource?: EventSource;\n private isInitialized = false;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n // sessionStorage key prefix — one entry per goal so we can introspect what\n // converted and when. Format: { ts: epoch_ms }.\n private static readonly CONVERSION_STORAGE_PREFIX = 'aegis_conv_';\n\n // ── Lifecycle event bus (1.8.0) ────────────────────────────────────────\n // Typed callback registry — host apps register handlers via the public\n // `on*` methods below. Each handler is stored once; calling the\n // returned unsubscribe fn removes it. Multiple subscribers per event are\n // supported. Callbacks that return `false` (or call `evt.preventDefault()`\n // on cancellable events) signal the SDK to skip its default behaviour.\n //\n // Industry parity: matches CleverTap `notificationCallback`, WebEngage\n // `onCallback`, MoEngage `onSelfHandledClick`, Iterable url-delegate, etc.\n // The DOM CustomEvent path (window.dispatchEvent('aegis:campaign-click'))\n // is preserved for backward compat + advanced multi-subscriber + pre-init\n // patterns where typed methods aren't reachable yet.\n private hooks = new Map<string, Set<(...args: unknown[]) => void | false | undefined>>();\n // `aegis.inApp.ready` resolves when the first refreshCampaigns completes\n // — matches CleverTap `onLoad`, WebEngage `onReady`. Lets host apps await\n // a known-good state before reading `campaigns` or registering handlers\n // that need armed campaigns visible.\n private readyResolve!: () => void;\n readonly ready: Promise<void> = new Promise((resolve) => {\n this.readyResolve = resolve;\n });\n\n private emit<T>(eventName: string, payload: T): boolean {\n const handlers = this.hooks.get(eventName);\n if (!handlers || handlers.size === 0) return true;\n let proceed = true;\n for (const handler of handlers) {\n try {\n const result = (handler as (p: T) => void | false | undefined)(payload);\n if (result === false) proceed = false;\n } catch (err) {\n // Don't let a misbehaving host handler crash the SDK render path.\n // Surface via onError but keep going for the remaining handlers.\n this.emitError(err, { event: eventName });\n }\n }\n return proceed;\n }\n\n private emitError(err: unknown, context?: Record<string, unknown>): void {\n const handlers = this.hooks.get('error');\n if (!handlers || handlers.size === 0) {\n // No subscriber — log to console so errors aren't completely silent.\n this.log(`Active Reach SDK error: ${err instanceof Error ? err.message : String(err)}`, 'error');\n return;\n }\n for (const handler of handlers) {\n try {\n (handler as (e: AegisErrorEvent) => void)({\n message: err instanceof Error ? err.message : String(err),\n error: err,\n context: context ?? {},\n });\n } catch {\n // ignore — we already tried to surface\n }\n }\n }\n\n private register<T>(\n eventName: string,\n handler: (payload: T) => void | false | undefined,\n ): () => void {\n if (!this.hooks.has(eventName)) this.hooks.set(eventName, new Set());\n this.hooks.get(eventName)!.add(handler as (...args: unknown[]) => void | false | undefined);\n return () => {\n this.hooks.get(eventName)?.delete(handler as (...args: unknown[]) => void | false | undefined);\n };\n }\n\n /**\n * Subscribe to in-app message lifecycle events. Returns an unsubscribe\n * function — call to remove the handler (matches React `useEffect` cleanup\n * convention).\n *\n * Supported events: `campaigns-loaded`, `campaign-will-show` (cancellable),\n * `campaign-shown`, `campaign-click` (cancellable), `campaign-dismiss`,\n * `error`. Cancellable events: handler returns `false` to suppress the\n * SDK's default behaviour (e.g., prevent the hard navigation on click,\n * skip rendering on will-show).\n */\n on<E extends keyof InAppLifecycleEventMap>(\n event: E,\n handler: InAppLifecycleEventMap[E],\n ): () => void {\n return this.register(event, handler as never);\n }\n\n /** Sugar: subscribe to `campaigns-loaded`. Fires after every successful\n * `/v1/in-app/active` poll with the full active-campaign list. */\n onCampaignsLoaded(\n handler: (campaigns: InAppCampaign[]) => void,\n ): () => void {\n return this.register('campaigns-loaded', handler);\n }\n\n /** Sugar: subscribe to `campaign-will-show`. CANCELLABLE — return `false`\n * to suppress rendering this campaign (host-side suppression rules,\n * route-aware blocking, etc.). */\n onCampaignWillShow(\n handler: (campaign: InAppCampaign) => void | false,\n ): () => void {\n return this.register('campaign-will-show', handler);\n }\n\n /** Sugar: subscribe to `campaign-shown`. Fires after the SDK has painted\n * the campaign to the DOM (post-impression). */\n onCampaignShown(\n handler: (campaign: InAppCampaign) => void,\n ): () => void {\n return this.register('campaign-shown', handler);\n }\n\n /** Sugar: subscribe to `campaign-click`. CANCELLABLE — return `false` (or\n * call `evt.preventDefault()`) to suppress the SDK's default\n * `window.location.href` navigation, e.g. to route via a SPA router. */\n onCampaignClick(\n handler: (evt: CampaignClickEvent) => void | false,\n ): () => void {\n return this.register('campaign-click', handler);\n }\n\n /** Sugar: subscribe to `campaign-dismiss`. Fires when user closes/swipes\n * away the campaign before clicking through. */\n onCampaignDismiss(\n handler: (evt: CampaignDismissEvent) => void,\n ): () => void {\n return this.register('campaign-dismiss', handler);\n }\n\n /** Sugar: subscribe to `error`. Aggregates non-fatal SDK errors — fetch\n * failures, render exceptions, host-handler throws. Wire this into your\n * error monitoring (Sentry, Datadog RUM, etc.). */\n onError(\n handler: (evt: AegisErrorEvent) => void,\n ): () => void {\n return this.register('error', handler);\n }\n \n constructor(config: AegisInAppConfig) {\n this.writeKey = config.writeKey;\n this.apiHost = config.apiHost || 'https://api.aegis.ai';\n // Fall back to the SDK's persisted anonymous_id (set on first /v1/sdk/bootstrap\n // and reused across the main analytics SDK) so anonymous storefront users\n // get journey-driven in-app messages keyed to the same identity used in\n // /v1/batch payloads. Without this, polling defaults to \"anonymous\" on the\n // server and bucket-misses every per-contact priority queue.\n this.userId = config.userId ?? readAnonIdFromStorage();\n this.contactId = config.contactId;\n this.contactToken = config.contactToken;\n this.organizationId = config.organizationId;\n this.propertyId = config.propertyId;\n this.debugMode = config.debugMode || false;\n // SSE is disabled by default. Kept as opt-in capability for future tenant\n // dashboards that embed the Active Reach SDK and need realtime updates. End-user\n // storefronts must NOT enable this — persistent-per-tab SSE doesn't scale.\n this.enableSSE = config.enableSSE === true;\n this.onActionHandler = config.onAction;\n if (config.productUrl) this.productUrl = config.productUrl;\n this.getWorkspaceId = config.getWorkspaceId;\n this.getCartState = config.getCartState;\n }\n \n async initialize(): Promise<void> {\n if (this.isInitialized) {\n this.log('AegisInApp already initialized');\n return;\n }\n\n // Mark the visitor as returning for the NEXT session. The helper resolves\n // this session's new-visitor verdict before it writes, so the fetch below\n // still sends is_new_user=true on a first visit (it used to read back its\n // own write and send false, which starved show_to_new_users_only).\n markReturningVisitor();\n\n // Page-view counter for the push primer's 'second_page_view'\n // prompt trigger (one increment per SDK boot = one page load;\n // SPA route changes intentionally don't count).\n try {\n if (typeof localStorage !== 'undefined') {\n const pv = parseInt(localStorage.getItem('aegis_pv_count') || '0', 10);\n localStorage.setItem('aegis_pv_count', String(pv + 1));\n }\n } catch {\n // storage blocked — trigger gate fails open\n }\n\n // Load the shared in-app font (Urbanist) once so EVERY in-app surface —\n // cards, rails, value bar, progress, stories, games, banners — renders in\n // it on any host page, not just the ACS storefront (which self-hosts it).\n ensureInAppFont();\n\n await this.refreshCampaigns();\n\n if (this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n\n this.isInitialized = true;\n\n // A surface/screen declared via aegis.page()/screen()/trackSurface() DURING\n // init (before `isInitialized` flipped) may have landed AFTER the fetch\n // above already built its request URL — leaving the first load with no\n // surface-/screen-targeted rails. Re-fetch once now that the declaration is\n // recorded: currentSurface/currentScreen flow into both the request and the\n // client-side slot gate, so cold-load rails appear without waiting for a\n // later navigation. No-op (flag stays false) for hosts that never declare a\n // surface pre-init, so this is back-compatible and bounded to one refetch.\n if (this.pendingPreInitRefresh) {\n this.pendingPreInitRefresh = false;\n this.log('Re-fetching campaigns for surface/screen declared during init');\n void this.refreshCampaigns();\n }\n\n // Granted-but-never-registered self-heal (2026-07-21). On hosts where\n // AegisWebPush is only constructed from the push-primer tap (ACS), a\n // user whose OS permission is already 'granted' — e.g. they allowed via\n // browser site settings, or a prior subscribe POST failed after the\n // grant — is a permanent dead end: the primer suppresses itself on\n // non-'default' permission, so nothing ever registers the subscription.\n // Silently run the same subscribe flow at boot; requestPermission() is\n // a no-op resolve when already granted (no prompt), and the backend\n // UPSERTs on (property_id, device_fingerprint) so repeat boots are\n // idempotent. Once-per-session guard keeps SPA re-inits cheap.\n if (\n typeof Notification !== 'undefined' &&\n Notification.permission === 'granted' &&\n this.propertyId\n ) {\n let alreadyHealed = false;\n try {\n alreadyHealed = sessionStorage.getItem('aegis_push_boot_heal') === '1';\n } catch {\n // storage blocked — run the heal; backend UPSERT keeps it idempotent\n }\n if (!alreadyHealed) {\n try {\n sessionStorage.setItem('aegis_push_boot_heal', '1');\n } catch {\n // ignore\n }\n this._lastHealOutcome = 'attempted';\n void this._subscribeFromPushPrimer()\n .then(() => { this._lastHealOutcome = 'completed'; })\n .catch((err) => {\n this._lastHealOutcome = `failed:${(err as Error)?.name || 'unknown'}`;\n this.log('boot push resubscribe failed', err);\n });\n } else {\n this._lastHealOutcome = 'skipped_session_guard';\n }\n } else if (\n typeof Notification !== 'undefined' &&\n Notification.permission === 'granted'\n ) {\n this._lastHealOutcome = 'skipped_no_property';\n }\n\n // Push boot-state beacon (1.36.4; delayed in 1.36.5) — one POST per\n // session so every device self-reports WHY push is (or isn't) active.\n // The 6s delay lets the config-load re-dispatch settle first, so the\n // beacon carries the actual overlay-pick decision + whether the primer\n // is physically in the DOM.\n setTimeout(() => void this._sendPushBootBeacon(), 6000);\n\n this.log('AegisInApp initialized successfully');\n }\n\n private _lastHealOutcome = 'not_attempted';\n\n private async _sendPushBootBeacon(): Promise<void> {\n // Guard is only set AFTER a successful send (1.36.6) — the 1.36.5\n // guard-first ordering meant one silent failure suppressed the beacon\n // for the whole session, which is exactly the blind spot the beacon\n // exists to remove.\n try {\n if (sessionStorage.getItem('aegis_push_boot_beacon') === '1') return;\n } catch {\n // storage blocked — send anyway\n }\n // Any internal failure is captured INTO the payload rather than\n // aborting the send: the config load gets a 3s cap and reports its\n // own error instead of hanging the whole beacon.\n let beaconError: string | null = null;\n let optIn: { mode: string; trigger: string } = { mode: 'unknown', trigger: 'unknown' };\n try {\n optIn =\n this._pushOptInCache ||\n (await Promise.race([\n this._fetchPushOptInConfig(),\n new Promise<{ mode: string; trigger: string }>((_, rej) =>\n setTimeout(() => rej(new Error('config_timeout_3s')), 3000),\n ),\n ]));\n this._pushOptInCache = optIn;\n } catch (err) {\n beaconError = `config:${(err as Error)?.message || 'unknown'}`.slice(0, 80);\n }\n try {\n let pv = 0;\n let snoozed = false;\n try {\n pv = parseInt(localStorage.getItem('aegis_pv_count') || '0', 10);\n snoozed = Date.now() < parseInt(localStorage.getItem('aegis_primer_snooze_until') || '0', 10);\n } catch {\n // storage blocked\n }\n await fetch(`${this.apiHost}/v1/web_push/boot-state`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey,\n ...(this.organizationId ? { 'X-Organization-ID': this.organizationId } : {}),\n ...(this.propertyId ? { 'X-Property-Id': this.propertyId } : {}),\n },\n body: JSON.stringify({\n sdk_version: '1.36.5',\n permission:\n typeof Notification !== 'undefined' ? Notification.permission : 'unsupported',\n opt_in_mode: optIn.mode,\n prompt_trigger: optIn.trigger,\n pv_count: pv,\n primer_snoozed: snoozed,\n heal_outcome: this._lastHealOutcome,\n dispatch: [\n this._lastDispatch\n ? `picked=${this._lastDispatch.picked} primer_present=${this._lastDispatch.primer_present} primer_eligible=${this._lastDispatch.primer_eligible} err=${this._lastDispatch.error} in_dom=${!!document.querySelector('[class*=\"push-primer\"]')}`\n : 'no_dispatch_recorded',\n beaconError ? `beacon_err=${beaconError}` : '',\n ].filter(Boolean).join(' '),\n user_agent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n }),\n });\n try {\n sessionStorage.setItem('aegis_push_boot_beacon', '1');\n } catch {\n // storage blocked — may re-send next boot; sink is log-only, harmless\n }\n } catch (err) {\n // Last resort: sendBeacon survives even page teardown and needs no\n // CORS preflight (text/plain) — the sink ignores auth for parse\n // failures but the gateway still logs arrival.\n try {\n navigator.sendBeacon(\n `${this.apiHost}/v1/web_push/boot-state`,\n JSON.stringify({ sdk_version: 'beacon_fetch_failed', heal_outcome: String((err as Error)?.message).slice(0, 60) }),\n );\n } catch {\n // truly unreachable — give up silently\n }\n }\n }\n \n updateUserId(userId: string): void {\n this.userId = userId;\n this.refreshCampaigns();\n }\n \n updateContactId(contactId: string, contactToken?: string): void {\n this.contactId = contactId;\n // A token is bound to ONE contact: a new id without its own token must not\n // keep the previous contact's proof.\n this.contactToken = contactToken;\n // Keep the chat launcher's identity in lockstep so the anon→known stitch\n // hits the indexed contact path on the next message.\n this.activeWebChat?.updateContactId(contactId);\n this.disconnectSSE();\n if (this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n this.refreshCampaigns();\n }\n\n // --- Qualifying-event refresh (DLR Tracker 1 / P1, 2026-05-28) ---------\n //\n // Burst-coalescing debounce timer for `refreshOnEvent`. Cleared and\n // re-armed on every call so a flurry of `aegis.track('cart_item_added')`\n // calls (e.g. 10 within 100ms while a shopper rapidly adds items)\n // collapses to a single `/v1/in-app/active` fetch ~300ms after the last\n // call. Without this debounce, an active storefront would hammer the\n // endpoint and our connection-load budget — see\n // memory/feedback_web_sdk_rest_not_sse.md for why polling has to stay\n // gentle.\n private refreshDebounceTimer?: ReturnType<typeof setTimeout>;\n private static readonly REFRESH_DEBOUNCE_MS = 300;\n\n // Set true when a surface/screen declaration arrives via refreshOnEvent while\n // the manager is still pre-init (initialize() owns the first fetch). Drives a\n // single follow-up refreshCampaigns() at the tail of initialize() so a cold\n // first load picks up a surface declared during the init network round-trip\n // (the \"rails only render after navigating/refreshing\" fix). Reset after use.\n private pendingPreInitRefresh = false;\n\n // Surface filter (In-App Surface Filter tracker P2). The logical surface\n // last declared by the host page via aegis.page(name) / aegis.screen(name).\n // Threaded to /v1/in-app/active?current_surface=<name> on the next refresh\n // and used as the in-memory slot-fill gate. Set-replace, no clear-on-unmount\n // (the latest declaration wins — see tracker Open decision #7).\n private currentSurface: string | null = null;\n\n // Dynamic Screens P2 — the RAW screen name last declared via\n // aegis.page(name) / aegis.screen(name), un-normalized (e.g.\n // 'storefront.cart'). Distinct from `currentSurface` (which is gated to the\n // 8 canonical keys): this is the free-form tier. Threaded to\n // /v1/in-app/active?current_screen=<name> and matched against a campaign's\n // `target_screens`. Only page/screen declarations set it — NOT track().\n private currentScreen: string | null = null;\n\n // Outlet context — the outlet the customer is ACTIVELY viewing (storefront\n // outlet switcher / location-code), set via aegis.setLocation(outletId).\n // Threaded to /v1/in-app/active?location_id=<id>; the server prefers it over\n // the property's STATIC configured outlet so outlet-scoped campaigns follow\n // the switcher on a SHARED multi-outlet storefront (one property, N outlets).\n // null → server falls back to the property's outlet, then brand-wide.\n private currentLocationId: string | null = null;\n\n // The 8 canonical surface keys. We only capture `eventName` as\n // `currentSurface` when it matches one of these — so an arbitrary\n // aegis.track('product_viewed') still triggers a refresh (existing\n // behavior) but never poisons the surface state (tracker Open decision #9).\n // Server-locked enum (cell-plane Pydantic + alembic CHECK); kept in lockstep\n // via the P6 cross-source triangulation guard.\n private static readonly KNOWN_SURFACES: ReadonlySet<string> = new Set([\n 'bill',\n 'storefront_catalog',\n 'storefront_rewards',\n 'storefront_feedback',\n 'storefront_reviews',\n 'storefront_members',\n 'storefront_orders',\n 'storefront_account',\n 'storefront_referrals',\n 'external_website',\n 'external_mobile_screen',\n ]);\n\n /**\n * Qualifying-event refresh — schedule a debounced `refreshCampaigns()`\n * call in response to a host-app SDK event (`aegis.page()`,\n * `aegis.screen()`, `aegis.track(name)`). Matches CleverTap / MoEngage /\n * WebEngage's \"refresh on qualifying event\" shape, translated to the\n * browser session.\n *\n * Why: new server-side eligibility opens on these events (a `page` view\n * arms route-targeted campaigns; a `track('product_viewed')` arms\n * trigger-gated campaigns whose `client_trigger.config.event` matches).\n * Pre-P1, the SDK only refreshed on `initialize()` + `updateContactId()`\n * + the SSE update channel (which storefront has disabled by design),\n * so newly armed campaigns were invisible until the next identity flip.\n *\n * Debounce: 300ms. Five `page()` calls inside the window coalesce to\n * one fetch; a sixth call ≥300ms after the prior burst starts a new\n * fetch. The trailing-edge schedule (re-arm on every call) is correct\n * for storefront rage-clicks + SPA route bursts — we want the LATEST\n * URL's eligibility, not the first.\n *\n * Safe to call on the server (no-op when `setTimeout` is unavailable\n * — though that's vanishingly rare in any modern environment we ship to).\n *\n * Surface capture (In-App Surface Filter tracker P2): when `eventName`\n * matches one of the 8 canonical surface keys, it is captured into\n * `currentSurface` and threaded to `/v1/in-app/active?current_surface=<name>`\n * on the next refresh — wiring the existing `aegis.page(name)` /\n * `aegis.screen(name)` argument through to campaign targeting. Unknown names\n * (e.g. `aegis.track('product_viewed')`) still trigger a refresh but do NOT\n * change `currentSurface`, so arbitrary events never poison the surface gate.\n */\n refreshOnEvent(eventName: string, isScreenDeclaration = false): void {\n // Capture the canonical surface declaration. 'page_view' (the default\n // aegis.page() forwards when called with no name) and arbitrary track()\n // event names are intentionally ignored here. This runs BEFORE the\n // `isInitialized` guard on purpose — see the pre-init note below.\n if (eventName && AegisInAppManager.KNOWN_SURFACES.has(eventName)) {\n this.currentSurface = eventName;\n }\n // Dynamic Screens P2 — capture the RAW screen name for the free-form tier,\n // but ONLY from a page/screen declaration (isScreenDeclaration), never a\n // track() event (a 'product_viewed' track must not masquerade as a screen).\n // 'page_view' is the no-arg aegis.page() sentinel → not a real screen.\n if (isScreenDeclaration && eventName && eventName !== 'page_view') {\n this.currentScreen = eventName;\n }\n if (!this.isInitialized) {\n // Pre-init declaration. The host (storefront / external site) declares\n // its surface+screen on mount via aegis.page()/screen()/trackSurface(),\n // which routinely fires BEFORE initialize()'s first refreshCampaigns()\n // resolves (it awaits a network round-trip). We must NOT drop the\n // declaration — we've already recorded currentSurface/currentScreen\n // above so the first fetch picks them up. We only skip scheduling a\n // duplicate debounced refetch here (initialize() owns the first fetch),\n // and flag a single follow-up in case this declaration landed AFTER\n // initialize() built its request URL (effect-ordering race). Without\n // this, a cold first load rendered no surface-/screen-targeted rails\n // until the next navigation re-declared the surface.\n this.pendingPreInitRefresh = true;\n this.log(`refreshOnEvent(${eventName}) before initialize — surface captured, refetch deferred to initialize()`);\n return;\n }\n if (this.refreshDebounceTimer) {\n clearTimeout(this.refreshDebounceTimer);\n }\n this.refreshDebounceTimer = setTimeout(() => {\n this.refreshDebounceTimer = undefined;\n this.log(`refreshOnEvent(${eventName}) — firing refreshCampaigns()`);\n void this.refreshCampaigns();\n }, AegisInAppManager.REFRESH_DEBOUNCE_MS);\n }\n\n /**\n * Outlet context — declare the outlet the customer is actively viewing\n * (storefront outlet switcher / location-code). The host calls this when the\n * shopper picks an outlet (or on initial outlet-scoped page load) and again\n * with the new id when they switch. Threaded to\n * `/v1/in-app/active?location_id=<id>` so the server serves outlet-scoped\n * campaigns for THIS outlet (preferred over the property's static outlet) —\n * the fix that makes a SHARED multi-outlet storefront's switcher actually\n * drive in-app targeting. Pass `null` to clear (back to property/brand-wide).\n *\n * No-ops when the value is unchanged; otherwise debounce-refreshes so the\n * armed campaign set follows the switcher within ~300ms.\n */\n setLocation(locationId: string | null): void {\n const next = locationId || null;\n if (next === this.currentLocationId) return;\n this.currentLocationId = next;\n if (!this.isInitialized) return; // first refresh fires from initialize()\n if (this.refreshDebounceTimer) {\n clearTimeout(this.refreshDebounceTimer);\n }\n this.refreshDebounceTimer = setTimeout(() => {\n this.refreshDebounceTimer = undefined;\n this.log(`setLocation(${next}) — firing refreshCampaigns()`);\n void this.refreshCampaigns();\n }, AegisInAppManager.REFRESH_DEBOUNCE_MS);\n }\n\n /**\n * Conversion-aware suppression. Call this when the host app observes a\n * goal event (purchase, order_placed, checkout_completed, or any custom\n * goal the workspace has configured per-campaign).\n *\n * For every armed campaign whose `frequency.suppress_after_conversion_seconds`\n * is set, this:\n * 1. Persists the conversion in sessionStorage (so subsequent page\n * loads in the same tab retain silence).\n * 2. Computes an expiry epoch_ms and stores it in `suppressedUntil`.\n * 3. The two campaign-evaluation paths (`tryDisplayNextCampaign`\n * and `onClientEvent`) skip campaigns whose `suppressedUntil`\n * has not yet passed.\n *\n * Scope: only `session` is implemented in P0a. `user` and `user_sku`\n * scopes degrade to session-only with a debug log; the schema accepts\n * them so future expansion is non-breaking.\n *\n * Safe to call repeatedly for the same goal — each call refreshes the\n * sessionStorage timestamp and re-applies suppression to any campaigns\n * loaded since the last call.\n */\n notifyConversion(goalName: string): void {\n if (!goalName) {\n this.log('notifyConversion called with empty goalName; ignored', 'warn');\n return;\n }\n const ts = Date.now();\n if (typeof sessionStorage !== 'undefined') {\n try {\n sessionStorage.setItem(\n `${AegisInAppManager.CONVERSION_STORAGE_PREFIX}${goalName}`,\n JSON.stringify({ ts }),\n );\n } catch {\n // sessionStorage may be disabled (Safari private mode, blocked\n // third-party context). Suppression still applies for the\n // current page lifetime via the in-memory map.\n }\n }\n this.applySuppressionFromCampaigns(ts);\n this.log(`notifyConversion: ${goalName} — suppressing ${this.suppressedUntil.size} armed campaigns`);\n }\n\n /**\n * Compute and store `suppressedUntil` entries for every armed campaign\n * whose `frequency.suppress_after_conversion_seconds` is set. Called by\n * `notifyConversion()` and `refreshCampaigns()` (the latter rehydrates\n * suppression for newly-loaded campaigns when a prior conversion event\n * exists in sessionStorage).\n *\n * `convertedAt` is the epoch_ms of the conversion; pass `Date.now()`\n * when called from `notifyConversion`, or the `ts` recovered from\n * sessionStorage when rehydrating.\n */\n private applySuppressionFromCampaigns(convertedAt: number): void {\n for (const c of this.campaigns) {\n const seconds = c.frequency?.suppress_after_conversion_seconds;\n if (typeof seconds !== 'number' || seconds <= 0) continue;\n const scope = c.frequency?.scope ?? 'session';\n if (scope !== 'session') {\n // P0a only implements session scope. Schema accepts user / user_sku\n // so future work doesn't break wire compatibility, but for now we\n // degrade to session and log.\n this.log(\n `applySuppressionFromCampaigns: scope=${scope} not implemented; degrading to session for campaign ${c.id}`,\n 'warn',\n );\n }\n const expiresAt = convertedAt + seconds * 1000;\n // Take the max so a later/longer suppression wins over an earlier one.\n const existing = this.suppressedUntil.get(c.id);\n if (existing === undefined || existing < expiresAt) {\n this.suppressedUntil.set(c.id, expiresAt);\n }\n }\n }\n\n /**\n * Recover any prior conversion timestamps from sessionStorage and apply\n * suppression to currently-loaded campaigns. Called from\n * `refreshCampaigns()` after `this.campaigns` is populated. This is the\n * mechanism that prevents a converted buyer from seeing a discount popup\n * on the next page load within the same session.\n */\n private rehydrateSuppressionFromStorage(): void {\n if (typeof sessionStorage === 'undefined') return;\n let earliestTs: number | null = null;\n try {\n for (let i = 0; i < sessionStorage.length; i++) {\n const key = sessionStorage.key(i);\n if (!key || !key.startsWith(AegisInAppManager.CONVERSION_STORAGE_PREFIX)) continue;\n const raw = sessionStorage.getItem(key);\n if (!raw) continue;\n const parsed = JSON.parse(raw) as { ts?: number };\n if (typeof parsed.ts === 'number') {\n earliestTs = earliestTs === null ? parsed.ts : Math.min(earliestTs, parsed.ts);\n }\n }\n } catch {\n // Malformed entries are silently skipped — suppression simply\n // doesn't apply for them.\n return;\n }\n if (earliestTs !== null) {\n this.applySuppressionFromCampaigns(earliestTs);\n }\n }\n\n /**\n * Returns true if the campaign is currently suppressed (Date.now() is\n * before the stored expiry). Used by both display paths.\n */\n private isSuppressed(campaignId: string): boolean {\n const expiresAt = this.suppressedUntil.get(campaignId);\n if (expiresAt === undefined) return false;\n if (Date.now() >= expiresAt) {\n this.suppressedUntil.delete(campaignId);\n return false;\n }\n return true;\n }\n\n /**\n * Whether this tab recorded a conversion (`notifyConversion`, persisted per\n * goal in sessionStorage). The smart trigger's `converted_in_session`.\n */\n hasConvertedThisSession(): boolean {\n if (typeof sessionStorage === 'undefined') return false;\n try {\n for (let i = 0; i < sessionStorage.length; i++) {\n if (sessionStorage.key(i)?.startsWith(AegisInAppManager.CONVERSION_STORAGE_PREFIX)) return true;\n }\n } catch {\n // Storage blocked: the in-memory signal set by the runtime still holds.\n }\n return false;\n }\n\n /**\n * Display one loaded trigger-gated campaign by id — a smart trigger's fire\n * decision (MicroIntentArming). False when it is not loaded, already shown,\n * suppressed, or the host cancelled it (`campaign-will-show`).\n */\n showTriggeredCampaign(campaignId: string): boolean {\n const campaign = this.campaigns.find((c) => c.id === campaignId);\n if (!campaign || this.displayedCampaigns.has(campaignId) || this.isSuppressed(campaignId)) {\n return false;\n }\n this.displayCampaign(campaign);\n return this.displayedCampaigns.has(campaignId);\n }\n \n private connectSSE(): void {\n if (this.eventSource) {\n this.disconnectSSE();\n }\n \n if (!this.organizationId) {\n this.log('Cannot connect SSE without organization ID', 'warn');\n return;\n }\n \n const url = new URL('/v1/stream/realtime', this.apiHost);\n // Non-secret routing hint (SSE_PER_CELL_ROUTING_SPEC D1) so the gateway\n // proxies to the tenant's own cell's SSE server (no-op single-cell).\n url.searchParams.set('org', this.organizationId);\n\n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Organization-ID': this.organizationId,\n };\n \n if (this.contactId) {\n headers['X-Contact-ID'] = this.contactId;\n }\n // C1b: only send the token with its own contact.\n if (this.contactId && this.contactToken) {\n headers['X-Contact-Token'] = this.contactToken;\n }\n\n // Append onto url.searchParams — the URL already carries `?org=…`, so\n // string-concatenating a second `?…` would corrupt the query.\n Object.entries(headers).forEach(([key, value]) => {\n url.searchParams.append(key, value);\n });\n\n this.eventSource = new EventSource(url.toString());\n \n this.eventSource.addEventListener('open', () => {\n this.log('SSE connection established');\n this.reconnectAttempts = 0;\n });\n \n this.eventSource.addEventListener('in_app_campaign_updated', (event: MessageEvent) => {\n try {\n const data = JSON.parse(event.data);\n this.log(`Received in-app campaign update: ${data.campaign_id}`);\n this.refreshCampaigns();\n } catch (error) {\n this.log(`Error parsing SSE event: ${error}`, 'error');\n }\n });\n \n this.eventSource.addEventListener('heartbeat', () => {\n this.log('SSE heartbeat received');\n });\n\n this.eventSource.addEventListener('error', () => {\n this.log('SSE connection error', 'error');\n\n if (this.eventSource?.readyState === EventSource.CLOSED) {\n this.attemptReconnect();\n }\n });\n }\n \n private disconnectSSE(): void {\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = undefined;\n this.log('SSE connection closed');\n }\n }\n \n private attemptReconnect(): void {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.log('Max reconnect attempts reached, giving up', 'warn');\n return;\n }\n \n this.reconnectAttempts++;\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\n \n this.log(`Reconnecting SSE in ${delay}ms (attempt ${this.reconnectAttempts})`);\n \n setTimeout(() => {\n if (this.isInitialized && this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n }, delay);\n }\n \n private async refreshCampaigns(): Promise<void> {\n try {\n // Build URL with client context for server-side targeting (Critical Fix B).\n // Send pathname + search so the server's page_url_match can evaluate\n // query-string patterns (e.g. `*placed=1*` for the post-purchase scratch,\n // `*?step=cart*` for cart nudges). Pathname-only silently dropped the\n // query, so any query-anchored display rule never matched on the\n // page-load surface. Prefix patterns (`/s/*`, `*/p/*`) are unaffected —\n // their trailing `.*` absorbs the appended query.\n const context = new URLSearchParams({\n device_type: this.detectDeviceType(),\n // Credential params keep their key and lose their value, so a\n // `*placed=1*` / `*?step=cart*` rule still matches.\n page_url:\n typeof window !== 'undefined'\n ? redactUrl(window.location.pathname + window.location.search)\n : '/',\n });\n\n // Geo is resolved server-side from IP — no need to send.\n // The new-visitor verdict is fixed for the session (utils/visitor.ts),\n // so every refresh in a session sends the same value.\n context.set('is_new_user', isNewVisitor() ? 'true' : 'false');\n\n // Surface filter (In-App Surface Filter tracker P2). Thread the\n // host-declared surface (from aegis.page(name) / aegis.screen(name))\n // through so the server drops campaigns scoped to other surfaces.\n // Omitted when no surface was declared → server applies no filter\n // (back-compat with hosts that haven't wired a surface declaration).\n if (this.currentSurface) {\n context.set('current_surface', this.currentSurface);\n }\n // Dynamic Screens P2 — thread the raw screen name so the server matches\n // it against campaigns' free-form `target_screens`. Omitted when no\n // screen declared (no free-form match attempted).\n if (this.currentScreen) {\n context.set('current_screen', this.currentScreen);\n }\n // Outlet switcher — thread the actively-viewed outlet so the server serves\n // outlet-scoped campaigns for THIS outlet (preferred over the property's\n // static outlet). Omitted when no outlet declared → property/brand-wide.\n if (this.currentLocationId) {\n context.set('location_id', this.currentLocationId);\n }\n\n const url = `${this.apiHost}/v1/in-app/active?${context.toString()}`;\n\n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n\n if (this.userId) {\n headers['X-User-ID'] = this.userId;\n }\n\n if (this.contactId) {\n headers['X-Contact-ID'] = this.contactId;\n }\n // C1b: only send the token with its own contact.\n if (this.contactId && this.contactToken) {\n headers['X-Contact-Token'] = this.contactToken;\n }\n\n if (this.organizationId) {\n headers['X-Organization-ID'] = this.organizationId;\n }\n\n if (this.propertyId) {\n headers['X-Property-Id'] = this.propertyId;\n }\n\n // Send cached A/B assignments so server can skip re-evaluation (Critical Fix C)\n const abAssignments = this.getABAssignments();\n if (Object.keys(abAssignments).length > 0) {\n headers['X-AB-Assignments'] = btoa(JSON.stringify(abAssignments));\n }\n\n const response = await fetch(url, {\n method: 'GET',\n headers,\n credentials: 'include',\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch campaigns: ${response.status}`);\n }\n\n const payload = await response.json();\n // Server returns an array; defensively handle error bodies (HTML\n // from misconfigured gateways, {} from maintenance endpoints, etc.)\n // so a bad response doesn't turn `updateContactId` into a thrower.\n this.campaigns = Array.isArray(payload) ? payload : [];\n\n // Process A/B assignments from server response (Critical Fix C)\n this.processABAssignments(this.campaigns);\n\n // Rehydrate conversion suppression from sessionStorage so a buyer\n // who converted on a prior page load in the same tab does not see\n // a discount popup on this load. Must run after this.campaigns is\n // populated and BEFORE tryDisplayNextCampaign so the first display\n // attempt honors suppression.\n this.rehydrateSuppressionFromStorage();\n\n this.log(`Fetched ${this.campaigns.length} campaigns`);\n\n // P2 of WEB_SDK_DLR_ENHANCEMENT_TRACKER — fire `served` for each\n // freshly-arrived campaign. Idempotent on the server side via\n // idempotency_key; sessionStorage dedup here suppresses re-fires\n // within the same tab (the same campaign can be in multiple\n // refreshCampaigns responses across a session).\n this.emitServedEvents(this.campaigns);\n\n // Lifecycle: `campaigns-loaded` — fires after every successful refresh\n // with the full active-campaign list. Host apps subscribe to drive\n // route-aware allowlists, sticky inboxes, badge counts, etc.\n this.emit('campaigns-loaded', this.campaigns);\n\n // Resolve `aegis.inApp.ready` after the FIRST successful poll so host\n // apps can `await aegis.inApp.ready` before reading `campaigns` or\n // making decisions that depend on a known-good initial state. Idempotent\n // — calling resolve() twice is a no-op for an already-settled promise.\n this.readyResolve();\n\n // Persistent launchers (active_web_chat) mount AMBIENTLY first — they are\n // a single always-on entry point, not an interruption, so they must not\n // depend on being the one campaign tryDisplayNextCampaign happens to pick.\n this.mountPersistentLaunchers();\n\n this.tryDisplayNextCampaign();\n\n // F2/F3 / U8 — fill any `[data-aegis-slot]` anchors with matching\n // `embedded_card`-mode campaigns. Independent of the overlay path:\n // a campaign with delivery_modes=[overlay,card] renders both ways\n // (overlay if conditions match + card if the page has a slot).\n // No-op when no slots are on the page.\n this.renderIntoSlots();\n\n // Surface-Canvas inline element anchoring — render inline-format\n // campaigns (progress_bar / banner / milestone / carousel / rec) into a\n // host element picked via the element inspector (interactive_config.anchor\n // .element_selector). Unlike `data-aegis-slot` (host declares the slot),\n // the operator anchors to ANY existing element — the Plotline/WebEngage\n // model. No-op when no campaign carries an `anchor`.\n this.renderIntoAnchors();\n\n // PR-B U7 — also fill any `[data-aegis-token-data]` anchors with\n // a server-injected campaign payload. Used by the standalone-page\n // route at `/s/[slug]/[workspace]/[page_key]?t=<token>`: the page\n // handler verifies the token server-side (via F16 internal),\n // embeds the resolved campaign data inline, and the SDK uses the\n // SAME renderer dispatch as the slot path. No separate widget\n // code, no public token-resolve endpoint.\n this.renderTokenAnchors();\n\n } catch (error) {\n this.emitError(error, { stage: 'refresh-campaigns' });\n this.log(`Error refreshing campaigns: ${error}`, 'error');\n }\n }\n\n // P2 of WEB_SDK_DLR_ENHANCEMENT_TRACKER — `served` event emission.\n //\n // Fired once per campaign per session after the SDK has the payload\n // in hand (post-refreshCampaigns). Distinct from `impression` which\n // fires only after DOM visibility is confirmed via IntersectionObserver.\n //\n // The (campaign × session) dedup key is `aegis_served_fired:<id>` in\n // sessionStorage so a campaign that survives across multiple page\n // navigations in one tab does NOT re-fire `served` on every refresh.\n // The server side is also idempotent via `idempotency_key`; this is\n // just to keep network volume sane.\n private static readonly SERVED_DEDUP_PREFIX = 'aegis_served_fired:';\n\n private emitServedEvents(campaigns: InAppCampaign[]): void {\n if (typeof sessionStorage === 'undefined') {\n // No session storage (SSR, worker context) — fire unconditionally;\n // the server idempotency key still prevents duplicates.\n for (const c of campaigns) {\n void this.trackEvent(c.id, 'served');\n }\n return;\n }\n for (const c of campaigns) {\n const key = AegisInAppManager.SERVED_DEDUP_PREFIX + c.id;\n if (sessionStorage.getItem(key)) continue;\n try {\n sessionStorage.setItem(key, '1');\n } catch {\n // sessionStorage quota / disabled — fire anyway, server dedupes.\n }\n void this.trackEvent(c.id, 'served');\n }\n }\n\n // --- Client Context Helpers (Critical Fix B) ---\n\n private detectDeviceType(): 'mobile' | 'desktop' | 'tablet' {\n if (typeof navigator === 'undefined') return 'desktop';\n const ua = navigator.userAgent;\n if (/Mobi|Android/i.test(ua)) return 'mobile';\n if (/iPad|Tablet/i.test(ua)) return 'tablet';\n return 'desktop';\n }\n\n // --- A/B Assignment Persistence (Critical Fix C) ---\n\n // Both A/B helpers sit on the campaign-fetch path, so blocked storage\n // (where even `typeof localStorage` throws) must not abort the fetch.\n private getABAssignments(): Record<string, string> {\n try {\n if (typeof localStorage === 'undefined') return {};\n return JSON.parse(localStorage.getItem('aegis_ab_assignments') || '{}');\n } catch {\n return {};\n }\n }\n\n private processABAssignments(campaigns: InAppCampaign[]): void {\n const stored = this.getABAssignments();\n for (const campaign of campaigns) {\n if (campaign.assigned_variant_id) {\n stored[campaign.id] = campaign.assigned_variant_id;\n }\n }\n try {\n if (typeof localStorage === 'undefined') return;\n localStorage.setItem('aegis_ab_assignments', JSON.stringify(stored));\n } catch {\n // storage blocked — assignments stay server-evaluated per fetch\n }\n }\n\n private getVariantId(campaignId: string): string | undefined {\n const assignments = this.getABAssignments();\n return assignments[campaignId] ?? undefined;\n }\n \n private tryDisplayNextCampaign(): void {\n // Only auto-display campaigns that DO NOT have a client_trigger.\n // Trigger-gated campaigns wait for the host app to call\n // `onClientEvent()` or for TriggerEngine to fire the matching\n // behavioural event. This preserves the preload-first contract —\n // the campaign is armed but the render is gated.\n // Conversion-aware suppression: campaigns silenced by a prior\n // notifyConversion() call (or rehydrated from sessionStorage) are\n // skipped until their suppression window expires.\n const campaign = this.campaigns.find((c) =>\n !this.displayedCampaigns.has(c.id) && !c.client_trigger && !this.isSuppressed(c.id)\n // active_web_chat is a persistent launcher, mounted ambiently by\n // mountPersistentLaunchers() — it must not consume the single overlay pick.\n && c.type !== 'active_web_chat'\n // push_primer eligibility runs BEFORE the pick (2026-07-21). Previously\n // the primer was picked first (top priority), consumed the page view's\n // single overlay slot, and THEN its gates (permission / opt-in mode /\n // trigger) declined inside the renderer — burning the slot with nothing\n // shown, spamming phantom impression/dismissed events into the server\n // frequency caps, and starving every other overlay. The observed\n // symptom was \"the prompt shows unreliably\". Ineligible primer is now\n // simply skipped (NOT marked displayed) so the next campaign takes the\n // slot and the primer stays eligible for a later view.\n && (c.sub_type !== 'push_primer' || this._primerEligibleNow())\n // Embedded-only campaigns (hero / rails / value bars) render via\n // renderIntoSlots, never as overlays. displayCampaign's embedded-only\n // guard early-returns WITHOUT marking them displayed, so picking one\n // here silently burned the single overlay attempt for the page load —\n // with any embedded-only campaign sorted first, NO overlay ever showed\n // (observed on the ST Shopify store: every overlay served, zero shown).\n && !(\n Array.isArray(c.delivery_modes) &&\n c.delivery_modes.length > 0 &&\n !c.delivery_modes.includes('in_app_overlay')\n ),\n );\n\n // Dispatch-decision diagnostics (1.36.5): capture what the overlay pick\n // decided so the boot beacon can report it — closes the observability\n // gap where 'primer eligible but nothing rendered' was undebuggable.\n const primer = this.campaigns.find((c) => c.sub_type === 'push_primer');\n this._lastDispatch = {\n picked: campaign?.id ?? null,\n primer_present: !!primer,\n primer_eligible: primer ? this._primerEligibleNow() : false,\n error: null,\n };\n\n if (campaign) {\n try {\n this.displayCampaign(campaign);\n } catch (err) {\n this._lastDispatch.error = `${(err as Error)?.name}: ${(err as Error)?.message}`.slice(0, 120);\n throw err;\n }\n }\n }\n\n private _lastDispatch: {\n picked: string | null;\n primer_present: boolean;\n primer_eligible: boolean;\n error: string | null;\n } | null = null;\n\n /** Persistent launchers (active_web_chat) mount AMBIENTLY on every arm — a\n * single always-on entry point, NOT an interruption, so they must not\n * compete in the one-at-a-time overlay queue (tryDisplayNextCampaign).\n * Idempotent: renderActiveWebChatLauncher no-ops once a launcher is live and\n * displayedCampaigns guards re-display. Before this, the launcher only\n * mounted when it happened to be the single campaign tryDisplayNextCampaign\n * picked — so aegis.chat.open() / the mobile \"Ask\" tab silently did nothing\n * whenever another non-triggered campaign was armed first. */\n private mountPersistentLaunchers(): void {\n for (const c of this.campaigns) {\n if (c.type !== 'active_web_chat') continue;\n if (this.displayedCampaigns.has(c.id)) continue;\n if (this.isSuppressed(c.id)) continue;\n this.displayCampaign(c);\n }\n }\n\n // --- F2/F3 / U8: embedded-card slot rendering ---------------------------\n //\n // Tracks slot DOM elements we've already filled so a second\n // refresh-cycle (SSE update, polling tick) doesn't re-render the same\n // campaign on top of itself. WeakSet so DOM removals get GC'd.\n private filledSlots = new WeakSet<Element>();\n // Per-slot signature (eligible campaign ids) + rotation timer so a re-render\n // CLEARS prior SDK-injected content before re-filling — otherwise repeated\n // refresh cycles append duplicate panes (the stacked value-bar bug).\n private slotSignatures = new WeakMap<Element, string>();\n private slotTimers = new WeakMap<Element, ReturnType<typeof setInterval>>();\n // Observes the DOM for slot elements mounting AFTER a poll (SPA navigation —\n // e.g. returning to the storefront home re-mounts the hero slot). Without it\n // an SPA-remounted slot stays empty until the next 5-min poll.\n private slotObserver: MutationObserver | null = null;\n private slotRenderScheduled = false;\n // slot_unfilled telemetry (Surface/Slot Contract tracker P2) — one beacon\n // per (campaign, path) per session, checked on a delayed sweep so SPA\n // hydration / late mounts don't false-positive.\n private slotUnfilledBeaconed = new Set<string>();\n private slotUnfilledSweepTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Scan the page for `[data-aegis-slot]` anchors and render any\n * campaign whose `widget_category` matches the slot key AND whose\n * `delivery_modes` includes `'embedded_card'`.\n *\n * Runs after every refresh-campaigns success — the WeakSet guard\n * keeps it idempotent across re-fetches. The slot key is the campaign's\n * `widget_category` (the wide grouping discriminator from F3), so a\n * `<div data-aegis-slot=\"feedback\">` will accept the first active\n * campaign with `widget_category='feedback'` + embedded_card mode.\n */\n /** Normalize a declared free-form screen name to a canonical surface key\n * ('storefront.catalog' → 'storefront_catalog'), or null when it maps\n * outside the enum (e.g. 'shopify.product'). Mirrors the server's\n * `_canonical_surface_from_screen`. */\n private canonicalSurfaceFromScreen(): string | null {\n if (!this.currentScreen) return null;\n const norm = this.currentScreen\n .trim()\n .toLowerCase()\n .replace(/[.\\- ]/g, '_');\n return AegisInAppManager.KNOWN_SURFACES.has(norm) ? norm : null;\n }\n\n /**\n * Dynamic Screens P2 + substrate isolation — combined surface + free-form\n * screen predicate, mirroring the server's `_screen_or_surface_matches`:\n * - screen-only campaign matches strictly on `currentScreen`\n * - a declared screen NORMALIZES into the canonical tier\n * ('storefront.catalog' → storefront_catalog) so surface pins match\n * - a declared screen that maps OUTSIDE the enum (e.g. 'shopify.product')\n * does NOT fall through to the all-surfaces default for surface-pinned\n * campaigns — actii.me-pinned campaigns never render on Shopify\n * - hosts that declare nothing keep the legacy render-everything default\n */\n private matchesCurrentScreenOrSurface(c: InAppCampaign): boolean {\n const screens = c.target_screens;\n const screenOk =\n !!screens && screens.length > 0 &&\n this.currentScreen !== null && screens.includes(this.currentScreen);\n if (screens && screens.length > 0 && (!c.surface || c.surface.length === 0)) {\n return screenOk;\n }\n if (screenOk) return true;\n if (!c.surface || c.surface.length === 0) return true;\n const effective = this.currentSurface ?? this.canonicalSurfaceFromScreen();\n if (effective !== null) return c.surface.includes(effective);\n if (this.currentScreen !== null) return false; // declared non-canonical substrate — isolation\n return true; // nothing declared — back-compat\n }\n\n // Re-fill slots that mount after a poll (SPA route changes). Observes the DOM\n // once; when a new [data-aegis-slot] appears it re-runs renderIntoSlots from\n // the cached campaigns (idempotent — already-filled slots are skipped). Does\n // NOT loop on the SDK's own injections (those add content INTO a slot, not a\n // new slot element).\n private ensureSlotObserver(): void {\n if (this.slotObserver || typeof MutationObserver === 'undefined' || typeof document === 'undefined') {\n return;\n }\n this.slotObserver = new MutationObserver((mutations) => {\n let sawSlot = false;\n for (const m of mutations) {\n m.addedNodes.forEach((n) => {\n if (sawSlot || n.nodeType !== 1) return;\n const el = n as Element;\n if (el.matches?.('[data-aegis-slot]') || el.querySelector?.('[data-aegis-slot]')) {\n sawSlot = true;\n }\n });\n if (sawSlot) break;\n }\n if (!sawSlot || this.slotRenderScheduled) return;\n this.slotRenderScheduled = true;\n const run = () => {\n this.slotRenderScheduled = false;\n this.renderIntoSlots();\n };\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(run);\n else setTimeout(run, 0);\n });\n this.slotObserver.observe(document.body, { childList: true, subtree: true });\n }\n\n private renderIntoSlots(): void {\n if (typeof document === 'undefined') return;\n this.ensureSlotObserver();\n // Surface/Slot Contract P2 — schedule the delayed unfilled-slot sweep on\n // every render attempt (including the zero-anchor early return below,\n // which is exactly the GAP-A drop this telemetry exists to expose).\n this.scheduleSlotUnfilledSweep();\n const slots = document.querySelectorAll('[data-aegis-slot]');\n if (slots.length === 0) return;\n\n // ALL eligible campaigns per category, priority-sorted (the cell-plane\n // endpoint already orders `this.campaigns` by priority DESC). A slot with\n // `data-aegis-slot-rotate` cycles through them; otherwise the first wins.\n const eligibleByCategory = new Map<string, InAppCampaign[]>();\n const seenIds = new Set<string>();\n for (const c of this.campaigns) {\n // Surface gate (In-App Surface Filter tracker P2). Defense-in-depth\n // for stale cached campaigns — the server-side filter at\n // /v1/in-app/active already drops mismatched campaigns, but this\n // catches any campaign cached from a fetch made on a different surface.\n if (!this.matchesCurrentScreenOrSurface(c)) continue;\n const modes = c.delivery_modes;\n const category = c.widget_category;\n if (!modes || !modes.includes('embedded_card')) continue;\n if (!category) continue;\n // Operator-pinned placement (interactive_config.anchor, the Surface-\n // Canvas element anchor rendered by renderIntoAnchors): when the pinned\n // element RESOLVES on this page, the anchor path owns the render — never\n // ALSO fill a category slot (one campaign, one render). Unresolved\n // selector (element absent on this page / typo) falls through to the\n // category slot so the campaign still shows somewhere.\n const anchorSel = (c.interactive_config as\n | { anchor?: { element_selector?: string } }\n | undefined)?.anchor?.element_selector;\n if (typeof anchorSel === 'string' && anchorSel.trim()) {\n let anchorResolves = false;\n try {\n anchorResolves = !!document.querySelector(anchorSel);\n } catch {\n anchorResolves = false;\n }\n if (anchorResolves) continue;\n }\n // Dedup by campaign id — a duplicate row (or a campaign cached twice)\n // must never produce two panes in the same slot.\n if (seenIds.has(c.id)) continue;\n seenIds.add(c.id);\n const arr = eligibleByCategory.get(category);\n if (arr) arr.push(c);\n else eligibleByCategory.set(category, [c]);\n }\n if (eligibleByCategory.size === 0) return;\n\n slots.forEach((slot) => {\n const key = slot.getAttribute('data-aegis-slot');\n if (!key) return;\n const list = eligibleByCategory.get(key);\n if (!list || list.length === 0) return;\n // Idempotency: only (re)fill when the eligible set for this slot CHANGED.\n // If unchanged and already filled, leave it — no churn, no re-fired\n // impressions. If changed (or first fill), CLEAR all prior SDK-injected\n // content + any rotation timer so re-renders never STACK duplicate panes.\n const sig = list.map((c) => `${c.id}:${c.assigned_variant_id ?? ''}`).join(',');\n if (this.filledSlots.has(slot) && this.slotSignatures.get(slot) === sig) return;\n const oldTimer = this.slotTimers.get(slot);\n if (oldTimer) {\n clearInterval(oldTimer);\n this.slotTimers.delete(slot);\n }\n slot\n .querySelectorAll(\n ':scope > [data-aegis-rotate-pane], :scope > [data-aegis-slot-rendered], :scope > [data-aegis-rotate-dots]',\n )\n .forEach((n) => n.remove());\n this.slotSignatures.set(slot, sig);\n // A slot may ship a host-rendered DEFAULT (e.g. the storefront value-bar's\n // free-delivery strip). Remove it before injecting the campaign(s) so the\n // two don't double up — the campaign REPLACES the default.\n slot.querySelectorAll(':scope > [data-aegis-slot-default]').forEach((d) => d.remove());\n // Rotation is OPT-IN per slot via `data-aegis-slot-rotate=\"<ms>\"`. When set\n // AND more than one campaign is eligible, cycle them one at a time (the\n // storefront sticky-footer value bar). Absent → first-active-per-category\n // wins (every existing slot, e.g. the bill, is unchanged).\n // Rotation is opt-in via the slot's `data-aegis-slot-rotate` attr OR any\n // eligible campaign declaring `interactive_config.slot_rotate_ms` (the\n // dashboard \"Rotate as a carousel\" toggle) — so a campaign can turn the\n // slot into a carousel without the host editing the embed div.\n const attrMs = parseInt(slot.getAttribute('data-aegis-slot-rotate') || '0', 10);\n const declaredMs = list.reduce((m, c) => {\n const v = Number((c.interactive_config as Record<string, unknown> | undefined)?.slot_rotate_ms);\n return Number.isFinite(v) && v > m ? v : m;\n }, 0);\n const rotateMs = attrMs > 0 ? attrMs : declaredMs;\n // `data-aegis-slot-flush` — the host already wraps this slot in its own\n // card (e.g. the storefront value bar's rounded+shadowed footer). Render\n // campaigns FLUSH (drop the per-card radius/shadow) so they read as one\n // unit with the host card instead of a jarring card-within-a-card.\n const flush = (slot as HTMLElement).hasAttribute('data-aegis-slot-flush');\n if (rotateMs > 0 && list.length > 1) {\n this.renderRotatingSlot(slot as HTMLElement, list, rotateMs, flush);\n } else {\n // Wrap in a marker host so a later re-fill can cleanly remove it\n // (prevents stacking) without touching host-rendered siblings.\n const host = document.createElement('div');\n host.setAttribute('data-aegis-slot-rendered', list[0].id);\n slot.appendChild(host);\n this.renderCampaignIntoSlot(list[0], host);\n // Renderer no-op (e.g. a `buy_again` reorder rail with zero items for a\n // first-time customer — the inline rec renderer skips on empty) → drop\n // the empty host so the slot stays truly `:empty` and a host CSS rule\n // (`has-[[data-aegis-slot]:empty]:hidden`) can collapse the whole\n // section. Clear the signature + leave the slot UNfilled so a later\n // re-fetch (after the shopper places their first order) can fill it.\n if (host.childElementCount === 0) {\n host.remove();\n this.slotSignatures.delete(slot);\n return;\n }\n if (flush) this._flattenSlotCard(host);\n }\n this.filledSlots.add(slot);\n });\n }\n\n /**\n * slot_unfilled telemetry (Surface/Slot Contract tracker P2).\n *\n * A served embedded_card campaign whose `widget_category` has NO\n * `data-aegis-slot` anchor on the page is silently discarded by\n * `renderIntoSlots` — the exact failure mode that made the storefront\n * value bar dark for browse traffic while the dashboard reported the\n * campaign \"active\". This sweep runs ~3s after a render attempt (late\n * mounts / SPA hydration get time to appear; the MutationObserver re-fill\n * handles anchors that arrive even later) and beacons one `slot_unfilled`\n * per (campaign, path) per session so the dashboard can show\n * \"served but never rendered\".\n */\n private scheduleSlotUnfilledSweep(): void {\n if (typeof document === 'undefined' || this.slotUnfilledSweepTimer) return;\n this.slotUnfilledSweepTimer = setTimeout(() => {\n this.slotUnfilledSweepTimer = null;\n this.sweepUnfilledSlots();\n }, 3000);\n }\n\n private sweepUnfilledSlots(): void {\n if (typeof document === 'undefined') return;\n const present = new Set<string>();\n document.querySelectorAll('[data-aegis-slot]').forEach((el) => {\n const k = el.getAttribute('data-aegis-slot');\n if (k) present.add(k);\n });\n const path =\n typeof window !== 'undefined' ? window.location.pathname : '';\n for (const c of this.campaigns) {\n if (!this.matchesCurrentScreenOrSurface(c)) continue;\n if (!c.delivery_modes || !c.delivery_modes.includes('embedded_card')) continue;\n const category = c.widget_category;\n if (!category || present.has(category)) continue;\n // Operator-pinned element anchors report via `anchor_unresolved`\n // (renderIntoAnchors) — never double-beacon them here.\n const anchorSel = (c.interactive_config as\n | { anchor?: { element_selector?: string } }\n | undefined)?.anchor?.element_selector;\n if (typeof anchorSel === 'string' && anchorSel.trim()) continue;\n const key = `${c.id}|${path}`;\n if (this.slotUnfilledBeaconed.has(key)) continue;\n this.slotUnfilledBeaconed.add(key);\n void this.trackEvent(c.id, 'slot_unfilled', {\n meta: {\n widget_category: category,\n surface: this.currentSurface ?? undefined,\n path,\n },\n });\n }\n }\n\n /**\n * Rotating slot — cycles eligible campaigns one at a time inside a single host\n * slot (the storefront sticky-footer \"value bar\"). Each campaign is pre-rendered\n * into its own hidden pane so a live `progress_bar` keeps tracking the cart even\n * while off-screen; only the visibility toggles every `rotateMs`. The impression\n * is deferred at render and fired per campaign on FIRST visibility, so hidden\n * panes never over-count. One rendered pane → no rotation (just shows it).\n */\n private renderRotatingSlot(\n slot: HTMLElement,\n campaigns: InAppCampaign[],\n rotateMs: number,\n flush = false,\n ): void {\n const panes: Array<{ el: HTMLElement; campaign: InAppCampaign }> = [];\n for (const c of campaigns) {\n const pane = document.createElement('div');\n pane.setAttribute('data-aegis-rotate-pane', c.id);\n pane.style.display = 'none';\n slot.appendChild(pane);\n this.renderCampaignIntoSlot(c, pane, { deferImpression: true });\n if (pane.childElementCount > 0) {\n // Host already supplies the card chrome (value bar) → render flush.\n if (flush) this._flattenSlotCard(pane);\n panes.push({ el: pane, campaign: c });\n } else pane.remove(); // renderer no-op'd (e.g. missing config) — drop empty pane\n }\n if (panes.length === 0) return;\n\n const impressed = new Set<string>();\n const markVisible = (c: InAppCampaign) => {\n if (impressed.has(c.id)) return;\n impressed.add(c.id);\n void this.trackEvent(c.id, 'impression');\n this.emit('campaign-shown', c);\n };\n\n let idx = 0;\n\n // Carousel dots — show the shopper this is a ROTATING rail with N options\n // (otherwise the other campaigns sharing the slot are invisible). Rendered as\n // a dedicated centered row at the BOTTOM of the slot (NOT floating above —\n // the storefront value bar wraps the slot in `overflow-hidden`, which would\n // clip an over-the-top indicator). Active dot = tenant brand accent; dots are\n // clickable to jump. Only when >1 pane.\n // Dot colours must contrast with the surface the panes paint. On a dark/\n // charcoal value bar the default slate dots vanish — derive a dark-surface\n // flag from the panes' campaigns (surface_style:'dark' or a dark card_template\n // frame.bg / background_color) and switch to light dots.\n const isDarkSurface = (c: InAppCampaign): boolean => {\n const ic = (c.interactive_config || {}) as Record<string, unknown>;\n if ((ic.surface_style as string) === 'dark') return true;\n const tplBg = (ic.card_template as { frame?: { bg?: string } } | undefined)?.frame?.bg;\n const bg = (typeof tplBg === 'string' ? tplBg : '') || (c.background_color as string) || '';\n if (bg.startsWith('#')) {\n const h = bg.length === 4 ? bg.slice(1).split('').map((x) => x + x).join('') : bg.slice(1, 7);\n const r = parseInt(h.slice(0, 2), 16), g = parseInt(h.slice(2, 4), 16), b = parseInt(h.slice(4, 6), 16);\n if (![r, g, b].some(Number.isNaN)) return (0.299 * r + 0.587 * g + 0.114 * b) / 255 < 0.5;\n }\n return false;\n };\n const darkDots = panes.length > 0 && panes.every((p) => isDarkSurface(p.campaign));\n const dotInactive = darkDots ? 'rgba(255,255,255,0.4)' : 'rgba(15,23,42,0.2)';\n const dotActive = darkDots ? '#ffffff' : 'var(--aegis-brand-accent, #4169e1)';\n\n const dots: HTMLElement[] = [];\n if (panes.length > 1) {\n const bar = document.createElement('div');\n bar.setAttribute('data-aegis-rotate-dots', '');\n bar.setAttribute('role', 'tablist');\n bar.setAttribute('aria-label', 'Offers');\n bar.style.cssText =\n 'display:flex;align-items:center;justify-content:center;gap:5px;padding:2px 0 8px;';\n panes.forEach((_, i) => {\n const dot = document.createElement('button');\n dot.type = 'button';\n dot.setAttribute('role', 'tab');\n dot.setAttribute('aria-label', `Offer ${i + 1} of ${panes.length}`);\n dot.style.cssText =\n `border:none;padding:0;cursor:pointer;height:6px;width:6px;border-radius:999px;background:${dotInactive};transition:width .25s ease,background .25s ease;`;\n dot.addEventListener('click', (e) => {\n e.stopPropagation();\n show(i, true);\n });\n dots.push(dot);\n bar.appendChild(dot);\n });\n slot.appendChild(bar); // appended AFTER the panes → sits below the rotating content\n }\n const paintDots = () => {\n dots.forEach((d, i) => {\n const active = i === idx;\n d.style.width = active ? '16px' : '6px';\n d.style.background = active ? dotActive : dotInactive;\n d.setAttribute('aria-selected', active ? 'true' : 'false');\n });\n };\n\n let timer: ReturnType<typeof setInterval> | null = null;\n const start = () =>\n setInterval(() => {\n show(idx + 1);\n }, rotateMs);\n function show(this: void, i: number, fromClick = false): void {\n panes[idx].el.style.display = 'none';\n idx = ((i % panes.length) + panes.length) % panes.length;\n panes[idx].el.style.display = '';\n markVisible(panes[idx].campaign);\n paintDots();\n // A manual jump restarts the dwell timer so it doesn't flip immediately.\n if (fromClick && timer) {\n clearInterval(timer);\n timer = start();\n }\n }\n\n panes[0].el.style.display = '';\n markVisible(panes[0].campaign);\n paintDots();\n if (panes.length === 1) return;\n\n timer = start();\n // Track so a slot re-fill clears this timer (else multiple timers stack).\n this.slotTimers.set(slot, timer);\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', () => timer && clearInterval(timer), { once: true });\n }\n }\n\n /**\n * Surface-Canvas inline element anchoring (Plotline / WebEngage model).\n *\n * Render inline-format campaigns into a HOST element the operator picked via\n * the element inspector — `interactive_config.anchor.element_selector` — instead\n * of a host-declared `[data-aegis-slot]`. `position` controls placement:\n * inside (default) — append into the element (e.g. the storefront footer card)\n * replace — clear the element's host-default children, then render in\n * before / after — render into a wrapper inserted as a sibling\n *\n * Reuses `renderCampaignIntoSlot` (same sub_type dispatch → `renderProgressBarInline`\n * etc.) so inline render code is single-sourced with the slot path. Embedded-card\n * gated (so an anchored campaign never ALSO renders as an overlay), surface-gated,\n * and idempotent via `filledSlots` keyed on the resolved element. A selector that\n * doesn't resolve emits `anchor_unresolved` (parity with tooltip anchoring).\n */\n private renderIntoAnchors(): void {\n if (typeof document === 'undefined') return;\n for (const c of this.campaigns) {\n if (!this.matchesCurrentScreenOrSurface(c)) continue;\n const modes = c.delivery_modes;\n if (!modes || !modes.includes('embedded_card')) continue;\n const ic = c.interactive_config as Record<string, unknown> | undefined;\n const anchor = ic?.anchor as\n | { element_selector?: string; position?: string; element_spacing?: number }\n | undefined;\n const selector = anchor?.element_selector;\n if (!selector) continue;\n\n let el: Element | null = null;\n try {\n el = document.querySelector(selector);\n } catch {\n // Invalid selector — treat as unresolved below.\n el = null;\n }\n if (!el) {\n void this.trackEvent(c.id, 'anchor_unresolved', {\n meta: { anchor_resolved: false, anchor_selector: selector, widget_type: c.sub_type },\n });\n continue;\n }\n\n const position = (anchor?.position as string) || 'inside';\n // Resolve the actual render target by position.\n let target: HTMLElement;\n if (position === 'before' || position === 'after') {\n target = document.createElement('div');\n target.setAttribute('data-aegis-anchor-wrapper', c.id);\n el.parentNode?.insertBefore(target, position === 'before' ? el : el.nextSibling);\n } else {\n target = el as HTMLElement;\n if (position === 'replace') {\n // Mirror the slot-default contract — clear host-rendered default\n // children so the campaign REPLACES them (never double-renders).\n target.querySelectorAll(':scope > [data-aegis-slot-default]').forEach((d) => d.remove());\n }\n }\n\n // Safe-zone margin so a card anchored onto a third-party site's element\n // doesn't crash into native borders (Ref 2). Applied to the wrapper for\n // before/after, or as padding on the host for inside/replace.\n const spacing = Number(anchor?.element_spacing);\n if (spacing > 0) {\n if (position === 'before' || position === 'after') {\n target.style.margin = `${spacing}px`;\n } else {\n target.style.padding = `${spacing}px`;\n }\n }\n\n if (this.filledSlots.has(target)) continue;\n this.renderCampaignIntoSlot(c, target);\n this.filledSlots.add(target);\n }\n }\n\n /**\n * U7 — render server-injected token-bound campaigns.\n *\n * The standalone-page route at `apps/cashier-portal/src/app/s/[slug]/\n * [workspace]/[page_key]/page.tsx` (and the `/c/[token]` Aegis-hosted\n * fallback) verify the URL token server-side via the F16 internal\n * endpoint, then embed the resolved campaign payload as JSON inside\n * a `<div data-aegis-token-data=\"...\">` anchor. This SDK method\n * scans for those anchors, parses the payload, and dispatches to\n * the SAME `renderCampaignIntoSlot` used by the [data-aegis-slot]\n * path — so widget rendering code is single-sourced.\n *\n * Why server-injected (not client-fetched): keeps INTERNAL_API_SECRET\n * server-side, avoids an extra browser round-trip for verify, gives\n * Next.js SSR-friendly initial HTML for SEO/no-JS users (rendered\n * via the page handler's React tree alongside the SDK anchor).\n *\n * Idempotency: filledSlots is the same WeakSet used by\n * renderIntoSlots — re-running on SSE/poll refresh is a no-op for\n * already-filled anchors.\n */\n private renderTokenAnchors(): void {\n if (typeof document === 'undefined') return;\n const anchors = document.querySelectorAll('[data-aegis-token-data]');\n if (anchors.length === 0) return;\n\n anchors.forEach((anchor) => {\n if (this.filledSlots.has(anchor)) return;\n const raw = anchor.getAttribute('data-aegis-token-data');\n if (!raw) return;\n\n let payload:\n | { campaign?: InAppCampaign; submit_url?: string }\n | null = null;\n try {\n payload = JSON.parse(raw);\n } catch (e) {\n this.log(\n `data-aegis-token-data: invalid JSON, skipping anchor (${e})`,\n 'warn',\n );\n this.filledSlots.add(anchor); // don't retry on next refresh\n return;\n }\n if (!payload || !payload.campaign || !payload.campaign.id) {\n this.log(\n 'data-aegis-token-data: missing `campaign` in payload, skipping',\n 'warn',\n );\n this.filledSlots.add(anchor);\n return;\n }\n\n // U7 step 3 — pass the submit_url through to the renderer so\n // click handlers can POST the structured response (rating value,\n // NPS score, etc.) and atomically consume the token.\n this.renderCampaignIntoSlot(\n payload.campaign,\n anchor as HTMLElement,\n { submitUrl: payload.submit_url },\n );\n this.filledSlots.add(anchor);\n });\n }\n\n /**\n * Slot-mode counterpart to `displayCampaign`. Reuses the existing\n * lifecycle hooks (`campaign-will-show`, `displayedCampaigns`,\n * `campaign-shown`, impression tracking) so analytics + frequency\n * caps work the same way as overlay renders.\n *\n * Only the sub_types that make UX sense embedded are handled; the\n * rest (modal, full_screen, half_interstitial, alert, pip) silently\n * skip — those types only make sense as fullscreen overlays.\n */\n private renderCampaignIntoSlot(\n campaign: InAppCampaign,\n target: HTMLElement,\n // deferImpression: the caller (a rotating slot) fires the impression when\n // the campaign actually becomes VISIBLE, not at render time — so hidden\n // panes in a rotator don't over-count.\n options?: { submitUrl?: string; deferImpression?: boolean },\n ): void {\n const proceed = this.emit('campaign-will-show', campaign);\n if (!proceed) {\n this.log(\n `slot campaign ${campaign.id} suppressed by campaign-will-show handler`,\n );\n return;\n }\n\n this.displayedCampaigns.add(campaign.id);\n this.addAnimationStyles();\n\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n // Inline size — honor display_size for embedded cards too (the overlay path\n // scales via its own sizer). Render into a scaling wrapper so the WHOLE card\n // scales uniformly inline; medium (mul 1) is a no-op so existing slots are\n // unchanged. transform-origin top so the card grows/shrinks within its slot.\n const inlineMul = this._sizeMul(ic);\n if (inlineMul !== 1) {\n const sizer = document.createElement('div');\n sizer.style.cssText = `transform: scale(${inlineMul}); transform-origin: top center;`;\n target.appendChild(sizer);\n target = sizer;\n }\n const { bg, text } = this._surfacePalette(campaign);\n\n // §7.13.I — an embedded FORM (profile completion). Recognised by its\n // field-map `interactive_config.form_fields` (the bill's `multi_step_form`\n // block), independent of sub_type. Renders input fields inline + submits to\n // /in-app/responses so the answers enrich the contact.\n const _formFields = Array.isArray(ic.form_fields)\n ? (ic.form_fields as Array<Record<string, unknown>>)\n : null;\n\n let rendered = false;\n if (_formFields && _formFields.length > 0) {\n rendered = this.renderFormSlot(campaign, _formFields, bg, text, target);\n if (!rendered) { this.displayedCampaigns.delete(campaign.id); return; }\n if (!options?.deferImpression) {\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n }\n return;\n }\n // The full-bleed storefront hero (retired native <HeroBanner>, now a\n // self-describing campaign) routes by widget_category, NOT sub_type ('hero'\n // isn't a valid sub_type) — so it renders identically on ACS / 3rd-party /\n // bill from interactive_config.cards + chrome.\n // Composed single card (rich card editor): any campaign carrying a top-level\n // `card_template` that ISN'T a multi-card surface renders through the generic\n // template engine. This is how the storefront value bar (and any future\n // composed card) renders — no bespoke per-design renderer. Multi-card\n // surfaces (product_recommendation / carousel_cards) keep their own renderers.\n // Per-SKU rating capture (G5) is now a composed-card campaign carrying the\n // `product_rating_list` element — it flows through the standard\n // card_template path below (renderComposedCardSlot → renderCardFromTemplate),\n // so there's no bespoke dispatch here. The element reads the slot's order\n // context off ctx.slotEl. See PRODUCT_RATING_LIST_ELEMENT_PLAN.md.\n // P6.full — flat survey slots get a synthesised card_template too, so they\n // render through the unified slot engine (mutates `ic` in place).\n this._ensureSurveyTemplate(campaign);\n const _isMultiCard =\n campaign.sub_type === 'product_recommendation' || campaign.sub_type === 'carousel_cards';\n if (this._hasComposedTemplate(ic) && !_isMultiCard) {\n rendered = this.renderComposedCardSlot(campaign, ic, target);\n } else if (campaign.widget_category === 'hero') {\n rendered = renderHeroInline(this.buildRenderContext(campaign), target);\n } else\n switch (campaign.sub_type) {\n // P6.full — star_rating / nps_survey / quick_poll slots route through the\n // unified composed-card slot engine via `_ensureSurveyTemplate` (the gate\n // above), so the bespoke *Slot survey renderers are RETIRED.\n // Parity tracker — built-in embedded gamification renderers (no\n // AegisMessageRuntime callback needed, so they work on the bill).\n case 'spin_wheel':\n rendered = this.renderSpinWheelSlot(campaign, ic, bg, text, target);\n break;\n case 'carousel_cards':\n rendered = this.renderCarouselCardsSlot(campaign, ic, bg, text, target);\n break;\n case 'countdown_offer':\n rendered = this.renderCountdownSlot(campaign, ic, bg, text, target);\n break;\n case 'scratch_card':\n rendered = this.renderScratchCardSlot(campaign, ic, bg, text, target);\n break;\n case 'custom_html':\n rendered = this.renderCustomHtmlSlot(campaign, ic, bg, text, target);\n break;\n case 'stories':\n // Inline story-group thumbnail launcher (Plotline-parity embedded\n // widget) — the rings/cover tray; tapping a thumbnail opens the player.\n rendered = renderStoriesRings(this.buildRenderContext(campaign), target);\n break;\n case 'video':\n // Inline video banner (the Video Banner surface); tap-to-unmute hero.\n rendered = renderVideoInline(this.buildRenderContext(campaign), target);\n break;\n case 'progress_bar':\n // Inline progress bar (free-delivery / cart-total goal) embedded into a\n // host slot — e.g. the storefront value-bar footer's `footer_nudge`\n // slot. Reads live cart via runtime.setCartState() and self-updates.\n rendered = renderProgressBarInline(this.buildRenderContext(campaign), target);\n break;\n case 'product_recommendation':\n // Inline recommendation rail (the PDP \"You might also like\" / storefront\n // commerce slot). Cards are pre-hydrated server-side from the page\n // anchor (similar_items) — renders as a horizontal product rail, not the\n // intrusive overlay sheet the trigger path uses.\n rendered = renderProductRecommendationInline(this.buildRenderContext(campaign), target);\n break;\n case 'reviews':\n // Native review wall (Reviews Display & Integrity §3.5, mockup B′). The\n // one I/O-doing inline renderer (Active-Chat precedent): does a single\n // REST read of /v1/reviews/public for the slot's data-product-id, then\n // renders pure DOM. Creds (apiHost/writeKey) come from the manager since\n // the campaign payload doesn't carry them.\n rendered = renderReviewsInline(this.buildRenderContext(campaign), target, {\n apiHost: this.apiHost,\n writeKey: this.writeKey,\n });\n break;\n case 'quiz':\n // Inline quiz on the slot; falls back to a generic card when it has no\n // questions authored (renderQuizSlot returns false).\n rendered = this.renderQuizSlot(campaign, ic, bg, text, target);\n if (!rendered) {\n rendered = this.renderGenericCardSlot(campaign, ic, bg, text, target);\n }\n break;\n // Remaining sub_types (sticky_bar, progress_bar,\n // product_recommendation) render as a generic card on the bill slot\n // (overlay path handles their full UX); dedicated slot variants get added\n // here as merchants need them.\n default:\n // §7.13.H/I — any other embedded card (banner / coupon nudge /\n // birthday / referral / app-install / win-back / generic) renders as a\n // card with its copy, optional image, coupon chip and CTA. Without this\n // the gallery nudges silently never appeared on the bill (only\n // star_rating / nps_survey / form were handled).\n rendered = this.renderGenericCardSlot(campaign, ic, bg, text, target);\n break;\n }\n\n if (!rendered) return;\n if (!options?.deferImpression) {\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n }\n }\n\n /** §7.13.H — generic embedded card: image / title / body / coupon chip / CTA.\n * The catch-all for banner + nudge sub_types (birthday, referral, app-install,\n * win-back, etc.) so EVERY gallery nudge renders on the bill (previously only\n * star_rating / nps_survey / form were handled → nudges never appeared). */\n /**\n * Whether `interactive_config` carries a composed `card_template` (non-empty\n * `elements[]`). The ONE detector shared by all three composed-card cutover\n * gates — inline slot, chrome overlay, and the P6 survey-overlay cutover — so\n * they can't drift (the inline gate previously omitted the non-empty check).\n */\n private _hasComposedTemplate(ic: Record<string, unknown>): boolean {\n const tpl = ic.card_template as { elements?: unknown } | undefined;\n return !!tpl && typeof tpl === 'object' && Array.isArray(tpl.elements) && tpl.elements.length > 0;\n }\n\n /**\n * P6.full — flat survey → card_template adapter. A `nps_survey` / `star_rating`\n * / `quick_poll` authored WITHOUT a card_template gets one synthesised in place\n * (carrying its follow-up config), so the EXISTING composed-template gates route\n * it through the ONE card-template engine + the shared element builders (with\n * the ported reason-capture/thank-you flows) — overlay AND slot. Back-compat:\n * the field names + follow-up defaults match the retired bespoke renderers.\n * No-op when a template already exists or the type isn't a single-card survey.\n */\n private _ensureSurveyTemplate(campaign: InAppCampaign): void {\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n if (this._hasComposedTemplate(ic)) return;\n let element: Record<string, unknown> | null = null;\n if (campaign.sub_type === 'nps_survey') {\n element = {\n type: 'nps',\n content: ic.question as string | undefined,\n thanks_text: ic.thank_you_message,\n nps_followup_enabled: ic.nps_followup_enabled !== false,\n nps_follow_up_promoter: ic.nps_follow_up_promoter,\n nps_follow_up_passive: ic.nps_follow_up_passive,\n nps_follow_up_detractor: ic.nps_follow_up_detractor,\n nps_reason_chips: ic.nps_reason_chips,\n nps_promoter_threshold: ic.nps_promoter_threshold,\n nps_promoter_url: ic.nps_promoter_url,\n nps_promoter_cta: ic.nps_promoter_cta,\n };\n } else if (campaign.sub_type === 'star_rating') {\n element = {\n type: 'rating_input',\n content: ic.question as string | undefined,\n rating_scale: ic.rating_scale,\n thanks_text: ic.thank_you_message,\n rating_followup_enabled: ic.rating_followup_enabled !== false,\n rating_followup_threshold: ic.rating_followup_threshold,\n // The dashboard's star-rating editor writes `rating_low_prompt`; the\n // card element reads `rating_followup_prompt`. Nothing mapped them, so\n // an operator's custom \"what went wrong?\" copy never appeared — the\n // follow-up rendered its hardcoded default instead.\n rating_followup_prompt: ic.rating_followup_prompt ?? ic.rating_low_prompt,\n // Same break, worse blast radius: the editor writes `rating_review_url`\n // (the operator pastes their Google review link there) while this read\n // `rating_high_url` — a key NOTHING writes — falling back to an NPS\n // field that a star-rating campaign never sets. Both resolved\n // undefined, the key was stripped as undefined, and\n // `appendRatingThanks` skipped the CTA entirely. So the SAD path worked\n // (comment box) and the HAPPY path silently did nothing: a 5-star rater\n // was never asked for the public review the operator had configured.\n // Operator key FIRST; the other two stay as back-compat for configs\n // authored against the older shapes.\n rating_high_url: ic.rating_review_url ?? ic.rating_high_url ?? ic.nps_promoter_url,\n rating_high_cta: ic.rating_high_cta,\n // Icon choice was authored + persisted (zod + pydantic) but had no\n // renderer support at all, so every widget drew stars regardless.\n rating_icon: ic.rating_icon,\n };\n } else if (campaign.sub_type === 'quick_poll') {\n const opts = Array.isArray(ic.poll_options)\n ? (ic.poll_options as string[])\n : Array.isArray(ic.options)\n ? (ic.options as string[])\n : [];\n if (opts.length >= 2) {\n element = { type: 'poll', content: ic.question as string | undefined, poll_options: opts, thanks_text: ic.thank_you_message };\n }\n }\n if (!element) return;\n // Strip undefined keys so the synthesised template stays clean.\n const cleaned = Object.fromEntries(Object.entries(element).filter(([, v]) => v !== undefined));\n // HEADER IMAGE (2026-08-14). Both the star-rating and NPS editors ship a\n // MediaField writing `interactive_config.header_image_url`, and the value\n // was persisted — but NOTHING in the SDK read it. The synthesised template\n // carried exactly one element (the nps / rating_input / poll), so an\n // operator could attach a header image, save, and the survey rendered\n // without it. Same authored-but-persisted-and-never-read shape as\n // `rating_icon` and `rating_low_prompt` above.\n //\n // The card template already has an `image` element that reads `image_url`,\n // so this is a wiring fix, not a new capability: prepend it when the\n // operator set one, leaving the element list unchanged otherwise.\n const _headerImage = typeof ic.header_image_url === 'string' ? ic.header_image_url.trim() : '';\n const elements = _headerImage\n ? [{ type: 'image', image_url: _headerImage, aspect: '16/9' }, cleaned]\n : [cleaned];\n // Mutate IN PLACE so callers that already captured `ic` see the template.\n ic.card_template = { frame: {}, elements };\n campaign.interactive_config = ic as InAppCampaign['interactive_config'];\n }\n\n /**\n * Composed single card — renders a campaign's top-level `card_template`\n * (frame + elements: icon/text/progress/image/cta/group/…) through the generic\n * `renderCardFromTemplate` engine. The canonical path for the rich card editor:\n * the storefront value bar, loyalty nudges, and any operator-composed card all\n * render here from declarative JSON, on ACS / custom / third-party sites.\n */\n private renderComposedCardSlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n target: HTMLElement,\n ): boolean {\n const c = campaign as unknown as Record<string, unknown>;\n const card: TemplateCard = {\n image_url: typeof c.image_url === 'string' ? (c.image_url as string) : undefined,\n title: campaign.title,\n body: campaign.body,\n cta_text: typeof c.button_text === 'string' ? (c.button_text as string) : undefined,\n cta_url: typeof c.action_url === 'string' ? (c.action_url as string) : undefined,\n metadata: (ic.card_meta as Record<string, unknown>) || {},\n };\n const template = resolveGlobalTemplate(ic);\n const { accent } = this._surfacePalette(campaign);\n // Expose the host slot to context-bound elements (e.g. product_rating_list\n // reads the order's data-rateable-items / data-order-id / data-token off it).\n const ctx = this.buildRenderContext(campaign);\n ctx.slotEl = (target.closest('[data-aegis-slot]') as HTMLElement | null) ?? target;\n const tile = renderCardFromTemplate(card, {\n template,\n ctx,\n action: 'navigate',\n layoutAccent: this.sanitizeColor((campaign.background_color as string) || accent),\n });\n target.appendChild(tile);\n return true;\n }\n\n /**\n * C2 — composed card body in an OVERLAY (modal / banner / full_screen /\n * half_interstitial). The overlay counterpart to `renderComposedCardSlot`: it\n * builds the same `TemplateCard` from the campaign's canonical fields and paints\n * it with the generic `renderCardFromTemplate` engine, then wraps it in the\n * standard overlay chrome (close button + size scaling) so a composed card looks\n * identical whether overlaid or slotted. Only invoked when a `card_template` is\n * present (see displayCampaign) — flat campaigns never reach here.\n * See IN_APP_FORMAT_LOCK_AND_COMPOSABLE_CARDS_TRACKER.md (Part B / C2).\n */\n private renderComposedCardOverlay(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n ): void {\n const { accent } = this._surfacePalette(campaign);\n const bg = this.sanitizeColor((campaign.background_color as string) || '#ffffff');\n const text = this.sanitizeColor((campaign.text_color as string) || '#111827');\n const fullScreen = campaign.type === 'full_screen';\n const overlay = this.createOverlay('aegis-in-app-composed-card-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = fullScreen\n ? `position: relative; width: 100%; height: 100%; max-width: 100%; overflow: auto;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;`\n : `position: relative; max-width: 380px; width: 92%; border-radius: 18px; overflow: hidden;\n background: ${bg}; color: ${text}; box-shadow: 0 16px 48px rgba(0,0,0,0.22);\n animation: aegisScaleIn 0.3s ease;`;\n\n const c = campaign as unknown as Record<string, unknown>;\n const card: TemplateCard = {\n image_url: typeof c.image_url === 'string' ? (c.image_url as string) : undefined,\n title: campaign.title,\n body: campaign.body,\n cta_text: typeof c.button_text === 'string' ? (c.button_text as string) : undefined,\n cta_url: typeof c.action_url === 'string' ? (c.action_url as string) : undefined,\n metadata: (ic.card_meta as Record<string, unknown>) || {},\n };\n const template = resolveGlobalTemplate(ic);\n const tile = renderCardFromTemplate(card, {\n template,\n ctx: this.buildRenderContext(campaign),\n action: 'navigate',\n layoutAccent: this.sanitizeColor((campaign.background_color as string) || accent),\n });\n modal.appendChild(tile);\n this._addCornerClose(modal, overlay, campaign.id, text);\n\n if (fullScreen) {\n overlay.appendChild(modal);\n } else {\n const mul = this._sizeMul(ic);\n const sizer = document.createElement('div');\n sizer.style.cssText = `display: flex; justify-content: center; align-items: center; width: 100%; transform: scale(${mul}); transform-origin: center;`;\n sizer.appendChild(modal);\n overlay.appendChild(sizer);\n }\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n private renderGenericCardSlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n target: HTMLElement,\n ): boolean {\n const st = this._slotStyle(campaign);\n const card = this._wrapInSlotCard('aegis-in-app-generic-card', campaign.id, bg, text, st.radius);\n const c = campaign as unknown as Record<string, unknown>;\n const img = typeof c.image_url === 'string' ? (c.image_url as string) : '';\n const couponCode = typeof ic.coupon_code === 'string' ? (ic.coupon_code as string) : '';\n const buttonText = typeof c.button_text === 'string' ? (c.button_text as string) : '';\n const actionUrl = typeof c.action_url === 'string' ? (c.action_url as string) : '';\n\n if (img) {\n const im = document.createElement('img');\n im.src = img;\n im.alt = '';\n im.style.cssText = 'width: 100%; height: 96px; object-fit: cover; display: block;';\n card.appendChild(im);\n }\n\n // Value-bar promo banner (e.g. the rotating loyalty login / points nudges):\n // a fixed 2-line ROW — title + body on the left (each 1 line, clamped), and\n // the CTA as a GHOST hyperlink on the extreme right (not a block button\n // below). The fixed two-line height keeps rotating cards the SAME height so\n // the value bar doesn't jump as it cycles.\n const widgetCat = typeof c.widget_category === 'string' ? (c.widget_category as string) : '';\n if (widgetCat === 'promo' && !img) {\n const row = document.createElement('div');\n row.style.cssText = `display:flex; align-items:center; gap:12px; min-height:46px; padding:${st.padY ?? 10}px ${st.padX ?? 14}px;`;\n const colL = document.createElement('div');\n colL.style.cssText = 'flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:2px;';\n if (campaign.title) {\n const t = document.createElement('div');\n t.style.cssText = `font-size:13px; font-weight:${st.weight ?? '700'}; line-height:1.25; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;`;\n t.textContent = campaign.title;\n colL.appendChild(t);\n }\n if (campaign.body) {\n const b = document.createElement('div');\n b.style.cssText = 'font-size:11.5px; opacity:0.7; line-height:1.3; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;';\n b.textContent = campaign.body;\n colL.appendChild(b);\n }\n row.appendChild(colL);\n if (buttonText) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.textContent = buttonText;\n // A compact LIGHT button — soft brand-accent tint with accent text — so\n // it reads as secondary and doesn't compete with the filled primary\n // \"View cart\" CTA below it. Right-aligned + centered; keeps the row 2 lines.\n const accentTok = `var(--aegis-brand-accent, ${text})`;\n btn.style.cssText =\n 'flex:0 0 auto; align-self:center; cursor:pointer; white-space:nowrap;' +\n `background:color-mix(in srgb, ${accentTok} 12%, #fff); color:${accentTok}; border:none;` +\n 'border-radius:999px; padding:7px 16px; font-size:12.5px; font-weight:700;';\n btn.addEventListener('click', () => {\n void this.trackEvent(campaign.id, 'clicked');\n if (actionUrl) {\n window.open(actionUrl, '_blank', 'noopener');\n } else {\n // No URL → canonical open_login action → host `onAction`.\n this.dispatchAction({ type: 'open_login' }, campaign.id);\n }\n });\n row.appendChild(btn);\n }\n card.appendChild(row);\n target.appendChild(card);\n return true;\n }\n\n const cbody = document.createElement('div');\n cbody.style.cssText = `padding: ${st.padY ?? 12}px ${st.padX ?? 12}px; display: flex; flex-direction: column; gap: 6px;`;\n if (campaign.title) {\n const t = document.createElement('div');\n t.style.cssText = `font-size: 13px; font-weight: ${st.weight ?? '700'};`;\n t.textContent = campaign.title;\n cbody.appendChild(t);\n }\n if (campaign.body) {\n const b = document.createElement('div');\n b.style.cssText = 'font-size: 11px; opacity: 0.85; line-height: 1.35;';\n b.textContent = campaign.body;\n cbody.appendChild(b);\n }\n if (couponCode) {\n const chip = document.createElement('span');\n chip.style.cssText = `align-self: flex-start; border: 1px dashed ${text}66; border-radius: 6px; padding: 2px 8px; font-family: monospace; font-size: 11px; letter-spacing: 0.04em;`;\n chip.textContent = couponCode;\n cbody.appendChild(chip);\n }\n if (buttonText) {\n const btn = document.createElement('button');\n btn.textContent = buttonText;\n btn.style.cssText = `margin-top: 2px; align-self: flex-start; padding: 8px 14px; border-radius: ${st.btnRadius ?? 9}px; border: none; background: ${text}; color: ${bg}; font-size: 13px; font-weight: 600; cursor: pointer;`;\n btn.addEventListener('click', () => {\n void this.trackEvent(campaign.id, 'clicked');\n if (actionUrl) window.open(actionUrl, '_blank', 'noopener');\n });\n cbody.appendChild(btn);\n }\n card.appendChild(cbody);\n target.appendChild(card);\n return true;\n }\n\n /**\n * §7.13.I — embedded profile FORM. Renders one input per `form_fields` entry\n * and, on submit, POSTs `{response_type:'form', payload:{fields:{key:value}}}`\n * to `/in-app/responses` — the cell-plane enrichment writes the mapped answers\n * back onto the contact. Keyed by each field's `key` so it matches the\n * campaign's field→contact map.\n */\n private renderFormSlot(\n campaign: InAppCampaign,\n fields: Array<Record<string, unknown>>,\n bg: string,\n text: string,\n target: HTMLElement,\n ): boolean {\n const st = this._slotStyle(campaign);\n const card = this._wrapInSlotCard('aegis-in-app-form-card', campaign.id, bg, text, st.radius);\n card.appendChild(this._buildFormBody(campaign, fields, bg, text, st));\n target.appendChild(card);\n return true;\n }\n\n /** Shared form body — the input fields + submit. Used embedded (slot) today;\n * the overlay form path reuses it too. */\n private _buildFormBody(\n campaign: InAppCampaign,\n fields: Array<Record<string, unknown>>,\n bg: string,\n text: string,\n slotStyle?: { padX?: number; padY?: number; btnRadius?: number; weight?: string },\n ): HTMLElement {\n const body = document.createElement('div');\n body.style.cssText = `padding: ${slotStyle?.padY ?? 16}px ${slotStyle?.padX ?? 16}px; display: flex; flex-direction: column; gap: 10px;`;\n\n if (campaign.title) {\n const t = document.createElement('div');\n t.style.cssText = `font-size: 14px; font-weight: ${slotStyle?.weight ?? '700'};`;\n t.textContent = campaign.title;\n body.appendChild(t);\n }\n if (campaign.body) {\n const b = document.createElement('div');\n b.style.cssText = 'font-size: 12px; opacity: 0.8; margin-top: -4px;';\n b.textContent = campaign.body;\n body.appendChild(b);\n }\n // Ways to earn (WAYS_TO_EARN_ACTION_EARN_SPEC R-A): a form that collects\n // profile fields ASKS; the loyalty `profile_complete` rule PAYS. The number\n // is read from the live rule (`/v1/in-app/earn-hint`), never authored, and\n // with no rule the line stays empty — no promise the rule would not keep.\n const _profileKeys = new Set(['first_name', 'email', 'date_of_birth']);\n const _collectsProfile = fields.some(\n (f) => typeof f.maps_to === 'string' && _profileKeys.has(f.maps_to),\n );\n if (_collectsProfile && this.organizationId && this.writeKey) {\n const earn = document.createElement('div');\n earn.style.cssText = 'font-size: 11px; font-weight: 600; opacity: 0.75; margin-top: -4px;';\n body.appendChild(earn); // filled async; stays empty (no layout shift) on miss\n // The rule is per brand: send the brand (workspace) as the `workspace_id`\n // query param responses.py `get_earn_hint` reads. Absent = org-level.\n const earnHintWs = this.getWorkspaceId?.();\n const earnHintQs = earnHintWs ? `&workspace_id=${encodeURIComponent(earnHintWs)}` : '';\n void fetch(`${this.apiHost}/v1/in-app/earn-hint?rule_type=profile_complete${earnHintQs}`, {\n headers: { 'X-Aegis-Write-Key': this.writeKey, 'X-Organization-ID': this.organizationId },\n })\n .then((r) => (r.ok ? (r.json() as Promise<Record<string, unknown>>) : null))\n .then((d) => {\n if (!d || d.enabled !== true || !(Number(d.points) > 0)) return;\n const name = typeof d.points_name === 'string' ? d.points_name : 'points';\n earn.textContent = `Earn ${Number(d.points)} ${name} for completing your profile`;\n })\n .catch(() => {\n /* the hint is optional */\n });\n }\n\n type FormControl = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;\n const inputs = new Map<string, FormControl>();\n const requiredKeys = new Set<string>();\n // Theme-aware control (was a hardcoded white input that clashed on dark\n // modals). A faint tint of the text color works on both light + dark.\n const ctrlCss = `padding: 9px 11px; border-radius: 9px; border: 1px solid ${text}33; background: ${text}0d; color: ${text}; font-size: 13px; width: 100%; box-sizing: border-box; font-family: inherit;`;\n for (const f of fields) {\n const key = typeof f.key === 'string' ? f.key : null;\n if (!key) continue;\n const ftype = typeof f.type === 'string' ? f.type : 'text';\n const labelText = (typeof f.label === 'string' && f.label) || key;\n const req = f.required === true;\n if (req) requiredKeys.add(key);\n\n const wrap = document.createElement('div');\n wrap.style.cssText = 'display: flex; flex-direction: column; gap: 4px;';\n // Visible label (was placeholder-only — bad a11y + vanished on focus).\n const lbl = document.createElement('label');\n lbl.textContent = labelText + (req ? ' *' : '');\n lbl.style.cssText = `font-size: 11px; font-weight: 600; opacity: 0.8; color: ${text};`;\n wrap.appendChild(lbl);\n\n let ctrl: FormControl;\n if (ftype === 'select') {\n const sel = document.createElement('select');\n sel.style.cssText = ctrlCss;\n const ph = document.createElement('option');\n ph.value = '';\n ph.textContent = (typeof f.placeholder === 'string' && f.placeholder) || 'Select…';\n sel.appendChild(ph);\n (Array.isArray(f.options) ? (f.options as string[]) : []).forEach((o) => {\n const op = document.createElement('option');\n op.value = o; op.textContent = o; sel.appendChild(op);\n });\n ctrl = sel;\n } else if (ftype === 'textarea') {\n const ta = document.createElement('textarea');\n ta.className = 'aegis-field';\n ta.rows = 3;\n ta.placeholder = (typeof f.placeholder === 'string' && f.placeholder) || labelText;\n ta.style.cssText = `${ctrlCss} resize: vertical;`;\n ctrl = ta;\n } else {\n const input = document.createElement('input');\n input.className = 'aegis-field';\n input.type = ftype === 'email' ? 'email' : ftype === 'date' ? 'date' : ftype === 'tel' ? 'tel' : ftype === 'number' ? 'number' : 'text';\n input.placeholder = (typeof f.placeholder === 'string' && f.placeholder) || labelText;\n input.style.cssText = ctrlCss;\n ctrl = input;\n }\n if (req) ctrl.required = true;\n inputs.set(key, ctrl);\n wrap.appendChild(ctrl);\n body.appendChild(wrap);\n }\n\n const submit = document.createElement('button');\n const _cAny = campaign as unknown as Record<string, unknown>;\n submit.textContent = (typeof _cAny.button_text === 'string' && (_cAny.button_text as string)) || 'Save';\n submit.style.cssText = `margin-top: 4px; padding: 10px 16px; border-radius: ${slotStyle?.btnRadius ?? 10}px; border: none; background: ${text}; color: ${bg}; font-size: 14px; font-weight: 600; cursor: pointer;`;\n submit.addEventListener('click', () => {\n const collected: Record<string, string> = {};\n let ok = true;\n for (const [key, input] of inputs) {\n const v = (input.value || '').trim();\n if (requiredKeys.has(key) && !v) { ok = false; input.style.borderColor = '#e11d48'; }\n else input.style.borderColor = `${text}33`;\n if (v) collected[key] = v;\n }\n if (!ok || Object.keys(collected).length === 0) return;\n void this.submitFormResponse(campaign, collected);\n void this.trackEvent(campaign.id, 'clicked', { meta: { fields: Object.keys(collected) } });\n // Thank-you state. The completion Lottie (~150px) would be CLIPPED by the\n // modal's overflow:hidden once the body collapses to one line — reserve\n // room + let the modal overflow (same fix as the star/nps thank-you).\n while (body.firstChild) body.removeChild(body.firstChild);\n const modalEl = body.parentElement as HTMLElement | null;\n if (modalEl) modalEl.style.overflow = 'visible';\n body.style.position = 'relative';\n body.style.minHeight = '176px';\n body.style.alignItems = 'center';\n body.style.justifyContent = 'center';\n const done = document.createElement('div');\n done.style.cssText = 'padding: 8px 0; font-size: 13px; font-weight: 600; text-align: center; display: inline-flex; align-items: center; justify-content: center; gap: 6px;';\n const customThanks =\n typeof (campaign.interactive_config as Record<string, unknown> | undefined)?.thank_you_message === 'string'\n ? ((campaign.interactive_config as Record<string, unknown>).thank_you_message as string)\n : null;\n if (customThanks) {\n done.textContent = customThanks;\n } else {\n done.innerHTML = lucideSvg('check', { size: 14 });\n done.appendChild(document.createTextNode('Thanks — your details are saved.'));\n }\n body.appendChild(done);\n this._playReaction(body, 'affirm', 'check.json', campaign.interactive_config as Record<string, unknown> | undefined); // form complete → check + confetti\n });\n body.appendChild(submit);\n return body;\n }\n\n /** POST a form response to /in-app/responses (gateway → cell-plane). The\n * cell-plane enriches the contact from the campaign's field-map. Best-effort. */\n private async submitFormResponse(\n campaign: InAppCampaign,\n fields: Record<string, string>,\n ): Promise<void> {\n if (!this.organizationId) {\n this.log('submitFormResponse: no organizationId — cannot submit', 'warn');\n return;\n }\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Organization-ID': this.organizationId,\n };\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.contactId && this.contactToken) headers['X-Contact-Token'] = this.contactToken;\n const variantId = (campaign as unknown as Record<string, unknown>).assigned_variant_id as string | undefined;\n const res = await fetch(`${this.apiHost}/v1/in-app/responses`, {\n method: 'POST',\n headers,\n keepalive: true,\n body: JSON.stringify({\n campaign_id: campaign.id,\n response_type: 'form',\n contact_id: this.contactId,\n // The device id, so a play before login joins the person once they\n // log in (RP1, Aman 2026-09-27). `user_id` may be the brand's own id.\n anonymous_id: readAnonIdFromStorage(),\n user_id: this.userId,\n variant_id: variantId,\n platform: 'web',\n payload: { fields },\n }),\n });\n if (!res.ok) this.log(`submitFormResponse failed (${res.status})`, 'warn');\n } catch (e) {\n this.log(`submitFormResponse error: ${e}`, 'warn');\n }\n }\n\n /**\n * Generic interactive response submit → POST /v1/in-app/responses. Returns the\n * parsed server response (which for spin/scratch carries the server-picked\n * `prize`) or null. Interactive Widgets Parity tracker — every embedded widget\n * records a response so analytics + prize-from-segments work uniformly.\n */\n private async submitWidgetResponse(\n campaign: InAppCampaign,\n responseType: string,\n payload: Record<string, unknown>,\n ): Promise<Record<string, unknown> | null> {\n if (!this.organizationId) return null;\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Organization-ID': this.organizationId,\n };\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.contactId && this.contactToken) headers['X-Contact-Token'] = this.contactToken;\n const variantId = (campaign as unknown as Record<string, unknown>).assigned_variant_id as string | undefined;\n const res = await fetch(`${this.apiHost}/v1/in-app/responses`, {\n method: 'POST',\n headers,\n keepalive: true,\n body: JSON.stringify({\n campaign_id: campaign.id,\n response_type: responseType,\n contact_id: this.contactId,\n // The device id, so a play before login joins the person once they\n // log in (RP1, Aman 2026-09-27). `user_id` may be the brand's own id.\n anonymous_id: readAnonIdFromStorage(),\n user_id: this.userId,\n variant_id: variantId,\n platform: 'web',\n payload,\n }),\n });\n if (!res.ok) {\n this.log(`submitWidgetResponse ${responseType} failed (${res.status})`, 'warn');\n return null;\n }\n return (await res.json().catch(() => null)) as Record<string, unknown> | null;\n } catch (e) {\n this.log(`submitWidgetResponse ${responseType} error: ${e}`, 'warn');\n return null;\n }\n }\n\n /**\n * Built-in embedded SPIN WHEEL (no AegisMessageRuntime callback dependency —\n * works on the bill). Slices come from `interactive_config.segments`; on Spin\n * it submits to /in-app/responses, the server picks the prize from the AUTHORED\n * segments, and the wheel animates to that segment + reveals the prize/coupon.\n */\n private renderSpinWheelSlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n target: HTMLElement,\n ): boolean {\n const st = this._slotStyle(campaign);\n const card = this._wrapInSlotCard('aegis-in-app-spin-card', campaign.id, bg, text, st.radius);\n const segs = Array.isArray(ic.segments) ? (ic.segments as Array<Record<string, unknown>>) : [];\n // Keep the authored segment objects aligned to the displayed slices so we\n // can read the landed slice's `prize_type` (a `no_prize` slice — e.g.\n // \"Try again\" — is a LOSS → empathetic reaction, not a celebration).\n const shown = segs.filter((s) => typeof s.label === 'string' && (s.label as string));\n const labels = shown.map((s) => s.label as string);\n const n = labels.length >= 2 ? labels.length : 8;\n const isLossSeg = (i: number): boolean =>\n !!shown[i] && String(shown[i].prize_type || '').toLowerCase() === 'no_prize';\n // W3: the contact's outstanding unredeemed prize for THIS campaign, bound\n // server-side from the canonical contact_entitlements ledger (rides ic like\n // every other serve-time bind). When present we show the won card instead of\n // a fresh, re-rollable wheel — covers returning visitors AND mid-spin dropouts.\n const alreadyWon = (ic.already_won && typeof ic.already_won === 'object')\n ? (ic.already_won as Record<string, unknown>) : null;\n const colorOf = (i: number): string => {\n const c = shown[i] && typeof shown[i].color === 'string' ? (shown[i].color as string) : '';\n return c || (i % 2 === 0 ? text : `${text}40`);\n };\n // Slice geometry is PROPORTIONAL to each segment's probability, so the wheel\n // visually reflects the odds (matches the editor odds bar). Falls back to\n // equal slices when no usable weights exist.\n const rawW = Array.from({ length: n }, (_, i) => {\n const p = shown[i] && typeof shown[i].probability === 'number' ? (shown[i].probability as number) : NaN;\n return Number.isFinite(p) && p > 0 ? p : 0;\n });\n const sumW = rawW.reduce((a, b) => a + b, 0);\n const weights = sumW > 0 ? rawW : Array.from({ length: n }, () => 1);\n const wTotal = sumW > 0 ? sumW : n;\n // `equal` = uniform visual slices (classic wheel; odds stay rigged by the\n // server). `proportional` = slice size reflects each segment's probability.\n // Either way, probability ALWAYS drives the server-picked prize.\n const sizing = typeof ic.spin_slice_sizing === 'string' ? ic.spin_slice_sizing : 'proportional';\n const spans = sizing === 'equal'\n ? Array.from({ length: n }, () => 360 / n)\n : weights.map((w) => (w / wTotal) * 360);\n const starts: number[] = [];\n spans.reduce((acc, s, i) => { starts[i] = acc; return acc + s; }, 0);\n const midOf = (i: number): number => starts[i] + spans[i] / 2;\n const sliceAt = (deg: number): number => {\n const d = ((deg % 360) + 360) % 360;\n for (let i = 0; i < n; i++) { if (d >= starts[i] && d < starts[i] + spans[i]) return i; }\n return n - 1;\n };\n const stops = Array.from({ length: n }, (_, i) => `${colorOf(i)} ${starts[i]}deg ${starts[i] + spans[i]}deg`).join(', ');\n\n // ── Look & feel + interactivity knobs (all optional, default-safe) ──────\n const cfgStr = (k: string): string => (typeof ic[k] === 'string' ? (ic[k] as string) : '');\n const spinMode = cfgStr('spin_mode') || 'both'; // button | drag | both\n const idleAnim = cfgStr('spin_idle_animation') || 'shimmer'; // none | shimmer | wobble\n const winReveal = cfgStr('spin_win_reveal') || 'inline'; // inline (default) | takeover\n const hapticsOn = ic.spin_haptics_enabled !== false;\n let soundOn = ic.spin_sound_enabled !== false;\n const pointerColor = cfgStr('spin_pointer_color') || '#ffffff';\n const hubImg = cfgStr('spin_hub_image_url');\n // W4 asset-swap: wheel_background_url replaces the conic slice face with\n // operator art (labels + dividers suppressed); pointer_image_url swaps the\n // CSS pointer for a custom image. Anchor stays top_center.\n const wheelBgImg = cfgStr('wheel_background_url');\n const pointerImg = cfgStr('pointer_image_url');\n // Pointer position: 'top' (ticker at the 12 o'clock rim, default) or 'center'\n // (a needle pivoting at the hub, pointing up). Either way the winner lands at\n // the top, so the landing math is unchanged — only the pointer's anchor/pivot\n // differs. pointerBase is the base transform the flapper physics builds on.\n const pointerCenter = (cfgStr('spin_pointer_position') || 'top') === 'center';\n const pointerBase = pointerCenter ? 'translate(-50%, -100%)' : 'translateX(-50%)';\n const isUrl = (s: string): boolean => /^https?:\\/\\//i.test(s);\n const now = (): number => (typeof performance !== 'undefined' && performance.now ? performance.now() : Date.now());\n let rotation = 0;\n\n // One-time keyframes for the idle attract loop + the win reveal.\n if (typeof document !== 'undefined' && !document.getElementById('aegis-spin-kf')) {\n const kf = document.createElement('style');\n kf.id = 'aegis-spin-kf';\n kf.textContent =\n '@keyframes aegisSpinWobble{0%,100%{transform:rotate(-5deg)}50%{transform:rotate(5deg)}}' +\n '@keyframes aegisSpinSheen{0%{transform:translateX(-130%)}100%{transform:translateX(360%)}}' +\n '@keyframes aegisSpinRevealIn{0%{opacity:0;transform:scale(0.86)}100%{opacity:1;transform:scale(1)}}';\n document.head.appendChild(kf);\n }\n\n // Lightweight WebAudio tick/chime — created lazily on the first user gesture\n // (AudioContext can only start after interaction). Synth-based; no assets.\n let audioCtx: AudioContext | null = null;\n const ensureAudio = (): AudioContext | null => {\n if (!soundOn) return null;\n try {\n if (!audioCtx) {\n const Ctx = window.AudioContext\n || (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Ctx) return null;\n audioCtx = new Ctx();\n }\n if (audioCtx.state === 'suspended') void audioCtx.resume();\n return audioCtx;\n } catch { return null; }\n };\n const beep = (freq: number, durMs: number, type: OscillatorType = 'triangle', gain = 0.06): void => {\n const ac = ensureAudio();\n if (!ac) return;\n const osc = ac.createOscillator();\n const g = ac.createGain();\n osc.type = type;\n osc.frequency.value = freq;\n g.gain.setValueAtTime(gain, ac.currentTime);\n g.gain.exponentialRampToValueAtTime(0.0001, ac.currentTime + durMs / 1000);\n osc.connect(g); g.connect(ac.destination);\n osc.start();\n osc.stop(ac.currentTime + durMs / 1000);\n };\n const vibrate = (p: number | number[]): void => {\n if (!hapticsOn) return;\n try { (navigator as unknown as { vibrate?: (x: number | number[]) => boolean }).vibrate?.(p); }\n catch { /* haptics unsupported — noop */ }\n };\n const tickFx = (): void => { beep(1180, 26, 'square', 0.035); vibrate(5); };\n const winFx = (): void => {\n [523, 659, 784, 1047].forEach((f, i) => window.setTimeout(() => beep(f, 170, 'sine', 0.07), i * 105));\n vibrate([0, 35, 25, 35, 25, 90]);\n };\n const loseFx = (): void => { beep(320, 220, 'sine', 0.05); vibrate(18); };\n\n const body = document.createElement('div');\n body.style.cssText = `padding: ${st.padY ?? 22}px ${st.padX ?? 20}px 20px; display: flex; flex-direction: column; align-items: center; gap: 10px;`;\n if (campaign.title) {\n const t = document.createElement('div');\n t.style.cssText =\n `font-size: 18px; font-weight: 800; letter-spacing: -0.01em; text-align: center; ` +\n `font-family: 'Urbanist', system-ui, -apple-system, sans-serif;`;\n t.textContent = campaign.title;\n body.appendChild(t);\n }\n if (campaign.body) {\n const sub = document.createElement('div');\n sub.style.cssText = 'font-size: 12.5px; opacity: 0.82; text-align: center; margin-top: -8px; line-height: 1.35;';\n sub.textContent = campaign.body;\n body.appendChild(sub);\n }\n // Wheel keeps a FIXED internal size; the whole card is scaled uniformly by\n // the overlay (display_size) so every element — wheel, fonts, padding, button\n // — keeps the same proportions / look & feel at small / medium / large.\n const SIZE = 172;\n const radius = SIZE / 2;\n const wheelWrap = document.createElement('div');\n wheelWrap.style.cssText = `position: relative; width: ${SIZE}px; height: ${SIZE}px; filter: drop-shadow(0 10px 22px rgba(0,0,0,0.30));`;\n const pointer = document.createElement('div');\n // Crisp teardrop pointer dipping into the rim, with a soft shadow.\n // transform-origin at the top-center = the flap's hinge; the tip (pointing\n // into the rim) swings when a peg kicks it (see the flapper physics below).\n if (pointerCenter) {\n // Center pointer: a SHORT roulette-style pointer anchored at the hub\n // (transform-origin = bottom), pointing UP — a compact marker near the\n // center, NOT a long needle to the rim. Pivots at the hub on a peg kick.\n const needleH = Math.round(radius * 0.42);\n pointer.style.cssText = `position: absolute; left: 50%; top: 50%; width: 20px; height: ${needleH}px; transform: ${pointerBase}; transform-origin: 50% 100%; z-index: 6; pointer-events: none;`;\n if (pointerImg) {\n const pim = document.createElement('img');\n pim.src = pointerImg; pim.alt = '';\n pim.style.cssText = 'width: 100%; height: 100%; object-fit: contain; object-position: top; display: block; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.4));';\n pointer.appendChild(pim);\n } else {\n const stem = document.createElement('div');\n stem.style.cssText = `position: absolute; left: 50%; bottom: 0; transform: translateX(-50%); width: 4px; height: 100%; background: ${pointerColor}; border-radius: 2px; box-shadow: 0 1px 2px rgba(0,0,0,0.35);`;\n const head = document.createElement('div');\n head.style.cssText = `position: absolute; left: 50%; top: -1px; transform: translateX(-50%); width: 0; height: 0; border-left: 8px solid transparent; border-right: 8px solid transparent; border-bottom: 14px solid ${pointerColor}; filter: drop-shadow(0 1px 1px rgba(0,0,0,0.4));`;\n pointer.appendChild(stem); pointer.appendChild(head);\n }\n } else if (pointerImg) {\n // Top pointer, custom <img> (object-fit contain) keeps the image's shape.\n pointer.style.cssText = `position: absolute; left: 50%; top: -12px; transform: ${pointerBase}; transform-origin: 50% 0; width: 30px; height: 34px; z-index: 5;`;\n const pim = document.createElement('img');\n pim.src = pointerImg; pim.alt = '';\n pim.style.cssText = 'width: 100%; height: 100%; object-fit: contain; display: block; filter: drop-shadow(0 2px 2px rgba(0,0,0,0.35));';\n pointer.appendChild(pim);\n } else {\n pointer.style.cssText = `position: absolute; left: 50%; top: -7px; transform: ${pointerBase}; transform-origin: 50% 0; width: 0; height: 0; border-left: 9px solid transparent; border-right: 9px solid transparent; border-top: 16px solid ${pointerColor}; z-index: 5; filter: drop-shadow(0 2px 2px rgba(0,0,0,0.35));`;\n }\n const wheel = document.createElement('div');\n // Thick white rim + inset shading for a tactile, glossy disc. Rotation is\n // JS-driven (rAF) so we can do flick-momentum + per-slice ticks, so NO CSS\n // transition on transform here.\n const wheelFace = wheelBgImg ? `center / cover no-repeat url(\"${wheelBgImg}\")` : `conic-gradient(${stops})`;\n // A custom wheel-face image fills edge-to-edge: drop the white rim + the heavy\n // inner vignette so there's no white boundary and the art isn't darkened.\n const wheelRim = wheelBgImg ? '0' : '5px solid #ffffff';\n const wheelShadow = wheelBgImg ? `0 0 0 2px ${text}2e` : `0 0 0 2px ${text}2e, inset 0 0 22px rgba(0,0,0,0.20)`;\n wheel.style.cssText = `position: relative; width: ${SIZE}px; height: ${SIZE}px; border-radius: 50%; border: ${wheelRim}; box-shadow: ${wheelShadow}; background: ${wheelFace}; overflow: hidden; will-change: transform; touch-action: none; cursor: ${spinMode === 'button' ? 'default' : 'grab'};`;\n if (idleAnim === 'wobble') wheel.style.animation = 'aegisSpinWobble 3s ease-in-out infinite';\n\n // Per-segment images FILL the wedge — a div covering the disc, masked to the\n // pie sector via clip-path (so it's wedge-shaped, NOT a square), with the text\n // label still drawn on top. `spin_slice_image_size` (40–100) sets how far the\n // image reaches from the hub; 100 = to the rim. Skipped under a full face image.\n const sliceImgFrac = Math.max(40, Math.min(100, Number(ic.spin_slice_image_size) || 100)) / 100;\n if (!wheelBgImg) shown.forEach((s, i) => {\n const simg = typeof s.image_url === 'string' ? (s.image_url as string) : '';\n if (!simg || !isUrl(simg)) return;\n const pts: string[] = ['50% 50%'];\n const steps = Math.max(2, Math.ceil(spans[i] / 6));\n for (let st = 0; st <= steps; st++) {\n const a = ((starts[i] + (spans[i] * st) / steps) * Math.PI) / 180;\n const x = 50 + 50 * sliceImgFrac * Math.sin(a);\n const y = 50 - 50 * sliceImgFrac * Math.cos(a);\n pts.push(`${x.toFixed(2)}% ${y.toFixed(2)}%`);\n }\n const segEl = document.createElement('div');\n segEl.style.cssText =\n `position: absolute; inset: 0; background: center / cover no-repeat url(\"${simg}\"); ` +\n `clip-path: polygon(${pts.join(',')}); pointer-events: none;`;\n wheel.appendChild(segEl);\n });\n\n // Crisp white hairline dividers at each (variable) slice boundary — drawn as\n // thin radial lines from center to rim so they track the proportional slices.\n // Operator art (wheelBgImg) carries its own slice lines → suppress ours.\n if (!wheelBgImg) starts.forEach((b) => {\n const ln = document.createElement('div');\n ln.style.cssText = `position: absolute; left: 50%; top: 0; width: 1.5px; height: 50%; background: rgba(255,255,255,0.55); transform-origin: bottom center; transform: translateX(-50%) rotate(${b}deg); pointer-events: none;`;\n wheel.appendChild(ln);\n });\n\n // Segment labels are RADIAL — laid along each slice's bisector (reading\n // outward), so they use the long radius instead of the narrow chord and\n // never run over the hub. Text is flipped on the lower hemisphere to stay\n // upright, the font AUTO-FITS the slice (shrinks for long labels / thin\n // slices), and the optional icon sits just outboard of the label.\n const rHub = 22; // keep clear of the center cap\n const rRim = radius - 8; // keep clear of the rim\n const bandLen = rRim - rHub; // usable radial length for the label\n const rText = (rHub + rRim) / 2; // band center\n if (!wheelBgImg) labels.forEach((label, i) => {\n const mid = midOf(i); // degrees clockwise from top (proportional slice mid)\n const icoVal = shown[i] && typeof shown[i].icon === 'string' ? (shown[i].icon as string) : '';\n // Auto-fit: cap by the slice's angular thickness (chord) AND by the radial\n // length the text would need on one line.\n const chord = 2 * rText * Math.sin((spans[i] * Math.PI) / 180 / 2);\n const byThickness = chord * 0.6;\n const byLength = bandLen / Math.max(4, label.length * 0.62 + (icoVal ? 1.6 : 0));\n const fontPx = Math.max(7, Math.min(13, Math.floor(Math.min(byThickness, byLength))));\n // Lower hemisphere would render upside-down → flip 180° and reverse order.\n const flip = mid > 180 && mid < 360;\n const textRot = flip ? 90 : -90;\n const lab = document.createElement('div');\n lab.style.cssText =\n `position: absolute; left: 50%; top: 50%; width: ${bandLen}px; ` +\n `transform: translate(-50%, -50%) rotate(${mid}deg) translateY(-${rText}px) rotate(${textRot}deg); ` +\n `display: flex; flex-direction: ${flip ? 'row-reverse' : 'row'}; align-items: center; justify-content: center; gap: 4px; ` +\n `white-space: nowrap; pointer-events: none;`;\n if (icoVal) {\n if (isUrl(icoVal)) {\n const im = document.createElement('img');\n im.src = icoVal; im.alt = '';\n im.style.cssText = `width: ${fontPx + 6}px; height: ${fontPx + 6}px; object-fit: contain; flex: 0 0 auto; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.5));`;\n lab.appendChild(im);\n } else {\n const em = document.createElement('span');\n em.textContent = icoVal;\n em.style.cssText = `font-size: ${fontPx + 4}px; line-height: 1; flex: 0 0 auto;`;\n lab.appendChild(em);\n }\n }\n const tx = document.createElement('span');\n tx.textContent = label;\n tx.style.cssText =\n `white-space: nowrap; line-height: 1.05; font-size: ${fontPx}px; font-weight: 900; ` +\n `font-family: 'Urbanist', system-ui, -apple-system, sans-serif; ` +\n `letter-spacing: 0.2px; text-transform: uppercase; color: #fff; ` +\n `text-shadow: 0 1px 3px rgba(0,0,0,0.65), 0 0 1px rgba(0,0,0,0.9); -webkit-font-smoothing: antialiased;`;\n lab.appendChild(tx);\n wheel.appendChild(lab);\n });\n\n // Static glossy sheen over the disc (light from the top — does NOT rotate).\n const sheen = document.createElement('div');\n sheen.style.cssText = `position: absolute; inset: 0; border-radius: 50%; pointer-events: none; z-index: 2; background: radial-gradient(circle at 50% 22%, rgba(255,255,255,0.30), rgba(255,255,255,0.04) 42%, transparent 62%);`;\n\n // Idle attract sweep (shimmer) — a clipped light bar that drifts across the\n // disc until the customer interacts, then it's removed.\n let idleClip: HTMLElement | null = null;\n if (idleAnim === 'shimmer') {\n const clip = document.createElement('div');\n clip.style.cssText = 'position: absolute; inset: 0; border-radius: 50%; overflow: hidden; pointer-events: none; z-index: 3;';\n const bar = document.createElement('div');\n bar.style.cssText = 'position: absolute; top: -25%; left: 0; width: 36%; height: 150%; transform: skewX(-12deg); background: linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent); animation: aegisSpinSheen 2.8s ease-in-out infinite; animation-delay: 0.5s;';\n clip.appendChild(bar);\n idleClip = clip;\n }\n\n const hub = document.createElement('div');\n // Glossy center cap (or a branded logo) — sits above the disc + sheen.\n hub.style.cssText = `position: absolute; left: 50%; top: 50%; width: 40px; height: 40px; transform: translate(-50%, -50%); border-radius: 50%; background: radial-gradient(circle at 35% 30%, #ffffff, #e9edf5); border: 3px solid #fff; box-shadow: 0 3px 8px rgba(0,0,0,0.30); z-index: 4; overflow: hidden; display: flex; align-items: center; justify-content: center;`;\n if (hubImg) {\n // Show the operator's center image in its NATURAL colors, filling the\n // round cap edge-to-edge (no grey tint, no white inner gap).\n hub.style.border = 'none';\n hub.style.background = 'transparent';\n const him = document.createElement('img');\n him.src = hubImg; him.alt = '';\n him.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%; display: block;';\n hub.appendChild(him);\n } else {\n const hubDot = document.createElement('div');\n hubDot.style.cssText = `width: 11px; height: 11px; border-radius: 50%; background: ${bg};`;\n hub.appendChild(hubDot);\n }\n wheelWrap.appendChild(pointer);\n wheelWrap.appendChild(wheel);\n wheelWrap.appendChild(sheen);\n if (idleClip) wheelWrap.appendChild(idleClip);\n wheelWrap.appendChild(hub);\n body.appendChild(wheelWrap);\n\n if (!card.style.position) card.style.position = 'relative';\n\n const result = document.createElement('div');\n result.style.cssText = 'font-size: 14px; font-weight: 800; min-height: 0; text-align: center; letter-spacing: 0.2px;';\n\n // Tiny unobtrusive mute toggle (only when sound is configured on).\n if (ic.spin_sound_enabled !== false) {\n const soundBtn = document.createElement('button');\n soundBtn.type = 'button';\n soundBtn.setAttribute('aria-label', 'Toggle sound');\n soundBtn.innerHTML = lucideSvg(soundOn ? 'volume-2' : 'volume-x', { size: 15 });\n soundBtn.style.cssText = 'position: absolute; top: 10px; left: 12px; background: transparent; border: none; font-size: 15px; cursor: pointer; opacity: 0.65; z-index: 6; line-height: 1;';\n soundBtn.addEventListener('click', () => { soundOn = !soundOn; soundBtn.innerHTML = lucideSvg(soundOn ? 'volume-2' : 'volume-x', { size: 15 }); });\n card.appendChild(soundBtn);\n }\n\n const btn = document.createElement('button');\n btn.textContent = (campaign as unknown as Record<string, unknown>).button_text as string || 'Spin the wheel';\n // Compact pill CTA with depth + a press/hover lift (sized to sit naturally\n // under the wheel; the whole card scales uniformly for small/medium/large).\n btn.style.cssText =\n `padding: 9px 22px; border-radius: 999px; border: none; background: ${text}; color: ${bg}; ` +\n `font-size: 13px; font-weight: 700; letter-spacing: 0.2px; cursor: pointer; ` +\n `box-shadow: 0 4px 12px rgba(0,0,0,0.20); transition: transform 0.15s, box-shadow 0.15s; ` +\n `font-family: 'Urbanist', system-ui, -apple-system, sans-serif;`;\n btn.addEventListener('mouseenter', () => {\n if (btn.disabled) return;\n btn.style.transform = 'translateY(-1px)';\n btn.style.boxShadow = '0 9px 22px rgba(0,0,0,0.28)';\n });\n btn.addEventListener('mouseleave', () => {\n if (btn.disabled) return;\n btn.style.transform = 'translateY(0)';\n btn.style.boxShadow = '0 6px 16px rgba(0,0,0,0.22)';\n });\n\n let spinning = false;\n let resolved = false; // one spin per customer — block re-spin via flick after resolve\n let lastTickSlice = -1;\n\n const stopIdle = (): void => {\n try { idleClip?.remove(); } catch { /* noop */ }\n wheel.style.animation = '';\n };\n\n // ── Flapper pointer physics ──────────────────────────────────────────────\n // The pointer is a hinged flap: each peg (slice boundary) passing under it\n // gives it a kick (scaled by the wheel's current speed) and a spring-damper\n // pulls it back to rest — so it ratchets WITH the wheel and the kicks shrink\n // as the wheel slows, settling at the stop.\n let spinSpeed = 0; // degrees the wheel moved on the last frame/move\n let pDefl = 0, pVel = 0; // pointer deflection (deg) + its angular velocity\n let pointerRaf = 0;\n const stepPointer = (): void => {\n pVel += -pDefl * 0.18; // spring toward rest\n pVel *= 0.7; // damping\n pDefl += pVel;\n if (Math.abs(pDefl) > 0.06 || Math.abs(pVel) > 0.06) {\n pointer.style.transform = `${pointerBase} rotate(${pDefl.toFixed(2)}deg)`;\n pointerRaf = requestAnimationFrame(stepPointer);\n } else {\n pDefl = 0; pVel = 0;\n pointer.style.transform = `${pointerBase} rotate(0deg)`;\n pointerRaf = 0;\n }\n };\n const flickPointer = (): void => {\n pVel += Math.min(9, 1.4 + spinSpeed * 0.5); // bigger kick when faster\n if (!pointerRaf) pointerRaf = requestAnimationFrame(stepPointer);\n };\n\n // Fire a tick (sound + haptic + pointer flick) each time a slice boundary\n // crosses the pointer.\n const playTicks = (deg: number): void => {\n const norm = (((360 - (deg % 360)) % 360) + 360) % 360;\n const slice = sliceAt(norm);\n if (slice !== lastTickSlice) { lastTickSlice = slice; tickFx(); flickPointer(); }\n };\n\n // rAF ease-out to an absolute target angle, with a small overshoot-settle.\n const animateTo = (finalDeg: number, durationMs: number, done: () => void): void => {\n const start = rotation;\n const delta = finalDeg - start;\n const t0 = now();\n const easeOut = (p: number): number => 1 - Math.pow(1 - p, 3);\n let lastRot = start;\n const frame = (): void => {\n const p = Math.min(1, (now() - t0) / durationMs);\n rotation = start + delta * easeOut(p);\n spinSpeed = Math.abs(rotation - lastRot); lastRot = rotation;\n wheel.style.transform = `rotate(${rotation}deg)`;\n playTicks(rotation);\n if (p < 1) { requestAnimationFrame(frame); return; }\n // Settle: tiny back-overshoot then snap to exact, for a tactile stop.\n wheel.style.transition = 'transform 0.18s ease-out';\n wheel.style.transform = `rotate(${finalDeg - 2}deg)`;\n window.setTimeout(() => {\n wheel.style.transform = `rotate(${finalDeg}deg)`;\n rotation = finalDeg;\n window.setTimeout(() => { wheel.style.transition = ''; done(); }, 180);\n }, 70);\n };\n requestAnimationFrame(frame);\n };\n\n type SpinResult = { label: string; code: string; won: boolean; kind?: string; expiresAt?: string | null };\n\n const retireCta = (): void => {\n btn.style.transition = 'opacity 0.3s ease';\n btn.style.opacity = '0';\n btn.style.pointerEvents = 'none';\n window.setTimeout(() => { btn.style.display = 'none'; }, 320);\n };\n\n const renderTakeover = (r: SpinResult): void => {\n const ov = document.createElement('div');\n ov.style.cssText = `position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 22px; text-align: center; background: ${bg}; color: ${text}; border-radius: inherit; animation: aegisSpinRevealIn 0.4s cubic-bezier(0.16,0.86,0.28,1); z-index: 7;`;\n const eyebrow = document.createElement('div');\n eyebrow.textContent = '🎉 You won'; // eslint-disable-line no-restricted-syntax -- game content (spin-wheel win eyebrow)\n eyebrow.style.cssText = 'font-size: 13px; font-weight: 700; letter-spacing: 0.3px; opacity: 0.8;';\n const big = document.createElement('div');\n big.textContent = r.label;\n big.style.cssText = `font-size: 24px; font-weight: 900; letter-spacing: -0.02em; font-family: 'Urbanist', system-ui, -apple-system, sans-serif;`;\n ov.appendChild(eyebrow); ov.appendChild(big);\n if (r.code) {\n const chip = document.createElement('button');\n chip.type = 'button';\n chip.textContent = `${r.code} ⧉`;\n chip.style.cssText = `margin-top: 4px; padding: 9px 16px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 14px; letter-spacing: 1px; cursor: pointer;`;\n chip.addEventListener('click', () => {\n try { void navigator.clipboard?.writeText(r.code); chip.textContent = 'Copied'; }\n catch { /* clipboard unsupported — noop */ }\n });\n ov.appendChild(chip);\n } else if (r.kind === 'freebie') {\n // Freebie has no auto-redeem code — it's claimed manually (in-store /\n // by the team). Say so, rather than implying an auto-applied reward.\n const hint = document.createElement('div');\n hint.textContent = '🎟️ Show this screen to claim'; // eslint-disable-line no-restricted-syntax -- game content (spin-wheel freebie claim hint)\n hint.style.cssText = 'margin-top: 2px; font-size: 12.5px; font-weight: 600; opacity: 0.85;';\n ov.appendChild(hint);\n }\n const validTill = this._validTillLine(r.expiresAt, text);\n if (validTill) ov.appendChild(validTill);\n const claim = document.createElement('button');\n claim.type = 'button';\n claim.textContent = 'Awesome!';\n claim.style.cssText = `margin-top: 6px; padding: 11px 28px; border-radius: 999px; border: none; background: ${text}; color: ${bg}; font-weight: 800; font-size: 14px; cursor: pointer;`;\n claim.addEventListener('click', () => {\n void this.trackEvent(campaign.id, 'clicked');\n ov.style.transition = 'opacity 0.25s ease'; ov.style.opacity = '0';\n window.setTimeout(() => ov.remove(), 250);\n });\n ov.appendChild(claim);\n card.appendChild(ov);\n };\n\n const reveal = (r: SpinResult): void => {\n resolved = true;\n if (r.won) { winFx(); this._playReaction(card, 'celebrate', undefined, ic); }\n else { loseFx(); this._playReaction(card, 'empathize', undefined, ic); }\n if (winReveal === 'takeover' && r.won) renderTakeover(r);\n else if (r.label && r.code) {\n // Inline reveal WITH a code — give it the same copy-to-clipboard chip the\n // takeover card has (W2), instead of printing the code as dead text.\n result.textContent = '';\n const tag = document.createElement('span');\n tag.textContent = `${r.label} — `;\n const chip = document.createElement('button');\n chip.type = 'button';\n chip.textContent = `${r.code} ⧉`;\n chip.style.cssText = `padding: 2px 8px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 12px; letter-spacing: 0.5px; cursor: pointer;`;\n chip.addEventListener('click', () => {\n try { void navigator.clipboard?.writeText(r.code); chip.textContent = 'Copied'; }\n catch { /* clipboard unsupported — noop */ }\n });\n result.appendChild(tag); result.appendChild(chip);\n const validTill = this._validTillLine(r.expiresAt, text);\n if (validTill) result.appendChild(validTill);\n }\n else result.textContent = r.label || 'Thanks for playing!';\n retireCta();\n };\n\n // velocityDegPerMs scales how many full turns the wheel spins (flick feel).\n // We DON'T wait for the server before moving: the wheel spins at a constant\n // velocity the instant the user commits (no dead pause), the ticker keeps\n // clicking, and we hand off to the ease-out the moment the authoritative\n // prize lands — matching the ease-out duration to the live loop speed so\n // there's no velocity jump. The server is the source of truth; the timeout\n // only guards a genuinely dead network. The prize itself is already durable\n // in contact_entitlements regardless of what this animation lands on, so a\n // graceful-loss fallback can never destroy a granted reward.\n const beginSpin = (velocityDegPerMs: number): void => {\n if (spinning || resolved) return;\n spinning = true;\n stopIdle();\n btn.disabled = true; btn.style.opacity = '0.6'; btn.style.transform = 'scale(0.97)';\n result.textContent = '';\n void this.trackEvent(campaign.id, 'clicked');\n ensureAudio();\n const v = Math.min(2.4, Math.max(0.25, velocityDegPerMs || 0.6));\n const turns = Math.round(3 + v * 2.0); // ~3..8 full rotations on the ease-out\n const provisional = Math.floor(Math.random() * n);\n\n // Constant-velocity idle loop — runs until the prize resolves. No landing\n // target yet; just keeps the wheel (and ticker) alive during the round-trip.\n const loopSpeed = 0.5 + v * 0.35; // deg/ms — flick speed sets the loop pace\n let looping = true;\n let done = false;\n let lastTs = now();\n const loopFrame = (): void => {\n if (!looping) return;\n const t = now();\n const moved = loopSpeed * (t - lastTs);\n lastTs = t;\n rotation += moved;\n spinSpeed = moved; // feed the pointer-flick magnitude\n wheel.style.transform = `rotate(${rotation}deg)`;\n playTicks(rotation);\n requestAnimationFrame(loopFrame);\n };\n requestAnimationFrame(loopFrame);\n\n // The slice to land on if the network is truly dead — prefer an authored\n // no_prize slice so the wheel still stops somewhere sensible.\n const lossIndex = (): number => {\n for (let i = 0; i < n; i++) { if (isLossSeg(i)) return i; }\n return provisional;\n };\n\n const launch = (idx: number, r: SpinResult): void => {\n if (done) return; done = true;\n looping = false; // animateTo now drives rotation, taking over from the LIVE angle\n const segMid = midOf(idx);\n const want = (((360 - segMid) % 360) + 360) % 360; // rotation that puts slice mid under pointer\n const base = rotation;\n let target = base - (((base % 360) + 360) % 360) + want + turns * 360;\n if (target <= base + 360) target += 360;\n // Match duration to entry speed: cubic ease-out opens at 3·delta/duration,\n // so this sets its opening slope ≈ loopSpeed — a smooth bleed, not a jump.\n const matched = (3 * (target - base)) / Math.max(0.05, loopSpeed);\n const duration = Math.min(4200, Math.max(2300, matched));\n animateTo(target, duration, () => { spinning = false; reveal(r); });\n };\n\n // Backend picks the authoritative prize from the AUTHORED segments.\n void this.submitWidgetResponse(campaign, 'spin', { segment_index: provisional }).then((resp) => {\n const prize = (resp && (resp as Record<string, unknown>).prize\n ? (resp as Record<string, unknown>).prize : null) as Record<string, unknown> | null;\n let idx = provisional;\n if (prize && typeof prize.segment_index === 'number') idx = (prize.segment_index as number) % n;\n const label = prize && typeof prize.prize_label === 'string' ? (prize.prize_label as string) : (labels[idx] || '');\n const code = prize && typeof prize.coupon_code === 'string' ? (prize.coupon_code as string) : '';\n const kind = prize && typeof prize.prize_type === 'string'\n ? (prize.prize_type as string).toLowerCase()\n : (shown[idx] && typeof shown[idx].prize_type === 'string' ? String(shown[idx].prize_type).toLowerCase() : '');\n const lost = prize ? String(prize.prize_type || '').toLowerCase() === 'no_prize' : isLossSeg(idx);\n const expiresAt = prize && typeof prize.expires_at === 'string' ? (prize.expires_at as string) : null;\n launch(idx, { label, code, won: !!label && !lost, kind, expiresAt });\n }).catch(() => { /* dead network → the timeout below lands a graceful loss */ });\n\n // Dead-network safety net: keep spinning, then (only after 8s) decelerate\n // to a graceful loss. Crucially it never resolves on a guessed prize\n // mid-flight, so a slow-but-OK response can't be overwritten by a wrong,\n // codeless result (the old 900ms trap). 8s is a tunable dead-halt bound.\n window.setTimeout(() => {\n if (done) return;\n const li = lossIndex();\n launch(li, {\n label: labels[li] || '', code: '', won: false,\n kind: shown[li] && typeof shown[li].prize_type === 'string' ? String(shown[li].prize_type).toLowerCase() : 'no_prize',\n });\n }, 8000);\n };\n\n if (spinMode === 'button' || spinMode === 'both') {\n btn.addEventListener('click', () => beginSpin(0.7));\n } else {\n btn.style.display = 'none';\n }\n\n if (spinMode === 'drag' || spinMode === 'both') {\n // Flick-to-spin: track the pointer's angle around the wheel center; the\n // release angular velocity drives the spin length.\n let dragging = false;\n let startAngle = 0;\n let startRotation = 0;\n let lastA = 0; let lastT = 0; let vel = 0;\n const angleAt = (e: PointerEvent): number => {\n const rect = wheel.getBoundingClientRect();\n const cx = rect.left + rect.width / 2; const cy = rect.top + rect.height / 2;\n return Math.atan2(e.clientY - cy, e.clientX - cx) * 180 / Math.PI;\n };\n wheel.addEventListener('pointerdown', (e: PointerEvent) => {\n if (spinning || resolved) return;\n dragging = true;\n startAngle = angleAt(e); startRotation = rotation;\n lastA = startAngle; lastT = now(); vel = 0;\n wheel.style.cursor = 'grabbing';\n stopIdle();\n try { wheel.setPointerCapture(e.pointerId); } catch { /* noop */ }\n });\n wheel.addEventListener('pointermove', (e: PointerEvent) => {\n if (!dragging) return;\n const a = angleAt(e);\n const prevRot = rotation;\n rotation = startRotation + (a - startAngle);\n spinSpeed = Math.abs(rotation - prevRot);\n wheel.style.transform = `rotate(${rotation}deg)`;\n playTicks(rotation);\n const t = now(); const dt = t - lastT;\n if (dt > 0) {\n let da = a - lastA;\n if (da > 180) da -= 360; if (da < -180) da += 360;\n vel = da / dt; lastA = a; lastT = t;\n }\n });\n const endDrag = (e: PointerEvent): void => {\n if (!dragging) return;\n dragging = false;\n wheel.style.cursor = 'grab';\n try { wheel.releasePointerCapture(e.pointerId); } catch { /* noop */ }\n const speed = Math.abs(vel); // deg/ms\n if (speed > 0.15) beginSpin(speed);\n };\n wheel.addEventListener('pointerup', endDrag);\n wheel.addEventListener('pointercancel', endDrag);\n }\n body.appendChild(result);\n body.appendChild(btn);\n card.appendChild(body);\n target.appendChild(card);\n // If the contact already won this campaign (returning visitor, or a mid-spin\n // dropout after the server granted), reveal that prize via the SAME reveal()\n // path instead of a fresh, re-rollable wheel. reveal() sets resolved=true,\n // which also neutralizes the button/drag triggers wired above.\n if (alreadyWon) {\n stopIdle();\n const wonKind = String(alreadyWon.prize_type || '').toLowerCase();\n reveal({\n label: String(alreadyWon.label || ''),\n code: String(alreadyWon.coupon_code || ''),\n won: wonKind !== 'no_prize',\n kind: wonKind,\n });\n }\n return true;\n }\n\n /**\n * Built-in embedded IMAGE/VIDEO CAROUSEL — a swipeable horizontal strip of\n * `interactive_config.cards` (image OR video + title + body + CTA), contained\n * to the slot (the parent overflow:hidden + this strip's own scroll). Each card\n * reuses the same shape as the In-App carousel editor authors.\n */\n private renderCarouselCardsSlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n target: HTMLElement,\n ): boolean {\n const st = this._slotStyle(campaign);\n const card = this._wrapInSlotCard('aegis-in-app-carousel-card', campaign.id, bg, text, st.radius);\n const cards = Array.isArray(ic.cards) ? (ic.cards as Array<Record<string, unknown>>) : [];\n if (campaign.title) {\n const h = document.createElement('div');\n h.style.cssText = `padding: ${st.padY ?? 12}px ${st.padX ?? 12}px 0; font-size: 13px; font-weight: ${st.weight ?? '700'};`;\n h.textContent = campaign.title;\n card.appendChild(h);\n }\n // Orientation (shared rec_layout vocabulary): 'row' = horizontal slide rail\n // (autoplay-capable) · 'grid' = 2-col · 'list' = vertical stack (both static).\n const layout = (ic.rec_layout as string) || 'row';\n const strip = document.createElement('div');\n const pad = `${st.padY ?? 12}px ${st.padX ?? 12}px`;\n if (layout === 'grid') {\n strip.style.cssText = `display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; padding: ${pad};`;\n } else if (layout === 'list') {\n strip.style.cssText = `display: flex; flex-direction: column; gap: 8px; padding: ${pad};`;\n } else {\n strip.style.cssText = `display: flex; gap: 8px; overflow-x: auto; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; scrollbar-width: none; padding: ${pad};`;\n }\n // Tiles painted by the SHARED card-template interpreter — same path as the\n // rec rail + the overlay carousel. Look authored in compose via card_template\n // (legacy card_style adapted). PER-CARD action: a product-LINKED card (carries\n // a sku/product_id) behaves like the rec rail — CTA → add_to_cart, body click →\n // open_product. An unlinked merchandising card navigates its cta_url. The\n // per-card `cta_action` override still wins over either default.\n const template = resolveGlobalTemplate(ic);\n const ctx = this.buildRenderContext(campaign);\n const accent = this.sanitizeColor((campaign.background_color as string) || text);\n cards.forEach((c, i) => {\n const meta = (c.metadata || {}) as Record<string, unknown>;\n const linked = !!(meta.product_id || meta.sku);\n const tile = renderCardFromTemplate(c as unknown as TemplateCard, {\n template,\n ctx,\n action: linked ? 'cart' : 'navigate',\n layout,\n layoutAccent: accent,\n });\n tile.setAttribute('data-card-index', String(i));\n strip.appendChild(tile);\n });\n card.appendChild(strip);\n target.appendChild(card);\n\n // Autoplay — only meaningful for the horizontal rail (grid/list show all at\n // once). Auto-advance one card at a time when the content actually overflows\n // (mobile: not all cards fit). Pauses on the first user interaction; loops per\n // `ic.loop`. Self-terminates when the strip detaches (no removal event fires).\n const autoplayMs = Number(ic.autoplay_ms) || 0;\n if (layout === 'row' && autoplayMs > 0 && cards.length > 1) {\n const loop = ic.loop !== false;\n const interval = Math.max(autoplayMs, 600); // floor so the smooth-scroll can finish\n let idx = 0;\n let timer: ReturnType<typeof setInterval> | null = null;\n const stop = () => {\n if (timer) {\n clearInterval(timer);\n timer = null;\n }\n };\n timer = setInterval(() => {\n if (!document.body.contains(strip)) {\n stop();\n return;\n }\n // Nothing to slide if everything already fits the viewport.\n if (strip.scrollWidth <= strip.clientWidth + 4) return;\n const tiles = Array.from(strip.children) as HTMLElement[];\n idx += 1;\n if (idx >= tiles.length) {\n if (!loop) {\n stop();\n return;\n }\n idx = 0;\n }\n const el = tiles[idx];\n if (el) {\n // Scroll the STRIP only (never scrollIntoView, which scrolls the page).\n const delta = el.getBoundingClientRect().left - strip.getBoundingClientRect().left;\n strip.scrollTo({ left: strip.scrollLeft + delta, behavior: 'smooth' });\n }\n }, interval);\n (['pointerdown', 'wheel', 'touchstart'] as const).forEach((evt) =>\n strip.addEventListener(evt, stop, { passive: true }),\n );\n }\n return true;\n }\n\n /** Built-in embedded COUNTDOWN — live H:M:S digits that pulse red in the\n * final stretch (<60s) to create urgency, plus the CTA. */\n private renderCountdownSlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n target: HTMLElement,\n ): boolean {\n const st = this._slotStyle(campaign);\n const card = this._wrapInSlotCard('aegis-in-app-countdown-card', campaign.id, bg, text, st.radius);\n const body = document.createElement('div');\n body.style.cssText = `padding: ${st.padY ?? 12}px ${st.padX ?? 12}px; display: flex; flex-direction: column; gap: 8px; align-items: center; text-align: center;`;\n if (campaign.title) {\n const t = document.createElement('div');\n t.style.cssText = `font-size: 13px; font-weight: ${st.weight ?? '700'};`;\n t.textContent = campaign.title;\n body.appendChild(t);\n }\n const label = document.createElement('div');\n label.style.cssText = 'font-size: 11px; opacity: 0.75;';\n label.textContent = (ic.countdown_label as string) || 'Ends in';\n body.appendChild(label);\n\n const digits = document.createElement('div');\n digits.style.cssText = 'display: flex; gap: 6px; justify-content: center;';\n const mkDigit = () => {\n const el = document.createElement('span');\n el.style.cssText = `padding: 6px 9px; border-radius: 8px; font-size: 18px; font-weight: 700; font-family: ui-monospace, monospace; background: ${text}1a; transition: background 0.3s ease, color 0.3s ease;`;\n el.textContent = '00';\n return el;\n };\n const hEl = mkDigit();\n const mEl = mkDigit();\n const sEl = mkDigit();\n [hEl, ':', mEl, ':', sEl].forEach((d) => {\n if (d === ':') {\n const sep = document.createElement('span');\n sep.style.cssText = 'font-size: 18px; font-weight: 700; align-self: center;';\n sep.textContent = ':';\n digits.appendChild(sep);\n } else {\n digits.appendChild(d as HTMLElement);\n }\n });\n body.appendChild(digits);\n\n const targetStr = ic.countdown_target as string | undefined;\n // No authored target (preview) → 10-min demo so it ticks in its NORMAL state\n // (a 10s demo opened already in the final-minute \"urgent\" style).\n const deadline = targetStr ? new Date(targetStr).getTime() : Date.now() + 600_000;\n let urgent = false;\n const tick = () => {\n if (!document.body.contains(card)) return; // stop when removed\n const diff = Math.max(0, deadline - Date.now());\n const h = Math.floor(diff / 3600000);\n const m = Math.floor((diff % 3600000) / 60000);\n const s = Math.floor((diff % 60000) / 1000);\n hEl.textContent = String(h).padStart(2, '0');\n mEl.textContent = String(m).padStart(2, '0');\n sEl.textContent = String(s).padStart(2, '0');\n // Final minute → red accent RING + pulse (readable numerals kept), not a\n // solid-red fill. Once.\n if (diff > 0 && diff <= 60000 && !urgent) {\n urgent = true;\n [hEl, mEl, sEl].forEach((el) => {\n el.style.boxShadow = '0 0 0 2px #ef4444';\n el.style.animation = 'aegisUrgencyPulse 1s ease-in-out infinite';\n });\n }\n if (diff > 0) requestAnimationFrame(tick);\n else if (!expired) {\n expired = true;\n void this.submitWidgetResponse(campaign, 'countdown', { state: 'expired' });\n this._playReaction(card, 'celebrate', 'alarm-clock.json', ic); // time's up → alarm clock\n }\n };\n let expired = false;\n this.addAnimationStyles();\n tick();\n\n if (campaign.body) {\n const desc = document.createElement('div');\n desc.style.cssText = 'font-size: 11px; opacity: 0.8;';\n desc.textContent = campaign.body;\n body.appendChild(desc);\n }\n if (campaign.button_text) {\n const btn = document.createElement('button');\n btn.textContent = campaign.button_text;\n btn.style.cssText = `margin-top: 2px; background: ${text}; color: ${bg}; border: none; padding: 7px 14px; border-radius: ${st.btnRadius ?? 999}px; font-size: 12px; font-weight: 600; cursor: pointer;`;\n btn.addEventListener('click', () => {\n void this.trackEvent(campaign.id, 'clicked');\n void this.submitWidgetResponse(campaign, 'countdown', { state: 'engaged' });\n const url = (campaign as unknown as Record<string, unknown>).action_url as string | undefined;\n if (url) window.open(url, '_blank', 'noopener');\n });\n body.appendChild(btn);\n }\n card.appendChild(body);\n target.appendChild(card);\n return true;\n }\n\n /** Foil palette — a brushed-metal gradient + a contrast \"ink\" (for the\n * SCRATCH-HERE text, sparkle motif, and logo tint) derived from the operator's\n * chosen foil colour (brand primary / custom hex). Unset / invalid → the\n * default silver. Light foils get dark ink, dark foils get white ink, so the\n * prompt stays legible on any colour. */\n private _scratchFoilPalette(hex?: string): { g0: string; g1: string; g2: string; ink: string } {\n return scratchFoilPalette(hex);\n }\n\n /** Built-in embedded SCRATCH CARD — a canvas foil the customer scratches to\n * reveal the server-picked prize, then submits (records + grants). Confetti\n * on reveal. No AegisMessageRuntime callback dependency (works on the bill). */\n private renderScratchCardSlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n target: HTMLElement,\n ): boolean {\n const st = this._slotStyle(campaign);\n const card = this._wrapInSlotCard('aegis-in-app-scratch-card', campaign.id, bg, text, st.radius);\n if (!card.style.position) card.style.position = 'relative';\n\n // ── Config (all default-safe). `scratch_threshold_pct` is the canonical\n // cross-SDK field (render contract); brush/feel knobs mirror the spin wheel.\n const cfgNum = (k: string, d: number): number => (typeof ic[k] === 'number' ? (ic[k] as number) : d);\n const cfgStr = (k: string): string => (typeof ic[k] === 'string' ? (ic[k] as string) : '');\n const threshold = Math.min(1, Math.max(0.1, cfgNum('scratch_threshold_pct', 0.5)));\n const brush = Math.min(60, Math.max(8, cfgNum('scratch_brush_radius', 22)));\n const idleShimmer = ic.scratch_idle_shimmer !== false;\n const winReveal = cfgStr('scratch_win_reveal') || 'inline';\n const hapticsOn = ic.scratch_haptics_enabled !== false;\n let soundOn = ic.scratch_sound_enabled !== false;\n const revealImg = cfgStr('reveal_image_url');\n const foilImg = cfgStr('scratch_foil_image_url');\n // Foil tint — brushed-metal palette + contrast ink derived from the chosen\n // colour (brand primary / custom), or the default silver when unset.\n const foilPal = this._scratchFoilPalette(cfgStr('scratch_foil_color'));\n const pool = Array.isArray(ic.prize_pool) ? (ic.prize_pool as Array<Record<string, unknown>>) : [];\n const isUrl = (s: string): boolean => /^https?:\\/\\//i.test(s);\n // Representative prize for preview/offline (no server response) — the first\n // real prize, so the reveal shows a true sample instead of a bare \"You won!\".\n const sampleWin = pool.find(\n (p) => typeof p.prize_type === 'string' && (p.prize_type as string).toLowerCase() !== 'no_prize',\n ) || null;\n\n // ── Lightweight WebAudio (scratch scrape + win chime) + haptics ──\n let audioCtx: AudioContext | null = null;\n const ensureAudio = (): AudioContext | null => {\n if (!soundOn) return null;\n try {\n if (!audioCtx) {\n const Ctx = window.AudioContext\n || (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n if (!Ctx) return null;\n audioCtx = new Ctx();\n }\n if (audioCtx.state === 'suspended') void audioCtx.resume();\n return audioCtx;\n } catch { return null; }\n };\n let lastScrape = 0;\n const scrapeFx = (): void => {\n const ac = ensureAudio();\n const now = (typeof performance !== 'undefined' ? performance.now() : Date.now());\n if (ac && now - lastScrape > 45) {\n lastScrape = now;\n // Short filtered white-noise burst = a scratchy scrape.\n const dur = 0.05;\n const buf = ac.createBuffer(1, Math.floor(ac.sampleRate * dur), ac.sampleRate);\n const ch = buf.getChannelData(0);\n for (let i = 0; i < ch.length; i++) ch[i] = (Math.sin(i * 12.9898) * 43758.5453 % 1) * 2 - 1;\n const src = ac.createBufferSource(); src.buffer = buf;\n const hp = ac.createBiquadFilter(); hp.type = 'highpass'; hp.frequency.value = 2200;\n const g = ac.createGain(); g.gain.setValueAtTime(0.05, ac.currentTime);\n g.gain.exponentialRampToValueAtTime(0.0001, ac.currentTime + dur);\n src.connect(hp); hp.connect(g); g.connect(ac.destination); src.start();\n }\n if (hapticsOn) { try { (navigator as unknown as { vibrate?: (x: number | number[]) => boolean }).vibrate?.(4); } catch { /* noop */ } }\n };\n const chordFx = (freqs: number[], gain: number): void => {\n const ac = ensureAudio(); if (!ac) return;\n freqs.forEach((f, i) => window.setTimeout(() => {\n const osc = ac.createOscillator(); const g = ac.createGain();\n osc.type = 'sine'; osc.frequency.value = f;\n g.gain.setValueAtTime(gain, ac.currentTime);\n g.gain.exponentialRampToValueAtTime(0.0001, ac.currentTime + 0.22);\n osc.connect(g); g.connect(ac.destination); osc.start(); osc.stop(ac.currentTime + 0.22);\n }, i * 100));\n };\n const winFx = (): void => { chordFx([523, 659, 784, 1047], 0.07); if (hapticsOn) { try { (navigator as unknown as { vibrate?: (x: number | number[]) => boolean }).vibrate?.([0, 35, 25, 60]); } catch { /* noop */ } } };\n const loseFx = (): void => { chordFx([392, 330], 0.05); if (hapticsOn) { try { (navigator as unknown as { vibrate?: (x: number | number[]) => boolean }).vibrate?.(18); } catch { /* noop */ } } };\n\n const body = document.createElement('div');\n body.style.cssText = `padding: ${st.padY ?? 12}px ${st.padX ?? 12}px; display: flex; flex-direction: column; gap: 8px; align-items: center; text-align: center;`;\n if (campaign.title) {\n const t = document.createElement('div');\n t.style.cssText = `font-size: 13px; font-weight: ${st.weight ?? '700'};`;\n t.textContent = campaign.title;\n body.appendChild(t);\n }\n const hint = document.createElement('div');\n hint.style.cssText = 'font-size: 11px; opacity: 0.7;';\n hint.textContent = cfgStr('scratch_hint') || 'Scratch to reveal your prize';\n body.appendChild(hint);\n\n // Mute toggle (only when sound is configured on).\n if (ic.scratch_sound_enabled !== false) {\n const sb = document.createElement('button');\n sb.type = 'button'; sb.setAttribute('aria-label', 'Toggle sound');\n sb.innerHTML = lucideSvg(soundOn ? 'volume-2' : 'volume-x', { size: 15 });\n sb.style.cssText = 'position: absolute; top: 10px; left: 12px; background: transparent; border: none; font-size: 15px; cursor: pointer; opacity: 0.65; z-index: 6; line-height: 1;';\n sb.addEventListener('click', () => { soundOn = !soundOn; sb.innerHTML = lucideSvg(soundOn ? 'volume-2' : 'volume-x', { size: 15 }); });\n card.appendChild(sb);\n }\n\n // Stage holds the prize under a scratch-off foil. Ticket aspect (3:2) reads\n // as a real scratch card, suits the horizontal scratch motion, and shows\n // landscape reveal images without cropping.\n let revealed = false;\n const prizeLayer = document.createElement('div');\n // Reveal background = operator image (cover) if set, else a soft tint. This\n // wires `reveal_image_url`, which was a declared-but-unrendered config field.\n prizeLayer.style.cssText =\n `position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; ` +\n `padding: 12px; text-align: center; font-size: 20px; font-weight: 800; color: ${text}; ` +\n (revealImg ? `background: ${text}0f center/cover no-repeat;` : `background: ${text}0f;`);\n if (revealImg) prizeLayer.style.backgroundImage = `url(\"${revealImg.replace(/\"/g, '%22')}\")`;\n const prizeText = document.createElement('div');\n // Print the prize UNDER the foil so scratching reveals it (like a real\n // scratch card), not a bare \"…\". Uses the representative prize; the reveal\n // confirms the server-picked one (a takeover card slides over on a win).\n const _sIco = sampleWin && typeof sampleWin.icon === 'string' ? (sampleWin.icon as string) : '';\n const _sLbl = sampleWin && typeof sampleWin.label === 'string' ? (sampleWin.label as string) : '';\n prizeText.textContent = _sLbl ? (_sIco && !isUrl(_sIco) ? `${_sIco} ${_sLbl}` : _sLbl) : '🎁'; // eslint-disable-line no-restricted-syntax -- game content (scratch-card prize default icon)\n prizeLayer.appendChild(prizeText);\n\n const wonConfigFor = (resp: Record<string, unknown> | null): Record<string, unknown> | null => {\n const prize = (resp && (resp.prize as Record<string, unknown> | undefined)) || null;\n const idx = prize && typeof prize.segment_index === 'number' ? (prize.segment_index as number) : -1;\n if (idx >= 0 && pool[idx]) return pool[idx];\n const label = this._extractPrizeLabel(resp);\n return pool.find((p) => p.label === label) || sampleWin || null;\n };\n\n // ONE visible celebration per reveal: the takeover card rains its own confetti;\n // the inline reveal plays the gift. (Previously a gift fired on `body` AND the\n // takeover covered it — a hidden/duplicate animation.)\n const showWin = (label: string, code: string, icoVal: string, expiresAt: string | null): void => {\n if (winReveal === 'takeover') {\n const ov = document.createElement('div');\n ov.style.cssText = `position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 22px; text-align: center; background: ${bg}; color: ${text}; border-radius: inherit; animation: aegisScratchPop 0.4s cubic-bezier(0.16,0.86,0.28,1); z-index: 7;`;\n if (icoVal) {\n const ic2 = document.createElement(isUrl(icoVal) ? 'img' : 'div');\n if (isUrl(icoVal)) { (ic2 as HTMLImageElement).src = icoVal; ic2.style.cssText = 'width: 42px; height: 42px; object-fit: contain;'; }\n else { ic2.textContent = icoVal; ic2.style.cssText = 'font-size: 36px; line-height: 1;'; }\n ov.appendChild(ic2);\n }\n const eb = document.createElement('div'); eb.textContent = 'You won'; eb.style.cssText = 'font-size: 12px; font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase; opacity: 0.7;';\n const big = document.createElement('div'); big.textContent = label; big.style.cssText = `font-size: 22px; font-weight: 900; letter-spacing: -0.02em; font-family: 'Urbanist', system-ui, sans-serif;`;\n ov.appendChild(eb); ov.appendChild(big);\n if (code) {\n const chip = document.createElement('button'); chip.type = 'button'; chip.textContent = `${code} ⧉`;\n chip.style.cssText = `margin-top: 4px; padding: 8px 14px; border-radius: 999px; border: 1.5px dashed ${text}66; background: transparent; color: ${text}; font-weight: 800; font-size: 13px; letter-spacing: 1px; cursor: pointer;`;\n chip.addEventListener('click', () => { try { void navigator.clipboard?.writeText(code); chip.textContent = 'Copied'; } catch { /* noop */ } });\n ov.appendChild(chip);\n }\n const validTill = this._validTillLine(expiresAt, text);\n if (validTill) ov.appendChild(validTill);\n card.appendChild(ov);\n try { this._fireConfetti(ov); } catch { /* noop */ }\n } else {\n prizeText.textContent = (icoVal && !isUrl(icoVal)) ? `${icoVal} ${label}` : label;\n const badge = document.createElement('div');\n badge.textContent = 'You won';\n badge.style.cssText = `font-size: 11px; font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase; padding: 2px 10px; border-radius: 999px; background: ${text}14; color: ${text}; animation: aegisScratchPop 0.4s cubic-bezier(0.16,0.86,0.28,1);`;\n body.appendChild(badge);\n if (code) {\n const chip = document.createElement('div');\n chip.style.cssText = `margin-top: 6px; font-family: ui-monospace, monospace; font-size: 12px; font-weight: 700; padding: 4px 8px; border-radius: 6px; background: ${text}14; color: ${text};`;\n chip.textContent = code;\n body.appendChild(chip);\n }\n const validTill = this._validTillLine(expiresAt, text);\n if (validTill) body.appendChild(validTill);\n this._playReaction(body, 'celebrate', 'gift.json', ic);\n }\n };\n\n const dealOnce = createDealOnce(\n // Only a contact the server can BIND (id + its token, C1b) is dealt: without\n // the token the server drops the contact and the card stays single-call.\n () => Boolean(this.contactId && this.contactToken),\n () => this.submitWidgetResponse(campaign, 'scratch', { state: 'dealt' }),\n );\n\n // The engine calls this ONCE when the foil is scratched past the threshold.\n // The deal (below) already stored the outcome for an identified contact; this\n // call reveals it, and its response shape is unchanged.\n const reveal = (): Promise<boolean> => {\n if (revealed) return Promise.resolve(true);\n revealed = true;\n this.addAnimationStyles();\n void this.trackEvent(campaign.id, 'clicked');\n // The reveal never overtakes the deal: the server reveals what the deal stored.\n return Promise.resolve(dealOnce()).then(() => this.submitWidgetResponse(campaign, 'scratch', { state: 'revealed' })).then((resp) => {\n if (!resp) {\n // No answer is not a win: put the foil back so the customer can scratch\n // again, instead of celebrating the sample prize (the card is held\n // server-side and the wallet can still reveal it).\n revealed = false;\n return false;\n }\n // Only a stated win celebrates. A 2xx that says neither (prize: null, reward:\n // null — nothing was drawn) is a loss, never the sample prize.\n const won = scratchOutcome(resp) === true;\n const sampleLabel = sampleWin && typeof sampleWin.label === 'string' ? (sampleWin.label as string) : '';\n const label = this._extractPrizeLabel(resp) || sampleLabel || (ic.scratch_prize_label as string) || 'You won!';\n const code = this._extractCouponCode(resp) || '';\n const wonCfg = wonConfigFor(resp);\n const icoVal = wonCfg && typeof wonCfg.icon === 'string' ? (wonCfg.icon as string) : '';\n const kind = wonCfg && typeof wonCfg.prize_type === 'string' ? (wonCfg.prize_type as string).toLowerCase() : '';\n if (!won) {\n loseFx();\n prizeText.textContent = 'Better luck next time';\n this._playReaction(body, 'empathize', undefined, ic);\n } else {\n winFx();\n showWin(label, code, icoVal, this._extractPrizeExpiry(resp)); // owns the single celebration (confetti or gift)\n // Freebie honesty — recorded for manual fulfilment, not auto-redeemed.\n if (!code && kind === 'freebie' && winReveal !== 'takeover') {\n const h = document.createElement('div');\n h.textContent = '🎟️ Show this screen to claim'; // eslint-disable-line no-restricted-syntax -- game content (scratch-card freebie claim hint)\n h.style.cssText = 'margin-top: 4px; font-size: 12px; font-weight: 600; opacity: 0.85;';\n body.appendChild(h);\n }\n }\n return true;\n });\n };\n\n mountScratchSurface(body, {\n prizeLayer,\n thresholdPct: threshold,\n brushRadius: brush,\n palette: foilPal,\n foilImageUrl: foilImg || undefined,\n idleShimmer,\n onScratch: scrapeFx,\n onThreshold: reveal,\n });\n\n // DEAL (Model B): an identified contact's outcome is chosen and stored\n // server-side the moment the card renders, before any scratch, so the card can\n // be scratched later from the wallet. The reply carries no prize. Anonymous\n // cards keep the single-call grant-at-threshold path.\n dealOnce();\n\n card.appendChild(body);\n target.appendChild(card);\n return true;\n }\n\n /** Extract a human prize label from a spin/scratch server response. */\n private _extractPrizeLabel(resp: Record<string, unknown> | null): string | null {\n if (!resp) return null;\n const reward = resp.reward as Record<string, unknown> | undefined;\n const cands = [\n resp.prize_label, resp.label, reward?.label,\n (resp.prize as Record<string, unknown> | undefined)?.prize_label,\n ];\n for (const c of cands) if (typeof c === 'string' && c) return c;\n return null;\n }\n\n /** Extract the granted prize's expiry (ISO) from a spin/scratch server\n * response — read back from the grant's ledger row by the server. */\n private _extractPrizeExpiry(resp: Record<string, unknown> | null): string | null {\n if (!resp) return null;\n const reward = resp.reward as Record<string, unknown> | undefined;\n const cands = [(resp.prize as Record<string, unknown> | undefined)?.expires_at, reward?.expires_at];\n for (const c of cands) if (typeof c === 'string' && c) return c;\n return null;\n }\n\n /** \"Valid till 12 Oct\" for a win screen. No expiry, or one that does not\n * parse, renders NOTHING — never \"no expiry\". */\n private _validTillLine(expiresAt: string | null | undefined, color: string): HTMLElement | null {\n if (!expiresAt) return null;\n const d = new Date(expiresAt);\n if (Number.isNaN(d.getTime())) return null;\n const opts: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'short' };\n if (d.getFullYear() !== new Date().getFullYear()) opts.year = 'numeric';\n const line = document.createElement('div');\n line.textContent = `Valid till ${d.toLocaleDateString('en-IN', opts)}`;\n line.style.cssText = `margin-top: 2px; font-size: 12px; font-weight: 600; opacity: 0.75; color: ${color};`;\n return line;\n }\n\n /** Extract a redeemable coupon code from a spin/scratch server response. */\n private _extractCouponCode(resp: Record<string, unknown> | null): string | null {\n if (!resp) return null;\n const reward = resp.reward as Record<string, unknown> | undefined;\n const cands = [resp.coupon_code, reward?.coupon_code];\n for (const c of cands) if (typeof c === 'string' && c) return c;\n return null;\n }\n\n // --- Shared body builders (used by both overlay + slot paths) ----------\n //\n // Sizing: `'overlay'` matches the legacy fullscreen-modal scale (24px\n // padding, 18px title, 32px stars). `'slot'` is the embedded card\n // scale (20px padding, 16px title, 28px stars) — proportional, doesn't\n // dwarf merchant page chrome. Adding a new variant means a new tuple\n // here, not a new renderer.\n\n /**\n * Slot styling — the operator's look-&-feel knobs (Style panel) applied to the\n * EMBEDDED render, CLAMPED to boundary-safe ranges so an authored value can\n * never break out of the dynamic block. Unauthored knobs are `undefined` so\n * each renderer keeps its own compact default (no visual regression). Colors\n * default to the current slot defaults. The slot card keeps `width:100%;\n * overflow:hidden` regardless, so the boundary guarantee is independent of\n * these values.\n */\n private _slotStyle(campaign: InAppCampaign): {\n bg: string; text: string; radius: number;\n padX?: number; padY?: number; btnRadius?: number; weight?: string;\n } {\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const clampOpt = (v: unknown, lo: number, hi: number): number | undefined => {\n if (v === undefined || v === null || v === '') return undefined;\n const n = Number(v);\n if (!Number.isFinite(n)) return undefined;\n return Math.max(lo, Math.min(hi, n));\n };\n const wRaw = ic.heading_font_weight != null ? String(ic.heading_font_weight) : '';\n const pal = this._surfacePalette(campaign);\n return {\n bg: pal.bg,\n text: pal.text,\n radius: clampOpt(ic.border_radius, 0, 24) ?? 12,\n padX: clampOpt(ic.padding_x, 0, 32),\n padY: clampOpt(ic.padding_y, 0, 24),\n btnRadius: clampOpt(ic.button_radius, 0, 999),\n weight: ['400', '500', '600', '700'].includes(wRaw) ? wRaw : undefined,\n };\n }\n\n private _wrapInSlotCard(\n className: string,\n campaignId: string,\n bg: string,\n text: string,\n radius = 12,\n ): HTMLElement {\n const card = document.createElement('div');\n card.className = className;\n card.setAttribute('data-campaign-id', campaignId);\n // width:100% + overflow:hidden = the boundary guarantee; the authored radius\n // is clamped (0–24) by `_slotStyle` so it can't distort the slot.\n card.style.cssText = `\n width: 100%; border-radius: ${radius}px; overflow: hidden;\n background: ${bg}; color: ${text};\n box-shadow: 0 4px 12px rgba(0,0,0,0.06);\n `;\n return card;\n }\n\n /**\n * Flush a rendered slot card into its host card — drop the per-campaign radius\n * + shadow so it reads as one unit with the host wrapper (used for the\n * storefront value bar via `data-aegis-slot-flush`). Targets every\n * `[data-campaign-id]` card inside `container` (or the container itself).\n */\n private _flattenSlotCard(container: HTMLElement): void {\n const cards = container.matches('[data-campaign-id]')\n ? [container]\n : Array.from(container.querySelectorAll<HTMLElement>('[data-campaign-id]'));\n for (const el of cards) {\n el.style.borderRadius = '0';\n el.style.boxShadow = 'none';\n }\n }\n\n /**\n * Evaluate the currently armed campaigns against a client-side event\n * and render any that match their `client_trigger`.\n *\n * Called by the host app (e.g., the EcommerceTracker on product_viewed),\n * or by future TriggerEngine bridges. Safe to call repeatedly for the\n * same event — dedup happens via `displayedCampaigns`.\n *\n * Supported trigger types (align with the cell-plane server-side\n * `display_rules.trigger_type`):\n * - `custom_event` : fire when eventName matches config.event\n * - `product_match` : fire on `product_viewed` when eventData.product_id\n * matches config.product_id (string or string[])\n * - `delay` : client-side setTimeout — evaluated at armeng\n * time (kicked off from `displayCampaign`), not here.\n * - `exit_intent` / `scroll_depth` / `time_on_page` / `inactivity` : the\n * TriggerEngine event named by `behaviouralTriggerEvent`;\n * AegisMessageRuntime arms the engine and forwards it.\n */\n onClientEvent(eventName: string, eventData: Record<string, unknown> = {}): void {\n for (const c of this.campaigns) {\n if (this.displayedCampaigns.has(c.id)) continue;\n if (!c.client_trigger) continue;\n // Conversion-aware suppression: trigger-gated campaigns silenced by\n // a prior notifyConversion() call are skipped until expiry.\n if (this.isSuppressed(c.id)) continue;\n if (this.matchesClientTrigger(c.client_trigger, eventName, eventData)) {\n this.displayCampaign(c);\n }\n }\n }\n\n private matchesClientTrigger(\n trigger: { type: string; config?: Record<string, unknown> },\n eventName: string,\n eventData: Record<string, unknown>,\n ): boolean {\n const cfg = trigger.config || {};\n switch (trigger.type) {\n case 'custom_event':\n return typeof cfg.event === 'string' && cfg.event === eventName;\n case 'product_match': {\n if (eventName !== 'product_viewed' && eventName !== 'product_view') {\n return false;\n }\n const wantedRaw = cfg.product_id;\n const wanted: string[] = Array.isArray(wantedRaw)\n ? (wantedRaw as string[])\n : typeof wantedRaw === 'string'\n ? [wantedRaw]\n : [];\n if (wanted.length === 0) return false;\n const actual = String(\n eventData.product_id ??\n eventData.productId ??\n (eventData.product as Record<string, unknown> | undefined)?.id ??\n '',\n );\n return wanted.includes(actual);\n }\n default: {\n // Behavioural triggers (exit intent / scroll depth / time on page /\n // inactivity) fire on the TriggerEngine event the runtime arms them on.\n // Unknown or server-evaluated — do not auto-fire from the client.\n const behavioural = behaviouralTriggerEvent(trigger);\n return behavioural !== null && behavioural === eventName;\n }\n }\n }\n \n private displayCampaign(campaign: InAppCampaign): void {\n // Embedded-only guard: a campaign whose delivery_modes is set and does NOT\n // include 'in_app_overlay' is a SLOT widget (hero / recommendation rail /\n // value bar / featured) — it renders via renderIntoSlots, never as a\n // centered overlay popup. Without this, an embedded-only campaign with no\n // client_trigger gets auto-picked by tryDisplayNextCampaign and pops up on\n // every load (the duplicate hero-carousel popup). Empty/missing\n // delivery_modes keeps the legacy auto-overlay behaviour.\n if (\n Array.isArray(campaign.delivery_modes) &&\n campaign.delivery_modes.length > 0 &&\n !campaign.delivery_modes.includes('in_app_overlay')\n ) {\n return;\n }\n // Lifecycle: `campaign-will-show` — CANCELLABLE. Host returns `false` to\n // suppress render (e.g. route-aware blocking, host-side frequency caps,\n // self-handled mode). When suppressed we DO NOT mark the campaign as\n // displayed so a subsequent `tryDisplayNextCampaign` (after the host\n // unblocks) can re-evaluate it.\n const proceed = this.emit('campaign-will-show', campaign);\n if (!proceed) {\n this.log(`campaign ${campaign.id} suppressed by campaign-will-show handler`);\n return;\n }\n\n this.displayedCampaigns.add(campaign.id);\n\n // Canonical display-size — resolve once for this campaign; createOverlay +\n // renderModal read it so every bounded overlay format scales uniformly.\n this._displaySizeMul = this._sizeMul(\n campaign.interactive_config as Record<string, unknown> | undefined,\n );\n\n // P6.full — flat surveys get a synthesised card_template so the composed-card\n // gates below route them through the unified engine (not the bespoke renderers).\n this._ensureSurveyTemplate(campaign);\n\n // Sub-type routing: interactive types are handled by dedicated renderers\n const interactiveSubTypes = new Set([\n 'spin_wheel', 'scratch_card', 'nps_survey', 'quiz',\n 'countdown_offer', 'star_rating', 'quick_poll',\n // SDK Demo v4 (W1b/W3): multi-step forms + push primers are in-app\n // render types (Plotline/CleverTap/WebEngage standard), NOT widgets.\n 'multi_step_form', 'push_primer',\n // Games / gamification (the 11 locked mechanics) — interactive widgets.\n ...GAME_SUB_TYPES,\n ]);\n\n if (campaign.sub_type && interactiveSubTypes.has(campaign.sub_type)) {\n this.renderInteractive(campaign);\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n }\n\n // Native review wall as a slide-up overlay (Reviews Display & Integrity\n // §3.5, mockup C) — for a reviews campaign whose delivery_modes include\n // 'in_app_overlay' (the embedded-only guard above already returned for the\n // PDP/standalone embedded_card reviews campaigns).\n if (campaign.sub_type === 'reviews') {\n renderReviewsOverlay(this.buildRenderContext(campaign), {\n apiHost: this.apiHost,\n writeKey: this.writeKey,\n });\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n }\n\n // C2 — composed card body: any bounded display campaign (modal / banner /\n // full_screen / half_interstitial) carrying a top-level `card_template`\n // renders its body through the generic template engine in an overlay, instead\n // of the flat title/body/image path. Mirrors the slot path's composed-card\n // branch (renderComposedCardSlot). Back-compat: only fires when a template is\n // present, so every existing flat campaign falls through to its per-type\n // renderer untouched. Multi-card surfaces keep their own renderers.\n // See IN_APP_FORMAT_LOCK_AND_COMPOSABLE_CARDS_TRACKER.md (Part B / C2).\n {\n const ic2 = (campaign.interactive_config || {}) as Record<string, unknown>;\n const multiCard2 =\n campaign.sub_type === 'product_recommendation' || campaign.sub_type === 'carousel_cards';\n const overlayCardType =\n campaign.type === 'modal' ||\n campaign.type === 'banner' ||\n campaign.type === 'full_screen' ||\n campaign.type === 'half_interstitial';\n if (this._hasComposedTemplate(ic2) && !multiCard2 && overlayCardType) {\n this.renderComposedCardOverlay(campaign, ic2);\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n }\n }\n\n switch (campaign.type) {\n case 'custom_html':\n this.renderCustomHtml(campaign);\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'modal':\n this.renderModal(campaign);\n break;\n case 'banner':\n this.renderBanner(campaign);\n break;\n case 'full_screen':\n this.renderFullScreen(campaign);\n break;\n case 'half_interstitial':\n this.renderHalfInterstitial(campaign);\n break;\n case 'alert':\n this.renderAlert(campaign);\n break;\n case 'pip':\n this.renderPIP(campaign);\n break;\n case 'tooltip':\n this.renderTooltip(campaign);\n break;\n // Preload-first display types (2026-04-22). These renderers own their\n // own impression tracking so we don't double-count alongside the\n // trailing `this.trackEvent(... 'impression')` below — return early.\n case 'carousel_cards':\n renderCarouselCards(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'sticky_bar':\n renderStickyBar(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'progress_bar':\n renderProgressBar(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'coachmark_tour':\n // coachmark_tour fires its own impression/dismiss/click events\n // from within its state machine — don't double-report here.\n renderCoachmarkTour(this.buildRenderContext(campaign));\n this.emit('campaign-shown', campaign);\n return;\n case 'product_recommendation':\n renderProductRecommendation(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'stories':\n // Stories fires its own per-frame impressions; the campaign-level\n // impression is the trailing trackEvent below — keep it (don't return\n // early like the self-tracking coachmark) so served→impression holds.\n renderStories(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'video':\n renderVideo(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'active_web_chat':\n // Persistent chat launcher backed by the AegisChat engine. The bubble\n // is the impression; session_start/session_end/message_sent flow as\n // campaign events from the engine's lifecycle callbacks. SSE is opened\n // only when the customer opens the panel (panel-gated inside AegisChat).\n this.renderActiveWebChatLauncher(campaign);\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n }\n\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n }\n\n /** The single chat launcher on the page (Active Web Chat). Persistent — not\n * re-mounted on SSE/poll refresh; torn down on manager teardown. */\n private activeWebChat?: AegisChat;\n\n /**\n * Mount the Active Web Chat launcher. Idempotent: a repeat delivery of the\n * same (or another) chat campaign is a no-op once one launcher is live — at\n * most one bubble per page, and an open session is never yanked out from\n * under the customer.\n */\n private renderActiveWebChatLauncher(campaign: InAppCampaign): void {\n if (this.activeWebChat) return;\n const chat = renderActiveWebChat(this.buildRenderContext(campaign), {\n writeKey: this.writeKey,\n apiHost: this.apiHost,\n contactId: this.contactId,\n channel: 'web',\n });\n if (chat) this.activeWebChat = chat;\n }\n\n /**\n * Build the shared context passed into the preload-first renderers.\n * Matches the interface in `./renderers/types.ts`. Kept as a private\n * method (rather than inlined at each call-site) so future renderer\n * additions stay consistent and easy to audit.\n */\n private buildRenderContext(campaign: InAppCampaign): RenderContext {\n return {\n campaign,\n getCartState: this.getCartState,\n properties: () => {\n // Flat, fail-soft token map for `card_template` text interpolation.\n // Live cart + any server-hydrated context the campaign carries\n // (`interactive_config.card_context`, e.g. cart.free_delivery_above,\n // loyalty.points, first_name). Client-side interpolation keeps the\n // cached bundle generic (see RICH_CARD_EDITOR_IMPLEMENTATION.md I.A).\n const cart = this.getCartState?.();\n const ctxObj =\n ((campaign.interactive_config as Record<string, unknown> | undefined)\n ?.card_context as Record<string, unknown>) || {};\n return {\n 'cart.total': cart?.total,\n 'cart.count': cart?.itemCount,\n ...ctxObj,\n };\n },\n trackEvent: (id, evt, extra) => {\n void this.trackEvent(id, evt, extra);\n },\n sanitizeUrl: (url: string) => this.sanitizeUrl(url),\n navigate: (url: string, buttonId?: string) =>\n this.navigateToCampaignAction(campaign, url, buttonId),\n dispatchAction: (action: AegisAction) => this.dispatchAction(action, campaign.id),\n submitResponse: (responseType: string, payload: Record<string, unknown>) =>\n this.submitWidgetResponse(campaign, responseType, payload),\n playReaction: (el, intent, lottieFile, scope) =>\n this._playReaction(el, intent, lottieFile, campaign.interactive_config as Record<string, unknown> | undefined, scope),\n haptic: (pattern) => {\n try {\n if (typeof navigator !== 'undefined' && typeof navigator.vibrate === 'function') {\n navigator.vibrate(pattern);\n }\n } catch { /* unsupported — no-op */ }\n },\n sanitizeColor: (color: string) => this.sanitizeColor(color),\n log: (msg: string, level?: 'log' | 'warn' | 'error') => this.log(msg, level),\n addAnimationStyles: () => this.addAnimationStyles(),\n apiHost: this.apiHost,\n writeKey: this.writeKey,\n organizationId: this.organizationId,\n workspaceId: this.getWorkspaceId?.(),\n };\n }\n\n /**\n * Renders interactive sub-type campaigns (spin wheel, NPS, quiz, etc.)\n * using DOM-safe rendering. These sub-types use the campaign's\n * interactive_config payload for type-specific behavior.\n */\n private renderInteractive(campaign: InAppCampaign): void {\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const { bg, text } = this._surfacePalette(campaign);\n\n // Games / gamification (the 11 locked mechanics) render via the shared\n // games module (centred modal scaffold + per-game body).\n if (campaign.sub_type && renderGame(this.buildRenderContext(campaign), campaign.sub_type)) {\n return;\n }\n\n // P6.full render-cutover (INTERACTIVE_WIDGET_UNIFICATION.md §15) — single-card\n // surveys (nps / star_rating / quick_poll) ALWAYS render through the ONE\n // card-template engine: `_ensureSurveyTemplate` (in displayCampaign) has\n // synthesised a card_template from the flat config, so the gate below routes\n // them to renderComposedCardOverlay → renderCardFromTemplate + the shared\n // element builders (which carry the ported band-conditional reason-capture /\n // thank-you flows). The bespoke nps/rating/poll renderers are RETIRED (P6.full\n // legacy sweep). Multi-step flows (quiz / multi_step_form) are EXCLUDED — a\n // single card can't express a step sequence (Class B flow engine).\n {\n const isMultiStepFlow =\n campaign.sub_type === 'quiz' || campaign.sub_type === 'multi_step_form';\n if (this._hasComposedTemplate(ic) && !isMultiStepFlow) {\n this.renderComposedCardOverlay(campaign, ic);\n return;\n }\n }\n\n switch (campaign.sub_type) {\n case 'countdown_offer':\n this.renderCountdownOffer(campaign, ic, bg, text);\n break;\n case 'quiz':\n this.renderQuiz(campaign, ic, bg, text);\n break;\n case 'multi_step_form':\n this.renderMultiStepForm(campaign, ic, bg, text);\n break;\n case 'push_primer':\n this.renderPushPrimer(campaign, ic, bg, text);\n break;\n case 'spin_wheel':\n case 'scratch_card':\n // ONE engine (R2, Aman 2026-09-28): every host renders games here, so a\n // turn posts /in-app/responses and the server picks + mints the prize\n // (RewardGrantService). The runtime used to hand these to the legacy\n // widget renderer, whose prize endpoint read a table that never existed.\n this.renderGamificationOverlay(campaign, ic, bg, text);\n break;\n default:\n this.log(`Unknown interactive sub_type: ${campaign.sub_type}`, 'warn');\n this.renderModal(campaign);\n }\n }\n\n /**\n * Built-in overlay for spin_wheel / scratch_card when no host runtime is\n * wired (dashboard preview, runtime-less hosts). Wraps the same interactive\n * slot renderers (`renderSpinWheelSlot` / `renderScratchCardSlot`) in a modal\n * so the gamified widget is fully playable — not a dead frame.\n */\n /** Display-size multiplier — operator control over how big a bounded overlay\n * renders (small 0.82 / medium 1 / large 1.18). Default medium = prior size.\n * Read from interactive_config.display_size; mirrors the schema enum + the\n * dashboard preview harness so the operator's choice is honored identically. */\n private _sizeMul(ic: Record<string, unknown> | undefined | null): number {\n const s = ic && typeof ic.display_size === 'string' ? (ic.display_size as string) : 'medium';\n return s === 'small' ? 0.82 : s === 'large' ? 1.18 : 1;\n }\n\n private renderGamificationOverlay(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n ): void {\n const overlay = this.createOverlay(`aegis-in-app-${campaign.sub_type}-overlay`);\n const modal = document.createElement('div');\n modal.style.cssText = `\n position: relative; max-width: 340px; width: 90%; border-radius: 20px;\n overflow: hidden; background: ${bg}; color: ${text};\n animation: aegisScaleIn 0.3s ease; box-shadow: 0 16px 48px rgba(0,0,0,0.22);\n `;\n if (campaign.sub_type === 'scratch_card') {\n this.renderScratchCardSlot(campaign, ic, bg, text, modal);\n } else {\n this.renderSpinWheelSlot(campaign, ic, bg, text, modal);\n }\n this._addCornerClose(modal, overlay, campaign.id, text);\n // Size control — scale the ENTIRE card uniformly (small 0.82 / medium 1 /\n // large 1.18) so the look & feel and aspect ratio are identical at every\n // size, just bigger/smaller. A wrapper holds the static scale so the modal's\n // own scale-in entry animation stays independent (no conflict).\n const mul = this._sizeMul(ic);\n const sizer = document.createElement('div');\n sizer.style.cssText = `display: flex; justify-content: center; align-items: center; width: 100%; transform: scale(${mul}); transform-origin: center;`;\n sizer.appendChild(modal);\n overlay.appendChild(sizer);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n /** A circular ✕ close button in the modal's top-right corner. */\n private _addCornerClose(\n modal: HTMLElement,\n overlay: HTMLElement,\n campaignId: string,\n text: string,\n ): void {\n const x = document.createElement('button');\n x.setAttribute('aria-label', 'Close');\n const closeIcon = svgNode(\n '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" ' +\n 'stroke-width=\"2.5\" stroke-linecap=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>' +\n '<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>',\n );\n if (closeIcon) x.appendChild(closeIcon); else x.innerHTML = lucideSvg('x', { size: 14 });\n x.style.cssText = `\n position: absolute; top: 12px; right: 12px; z-index: 4; width: 28px; height: 28px;\n display: flex; align-items: center; justify-content: center; border: none;\n border-radius: 50%; cursor: pointer; color: ${text};\n background: ${text}1f; backdrop-filter: blur(4px); transition: background 0.15s, transform 0.15s;\n `;\n x.addEventListener('mouseenter', () => { x.style.background = `${text}33`; x.style.transform = 'scale(1.08)'; });\n x.addEventListener('mouseleave', () => { x.style.background = `${text}1f`; x.style.transform = 'scale(1)'; });\n x.addEventListener('click', () => {\n this.trackEvent(campaignId, 'dismissed');\n this.removeModal(overlay);\n });\n modal.appendChild(x);\n }\n\n /**\n * Custom HTML type — render operator-authored HTML in a SANDBOXED iframe.\n * The frame runs with `sandbox=\"allow-scripts\"` and NO `allow-same-origin`, so\n * it's an opaque/null origin: no access to the host DOM, cookies, localStorage,\n * the SDK write-key, or same-origin creds. The only channel out is a validated\n * `postMessage` bridge (close / track / cta / resize), exposed to authors as a\n * tiny injected `aegis` shim. See IN_APP_CUSTOM_HTML_TRACKER.md (security model).\n */\n private renderCustomHtml(campaign: InAppCampaign): void {\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const overlay = this.createOverlay('aegis-in-app-custom-html-overlay');\n const modal = document.createElement('div');\n modal.style.cssText =\n 'position: relative; max-width: 420px; width: 92%; border-radius: 16px; ' +\n 'overflow: hidden; background: #ffffff; box-shadow: 0 16px 48px rgba(0,0,0,0.22); ' +\n 'animation: aegisScaleIn 0.3s ease;';\n modal.appendChild(this._buildCustomHtmlFrame(campaign, ic, overlay));\n this._addCornerClose(modal, overlay, campaign.id, '#111827');\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n /** Build the sandboxed iframe + the validated message bridge. Reused by the\n * overlay + embedded-slot custom-HTML renderers. */\n private _buildCustomHtmlFrame(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n overlay: HTMLElement,\n ): HTMLIFrameElement {\n const html = typeof ic.html === 'string' ? (ic.html as string) : '';\n const height = typeof ic.html_height === 'number' ? (ic.html_height as number) : 360;\n // Expose the brand palette as CSS variables so authored HTML can be on-brand\n // (var(--aegis-primary) etc.) without hardcoding hexes per campaign.\n const bst = this.inAppStyle(campaign);\n const brandVars =\n `:root{--aegis-bg:${bst.bg};--aegis-text:${bst.text};` +\n `--aegis-primary:${bst.btnBg};--aegis-on-primary:${bst.btnText};}`;\n const iframe = document.createElement('iframe');\n // allow-scripts WITHOUT allow-same-origin → opaque origin, fully isolated.\n iframe.setAttribute('sandbox', 'allow-scripts');\n iframe.setAttribute('referrerpolicy', 'no-referrer');\n // `transform: translateZ(0)` forces the iframe onto its own compositing\n // layer — nested iframes otherwise fail to paint when an ancestor is\n // CSS-scaled (the small editor preview uses `transform: scale()`), which is\n // why custom HTML showed full-screen but not in the small frame.\n iframe.style.cssText =\n `width: 100%; height: ${height}px; border: none; display: block; background: #fff; ` +\n `transform: translateZ(0); backface-visibility: hidden;`;\n iframe.title = campaign.title || 'In-app content';\n\n // Injected author shim — `aegis.close()/track()/cta()/resize()` post tagged\n // messages so authors never hand-write postMessage.\n const shim =\n \"<script>window.aegis={\" +\n \"close:function(){parent.postMessage({__aegis:'close'},'*')},\" +\n \"track:function(e,p){parent.postMessage({__aegis:'track',event:e,payload:p},'*')},\" +\n \"cta:function(u){parent.postMessage({__aegis:'cta',url:u},'*')},\" +\n \"resize:function(h){parent.postMessage({__aegis:'resize',height:h},'*')}\" +\n \"};<\\/script>\";\n iframe.srcdoc =\n '<!DOCTYPE html><html><head><meta charset=\"utf-8\">' +\n '<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">' +\n '<style>' + brandVars + 'html,body{margin:0;padding:0;font-family:system-ui,-apple-system,sans-serif;}</style>' +\n shim + '</head><body>' + html + '</body></html>';\n\n // Bridge — only accept messages from THIS frame, only the tagged allow-list.\n const onMsg = (e: MessageEvent) => {\n if (e.source !== iframe.contentWindow) return;\n const d = e.data as { __aegis?: string; event?: unknown; url?: unknown; height?: unknown };\n if (!d || typeof d.__aegis !== 'string') return;\n switch (d.__aegis) {\n case 'close':\n window.removeEventListener('message', onMsg);\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n break;\n case 'track':\n // Author-defined events record as a click (the custom name rides metadata).\n this.trackEvent(campaign.id, 'clicked');\n break;\n case 'cta': {\n this.trackEvent(campaign.id, 'clicked');\n const safe = typeof d.url === 'string' ? this.sanitizeUrl(d.url) : null;\n if (safe) {\n const ev = new CustomEvent('aegis:campaign-click', {\n detail: { campaign, url: safe }, cancelable: true,\n });\n const notPrevented = window.dispatchEvent(ev);\n if (notPrevented) window.open(safe, '_blank', 'noopener');\n }\n break;\n }\n case 'resize':\n if (typeof d.height === 'number' && d.height >= 40 && d.height <= 2000) {\n iframe.style.height = `${d.height}px`;\n }\n break;\n }\n };\n window.addEventListener('message', onMsg);\n return iframe;\n }\n\n /** Embedded-slot custom HTML (e.g. on the bill) — the same sandboxed frame in\n * a slot card instead of an overlay. */\n private renderCustomHtmlSlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n target: HTMLElement,\n ): boolean {\n const st = this._slotStyle(campaign);\n const card = this._wrapInSlotCard('aegis-in-app-custom-html-card', campaign.id, bg, text, st.radius);\n card.appendChild(this._buildCustomHtmlFrame(campaign, ic, card));\n target.appendChild(card);\n return true;\n }\n\n // --- Interactive Renderers ---\n\n private renderCountdownOffer(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-countdown-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 320px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 16px 48px rgba(0,0,0,0.22);\n `;\n\n const body = document.createElement('div');\n body.style.cssText = 'padding: 24px; text-align: center;';\n\n const title = document.createElement('div');\n title.style.cssText = 'font-size: 20px; font-weight: 700; margin-bottom: 8px;';\n title.textContent = campaign.title || 'Flash Sale';\n body.appendChild(title);\n\n const label = document.createElement('div');\n label.style.cssText = 'font-size: 13px; opacity: 0.8; margin-bottom: 12px;';\n label.textContent = (ic.countdown_label as string) || 'Sale ends in:';\n body.appendChild(label);\n\n // Start the countdown. With an authored `countdown_target` we run to it;\n // with none (editor preview) a short demo so the timer is visibly ticking.\n const targetStr = ic.countdown_target as string | undefined;\n // No authored target (e.g. editor preview) → a 10-minute demo so the timer\n // is visibly ticking in its NORMAL state (a 10s demo would open already in\n // the final-minute \"urgent\" style, which read as broken).\n const target = targetStr ? new Date(targetStr).getTime() : Date.now() + 600_000;\n\n // Labelled, boxed units (Days/Hrs/Min/Sec) — the standard countdown look\n // (was a bare \"00:00:00\" with no captions + no days, so a multi-day sale\n // showed huge hour counts). Days appear only when the sale is >24h out.\n const includeDays = (target - Date.now()) >= 86_400_000;\n const UNITS: Array<['d' | 'h' | 'm' | 's', string]> = includeDays\n ? [['d', 'Days'], ['h', 'Hrs'], ['m', 'Min'], ['s', 'Sec']]\n : [['h', 'Hrs'], ['m', 'Min'], ['s', 'Sec']];\n const cells: Partial<Record<'d' | 'h' | 'm' | 's', HTMLElement>> = {};\n const digits = document.createElement('div');\n digits.style.cssText = 'display: flex; gap: 10px; justify-content: center; margin-bottom: 16px;';\n for (const [key, cap] of UNITS) {\n const group = document.createElement('div');\n group.style.cssText = 'display: flex; flex-direction: column; align-items: center; gap: 4px;';\n const num = document.createElement('div');\n num.style.cssText = `min-width: 46px; padding: 8px 6px; border-radius: 8px; font-size: 24px; font-weight: 700; font-family: monospace; background: ${text}22; color: ${text}; transition: background 0.2s, color 0.2s;`;\n num.textContent = '00';\n const lbl = document.createElement('div');\n lbl.style.cssText = 'font-size: 10px; opacity: 0.6; text-transform: uppercase; letter-spacing: 0.5px;';\n lbl.textContent = cap;\n group.appendChild(num);\n group.appendChild(lbl);\n cells[key] = num;\n digits.appendChild(group);\n }\n body.appendChild(digits);\n\n const pad = (n: number) => String(n).padStart(2, '0');\n let expired = false;\n let urgent = false;\n const update = () => {\n const diff = Math.max(0, target - Date.now());\n const d = Math.floor(diff / 86_400_000);\n const h = Math.floor((diff % 86_400_000) / 3_600_000);\n const m = Math.floor((diff % 3_600_000) / 60_000);\n const s = Math.floor((diff % 60_000) / 1000);\n if (cells.d) cells.d.textContent = pad(d);\n if (cells.h) cells.h.textContent = pad(includeDays ? h : Math.floor(diff / 3_600_000));\n if (cells.m) cells.m.textContent = pad(m);\n if (cells.s) cells.s.textContent = pad(s);\n // Final-minute urgency: keep the numerals their READABLE colour and signal\n // urgency with a red accent RING + a gentle breathing pulse — NOT red text\n // on a red tint (muddy/unreadable) nor a harsh solid-red fill. Applied once.\n if (!urgent && diff > 0 && diff <= 60000) {\n urgent = true;\n Object.values(cells).forEach((el) => {\n if (el) { el.style.boxShadow = '0 0 0 2px #ef4444'; }\n });\n digits.style.animation = 'aegisCountdownPulse 1.1s ease-in-out infinite';\n }\n if (diff > 0) {\n requestAnimationFrame(update);\n } else if (!expired) {\n expired = true;\n // Time's up — ring the alarm clock.\n void this.submitWidgetResponse(campaign, 'countdown', { state: 'expired' });\n this._playReaction(body, 'celebrate', 'alarm-clock.json', ic); // time's up → alarm clock\n }\n };\n update();\n\n if (campaign.body) {\n const desc = document.createElement('div');\n desc.style.cssText = 'font-size: 14px; opacity: 0.85; margin-bottom: 16px;';\n desc.textContent = campaign.body;\n body.appendChild(desc);\n }\n\n if (campaign.button_text) {\n const btn = this.createCTAButton(campaign, bg, text);\n body.appendChild(btn);\n }\n\n this.addCloseButton(body, overlay, campaign.id);\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n /**\n * Multi-step form (SDK Demo v4 W1b) — in-app render type matching the\n * Plotline multi-step flow pattern: a progress bar + per-step question with\n * options, Back/Next navigation, Submit on the last step. `ic.steps` is an\n * array of `{ question, options[] }`. DOM-safe (no innerHTML).\n */\n private renderMultiStepForm(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-multistep-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 360px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 16px 48px rgba(0,0,0,0.22);\n `;\n\n // §7.13.I — a profile FORM (field-map) renders as input fields + submit (the\n // SAME body the embedded slot uses), not the question/options stepper.\n const formFields = Array.isArray(ic.form_fields)\n ? (ic.form_fields as Array<Record<string, unknown>>)\n : null;\n if (formFields && formFields.length > 0) {\n const formBody = this._buildFormBody(campaign, formFields, bg, text);\n this.addCloseButton(formBody, overlay, campaign.id);\n modal.appendChild(formBody);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n return;\n }\n\n const body = document.createElement('div');\n body.style.cssText = 'padding: 24px;';\n\n const rawSteps = Array.isArray(ic.steps)\n ? (ic.steps as Array<Record<string, unknown>>)\n : [];\n const steps = rawSteps.length > 0\n ? rawSteps\n : [{ question: campaign.title || 'Tell us more', options: [] }];\n const total = steps.length;\n let current = 0;\n const answers: Array<string | null> = new Array(total).fill(null);\n // P3 flow engine (§16.3) — per-step TELEMETRY (fire-and-forget, for funnel\n // drop-off) accumulated alongside the in-memory answers; the consolidated\n // RESPONSE is still flushed ONCE at completion (below) so an abandon mid-flow\n // never writes a fragmented row. `viewedSteps` de-dups the per-step impression.\n const flow = createFlowAccumulator();\n const viewedSteps = new Set<number>();\n const fireStepViewed = (idx: number): void => {\n if (viewedSteps.has(idx)) return;\n viewedSteps.add(idx);\n void this.trackEvent(campaign.id, 'impression', { stepId: `step_${idx + 1}` });\n };\n\n // Progress bar — keeps users motivated to finish (Plotline best practice).\n const progressTrack = document.createElement('div');\n progressTrack.style.cssText = `height: 4px; border-radius: 2px; background: ${text}22; margin-bottom: 16px; overflow: hidden;`;\n const progressFill = document.createElement('div');\n progressFill.style.cssText = `height: 100%; background: ${text}; transition: width 0.2s ease; width: 0%;`;\n progressTrack.appendChild(progressFill);\n body.appendChild(progressTrack);\n\n const stepLabel = document.createElement('div');\n stepLabel.style.cssText = 'font-size: 11px; opacity: 0.7; margin-bottom: 6px;';\n body.appendChild(stepLabel);\n\n const question = document.createElement('div');\n question.style.cssText = 'font-size: 16px; font-weight: 700; margin-bottom: 14px;';\n body.appendChild(question);\n\n const optionsList = document.createElement('div');\n optionsList.style.cssText = 'display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px;';\n body.appendChild(optionsList);\n\n const nav = document.createElement('div');\n nav.style.cssText = 'display: flex; gap: 8px; justify-content: space-between;';\n const backBtn = document.createElement('button');\n backBtn.textContent = 'Back';\n backBtn.style.cssText = `padding: 10px 16px; border-radius: 10px; border: 1px solid ${text}33; background: transparent; color: ${text}; font-size: 14px; cursor: pointer;`;\n const nextBtn = document.createElement('button');\n nextBtn.style.cssText = `padding: 10px 16px; border-radius: 10px; border: none; background: ${text}; color: ${bg}; font-size: 14px; font-weight: 600; cursor: pointer; flex: 1;`;\n nav.appendChild(backBtn);\n nav.appendChild(nextBtn);\n body.appendChild(nav);\n\n const renderStep = () => {\n const step = steps[current] || {};\n const q = (step.question as string) || (step.label as string) || `Step ${current + 1}`;\n const opts = Array.isArray(step.options) ? (step.options as string[]) : [];\n fireStepViewed(current); // funnel telemetry — once per step reached\n progressFill.style.width = `${Math.round((current / total) * 100)}%`;\n stepLabel.textContent = `Step ${current + 1} of ${total}`;\n question.textContent = q;\n while (optionsList.firstChild) optionsList.removeChild(optionsList.firstChild);\n opts.forEach((opt) => {\n const ob = document.createElement('button');\n ob.textContent = opt;\n const selected = answers[current] === opt;\n ob.style.cssText = `padding: 10px 16px; border-radius: 10px; border: 1px solid ${text}33; background: ${selected ? text + '22' : 'transparent'}; color: ${text}; font-size: 14px; cursor: pointer; text-align: left;`;\n ob.addEventListener('click', () => {\n answers[current] = opt;\n // Buffer the step answer in the flow accumulator (dedups by step_id).\n flow.recordStep({ step_id: `step_${current + 1}`, response_type: 'form_step', payload: { question: q, answer: opt } });\n renderStep();\n });\n optionsList.appendChild(ob);\n });\n backBtn.style.visibility = current === 0 ? 'hidden' : 'visible';\n nextBtn.textContent = current === total - 1 ? 'Submit' : 'Next';\n // Gate Next/Submit until the step is answered (was skippable → null\n // answers submitted). A step with no options has nothing to answer.\n const needsAnswer = opts.length > 0 && answers[current] == null;\n nextBtn.disabled = needsAnswer;\n nextBtn.style.opacity = needsAnswer ? '0.5' : '1';\n nextBtn.style.cursor = needsAnswer ? 'not-allowed' : 'pointer';\n };\n\n backBtn.addEventListener('click', () => {\n if (current > 0) {\n current -= 1;\n renderStep();\n }\n });\n nextBtn.addEventListener('click', () => {\n if (current < total - 1) {\n current += 1;\n renderStep();\n } else {\n progressFill.style.width = '100%';\n flow.markCompleted();\n void this.trackEvent(campaign.id, 'impression', { stepId: 'flow_completed' });\n void this.trackEvent(campaign.id, 'clicked', { meta: { answers } });\n // ONE consolidated response flush at completion (never per-step) — carries\n // the buffered step records so the funnel + the answer set stay consistent.\n void this.submitWidgetResponse(campaign, 'form', { answers, flow: flow.consolidated() });\n // Completed → green check + confetti, then close once it's been seen.\n this._playReaction(body, 'affirm', 'check.json', ic);\n window.setTimeout(() => { if (overlay.parentNode) document.body.removeChild(overlay); }, 2300);\n }\n });\n\n renderStep();\n\n this.addCloseButton(body, overlay, campaign.id);\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n /**\n * Push primer (SDK Demo v4 W3) — in-app render type that \"soft-asks\" before\n * the OS hard-ask. The Enable CTA calls `Notification.requestPermission()`;\n * we report `permission_granted` / `permission_denied` / `permission_default`\n * so journeys can react. Degrades gracefully where Notification is absent.\n */\n private renderPushPrimer(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n // Eligibility (permission / opt-in mode / trigger / snooze) is checked\n // synchronously BEFORE the dispatcher picks this campaign\n // (_primerEligibleNow) — so by the time we're here, rendering is the\n // correct outcome. The safety re-check below covers only the direct\n // renderInteractive path (previews, host-forced renders); it declines\n // silently, never tracks phantom dismissals.\n if (\n typeof Notification !== 'undefined' &&\n Notification.permission !== 'default'\n ) {\n return;\n }\n this._renderPushPrimerModal(campaign, ic, bg, text);\n }\n\n // Cached opt-in config for SYNCHRONOUS pre-dispatch eligibility. Loaded\n // once per manager (kicked off lazily on the first eligibility check);\n // undefined = not yet loaded, in which case the primer defers to a later\n // page view rather than rendering against unknown property policy.\n private _pushOptInCache: { mode: string; trigger: string } | undefined;\n private _pushOptInLoading = false;\n\n /** Synchronous \"can the primer render RIGHT NOW\" gate used by the overlay\n * selection loop. Never tracks events — an ineligible primer is a\n * non-event, not a dismissal. */\n private _primerEligibleNow(): boolean {\n if (typeof Notification === 'undefined' || Notification.permission !== 'default') {\n return false;\n }\n try {\n const snooze = parseInt(localStorage.getItem('aegis_primer_snooze_until') || '0', 10);\n if (snooze && Date.now() < snooze) return false;\n } catch {\n // storage blocked — no snooze state to honour\n }\n if (!this._pushOptInCache) {\n // Kick off the config load; defer the primer until it's cached.\n if (!this._pushOptInLoading) {\n this._pushOptInLoading = true;\n void this._fetchPushOptInConfig().then((cfg) => {\n this._pushOptInCache = cfg;\n this._pushOptInLoading = false;\n // Config arrived — re-evaluate so the primer can take a slot on\n // THIS page view if it's now eligible and no overlay showed yet.\n this.tryDisplayNextCampaign();\n });\n }\n return false;\n }\n return (\n this._pushOptInCache.mode === 'standard' &&\n this._promptTriggerSatisfied(this._pushOptInCache.trigger)\n );\n }\n\n /**\n * Fetch the property's push opt-in mode/trigger from /v1/sdk/config\n * (browser HTTP cache + ETag make repeat calls cheap). Fail-open to\n * 'standard'/'immediate' — a config blip must not silently kill the\n * only web-push opt-in surface.\n */\n private async _fetchPushOptInConfig(): Promise<{ mode: string; trigger: string }> {\n try {\n const res = await fetch(`${this.apiHost}/v1/sdk/config`, {\n headers: { 'X-Aegis-Write-Key': this.writeKey },\n });\n const cfg = (await res.json()) as {\n push?: { opt_in_mode?: string; prompt_trigger?: string };\n };\n return {\n mode: cfg.push?.opt_in_mode || 'standard',\n trigger: cfg.push?.prompt_trigger || 'immediate',\n };\n } catch {\n return { mode: 'standard', trigger: 'immediate' };\n }\n }\n\n /**\n * Prompt-timing gate for the standard primer.\n * immediate → always\n * second_page_view → from the 2nd page load on (localStorage counter,\n * incremented once per manager init)\n * after_first_order→ once `aegis_ordered` is set (host runtimes set it\n * on purchase; storefront checkout does)\n * manual → never auto-shows; host calls the primer explicitly\n */\n private _promptTriggerSatisfied(trigger: string): boolean {\n try {\n switch (trigger) {\n case 'second_page_view':\n return parseInt(localStorage.getItem('aegis_pv_count') || '0', 10) >= 2;\n case 'after_first_order':\n return localStorage.getItem('aegis_ordered') === '1';\n case 'manual':\n return false;\n default:\n return true;\n }\n } catch {\n return true; // storage blocked — fail-open to showing\n }\n }\n\n private _renderPushPrimerModal(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-push-primer-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 340px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 16px 48px rgba(0,0,0,0.22);\n `;\n\n const body = document.createElement('div');\n body.style.cssText = 'padding: 24px; text-align: center;';\n\n const icon = document.createElement('div');\n icon.style.cssText = 'margin-bottom: 8px; display: flex; justify-content: center;';\n // Default hero = inline lucide Bell SVG (no emoji chrome — CLAUDE.md Icons\n // rule). An operator-supplied `ic.icon` string still overrides as text.\n if (typeof ic.icon === 'string' && ic.icon.length > 0) {\n icon.textContent = ic.icon as string;\n icon.style.fontSize = '36px';\n } else {\n icon.innerHTML = lucideSvg('bell', { size: 36, color: text });\n }\n body.appendChild(icon);\n\n const title = document.createElement('div');\n title.style.cssText = 'font-size: 18px; font-weight: 700; margin-bottom: 8px;';\n title.textContent = campaign.title || 'Stay in the loop';\n body.appendChild(title);\n\n const desc = document.createElement('div');\n desc.style.cssText = 'font-size: 14px; opacity: 0.85; margin-bottom: 18px;';\n desc.textContent = campaign.body || 'Enable notifications so you never miss an offer.';\n body.appendChild(desc);\n\n const supported =\n typeof Notification !== 'undefined' &&\n typeof Notification.requestPermission === 'function';\n\n const enableBtn = document.createElement('button');\n enableBtn.textContent = (ic.allow_text as string) || 'Enable notifications';\n enableBtn.style.cssText = `width: 100%; padding: 12px 16px; border-radius: 10px; border: none; background: ${text}; color: ${bg}; font-size: 15px; font-weight: 600; cursor: pointer; margin-bottom: 8px;`;\n enableBtn.addEventListener('click', () => {\n if (!supported) {\n void this.trackEvent(campaign.id, 'dismissed', { meta: { reason: 'unsupported' } });\n if (overlay.parentNode) document.body.removeChild(overlay);\n return;\n }\n enableBtn.disabled = true;\n // Full subscribe flow — NOT a bare Notification.requestPermission().\n // The primer is the ONLY web-push opt-in surface (canonical, replaces\n // per-host banners like ACS PushOptInBanner), so on grant it MUST also\n // register the SW + subscribe via VAPID + POST the subscription +\n // stitch the contact — otherwise permission is granted but the user\n // is never registered and can never receive a push.\n void this._subscribeFromPushPrimer().then((perm) => {\n void this.trackEvent(campaign.id, 'clicked', { meta: { permission: perm } });\n if (overlay.parentNode) document.body.removeChild(overlay);\n });\n });\n body.appendChild(enableBtn);\n\n const dismissBtn = document.createElement('button');\n dismissBtn.textContent = (ic.dismiss_text as string) || 'Not now';\n dismissBtn.style.cssText = `width: 100%; padding: 10px 16px; border-radius: 10px; border: none; background: transparent; color: ${text}; opacity: 0.7; font-size: 14px; cursor: pointer;`;\n dismissBtn.addEventListener('click', () => {\n void this.trackEvent(campaign.id, 'dismissed');\n // Respect the \"Not now\": snooze the soft-ask for 3 days instead of\n // re-prompting on the next page view (industry-standard re-ask\n // cadence; the OS permission itself is untouched).\n try {\n localStorage.setItem(\n 'aegis_primer_snooze_until',\n String(Date.now() + 3 * 24 * 60 * 60 * 1000),\n );\n } catch {\n // storage blocked — session-only suppression via displayedCampaigns\n }\n if (overlay.parentNode) document.body.removeChild(overlay);\n });\n body.appendChild(dismissBtn);\n\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n /**\n * Push-primer Enable handler — runs the SAME end-to-end subscribe flow the\n * (now-retired) ACS PushOptInBanner did, so the primer is a complete web-push\n * opt-in on every surface (ACS / Shopify / 3rd-party site), not just an OS\n * permission prompt. Steps: fetch VAPID public key → register SW → hard-ask →\n * subscribe → POST /v1/web_push/subscriptions → stitch known contact.\n *\n * Push runtime resolution prefers the already-loaded `window.AegisWebPush`\n * global (the Shopify embed lazy-loads it via /apps/active_reach/push.js, so\n * we reuse that single instance instead of registering the SW twice), and\n * only dynamic-imports the module as a fallback for hosts that didn't preload\n * it (ACS / 3rd-party sites). Returns the resolved permission string for\n * engagement reporting; falls back to a bare permission ask if push can't be\n * wired (no propertyId, no VAPID).\n */\n private async _subscribeFromPushPrimer(): Promise<NotificationPermission> {\n // Without a property binding the backend 422s the subscribe (missing\n // X-Property-Id) — degrade to the OS ask alone rather than dead-end.\n if (!this.propertyId) {\n return Notification.requestPermission();\n }\n try {\n const cfgRes = await fetch(`${this.apiHost}/v1/sdk/config`, {\n headers: { 'X-Aegis-Write-Key': this.writeKey },\n });\n const cfg = (await cfgRes.json()) as {\n push?: {\n enabled?: boolean;\n vapid_public_key?: string | null;\n service_worker_path?: string | null;\n service_worker_scope?: string | null;\n opt_in_mode?: string;\n };\n };\n const vapidPublicKey = cfg.push?.vapid_public_key || '';\n if (!cfg.push?.enabled || !vapidPublicKey) {\n return Notification.requestPermission();\n }\n // Property opted out of Aegis push entirely — never register.\n if (cfg.push?.opt_in_mode === 'off') {\n return Notification.permission;\n }\n // Per-surface SW location — Shopify's is proxied at /apps/active_reach/sw.js,\n // ACS/3rd-party host it at /aegis-sw.js. Config is the single source so no\n // surface hardcodes it.\n const serviceWorkerPath = cfg.push?.service_worker_path || '/aegis-sw.js';\n const serviceWorkerScope = cfg.push?.service_worker_scope || '/';\n\n // Bind the subscribe to the known contact BEFORE initialize() — the push\n // runtime's resolveContactId() reads aegis_contact_id from localStorage,\n // so seeding it first makes the very first subscribe row land on the\n // logged-in contact instead of the anonymous device id.\n if (this.contactId && typeof window !== 'undefined') {\n try {\n window.localStorage.setItem('aegis_contact_id', this.contactId);\n } catch {\n // storage blocked — backend still binds via authenticated identity\n }\n }\n\n // Duck-typed apiClient (same shape the AegisWebPush constructor expects);\n // POSTs /v1/web_push/* through the public gateway with the SDK's creds.\n const writeKey = this.writeKey;\n const apiHost = this.apiHost;\n const organizationId = this.organizationId;\n const apiClient = {\n async post(\n endpoint: string,\n payload: Record<string, unknown>,\n headers?: Record<string, string>,\n ) {\n const res = await fetch(`${apiHost}${endpoint}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': writeKey,\n ...(organizationId ? { 'X-Organization-ID': organizationId } : {}),\n ...(headers ?? {}),\n },\n body: JSON.stringify(payload),\n });\n if (!res.ok) throw new Error(`push api ${endpoint} → ${res.status}`);\n return res.json().catch(() => ({}));\n },\n };\n\n // Reuse a preloaded global (Shopify embed) to avoid a second SW\n // registration; else lazy-load the module (ESM code-splits, IIFE inlines\n // the ~8KB runtime).\n const AegisWebPush =\n (typeof window !== 'undefined' &&\n (window as unknown as { AegisWebPush?: typeof import('../push/AegisWebPush').AegisWebPush })\n .AegisWebPush) ||\n (await import('../push/AegisWebPush')).AegisWebPush;\n const webPush = new AegisWebPush(apiClient, {\n vapidPublicKey,\n propertyId: this.propertyId,\n // Anon id doubles as the device-fingerprint salt so a re-subscribe\n // after VAPID rotation upserts onto the same subscription row.\n firstPartyCookieId: readAnonIdFromStorage() || this.contactId || this.organizationId || '',\n serviceWorkerPath,\n serviceWorkerScope,\n autoPrompt: false,\n writeKey,\n organizationId,\n });\n await webPush.initialize();\n const granted = await webPush.requestPermission();\n // Stitch the subscription to the known contact so sends fan out to this\n // person (not just the anonymous device row).\n if (granted && this.contactId) {\n await webPush.identify({ contactId: this.contactId });\n }\n return Notification.permission;\n } catch (err) {\n console.warn('[Aegis in-app] push-primer subscribe failed:', err);\n // Best-effort fallback so the user still gets the OS grant recorded.\n try {\n return await Notification.requestPermission();\n } catch {\n return Notification.permission;\n }\n }\n }\n\n private renderQuiz(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-quiz-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 360px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 16px 48px rgba(0,0,0,0.22);\n `;\n\n const body = document.createElement('div');\n body.style.cssText = 'padding: 22px; text-align: center;';\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n\n this._runQuizFlow(campaign, ic, bg, text, body, overlay);\n }\n\n /**\n * Embedded quiz on a bill/page slot — same question-stepping flow as the\n * overlay, rendered inline with no overlay/close (Open/Closed: both paths\n * share `_runQuizFlow`, so a feature added once lights up both). Returns\n * false when there are no questions so dispatch can fall back to a card.\n */\n private renderQuizSlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n target: HTMLElement,\n ): boolean {\n const questions = Array.isArray(ic.questions) ? (ic.questions as unknown[]) : [];\n if (questions.length === 0) return false;\n const st = this._slotStyle(campaign);\n const card = this._wrapInSlotCard('aegis-in-app-quiz-card', campaign.id, bg, text, st.radius);\n const body = document.createElement('div');\n body.style.cssText = `padding: ${st.padY ?? 18}px ${st.padX ?? 18}px; text-align: center;`;\n card.appendChild(body);\n target.appendChild(card);\n this._runQuizFlow(campaign, ic, bg, text, body, null);\n return true;\n }\n\n /**\n * Shared quiz engine — drives the question stepper, scoring (knowledge =\n * correct_index/points, personality = outcome tally), result-band selection,\n * CTA, reaction and the `quiz` response submit. Renders into the provided\n * `body`. `overlay` is the dismiss target for the close button on the overlay\n * path; pass null on the slot path (no close affordance inline).\n */\n private _runQuizFlow(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n body: HTMLElement,\n overlay: HTMLElement | null,\n ): void {\n const isUrl = (s: string): boolean => /^https?:\\/\\//i.test(s);\n const questions = Array.isArray(ic.questions) ? (ic.questions as Array<Record<string, unknown>>) : [];\n const resultMap = Array.isArray(ic.result_mapping) ? (ic.result_mapping as Array<Record<string, unknown>>) : [];\n // knowledge = right/wrong by `correct_index` (+ per-question `points`); personality\n // = each option's index contributes to a score that maps to a result band.\n const knowledge = (ic.quiz_type as string) === 'knowledge';\n const total = questions.length;\n const answers: number[] = [];\n const tally: Record<string, number> = {}; // personality: outcome id → pick count\n const maxPts = knowledge ? questions.reduce((s, q) => s + (typeof q.points === 'number' ? (q.points as number) : 1), 0) : 0;\n let score = 0;\n let cur = 0;\n\n const clear = (): void => { while (body.firstChild) body.removeChild(body.firstChild); };\n const heading = (txt: string, big = false): HTMLElement => {\n const t = document.createElement('div');\n t.style.cssText = `font-size: ${big ? 19 : 14}px; font-weight: ${big ? 800 : 600}; letter-spacing: -0.01em; line-height: 1.3; margin-bottom: ${big ? 6 : 14}px; font-family: 'Urbanist', system-ui, -apple-system, sans-serif;`;\n t.textContent = txt;\n return t;\n };\n\n // Final screen: pick the result band the score falls into, show its title/body\n // (+ optional CTA), react, AND submit so the server records it (analytics +\n // completion reward + response-mapping enrichment + the quiz_completed event).\n const showResult = (): void => {\n clear();\n let band: Record<string, unknown> | null = null;\n let outScore = score;\n if (knowledge) {\n band = resultMap.find((r) => {\n const mn = typeof r.min_score === 'number' ? (r.min_score as number) : -Infinity;\n const mx = typeof r.max_score === 'number' ? (r.max_score as number) : Infinity;\n return score >= mn && score <= mx;\n }) || resultMap[resultMap.length - 1] || null;\n } else {\n // Personality: the outcome picked most across all questions wins (ties →\n // the first-authored outcome, by iterating resultMap in order).\n let best = -1;\n for (const r of resultMap) {\n const rid = typeof r.id === 'string' ? (r.id as string) : '';\n const c = rid ? (tally[rid] || 0) : 0;\n if (c > best) { best = c; band = r; }\n }\n outScore = best < 0 ? 0 : best;\n band = band || resultMap[0] || null;\n }\n const rTitle = band && typeof band.title === 'string'\n ? (band.title as string)\n : (knowledge && maxPts ? `You scored ${score} of ${maxPts}` : 'Your result');\n const rBody = band && typeof band.body === 'string'\n ? (band.body as string)\n : (ic.thank_you_message as string) || 'Thanks for completing the quiz!';\n body.appendChild(heading(rTitle, true));\n const sub = document.createElement('div');\n sub.style.cssText = 'font-size: 13.5px; opacity: 0.85; line-height: 1.45; margin: 2px 0 14px;';\n sub.textContent = rBody;\n body.appendChild(sub);\n const actionUrl = band && typeof band.action_url === 'string' ? (band.action_url as string) : '';\n if (actionUrl) {\n const cta = document.createElement('button');\n cta.type = 'button';\n cta.textContent = (campaign as unknown as Record<string, unknown>).button_text as string || 'Continue';\n cta.style.cssText = `padding: 11px 28px; border-radius: 999px; border: none; background: ${text}; color: ${bg}; font-weight: 800; font-size: 14px; cursor: pointer; margin-top: 2px;`;\n cta.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n const safe = this.sanitizeUrl(actionUrl);\n if (safe) this.navigateToCampaignAction(campaign, safe);\n });\n body.appendChild(cta);\n }\n this._playReaction(body, 'affirm', 'check.json', ic);\n if (overlay) this.addCloseButton(body, overlay, campaign.id);\n void this.submitWidgetResponse(campaign, 'quiz', {\n answers,\n score: outScore,\n result_title: rTitle,\n result_id: band && typeof band.id === 'string' ? (band.id as string) : undefined,\n quiz_type: knowledge ? 'knowledge' : 'personality',\n });\n };\n\n const renderQuestion = (): void => {\n if (cur >= total) { showResult(); return; }\n clear();\n const q = questions[cur];\n if (campaign.title) {\n const hd = document.createElement('div');\n hd.style.cssText = 'font-size: 11px; font-weight: 700; letter-spacing: 0.3px; text-transform: uppercase; opacity: 0.55; margin-bottom: 10px;';\n hd.textContent = campaign.title;\n body.appendChild(hd);\n }\n const track = document.createElement('div');\n track.style.cssText = `height: 4px; border-radius: 999px; background: ${text}1f; overflow: hidden; margin-bottom: 8px;`;\n const fill = document.createElement('div');\n fill.style.cssText = `height: 100%; width: ${total ? Math.round((cur / total) * 100) : 0}%; background: ${text}; border-radius: 999px; transition: width 0.3s ease;`;\n track.appendChild(fill); body.appendChild(track);\n const step = document.createElement('div');\n step.style.cssText = 'font-size: 11.5px; opacity: 0.6; margin-bottom: 12px; font-weight: 600;';\n step.textContent = `Question ${cur + 1} of ${total}`;\n body.appendChild(step);\n const imgUrl = q.image_url;\n if (typeof imgUrl === 'string' && isUrl(imgUrl)) {\n const im = document.createElement('img');\n im.src = imgUrl; im.alt = '';\n im.style.cssText = 'width: 100%; max-height: 140px; object-fit: cover; border-radius: 10px; margin-bottom: 12px;';\n body.appendChild(im);\n }\n body.appendChild(heading(typeof q.question === 'string' ? (q.question as string) : ''));\n const opts = Array.isArray(q.options) ? (q.options as unknown[]).filter((o): o is string => typeof o === 'string') : [];\n const correctIdx = typeof q.correct_index === 'number' ? (q.correct_index as number) : -1;\n const pts = typeof q.points === 'number' ? (q.points as number) : 1;\n const wrap = document.createElement('div');\n wrap.style.cssText = 'display: flex; flex-direction: column; gap: 8px;';\n const btns: HTMLButtonElement[] = [];\n opts.forEach((opt, j) => {\n const b = document.createElement('button');\n b.type = 'button';\n b.style.cssText = `padding: 11px 14px; border-radius: 10px; border: 1.5px solid ${text}2e; background: transparent; color: ${text}; font-size: 14px; cursor: pointer; text-align: left; transition: all 0.15s; font-weight: 600;`;\n b.textContent = opt;\n b.addEventListener('click', () => {\n if (b.disabled) return;\n btns.forEach((x) => { x.disabled = true; });\n answers.push(j);\n let delay = 280;\n if (knowledge) {\n if (j === correctIdx) score += pts;\n if (correctIdx >= 0) {\n const good = j === correctIdx;\n b.style.borderColor = good ? '#10b981' : '#ef4444';\n b.style.background = (good ? '#10b981' : '#ef4444') + '22';\n if (!good && btns[correctIdx]) {\n btns[correctIdx].style.borderColor = '#10b981';\n btns[correctIdx].style.background = '#10b98114';\n }\n delay = 680;\n }\n } else {\n // personality: tally the outcome this option maps to (option_results[j]).\n const optRes = Array.isArray(q.option_results) ? (q.option_results as unknown[]) : [];\n const rid = optRes[j];\n if (typeof rid === 'string' && rid) tally[rid] = (tally[rid] || 0) + 1;\n b.style.borderColor = text; b.style.background = `${text}14`;\n }\n window.setTimeout(() => { cur += 1; renderQuestion(); }, delay);\n });\n wrap.appendChild(b); btns.push(b);\n });\n body.appendChild(wrap);\n if (overlay) this.addCloseButton(body, overlay, campaign.id);\n };\n\n renderQuestion();\n }\n\n // --- Shared Rendering Helpers ---\n\n private createOverlay(className: string): HTMLElement {\n const overlay = document.createElement('div');\n overlay.className = className;\n overlay.style.cssText = `\n position: fixed; top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5); display: flex; align-items: center;\n justify-content: center; z-index: 99999; animation: aegisFadeIn 0.3s ease;\n `;\n // Canonical display-size: every createOverlay-based format (spin/scratch,\n // nps, quiz, countdown, rating, poll, multi-step, push-primer, custom-html…)\n // scales uniformly. The card is appended by the renderer synchronously after\n // this returns, so we wrap it on the next microtask — one place, all formats,\n // no per-renderer edits. The card keeps its own entrance animation; the\n // wrapper carries the scale (transform-origin center, so it stays centered).\n if (this._displaySizeMul !== 1) {\n const mul = this._displaySizeMul;\n queueMicrotask(() => {\n const card = overlay.firstElementChild as HTMLElement | null;\n if (!card || (card as HTMLElement).dataset.aegisSized) return;\n (card as HTMLElement).dataset.aegisSized = '1';\n const wrap = document.createElement('div');\n wrap.style.cssText =\n `display: flex; align-items: center; justify-content: center; ` +\n `transform: scale(${mul}); transform-origin: center;`;\n overlay.insertBefore(wrap, card);\n wrap.appendChild(card);\n });\n }\n return overlay;\n }\n\n /** Wrap a card in a display-size scaling container (no-op at medium/×1). Used\n * by formats that build their overlay inline (renderModal) rather than via\n * createOverlay, so size is honored identically. */\n private _wrapScaled(card: HTMLElement): HTMLElement {\n if (this._displaySizeMul === 1) return card;\n const wrap = document.createElement('div');\n wrap.style.cssText =\n `display: flex; align-items: center; justify-content: center; ` +\n `transform: scale(${this._displaySizeMul}); transform-origin: center;`;\n wrap.appendChild(card);\n return wrap;\n }\n\n private createCTAButton(campaign: InAppCampaign, bg: string, text: string): HTMLElement {\n const btn = document.createElement('button');\n btn.className = 'aegis-cta';\n btn.style.cssText = `\n display: inline-block; padding: 10px 28px; border-radius: 999px;\n font-size: 14px; font-weight: 600; cursor: pointer; border: none;\n background: ${text}; color: ${bg}; transition: transform 0.15s;\n `;\n btn.textContent = campaign.button_text || 'OK';\n btn.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (campaign.action_url) {\n const safeUrl = this.sanitizeUrl(campaign.action_url);\n if (safeUrl) this.navigateToCampaignAction(campaign, safeUrl);\n }\n });\n return btn;\n }\n\n private addCloseButton(container: HTMLElement, overlay: HTMLElement, campaignId: string): void {\n const close = document.createElement('div');\n close.style.cssText = 'margin-top: 12px; font-size: 12px; opacity: 0.6; cursor: pointer;';\n close.textContent = 'Close';\n close.addEventListener('click', () => {\n this.trackEvent(campaignId, 'dismissed');\n this.removeModal(overlay);\n });\n container.appendChild(close);\n }\n \n /**\n * Navigate to a campaign CTA URL.\n *\n * Dispatches a cancellable `aegis:campaign-click` CustomEvent so host\n * apps (e.g., the storefront SPA) can intercept and route in-place via\n * their client-side router, avoiding a full page reload that re-runs\n * SSR + re-renders the location picker / banner / cart drawer state.\n *\n * The host calls `event.preventDefault()` to suppress the hard nav and\n * handle the URL itself. If no listener calls preventDefault, the SDK\n * falls through to `window.location.href` so plain HTML pages without a\n * SPA still get the user to the destination.\n *\n * Industry parallel: CleverTap dispatches `clevertap-deeplink-click`,\n * MoEngage dispatches `MoE_inappRedirect`. The pattern lets the same\n * SDK serve both vanilla websites (full nav fallback) and SPAs\n * (in-place state change) without per-host configuration.\n */\n private navigateToCampaignAction(\n campaign: InAppCampaign,\n safeUrl: string,\n buttonId: string = 'cta',\n ): void {\n if (typeof window === 'undefined') return;\n\n // Phase-2 action verbs — `aegis://<verb>` performs an in-app action\n // (dismiss / request push permission / add to cart) instead of navigating.\n if (safeUrl.startsWith('aegis://')) {\n this.runCampaignActionVerb(campaign, safeUrl);\n return;\n }\n\n // Typed lifecycle hook (1.8.0). Mirror the DOM `preventDefault` contract\n // — a handler returning `false` OR mutating `defaultPrevented` via the\n // helper suppresses the SDK's hard-nav. Built first so handlers attached\n // via the typed API see the click before the DOM event fires.\n let typedDefaultPrevented = false;\n const typedEvent: CampaignClickEvent = {\n campaign,\n action_url: safeUrl,\n button_id: buttonId,\n preventDefault: () => {\n typedDefaultPrevented = true;\n typedEvent.defaultPrevented = true;\n },\n defaultPrevented: false,\n };\n const typedProceed = this.emit('campaign-click', typedEvent);\n\n // DOM CustomEvent path — preserved for backward compat (1.7.x consumers\n // already listen for `aegis:campaign-click`) and for advanced patterns\n // (multi-frame embeds, host-app code that runs before the SDK is\n // instantiated, debugging via DevTools event listener panel).\n const domEvent = new CustomEvent('aegis:campaign-click', {\n detail: {\n campaign_id: campaign.id,\n campaign_type: campaign.type,\n action_url: safeUrl,\n button_id: buttonId,\n },\n cancelable: true,\n });\n const domProceed = window.dispatchEvent(domEvent);\n\n // Either path can suppress the hard-nav. We OR the prevent signals so\n // host apps don't have to remember to handle both — registering on\n // either API is enough.\n const suppressed =\n !typedProceed ||\n typedDefaultPrevented ||\n !domProceed ||\n domEvent.defaultPrevented;\n\n if (!suppressed) {\n window.location.href = safeUrl;\n }\n }\n\n /** Register/replace the canonical CTA-action handler after construction (the\n * config `onAction` is the construct-time equivalent). Idempotent. */\n setOnAction(handler: (action: AegisAction) => boolean | void): void {\n this.onActionHandler = handler;\n }\n\n /**\n * Canonical CTA-action dispatcher (CANONICAL_ACTION_MODEL_TRACKER.md). ONE path\n * for every host-facing CTA action:\n * 1. give the host's `onAction` handler first refusal (return `true` = handled);\n * 2. ALSO fire the legacy `aegis:*` DOM event (cancelable) so existing\n * listeners (ACS) keep working unchanged during the migration;\n * 3. if STILL unhandled → canonical fallback (navigate to the product/cart\n * page) so a CTA is never a dead no-op (the rail-\"Add\" 3rd-party gap).\n * Both add-to-cart paths (card \"Add\" + the `aegis://cart-add` verb) funnel here,\n * so they're one behaviour delivered to the host's single `onAction` handler.\n * (Phase E.2 hard cutover — the legacy `aegis:*` DOM events were removed.)\n */\n dispatchAction(action: AegisAction, _campaignId?: string): void {\n let handled = false;\n try {\n handled = this.onActionHandler?.(action) === true;\n } catch {\n /* a host handler must never break the SDK */\n }\n\n // No legacy `aegis:*` DOM events — `onAction` is the ONLY host seam (Phase\n // E.2 hard cutover). If the host didn't claim the action, apply the canonical\n // fallback so a CTA is never a dead no-op.\n if (!handled) {\n const url = fallbackUrlForAction(action, this.productUrl);\n if (url && typeof window !== 'undefined') window.location.href = url;\n }\n }\n\n /**\n * Phase-2 CTA action verbs (`aegis://<verb>?<params>`). Performs an in-app\n * behaviour instead of navigating. Verbs:\n * dismiss — close the message (tracked as dismissed)\n * request-push — prompt for web-push permission, then close\n * cart-add?sku=X — canonical `add_to_cart` action via dispatchAction\n * (cancelable + product-page fallback)\n */\n private runCampaignActionVerb(campaign: InAppCampaign, url: string): void {\n const rest = url.slice('aegis://'.length);\n const [verb, qs] = rest.split('?');\n const params = new URLSearchParams(qs || '');\n\n switch (verb) {\n case 'dismiss':\n void this.trackEvent(campaign.id, 'dismissed', { meta: { reason: 'cta' } });\n this.removeCampaignEl(campaign.id);\n break;\n\n case 'request-push':\n void this.trackEvent(campaign.id, 'clicked', { meta: { verb: 'request-push' } });\n if (\n typeof Notification !== 'undefined' &&\n typeof Notification.requestPermission === 'function'\n ) {\n void Promise.resolve(Notification.requestPermission()).then((perm) => {\n void this.trackEvent(campaign.id, 'clicked', { meta: { permission: perm } });\n });\n }\n this.removeCampaignEl(campaign.id);\n break;\n\n case 'cart-add': {\n const sku = params.get('sku') || '';\n void this.trackEvent(campaign.id, 'clicked', { meta: { verb: 'cart-add', sku } });\n // Canonical path — funnels through dispatchAction (host `onAction`\n // handler → product-page fallback). Replaces the old bespoke\n // `aegis:cart-add` event that nothing listened for.\n this.dispatchAction(parseAction(url, { sku } as ActionMeta), campaign.id);\n break;\n }\n\n default:\n this.log(`Unknown campaign action verb: ${verb}`, 'warn');\n }\n }\n\n /** Remove a rendered campaign's element(s) from the DOM by campaign id. */\n private removeCampaignEl(campaignId: string): void {\n if (typeof document === 'undefined') return;\n document\n .querySelectorAll(`[data-campaign-id=\"${campaignId}\"]`)\n .forEach((el) => el.parentNode?.removeChild(el));\n }\n\n /**\n * Resolve the operator's colour + style choices into concrete CSS values.\n * Mirrors the studio preview's styleFrom() 1:1 (apps/web/src/app/api/preview/\n * in-app/route.tsx) — same keys, same defaults — so what the operator designs\n * in the editor is what the live SDK renders. The container uses the\n * campaign's background_color/text_color and the CTA button is the inverse\n * (so it pops on the coloured surface), exactly like the preview. Earlier the\n * card renderers hardcoded a white card + dark text and ignored the Style\n * panel entirely, so the live message looked nothing like the preview.\n */\n /** Pick a legible label colour (#fff or near-black) for a solid fill, by\n * relative luminance — so a brand-accent button is always readable. */\n private _contrastText(hex: string): string {\n const m = /^#?([0-9a-fA-F]{6})$/.exec(this.sanitizeColor(hex));\n if (!m) return '#ffffff';\n const n = parseInt(m[1], 16);\n const L = (0.299 * ((n >> 16) & 255) + 0.587 * ((n >> 8) & 255) + 0.114 * (n & 255)) / 255;\n return L > 0.62 ? '#0f172a' : '#ffffff';\n }\n\n /** Central surface palette — the ONE place the brand colour becomes either a\n * surface fill or an accent, so every widget is consistent.\n *\n * `surface_style` (interactive_config), default `clean`:\n * - `clean` → NEUTRAL surface (white) + dark text + the brand colour as the\n * CTA ACCENT. The 60-30-10 / WCAG-safe default: legible copy and\n * a CTA that actually pops (higher CTR than a flat colour card).\n * - `branded`→ brand-colour SURFACE + contrasting text + inverse CTA. For\n * high-impact, low-text moments (flash sale, celebration).\n * `background_color` is the operator's brand colour in BOTH modes — surface\n * fill when branded, CTA accent when clean. */\n private _surfacePalette(campaign: InAppCampaign): {\n bg: string; text: string; btnBg: string; btnText: string; accent: string; branded: boolean;\n } {\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const brand = this.sanitizeColor(campaign.background_color || '#4169e1');\n const surface = ic.surface_style as string;\n if (surface === 'branded') {\n const bg = brand;\n const text = campaign.text_color ? this.sanitizeColor(campaign.text_color) : '#ffffff';\n return { bg, text, btnBg: text, btnText: bg, accent: text, branded: true };\n }\n // Dark / charcoal surface (Zepto-style value bar). The card bg is the\n // operator's background_color (default charcoal); the brand accent is kept\n // for CTAs via the --aegis-brand-accent var the row buttons read directly.\n if (surface === 'dark') {\n const bg = this.sanitizeColor(campaign.background_color || '#0f172a');\n const text = campaign.text_color ? this.sanitizeColor(campaign.text_color) : '#ffffff';\n return { bg, text, btnBg: '#ffffff', btnText: bg, accent: text, branded: false };\n }\n return { bg: '#ffffff', text: '#0f172a', btnBg: brand, btnText: this._contrastText(brand), accent: brand, branded: false };\n }\n\n private inAppStyle(campaign: InAppCampaign): {\n bg: string; text: string; btnBg: string; btnText: string; accent: string;\n radius: number; btnRadius: number; padX: number; padY: number;\n weight: string; mediaHeight: number; overlay: number;\n } {\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const numOr = (v: unknown, d: number): number => {\n const n = Number(v);\n return Number.isFinite(n) ? n : d;\n };\n const p = this._surfacePalette(campaign);\n return {\n bg: p.bg,\n text: p.text,\n btnBg: p.btnBg,\n btnText: p.btnText,\n accent: p.accent,\n radius: numOr(ic.border_radius, 16),\n btnRadius: numOr(ic.button_radius, 999),\n padX: numOr(ic.padding_x, 24),\n padY: numOr(ic.padding_y, 24),\n weight: String((ic.heading_font_weight as string | number) || '700'),\n mediaHeight: numOr(ic.media_height, 160),\n // 0–100 in the Style panel → 0–1 alpha for the backdrop dim.\n overlay: numOr(ic.overlay_opacity, 50) / 100,\n };\n }\n\n private renderBanner(campaign: InAppCampaign): void {\n const banner = document.createElement('div');\n banner.className = 'aegis-in-app-banner';\n banner.setAttribute('data-campaign-id', campaign.id);\n\n // Honor the studio Style-panel padding so the live strip matches the\n // editor preview. Other knobs (border_radius) are N/A for an edge-to-edge\n // top strip.\n const st = this.inAppStyle(campaign);\n const padY = st.padY;\n const padX = st.padX;\n const ic = (campaign.interactive_config as Record<string, unknown> | undefined) || {};\n const atBottom = (ic.banner_position as string) === 'bottom';\n\n banner.style.cssText = `\n position: fixed;\n ${atBottom ? 'bottom: 0;' : 'top: 0;'}\n left: 0;\n right: 0;\n background: ${st.bg};\n color: ${st.text};\n padding: ${padY}px ${padX}px;\n z-index: 999999;\n box-shadow: 0 ${atBottom ? '-2px' : '2px'} 8px rgba(0,0,0,0.1);\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: ${atBottom ? 'aegisSlideInFromBottom' : 'aegisSlideDown'} 0.3s ease-out;\n `;\n // Tag the anchor so removeBanner can mirror the exit direction (a bottom\n // banner must slide DOWN out, a top banner UP out).\n banner.setAttribute('data-banner-position', atBottom ? 'bottom' : 'top');\n \n const contentContainer = document.createElement('div');\n contentContainer.style.cssText = 'flex: 1; display: flex; align-items: center; gap: 12px;';\n \n if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 40px; height: 40px; border-radius: 4px; object-fit: cover;';\n contentContainer.appendChild(img);\n }\n }\n \n const textContainer = document.createElement('div');\n textContainer.style.cssText = 'flex: 1;';\n \n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = `font-weight: ${st.weight}; font-size: 14px; margin-bottom: 4px;`;\n textContainer.appendChild(title);\n\n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 13px; opacity: 0.9;';\n textContainer.appendChild(body);\n\n contentContainer.appendChild(textContainer);\n\n const actionsContainer = document.createElement('div');\n actionsContainer.style.cssText = 'display: flex; align-items: center; gap: 12px; margin-left: 16px;';\n\n // CTA renders whenever it's LABELLED (was hidden unless BOTH set).\n const bannerCtaLabel = campaign.button_text || (campaign.action_url ? 'Learn more' : '');\n if (bannerCtaLabel) {\n const ctaButton = document.createElement('button');\n ctaButton.className = 'aegis-cta';\n ctaButton.textContent = bannerCtaLabel;\n ctaButton.style.cssText = `\n background: ${st.btnBg};\n color: ${st.btnText};\n border: none;\n padding: 8px 16px;\n border-radius: ${st.btnRadius}px;\n font-weight: 600;\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n `;\n\n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (campaign.action_url) {\n const safeUrl = this.sanitizeUrl(campaign.action_url);\n if (safeUrl) this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeBanner(banner);\n });\n\n actionsContainer.appendChild(ctaButton);\n }\n \n const closeButton = document.createElement('button');\n closeButton.innerHTML = lucideSvg('x', { size: 18 });\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n background: transparent;\n border: none;\n color: inherit;\n font-size: 20px;\n cursor: pointer;\n padding: 0;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0.7;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeBanner(banner);\n });\n \n actionsContainer.appendChild(closeButton);\n \n banner.appendChild(contentContainer);\n banner.appendChild(actionsContainer);\n\n this.addAnimationStyles();\n document.body.appendChild(banner);\n\n // Optional auto-hide after N ms (0 / absent = stays until dismissed).\n const autoMs = Number(ic.banner_auto_dismiss_ms) || 0;\n if (autoMs > 0) {\n window.setTimeout(() => { if (banner.parentNode) this.removeBanner(banner); }, autoMs);\n }\n }\n\n private renderModal(campaign: InAppCampaign): void {\n const st = this.inAppStyle(campaign);\n const ic = (campaign.interactive_config as Record<string, unknown> | undefined) || {};\n const videoUrl = campaign.video_url || (ic.video_url as string | undefined);\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-modal-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n\n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, ${st.overlay});\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n\n const modal = document.createElement('div');\n modal.className = 'aegis-in-app-modal';\n modal.style.cssText = `\n background: ${st.bg};\n color: ${st.text};\n border-radius: ${st.radius}px;\n max-width: 500px;\n width: 90%;\n max-height: 80vh;\n overflow: auto;\n box-shadow: 0 16px 48px rgba(0,0,0,0.22);\n animation: aegisScaleIn 0.3s ease-out;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n\n // Hero media — a VIDEO (was authored-but-unrendered: modal ignored video_url)\n // or an image. Both fill the top media band with rounded top corners.\n if (videoUrl) {\n const mediaWrap = document.createElement('div');\n mediaWrap.style.cssText = `position: relative; width: 100%; height: ${st.mediaHeight}px; overflow: hidden; border-radius: ${st.radius}px ${st.radius}px 0 0; background: #000;`;\n this._heroVideo(videoUrl, campaign.image_url, mediaWrap, 'bottom-left');\n modal.appendChild(mediaWrap);\n } else if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = `width: 100%; height: ${st.mediaHeight}px; object-fit: cover; border-radius: ${st.radius}px ${st.radius}px 0 0;`;\n modal.appendChild(img);\n }\n }\n\n const content = document.createElement('div');\n content.style.cssText = `padding: ${st.padY}px ${st.padX}px;`;\n\n const title = document.createElement('h2');\n title.textContent = campaign.title;\n title.style.cssText = `margin: 0 0 12px 0; font-size: 20px; font-weight: ${st.weight}; color: ${st.text};`;\n content.appendChild(title);\n\n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = `margin: 0 0 20px 0; font-size: 15px; line-height: 1.5; color: ${st.text}; opacity: 0.9;`;\n content.appendChild(body);\n\n const actions = document.createElement('div');\n actions.style.cssText = 'display: flex; gap: 12px; justify-content: flex-end; align-items: center;';\n\n // Optional secondary CTA (\"No thanks\") before the primary.\n const secondaryText = ic.modal_secondary_text as string | undefined;\n if (secondaryText) {\n const secondaryUrl = ic.modal_secondary_url as string | undefined;\n const sec = document.createElement('button');\n sec.type = 'button';\n sec.textContent = secondaryText;\n sec.style.cssText = `background: transparent; border: 1px solid ${st.text}55; color: ${st.text}; opacity: 0.8; padding: 10px 24px; border-radius: ${st.btnRadius}px; font-weight: 600; cursor: pointer; font-size: 14px;`;\n sec.addEventListener('click', () => {\n if (secondaryUrl) {\n this.trackEvent(campaign.id, 'clicked', { meta: { action: 'secondary' } });\n const safe = this.sanitizeUrl(secondaryUrl);\n if (safe) { this.navigateToCampaignAction(campaign, safe); this.removeModal(overlay); return; }\n }\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n actions.appendChild(sec);\n }\n\n // Primary CTA — render whenever it's LABELLED (was hidden unless BOTH\n // button_text AND action_url were set). No URL → just dismiss.\n const ctaLabel = campaign.button_text || (campaign.action_url ? 'Continue' : '');\n if (ctaLabel) {\n const ctaButton = document.createElement('button');\n ctaButton.className = 'aegis-cta';\n ctaButton.textContent = ctaLabel;\n ctaButton.style.cssText = `\n background: ${st.btnBg};\n color: ${st.btnText};\n border: none;\n padding: 10px 24px;\n border-radius: ${st.btnRadius}px;\n font-weight: 600;\n cursor: pointer;\n font-size: 14px;\n `;\n\n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (campaign.action_url) {\n const safeUrl = this.sanitizeUrl(campaign.action_url);\n if (safeUrl) this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n\n actions.appendChild(ctaButton);\n }\n\n const closeButton = document.createElement('button');\n closeButton.textContent = 'Close';\n closeButton.style.cssText = `\n background: transparent;\n border: 1px solid ${st.text}55;\n color: ${st.text};\n opacity: 0.8;\n padding: 10px 24px;\n border-radius: ${st.btnRadius}px;\n font-weight: 600;\n cursor: pointer;\n font-size: 14px;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n \n actions.appendChild(closeButton);\n\n content.appendChild(actions);\n modal.appendChild(content);\n // Canonical display-size — wrap the card so the operator's size choice scales\n // the modal (no-op at medium). Backdrop-dismiss still keys on the overlay.\n overlay.appendChild(this._wrapScaled(modal));\n\n // Dismiss on backdrop click (standard modal UX; default on). A click that\n // lands on the overlay itself — not the modal card — closes it.\n if (ic.modal_dismiss_on_overlay !== false) {\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n }\n });\n }\n\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n \n // Build a muted-autoplay-loop video with a tap-to-unmute pill (shared by the\n // pip + full-screen heroes). Returns the <video> (or null if no safe URL).\n private _heroVideo(\n url: string,\n poster: string | undefined,\n coverHost: HTMLElement,\n pillCorner: 'top-left' | 'bottom-left',\n ): HTMLVideoElement | null {\n const safe = this.sanitizeUrl(url);\n if (!safe) return null;\n const v = document.createElement('video');\n v.src = safe;\n v.muted = true; v.loop = true; v.autoplay = true; v.playsInline = true;\n v.setAttribute('playsinline', ''); v.setAttribute('webkit-playsinline', '');\n v.controls = false;\n if (poster) { const p = this.sanitizeUrl(poster); if (p) v.poster = p; }\n v.style.cssText = 'position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; background: #000;';\n coverHost.appendChild(v);\n const pill = document.createElement('button');\n pill.type = 'button';\n const sync = () => {\n pill.replaceChildren();\n const ic = document.createElement('span'); ic.style.fontSize = '13px'; ic.innerHTML = lucideSvg(v.muted ? 'volume-x' : 'volume-2', { size: 13 });\n pill.appendChild(ic);\n if (v.muted) { const t = document.createElement('span'); t.textContent = 'Tap for sound'; pill.appendChild(t); }\n };\n pill.style.cssText =\n `position: absolute; ${pillCorner === 'top-left' ? 'top: 16px; left: 16px;' : 'bottom: 16px; left: 16px;'} ` +\n 'display: inline-flex; align-items: center; gap: 6px; background: rgba(0,0,0,0.6); color: #fff; border: none; ' +\n 'border-radius: 999px; padding: 6px 11px; font-size: 11.5px; font-weight: 700; cursor: pointer; z-index: 3; backdrop-filter: blur(3px);';\n pill.setAttribute('aria-label', 'Toggle sound');\n pill.addEventListener('click', (e) => { e.stopPropagation(); v.muted = !v.muted; if (!v.muted && v.paused) void v.play().catch(() => {}); sync(); });\n v.addEventListener('volumechange', sync);\n sync();\n coverHost.appendChild(pill);\n void v.play().catch(() => {});\n return v;\n }\n\n private renderFullScreen(campaign: InAppCampaign): void {\n const st = this.inAppStyle(campaign);\n const ic = (campaign.interactive_config as Record<string, unknown> | undefined) || {};\n const videoUrl = campaign.video_url || (ic.video_url as string | undefined);\n const hasMedia = !!(videoUrl || campaign.image_url);\n // Full-bleed background when there's media (the modern takeover look),\n // centered text otherwise. Operator can force either via fullscreen_layout.\n const layout = (ic.fullscreen_layout as string) || (hasMedia ? 'background' : 'centered');\n const isBg = layout === 'background' && hasMedia;\n const contentColor = isBg ? '#ffffff' : st.text;\n\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-fullscreen-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n overlay.style.cssText = `\n position: fixed; inset: 0; background: ${st.bg}; color: ${st.text};\n z-index: 1000001; display: flex; flex-direction: column; align-items: center;\n justify-content: ${isBg ? 'flex-end' : 'center'}; padding: ${isBg ? '0' : '40px 20px'};\n animation: aegisFadeIn 0.3s ease-out; overflow: hidden;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n\n if (isBg) {\n // ── Full-bleed media + gradient scrim ──\n const media = document.createElement('div');\n media.style.cssText = 'position: absolute; inset: 0; z-index: 0; background: #000;';\n if (videoUrl) {\n this._heroVideo(videoUrl, campaign.image_url, media, 'top-left');\n } else if (campaign.image_url) {\n const safe = this.sanitizeUrl(campaign.image_url);\n if (safe) {\n const img = document.createElement('img');\n img.src = safe; img.alt = '';\n img.style.cssText = 'position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;';\n media.appendChild(img);\n }\n }\n const scrim = document.createElement('div');\n scrim.style.cssText = 'position: absolute; inset: 0; z-index: 1; background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.35) 42%, rgba(0,0,0,0.05) 100%);';\n media.appendChild(scrim);\n overlay.appendChild(media);\n } else if (videoUrl) {\n // Centered layout with a contained video (was authored-but-unrendered).\n const wrap = document.createElement('div');\n wrap.style.cssText = 'position: relative; width: min(100%, 560px); aspect-ratio: 16/9; margin-bottom: 32px; border-radius: 14px; overflow: hidden; background: #000;';\n this._heroVideo(videoUrl, campaign.image_url, wrap, 'bottom-left');\n overlay.appendChild(wrap);\n } else if (campaign.image_url) {\n const safe = this.sanitizeUrl(campaign.image_url);\n if (safe) {\n const img = document.createElement('img');\n img.src = safe; img.alt = '';\n img.style.cssText = 'max-width: 100%; max-height: 40vh; object-fit: contain; margin-bottom: 32px;';\n overlay.appendChild(img);\n }\n }\n\n // ── Content ──\n const contentContainer = document.createElement('div');\n contentContainer.style.cssText = isBg\n ? `position: relative; z-index: 2; max-width: 640px; width: 100%; box-sizing: border-box; text-align: center; padding: 0 24px 52px; color: ${contentColor}; animation: aegisFadeIn 0.45s ease-out 0.1s both;`\n : 'max-width: 600px; text-align: center;';\n\n const title = document.createElement('h1');\n title.textContent = campaign.title;\n title.style.cssText = `margin: 0 0 16px 0; font-size: ${isBg ? '30px' : '32px'}; font-weight: ${st.weight}; color: ${contentColor};` + (isBg ? ' text-shadow: 0 2px 12px rgba(0,0,0,0.4);' : '');\n contentContainer.appendChild(title);\n\n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = `margin: 0 0 28px 0; font-size: ${isBg ? '16px' : '18px'}; line-height: 1.6; opacity: 0.92; color: ${contentColor};`;\n contentContainer.appendChild(body);\n\n // Render the primary CTA whenever it's LABELLED — don't require an\n // action_url (a labelled button with no URL just dismisses). Requiring both\n // silently hid the button, so a takeover with copy but no chosen link showed\n // no CTA at all. Fall back to a sensible label if only the URL is set.\n const fsCtaLabel = campaign.button_text || (campaign.action_url ? 'Continue' : '');\n if (fsCtaLabel) {\n const ctaButton = document.createElement('button');\n ctaButton.className = 'aegis-cta';\n ctaButton.textContent = fsCtaLabel;\n ctaButton.style.cssText = `\n background: ${st.btnBg}; color: ${st.btnText}; border: none;\n padding: 16px 48px; border-radius: ${st.btnRadius}px; font-weight: 700;\n font-size: 18px; cursor: pointer; display: block; margin: 0 auto 12px;\n `;\n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (campaign.action_url) {\n const safeUrl = this.sanitizeUrl(campaign.action_url);\n if (safeUrl) this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n contentContainer.appendChild(ctaButton);\n }\n\n // Secondary CTA / dismiss link — a subtle second action (\"No thanks\").\n const secondaryText = ic.fullscreen_secondary_text as string | undefined;\n if (secondaryText) {\n const secondaryUrl = ic.fullscreen_secondary_url as string | undefined;\n const sec = document.createElement('button');\n sec.type = 'button';\n sec.textContent = secondaryText;\n sec.style.cssText = `background: transparent; border: none; color: ${contentColor}; opacity: 0.75; font-size: 14px; font-weight: 600; cursor: pointer; padding: 8px; display: block; margin: 0 auto; text-decoration: underline;`;\n sec.addEventListener('click', () => {\n if (secondaryUrl) {\n this.trackEvent(campaign.id, 'clicked', { meta: { action: 'secondary' } });\n const safe = this.sanitizeUrl(secondaryUrl);\n if (safe) { this.navigateToCampaignAction(campaign, safe); this.removeModal(overlay); return; }\n }\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n contentContainer.appendChild(sec);\n }\n\n overlay.appendChild(contentContainer);\n\n const closeButton = document.createElement('button');\n closeButton.innerHTML = lucideSvg('x', { size: isBg ? 18 : 28 });\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute; top: 20px; right: 20px; z-index: 3; background: ${isBg ? 'rgba(0,0,0,0.45)' : 'transparent'};\n border: none; color: ${isBg ? '#fff' : 'inherit'}; font-size: ${isBg ? '20px' : '32px'}; cursor: pointer;\n padding: 0; width: ${isBg ? '36px' : '48px'}; height: ${isBg ? '36px' : '48px'}; border-radius: 50%;\n display: flex; align-items: center; justify-content: center; opacity: ${isBg ? '0.9' : '0.6'};\n `;\n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n overlay.appendChild(closeButton);\n\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n \n private renderHalfInterstitial(campaign: InAppCampaign): void {\n const st = this.inAppStyle(campaign);\n const ic = (campaign.interactive_config as Record<string, unknown> | undefined) || {};\n const videoUrl = campaign.video_url || (ic.video_url as string | undefined);\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-half-interstitial-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n\n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, ${st.overlay});\n z-index: 1000000;\n display: flex;\n align-items: flex-end;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n\n const modal = document.createElement('div');\n modal.className = 'aegis-in-app-half-interstitial';\n modal.style.cssText = `\n background: ${st.bg};\n color: ${st.text};\n border-radius: ${st.radius}px ${st.radius}px 0 0;\n width: 100%;\n max-width: 600px;\n max-height: 60vh;\n overflow: auto;\n box-shadow: 0 -4px 20px rgba(0,0,0,0.2);\n animation: aegisSlideUp 0.3s ease-out;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n\n // Grabber — the bottom-sheet drag affordance (was missing; sheets read as\n // half-finished without it).\n const grabber = document.createElement('div');\n grabber.style.cssText = `width: 40px; height: 4px; border-radius: 2px; background: ${st.text}33; margin: 10px auto 0;`;\n modal.appendChild(grabber);\n\n // Hero media — a VIDEO (was image-only: video_url was ignored) or an image.\n if (videoUrl) {\n const mediaWrap = document.createElement('div');\n mediaWrap.style.cssText = `position: relative; width: 100%; height: ${st.mediaHeight}px; overflow: hidden; margin-top: 10px; background: #000;`;\n this._heroVideo(videoUrl, campaign.image_url, mediaWrap, 'bottom-left');\n modal.appendChild(mediaWrap);\n } else if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = `width: 100%; height: ${st.mediaHeight}px; object-fit: cover; margin-top: 10px;`;\n modal.appendChild(img);\n }\n }\n\n const content = document.createElement('div');\n content.style.cssText = `padding: ${st.padY}px ${st.padX}px;`;\n\n const title = document.createElement('h2');\n title.textContent = campaign.title;\n title.style.cssText = `margin: 0 0 12px 0; font-size: 24px; font-weight: ${st.weight}; color: ${st.text};`;\n content.appendChild(title);\n\n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = `margin: 0 0 24px 0; font-size: 16px; line-height: 1.5; color: ${st.text}; opacity: 0.9;`;\n content.appendChild(body);\n\n // Primary CTA — render whenever LABELLED (was hidden unless BOTH set).\n const halfCtaLabel = campaign.button_text || (campaign.action_url ? 'Continue' : '');\n if (halfCtaLabel) {\n const ctaButton = document.createElement('button');\n ctaButton.className = 'aegis-cta';\n ctaButton.textContent = halfCtaLabel;\n ctaButton.style.cssText = `\n background: ${st.btnBg};\n color: ${st.btnText};\n border: none;\n padding: 14px 32px;\n border-radius: ${st.btnRadius}px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n width: 100%;\n `;\n\n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (campaign.action_url) {\n const safeUrl = this.sanitizeUrl(campaign.action_url);\n if (safeUrl) this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n\n content.appendChild(ctaButton);\n }\n\n // Optional secondary CTA / dismiss link.\n const secondaryText = ic.half_secondary_text as string | undefined;\n if (secondaryText) {\n const secondaryUrl = ic.half_secondary_url as string | undefined;\n const sec = document.createElement('button');\n sec.type = 'button';\n sec.textContent = secondaryText;\n sec.style.cssText = `margin-top: 10px; background: transparent; border: none; color: ${st.text}; opacity: 0.75; font-weight: 600; font-size: 14px; cursor: pointer; width: 100%; text-decoration: underline;`;\n sec.addEventListener('click', () => {\n if (secondaryUrl) {\n this.trackEvent(campaign.id, 'clicked', { meta: { action: 'secondary' } });\n const safe = this.sanitizeUrl(secondaryUrl);\n if (safe) { this.navigateToCampaignAction(campaign, safe); this.removeModal(overlay); return; }\n }\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n content.appendChild(sec);\n }\n\n modal.appendChild(content);\n \n const closeButton = document.createElement('button');\n closeButton.innerHTML = lucideSvg('x', { size: 22 });\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: rgba(255,255,255,0.9);\n border: none;\n color: #333;\n font-size: 24px;\n cursor: pointer;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n \n modal.style.position = 'relative';\n modal.appendChild(closeButton);\n\n overlay.appendChild(modal);\n\n // Dismiss on backdrop click (default on) — standard bottom-sheet behaviour.\n if (ic.half_dismiss_on_overlay !== false) {\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n }\n });\n }\n\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n private renderAlert(campaign: InAppCampaign): void {\n const st = this.inAppStyle(campaign);\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-alert-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n\n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, ${st.overlay});\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.2s ease-out;\n `;\n\n const alert = document.createElement('div');\n alert.className = 'aegis-in-app-alert';\n alert.style.cssText = `\n background: ${st.bg};\n color: ${st.text};\n border-radius: ${st.radius}px;\n max-width: 320px;\n width: 85%;\n box-shadow: 0 16px 48px rgba(0,0,0,0.22);\n animation: aegisScaleIn 0.2s ease-out;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n overflow: hidden;\n `;\n\n const content = document.createElement('div');\n content.style.cssText = `padding: ${st.padY}px ${st.padX}px; text-align: center;`;\n\n // Tone icon badge — sets the emotional register of the alert at a glance.\n const ic = (campaign.interactive_config as Record<string, unknown> | undefined) || {};\n const variant = (ic.alert_variant as string) || '';\n const VARIANTS: Record<string, { icon: string; color: string }> = {\n info: { icon: 'info', color: '#3b82f6' },\n success: { icon: 'check', color: '#10b981' },\n warning: { icon: 'triangle-alert', color: '#f59e0b' },\n error: { icon: 'x', color: '#ef4444' },\n };\n const vspec = VARIANTS[variant];\n if (vspec) {\n const badge = document.createElement('div');\n badge.style.cssText = `width: 48px; height: 48px; border-radius: 50%; margin: 0 auto 12px; display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: 800; background: ${vspec.color}22; color: ${vspec.color};`;\n badge.innerHTML = lucideSvg(vspec.icon, { size: 24 });\n content.appendChild(badge);\n }\n\n const title = document.createElement('h3');\n title.textContent = campaign.title;\n title.style.cssText = `margin: 0 0 8px 0; font-size: 18px; font-weight: ${st.weight}; color: ${st.text};`;\n content.appendChild(title);\n\n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = `margin: 0; font-size: 14px; line-height: 1.4; color: ${st.text}; opacity: 0.85;`;\n content.appendChild(body);\n\n alert.appendChild(content);\n\n const buttonContainer = document.createElement('div');\n buttonContainer.style.cssText = `display: flex; gap: 8px; justify-content: center; padding: 0 ${st.padX}px ${st.padY}px;`;\n\n const alertCtaLabel = campaign.button_text || (campaign.action_url ? 'OK' : '');\n if (alertCtaLabel) {\n const ctaButton = document.createElement('button');\n ctaButton.className = 'aegis-cta';\n ctaButton.textContent = alertCtaLabel;\n ctaButton.style.cssText = `\n flex: 1;\n background: ${st.btnBg};\n color: ${st.btnText};\n border: none;\n border-radius: ${st.btnRadius}px;\n padding: 12px;\n font-weight: 600;\n font-size: 15px;\n cursor: pointer;\n `;\n\n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (campaign.action_url) {\n const safeUrl = this.sanitizeUrl(campaign.action_url);\n if (safeUrl) this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n\n buttonContainer.appendChild(ctaButton);\n }\n\n const cancelButton = document.createElement('button');\n cancelButton.textContent = 'Cancel';\n cancelButton.style.cssText = `\n flex: 1;\n background: transparent;\n border: 1px solid ${st.text}55;\n color: ${st.text};\n opacity: 0.85;\n border-radius: ${st.btnRadius}px;\n padding: 12px;\n font-weight: 600;\n font-size: 15px;\n cursor: pointer;\n `;\n \n cancelButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n \n buttonContainer.appendChild(cancelButton);\n \n alert.appendChild(buttonContainer);\n overlay.appendChild(alert);\n \n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n \n // Corner → fixed-position CSS for the floating PiP card.\n private _pipCornerCss(position: string, gap: number): string {\n switch (position) {\n case 'bottom-left':\n return `bottom: ${gap}px; left: ${gap}px;`;\n case 'top-right':\n return `top: ${gap}px; right: ${gap}px;`;\n case 'top-left':\n return `top: ${gap}px; left: ${gap}px;`;\n case 'bottom-right':\n default:\n return `bottom: ${gap}px; right: ${gap}px;`;\n }\n }\n\n private renderPIP(campaign: InAppCampaign): void {\n const ic = (campaign.interactive_config as Record<string, unknown> | undefined) || {};\n // Authored options — every one is additive; a legacy pip (none set) keeps\n // the old bottom-right, muted-autoplay, looping behaviour.\n const position = (ic.pip_position as string) || 'bottom-right';\n const size = (ic.pip_size as string) || 'standard';\n const autoplay = ic.pip_autoplay !== false; // default ON\n const loop = ic.pip_loop !== false; // default ON\n const startMuted = ic.pip_muted !== false; // default ON (browser autoplay needs it)\n const expandable = ic.pip_expandable !== false; // default ON\n const draggable = ic.pip_draggable === true; // default OFF\n\n const SIZE_W: Record<string, number> = { compact: 250, standard: 320, large: 400 };\n const gap = 20;\n const vw = typeof window !== 'undefined' ? window.innerWidth || 360 : 360;\n const cornerW = Math.min(SIZE_W[size] ?? 320, vw - gap * 2);\n\n const pip = document.createElement('div');\n pip.className = 'aegis-in-app-pip';\n pip.setAttribute('data-campaign-id', campaign.id);\n const cornerCss = this._pipCornerCss(position, gap);\n const slideAnim = position.startsWith('top') ? 'aegisSlideDown' : 'aegisSlideUp';\n pip.style.cssText = `\n position: fixed; ${cornerCss} width: ${cornerW}px; max-width: calc(100vw - ${gap * 2}px);\n background: #000; border-radius: 14px; overflow: hidden;\n box-shadow: 0 10px 30px rgba(0,0,0,0.38); z-index: 999999;\n animation: ${slideAnim} 0.3s ease-out; transition: width 0.25s ease, border-radius 0.25s ease;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n\n // ── Media (16:9) ──────────────────────────────────────────────────────\n const mediaWrap = document.createElement('div');\n mediaWrap.style.cssText = 'position: relative; width: 100%; background: #000; line-height: 0; aspect-ratio: 16 / 9;';\n\n const videoUrl =\n campaign.video_url || (ic.video_url as string | undefined);\n let video: HTMLVideoElement | null = null;\n let hasMedia = false;\n\n if (videoUrl) {\n const safe = this.sanitizeUrl(videoUrl);\n if (safe) {\n video = document.createElement('video');\n video.src = safe;\n video.muted = startMuted;\n video.loop = loop;\n video.autoplay = autoplay;\n video.playsInline = true;\n video.setAttribute('playsinline', '');\n video.setAttribute('webkit-playsinline', '');\n video.controls = false;\n if (campaign.image_url) {\n const poster = this.sanitizeUrl(campaign.image_url);\n if (poster) video.poster = poster;\n }\n video.style.cssText = 'width: 100%; height: 100%; display: block; object-fit: cover; background: #000;';\n mediaWrap.appendChild(video);\n hasMedia = true;\n }\n } else if (campaign.image_url) {\n const safe = this.sanitizeUrl(campaign.image_url);\n if (safe) {\n const img = document.createElement('img');\n img.src = safe;\n img.alt = '';\n img.style.cssText = 'width: 100%; height: 100%; display: block; object-fit: cover;';\n mediaWrap.appendChild(img);\n hasMedia = true;\n }\n }\n\n if (!hasMedia) {\n const placeholder = document.createElement('div');\n placeholder.style.cssText =\n 'width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #1f2937, #0f172a); color: rgba(255,255,255,0.35); font-size: 34px;';\n placeholder.innerHTML = lucideSvg('play', { size: 34 });\n mediaWrap.appendChild(placeholder);\n }\n pip.appendChild(mediaWrap);\n\n // ── Video controls: tap-to-play/pause + unmute pill ───────────────────\n if (video) {\n const v = video;\n // Center play/pause glyph that fades after each toggle.\n const playGlyph = document.createElement('button');\n playGlyph.type = 'button';\n playGlyph.setAttribute('aria-label', 'Play or pause');\n playGlyph.style.cssText =\n 'position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 52px; height: 52px; border-radius: 50%; border: none; background: rgba(0,0,0,0.45); color: #fff; font-size: 22px; cursor: pointer; display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.25s ease; backdrop-filter: blur(2px);';\n const syncGlyph = () => {\n playGlyph.innerHTML = lucideSvg(v.paused ? 'play' : 'pause', { size: 22 });\n playGlyph.style.opacity = v.paused ? '1' : '0';\n };\n const togglePlay = () => {\n if (v.paused) void v.play().catch(() => {});\n else v.pause();\n };\n playGlyph.addEventListener('click', (e) => { e.stopPropagation(); togglePlay(); });\n mediaWrap.addEventListener('click', togglePlay);\n v.addEventListener('play', () => { syncGlyph(); setTimeout(() => { if (!v.paused) playGlyph.style.opacity = '0'; }, 600); });\n v.addEventListener('pause', syncGlyph);\n mediaWrap.appendChild(playGlyph);\n\n // Unmute pill — autoplay must start muted, so the sound affordance is the\n // single most important PiP interaction. Prominent when muted.\n const mutePill = document.createElement('button');\n mutePill.type = 'button';\n const syncMute = () => {\n mutePill.replaceChildren();\n const ic = document.createElement('span'); ic.style.fontSize = '13px'; ic.innerHTML = lucideSvg(v.muted ? 'volume-x' : 'volume-2', { size: 13 });\n mutePill.appendChild(ic);\n if (v.muted) { const t = document.createElement('span'); t.textContent = 'Tap for sound'; mutePill.appendChild(t); }\n mutePill.style.padding = v.muted ? '6px 11px' : '6px 9px';\n };\n mutePill.style.cssText =\n 'position: absolute; left: 10px; bottom: 10px; display: inline-flex; align-items: center; gap: 6px; background: rgba(0,0,0,0.6); color: #fff; border: none; border-radius: 999px; font-size: 11.5px; font-weight: 700; cursor: pointer; z-index: 3; backdrop-filter: blur(3px);';\n mutePill.setAttribute('aria-label', 'Toggle sound');\n mutePill.addEventListener('click', (e) => {\n e.stopPropagation();\n v.muted = !v.muted;\n if (!v.muted && v.paused) void v.play().catch(() => {});\n syncMute();\n });\n v.addEventListener('volumechange', syncMute);\n syncMute();\n mediaWrap.appendChild(mutePill);\n\n if (autoplay) void v.play().catch(() => {});\n }\n\n // ── Text + CTA overlay ────────────────────────────────────────────────\n const st = this.inAppStyle(campaign);\n const accent = st.btnBg;\n const accentText = st.btnText;\n const overlay = document.createElement('div');\n overlay.style.cssText =\n 'position: absolute; bottom: 0; left: 0; right: 0; background: linear-gradient(to top, rgba(0,0,0,0.92), rgba(0,0,0,0.2) 70%, transparent); padding: 36px 14px 13px; color: #fff; pointer-events: none;';\n\n if (campaign.title) {\n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-size: 14px; font-weight: 700; margin-bottom: 3px; line-height: 1.25;';\n overlay.appendChild(title);\n }\n if (campaign.body) {\n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 12px; opacity: 0.9; line-height: 1.35;';\n overlay.appendChild(body);\n }\n\n // Real CTA button (was a whole-card click → easy mis-tap on the close X).\n if (campaign.action_url && campaign.button_text) {\n const cta = document.createElement('button');\n cta.type = 'button';\n cta.textContent = campaign.button_text;\n cta.style.cssText = `pointer-events: auto; margin-top: 10px; padding: 8px 18px; border-radius: 999px; border: none; background: ${accent}; color: ${accentText}; font-weight: 800; font-size: 12.5px; cursor: pointer;`;\n cta.addEventListener('click', (e) => {\n e.stopPropagation();\n this.trackEvent(campaign.id, 'clicked');\n const safe = this.sanitizeUrl(campaign.action_url!);\n if (safe) this.navigateToCampaignAction(campaign, safe);\n });\n overlay.appendChild(cta);\n }\n pip.appendChild(overlay);\n\n // ── Top-right controls: expand + close ────────────────────────────────\n const controls = document.createElement('div');\n controls.style.cssText = 'position: absolute; top: 8px; right: 8px; display: flex; gap: 6px; z-index: 4;';\n\n const ctrlBtnCss =\n 'background: rgba(0,0,0,0.6); border: none; color: #fff; font-size: 14px; cursor: pointer; width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(3px);';\n\n let expanded = false;\n let backdrop: HTMLElement | null = null;\n let expandBtn: HTMLButtonElement | null = null;\n const collapse = () => {\n expanded = false;\n if (backdrop) { backdrop.remove(); backdrop = null; }\n pip.style.position = 'fixed';\n pip.style.cssText = `\n position: fixed; ${cornerCss} width: ${cornerW}px; max-width: calc(100vw - ${gap * 2}px);\n background: #000; border-radius: 14px; overflow: hidden;\n box-shadow: 0 10px 30px rgba(0,0,0,0.38); z-index: 999999; transition: none;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n if (expandBtn) { expandBtn.textContent = '⤢'; expandBtn.setAttribute('aria-label', 'Expand'); }\n };\n const expand = () => {\n expanded = true;\n this.trackEvent(campaign.id, 'impression', { meta: { action: 'pip_expand' } });\n backdrop = document.createElement('div');\n backdrop.style.cssText = 'position: fixed; inset: 0; background: rgba(0,0,0,0.72); z-index: 999998; animation: aegisFadeIn 0.2s ease-out;';\n backdrop.addEventListener('click', collapse);\n document.body.appendChild(backdrop);\n const theaterW = Math.min(760, vw - gap * 2);\n pip.style.cssText = `\n position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);\n width: ${theaterW}px; max-width: calc(100vw - ${gap * 2}px);\n background: #000; border-radius: 16px; overflow: hidden;\n box-shadow: 0 24px 60px rgba(0,0,0,0.55); z-index: 999999; transition: none;\n font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n if (expandBtn) { expandBtn.innerHTML = lucideSvg('x', { size: 16 }); expandBtn.setAttribute('aria-label', 'Minimize'); }\n if (video && video.muted) { video.muted = false; video.dispatchEvent(new Event('volumechange')); }\n };\n\n if (expandable && hasMedia) {\n expandBtn = document.createElement('button');\n expandBtn.type = 'button';\n expandBtn.textContent = '⤢';\n expandBtn.setAttribute('aria-label', 'Expand');\n expandBtn.style.cssText = ctrlBtnCss;\n expandBtn.addEventListener('click', (e) => { e.stopPropagation(); if (expanded) collapse(); else expand(); });\n controls.appendChild(expandBtn);\n }\n\n const closeButton = document.createElement('button');\n closeButton.type = 'button';\n closeButton.innerHTML = lucideSvg('x', { size: 16 });\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = ctrlBtnCss;\n closeButton.addEventListener('click', (e) => {\n e.stopPropagation();\n this.trackEvent(campaign.id, 'dismissed');\n if (backdrop) backdrop.remove();\n pip.style.animation = `${slideAnim === 'aegisSlideDown' ? 'aegisSlideUp' : 'aegisSlideDown'} 0.3s ease-out`;\n setTimeout(() => { if (pip.parentNode) pip.parentNode.removeChild(pip); }, 300);\n });\n controls.appendChild(closeButton);\n pip.appendChild(controls);\n\n // ── Optional drag-to-reposition (default off) ─────────────────────────\n if (draggable) {\n let dragging = false;\n let sx = 0, sy = 0, ox = 0, oy = 0;\n const onDown = (e: PointerEvent) => {\n if (expanded) return;\n const tgt = e.target as HTMLElement;\n if (tgt.closest('button')) return; // don't hijack control taps\n dragging = true;\n const rect = pip.getBoundingClientRect();\n ox = rect.left; oy = rect.top; sx = e.clientX; sy = e.clientY;\n pip.style.bottom = 'auto'; pip.style.right = 'auto';\n pip.style.left = `${ox}px`; pip.style.top = `${oy}px`;\n pip.setPointerCapture(e.pointerId);\n };\n const onMove = (e: PointerEvent) => {\n if (!dragging) return;\n const nx = Math.max(4, Math.min(vw - pip.offsetWidth - 4, ox + (e.clientX - sx)));\n const ny = Math.max(4, Math.min((window.innerHeight || 800) - pip.offsetHeight - 4, oy + (e.clientY - sy)));\n pip.style.left = `${nx}px`; pip.style.top = `${ny}px`;\n };\n const onUp = () => { dragging = false; };\n pip.addEventListener('pointerdown', onDown);\n pip.addEventListener('pointermove', onMove);\n pip.addEventListener('pointerup', onUp);\n pip.style.touchAction = 'none';\n }\n\n this.addAnimationStyles();\n document.body.appendChild(pip);\n this.trackEvent(campaign.id, 'impression');\n }\n \n private removeBanner(banner: HTMLElement): void {\n // Exit mirrors the entrance: bottom banner slides DOWN out, top banner UP.\n const atBottom = banner.getAttribute('data-banner-position') === 'bottom';\n banner.style.animation = `${atBottom ? 'aegisSlideOutToBottom' : 'aegisSlideUp'} 0.3s ease-out`;\n setTimeout(() => {\n if (banner.parentNode) {\n banner.parentNode.removeChild(banner);\n }\n }, 300);\n }\n \n private removeModal(overlay: HTMLElement): void {\n overlay.style.animation = 'aegisFadeOut 0.3s ease-out';\n setTimeout(() => {\n if (overlay.parentNode) {\n overlay.parentNode.removeChild(overlay);\n }\n }, 300);\n }\n \n /**\n * Spotlight backdrop colour → a dim of the chosen base at `dim` opacity.\n * `dark` (slate, default) · `brand` (the campaign brand colour) · `black` ·\n * `custom` (a picked hex). Encoded as 8-digit hex alpha; falls back to slate\n * rgba when the base isn't a 6-digit hex (e.g. an rgb() brand colour).\n */\n private _spotlightDimColor(ic: Record<string, unknown>, brand: string, dim: number): string {\n const mode = (ic.spotlight_dim_color as string) || 'dark';\n const base =\n mode === 'black' ? '#000000'\n : mode === 'brand' ? brand\n : mode === 'custom' ? this.sanitizeColor((ic.spotlight_dim_custom as string) || '#0f172a')\n : '#0f172a';\n const alpha = Math.round(Math.max(0, Math.min(1, dim)) * 255).toString(16).padStart(2, '0');\n return /^#[0-9a-fA-F]{6}$/.test(base) ? `${base}${alpha}` : `rgba(15,23,42,${dim})`;\n }\n\n private renderTooltip(campaign: InAppCampaign): void {\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const anchorSelector = (ic.tooltip_anchor_selector as string) || '[data-aegis-tooltip]';\n const preferredPosition = (ic.tooltip_position as string) || 'auto'; // auto | top | bottom | left | right\n const highlightOn = ic.tooltip_highlight !== false; // default ON — ring the target\n\n let anchor = document.querySelector(anchorSelector);\n if (!anchor) {\n // Silent-anchor footgun: the tooltip's target isn't on this surface, so\n // nothing renders. Without a signal the operator sees zero impressions\n // and assumes the campaign simply didn't fire. Emit an always-on dev\n // warning + a dedicated `anchor_unresolved` event so the dashboard can\n // flag the misconfiguration. (Mobile anchors are registered keys, so the\n // same selector can resolve on web yet be unregistered on a native app —\n // see registerInAppAnchor.)\n console.warn(\n `[AegisInApp] Tooltip anchor \"${anchorSelector}\" not found on this surface — tooltip not shown. ` +\n `On native apps this must be a key registered via registerInAppAnchor(); adding a new anchor needs an app release.`,\n );\n void this.trackEvent(campaign.id, 'anchor_unresolved', {\n meta: { anchor_resolved: false, anchor_selector: anchorSelector, widget_type: 'tooltip' },\n });\n return;\n }\n\n const bg = this.sanitizeColor(campaign.background_color || '#1a1a1a');\n const textColor = this.sanitizeColor(campaign.text_color || '#ffffff');\n\n // Tooltip container (positioned absolute to body)\n const tooltip = document.createElement('div');\n tooltip.className = 'aegis-in-app-tooltip';\n tooltip.setAttribute('data-campaign-id', campaign.id);\n tooltip.style.cssText = `\n position: absolute; z-index: 1000001; max-width: 280px; width: max-content;\n background: ${bg}; color: ${textColor}; border-radius: 10px; padding: 14px 16px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.18); font-family: 'Urbanist', system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.2s ease-out; pointer-events: auto;\n `;\n\n // Arrow\n const arrow = document.createElement('div');\n arrow.className = 'aegis-tooltip-arrow';\n arrow.style.cssText = `\n position: absolute; width: 10px; height: 10px; background: ${bg};\n transform: rotate(45deg);\n `;\n\n let highlightEl: HTMLElement | null = null;\n let outsideHandler: ((e: MouseEvent) => void) | null = null;\n // P5 — live re-resolution: scroll/resize listeners + observers are detached\n // here so a dismissed tooltip leaves nothing behind.\n let cleanupReflow: (() => void) | null = null;\n const dismiss = () => {\n tooltip.style.animation = 'aegisFadeOut 0.2s ease-out';\n if (highlightEl) { highlightEl.remove(); highlightEl = null; }\n if (outsideHandler) { document.removeEventListener('click', outsideHandler); outsideHandler = null; }\n if (cleanupReflow) { cleanupReflow(); cleanupReflow = null; }\n setTimeout(() => { tooltip.parentNode?.removeChild(tooltip); }, 200);\n this.trackEvent(campaign.id, 'dismissed');\n };\n\n // Close button \\u2014 a real hit area (was a ~9px glyph that was easy to miss).\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.textContent = '\\u00d7';\n closeBtn.setAttribute('aria-label', 'Close');\n closeBtn.style.cssText = `\n position: absolute; top: 4px; right: 4px; z-index: 1; background: none; border: none;\n color: ${textColor}; opacity: 0.7; font-size: 16px; line-height: 1; cursor: pointer;\n width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; padding: 0;\n `;\n closeBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); dismiss(); });\n\n // Title\n if (campaign.title) {\n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-size: 13px; font-weight: 700; margin-bottom: 4px; padding-right: 18px;';\n tooltip.appendChild(title);\n }\n\n // Body \\u2014 never blank (a tooltip with no copy reads as broken).\n const body = document.createElement('div');\n body.textContent = campaign.body || 'Here\\u2019s a quick tip.';\n body.style.cssText = 'font-size: 12px; line-height: 1.4; opacity: 0.9; padding-right: 18px;';\n tooltip.appendChild(body);\n\n // CTA button\n if (campaign.button_text && campaign.action_url) {\n const cta = document.createElement('a');\n cta.textContent = campaign.button_text;\n cta.href = campaign.action_url;\n cta.style.cssText = `\n display: inline-block; margin-top: 8px; font-size: 12px; font-weight: 600;\n color: ${textColor}; text-decoration: underline; cursor: pointer;\n `;\n cta.addEventListener('click', () => { this.trackEvent(campaign.id, 'clicked'); });\n tooltip.appendChild(cta);\n }\n\n tooltip.appendChild(closeBtn);\n tooltip.appendChild(arrow);\n document.body.appendChild(tooltip);\n\n // Highlight the target — a soft pulsing ring (the point of a tooltip is to\n // draw the eye to a specific element). Created once; repositioned live.\n // Spotlight (masked cutout) vs the legacy light ring. Spotlight dims the\n // whole screen except a transparent hole around the anchor.\n const spotlight = (ic.highlight_style as string) === 'spotlight';\n const spotPad = typeof ic.spotlight_padding === 'number' ? (ic.spotlight_padding as number) : (spotlight ? 6 : 4);\n const spotRadius = (ic.spotlight_shape as string) === 'circle' ? '50%' : '12px';\n const spotDim = typeof ic.spotlight_dim === 'number' ? Math.max(0, Math.min(95, ic.spotlight_dim as number)) / 100 : 0.64;\n // Backdrop colour: dark slate (default) · brand accent (the tooltip bg) ·\n // black · custom hex. Applied at the dim opacity via an 8-digit hex alpha.\n const dimColor = this._spotlightDimColor(ic, bg, spotDim);\n if (highlightOn) {\n highlightEl = document.createElement('div');\n highlightEl.className = 'aegis-tooltip-highlight';\n const shadow = spotlight\n ? `box-shadow: 0 0 0 3px rgba(255,255,255,0.92), 0 0 0 6px ${bg}66, 0 0 0 9999px ${dimColor};`\n : `box-shadow: 0 0 0 3px ${bg}55, 0 0 0 9999px rgba(0,0,0,0.04);`;\n // The opacity pulse is fine for the faint RING, but it would make the\n // spotlight's strong dim flicker/halve (it reads as broken) — so the\n // spotlight gets a STEADY dim (standard for masked spotlights).\n const anim = spotlight ? '' : 'animation: aegisTooltipPulse 1.6s ease-in-out infinite;';\n highlightEl.style.cssText =\n `position: absolute; z-index: 1000000; pointer-events: none; border-radius: ${spotlight ? spotRadius : '8px'}; ` +\n `${shadow} ${anim}`;\n document.body.appendChild(highlightEl);\n }\n\n // P5 — extra gap from interactive_config (`tooltip_offset`).\n const gap = 10 + (typeof ic.tooltip_offset === 'number' ? (ic.tooltip_offset as number) : 0);\n const OPP: Record<string, string> = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' };\n\n // P5 — position relative to the (live) anchor. Re-run on scroll / resize /\n // layout shifts so the tooltip tracks the element instead of going stale.\n const reposition = (): void => {\n if (!anchor) return;\n const anchorRect = anchor.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n const scrollX = window.scrollX, scrollY = window.scrollY;\n const vw = window.innerWidth, vh = window.innerHeight;\n if (highlightEl) {\n highlightEl.style.top = `${anchorRect.top + scrollY - spotPad}px`;\n highlightEl.style.left = `${anchorRect.left + scrollX - spotPad}px`;\n highlightEl.style.width = `${anchorRect.width + spotPad * 2}px`;\n highlightEl.style.height = `${anchorRect.height + spotPad * 2}px`;\n }\n const room = { bottom: vh - anchorRect.bottom, top: anchorRect.top, right: vw - anchorRect.right, left: anchorRect.left };\n const needV = tooltipRect.height + gap, needH = tooltipRect.width + gap;\n const fits = (s: string): boolean =>\n s === 'top' ? room.top >= needV : s === 'bottom' ? room.bottom >= needV\n : s === 'left' ? room.left >= needH : room.right >= needH;\n let side = preferredPosition;\n if (side === 'auto') {\n side = room.bottom >= needV ? 'bottom'\n : room.top >= needV ? 'top'\n : room.right >= needH ? 'right'\n : room.left >= needH ? 'left'\n : (room.bottom >= room.top ? 'bottom' : 'top');\n } else if (!fits(side) && fits(OPP[side])) {\n side = OPP[side];\n }\n let top = 0, left = 0;\n let arrowTop = '', arrowLeft = '', arrowBottom = '', arrowRight = '';\n switch (side) {\n case 'top':\n top = anchorRect.top + scrollY - tooltipRect.height - gap;\n left = anchorRect.left + scrollX + (anchorRect.width - tooltipRect.width) / 2;\n arrowBottom = '-5px'; arrowLeft = `${tooltipRect.width / 2 - 5}px`;\n break;\n case 'left':\n top = anchorRect.top + scrollY + (anchorRect.height - tooltipRect.height) / 2;\n left = anchorRect.left + scrollX - tooltipRect.width - gap;\n arrowRight = '-5px'; arrowTop = `${tooltipRect.height / 2 - 5}px`;\n break;\n case 'right':\n top = anchorRect.top + scrollY + (anchorRect.height - tooltipRect.height) / 2;\n left = anchorRect.right + scrollX + gap;\n arrowLeft = '-5px'; arrowTop = `${tooltipRect.height / 2 - 5}px`;\n break;\n default: // bottom\n top = anchorRect.bottom + scrollY + gap;\n left = anchorRect.left + scrollX + (anchorRect.width - tooltipRect.width) / 2;\n arrowTop = '-5px'; arrowLeft = `${tooltipRect.width / 2 - 5}px`;\n break;\n }\n left = Math.max(8, Math.min(left, vw + scrollX - tooltipRect.width - 8));\n top = Math.max(8 + scrollY, Math.min(top, vh + scrollY - tooltipRect.height - 8));\n tooltip.style.top = `${top}px`;\n tooltip.style.left = `${left}px`;\n arrow.style.top = arrowTop || '';\n arrow.style.left = arrowLeft || '';\n arrow.style.bottom = arrowBottom || '';\n arrow.style.right = arrowRight || '';\n };\n\n // P5 — runtime re-resolution: if the anchor leaves the DOM (SPA re-render),\n // re-query the selector; if it's truly gone, signal `anchor_unresolved` and\n // dismiss instead of leaving a tooltip pointing at nothing.\n const ensureAnchor = (): boolean => {\n if (anchor && anchor.isConnected) return true;\n const next = document.querySelector(anchorSelector);\n if (next) { anchor = next; return true; }\n void this.trackEvent(campaign.id, 'anchor_unresolved', {\n meta: { anchor_resolved: false, anchor_selector: anchorSelector, widget_type: 'tooltip', phase: 'runtime' },\n });\n dismiss();\n return false;\n };\n\n // rAF-debounced so a burst of mutations/scroll events coalesces to one paint.\n let rafId = 0;\n const onReflow = (): void => {\n if (rafId) return;\n rafId = window.requestAnimationFrame(() => {\n rafId = 0;\n if (ensureAnchor()) reposition();\n });\n };\n\n reposition();\n window.addEventListener('scroll', onReflow, { passive: true, capture: true });\n window.addEventListener('resize', onReflow, { passive: true });\n let ro: ResizeObserver | null = null;\n let mo: MutationObserver | null = null;\n if (typeof ResizeObserver !== 'undefined') {\n ro = new ResizeObserver(onReflow);\n if (anchor) ro.observe(anchor);\n ro.observe(document.documentElement);\n }\n if (typeof MutationObserver !== 'undefined') {\n mo = new MutationObserver(onReflow);\n mo.observe(document.body, { childList: true, subtree: true, attributes: true });\n }\n cleanupReflow = () => {\n if (rafId) { window.cancelAnimationFrame(rafId); rafId = 0; }\n window.removeEventListener('scroll', onReflow, { capture: true } as EventListenerOptions);\n window.removeEventListener('resize', onReflow);\n if (ro) { ro.disconnect(); ro = null; }\n if (mo) { mo.disconnect(); mo = null; }\n };\n\n // Dismiss on outside click (handler tracked so `dismiss()` can detach it too).\n outsideHandler = (e: MouseEvent) => {\n if (!tooltip.contains(e.target as Node) && !(anchor && anchor.contains(e.target as Node))) {\n dismiss();\n }\n };\n setTimeout(() => { if (outsideHandler) document.addEventListener('click', outsideHandler); }, 100);\n }\n\n /** Fire a short confetti burst from the top of an anchor element. Vanilla\n * (no deps) — mirrors the cashier-portal confetti. Respects\n * prefers-reduced-motion. Used on positive moments (high rating, won prize,\n * form complete). */\n private _fireConfetti(anchor?: HTMLElement): void {\n try {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {\n return;\n }\n this.addAnimationStyles();\n const host = document.createElement('div');\n host.className = 'aegis-in-app-confetti';\n host.style.cssText =\n 'position: fixed; left: 0; right: 0; top: 0; height: 0; z-index: 2147483647; pointer-events: none;';\n const colors = ['#fbbf24', '#10b981', '#4169e1', '#ef4444', '#a855f7'];\n const n = 80;\n for (let i = 0; i < n; i++) {\n const p = document.createElement('span');\n const left = Math.round((i / n) * 100);\n const dx = Math.round((Math.random() - 0.5) * 240) + 'px';\n const r = Math.round(Math.random() * 1080) + 'deg';\n const delay = (Math.random() * 0.2).toFixed(2);\n const dur = (1.6 + Math.random() * 1.2).toFixed(2);\n const c = colors[i % colors.length];\n const size = 6 + Math.round(Math.random() * 6);\n p.style.cssText =\n `position: absolute; top: 0; left: ${left}%; width: ${size}px; height: ${size}px; ` +\n `background: ${c}; border-radius: 2px; --dx: ${dx}; --r: ${r}; ` +\n `animation: aegisConfetti ${dur}s cubic-bezier(.2,.6,.3,1) ${delay}s forwards;`;\n host.appendChild(p);\n }\n (anchor?.ownerDocument?.body || document.body).appendChild(host);\n window.setTimeout(() => host.remove(), 3200);\n } catch {\n /* confetti is decorative — never throw */\n }\n }\n\n /**\n * Emotional reaction layer (CEP parity — CleverTap exposes configurable\n * outcome animation; MoEngage sets distinct win/lose icons). Three CONTEXT-\n * AWARE intents drive a premium Lottie animation (with an instant SVG\n * placeholder + graceful fallback):\n *\n * - `celebrate` → a real WIN in a game (spin/scratch prize). Trophy Lottie,\n * gold rings, confetti. The big, earned moment.\n * - `affirm` → positive FEEDBACK (high rating, NPS promoter, quiz/form\n * done). An animated success check — friendly, NOT a trophy\n * (a survey isn't a contest).\n * - `empathize` → a negative outcome (low rating, NPS detractor, no-prize /\n * \"Try again\"). A soft breathing heart — supportive, NO\n * confetti, NO rings.\n *\n * Honors prefers-reduced-motion (static badge, no motion).\n */\n private _playReaction(\n host: HTMLElement | undefined,\n intent: 'celebrate' | 'affirm' | 'acknowledge' | 'empathize',\n lottieFile?: string,\n ic?: Record<string, unknown>,\n scope?: 'card' | 'viewport',\n ): void {\n try {\n if (typeof document === 'undefined') return;\n this.addAnimationStyles();\n const reduce =\n typeof window !== 'undefined' &&\n window.matchMedia &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n // Scope decides the celebration's bounds (Celebration-as-Campaign cutover):\n // 'card' → anchored to `host`, position:absolute inset:0 (micro-delight,\n // spills past the card via overflow:visible). The default —\n // byte-identical to the pre-cutover behavior.\n // 'viewport' → full-viewport fixed burst (hero moment / overlay surface),\n // pinned to document.body regardless of `host`.\n const viewport = scope === 'viewport';\n // Card scope: re-anchor to the enclosing card tile (if any) so the reaction\n // centers on the WHOLE card, not on the inner element that fired it (e.g.\n // the progress bar's column sits lower in a row card → trophy looked low).\n // Falls back to `host` for hosts not inside a card-template tile.\n const cardAnchor =\n !viewport && host?.closest\n ? (host.closest('[data-aegis-reaction-anchor]') as HTMLElement | null)\n : null;\n const anchor = viewport ? document.body : cardAnchor || host || document.body;\n const prevPos = anchor.style.position;\n if (!viewport && (!prevPos || prevPos === 'static')) anchor.style.position = 'relative';\n\n const layer = document.createElement('div');\n layer.className = 'aegis-in-app-reaction';\n layer.style.cssText = viewport\n ? 'position: fixed; inset: 0; z-index: 2147483646; pointer-events: none; overflow: visible;'\n : 'position: absolute; inset: 0; z-index: 2147483646; pointer-events: none; overflow: visible;';\n\n // intent → (Lottie file, SVG fallback variant, ring color, festive?).\n // `festive` = rings + confetti (an earned/delight moment). `acknowledge`\n // is the calm neutral band (NPS 7–8 / mid-★): a green tick, no fanfare.\n const SPEC = {\n celebrate: { file: 'trophy.json', svg: 'gift', ring: '#fbbf24', festive: true },\n affirm: { file: 'affirm.json', svg: 'star', ring: '#fbbf24', festive: true },\n acknowledge: { file: 'check.json', svg: 'check', ring: '#34d399', festive: false },\n empathize: { file: 'empathize.json', svg: 'heart', ring: '', festive: false },\n } as const;\n const spec = SPEC[intent];\n const festive = spec.festive;\n\n // Badge + ring size. Default to the intent's design size, but when the\n // reaction is anchored to a CARD (not the viewport) scale it DOWN to fit\n // the host's smaller dimension — otherwise the 150px trophy dwarfs a short\n // horizontal card (e.g. the Free Delivery progress bar). Only ever scales\n // down (fit < base); large cards and viewport bursts keep the full size.\n let px = festive ? 150 : intent === 'acknowledge' ? 92 : 124;\n let ringPx = 60;\n if (!viewport && host) {\n const rect = host.getBoundingClientRect();\n const fit = Math.min(rect.width, rect.height);\n if (fit > 0 && fit < px) {\n const scaled = Math.max(44, Math.round(fit * 0.82));\n ringPx = Math.max(24, Math.round(ringPx * (scaled / px)));\n px = scaled;\n }\n }\n\n if (festive && !reduce) {\n for (let i = 0; i < 3; i++) {\n const ring = document.createElement('div');\n ring.style.cssText =\n `position: absolute; left: 50%; top: 50%; width: ${ringPx}px; height: ${ringPx}px; ` +\n `border-radius: 50%; border: 3px solid ${spec.ring}; ` +\n `animation: aegisRing 1.1s ease-out ${i * 0.18}s forwards;`;\n layer.appendChild(ring);\n }\n }\n\n const g = document.createElement('div');\n const reactionIcon = svgNode(this._reactionSvg(spec.svg)); // SDK-constant SVG\n if (reactionIcon) g.appendChild(reactionIcon);\n const motion = reduce\n ? ''\n : intent !== 'empathize'\n ? 'animation: aegisReactionPop 0.7s cubic-bezier(.2,.9,.3,1.2) forwards, aegisReactionFloatOut 0.7s ease-in 1.9s forwards;'\n : 'animation: aegisReactionSoft 0.9s ease-out forwards;';\n g.style.cssText =\n `position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); ` +\n `width: ${px}px; height: ${px}px; ` +\n `display: flex; align-items: center; justify-content: center; ` +\n `line-height: 0; filter: drop-shadow(0 8px 16px rgba(0,0,0,0.30)); ${motion}`;\n layer.appendChild(g);\n anchor.appendChild(layer);\n\n // Confetti only on festive (earned/delight) moments — not the calm tick.\n if (festive && !reduce) this._fireConfetti(cardAnchor || host);\n\n // Upgrade EVERY intent's badge to its premium Lottie (trophy for a win, an\n // animated check for feedback, a beating heart for empathy). The instant\n // SVG stays as the graceful fallback. The empathetic heart loops gently.\n // Resolution: operator's no-code custom animation (per intent) wins, then\n // the per-call default (e.g. scratch → gift), then the intent default.\n // A custom value is a full URL; the bundled defaults resolve under /sdk/lottie/.\n const ra = ic && typeof ic === 'object'\n ? (ic.reaction_animations as Record<string, string> | undefined)\n : undefined;\n const custom = ra && typeof ra[intent] === 'string' ? ra[intent] : undefined;\n const file = custom || lottieFile || spec.file;\n if (!reduce && file) {\n const base = this._assetBase();\n const loop = intent === 'empathize';\n const path = /^https?:\\/\\//.test(file) ? file : `${base}lottie/${file}`;\n this._loadLottie()\n .then((lot) => {\n if (!lot || !g.isConnected) return;\n g.replaceChildren();\n (lot as { loadAnimation: (o: Record<string, unknown>) => void }).loadAnimation({\n container: g,\n renderer: 'svg',\n loop,\n autoplay: true,\n path,\n });\n })\n .catch(() => { /* keep the SVG fallback */ });\n }\n\n window.setTimeout(\n () => {\n layer.remove();\n if (anchor.style.position !== prevPos) anchor.style.position = prevPos;\n },\n intent === 'acknowledge' ? 1800 : festive ? 2700 : 2200,\n );\n } catch {\n /* reactions are decorative — never throw */\n }\n }\n\n /** Hand-built, brand-neutral animated SVG badges for the reaction layer — a\n * premium alternative to system emoji (consistent across browsers, no Lottie\n * dependency). The check strokes itself in; the heart breathes gently. */\n private _reactionSvg(variant: string): string {\n switch (variant) {\n case 'star':\n return (\n '<svg width=\"74\" height=\"74\" viewBox=\"0 0 74 74\" fill=\"none\">' +\n '<circle cx=\"37\" cy=\"37\" r=\"31\" fill=\"#fbbf24\"/>' +\n '<circle cx=\"37\" cy=\"37\" r=\"31\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" opacity=\"0.6\"/>' +\n '<path d=\"M37 19 l5.6 11.8 13 1.7 -9.5 9 2.4 12.8 -11.5 -6.2 -11.5 6.2 2.4 -12.8 -9.5 -9 13 -1.7 Z\" ' +\n 'fill=\"#fff\"/></svg>'\n );\n case 'gift':\n return (\n '<svg width=\"74\" height=\"74\" viewBox=\"0 0 74 74\" fill=\"none\">' +\n '<circle cx=\"37\" cy=\"37\" r=\"31\" fill=\"#eef2ff\"/>' +\n '<rect x=\"20\" y=\"35\" width=\"34\" height=\"22\" rx=\"3\" fill=\"#6366f1\"/>' +\n '<rect x=\"17\" y=\"28\" width=\"40\" height=\"10\" rx=\"3\" fill=\"#4f46e5\"/>' +\n '<rect x=\"34\" y=\"28\" width=\"6\" height=\"29\" fill=\"#fbbf24\"/>' +\n '<path d=\"M34 28 C28 20 20 24 24 28 Z\" fill=\"#fbbf24\"/>' +\n '<path d=\"M40 28 C46 20 54 24 50 28 Z\" fill=\"#fbbf24\"/></svg>'\n );\n case 'heart':\n return (\n '<svg width=\"66\" height=\"66\" viewBox=\"0 0 66 66\" fill=\"none\" ' +\n 'style=\"animation: aegisBreathe 1.6s ease-in-out infinite; transform-origin: center;\">' +\n '<circle cx=\"33\" cy=\"33\" r=\"30\" fill=\"#f1f5f9\"/>' +\n '<path d=\"M33 49 C16 38 19 23 28 23 C32 23 33 27 33 27 C33 27 34 23 38 23 C47 23 50 38 33 49 Z\" ' +\n 'fill=\"rgba(244,114,182,0.18)\" stroke=\"#f472b6\" stroke-width=\"2.5\" stroke-linejoin=\"round\"/></svg>'\n );\n case 'check':\n default:\n return (\n '<svg width=\"74\" height=\"74\" viewBox=\"0 0 74 74\" fill=\"none\">' +\n '<circle cx=\"37\" cy=\"37\" r=\"31\" fill=\"#10b981\"/>' +\n '<circle cx=\"37\" cy=\"37\" r=\"31\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" opacity=\"0.6\"/>' +\n '<path d=\"M24 38 l9 9 l18 -21\" stroke=\"#fff\" stroke-width=\"6\" stroke-linecap=\"round\" ' +\n 'stroke-linejoin=\"round\" pathLength=\"100\" stroke-dasharray=\"100\" stroke-dashoffset=\"100\" ' +\n 'style=\"animation: aegisDraw 0.45s 0.25s ease forwards\"/></svg>'\n );\n }\n }\n\n /** Resolve the base URL the SDK assets are served from (so Lottie JSON +\n * player load from the same origin/path as the SDK bundle — works in the\n * dashboard preview and same-origin installs; cross-origin CDN installs fall\n * back to the inline SVG when the asset 404s). */\n private _assetBase(): string {\n try {\n const scripts = Array.from(document.getElementsByTagName('script'));\n const s = scripts.find((x) => /\\/(?:aegis|lottie)\\.min\\.js/.test(x.src));\n if (s && s.src) return s.src.replace(/[^/]*(?:\\?.*)?$/, '');\n } catch {\n /* fall through */\n }\n return '/sdk/';\n }\n\n /** Lazy-load the Lottie player once (cached on window). Resolves to the\n * global `lottie`, or rejects if it can't be fetched (→ SVG fallback). */\n private _loadLottie(): Promise<unknown> {\n const w = window as unknown as { lottie?: unknown; __aegisLottieP?: Promise<unknown> };\n if (w.lottie) return Promise.resolve(w.lottie);\n if (w.__aegisLottieP) return w.__aegisLottieP;\n w.__aegisLottieP = new Promise<unknown>((resolve, reject) => {\n try {\n const s = document.createElement('script');\n s.src = `${this._assetBase()}lottie.min.js`;\n s.async = true;\n s.onload = () => resolve((window as unknown as { lottie?: unknown }).lottie);\n s.onerror = () => reject(new Error('lottie player failed to load'));\n document.head.appendChild(s);\n } catch (e) {\n reject(e instanceof Error ? e : new Error('lottie load error'));\n }\n });\n return w.__aegisLottieP;\n }\n\n private addAnimationStyles(): void {\n if (document.getElementById('aegis-in-app-styles')) {\n return;\n }\n \n const style = document.createElement('style');\n style.id = 'aegis-in-app-styles';\n style.textContent = `\n @keyframes aegisSlideDown {\n from { transform: translateY(-100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n \n @keyframes aegisSlideUp {\n from { transform: translateY(0); opacity: 1; }\n to { transform: translateY(-100%); opacity: 0; }\n }\n \n @keyframes aegisFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n \n @keyframes aegisFadeOut {\n from { opacity: 1; }\n to { opacity: 0; }\n }\n \n @keyframes aegisScaleIn {\n from { transform: scale(0.9); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n }\n\n /* Bottom-anchored slide IN (opposite of aegisSlideUp which slides OUT).\n Used by the preload-first renderers: sticky_bar (bottom),\n progress_bar, carousel_cards, product_recommendation. */\n @keyframes aegisSlideInFromBottom {\n from { transform: translateY(100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n\n /* Bottom-anchored slide OUT (mirror of aegisSlideInFromBottom). A\n bottom banner must exit DOWNWARD, not up through the viewport. */\n @keyframes aegisSlideOutToBottom {\n from { transform: translateY(0); opacity: 1; }\n to { transform: translateY(100%); opacity: 0; }\n }\n\n /* Celebration confetti (ported from the cashier-portal aegis-confetti\n keyframe) — fired on positive moments: high star rating, won spin/\n scratch prize, completed form. Empathetic (no confetti) on negatives. */\n @keyframes aegisConfetti {\n 0% { transform: translate3d(0,0,0) rotate(0); opacity: 1; }\n 100% { transform: translate3d(var(--dx), 112vh, 0) rotate(var(--r)); opacity: 0; }\n }\n\n /* Poll result bar — fills to its share once a vote lands. */\n @keyframes aegisPollFill {\n from { width: 0%; }\n to { width: var(--pct, 0%); }\n }\n\n /* Countdown urgency — pulse the digits red in the final stretch (<60s). */\n @keyframes aegisUrgencyPulse {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.08); }\n }\n\n /* Emotional reaction (success) — the hero glyph springs in with an\n overshoot, gives a happy wiggle, then gently settles + floats out. */\n @keyframes aegisReactionPop {\n 0% { transform: translate(-50%, -40%) scale(0.2) rotate(-12deg); opacity: 0; }\n 45% { transform: translate(-50%, -52%) scale(1.18) rotate(6deg); opacity: 1; }\n 62% { transform: translate(-50%, -50%) scale(0.94) rotate(-3deg); }\n 78% { transform: translate(-50%, -50%) scale(1.05) rotate(1deg); }\n 100% { transform: translate(-50%, -50%) scale(1) rotate(0); opacity: 1; }\n }\n @keyframes aegisReactionFloatOut {\n 0% { transform: translate(-50%, -50%) scale(1); opacity: 1; }\n 100% { transform: translate(-50%, -150%) scale(0.9); opacity: 0; }\n }\n /* Empathetic reaction — soft fade-in + gentle sway, NO celebration. */\n @keyframes aegisReactionSoft {\n 0% { transform: translate(-50%, -45%) scale(0.7); opacity: 0; }\n 25% { transform: translate(-50%, -50%) scale(1); opacity: 1; }\n 50% { transform: translate(-52%, -50%) scale(1) rotate(-4deg); }\n 75% { transform: translate(-48%, -50%) scale(1) rotate(4deg); }\n 100% { transform: translate(-50%, -50%) scale(1) rotate(0); opacity: 1; }\n }\n /* Radiating celebration rings behind the success glyph. */\n @keyframes aegisRing {\n 0% { transform: translate(-50%, -50%) scale(0.3); opacity: 0.55; }\n 100% { transform: translate(-50%, -50%) scale(2.4); opacity: 0; }\n }\n /* Self-drawing stroke (the check / outline draws itself in). */\n @keyframes aegisDraw {\n from { stroke-dashoffset: 100; }\n to { stroke-dashoffset: 0; }\n }\n /* Gentle breathing for the empathetic heart. */\n @keyframes aegisBreathe {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.08); }\n }\n /* Comment-box placeholder — inherit the modal's text color (not the\n browser default near-black, which is invisible on a dark/accent modal). */\n .aegis-rating-comment::placeholder, .aegis-field::placeholder { color: currentColor; opacity: 0.55; }\n /* Tooltip target highlight — a soft pulsing ring drawing the eye. */\n @keyframes aegisTooltipPulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n /* Countdown final-minute urgency — a gentle breathing pulse (NOT a harsh\n solid-red fill). */\n @keyframes aegisCountdownPulse {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.04); }\n }\n /* Shared CTA micro-interaction — one consistent hover-lift + press across\n every widget's primary button, so they feel like one system. */\n .aegis-cta { transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease; }\n .aegis-cta:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(0,0,0,0.18); filter: brightness(1.04); }\n .aegis-cta:active { transform: translateY(0); box-shadow: 0 2px 6px rgba(0,0,0,0.15); filter: brightness(0.98); }\n @media (prefers-reduced-motion: reduce) { .aegis-cta { transition: none; } .aegis-cta:hover { transform: none; } }\n `;\n\n document.head.appendChild(style);\n }\n \n private sanitizeUrl(url: string): string | null {\n try {\n const parsedUrl = new URL(url, window.location.origin);\n \n if (parsedUrl.protocol === 'javascript:' || parsedUrl.protocol === 'data:') {\n this.log(`Blocked unsafe URL: ${url}`, 'error');\n return null;\n }\n \n return parsedUrl.href;\n } catch (error) {\n this.log(`Invalid URL: ${url}`, 'error');\n return null;\n }\n }\n \n private sanitizeColor(color: string): string {\n if (/^#[0-9A-Fa-f]{3,6}$/.test(color)) {\n return color;\n }\n \n if (/^rgb\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)$/.test(color)) {\n return color;\n }\n \n if (/^rgba\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*[\\d.]+\\s*\\)$/.test(color)) {\n return color;\n }\n \n const namedColors = ['white', 'black', 'red', 'green', 'blue', 'yellow', 'orange', 'purple', 'pink', 'gray', 'transparent'];\n if (namedColors.includes(color.toLowerCase())) {\n return color;\n }\n \n return '#000000';\n }\n \n /**\n * Track in-app event via the standard Event Ingress pipeline.\n *\n * Events flow: SDK → Event Ingress → Kafka → Governance Consumer\n * → Event Engine → ClickHouse (in_app_events + events_with_ttl)\n *\n * The event_type uses the canonical prefix `in_app.` so the event\n * switchboard maps it to the correct canonical form:\n * in_app.impression → engagement.view\n * in_app.clicked → engagement.click\n * in_app.dismissed → engagement.dismiss\n */\n private async trackEvent(\n campaignId: string,\n eventType: 'served' | 'impression' | 'clicked' | 'dismissed' | 'anchor_unresolved' | 'slot_unfilled',\n extra?: { stepId?: string; meta?: Record<string, unknown> },\n ): Promise<void> {\n // Lifecycle: `campaign-dismiss` — fire from this single chokepoint so\n // every renderer (8 sites today + future ones) emits consistently.\n // Source granularity (close_button / overlay / esc / auto_timeout) can\n // be refined in a future minor by passing source through to trackEvent;\n // for 1.8.0 we surface the event itself since that's the high-value\n // signal host apps need (inbox state updates, dismiss analytics).\n // Resolve the served campaign once — its attribution context (echoed from\n // the in-app serve for queued journey/campaign messages; NULL for catalog)\n // is forwarded on the engagement event so the switchboard lands\n // journey/template/outlet on the delivery_event.\n const trackedCampaign = this.campaigns.find((c) => c.id === campaignId) as\n | (InAppCampaign & {\n journey_execution_id?: string;\n lifecycle_fork_id?: string;\n location_id?: string;\n })\n | undefined;\n\n if (eventType === 'dismissed') {\n if (trackedCampaign) {\n this.emit('campaign-dismiss', {\n campaign: trackedCampaign,\n source: 'close_button',\n });\n }\n }\n\n try {\n // Canonical in-app SDK path scheme — hyphen `/v1/in-app/events`. The\n // gateway has an explicit handler that auths via X-Aegis-Write-Key and\n // forwards to event-switchboard. Direct POST to /api/v1/events bypasses\n // the SDK auth model and hits the JWT preHandler → 401.\n // See docs/architecture/API_ROUTING.md §5 +\n // feedback_in_app_sdk_paths_canonical_hyphen.\n const url = `${this.apiHost}/v1/in-app/events`;\n const externalEventId = `inapp_${campaignId}_${eventType}_${Date.now()}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey,\n };\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.contactId && this.contactToken) headers['X-Contact-Token'] = this.contactToken; // C1b\n\n // Plumb the same workspace cascade the main analytics SDK uses\n // (config → ?ws= → URL path slug → setWorkspace runtime → session\n // storage). Without this, in-app engagement events arrive at\n // event-ingress unscoped and fall back to the org's primary\n // workspace — see PLUGIN_HANDSHAKE_AUTOMATION_TRACKER.md §P1.b.\n const workspaceId = this.getWorkspaceId?.();\n\n // Identity on the ENGAGEMENT EVENT (docs/IDENTITY_RESOLUTION.md, G3).\n // `this.userId` falls back to the stored anonymous id so that SERVING\n // keys per-visitor queues correctly — but on an event, sending that value\n // as `user_id` made the server treat an anonymous visitor as a person (a\n // phantom contact + profile per visitor), and sending `this.userId` as\n // `anonymous_id` after login broke the anonymous→contact link. So:\n // user_id — only a real identity (never the anonymous id)\n // anonymous_id — always the stored anonymous id, before and after login\n // Read fresh: the anonymous id can be minted after this manager starts.\n const storedAnonId = readAnonIdFromStorage();\n const identifiedUserId =\n this.userId && this.userId !== storedAnonId ? this.userId : undefined;\n\n const body = {\n campaign_id: campaignId,\n event_type: eventType,\n user_id: identifiedUserId,\n contact_id: this.contactId,\n anonymous_id: storedAnonId,\n platform: 'web',\n workspace_id: workspaceId,\n // Per-surface traffic (PROPERTY_IN_APP_TAB_SURFACE_BREAKDOWN_TRACKER\n // Phase B): the canonical screen this campaign actually rendered on,\n // as last declared by the host via aegis.page()/screen(). Null when\n // the host page didn't declare a surface. The gateway forwards this\n // to in_app_events.surface so the property By-screen breakdown can\n // show real per-screen impressions/CTR.\n current_surface: this.currentSurface ?? undefined,\n // The products this card actually rendered. Lands on the canonical\n // event's `properties.items` (gateway → switchboard), which is the\n // same array shape the bill page and the purchase event use, so ONE\n // materialized view turns every surface into per-product impressions.\n // See `productIdentitiesFor` for why this exists.\n //\n // ONLY on `impression` and `clicked`. `served` means \"the SDK received\n // the payload\", not \"a customer saw it\" — on ST there are 3,179 served\n // against 1,077 impressions, so counting both would treble the\n // impression denominator with renders that never reached a screen, and\n // put ~2KB of product JSON on three times as many requests for the\n // privilege. dismissed / anchor_unresolved / slot_unfilled describe the\n // widget, not the products.\n items:\n eventType === 'impression' || eventType === 'clicked'\n ? productIdentitiesFor(trackedCampaign)\n : undefined,\n // P3 — optional step_id surfaces multi-step granularity\n // (WEB_SDK_DLR_ENHANCEMENT_TRACKER P3). Server validator + CH\n // schema accept null/empty as the single-step default.\n step_id: extra?.stepId,\n // Attribution echoed from the served message (queued journey/campaign\n // in-app); NULL for catalog campaigns. Lets the switchboard land\n // journey/template/outlet on the engagement delivery_event.\n journey_execution_id: trackedCampaign?.journey_execution_id,\n lifecycle_fork_id: trackedCampaign?.lifecycle_fork_id,\n // Outlet the customer is ACTIVELY viewing (setLocation) wins — that's\n // where the impression/click actually happened; fall back to the served\n // campaign's outlet, else brand-tier. The gateway promotes this to the\n // canonical event's top-level for per-outlet in-app analytics.\n location_id: this.currentLocationId ?? trackedCampaign?.location_id,\n metadata: {\n property_id: this.propertyId,\n variant_id: this.getVariantId(campaignId) ?? undefined,\n lifecycle_fork_id: trackedCampaign?.lifecycle_fork_id,\n location_id: this.currentLocationId ?? trackedCampaign?.location_id,\n // Renderer-supplied metadata (e.g. anchor_resolved/anchor_selector on\n // an anchor_unresolved event). The gateway spreads payload.metadata\n // into the canonical event so these land in in_app_events.metadata.\n ...(extra?.meta ?? {}),\n },\n idempotency_key: externalEventId,\n };\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n credentials: 'include',\n }).catch(error => {\n this.log(`Error tracking event: ${error}`, 'error');\n });\n\n this.log(`Tracked ${eventType} event for campaign ${campaignId}`);\n } catch (error) {\n this.log(`Error tracking event: ${error}`, 'error');\n }\n\n // A queued journey/campaign send is ALSO acknowledged on its queue row.\n // The engagement event above feeds analytics only — nothing on the server\n // reads it back into `contact_in_app_queue` — so without this the row\n // stayed `queued` and `/v1/in-app/active` re-served the same message on\n // every poll until its 48h expiry. Catalog campaigns carry no step id and\n // are untouched. The step id is remembered at first sight because a\n // refresh between render and dismiss can drop the item from\n // `this.campaigns` once the render ack has retired it server-side.\n const step =\n trackedCampaign?.journey_step_execution_id || this.journeyStepByCampaign.get(campaignId);\n const ackAction = JOURNEY_ACK_ACTION[eventType];\n if (step) {\n this.journeyStepByCampaign.set(campaignId, step);\n if (ackAction) void this.ackJourneyQueueItem(step, ackAction);\n }\n }\n\n /** campaign id → journey_step_execution_id for queued items seen this page. */\n private journeyStepByCampaign = new Map<string, string>();\n /** `${step}:${action}` acks sent (or in flight) this page — one per render. */\n private journeyAcks = new Set<string>();\n private static readonly JOURNEY_ACK_RETRY_MS = 2000;\n\n /**\n * POST /v1/in-app/widgets/{step}/ack — the canonical writer of a queue row's\n * seen-state (the same endpoint the Android and iOS SDKs call). Fail-soft by\n * construction: it runs after the render, is never awaited by it, and never\n * throws. A network error or 5xx is retried once; if that also fails the key\n * is released, so the next render of the (still re-served) message tries\n * again. A 4xx is the server's answer and is not retried. The server side is\n * idempotent — a repeated action is answered `late_arrival` and writes\n * nothing — so a duplicate ack is harmless.\n */\n private async ackJourneyQueueItem(\n step: string,\n action: 'viewed' | 'clicked' | 'dismissed',\n attempt = 0,\n ): Promise<void> {\n const key = `${step}:${action}`;\n if (attempt === 0) {\n if (this.journeyAcks.has(key)) return;\n this.journeyAcks.add(key);\n }\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey,\n };\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.contactId && this.contactToken) headers['X-Contact-Token'] = this.contactToken; // C1b\n\n let retryable = true;\n try {\n const response = await fetch(\n `${this.apiHost}/v1/in-app/widgets/${encodeURIComponent(step)}/ack`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({ action }),\n credentials: 'include',\n // A click usually navigates away at once; keepalive lets the ack\n // outlive the page instead of being cancelled with it.\n keepalive: true,\n },\n );\n if (response.ok) {\n this.log(`Acknowledged queued message ${step}: ${action}`);\n return;\n }\n retryable = response.status >= 500 || response.status === 408 || response.status === 429;\n this.log(`Queue ack ${action} for ${step} failed: HTTP ${response.status}`, 'warn');\n } catch (error) {\n this.log(`Queue ack ${action} for ${step} failed: ${error}`, 'warn');\n }\n if (!retryable) return;\n if (attempt === 0) {\n setTimeout(() => {\n void this.ackJourneyQueueItem(step, action, 1);\n }, AegisInAppManager.JOURNEY_ACK_RETRY_MS);\n return;\n }\n this.journeyAcks.delete(key);\n }\n \n private log(message: string, level: 'log' | 'warn' | 'error' = 'log'): void {\n if (this.debugMode) {\n console[level](`[AegisInApp] ${message}`);\n }\n }\n \n destroy(options?: { clearABState?: boolean }): void {\n this.disconnectSSE();\n\n // Tear down the Active Web Chat launcher (its own SSE is already panel-gated,\n // but destroy() removes the bubble + any open panel + listeners).\n disposeActiveWebChat();\n this.activeWebChat = undefined;\n\n // P1 — clear any pending qualifying-event refresh so a destroy() during\n // the debounce window doesn't fire a stray fetch after the manager is gone.\n if (this.refreshDebounceTimer) {\n clearTimeout(this.refreshDebounceTimer);\n this.refreshDebounceTimer = undefined;\n }\n\n if (typeof document !== 'undefined') {\n document.querySelectorAll(\n '.aegis-in-app-banner, .aegis-in-app-modal-overlay, .aegis-in-app-fullscreen-overlay, ' +\n '.aegis-in-app-half-interstitial-overlay, .aegis-in-app-alert-overlay, .aegis-in-app-pip, ' +\n '.aegis-in-app-nps-overlay, .aegis-in-app-countdown-overlay, .aegis-in-app-rating-overlay, ' +\n '.aegis-in-app-poll-overlay, .aegis-in-app-quiz-overlay, ' +\n // F2/F3 / U8 — slot-rendered card classes\n '.aegis-in-app-rating-card, .aegis-in-app-nps-card'\n ).forEach(el => {\n if (el.parentNode) {\n el.parentNode.removeChild(el);\n }\n });\n\n const styles = document.getElementById('aegis-in-app-styles');\n if (styles && styles.parentNode) {\n styles.parentNode.removeChild(styles);\n }\n }\n\n // Optionally clear A/B assignment state\n if (options?.clearABState && typeof localStorage !== 'undefined') {\n localStorage.removeItem('aegis_ab_assignments');\n }\n\n this.isInitialized = false;\n this.log('AegisInApp destroyed');\n }\n}\n","/**\n * ProductRecommendation renderer — personalized product grid/row rendered\n * from `interactive_config.cards[]` that the cell-plane populated at\n * bundle-assembly time. The SDK never calls a product-API at render\n * time — the preload contract is that the recommended products are\n * already in the bundle.\n *\n * The card TILE itself is painted by the shared card-template interpreter\n * (`renderCardFromTemplate`) — the SAME path used by the inline rail and the\n * carousel — so the look is authored in compose (`interactive_config.card_template`)\n * and the SDK carries no per-surface card layout. This module owns only the\n * SHELL: the overlay sheet vs the inline rail, the header, grid/row layout,\n * scroll affordance and dismiss.\n *\n * interactive_config:\n * rec_source?: 'similar_items' | 'also_viewed' | 'also_purchased' | 'trending'\n * | 'personalized' | 'manual'\n * (metadata only — the actual product list is in `cards[]`)\n * rec_count?: number (display hint; rendering is driven by cards.length)\n * rec_layout?: 'grid' | 'row' default 'grid'\n * rec_cta_text?: string default CTA label (also carried per-card on cta_text)\n * card_template?: CardTemplate (the declarative card look; legacy card_style adapted)\n * cards: Array<{ image_url?, title?, body?, cta_url?, metadata?, style? }>\n */\n\nimport { lucideSvg } from '../lucide-svg';\nimport type { CardLook } from './card-style';\nimport { renderCardFromTemplate, resolveGlobalTemplate, type TemplateCard } from './card-template';\nimport { IN_APP_FONT_STACK, ensureInAppFont } from '../font';\nimport type { RenderContext } from './types';\n\ntype Card = TemplateCard & {\n /** Per-card look & feel — stamped server-side from card_style +\n * card_overrides[sku], or authored directly on manual cards. Overlaid on the\n * resolved card_template at render time. */\n style?: CardLook;\n};\n\nconst MAX_PRODUCTS = 24;\n\nexport function renderProductRecommendation(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const rawCards = Array.isArray(ic.cards) ? (ic.cards as Card[]) : [];\n const cards = rawCards.slice(0, MAX_PRODUCTS);\n if (cards.length === 0) {\n log('product_recommendation rendered with zero products — skipping', 'warn');\n return;\n }\n\n const layout = (ic.rec_layout as string) || 'grid';\n const template = resolveGlobalTemplate(ic);\n\n addAnimationStyles();\n ensureInAppFont();\n\n // Commerce surface pattern: the product SHEET stays a clean NEUTRAL canvas\n // (saturated brand colour fights product photos), and the BRAND accent\n // (operator's background_color — the same hue the promo widgets fill with) is\n // applied to the emphasis elements (CTA / price / dots) so it still reads\n // on-brand. accent falls back to brand blue when none is set.\n const bg = '#ffffff';\n const fg = '#0f172a';\n const accent = sanitizeColor((campaign.background_color as string) || '#4169e1');\n\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-product-rec';\n overlay.setAttribute('data-campaign-id', campaign.id);\n overlay.style.cssText = `\n position: fixed; inset: 0;\n background: rgba(15,23,42,0.55); backdrop-filter: blur(4px);\n display: flex; align-items: flex-end; justify-content: center;\n z-index: 99999; animation: aegisFadeIn 0.25s ease;\n `;\n\n const sheet = document.createElement('div');\n sheet.style.cssText = `\n background: ${bg}; color: ${fg};\n max-width: 560px; width: 100%; max-height: 80vh; overflow-y: auto;\n border-radius: 20px 20px 0 0;\n padding: 18px 16px 22px;\n font-family: ${IN_APP_FONT_STACK};\n animation: aegisSlideInFromBottom 0.3s ease-out;\n box-shadow: 0 -12px 30px rgba(0,0,0,0.12);\n `;\n\n // Drag handle (decorative, for mobile context)\n const handle = document.createElement('div');\n handle.style.cssText = `\n width: 36px; height: 4px; border-radius: 999px; background: ${fg}1f;\n margin: 0 auto 12px;\n `;\n sheet.appendChild(handle);\n\n const header = document.createElement('div');\n header.style.cssText = 'display: flex; justify-content: space-between; align-items: flex-start; gap: 12px;';\n\n const headerText = document.createElement('div');\n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-weight: 700; font-size: 16px; margin-bottom: 2px;';\n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 13px; opacity: 0.7; line-height: 1.4;';\n headerText.appendChild(title);\n headerText.appendChild(body);\n header.appendChild(headerText);\n\n const close = document.createElement('button');\n close.innerHTML = lucideSvg('x', { size: 18 });\n close.setAttribute('aria-label', 'Close');\n close.style.cssText = `\n background: transparent; border: none; color: inherit;\n font-size: 18px; cursor: pointer; padding: 4px 8px; opacity: 0.7;\n `;\n close.addEventListener('click', () => {\n trackEvent(campaign.id, 'dismissed');\n overlay.remove();\n });\n header.appendChild(close);\n sheet.appendChild(header);\n\n const grid = document.createElement('div');\n if (layout === 'row') {\n // Bottom/side padding so `overflow-x: auto` doesn't clip the card shadows.\n grid.style.cssText = `\n display: flex; gap: 10px; overflow-x: auto;\n scroll-snap-type: x mandatory; padding: 14px 2px 18px;\n `;\n } else if (layout === 'list') {\n // Vertical single-column stack — full-width cards, no horizontal scroll.\n grid.style.cssText = `\n display: flex; flex-direction: column; gap: 10px; padding: 14px 2px 6px;\n `;\n } else {\n grid.style.cssText = `\n display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;\n padding: 14px 2px 6px;\n `;\n }\n\n cards.forEach((c) => {\n grid.appendChild(\n renderCardFromTemplate(c, { template, ctx, action: 'navigate', layout, layoutAccent: accent }),\n );\n });\n\n sheet.appendChild(grid);\n overlay.appendChild(sheet);\n\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) {\n trackEvent(campaign.id, 'dismissed');\n overlay.remove();\n }\n });\n\n document.body.appendChild(overlay);\n}\n\n/**\n * Inline (embedded_card) variant — renders the recommendation rail INTO a\n * host `[data-aegis-slot]` element instead of a fixed overlay sheet. This is\n * the \"You might also like\" rail on the PDP / storefront commerce slot.\n *\n * Returns `true` when a rail was painted, `false` when there are no products\n * (so the caller can avoid firing an impression for an empty slot). The slot\n * collapses to nothing when empty — no orphaned heading.\n */\nexport function renderProductRecommendationInline(\n ctx: RenderContext,\n target: HTMLElement,\n): boolean {\n const { campaign, sanitizeColor, addAnimationStyles, log } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const rawCards = Array.isArray(ic.cards) ? (ic.cards as Card[]) : [];\n const cards = rawCards.slice(0, MAX_PRODUCTS);\n if (cards.length === 0) {\n log('product_recommendation (inline) rendered with zero products — skipping', 'warn');\n return false;\n }\n\n const layout = (ic.rec_layout as string) || 'row';\n const template = resolveGlobalTemplate(ic);\n const fg = '#0f172a';\n const accent = sanitizeColor((campaign.background_color as string) || '#4169e1');\n\n addAnimationStyles();\n ensureInAppFont();\n\n const section = document.createElement('section');\n section.className = 'aegis-in-app-product-rec-inline';\n section.setAttribute('data-campaign-id', campaign.id);\n section.style.cssText = `\n font-family: ${IN_APP_FONT_STACK};\n color: ${fg};\n `;\n\n if (campaign.title) {\n const title = document.createElement('h2');\n title.textContent = campaign.title;\n title.style.cssText = 'font-weight: 700; font-size: 16px; margin: 0 0 2px;';\n section.appendChild(title);\n }\n if (campaign.body) {\n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 13px; opacity: 0.65; line-height: 1.4; margin-bottom: 10px;';\n section.appendChild(body);\n }\n\n const grid = document.createElement('div');\n const isRow = layout === 'row' || (layout !== 'grid' && layout !== 'list');\n if (layout === 'grid') {\n grid.style.cssText = 'display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;';\n } else if (layout === 'list') {\n // Vertical single-column stack — full-width cards, no horizontal scroll.\n grid.style.cssText = 'display: flex; flex-direction: column; gap: 10px;';\n } else {\n // `overflow-x: auto` also clips the Y axis, which would slice off the\n // cards' drop-shadow. Pad the scroll box on all sides so the shadow has\n // room, and pull it back with matching negative margins so the first/last\n // card still sit FLUSH with the slot's edge (the rail's left boundary\n // matches the host container's padding exactly).\n grid.style.cssText = `\n display: flex; gap: 10px; overflow-x: auto;\n scroll-snap-type: x proximity; padding: 10px 8px 18px; margin: -8px -8px -6px;\n -webkit-overflow-scrolling: touch; scrollbar-width: none;\n `;\n (grid.style as unknown as { msOverflowStyle?: string }).msOverflowStyle = 'none';\n }\n\n cards.forEach((c) => {\n grid.appendChild(\n renderCardFromTemplate(c, { template, ctx, action: 'cart', layout, layoutAccent: accent }),\n );\n });\n\n if (isRow && cards.length > 2) {\n // Right-edge scroll affordance — a circular › button that nudges the rail\n // right, signalling there's more beyond the fold (Zepto/categories rail\n // pattern). Self-hides once scrolled to the end.\n const wrap = document.createElement('div');\n wrap.style.cssText = 'position: relative;';\n wrap.appendChild(grid);\n const arrow = document.createElement('button');\n arrow.setAttribute('aria-label', 'Scroll for more');\n arrow.innerHTML = '›';\n arrow.style.cssText = `\n position: absolute; top: 50%; right: 0; transform: translateY(-50%);\n width: 30px; height: 30px; border-radius: 999px; border: 1px solid ${fg}1a;\n background: #fff; color: ${fg}; font-size: 18px; line-height: 1; cursor: pointer;\n box-shadow: 0 2px 8px rgba(0,0,0,0.12); display: flex; align-items: center;\n justify-content: center; z-index: 2;\n `;\n arrow.addEventListener('click', (e) => {\n e.stopPropagation();\n grid.scrollBy({ left: Math.round(grid.clientWidth * 0.8), behavior: 'smooth' });\n });\n const syncArrow = () => {\n const atEnd = grid.scrollLeft + grid.clientWidth >= grid.scrollWidth - 8;\n arrow.style.display = atEnd ? 'none' : 'flex';\n };\n grid.addEventListener('scroll', syncArrow, { passive: true });\n setTimeout(syncArrow, 0);\n wrap.appendChild(arrow);\n section.appendChild(wrap);\n } else {\n section.appendChild(grid);\n }\n target.appendChild(section);\n return true;\n}\n","/**\n * CarouselCards renderer — a swipeable / autoplaying sequence of product\n * cards. Used for post-purchase upsells, cross-sell, and \"recently viewed\"\n * surfaces. The entire card list is pre-bundled — no network call on\n * trigger; slide advance is pure client state.\n *\n * Data shape (interactive_config, populated at campaign create time and\n * validated server-side):\n * cards: Array<{ image_url?, title, body?, cta_text?, cta_url? }>\n * autoplay_ms?: number // 0 = manual advance only\n * loop?: boolean // wrap to card 0 on last-next\n */\n\nimport { lucideSvg } from '../lucide-svg';\nimport type { CardLook } from './card-style';\nimport { renderCardFromTemplate, resolveGlobalTemplate, type TemplateCard } from './card-template';\nimport { IN_APP_FONT_STACK, ensureInAppFont } from '../font';\nimport type { RenderContext } from './types';\n\ntype Card = TemplateCard & {\n /** Per-card look & feel — overlaid on the resolved card_template. */\n style?: CardLook;\n};\n\nconst MAX_CARDS_RENDERED = 10;\n\nexport function renderCarouselCards(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const rawCards = Array.isArray(ic.cards) ? (ic.cards as Card[]) : [];\n const cards = rawCards.slice(0, MAX_CARDS_RENDERED);\n\n if (cards.length === 0) {\n log('carousel_cards rendered with zero cards — skipping', 'warn');\n return;\n }\n\n const autoplay = Number(ic.autoplay_ms) || 0;\n const loop = ic.loop !== false; // default true\n // Orientation (shared rec_layout vocabulary): 'row' = the autoplay swipe\n // carousel (default) · 'grid' = static 2-col · 'list' = static vertical stack.\n // Autoplay/dots/arrows only apply to 'row' — the other two are static.\n const layout = (ic.rec_layout as string) || 'row';\n\n addAnimationStyles();\n ensureInAppFont();\n\n // Commerce surface: neutral card canvas (product imagery needs it) + the BRAND\n // accent (operator's background_color — what the promo widgets fill with) on\n // the emphasis elements (CTA / active dot). Falls back to brand blue.\n const bg = '#ffffff';\n const fg = '#0f172a';\n const accent = sanitizeColor((campaign.background_color as string) || '#4169e1');\n\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-carousel-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n overlay.style.cssText = `\n position: fixed; left: 0; right: 0; bottom: 0;\n z-index: 99999; padding: 12px 12px 20px;\n background: rgba(0,0,0,0.12); backdrop-filter: blur(8px);\n font-family: ${IN_APP_FONT_STACK};\n animation: aegisSlideInFromBottom 0.3s ease-out;\n `;\n\n const card = document.createElement('div');\n card.style.cssText = `\n background: ${bg}; color: ${fg}; border-radius: 16px;\n box-shadow: 0 8px 20px rgba(0,0,0,0.08); padding: 16px;\n max-width: 520px; margin: 0 auto; position: relative;\n `;\n\n const header = document.createElement('div');\n header.style.cssText = 'display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 12px;';\n\n const headerText = document.createElement('div');\n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-weight: 700; font-size: 15px; margin-bottom: 2px;';\n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 13px; opacity: 0.75;';\n headerText.appendChild(title);\n headerText.appendChild(body);\n header.appendChild(headerText);\n\n const closeBtn = document.createElement('button');\n closeBtn.innerHTML = lucideSvg('x', { size: 16 });\n closeBtn.setAttribute('aria-label', 'Close');\n closeBtn.style.cssText = `\n background: transparent; border: none; color: inherit;\n font-size: 16px; cursor: pointer; opacity: 0.6; padding: 2px 6px;\n `;\n closeBtn.addEventListener('click', () => {\n trackEvent(campaign.id, 'dismissed');\n overlay.remove();\n });\n header.appendChild(closeBtn);\n card.appendChild(header);\n\n const track = document.createElement('div');\n if (layout === 'grid') {\n track.style.cssText = 'display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; padding: 4px 2px;';\n } else if (layout === 'list') {\n track.style.cssText = 'display: flex; flex-direction: column; gap: 10px; padding: 4px 2px;';\n } else {\n // Native horizontal scroll (same as the product-rec row, which scrolls\n // smoothly). `proximity` snap lets the user rest anywhere — `mandatory` +\n // start-align made the LAST card unreachable (it can't align to the start\n // when there's no content after it) and felt janky. No custom wheel handler:\n // it double-scrolled and fought the native scroller.\n track.style.cssText = `\n display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x proximity;\n scroll-padding-left: 0; scrollbar-width: none; -ms-overflow-style: none;\n /* room so overflow-x: auto doesn't clip the card drop-shadow */\n padding: 8px 6px 16px; margin: -8px -6px -6px; -webkit-overflow-scrolling: touch;\n `;\n (track.style as unknown as { msOverflowStyle?: string }).msOverflowStyle = 'none';\n }\n\n // Card tiles painted by the SHARED card-template interpreter (same path as\n // the rec rail) — the look is authored in compose via card_template. The\n // carousel is merchandising, so CTA + body click NAVIGATE (action:'navigate').\n const template = resolveGlobalTemplate(ic);\n cards.forEach((c, i) => {\n const tile = renderCardFromTemplate(c, {\n template,\n ctx,\n action: 'navigate',\n layout,\n layoutAccent: accent,\n });\n tile.setAttribute('data-card-index', String(i));\n // P3 — per-card CLICK funnel signal (mirrors the per-card impression at\n // setActive). The navigate itself is owned by the card engine (action:\n // 'navigate'); this stamps the card index so the drop-off report can show\n // which card converted. Downstream dedups on (campaign_id, step_id, contact).\n tile.addEventListener('click', () => {\n trackEvent(campaign.id, 'clicked', { stepId: `card_${i}` });\n });\n track.appendChild(tile);\n });\n card.appendChild(track);\n\n // Static orientations (grid / list) show every card at once — no autoplay,\n // dots, arrows or scroll-sync. Emit one impression per visible card and skip\n // the carousel machinery entirely.\n if (layout !== 'row') {\n cards.forEach((_, i) => trackEvent(campaign.id, 'impression', { stepId: `card_${i}` }));\n overlay.appendChild(card);\n document.body.appendChild(overlay);\n return;\n }\n\n // Dots + autoplay\n const dots = document.createElement('div');\n dots.style.cssText = 'display: flex; justify-content: center; gap: 6px; margin-top: 10px;';\n const dotEls: HTMLSpanElement[] = [];\n cards.forEach(() => {\n const dot = document.createElement('span');\n dot.style.cssText = `\n width: 6px; height: 6px; border-radius: 999px; background: ${fg};\n opacity: 0.25; transition: opacity 0.2s;\n `;\n dotEls.push(dot);\n dots.appendChild(dot);\n });\n card.appendChild(dots);\n\n // P3 — emit `impression` per card so the funnel-drop-off report can\n // measure how many users actually viewed each carousel position.\n // sessionStorage dedup on (campaign × card) prevents flooding when\n // a user swipes back and forth.\n const seenCards = new Set<number>();\n const setActive = (idx: number) => {\n dotEls.forEach((d, i) => {\n d.style.opacity = i === idx ? '1' : '0.25';\n d.style.background = i === idx ? accent : fg; // active dot = brand accent\n });\n if (!seenCards.has(idx)) {\n seenCards.add(idx);\n trackEvent(campaign.id, 'impression', { stepId: `card_${idx}` });\n }\n };\n setActive(0);\n\n let activeIdx = 0;\n const tiles = track.querySelectorAll<HTMLDivElement>('[data-card-index]');\n const goto = (idx: number) => {\n activeIdx = ((idx % cards.length) + cards.length) % cards.length;\n const el = tiles[activeIdx];\n if (el) {\n // Scroll the TRACK only — never `el.scrollIntoView()`, which scrolls the\n // nearest scrollable ancestor (the page / preview iframe) and visibly\n // jitters when it fights the user's own scroll. Compute the horizontal\n // delta from the track's own viewport and move just `track.scrollLeft`.\n const delta =\n el.getBoundingClientRect().left - track.getBoundingClientRect().left;\n track.scrollTo({ left: track.scrollLeft + delta, behavior: 'smooth' });\n setActive(activeIdx);\n }\n };\n\n // Prev/next arrows — swipe + dots are touch-first; desktop needs buttons to\n // advance the carousel. Flanks the dots row.\n if (cards.length > 1) {\n const mkArrow = (glyph: string, label: string, step: number): HTMLButtonElement => {\n const b = document.createElement('button');\n b.type = 'button';\n b.textContent = glyph;\n b.setAttribute('aria-label', label);\n b.style.cssText = `background: transparent; border: none; color: ${fg}; opacity: 0.55; font-size: 18px; line-height: 1; cursor: pointer; padding: 0 4px;`;\n b.addEventListener('click', () => { stopAutoplay(); goto(activeIdx + step); });\n return b;\n };\n dots.style.alignItems = 'center';\n dots.insertBefore(mkArrow('‹', 'Previous', -1), dots.firstChild);\n dots.appendChild(mkArrow('›', 'Next', 1));\n }\n\n let autoplayTimer: ReturnType<typeof setInterval> | null = null;\n const stopAutoplay = () => {\n if (autoplayTimer) {\n clearInterval(autoplayTimer);\n autoplayTimer = null;\n }\n };\n if (autoplay > 0 && cards.length > 1) {\n autoplayTimer = setInterval(() => {\n // The overlay may have been removed (e.g. a preview re-render replaced\n // it) — DOM `removeChild` fires no event, so self-terminate here rather\n // than leak a timer that scrolls a detached node.\n if (!document.body.contains(overlay)) {\n stopAutoplay();\n return;\n }\n const next = activeIdx + 1;\n if (next >= cards.length && !loop) {\n stopAutoplay();\n return;\n }\n goto(next);\n }, autoplay);\n }\n\n // Stop auto-advancing the moment the user takes manual control — autoplay\n // fighting a live scroll/drag is the classic carousel jitter.\n (['pointerdown', 'wheel', 'touchstart'] as const).forEach((evt) =>\n track.addEventListener(evt, stopAutoplay, { passive: true }),\n );\n\n // Observe scroll to keep dots in sync when the user swipes.\n track.addEventListener('scroll', () => {\n const approx = Math.round(track.scrollLeft / 150);\n if (approx !== activeIdx && approx >= 0 && approx < cards.length) {\n activeIdx = approx;\n setActive(activeIdx);\n }\n });\n\n overlay.appendChild(card);\n document.body.appendChild(overlay);\n}\n","/**\n * Aegis device pairing — WEB client (Surface Canvas P6).\n *\n * Pairs a web property to the dashboard canvas so an operator can anchor in-app\n * overlays to REAL page elements. Flow:\n * 1. operator opens a session in the dashboard → short pairing code\n * 2. `pairWebDevice(apiHost, code)` claims it, then captures the current\n * screen: the page's addressable element tree (stable CSS selectors +\n * bounds + roles) + viewport, and POSTs it to the backend.\n * 3. re-captures on navigation (SPA route changes) so the canvas tracks the\n * live page.\n *\n * This is the web sibling of the native (Flutter/Android/iOS/RN) pairing clients\n * — they stream the native view hierarchy; this walks the DOM. Same backend\n * contract (`/v1/device-pairing/...`).\n */\n\nexport interface PairingElementNode {\n /** A stable CSS selector the SDK can re-resolve at runtime (id > data-* > path). */\n selector: string;\n role: string;\n text?: string;\n bounds: { x: number; y: number; w: number; h: number };\n}\n\nexport interface CapturedWebScreen {\n screen_name: string;\n view_tree: PairingElementNode[];\n viewport: { w: number; h: number; scale: number };\n platform: 'web';\n}\n\nexport interface PairWebOptions {\n /** Override the screen name (defaults to location.pathname). */\n screenName?: string;\n deviceLabel?: string;\n /** Keep re-capturing on SPA navigation + a slow heartbeat. Default true. */\n live?: boolean;\n}\n\nconst MAX_NODES = 400; // bound the payload — anchor candidates, not the whole DOM\n\n/** Build a stable-ish CSS selector for an element: prefer #id, then a unique\n * [data-*] attribute, else a nth-of-type path from the nearest id'd ancestor. */\nfunction selectorFor(el: Element): string {\n if (el.id) return `#${CSS.escape(el.id)}`;\n for (const attr of ['data-testid', 'data-test', 'data-aegis-anchor', 'data-qa', 'name']) {\n const v = el.getAttribute(attr);\n if (v) {\n const sel = `${el.tagName.toLowerCase()}[${attr}=\"${CSS.escape(v)}\"]`;\n if (document.querySelectorAll(sel).length === 1) return sel;\n }\n }\n // Path from nearest id'd ancestor, using nth-of-type for stability.\n const parts: string[] = [];\n let node: Element | null = el;\n while (node && node !== document.body && parts.length < 6) {\n if (node.id) { parts.unshift(`#${CSS.escape(node.id)}`); break; }\n const tag = node.tagName.toLowerCase();\n const parentEl: Element | null = node.parentElement;\n if (!parentEl) { parts.unshift(tag); break; }\n const sibs = Array.from(parentEl.children).filter((c) => c.tagName === node!.tagName);\n const idx = sibs.indexOf(node) + 1;\n parts.unshift(sibs.length > 1 ? `${tag}:nth-of-type(${idx})` : tag);\n node = parentEl;\n }\n return parts.join(' > ');\n}\n\n/** Walk the DOM for addressable anchor candidates (interactive + labelled\n * elements that are visible), with bounds + a re-resolvable selector. */\nexport function captureWebScreen(screenName?: string): CapturedWebScreen {\n const nodes: PairingElementNode[] = [];\n const candidates = document.querySelectorAll(\n 'a,button,input,select,textarea,[role],[data-aegis-anchor],[data-testid],h1,h2,h3,img,[data-aegis-slot]',\n );\n for (const el of Array.from(candidates)) {\n if (nodes.length >= MAX_NODES) break;\n const rect = el.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) continue; // skip hidden/zero-size\n // Flag built-in slots so the dashboard canvas highlights them as the\n // recommended targets (\"the value-bar slot\") rather than generic elements.\n const slotKey = el.getAttribute('data-aegis-slot');\n const role = slotKey\n ? `slot:${slotKey}`\n : el.getAttribute('role') || el.tagName.toLowerCase();\n const text = (el.textContent || el.getAttribute('aria-label') || '').trim().slice(0, 80);\n nodes.push({\n selector: selectorFor(el),\n role,\n ...(text ? { text } : {}),\n bounds: {\n x: Math.round(rect.left + window.scrollX),\n y: Math.round(rect.top + window.scrollY),\n w: Math.round(rect.width),\n h: Math.round(rect.height),\n },\n });\n }\n return {\n screen_name: screenName || window.location.pathname || '/',\n view_tree: nodes,\n viewport: { w: window.innerWidth, h: window.innerHeight, scale: window.devicePixelRatio || 1 },\n platform: 'web',\n };\n}\n\n/** Claim a pairing code + stream this web page as a captured screen. Returns a\n * stop() to end live re-capture. */\nexport async function pairWebDevice(\n apiHost: string,\n code: string,\n opts: PairWebOptions = {},\n): Promise<{ ok: boolean; stop: () => void; error?: string }> {\n const base = `${apiHost.replace(/\\/$/, '')}/v1/device-pairing`;\n const noop = () => {};\n try {\n const claim = await fetch(`${base}/${encodeURIComponent(code)}/claim`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ platform: 'web', device_label: opts.deviceLabel || 'Web browser' }),\n });\n if (!claim.ok) return { ok: false, stop: noop, error: `claim failed (${claim.status})` };\n\n const push = async (): Promise<void> => {\n try {\n const screen = captureWebScreen(opts.screenName);\n await fetch(`${base}/${encodeURIComponent(code)}/screens`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(screen),\n });\n } catch {\n /* transient — the next capture retries */\n }\n };\n\n await push();\n\n if (opts.live === false) return { ok: true, stop: noop };\n\n // Live re-capture: SPA route changes + a slow heartbeat for late layout.\n let timer: ReturnType<typeof setInterval> | null = null;\n const onNav = () => { void push(); };\n window.addEventListener('popstate', onNav);\n window.addEventListener('hashchange', onNav);\n timer = setInterval(() => { void push(); }, 4000);\n const stop = () => {\n window.removeEventListener('popstate', onNav);\n window.removeEventListener('hashchange', onNav);\n if (timer) { clearInterval(timer); timer = null; }\n };\n return { ok: true, stop };\n } catch (e) {\n return { ok: false, stop: noop, error: e instanceof Error ? e.message : 'pairing error' };\n }\n}\n\nlet _pairingStarted = false;\n\n/**\n * Read `?aegis_pair=CODE` from the URL and enter pairing — ONCE per page load.\n *\n * Shared entry point so pairing fires the SAME way for BOTH distribution styles:\n * - npm package consumers — the PREFERRED path: the Active Commerce Store AND\n * external websites that `npm i @active-reach/web-sdk` + `new AegisMessageRuntime(...)`\n * → called from `AegisMessageRuntime.initialize()`.\n * - CDN bundle (`aegis.min.js`) — only for platforms that can't consume npm\n * (Shopify / Magento / WooCommerce) → called from `cdn.ts` on bundle boot.\n * The module-level guard dedupes when both paths exist in one bundle. This is\n * what makes the storefront a true testbed: it pairs exactly like any external\n * npm-SDK site — no per-integration divergence.\n */\nexport function maybeStartPairing(apiHost?: string): void {\n if (_pairingStarted || typeof window === 'undefined') return;\n try {\n const code = new URLSearchParams(window.location.search).get('aegis_pair');\n if (!code) return;\n _pairingStarted = true;\n void pairWebDevice(apiHost || 'https://api.active-reach.ai', code, { live: true });\n } catch {\n /* pairing is best-effort — never block SDK boot */\n }\n}\n","/**\n * AegisWidgetManager - Web SDK Widget Module\n * \n * Smart Pull architecture for persistent web widgets.\n * \n * Features:\n * - Games (spin wheel, scratch card) are in-app campaigns (AegisInAppManager), not widgets\n * - Chat bubbles (WhatsApp, support)\n * - Social proof toasts (real-time purchase notifications)\n * - Feedback forms (NPS, surveys)\n * \n * Security:\n * - XSS Protection: Uses DOM APIs instead of innerHTML\n * - Server-side prize generation prevents client manipulation\n * - URL validation prevents javascript: protocol injection\n * \n * Architecture:\n * - Fetches widget configs from /v1/widgets/config on page load\n * - Integrates with TriggerEngine for behavioral triggers\n * - Tracks widget events (show, click, dismiss, submit)\n * - Server-side prize generation for gamification\n */\n\nimport { TriggerEngine } from '../triggers/TriggerEngine';\nimport { lucideSvg } from '../inapp/lucide-svg';\nimport { Storage } from '../utils/storage';\nimport { redactUrl } from '../utils/redact-url';\n\n// Read the SDK's persisted anonymous_id from localStorage/cookie if available.\n// Returns undefined when called server-side or when no id has been minted yet.\n// See AegisInAppManager for the same pattern + canonical contract in\n// docs/architecture/API_ROUTING.md §3.\nfunction readAnonIdFromStorage(): string | undefined {\n if (typeof document === 'undefined') return undefined;\n try {\n const storage = new Storage();\n return storage.get('anon_id') ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface WidgetConfig {\n widget_id: string;\n widget_type: 'chat_bubble' | 'toast' | 'feedback_form' | 'exit_intent_popup';\n name: string;\n config: Record<string, any>;\n position?: string;\n priority: number;\n trigger_rules?: {\n type: 'exit_intent' | 'scroll_depth' | 'time_on_page' | 'immediate';\n config?: Record<string, any>;\n };\n}\n\nexport interface AegisWidgetConfig {\n writeKey: string;\n apiHost?: string;\n userId?: string;\n contactId?: string;\n /** Server-minted proof of `contactId` (C1b). Sent alongside `X-Contact-ID`\n * so the cell-plane will honour the id. */\n contactToken?: string;\n organizationId?: string;\n debugMode?: boolean;\n triggerEngine?: TriggerEngine;\n /** If true, WidgetManager takes ownership of the TriggerEngine and\n * will call `triggerEngine.stop()` on destroy(). Leave false when\n * the caller shares the engine across managers (e.g. the facade). */\n ownsTriggerEngine?: boolean;\n enablePrefetch?: boolean;\n cssCustomization?: WidgetCSSCustomization;\n onEvent?: (eventType: string, data: any) => void;\n /** Source platform passed as X-Source-Platform header so the API\n * can filter campaigns whose display_rules.sources restricts them\n * to specific integrations (e.g. 'shopify', 'woocommerce'). */\n sourcePlatform?: 'web' | 'shopify' | 'woocommerce' | 'mobile_sdk';\n /**\n * Workspace cascade reader. Host SDK sets this to\n * `() => aegis.getEffectiveWorkspaceId()` so widget interaction events\n * (impressions, clicks, feedback submissions)\n * arrive at the gateway with the same workspace context the main\n * analytics SDK uses. See PLUGIN_HANDSHAKE_AUTOMATION_TRACKER.md §P1.b.\n */\n getWorkspaceId?: () => string | undefined;\n /**\n * Cart-store unification: invoked whenever the widget subsystem learns the\n * cart — platform auto-detect (Shopify/Woo/Magento) or an explicit\n * `setCartData(...)`. The runtime wires this to `applyCart(...)` so the ONE\n * cart store (read by the Free-Delivery card / progress bar / rule eval) stays\n * in sync. See CART_STORE_UNIFICATION_TRACKER.md.\n */\n onCartDetected?: (cart: CartData) => void;\n}\n\nexport interface WidgetCSSCustomization {\n exitIntent?: {\n backgroundColor?: string;\n textColor?: string;\n accentColor?: string;\n borderRadius?: string;\n fontFamily?: string;\n overlayOpacity?: number;\n };\n chatBubble?: {\n backgroundColor?: string;\n textColor?: string;\n position?: 'bottom_right' | 'bottom_left' | 'top_right' | 'top_left';\n size?: 'small' | 'medium' | 'large';\n };\n}\n\nexport interface PrefetchWidgetConfig {\n exit_intent?: {\n enabled: boolean;\n type: 'cart_recovery' | 'lead_gen';\n tier?: string;\n title?: string;\n message?: string;\n discount_code?: string;\n discount_percentage?: number;\n cta_text?: string;\n cta_url?: string;\n background_color?: string;\n text_color?: string;\n accent_color?: string;\n show_cart_items?: boolean;\n show_timer?: boolean;\n timer_minutes?: number;\n priority: number;\n mobile_triggers?: {\n scroll_velocity?: boolean;\n scroll_threshold?: number;\n scroll_min_position?: number;\n scroll_cooldown?: number;\n idle_timer?: boolean;\n idle_seconds?: number;\n visibility_change?: boolean;\n back_button?: boolean;\n };\n };\n}\n\nexport interface CartData {\n cart_id: string;\n cart_total: number;\n cart_currency: string;\n cart_items: Array<{\n product_id: string;\n product_name?: string;\n quantity: number;\n price?: number;\n }>;\n}\n\nexport class AegisWidgetManager {\n private writeKey: string;\n private apiHost: string;\n private userId?: string;\n private contactId?: string;\n private contactToken?: string;\n private organizationId?: string;\n private debugMode: boolean;\n private triggerEngine?: TriggerEngine;\n private enablePrefetch: boolean;\n private cssCustomization: WidgetCSSCustomization;\n private onEvent?: (eventType: string, data: any) => void;\n private sourcePlatform?: string;\n private getWorkspaceId?: () => string | undefined;\n private onCartDetected?: (cart: CartData) => void;\n\n private widgets: WidgetConfig[] = [];\n private renderedWidgets = new Set<string>();\n private isInitialized = false;\n private isDestroyed = false;\n private ownsTriggerEngine: boolean;\n\n private prefetchWidgetConfigs: PrefetchWidgetConfig = {};\n private cartData?: CartData;\n \n constructor(config: AegisWidgetConfig) {\n this.writeKey = config.writeKey;\n this.apiHost = config.apiHost || 'https://api.aegis.ai';\n // Fall back to the SDK's persisted anonymous_id when callers don't pass\n // userId explicitly. Without this, /v1/widgets/config requests omit\n // X-User-ID and the cell-plane buckets the call into \"anonymous\", which\n // misses any user-targeted widget configs. Mirror in AegisInAppManager.\n this.userId = config.userId ?? readAnonIdFromStorage();\n this.contactId = config.contactId;\n this.contactToken = config.contactToken;\n this.organizationId = config.organizationId;\n this.debugMode = config.debugMode || false;\n this.triggerEngine = config.triggerEngine;\n this.ownsTriggerEngine = config.ownsTriggerEngine === true;\n this.enablePrefetch = config.enablePrefetch !== false;\n this.cssCustomization = config.cssCustomization || {};\n this.onEvent = config.onEvent;\n this.sourcePlatform = config.sourcePlatform;\n this.getWorkspaceId = config.getWorkspaceId;\n this.onCartDetected = config.onCartDetected;\n }\n \n updateCSSCustomization(customization: WidgetCSSCustomization): void {\n this.cssCustomization = { ...this.cssCustomization, ...customization };\n this.log('CSS customization updated');\n }\n\n /**\n * Swap the identity that outgoing widget requests are attributed to.\n * Safe to call at any time; if prefetch is enabled and the manager\n * has already initialised, this will re-fetch the per-contact widget\n * configs so subsequent renders target the new identity. Does NOT\n * re-render already-visible widgets — a full teardown requires destroy().\n */\n async updateContactId(contactId: string | undefined, contactToken?: string): Promise<void> {\n if (this.contactId === contactId && this.contactToken === contactToken) return;\n this.contactId = contactId;\n // C1b: a token is bound to ONE contact — a call that changes the id\n // without a matching token must not keep the previous contact's proof.\n this.contactToken = contactToken;\n this.log(`Contact ID updated → ${contactId ?? '(cleared)'}`);\n if (this.isInitialized && !this.isDestroyed && this.enablePrefetch && contactId) {\n await this.fetchPrefetchConfigs();\n this.preloadWidgetAssets();\n }\n }\n\n /**\n * Tear down everything the manager installed: trigger listeners (if\n * we own the TriggerEngine), injected style sheets, and any rendered\n * widget roots. Further render attempts from in-flight triggers are\n * short-circuited by the `isDestroyed` flag.\n */\n destroy(): void {\n if (this.isDestroyed) return;\n this.isDestroyed = true;\n\n if (this.triggerEngine && this.ownsTriggerEngine) {\n this.triggerEngine.stop();\n }\n\n if (typeof document !== 'undefined') {\n const animationStyle = document.getElementById('aegis-widget-animations');\n animationStyle?.remove();\n document.querySelectorAll('[data-aegis-widget-root]').forEach((node) => {\n node.remove();\n });\n }\n\n this.renderedWidgets.clear();\n this.widgets = [];\n this.isInitialized = false;\n this.log('AegisWidgets destroyed');\n this.emitEvent('destroyed', {});\n }\n\n /**\n * Fire a lifecycle event to the consumer-supplied callback. Public-API\n * hook configured via `AegisWidgetConfig.onEvent`. Currently only used\n * by external callers passing their own logger; internal widget lifecycle\n * events get wired through here as they're added.\n */\n private emitEvent(eventType: string, data: unknown): void {\n if (this.onEvent) {\n try {\n this.onEvent(eventType, data);\n } catch (error) {\n this.log(`Error in event callback: ${error}`, true);\n }\n }\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) {\n this.log('AegisWidgets already initialized');\n return;\n }\n \n if (this.enablePrefetch && this.contactId) {\n await this.fetchPrefetchConfigs();\n this.preloadWidgetAssets();\n }\n \n await this.fetchWidgets();\n \n this.renderImmediateWidgets();\n \n this.setupTriggerListeners();\n \n this.setupExitIntentWithPrefetch();\n \n this.isInitialized = true;\n this.log('AegisWidgets initialized successfully');\n }\n \n setCartData(cartData: CartData): void {\n this.cartData = cartData;\n this.log(`Cart data updated: ${cartData.cart_items.length} items, total: ${cartData.cart_currency} ${cartData.cart_total}`);\n // Flow the explicit cart feed UP into the ONE cart store.\n this.onCartDetected?.(cartData);\n }\n\n private detectPlatformCart(): CartData | null {\n const detected =\n this.normalizeShopifyCart() ??\n this.normalizeWooCart() ??\n this.normalizeMagentoCart();\n if (detected) {\n this.log('Detected platform cart (Shopify/Woo/Magento)');\n // Flow the auto-detected cart UP into the ONE cart store so the\n // Free-Delivery card / progress bar / rule eval see it too.\n this.onCartDetected?.(detected);\n return detected;\n }\n\n if (this.cartData) {\n this.log('Using manually set cart data');\n return this.cartData;\n }\n\n return null;\n }\n \n private normalizeShopifyCart(): CartData | null {\n try {\n if (typeof window === 'undefined') return null;\n \n const win = window as any;\n \n if (win.Shopify?.checkout) {\n const checkout = win.Shopify.checkout;\n return {\n cart_id: checkout.token || `shopify_${Date.now()}`,\n cart_total: parseFloat(checkout.total_price) || 0,\n cart_currency: checkout.currency || 'USD',\n cart_items: (checkout.line_items || []).map((item: any) => ({\n product_id: String(item.product_id || item.id),\n product_name: item.title || item.product_title,\n quantity: item.quantity || 1,\n price: parseFloat(item.price) || 0\n }))\n };\n }\n \n const cartJsonEl = document.getElementById('cart-json');\n if (cartJsonEl?.textContent) {\n const cart = JSON.parse(cartJsonEl.textContent);\n return {\n cart_id: cart.token || `shopify_${Date.now()}`,\n cart_total: (cart.total_price || 0) / 100,\n cart_currency: cart.currency || 'USD',\n cart_items: (cart.items || []).map((item: any) => ({\n product_id: String(item.product_id || item.id),\n product_name: item.product_title || item.title,\n quantity: item.quantity || 1,\n price: (item.price || 0) / 100\n }))\n };\n }\n \n return null;\n } catch (error) {\n this.log(`Error detecting Shopify cart: ${error}`, true);\n return null;\n }\n }\n \n private normalizeWooCart(): CartData | null {\n try {\n if (typeof window === 'undefined') return null;\n \n const win = window as any;\n \n if (win.aegis_cart) {\n const cart = win.aegis_cart;\n return {\n cart_id: cart.cart_id || `woo_${Date.now()}`,\n cart_total: parseFloat(cart.cart_total) || 0,\n cart_currency: cart.cart_currency || 'USD',\n cart_items: (cart.cart_items || []).map((item: any) => ({\n product_id: String(item.product_id),\n product_name: item.product_name,\n quantity: item.quantity || 1,\n price: parseFloat(item.price) || 0\n }))\n };\n }\n \n return null;\n } catch (error) {\n this.log(`Error detecting WooCommerce cart: ${error}`, true);\n return null;\n }\n }\n \n private normalizeMagentoCart(): CartData | null {\n try {\n if (typeof window === 'undefined' || !window.localStorage) return null;\n \n const mageCacheStr = localStorage.getItem('mage-cache-storage');\n if (!mageCacheStr) return null;\n \n const mageCache = JSON.parse(mageCacheStr);\n if (!mageCache?.cart) return null;\n \n const cart = mageCache.cart;\n \n return {\n cart_id: cart.quote_id || cart.id || `magento_${Date.now()}`,\n cart_total: parseFloat(cart.subtotalAmount || cart.subtotal || 0),\n cart_currency: cart.currencyCode || 'USD',\n cart_items: (cart.items || []).map((item: any) => ({\n product_id: String(item.item_id || item.product_id),\n product_name: item.product_name || item.name,\n quantity: item.qty || 1,\n price: parseFloat(item.product_price_value || item.price || 0)\n }))\n };\n } catch (error) {\n this.log(`Error detecting Magento cart: ${error}`, true);\n return null;\n }\n }\n \n private preloadWidgetAssets(): void {\n try {\n const exitIntentConfig = this.prefetchWidgetConfigs.exit_intent;\n if (exitIntentConfig?.enabled) {\n const imageUrl = (exitIntentConfig as any).image_url;\n if (imageUrl) {\n const img = new Image();\n img.src = imageUrl;\n this.log(`Preloading exit intent image: ${imageUrl}`);\n }\n }\n } catch (error) {\n this.log(`Error preloading widget assets: ${error}`, true);\n }\n }\n \n private async fetchPrefetchConfigs(): Promise<void> {\n try {\n const startTime = performance.now();\n const url = `${this.apiHost}/v1/widgets/config/prefetch`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n };\n \n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.contactId && this.contactToken) headers['X-Contact-Token'] = this.contactToken; // C1b\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n const response = await fetch(url, { headers, credentials: 'include' });\n \n if (!response.ok) {\n throw new Error(`Failed to fetch prefetch configs: ${response.status}`);\n }\n \n this.prefetchWidgetConfigs = await response.json();\n \n const elapsed = performance.now() - startTime;\n this.log(`Fetched prefetch widget configs in ${elapsed.toFixed(2)}ms`);\n \n } catch (error) {\n this.log(`Error fetching prefetch configs: ${error}`, true);\n }\n }\n \n private async fetchWidgets(): Promise<void> {\n try {\n const url = `${this.apiHost}/v1/widgets/config`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Device-Platform': 'web',\n 'X-Device-Type': this.getDeviceType()\n };\n\n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.contactId && this.contactToken) headers['X-Contact-Token'] = this.contactToken; // C1b\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n if (this.sourcePlatform) headers['X-Source-Platform'] = this.sourcePlatform;\n \n const response = await fetch(url, { headers, credentials: 'include' });\n \n if (!response.ok) {\n throw new Error(`Failed to fetch widgets: ${response.status}`);\n }\n \n this.widgets = await response.json();\n \n this.log(`Fetched ${this.widgets.length} widgets`);\n \n } catch (error) {\n this.log(`Error fetching widgets: ${error}`, true);\n }\n }\n \n private renderImmediateWidgets(): void {\n const immediateWidgets = this.widgets.filter(w => \n !w.trigger_rules || w.trigger_rules.type === 'immediate'\n );\n \n immediateWidgets.forEach(widget => this.renderWidget(widget));\n }\n \n private setupTriggerListeners(): void {\n if (!this.triggerEngine) {\n this.log('TriggerEngine not provided, skipping trigger-based widgets');\n return;\n }\n \n this.widgets.forEach(widget => {\n if (!widget.trigger_rules) return;\n \n const { type, config } = widget.trigger_rules;\n \n switch (type) {\n case 'exit_intent':\n this.triggerEngine!.registerExitIntent();\n this.triggerEngine!.on('exit_intent', () => {\n if (!this.renderedWidgets.has(widget.widget_id)) {\n this.renderWidget(widget);\n }\n });\n break;\n \n case 'scroll_depth':\n const depthPercent = (config as any)?.depth_percent || 50;\n this.triggerEngine!.registerScrollDepth(depthPercent);\n this.triggerEngine!.on(`scroll_depth_${depthPercent}`, () => {\n if (!this.renderedWidgets.has(widget.widget_id)) {\n this.renderWidget(widget);\n }\n });\n break;\n \n case 'time_on_page':\n const seconds = (config as any)?.seconds || 30;\n this.triggerEngine!.registerTimeOnPage(seconds);\n this.triggerEngine!.on(`time_on_page_${seconds}`, () => {\n if (!this.renderedWidgets.has(widget.widget_id)) {\n this.renderWidget(widget);\n }\n });\n break;\n }\n });\n }\n \n private setupExitIntentWithPrefetch(): void {\n if (!this.enablePrefetch || !this.triggerEngine) {\n return;\n }\n \n const exitIntentConfig = this.prefetchWidgetConfigs.exit_intent;\n \n if (!exitIntentConfig || !exitIntentConfig.enabled) {\n this.log('No exit intent config found in prefetch');\n return;\n }\n \n const isMobile = this.isMobileDevice();\n const mobileConfig = exitIntentConfig.mobile_triggers || {};\n \n const handleExitIntent = () => {\n const widgetId = `prefetch_exit_intent_${Date.now()}`;\n \n if (this.renderedWidgets.has(widgetId)) {\n return;\n }\n \n this.renderedWidgets.add(widgetId);\n \n const detectedCart = this.detectPlatformCart();\n \n if (exitIntentConfig.type === 'cart_recovery' && detectedCart) {\n this.cartData = detectedCart;\n this.renderCartRecoveryPopup(widgetId, exitIntentConfig);\n this.sendCartAbandonmentBeacon();\n } else if (exitIntentConfig.type === 'lead_gen') {\n this.renderLeadGenPopup(widgetId, exitIntentConfig);\n } else {\n this.log('Unknown exit intent type or missing cart data');\n }\n };\n \n if (isMobile) {\n const scrollVelocityEnabled = mobileConfig.scroll_velocity !== false;\n if (scrollVelocityEnabled) {\n this.triggerEngine.registerScrollVelocity({\n threshold: mobileConfig.scroll_threshold || 0.5,\n minScrollPosition: mobileConfig.scroll_min_position || 100,\n cooldown: mobileConfig.scroll_cooldown || 5000\n });\n this.triggerEngine.on('mobile_exit_intent', handleExitIntent);\n this.log('Registered mobile scroll velocity trigger');\n }\n \n const idleTimerEnabled = mobileConfig.idle_timer !== false;\n if (idleTimerEnabled) {\n const idleSeconds = mobileConfig.idle_seconds || 15;\n this.triggerEngine.registerInactivity(idleSeconds);\n this.triggerEngine.on(`inactivity_${idleSeconds}`, handleExitIntent);\n this.log(`Registered mobile idle timer trigger: ${idleSeconds}s`);\n }\n \n const visibilityChangeEnabled = mobileConfig.visibility_change !== false;\n if (visibilityChangeEnabled) {\n const detectedCart = this.detectPlatformCart();\n if (detectedCart && detectedCart.cart_items && detectedCart.cart_items.length > 0) {\n this.triggerEngine.registerVisibilityChange();\n this.triggerEngine.on('visibility_hidden', handleExitIntent);\n this.log('Registered mobile visibility change trigger (cart detected)');\n } else {\n this.log('Skipped visibility change trigger (no cart items)');\n }\n }\n \n const backButtonEnabled = mobileConfig.back_button === true;\n if (backButtonEnabled) {\n this.triggerEngine.registerBackButton();\n this.triggerEngine.on('back_button', handleExitIntent);\n this.log('Registered mobile back button trigger (aggressive mode)');\n }\n \n this.log(`Setup mobile exit intent: type=${exitIntentConfig.type}, priority=${exitIntentConfig.priority}`);\n } else {\n this.triggerEngine.registerExitIntent();\n this.triggerEngine.on('exit_intent', handleExitIntent);\n this.log(`Setup desktop exit intent: type=${exitIntentConfig.type}, priority=${exitIntentConfig.priority}`);\n }\n }\n \n private sendCartAbandonmentBeacon(): void {\n if (!this.cartData) {\n this.log('No cart data available for beacon', true);\n return;\n }\n \n const beaconData = {\n organization_id: this.organizationId || 'default',\n contact_id: this.contactId,\n cart_id: this.cartData.cart_id,\n cart_total: this.cartData.cart_total,\n cart_currency: this.cartData.cart_currency,\n cart_items: this.cartData.cart_items,\n user_email: this.userId,\n abandoned_at: new Date().toISOString(),\n page_url: redactUrl(window.location.href),\n referrer: redactUrl(document.referrer)\n };\n \n const beaconUrl = `${this.apiHost}/v1/widgets/beacon/cart-abandoned`;\n \n if (navigator.sendBeacon) {\n const blob = new Blob([JSON.stringify(beaconData)], { type: 'application/json' });\n const sent = navigator.sendBeacon(beaconUrl, blob);\n \n if (sent) {\n this.log('Cart abandonment beacon sent successfully');\n } else {\n this.log('Failed to send cart abandonment beacon', true);\n }\n } else {\n fetch(beaconUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey\n },\n body: JSON.stringify(beaconData),\n keepalive: true,\n credentials: 'include',\n }).catch(err => {\n this.log(`Error sending cart abandonment via fetch: ${err}`, true);\n });\n }\n }\n \n private renderWidget(widget: WidgetConfig): void {\n if (this.isDestroyed) return;\n if (this.renderedWidgets.has(widget.widget_id)) {\n return;\n }\n\n this.renderedWidgets.add(widget.widget_id);\n \n switch (widget.widget_type) {\n case 'chat_bubble':\n this.renderChatBubble(widget);\n break;\n case 'toast':\n this.renderToast(widget);\n break;\n case 'feedback_form':\n this.renderFeedbackForm(widget);\n break;\n case 'exit_intent_popup':\n this.renderExitIntentPopup(widget);\n break;\n default:\n this.log(`Unknown widget type: ${widget.widget_type}`, true);\n }\n \n this.trackEvent(widget.widget_id, 'show');\n }\n \n private renderChatBubble(widget: WidgetConfig): void {\n const { text, icon_url, link_url, background_color, text_color } = widget.config;\n const position = widget.position || 'bottom_right';\n \n const bubble = document.createElement('div');\n bubble.className = 'aegis-chat-bubble';\n bubble.setAttribute('data-widget-id', widget.widget_id);\n \n const positionStyles: Record<string, string> = {\n bottom_right: 'bottom: 20px; right: 20px;',\n bottom_left: 'bottom: 20px; left: 20px;',\n top_right: 'top: 20px; right: 20px;',\n top_left: 'top: 20px; left: 20px;'\n };\n \n bubble.style.cssText = `\n position: fixed;\n ${positionStyles[position] || positionStyles.bottom_right}\n background: ${this.sanitizeColor(background_color || '#25D366')};\n color: ${this.sanitizeColor(text_color || '#ffffff')};\n padding: 16px 24px;\n border-radius: 50px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n cursor: pointer;\n z-index: 999999;\n display: flex;\n align-items: center;\n gap: 12px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n font-weight: 600;\n font-size: 14px;\n animation: aegisBubbleIn 0.3s ease-out;\n `;\n \n if (icon_url) {\n const icon = document.createElement('img');\n const safeUrl = this.sanitizeUrl(icon_url);\n if (safeUrl) {\n icon.src = safeUrl;\n icon.alt = '';\n icon.style.cssText = 'width: 24px; height: 24px;';\n bubble.appendChild(icon);\n }\n }\n \n const textEl = document.createElement('span');\n textEl.textContent = text || 'Chat with us';\n bubble.appendChild(textEl);\n \n bubble.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'click');\n const safeUrl = this.sanitizeUrl(link_url);\n if (safeUrl) {\n window.open(safeUrl, '_blank');\n }\n });\n \n this.addAnimationStyles();\n bubble.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(bubble);\n }\n \n private getUTMParams(): { utm_source?: string; utm_medium?: string; utm_campaign?: string } {\n const params = new URLSearchParams(window.location.search);\n return {\n utm_source: params.get('utm_source') || undefined,\n utm_medium: params.get('utm_medium') || undefined,\n utm_campaign: params.get('utm_campaign') || undefined\n };\n }\n \n private renderToast(widget: WidgetConfig): void {\n const { message, icon, duration } = widget.config;\n const position = widget.position || 'bottom_left';\n \n const toast = document.createElement('div');\n toast.className = 'aegis-toast';\n toast.setAttribute('data-widget-id', widget.widget_id);\n \n const positionStyles: Record<string, string> = {\n bottom_left: 'bottom: 20px; left: 20px;',\n bottom_right: 'bottom: 20px; right: 20px;',\n top_left: 'top: 20px; left: 20px;',\n top_right: 'top: 20px; right: 20px;'\n };\n \n toast.style.cssText = `\n position: fixed;\n ${positionStyles[position] || positionStyles.bottom_left}\n background: white;\n padding: 16px 20px;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n z-index: 999998;\n display: flex;\n align-items: center;\n gap: 12px;\n max-width: 350px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisToastIn 0.3s ease-out;\n `;\n \n if (icon) {\n const iconEl = document.createElement('div');\n iconEl.textContent = icon;\n iconEl.style.cssText = 'font-size: 20px;';\n toast.appendChild(iconEl);\n }\n \n const messageEl = document.createElement('div');\n messageEl.textContent = message || 'Someone just made a purchase!';\n messageEl.style.cssText = 'flex: 1; font-size: 13px; color: #333;';\n toast.appendChild(messageEl);\n \n this.addAnimationStyles();\n toast.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(toast);\n \n setTimeout(() => {\n toast.style.animation = 'aegisToastOut 0.3s ease-out';\n setTimeout(() => {\n if (document.body.contains(toast)) {\n document.body.removeChild(toast);\n }\n this.renderedWidgets.delete(widget.widget_id);\n }, 300);\n }, duration || 5000);\n }\n \n private renderFeedbackForm(widget: WidgetConfig): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-feedback-overlay';\n overlay.setAttribute('data-widget-id', widget.widget_id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 400px;\n background: white;\n box-shadow: -4px 0 20px rgba(0,0,0,0.15);\n z-index: 1000000;\n padding: 32px;\n overflow-y: auto;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideInRight 0.3s ease-out;\n `;\n \n const title = document.createElement('h2');\n title.textContent = widget.config.title || 'We value your feedback!';\n title.style.cssText = 'margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #1a1a1a;';\n overlay.appendChild(title);\n \n const description = document.createElement('p');\n description.textContent = widget.config.description || 'Help us improve your experience.';\n description.style.cssText = 'margin: 0 0 24px 0; font-size: 14px; color: #666;';\n overlay.appendChild(description);\n \n const form = document.createElement('form');\n \n const label = document.createElement('label');\n label.textContent = 'How likely are you to recommend us? (0-10)';\n label.style.cssText = 'display: block; font-size: 14px; font-weight: 600; margin-bottom: 8px; color: #333;';\n form.appendChild(label);\n \n const input = document.createElement('input');\n input.type = 'number';\n input.min = '0';\n input.max = '10';\n input.style.cssText = `\n width: 100%;\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n margin-bottom: 16px;\n `;\n form.appendChild(input);\n \n const textareaLabel = document.createElement('label');\n textareaLabel.textContent = 'Tell us more (optional)';\n textareaLabel.style.cssText = 'display: block; font-size: 14px; font-weight: 600; margin-bottom: 8px; color: #333;';\n form.appendChild(textareaLabel);\n \n const textarea = document.createElement('textarea');\n textarea.rows = 4;\n textarea.style.cssText = `\n width: 100%;\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n resize: vertical;\n margin-bottom: 16px;\n font-family: inherit;\n `;\n form.appendChild(textarea);\n \n const submitButton = document.createElement('button');\n submitButton.type = 'submit';\n submitButton.textContent = 'Submit Feedback';\n submitButton.style.cssText = `\n width: 100%;\n background: #1a73e8;\n color: white;\n border: none;\n padding: 12px;\n border-radius: 6px;\n font-weight: 600;\n font-size: 14px;\n cursor: pointer;\n `;\n form.appendChild(submitButton);\n \n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n \n submitButton.disabled = true;\n submitButton.textContent = 'Submitting...';\n \n try {\n await this.submitFeedback(widget.widget_id, {\n score: parseInt(input.value),\n comment: textarea.value\n });\n \n overlay.innerHTML = '<div style=\"text-align: center; padding: 60px 20px;\"><h3 style=\"margin: 0 0 12px 0; color: #1a73e8;\">Thank you!</h3><p style=\"margin: 0; color: #666;\">Your feedback helps us improve.</p></div>';\n \n setTimeout(() => {\n document.body.removeChild(overlay);\n }, 2000);\n \n } catch (error) {\n this.log(`Error submitting feedback: ${error}`, true);\n submitButton.disabled = false;\n submitButton.textContent = 'Submit Feedback';\n }\n });\n \n overlay.appendChild(form);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: transparent;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #999;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'dismiss');\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widget.widget_id);\n });\n \n overlay.appendChild(closeButton);\n \n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n }\n \n private renderExitIntentPopup(widget: WidgetConfig): void {\n const { title, description, cta_text, cta_url, image_url } = widget.config;\n \n const overlay = document.createElement('div');\n overlay.className = 'aegis-exit-popup-overlay';\n overlay.setAttribute('data-widget-id', widget.widget_id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-exit-popup-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 16px;\n max-width: 600px;\n width: 90%;\n overflow: hidden;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n `;\n \n if (image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 100%; height: 250px; object-fit: cover;';\n modal.appendChild(img);\n }\n }\n \n const content = document.createElement('div');\n content.style.cssText = 'padding: 32px;';\n \n const titleEl = document.createElement('h2');\n titleEl.textContent = title || 'Wait! Don\\'t leave yet!';\n titleEl.style.cssText = 'margin: 0 0 16px 0; font-size: 28px; font-weight: 700; color: #1a1a1a;';\n content.appendChild(titleEl);\n \n const descEl = document.createElement('p');\n descEl.textContent = description || 'Get 10% off your first order!';\n descEl.style.cssText = 'margin: 0 0 24px 0; font-size: 16px; color: #666; line-height: 1.5;';\n content.appendChild(descEl);\n \n if (cta_text && cta_url) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = cta_text;\n ctaButton.style.cssText = `\n background: #1a73e8;\n color: white;\n border: none;\n padding: 16px 32px;\n border-radius: 8px;\n font-weight: 600;\n font-size: 16px;\n cursor: pointer;\n width: 100%;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'click');\n const safeUrl = this.sanitizeUrl(cta_url);\n if (safeUrl) {\n window.location.href = safeUrl;\n }\n });\n \n content.appendChild(ctaButton);\n }\n \n modal.appendChild(content);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: white;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #666;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'dismiss');\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widget.widget_id);\n });\n \n modal.style.position = 'relative';\n modal.appendChild(closeButton);\n \n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n }\n \n private async submitFeedback(widgetId: string, data: Record<string, any>): Promise<void> {\n const url = `${this.apiHost}/v1/widgets/track-event`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.contactId && this.contactToken) headers['X-Contact-Token'] = this.contactToken; // C1b\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n // Plumb workspace cascade so feedback submissions are attributed to\n // the same outlet the customer was viewing. See\n // PLUGIN_HANDSHAKE_AUTOMATION_TRACKER.md §P1.b.\n const workspaceId = this.getWorkspaceId?.();\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n widget_id: widgetId,\n event_type: 'submit',\n event_data: data,\n workspace_id: workspaceId,\n }),\n credentials: 'include',\n });\n \n if (!response.ok) {\n throw new Error(`Failed to submit feedback: ${response.status}`);\n }\n }\n \n private async trackEvent(widgetId: string, eventType: string, metadata?: Record<string, any>): Promise<void> {\n try {\n const url = `${this.apiHost}/v1/widgets/track-event`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.contactId && this.contactToken) headers['X-Contact-Token'] = this.contactToken; // C1b\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n\n const workspaceId = this.getWorkspaceId?.();\n\n await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n widget_id: widgetId,\n event_type: eventType,\n event_data: metadata || {},\n workspace_id: workspaceId,\n }),\n credentials: 'include',\n });\n\n } catch (error) {\n this.log(`Error tracking widget event: ${error}`, true);\n }\n }\n \n private sanitizeUrl(url?: string): string | null {\n if (!url) return null;\n \n try {\n const parsed = new URL(url, window.location.href);\n if (parsed.protocol === 'javascript:' || parsed.protocol === 'data:') {\n this.log(`Blocked dangerous URL: ${url}`, true);\n return null;\n }\n return parsed.href;\n } catch {\n this.log(`Invalid URL: ${url}`, true);\n return null;\n }\n }\n \n private sanitizeColor(color: string): string {\n if (/^#[0-9A-F]{3,8}$/i.test(color)) {\n return color;\n }\n \n if (/^rgb\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)$/i.test(color)) {\n return color;\n }\n \n if (/^rgba\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*[\\d.]+\\s*\\)$/i.test(color)) {\n return color;\n }\n \n return '#1a73e8';\n }\n \n private getDeviceType(): string {\n const ua = navigator.userAgent;\n if (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\n return 'tablet';\n }\n if (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\n return 'mobile';\n }\n return 'desktop';\n }\n \n private isMobileDevice(): boolean {\n const deviceType = this.getDeviceType();\n return deviceType === 'mobile' || deviceType === 'tablet';\n }\n \n private addAnimationStyles(): void {\n if (document.getElementById('aegis-widget-animations')) {\n return;\n }\n \n const style = document.createElement('style');\n style.id = 'aegis-widget-animations';\n style.textContent = `\n @keyframes aegisFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n \n @keyframes aegisScaleIn {\n from { transform: scale(0.8); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n }\n \n @keyframes aegisBubbleIn {\n from { transform: translateY(20px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n \n @keyframes aegisToastIn {\n from { transform: translateX(-100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n }\n \n @keyframes aegisToastOut {\n from { transform: translateX(0); opacity: 1; }\n to { transform: translateX(-100%); opacity: 0; }\n }\n \n @keyframes aegisSlideInRight {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n \n @keyframes aegisSpin {\n from { transform: rotate(0deg); }\n to { transform: rotate(1440deg); }\n }\n `;\n document.head.appendChild(style);\n }\n \n private renderCartRecoveryPopup(widgetId: string, config: NonNullable<PrefetchWidgetConfig['exit_intent']>): void {\n if (this.isDestroyed) return;\n const overlay = document.createElement('div');\n overlay.className = 'aegis-exit-popup-overlay';\n overlay.setAttribute('data-widget-id', widgetId);\n overlay.setAttribute('data-aegis-widget-root', '');\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-cart-recovery-modal';\n modal.style.cssText = `\n background: ${this.sanitizeColor(config.background_color || '#FFFFFF')};\n color: ${this.sanitizeColor(config.text_color || '#333333')};\n border-radius: 16px;\n max-width: 600px;\n width: 90%;\n padding: 40px;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n position: relative;\n `;\n \n const title = document.createElement('h2');\n const titleText = this.interpolateCartVariables(config.title || 'Complete Your Order');\n title.textContent = titleText;\n title.style.cssText = `margin: 0 0 16px 0; font-size: 28px; font-weight: 700; color: ${this.sanitizeColor(config.accent_color || '#FF6B00')};`;\n modal.appendChild(title);\n \n const message = document.createElement('p');\n const messageText = this.interpolateCartVariables(config.message || 'Your cart is waiting for you!');\n message.textContent = messageText;\n message.style.cssText = 'margin: 0 0 24px 0; font-size: 16px; line-height: 1.5;';\n modal.appendChild(message);\n \n if (config.show_cart_items && this.cartData) {\n const cartItems = document.createElement('div');\n cartItems.style.cssText = 'margin: 0 0 24px 0; padding: 16px; background: rgba(0,0,0,0.05); border-radius: 8px;';\n \n const cartTitle = document.createElement('div');\n cartTitle.textContent = `${this.cartData.cart_items.length} items in your cart`;\n cartTitle.style.cssText = 'font-weight: 600; margin-bottom: 12px;';\n cartItems.appendChild(cartTitle);\n \n this.cartData.cart_items.slice(0, 3).forEach(item => {\n const itemDiv = document.createElement('div');\n itemDiv.textContent = `${item.quantity}× ${item.product_name || item.product_id}`;\n itemDiv.style.cssText = 'font-size: 14px; margin-bottom: 4px;';\n cartItems.appendChild(itemDiv);\n });\n \n modal.appendChild(cartItems);\n }\n \n if (config.discount_code) {\n const discountBox = document.createElement('div');\n discountBox.style.cssText = `\n margin: 0 0 24px 0;\n padding: 16px;\n background: ${this.sanitizeColor(config.accent_color || '#FF6B00')};\n color: white;\n border-radius: 8px;\n text-align: center;\n font-weight: 700;\n font-size: 18px;\n `;\n discountBox.textContent = `Use code: ${config.discount_code} for ${config.discount_percentage || 10}% off!`;\n modal.appendChild(discountBox);\n }\n \n if (config.show_timer && config.timer_minutes) {\n const timer = document.createElement('div');\n timer.style.cssText = 'margin: 0 0 24px 0; display: flex; align-items: center; justify-content: center; gap: 6px; font-size: 14px; color: #999;';\n timer.innerHTML = lucideSvg('clock', { size: 14 });\n timer.appendChild(document.createTextNode(`Offer expires in ${config.timer_minutes} minutes`));\n modal.appendChild(timer);\n }\n \n const ctaButton = document.createElement('button');\n ctaButton.textContent = config.cta_text || 'Complete Checkout';\n ctaButton.style.cssText = `\n background: ${this.sanitizeColor(config.accent_color || '#FF6B00')};\n color: white;\n border: none;\n padding: 16px 32px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n width: 100%;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(widgetId, 'click', { type: 'cart_recovery' });\n const checkoutUrl = this.sanitizeUrl(config.cta_url || '/checkout');\n if (checkoutUrl) {\n window.location.href = checkoutUrl;\n }\n });\n \n modal.appendChild(ctaButton);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: transparent;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #999;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widgetId, 'dismiss', { type: 'cart_recovery' });\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widgetId);\n });\n \n modal.appendChild(closeButton);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n \n this.trackEvent(widgetId, 'show', { type: 'cart_recovery', tier: config.tier });\n }\n \n private renderLeadGenPopup(widgetId: string, config: NonNullable<PrefetchWidgetConfig['exit_intent']>): void {\n if (this.isDestroyed) return;\n const overlay = document.createElement('div');\n overlay.className = 'aegis-exit-popup-overlay';\n overlay.setAttribute('data-widget-id', widgetId);\n overlay.setAttribute('data-aegis-widget-root', '');\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-leadgen-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 16px;\n max-width: 500px;\n width: 90%;\n padding: 40px;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n text-align: center;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n position: relative;\n `;\n \n const title = document.createElement('h2');\n title.textContent = config.title || 'Get 10% Off Your First Order!';\n title.style.cssText = 'margin: 0 0 16px 0; font-size: 28px; font-weight: 700; color: #1a1a1a;';\n modal.appendChild(title);\n \n const message = document.createElement('p');\n message.textContent = config.message || 'Subscribe to our newsletter and get an exclusive discount code.';\n message.style.cssText = 'margin: 0 0 24px 0; font-size: 16px; color: #666;';\n modal.appendChild(message);\n \n const form = document.createElement('form');\n \n const emailInput = document.createElement('input');\n emailInput.type = 'email';\n emailInput.placeholder = 'Enter your email';\n emailInput.required = true;\n emailInput.style.cssText = `\n width: 100%;\n padding: 14px;\n border: 2px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n margin-bottom: 16px;\n box-sizing: border-box;\n `;\n form.appendChild(emailInput);\n \n const submitButton = document.createElement('button');\n submitButton.type = 'submit';\n submitButton.textContent = 'Get My Discount';\n submitButton.style.cssText = `\n width: 100%;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border: none;\n padding: 16px 32px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n `;\n form.appendChild(submitButton);\n \n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n \n submitButton.disabled = true;\n submitButton.textContent = 'Submitting...';\n \n try {\n await this.trackEvent(widgetId, 'submit', { \n type: 'lead_gen',\n email: emailInput.value \n });\n \n modal.innerHTML = `\n <div style=\"text-align: center; padding: 20px;\">\n <div style=\"margin-bottom: 16px; color: #10b981;\">${lucideSvg('check', { size: 48 })}</div>\n <h3 style=\"margin: 0 0 12px 0; color: #1a73e8;\">Thank You!</h3>\n <p style=\"margin: 0; color: #666;\">Check your email for your discount code!</p>\n </div>\n `;\n \n setTimeout(() => {\n document.body.removeChild(overlay);\n }, 2500);\n \n } catch (error) {\n this.log(`Error submitting lead gen form: ${error}`, true);\n submitButton.disabled = false;\n submitButton.textContent = 'Get My Discount';\n }\n });\n \n modal.appendChild(form);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: transparent;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #999;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widgetId, 'dismiss', { type: 'lead_gen' });\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widgetId);\n });\n \n modal.appendChild(closeButton);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n \n this.trackEvent(widgetId, 'show', { type: 'lead_gen' });\n }\n \n private interpolateCartVariables(template: string): string {\n if (!this.cartData) return template;\n \n return template\n .replace(/\\{\\{cart_total\\}\\}/g, `${this.cartData.cart_currency} ${this.cartData.cart_total.toFixed(2)}`)\n .replace(/\\{\\{cart_currency\\}\\}/g, this.cartData.cart_currency)\n .replace(/\\{\\{cart_items_count\\}\\}/g, this.cartData.cart_items.length.toString());\n }\n \n private log(message: string, isError: boolean = false): void {\n if (this.debugMode || isError) {\n const method = isError ? 'error' : 'log';\n console[method](`[AegisWidgets] ${message}`);\n }\n }\n}\n","/**\n * Smart trigger (`micro_intent`) arming — the production caller the\n * IntentRuleEvaluator never had (Aman 2026-09-28, plan\n * GAMIFICATION_ENGAGEMENT_TARGETING_PLAN §0d).\n *\n * A `micro_intent` campaign is served as `client_trigger: { type:\n * 'micro_intent', config: <IntentRule> }`. Before this module nothing called\n * `setArmed`, nothing turned a TriggerEngine event into an evaluator signal,\n * and `converted_in_session` was never written — so `NOT(converted)` stayed\n * null and every rule carrying it could never fire.\n *\n * This arms the evaluator on every campaign load, registers the engine\n * thresholds the rules read (the `fire_on` signals and every numeric leaf of\n * `time_on_page` / `scroll_depth` / `inactivity`), writes each engine event\n * into the snapshot, and asks the evaluator for a decision.\n *\n * Signals owned elsewhere: Layer-1 scores (ContactScoresFetcher), `cart_value`\n * (runtime updateContext). Hover / rage-click / SKU dwell need the\n * IntentSnapshotCollector + SelectorBinder, which no host wires yet — rules on\n * them stay unmet (null), never fire.\n */\n\nimport type { TriggerEngine, TriggerEvent } from './TriggerEngine';\nimport type {\n ArmedCampaign,\n IntentExpr,\n IntentRule,\n IntentRuleEvaluator,\n IntentSignal,\n} from './IntentRuleEvaluator';\nimport {\n DEFAULT_IDLE_SECONDS,\n DEFAULT_SCROLL_DEPTH_PERCENT,\n DEFAULT_TIME_ON_PAGE_SECONDS,\n type ClientTrigger,\n} from './behaviouralTriggers';\n\nexport const MICRO_INTENT_TRIGGER_TYPE = 'micro_intent';\n\n/** Signals whose value is a count the engine reports at registered thresholds. */\nconst THRESHOLD_SIGNALS = {\n time_on_page: DEFAULT_TIME_ON_PAGE_SECONDS,\n scroll_depth: DEFAULT_SCROLL_DEPTH_PERCENT,\n inactivity: DEFAULT_IDLE_SECONDS,\n} as const;\ntype ThresholdSignal = keyof typeof THRESHOLD_SIGNALS;\n\nconst THRESHOLD_EVENT = /^(time_on_page|scroll_depth|inactivity)_(\\d+(?:\\.\\d+)?)$/;\n\n/** The rule a `micro_intent` trigger carries, or null when it is not one or is malformed. */\nexport function microIntentRule(trigger: ClientTrigger | null | undefined): IntentRule | null {\n if (!trigger || trigger.type !== MICRO_INTENT_TRIGGER_TYPE) return null;\n const cfg = trigger.config || {};\n const fireOn = cfg.fire_on;\n const when = cfg.when;\n if (!Array.isArray(fireOn) || fireOn.length === 0) return null;\n if (!fireOn.every((s) => typeof s === 'string')) return null;\n if (!when || typeof when !== 'object') return null;\n const priority = Number(cfg.priority);\n return {\n fire_on: fireOn as IntentSignal[],\n when: when as IntentExpr,\n priority: Number.isFinite(priority) ? priority : 0,\n suppress_competing: cfg.suppress_competing === true,\n };\n}\n\n/** Every loaded campaign whose client trigger is a well-formed smart trigger. */\nexport function armedMicroIntentCampaigns(\n campaigns: ReadonlyArray<{ id: string; client_trigger?: ClientTrigger | null }>,\n): ArmedCampaign[] {\n const armed: ArmedCampaign[] = [];\n for (const c of campaigns) {\n const rule = microIntentRule(c.client_trigger);\n if (rule) armed.push({ id: c.id, rule });\n }\n return armed;\n}\n\n/**\n * The engine thresholds at which `signal` must be observed for `rules` to be\n * decided truthfully: each numeric leaf's crossing point (`gte 8` → 8,\n * `gt 8` → 9), plus the builder default when a rule fires on the signal but\n * never names a value.\n */\nexport function signalThresholds(rules: ReadonlyArray<IntentRule>, signal: ThresholdSignal): number[] {\n const out = new Set<number>();\n const visit = (expr: IntentExpr): void => {\n if ('operands' in expr) {\n expr.operands.forEach(visit);\n return;\n }\n if (expr.signal !== signal || typeof expr.value !== 'number') return;\n const crossing = expr.op === 'gt' || expr.op === 'lte' ? Math.floor(expr.value) + 1 : Math.ceil(expr.value);\n if (crossing > 0) out.add(crossing);\n };\n for (const rule of rules) {\n visit(rule.when);\n if (rule.fire_on.includes(signal) && !hasLeaf(rule.when, signal)) out.add(THRESHOLD_SIGNALS[signal]);\n }\n return [...out].sort((a, b) => a - b);\n}\n\nfunction hasLeaf(expr: IntentExpr, signal: IntentSignal): boolean {\n if ('operands' in expr) return expr.operands.some((o) => hasLeaf(o, signal));\n return expr.signal === signal;\n}\n\nexport interface MicroIntentArmingConfig {\n engine: TriggerEngine;\n evaluator: IntentRuleEvaluator;\n /** Display the campaign; true only when it actually showed. */\n show: (campaignId: string) => boolean;\n /** Whether this session already converted (the in-app manager's record). */\n hasConverted: () => boolean;\n /** No hovering pointer: exit intent is the engine's fast scroll up. */\n isMobile: boolean;\n}\n\nexport class MicroIntentArming {\n private readonly cfg: MicroIntentArmingConfig;\n private readonly registered = new Set<string>();\n private listening = false;\n private readonly onEngineEvent = (event: TriggerEvent): void => this.handle(event.type);\n\n constructor(cfg: MicroIntentArmingConfig) {\n this.cfg = cfg;\n }\n\n /** Replace the armed rules with the smart triggers among `campaigns`. */\n arm(campaigns: ReadonlyArray<{ id: string; client_trigger?: ClientTrigger | null }>): void {\n const armed = armedMicroIntentCampaigns(campaigns);\n this.cfg.evaluator.setArmed(armed);\n if (armed.length === 0) return;\n this.listen();\n const rules = armed.map((a) => a.rule);\n const { engine } = this.cfg;\n for (const signal of Object.keys(THRESHOLD_SIGNALS) as ThresholdSignal[]) {\n for (const n of signalThresholds(rules, signal)) {\n if (!this.once(`${signal}_${n}`)) continue;\n if (signal === 'time_on_page') engine.registerTimeOnPage(n);\n else if (signal === 'scroll_depth') engine.registerScrollDepth(n);\n else engine.registerInactivity(n);\n }\n }\n if (rules.some((r) => r.fire_on.includes('exit_intent')) && this.once('exit_intent')) {\n if (this.cfg.isMobile) engine.registerScrollVelocity();\n else engine.registerExitIntent();\n }\n if (rules.some((r) => r.fire_on.includes('back_button')) && this.once('back_button')) {\n engine.registerBackButton();\n }\n }\n\n /** The host reported a conversion: rules gated on NOT converted stop matching. */\n noteConversion(): void {\n this.cfg.evaluator.updateSignal('converted_in_session', true);\n }\n\n destroy(): void {\n if (this.listening) this.cfg.engine.off('*', this.onEngineEvent);\n this.listening = false;\n }\n\n private once(key: string): boolean {\n if (this.registered.has(key)) return false;\n this.registered.add(key);\n return true;\n }\n\n /** Subscribe once, and seed the page-start values: nothing scrolled, no\n * time elapsed, no exit yet, conversion as the manager recorded it. A null\n * here would make every AND/NOT over these signals unprovable. */\n private listen(): void {\n if (this.listening) return;\n this.listening = true;\n const { evaluator } = this.cfg;\n const seen = evaluator.getSnapshot();\n const seed: Partial<Record<IntentSignal, number | boolean>> = {\n time_on_page: 0,\n scroll_depth: 0,\n inactivity: 0,\n exit_intent: false,\n back_button: false,\n converted_in_session: this.cfg.hasConverted(),\n };\n for (const [signal, value] of Object.entries(seed) as [IntentSignal, number | boolean][]) {\n if (seen[signal] === undefined || seen[signal] === null) evaluator.updateSignal(signal, value);\n }\n this.cfg.engine.on('*', this.onEngineEvent);\n }\n\n private handle(eventType: string): void {\n const { evaluator } = this.cfg;\n const threshold = THRESHOLD_EVENT.exec(eventType);\n if (threshold) {\n const signal = threshold[1] as ThresholdSignal;\n const n = Number(threshold[2]);\n // Time and depth only grow; idle time is the stretch just observed.\n const prev = evaluator.getSnapshot()[signal];\n const value = signal !== 'inactivity' && typeof prev === 'number' ? Math.max(prev, n) : n;\n evaluator.updateSignal(signal, value);\n this.decide(signal);\n return;\n }\n if (eventType === 'exit_intent' || (eventType === 'mobile_exit_intent' && this.cfg.isMobile)) {\n evaluator.updateSignal('exit_intent', true);\n this.decide('exit_intent');\n return;\n }\n if (eventType === 'back_button') {\n evaluator.updateSignal('back_button', true);\n this.decide('back_button');\n }\n }\n\n private decide(signal: IntentSignal): void {\n const decision = this.cfg.evaluator.onSignalChanged(signal);\n // A `suppress` decision is recorded inside the evaluator (the silenced\n // rules never fire again this session); only a `fire` needs a display.\n if (decision.kind === 'fire' && decision.campaignId && this.cfg.show(decision.campaignId)) {\n this.cfg.evaluator.markFired(decision.campaignId);\n }\n }\n}\n","/**\n * Micro-Intent Engine — compound rule evaluator (P1 Task 4).\n *\n * Pure, side-effect-free evaluation of an IntentRule predicate tree against\n * a current snapshot of Layer-1 (identity) + Layer-2 (session) signal values.\n *\n * Why a separate class from TriggerEngine\n * ----------------------------------------\n * TriggerEngine.ts owns signal CAPTURE (scroll, time-on-page, exit-intent,\n * etc.) and emits named events like `scroll_depth_50`. IntentRuleEvaluator\n * owns rule EVALUATION — given a compound predicate and the current state\n * of all known signals, return whether the rule fires now. The two\n * concerns separate cleanly: capture is global per-page (one engine\n * instance), evaluation is per-rule (the manager re-runs every armed\n * rule when a `fire_on` signal fires).\n *\n * Type strategy\n * -------------\n * Types are declared LOCALLY in this file rather than imported from\n * `@aegis/shared-types`. The SDK is published standalone on npm and\n * cannot pull in workspace packages at runtime. A drift-check test\n * (`libs/web-sdk/tests/intent-evaluator-shared-types-drift.test.ts`)\n * parses both files and asserts the local types and the canonical\n * `libs/shared-types/src/intent.ts` declare the same set of signal /\n * comparator values.\n *\n * Plan reference: docs/architecture/MICRO_INTENT_ENGINE.md §\"Schemas\"\n */\n\n// ───────────────────────────────────────────────────────────────────────────\n// Types — local mirror of libs/shared-types/src/intent.ts\n// ───────────────────────────────────────────────────────────────────────────\n\nexport type IntentSignal =\n // Layer-2 session\n | 'scroll_depth'\n | 'time_on_page'\n | 'exit_intent'\n | 'back_button'\n | 'scroll_velocity'\n | 'inactivity'\n | 'visibility_change'\n | 'rage_click'\n | 'price_hover_ms'\n | 'cta_hover_ms'\n | 'cum_dwell_sku'\n | 'session_count_sku'\n | 'cart_value'\n | 'converted_in_session'\n // Layer-1 identity scores\n | 'rfm_segment'\n | 'rfm_score'\n | 'lifecycle_stage'\n | 'churn_risk_tier'\n | 'predicted_ltv'\n | 'predicted_ltv_tier'\n | 'uninstall_risk_tier'\n | 'engagement_level'\n | 'price_tier'\n | 'price_sensitivity_score'\n | 'top_category'\n | 'avg_cart_value'\n | 'scoring_tier';\n\nexport type Comparator = 'gte' | 'lte' | 'gt' | 'lt' | 'eq' | 'neq' | 'in' | 'nin';\n\nexport type IntentLeafValue =\n | number\n | string\n | boolean\n | ReadonlyArray<number | string | boolean>;\n\nexport interface IntentLeaf {\n /** Canonical enum signal (Layer-1/2). Mutually exclusive with `path`. */\n signal?: IntentSignal;\n /** Dot-path into the updateContext namespaces (cart.total / user.tier /\n * session.idle_s). Mutually exclusive with `signal`. */\n path?: string;\n op: Comparator;\n value: IntentLeafValue;\n}\n\n/** A value/node in the generic reactive context (fed via updateContext). */\nexport type ContextValue = string | number | boolean | null | ContextNode | ReadonlyArray<ContextValue>;\nexport interface ContextNode {\n [key: string]: ContextValue;\n}\n/** Named namespaces: { cart: {...}, user: {...}, session: {...}, custom: {...} }. */\nexport type ContextNamespaces = Record<string, ContextNode>;\n\nexport interface IntentNode {\n op: 'AND' | 'OR' | 'NOT';\n operands: ReadonlyArray<IntentExpr>;\n}\n\nexport type IntentExpr = IntentNode | IntentLeaf;\n\nexport interface IntentRule {\n fire_on: ReadonlyArray<IntentSignal>;\n when: IntentExpr;\n priority: number;\n suppress_competing: boolean;\n}\n\n/** A scalar a signal can hold in the snapshot. null = signal not (yet)\n * populated — happens for Layer-1 scores on anonymous sessions and for\n * Layer-2 signals before the user triggers them. */\nexport type SignalValue = number | string | boolean | null;\n\n/** Current state of every known signal. Missing keys are treated the\n * same as `null`. */\nexport type IntentSnapshot = Partial<Record<IntentSignal, SignalValue>>;\n\n// ───────────────────────────────────────────────────────────────────────────\n// Type guards\n// ───────────────────────────────────────────────────────────────────────────\n\nexport function isIntentLeaf(expr: IntentExpr): expr is IntentLeaf {\n // A leaf is anything WITHOUT `operands` (it may carry `signal` or `path`).\n return !('operands' in expr);\n}\n\n/** Resolve a dot-path (`cart.total`) against the context namespaces.\n * Returns undefined on any miss → tri-state null in evaluateLeaf (same as a\n * missing signal). Never throws. */\nexport function getValueByPath(\n namespaces: ContextNamespaces | undefined,\n path: string,\n): ContextValue | undefined {\n if (!namespaces || !path) return undefined;\n let cur: ContextValue | undefined = namespaces as unknown as ContextValue;\n for (const part of path.split('.')) {\n if (cur === null || typeof cur !== 'object' || Array.isArray(cur)) return undefined;\n cur = (cur as ContextNode)[part];\n }\n return cur;\n}\n\nexport function isIntentNode(expr: IntentExpr): expr is IntentNode {\n return 'operands' in expr;\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// Pure leaf evaluator\n// ───────────────────────────────────────────────────────────────────────────\n\n/**\n * Evaluate a single leaf comparison against a snapshot.\n *\n * Missing-signal semantics (per the plan's \"Graceful degradation rules\"):\n * - The CALLER (evaluateExpr) decides AND/OR semantics on missing\n * operands; this function answers strictly for THIS leaf.\n * - When the signal is missing from the snapshot, the leaf is\n * \"unknown\" → return null so the parent node can interpret it.\n *\n * Comparator semantics:\n * - gte/lte/gt/lt: numeric only; comparing strings/bools returns false.\n * - eq/neq: any type; strict equality.\n * - in/nin: leaf value must be a list; membership test on the snapshot\n * value. `nin` returns true for missing snapshot values (the\n * contact's RFM segment is \"not in\" the forbidden list if we don't\n * know it yet — conservative).\n */\nexport function evaluateLeaf(\n leaf: IntentLeaf,\n snapshot: IntentSnapshot,\n namespaces?: ContextNamespaces,\n): boolean | null {\n // Resolve from a dot-path (generic context) when present, else the enum\n // signal snapshot. Both share the SAME tri-state (missing → null) + operator\n // semantics — one evaluator, not two.\n const actual = leaf.path !== undefined\n ? getValueByPath(namespaces, leaf.path)\n : (leaf.signal !== undefined ? snapshot[leaf.signal] : undefined);\n if (actual === undefined || actual === null) {\n // Missing signal/path — let the parent decide.\n return null;\n }\n\n const want = leaf.value;\n\n switch (leaf.op) {\n case 'gte':\n case 'lte':\n case 'gt':\n case 'lt': {\n if (typeof actual !== 'number' || typeof want !== 'number') return false;\n if (leaf.op === 'gte') return actual >= want;\n if (leaf.op === 'lte') return actual <= want;\n if (leaf.op === 'gt') return actual > want;\n return actual < want; // lt\n }\n case 'eq':\n return actual === want;\n case 'neq':\n return actual !== want;\n case 'in':\n case 'nin': {\n if (!Array.isArray(want)) return false;\n const isMember = want.some((v) => v === actual);\n return leaf.op === 'in' ? isMember : !isMember;\n }\n default: {\n // Exhaustiveness check — TS will flag a new Comparator that doesn't\n // have a case here, but the runtime fallback returns false.\n const _exhaustive: never = leaf.op;\n void _exhaustive;\n return false;\n }\n }\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// Pure tree evaluator\n// ───────────────────────────────────────────────────────────────────────────\n\n/**\n * Evaluate the full predicate tree.\n *\n * AND/OR/NOT semantics with missing-value handling (per plan §\"Graceful\n * degradation rules\"):\n *\n * AND(a, b, c) where any operand is null → false (conservative; the\n * rule doesn't fire when we can't prove all conditions).\n * OR(a, b, c) where some operands are null → if any non-null operand\n * is true, returns true. If all operands are null OR all are\n * false, returns false.\n * NOT(a) where a is null → null (parent decides). When this propagates\n * to the top-level evaluateExpr, the rule does NOT fire.\n *\n * The \"AND treats missing as unmet\" + \"OR treats missing as inert\" rule\n * from the plan maps exactly onto this implementation: AND short-circuits\n * to false on any null operand; OR ignores nulls and looks at the rest.\n *\n * Top-level: a null result from the root expression means the rule\n * cannot be evaluated yet — do NOT fire.\n */\nexport function evaluateExpr(\n expr: IntentExpr,\n snapshot: IntentSnapshot,\n namespaces?: ContextNamespaces,\n): boolean | null {\n if (isIntentLeaf(expr)) {\n return evaluateLeaf(expr, snapshot, namespaces);\n }\n\n switch (expr.op) {\n case 'AND': {\n // AND requires ALL operands true. Any null = unprovable = false.\n for (const operand of expr.operands) {\n const v = evaluateExpr(operand, snapshot, namespaces);\n if (v === null) return false;\n if (!v) return false;\n }\n return true;\n }\n case 'OR': {\n // OR is satisfied by any true operand. Nulls are skipped (inert).\n let allNull = true;\n for (const operand of expr.operands) {\n const v = evaluateExpr(operand, snapshot, namespaces);\n if (v === true) return true;\n if (v !== null) allNull = false;\n }\n // If every operand was null, propagate null upward — caller decides.\n // Otherwise all operands evaluated false.\n return allNull ? null : false;\n }\n case 'NOT': {\n // Validator at write time guarantees operands.length === 1; the\n // evaluator trusts that and reads operands[0].\n const v = evaluateExpr(expr.operands[0], snapshot, namespaces);\n if (v === null) return null;\n return !v;\n }\n default: {\n // Exhaustiveness check — if a new op is added to IntentExpr and\n // not handled above, TypeScript narrows `expr` to `never` here\n // and assigning it to a non-`never` would fail at compile time.\n // The earlier draft did `expr.op` which itself dereferences\n // `never` and explodes; assign `expr` directly instead.\n const _exhaustive: never = expr;\n void _exhaustive;\n return false;\n }\n }\n}\n\n/** Top-level \"does the rule fire right now?\" check. Treats a null\n * evaluation result as \"do not fire\" (we can't prove the rule's\n * conditions are met). */\nexport function evaluateRule(\n rule: IntentRule,\n snapshot: IntentSnapshot,\n namespaces?: ContextNamespaces,\n): boolean {\n const result = evaluateExpr(rule.when, snapshot, namespaces);\n return result === true;\n}\n\n// ───────────────────────────────────────────────────────────────────────────\n// Stateful evaluator class — what AegisInAppManager talks to\n// ───────────────────────────────────────────────────────────────────────────\n\n/** A campaign carrying a compound rule, in the SDK's runtime shape.\n * Mirrors the cell-plane payload at /v1/in-app/active when\n * client_trigger.type === 'micro_intent'. */\nexport interface ArmedCampaign {\n id: string;\n rule: IntentRule;\n}\n\n/** The decision the evaluator returns when a signal fires.\n *\n * fire : caller should display this campaign now.\n * suppress : caller should silence the listed competing campaign IDs\n * for the rest of the session (the bouncing-researcher\n * pattern — a rule whose only job is to silence others).\n * none : no rule matched, do nothing.\n */\nexport interface EvaluationDecision {\n kind: 'fire' | 'suppress' | 'none';\n campaignId?: string;\n suppressIds?: ReadonlyArray<string>;\n}\n\nexport class IntentRuleEvaluator {\n /** All campaigns currently armed on this page. Caller (the\n * AegisInAppManager) replaces this on every refresh. */\n private armed: ArmedCampaign[] = [];\n\n /** Latest signal values. Caller pushes updates via updateSignal /\n * updateSnapshot whenever TriggerEngine emits a state change or\n * contact-scores fetch completes. */\n private snapshot: IntentSnapshot = {};\n\n /** Generic reactive context namespaces fed via updateContext() — the SINGLE\n * store path-leaves resolve against (no parallel state elsewhere). */\n private namespaces: ContextNamespaces = {};\n\n /** Campaigns that have already fired this session — never re-fire. */\n private firedThisSession = new Set<string>();\n\n /** Campaigns silenced by a prior `suppress_competing` win. The set\n * resets on session boundary (new tab / new pageview after timeout).\n * The AegisInAppManager owns durability semantics; this set is\n * in-memory only. */\n private silencedThisSession = new Set<string>();\n\n // ── Governance: \"rich, not overwhelming\" (plan ACS_ENGAGEMENT_COMMERCE_PACK §5) ──\n /** ONE interruptive overlay per session. Once an interruption actually fires\n * (signalled via markFired), further fires are blocked until reset().\n * OPT-IN (default off) so the evaluator's base contract is unchanged for\n * existing consumers (mobile, etc.); the storefront runtime turns it ON via\n * setGovernance() for the ACS \"rich, not overwhelming\" posture. */\n private oneInterruptionPerSession = false;\n private interruptiveBudgetSpent = false;\n /** TTFI floor — never fire an overlay in the first `ttfiMs` of the session\n * (\"no overlay at second 0\"). Exit/leave signals are exempt. OPT-IN (default\n * 0 = off); the storefront enables a ~4s floor via setGovernance(). */\n private ttfiMs = 0;\n private sessionStartMs: number = Date.now();\n /** Injectable clock for deterministic tests. */\n private nowFn: () => number = () => Date.now();\n private static readonly EXIT_SIGNALS: ReadonlyArray<IntentSignal> = [\n 'exit_intent',\n 'back_button',\n ];\n\n /** Tune governance. `now` also (re)bases the TTFI session clock. */\n setGovernance(opts: {\n oneInterruptionPerSession?: boolean;\n ttfiMs?: number;\n now?: () => number;\n }): void {\n if (opts.oneInterruptionPerSession !== undefined) {\n this.oneInterruptionPerSession = opts.oneInterruptionPerSession;\n }\n if (opts.ttfiMs !== undefined) this.ttfiMs = opts.ttfiMs;\n if (opts.now) {\n this.nowFn = opts.now;\n this.sessionStartMs = opts.now();\n }\n }\n\n // ─── snapshot management ───\n\n updateSignal(signal: IntentSignal, value: SignalValue): void {\n this.snapshot[signal] = value;\n }\n\n updateSnapshot(partial: IntentSnapshot): void {\n Object.assign(this.snapshot, partial);\n }\n\n getSnapshot(): Readonly<IntentSnapshot> {\n return this.snapshot;\n }\n\n // ─── generic reactive context (updateContext) ───\n\n /** Merge a namespace's data (shallow). Path-leaves (`cart.total`) resolve\n * against this. The runtime's updateContext() delegates here so there is ONE\n * context store, then bridges well-known paths to enum signals. */\n updateContext(namespace: string, data: ContextNode): void {\n if (!namespace || !data) return;\n this.namespaces[namespace] = { ...this.namespaces[namespace], ...data };\n }\n\n getNamespaces(): Readonly<ContextNamespaces> {\n return this.namespaces;\n }\n\n /** Resolve a dot-path against the live context (for previews / debugging). */\n getValueByPath(path: string): ContextValue | undefined {\n return getValueByPath(this.namespaces, path);\n }\n\n // ─── armed-campaign management ───\n\n setArmed(campaigns: ArmedCampaign[]): void {\n this.armed = [...campaigns];\n }\n\n getArmed(): ReadonlyArray<ArmedCampaign> {\n return this.armed;\n }\n\n markFired(campaignId: string): void {\n this.firedThisSession.add(campaignId);\n // An interruption was actually shown → spend the per-session budget so the\n // next eligible interruptive stays quiet (plan §5 \"≤1 interruption/session\").\n this.interruptiveBudgetSpent = true;\n }\n\n /** Clear all per-session state. Caller invokes on logout or explicit\n * session reset. */\n reset(): void {\n this.firedThisSession.clear();\n this.silencedThisSession.clear();\n this.interruptiveBudgetSpent = false;\n this.sessionStartMs = this.nowFn();\n }\n\n // ─── the main dispatch ───\n\n /**\n * Called every time a signal value changes meaningfully (e.g. a\n * TriggerEngine `exit_intent` event, or a `scroll_depth_50` crossing\n * threshold, or the Layer-1 fetch finishing). Returns the decision the\n * caller should act on.\n *\n * Algorithm:\n * 1. Filter armed campaigns to those whose `fire_on` includes the\n * signal that just changed.\n * 2. Drop already-fired and silenced campaigns.\n * 3. Sort the remainder by priority DESC, tie-broken by campaign id\n * lexicographic (per plan §\"Pattern ordering rules\").\n * 4. Walk the sorted list:\n * - If a suppress_competing rule matches, emit a 'suppress'\n * decision listing every lower-priority armed campaign id.\n * Mark those as silenced internally too.\n * - Otherwise the first matching rule emits 'fire'.\n * 5. If nothing matches, return 'none'.\n */\n onSignalChanged(signal: IntentSignal): EvaluationDecision {\n // Governance: one interruptive overlay per session. Once an interruption\n // has fired (markFired), stay quiet until reset() (plan §5).\n if (this.oneInterruptionPerSession && this.interruptiveBudgetSpent) {\n return { kind: 'none' };\n }\n // TTFI floor: how long into the session are we? Computed once per dispatch.\n const sessionElapsedMs = this.nowFn() - this.sessionStartMs;\n const exemptFromTtfi = IntentRuleEvaluator.EXIT_SIGNALS.includes(signal);\n\n const candidates = this.armed\n .filter((c) => c.rule.fire_on.includes(signal))\n .filter((c) => !this.firedThisSession.has(c.id))\n .filter((c) => !this.silencedThisSession.has(c.id))\n .sort((a, b) => {\n if (b.rule.priority !== a.rule.priority) {\n return b.rule.priority - a.rule.priority;\n }\n // Tie-break: lexicographic on id, ascending.\n return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;\n });\n\n for (let i = 0; i < candidates.length; i++) {\n const c = candidates[i];\n if (!evaluateRule(c.rule, this.snapshot, this.namespaces)) continue;\n\n // TTFI floor: don't fire an overlay in the session's first `ttfiMs`,\n // unless this is a deliberate exit/leave signal. The campaign stays\n // eligible — it fires on a later signal once the floor passes.\n if (this.ttfiMs > 0 && sessionElapsedMs < this.ttfiMs && !exemptFromTtfi) {\n continue;\n }\n\n if (c.rule.suppress_competing) {\n // Silence every other armed campaign for the rest of session.\n // Specifically: every armed campaign EXCEPT this one. We don't\n // restrict to \"lower priority\" because suppress_competing is\n // explicitly a bouncing-researcher / margin-protection signal —\n // it means \"if this matches, don't show ANYTHING\".\n const suppressIds = this.armed\n .filter((other) => other.id !== c.id)\n .map((other) => other.id);\n suppressIds.forEach((id) => this.silencedThisSession.add(id));\n return {\n kind: 'suppress',\n campaignId: c.id,\n suppressIds,\n };\n }\n\n return { kind: 'fire', campaignId: c.id };\n }\n\n return { kind: 'none' };\n }\n}\n\n// Re-export pure helpers so tests can reach them without instantiating\n// the class.\nexport const MAX_RULE_DEPTH = 3 as const;\n","/**\n * Micro-Intent Engine — contact-scores fetcher (P4a bridge).\n *\n * Bridges `GET /v1/sdk/contact-scores` into the SDK's\n * `IntentRuleEvaluator` snapshot. Without this, identified-contact\n * micro-intent patterns (VIP Whisper, Win-back At-Risk, Price-Sensitive\n * Nudge, etc.) cannot fire because their AND-compound rules evaluate\n * `rfm_segment` / `lifecycle_stage` / `churn_risk_tier` /\n * `predicted_ltv_tier` / `price_sensitivity_score` against an empty\n * snapshot and always return false.\n *\n * Lifecycle\n * ---------\n * AegisMessageRuntime.initialize() → fetch if contactId known\n * AegisMessageRuntime.updateContactId → fetch for the new contact\n *\n * The fetcher is session-deduped: a second `fetchForContact(sameId)`\n * call within `dedupWindowMs` is a no-op. This stops a hot navigation\n * loop from hammering the endpoint when SPA route changes re-call\n * identify() with the same id.\n *\n * Empty state\n * -----------\n * The endpoint returns 204 for unknown contacts AND for contacts whose\n * `scoring_tier='none'`. Either way the fetcher leaves the snapshot\n * unchanged — the evaluator's leaf eval treats absent Layer-1 keys as\n * null, which fail-closes any AND clause involving them. Anonymous\n * patterns continue to work normally.\n *\n * Privacy posture\n * ---------------\n * The endpoint's default response is the tiered form (`predicted_ltv_tier`).\n * Tenants who opt into raw `predicted_ltv` via\n * `organizations.settings.score_serving_raw_ltv = true` will see the\n * float on the snapshot too. Both are valid IntentSignals; rule\n * authors pick whichever matches their tenant config.\n *\n * Plan reference: docs/architecture/MICRO_INTENT_ENGINE.md §P4a.\n */\n\nimport type { IntentRuleEvaluator, IntentSnapshot } from './IntentRuleEvaluator';\n\n/** Shape of the Layer-1 score response from /v1/sdk/contact-scores. */\nexport interface ContactScoresResponse {\n rfm_segment?: string;\n lifecycle_stage?: string;\n /** 'unscored' = the model never scored this contact (never 'low'). */\n churn_risk_tier?: 'low' | 'medium' | 'high' | 'unscored';\n predicted_ltv_tier?: 'low' | 'medium' | 'high';\n predicted_ltv?: number;\n price_sensitivity_score?: number;\n scoring_tier?: string;\n scores_updated_at?: string | null;\n /** Governed contact context — namespaced { user: { custom: {…} }, loyalty:\n * { points, tier } } from contacts.attributes + the loyalty record (the SAME\n * source segments + {{custom.x}} merge fields use). Pushed into the rule\n * engine's namespaces so `user.custom.<attr>` / `loyalty.points` path-leaves\n * resolve client-side off one catalog. */\n context?: Record<string, Record<string, unknown>>;\n}\n\nexport interface ContactScoresFetcherConfig {\n apiHost: string;\n writeKey: string;\n organizationId?: string;\n /** Optional. When provided, the fetcher pushes the response into the\n * evaluator's snapshot automatically on every successful fetch. */\n evaluator?: IntentRuleEvaluator;\n /** Minimum time between repeat fetches for the same contactId.\n * Default 60 seconds. */\n dedupWindowMs?: number;\n /** Override for tests / non-browser environments. */\n fetchImpl?: typeof fetch;\n /** Console.warn channel. */\n onError?: (err: unknown, contactId: string) => void;\n}\n\nexport class ContactScoresFetcher {\n private readonly apiHost: string;\n private readonly writeKey: string;\n private organizationId?: string;\n private readonly evaluator?: IntentRuleEvaluator;\n private readonly dedupWindowMs: number;\n private readonly fetchImpl: typeof fetch;\n private readonly onError?: (err: unknown, contactId: string) => void;\n\n private lastFetchedContactId?: string;\n private lastFetchedAt = 0;\n private latestSnapshot: ContactScoresResponse | null = null;\n\n constructor(cfg: ContactScoresFetcherConfig) {\n this.apiHost = cfg.apiHost.replace(/\\/$/, '');\n this.writeKey = cfg.writeKey;\n this.organizationId = cfg.organizationId;\n this.evaluator = cfg.evaluator;\n this.dedupWindowMs = cfg.dedupWindowMs ?? 60_000;\n this.fetchImpl = cfg.fetchImpl ?? (typeof fetch !== 'undefined' ? fetch.bind(globalThis) : (() => Promise.reject(new Error('fetch unavailable'))) as typeof fetch);\n this.onError = cfg.onError;\n }\n\n /** Update the org so subsequent fetches scope correctly (e.g. when\n * the SDK resolves an org id after init). */\n updateOrganizationId(organizationId: string | undefined): void {\n this.organizationId = organizationId;\n }\n\n /** Most recent successful response. `null` when no fetch has succeeded\n * yet, OR when the endpoint returned 204 (unscored). Consumers can\n * poll this for diagnostic UIs; campaign evaluation does not need to\n * read it directly because the evaluator (if configured) already has\n * the snapshot applied. */\n getSnapshot(): Readonly<ContactScoresResponse> | null {\n return this.latestSnapshot;\n }\n\n /** Fetch scores for `contactId` and apply to the configured evaluator\n * (if any). Returns the response on success, null on 204 / error.\n *\n * Session-dedup: repeat calls for the same contactId within\n * `dedupWindowMs` are no-ops. Pass `force: true` to override. */\n async fetchForContact(\n contactId: string,\n opts: { force?: boolean; contactToken?: string } = {},\n ): Promise<ContactScoresResponse | null> {\n if (!contactId) return null;\n\n const now = Date.now();\n if (\n !opts.force &&\n this.lastFetchedContactId === contactId &&\n now - this.lastFetchedAt < this.dedupWindowMs\n ) {\n return this.latestSnapshot;\n }\n\n this.lastFetchedContactId = contactId;\n this.lastFetchedAt = now;\n\n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Contact-ID': contactId,\n 'X-User-ID': contactId,\n };\n // C1b: only send the token with its own contact.\n if (opts.contactToken) {\n headers['X-Contact-Token'] = opts.contactToken;\n }\n if (this.organizationId) {\n headers['X-Organization-ID'] = this.organizationId;\n }\n\n try {\n const resp = await this.fetchImpl(\n `${this.apiHost}/v1/sdk/contact-scores`,\n {\n method: 'GET',\n headers,\n credentials: 'include',\n },\n );\n if (resp.status === 204) {\n this.latestSnapshot = null;\n return null;\n }\n if (!resp.ok) {\n this.latestSnapshot = null;\n return null;\n }\n const payload = (await resp.json()) as ContactScoresResponse;\n this.latestSnapshot = payload;\n if (this.evaluator) {\n this.evaluator.updateSnapshot(toIntentSnapshot(payload));\n // Bridge the governed contact context into the rule engine's namespaces\n // — the SAME source as segments + merge fields, so client path-rules\n // (user.custom.<attr> / loyalty.points) resolve off one catalog. Any\n // product enriching contacts.attributes or the loyalty record flows here.\n if (payload.context && typeof payload.context === 'object') {\n for (const [ns, data] of Object.entries(payload.context)) {\n if (data && typeof data === 'object') {\n this.evaluator.updateContext(ns, data as Record<string, never>);\n }\n }\n }\n }\n return payload;\n } catch (err) {\n // Network errors / aborted fetches must not block in-app rendering.\n // Anonymous patterns keep working; identified-contact patterns\n // simply don't fire this session.\n this.onError?.(err, contactId);\n return null;\n }\n }\n}\n\n/** Project the wire response onto the evaluator's IntentSnapshot shape.\n * Strings (rfm_segment / lifecycle_stage / *_tier) map straight across;\n * numerics (price_sensitivity_score / predicted_ltv) are passed through\n * as numbers. Missing keys are omitted so the evaluator's leaf eval\n * treats them as null (per IntentRuleEvaluator semantics).\n *\n * Exported for the smoke test — drift guard pins the mapping. */\nexport function toIntentSnapshot(r: ContactScoresResponse): IntentSnapshot {\n const snap: IntentSnapshot = {};\n if (r.rfm_segment) snap.rfm_segment = r.rfm_segment;\n if (r.lifecycle_stage) snap.lifecycle_stage = r.lifecycle_stage;\n if (r.churn_risk_tier) snap.churn_risk_tier = r.churn_risk_tier;\n if (r.predicted_ltv_tier) snap.predicted_ltv_tier = r.predicted_ltv_tier;\n if (typeof r.predicted_ltv === 'number') snap.predicted_ltv = r.predicted_ltv;\n if (typeof r.price_sensitivity_score === 'number') {\n snap.price_sensitivity_score = r.price_sensitivity_score;\n }\n if (r.scoring_tier) snap.scoring_tier = r.scoring_tier;\n return snap;\n}\n","/**\n * AegisLoyaltyManager — public SDK surface for the closed-loop store\n * credit + gift cards feature.\n *\n * Phase 7 of the Active Loyalty — Store Credit + Gift Cards rollout.\n * Tracker: docs/architecture/ACTIVE_LOYALTY_STORE_CREDIT_GIFT_CARDS_TRACKER.md\n *\n * Public API (call via `runtime.loyalty.storeCredit.*` / `runtime.loyalty.giftCards.*`):\n *\n * await runtime.loyalty.storeCredit.listActive(phone)\n * await runtime.loyalty.storeCredit.startReload({ phone, workspaceId, amountPaise })\n * await runtime.loyalty.giftCards.startPurchase({ ... })\n * await runtime.loyalty.giftCards.claim({ giftCardId, phone })\n * await runtime.loyalty.giftCards.listReceived(phone)\n *\n * Closed-loop constraints surfaced through the API:\n * - workspaceId is required on every reload / gift card purchase\n * (C1 — single-workspace redemption).\n * - listActive returns ONE row per workspace; clients render one\n * card per workspace, never pooled.\n * - claim() requires the recipient phone to authenticate as the\n * directed recipient (C3 — no bearer codes).\n *\n * Network shape — all calls hit the storefront-facing commerce router\n * via the configured apiHost / same-origin.\n */\n\nexport interface StoreCreditBalance {\n workspaceId: string;\n balancePaise: number;\n expiresAt: string | null;\n lastActivityAt: string | null;\n}\n\nexport interface ReloadCheckoutResult {\n contactId: string;\n checkoutUrl: string;\n provider: string;\n paymentLinkId: string;\n amountPaise: number;\n}\n\nexport interface GiftCardPurchaseResult {\n senderContactId: string;\n giftCardId: string;\n checkoutUrl: string;\n provider: string;\n paymentLinkId: string;\n amountPaise: number;\n}\n\nexport interface GiftCard {\n id: string;\n workspaceId: string;\n senderContactId: string;\n amountPaise: number;\n status: string;\n personalMessage: string | null;\n deliveredAt: string | null;\n claimedAt: string | null;\n expiresAt: string | null;\n createdAt: string | null;\n}\n\nexport interface GiftCardClaimResult {\n recipientContactId: string;\n giftCardId: string;\n amountCreditedPaise: number;\n newBalancePaise: number;\n}\n\nexport interface AegisLoyaltyManagerConfig {\n /** Storefront slug — `{slug}.actii.me/...` */\n slug: string;\n /** Optional explicit API host. When null, uses same-origin. */\n apiHost?: string;\n /** When true, attaches credentials so cookies flow (anon-id, csrf). */\n credentials?: 'include' | 'same-origin' | 'omit';\n /** Optional debug logger. */\n debugMode?: boolean;\n}\n\ninterface ListActiveResponse {\n balances: Array<{\n workspace_id: string;\n balance_paise: number;\n expires_at: string | null;\n last_activity_at: string | null;\n }>;\n}\n\ninterface StartReloadResponse {\n contact_id: string;\n checkout_url: string;\n provider: string;\n payment_link_id: string;\n amount_paise: number;\n}\n\ninterface GiftCardPurchaseResponse {\n sender_contact_id: string;\n gift_card_id: string;\n checkout_url: string;\n provider: string;\n payment_link_id: string;\n amount_paise: number;\n}\n\ninterface GiftCardClaimResponse {\n recipient_contact_id: string;\n gift_card_id: string;\n amount_credited_paise: number;\n new_balance_paise: number;\n}\n\ninterface GiftCardListReceivedResponse {\n gift_cards: Array<{\n id: string;\n workspace_id: string;\n sender_contact_id: string;\n amount_paise: number;\n status: string;\n personal_message: string | null;\n delivered_at: string | null;\n claimed_at: string | null;\n expires_at: string | null;\n created_at: string | null;\n }>;\n}\n\nclass HttpClient {\n constructor(private config: AegisLoyaltyManagerConfig) {}\n\n private url(path: string) {\n const host = (this.config.apiHost ?? '').replace(/\\/$/, '');\n return `${host}/api/v1/commerce/store/${encodeURIComponent(this.config.slug)}${path}`;\n }\n\n async get<T>(path: string): Promise<T> {\n const res = await fetch(this.url(path), {\n method: 'GET',\n credentials: this.config.credentials ?? 'include',\n headers: { 'content-type': 'application/json' },\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`GET ${path} failed (${res.status}): ${body}`);\n }\n return res.json() as Promise<T>;\n }\n\n async post<T>(path: string, body: Record<string, unknown>): Promise<T> {\n const res = await fetch(this.url(path), {\n method: 'POST',\n credentials: this.config.credentials ?? 'include',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const errBody = await res.text();\n let detail = errBody;\n try {\n detail = JSON.parse(errBody).detail ?? errBody;\n } catch {\n // not JSON\n }\n throw new Error(`POST ${path} failed (${res.status}): ${detail}`);\n }\n return res.json() as Promise<T>;\n }\n}\n\nclass StoreCreditClient {\n constructor(private http: HttpClient) {}\n\n async listActive(phone: string): Promise<StoreCreditBalance[]> {\n const body = await this.http.get<ListActiveResponse>(\n `/store-credit/list-active?phone=${encodeURIComponent(phone)}`,\n );\n return (body.balances ?? []).map((b) => ({\n workspaceId: b.workspace_id,\n balancePaise: b.balance_paise,\n expiresAt: b.expires_at,\n lastActivityAt: b.last_activity_at,\n }));\n }\n\n async startReload(opts: {\n phone: string;\n workspaceId: string;\n amountPaise: number;\n firstName?: string;\n email?: string;\n callbackUrl?: string;\n }): Promise<ReloadCheckoutResult> {\n if (!opts.workspaceId) {\n throw new Error('workspaceId is required for store credit reload (closed-loop C1)');\n }\n const body = await this.http.post<StartReloadResponse>(\n '/store-credit/reload',\n {\n phone: opts.phone,\n workspace_id: opts.workspaceId,\n amount_paise: opts.amountPaise,\n first_name: opts.firstName,\n email: opts.email,\n callback_url: opts.callbackUrl,\n },\n );\n return {\n contactId: body.contact_id,\n checkoutUrl: body.checkout_url,\n provider: body.provider,\n paymentLinkId: body.payment_link_id,\n amountPaise: body.amount_paise,\n };\n }\n}\n\nclass GiftCardsClient {\n constructor(private http: HttpClient) {}\n\n async startPurchase(opts: {\n senderPhone: string;\n workspaceId: string;\n recipientPhone: string;\n recipientName?: string;\n amountPaise: number;\n personalMessage?: string;\n callbackUrl?: string;\n }): Promise<GiftCardPurchaseResult> {\n if (!opts.workspaceId) {\n throw new Error('workspaceId is required for gift card purchase (closed-loop C1)');\n }\n if (!opts.recipientPhone) {\n throw new Error('recipientPhone is required (no bearer cards — C3)');\n }\n const body = await this.http.post<GiftCardPurchaseResponse>(\n '/gift-cards/purchase',\n {\n sender_phone: opts.senderPhone,\n workspace_id: opts.workspaceId,\n recipient_phone: opts.recipientPhone,\n recipient_name: opts.recipientName,\n amount_paise: opts.amountPaise,\n personal_message: opts.personalMessage,\n callback_url: opts.callbackUrl,\n },\n );\n return {\n senderContactId: body.sender_contact_id,\n giftCardId: body.gift_card_id,\n checkoutUrl: body.checkout_url,\n provider: body.provider,\n paymentLinkId: body.payment_link_id,\n amountPaise: body.amount_paise,\n };\n }\n\n async claim(opts: { giftCardId: string; phone: string }): Promise<GiftCardClaimResult> {\n const body = await this.http.post<GiftCardClaimResponse>('/gift-cards/claim', {\n gift_card_id: opts.giftCardId,\n phone: opts.phone,\n });\n return {\n recipientContactId: body.recipient_contact_id,\n giftCardId: body.gift_card_id,\n amountCreditedPaise: body.amount_credited_paise,\n newBalancePaise: body.new_balance_paise,\n };\n }\n\n async listReceived(phone: string): Promise<GiftCard[]> {\n const body = await this.http.get<GiftCardListReceivedResponse>(\n `/gift-cards/received?phone=${encodeURIComponent(phone)}`,\n );\n return (body.gift_cards ?? []).map((g) => ({\n id: g.id,\n workspaceId: g.workspace_id,\n senderContactId: g.sender_contact_id,\n amountPaise: g.amount_paise,\n status: g.status,\n personalMessage: g.personal_message,\n deliveredAt: g.delivered_at,\n claimedAt: g.claimed_at,\n expiresAt: g.expires_at,\n createdAt: g.created_at,\n }));\n }\n}\n\nexport class AegisLoyaltyManager {\n readonly storeCredit: StoreCreditClient;\n readonly giftCards: GiftCardsClient;\n\n constructor(config: AegisLoyaltyManagerConfig) {\n if (!config.slug) {\n throw new Error('AegisLoyaltyManager requires a slug');\n }\n const http = new HttpClient(config);\n this.storeCredit = new StoreCreditClient(http);\n this.giftCards = new GiftCardsClient(http);\n }\n}\n","declare const __AEGIS_SDK_VERSION__: string;\n/**\n * AegisMessageRuntime — unified facade for in-app messaging.\n *\n * The sole public entry point for all 20 in-app types (announce +\n * collect + assist buckets) plus the legacy gamification surface\n * (chat_bubble / toast / exit_intent_popup configured via\n * /v1/widgets/config). Under the hood it composes an\n * `AegisInAppManager` + `AegisWidgetManager`; callers never touch them\n * directly.\n *\n * Public API:\n * new AegisMessageRuntime({ writeKey, apiHost, ... })\n * await runtime.initialize()\n * await runtime.updateContactId(id)\n * runtime.onClientEvent(eventName, data)\n * runtime.destroy()\n *\n * Internal wiring: spin_wheel + scratch_card campaigns render in the\n * InAppManager like every other in-app type (one engine: a turn posts\n * /in-app/responses). The facade owns the page's TriggerEngine: it arms the\n * behavioural `client_trigger`s (exit intent / scroll depth / time on page /\n * inactivity) of every campaign load, forwards each firing to\n * `inApp.onClientEvent`, and starts the engine it created.\n */\n\nimport { AegisInAppManager, maybeStartPairing, type AegisInAppConfig, type InAppCampaign, type CartState, type AegisAction } from '../inapp';\nimport { AegisWidgetManager, type AegisWidgetConfig } from '../widgets';\nimport { TriggerEngine } from '../triggers/TriggerEngine';\nimport { armBehaviouralTriggers, triggerFromEngineEvent } from '../triggers/behaviouralTriggers';\nimport { MicroIntentArming } from '../triggers/microIntentTriggers';\nimport { IntentRuleEvaluator } from '../triggers/IntentRuleEvaluator';\nimport { ContactScoresFetcher } from '../triggers/ContactScoresFetcher';\nimport { AegisLoyaltyManager } from '../loyalty';\nimport { AegisChat, type AegisChatConfig } from '../chat';\n\nexport interface AegisMessageRuntimeConfig extends AegisInAppConfig {\n /** If set, a `TriggerEngine` from the SDK is wired to the WidgetManager\n * for its exit-intent / scroll-velocity / inactivity handlers. Leave\n * undefined to have the runtime construct one internally. */\n triggerEngine?: AegisWidgetConfig['triggerEngine'];\n /** WidgetManager prefetch toggle. Defaults to true — the exit-intent\n * popup config comes from `/v1/widgets/config/prefetch`. */\n enableWidgetPrefetch?: boolean;\n /** Source platform for gamification attribution (shopify / woocommerce\n * / magento / mobile_sdk / web). Passed through to WidgetManager. */\n sourcePlatform?: AegisWidgetConfig['sourcePlatform'];\n /** Storefront slug for the loyalty subsystem — needed when the host\n * app calls `runtime.loyalty.storeCredit.*` / `runtime.loyalty.giftCards.*`.\n * When omitted, the loyalty manager is constructed but every call\n * will throw until configure() is invoked. */\n storefrontSlug?: string;\n /** Mount the customer chat launcher (\"Shopping Assistant\") — a floating\n * bubble that talks to the channel-neutral chat agent (Track A). Off by\n * default; opt in per host page. */\n enableChat?: boolean;\n /** Presentation overrides for the chat launcher (title / greeting / accent),\n * plus proactive auto-open. writeKey / apiHost / org / contact are inherited\n * from the runtime. */\n chat?: Pick<AegisChatConfig, 'title' | 'greeting' | 'accentColor' | 'surfaceStyle' | 'channel' | 'icon' | 'logoUrl' | 'position' | 'displayMode'> & {\n /** Client-side TriggerEngine events that auto-open the launcher (e.g.\n * ['exit_intent', 'inactivity_90']). The trigger ONLY opens the panel —\n * it never creates an offer. The proactive *opener* is owned server-side\n * by the journey→warm-offer-queue bridge (Track B B4); the bot surfaces it\n * on the customer's first turn. This keeps a single offer source — no\n * parallel client-side offer infra. */\n proactiveTriggers?: string[];\n };\n /** \"Rich, not overwhelming\" interruption governance (plan\n * ACS_ENGAGEMENT_COMMERCE_PACK §5). Applied to the shared\n * `intentRuleEvaluator` on construct. Default off (base SDK contract is\n * unchanged for existing consumers); the storefront opts in. */\n governance?: {\n /** Cap interruptive overlays at one per session. */\n oneInterruptionPerSession?: boolean;\n /** TTFI floor (ms) — no overlay in the session's first N ms (exit/back exempt). */\n ttfiMs?: number;\n };\n}\n\n/**\n * One runtime, all in-app types. Consumers should prefer this over\n * importing AegisInAppManager / AegisWidgetManager directly. Those two\n * classes remain exported for a deprecation window — see `./deprecated.ts`.\n */\nexport class AegisMessageRuntime {\n /** Guards the one-time console version banner across multiple instances. */\n private static _bannerPrinted = false;\n readonly inApp: AegisInAppManager;\n readonly widgets: AegisWidgetManager;\n /** Shared IntentRuleEvaluator for identified-contact micro-intent\n * patterns. The ContactScoresFetcher writes Layer-1 signals into its\n * snapshot; IntentSnapshotCollector (if wired by the consumer) writes\n * Layer-2 signals. Exposed so apps that pre-existed P4a can swap in\n * their own evaluator if they need to. */\n readonly intentRuleEvaluator: IntentRuleEvaluator;\n /** Bridges GET /v1/sdk/contact-scores into the evaluator snapshot.\n * Triggered automatically on initialize() and updateContactId(). */\n readonly contactScores: ContactScoresFetcher;\n /** Closed-loop store credit + directed gift cards (Phase 7).\n * Usage:\n * await runtime.loyalty.storeCredit.listActive(phone)\n * await runtime.loyalty.storeCredit.startReload({ phone, workspaceId, amountPaise })\n * await runtime.loyalty.giftCards.startPurchase({ ... })\n * await runtime.loyalty.giftCards.claim({ giftCardId, phone })\n * Tracker: docs/architecture/ACTIVE_LOYALTY_STORE_CREDIT_GIFT_CARDS_TRACKER.md */\n readonly loyalty: AegisLoyaltyManager;\n /** Customer chat launcher (\"Shopping Assistant\"). Constructed only when\n * `enableChat` is set; undefined otherwise. (Track A) */\n readonly chat?: AegisChat;\n private contactId?: string;\n private contactToken?: string;\n private initialized = false;\n private readonly triggerEngine: TriggerEngine;\n private readonly ownsTriggerEngine: boolean;\n /** Engine events already armed for in-app client triggers (one subscription each). */\n private readonly armedInAppTriggers = new Set<string>();\n /** Smart triggers (`micro_intent`): armed into the evaluator on every campaign load. */\n private readonly microIntent: MicroIntentArming;\n private readonly unsubscribeCampaignsLoaded: () => void;\n /** apiHost captured for the device-pairing entry (`?aegis_pair=CODE`). */\n private readonly pairingApiHost: string;\n // Context is held in ONE place — `intentRuleEvaluator.namespaces` (fed via\n // updateContext). The renderer cart reader + the cart_value signal are DERIVED\n // from it; there is no parallel cart/context store on the runtime.\n\n constructor(config: AegisMessageRuntimeConfig) {\n // Announce the loaded SDK version once, so a host page can verify (in the\n // console) exactly which @active-reach/web-sdk build is live. `__AEGIS_SDK_VERSION__`\n // is baked in at build time (vite + build-cdn define).\n {\n const v =\n typeof __AEGIS_SDK_VERSION__ !== 'undefined' ? __AEGIS_SDK_VERSION__ : 'unknown';\n try {\n // Expose for on-demand check: type `__AEGIS_SDK_VERSION__` (or\n // `window.__AEGIS_SDK_VERSION__`) in the console at any time — no need to\n // catch the boot banner.\n (globalThis as unknown as Record<string, unknown>).__AEGIS_SDK_VERSION__ = v;\n } catch {\n /* globalThis may be frozen */\n }\n if (!AegisMessageRuntime._bannerPrinted) {\n AegisMessageRuntime._bannerPrinted = true;\n try {\n // console.log (not .info) so it survives the default console level filter.\n // eslint-disable-next-line no-console\n console.log(\n `%c[Aegis SDK]%c v${v} loaded`,\n 'color:#fff;background:#7c3aed;padding:1px 6px;border-radius:4px;font-weight:700',\n 'color:#7c3aed;font-weight:600',\n );\n } catch {\n /* console may be unavailable */\n }\n }\n }\n const triggerEngine = config.triggerEngine ?? new TriggerEngine();\n const ownsTriggerEngine = !config.triggerEngine;\n this.triggerEngine = triggerEngine;\n this.ownsTriggerEngine = ownsTriggerEngine;\n this.contactId = config.contactId;\n this.contactToken = config.contactToken;\n this.pairingApiHost = config.apiHost ?? '';\n // Ownership of the engine is tracked downstream by WidgetManager\n // (which is the only consumer that needs to call lifecycle methods\n // on it). The facade itself only forwards.\n\n this.widgets = new AegisWidgetManager({\n writeKey: config.writeKey,\n apiHost: config.apiHost,\n userId: config.userId,\n contactId: config.contactId,\n contactToken: config.contactToken,\n organizationId: config.organizationId,\n debugMode: config.debugMode,\n triggerEngine,\n ownsTriggerEngine,\n enablePrefetch: config.enableWidgetPrefetch !== false,\n sourcePlatform: config.sourcePlatform,\n getWorkspaceId: config.getWorkspaceId,\n // Cart-store unification: when the widget subsystem detects a platform\n // cart (Shopify/Woo/Magento) or the host calls `widgets.setCartData`, flow\n // it UP into the ONE cart store so the Free-Delivery card / progress bar /\n // rule eval see the same cart. See CART_STORE_UNIFICATION_TRACKER.md.\n onCartDetected: (cart) =>\n this.applyCart({\n total: cart.cart_total,\n itemCount: Array.isArray(cart.cart_items)\n ? cart.cart_items.reduce((s, it) => s + (Number((it as { quantity?: number }).quantity) || 1), 0)\n : 0,\n currency: cart.cart_currency,\n }),\n });\n\n this.inApp = new AegisInAppManager({\n writeKey: config.writeKey,\n apiHost: config.apiHost,\n userId: config.userId,\n contactId: config.contactId,\n contactToken: config.contactToken,\n organizationId: config.organizationId,\n propertyId: config.propertyId,\n debugMode: config.debugMode,\n enableSSE: config.enableSSE,\n getWorkspaceId: config.getWorkspaceId,\n // First-party cart reader — renderers (progress-bar) read live cart\n // state from here instead of `window.*` cart globals. See setCartState().\n getCartState: () => this.deriveCartState(),\n // Canonical CTA-action handler + platform product-URL resolver\n // (CANONICAL_ACTION_MODEL_TRACKER.md). A host (ACS / Shopify-Woo plugin /\n // native) registers ONE `onAction` to handle add_to_cart/open_product/…;\n // legacy `aegis:*` DOM events still fire for back-compat.\n onAction: config.onAction,\n productUrl: config.productUrl,\n });\n\n this.intentRuleEvaluator = new IntentRuleEvaluator();\n // Opt-in interruption governance (plan §5) — the storefront enables\n // 1/session + a TTFI floor; other consumers keep the unchanged base contract.\n if (config.governance) {\n this.intentRuleEvaluator.setGovernance(config.governance);\n }\n this.contactScores = new ContactScoresFetcher({\n // Falls back to same-origin when no apiHost configured (storefront\n // typical case) — matches WidgetManager/InAppManager handling\n // where `config.apiHost` is also `string | undefined`.\n apiHost: config.apiHost ?? '',\n writeKey: config.writeKey,\n organizationId: config.organizationId,\n evaluator: this.intentRuleEvaluator,\n });\n\n // Loyalty subsystem — closed-loop store credit + gift cards.\n // Constructed even when no slug is provided so the readonly prop\n // is stable; calls throw until configured.\n this.loyalty = new AegisLoyaltyManager({\n slug: config.storefrontSlug ?? '__unconfigured__',\n apiHost: config.apiHost,\n debugMode: config.debugMode,\n });\n\n // Customer chat launcher (Track A) — opt-in, only mounted when enabled so\n // non-chat tenants pay nothing. Inherits identity/transport from the runtime.\n if (config.enableChat) {\n this.chat = new AegisChat({\n writeKey: config.writeKey,\n apiHost: config.apiHost,\n organizationId: config.organizationId,\n contactId: config.contactId,\n debugMode: config.debugMode,\n ...config.chat,\n });\n\n // Proactive auto-open: subscribe the configured client triggers (cart-\n // stall surfaces as `inactivity_*`, exit-intent as `exit_intent`). The\n // trigger ONLY opens the panel — the warm-offer opener itself is enqueued\n // server-side (journey→warm-offer-queue bridge) and surfaced by the bot\n // on the first turn. No parallel client-side offer infra.\n // Each name is armed on the engine (registered + subscribed): a bare\n // `on()` never fires, the engine only detects what is registered.\n const proactive = (config.chat?.proactiveTriggers ?? []).map(triggerFromEngineEvent);\n armBehaviouralTriggers(\n triggerEngine,\n proactive,\n new Set<string>(),\n () => this.chat?.openPanel(),\n AegisMessageRuntime.hasNoHover(),\n );\n }\n\n this.microIntent = new MicroIntentArming({\n engine: triggerEngine,\n evaluator: this.intentRuleEvaluator,\n show: (campaignId) => this.inApp.showTriggeredCampaign(campaignId),\n hasConverted: () => this.inApp.hasConvertedThisSession(),\n isMobile: AegisMessageRuntime.hasNoHover(),\n });\n\n // Behavioural in-app triggers and smart triggers: armed on every campaign\n // load (refreshes bring new campaigns), each engine event subscribed once.\n this.unsubscribeCampaignsLoaded = this.inApp.onCampaignsLoaded((campaigns) => {\n armBehaviouralTriggers(\n triggerEngine,\n campaigns.map((c) => c.client_trigger),\n this.armedInAppTriggers,\n (eventName) => this.inApp.onClientEvent(eventName),\n AegisMessageRuntime.hasNoHover(),\n );\n this.microIntent.arm(campaigns);\n });\n }\n\n /** No hovering pointer (phones, tablets): exit intent is a fast scroll up,\n * not the mouse leaving the window. */\n private static hasNoHover(): boolean {\n try {\n return typeof window !== 'undefined' && window.matchMedia?.('(hover: none)')?.matches === true;\n } catch {\n return false;\n }\n }\n\n /**\n * Boots both managers in parallel. Safe to call multiple times — the\n * second + subsequent calls are no-ops.\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n this.initialized = true;\n // Surface-Canvas pairing entry — `?aegis_pair=CODE`. Works for EVERY runtime\n // consumer (the Active Commerce Store + any external site that npm-installs\n // the SDK — the preferred distribution), so the first-party storefront pairs\n // exactly like an external npm-SDK site. Idempotent (shared module guard with\n // the cdn.ts / Shopify-Magento-Woo path); no-op when the param is absent.\n maybeStartPairing(this.pairingApiHost);\n await Promise.all([\n this.inApp.initialize(),\n this.widgets.initialize(),\n ]);\n // Mount the chat launcher bubble (DOM only — no network until opened).\n this.chat?.initialize();\n // Start the engine this runtime created (a caller-supplied one is the\n // caller's to start). Triggers armed later attach on registration.\n if (this.ownsTriggerEngine) {\n this.triggerEngine.start();\n }\n // Fire-and-forget the Layer-1 score fetch when we already know the\n // contact at init. Identified-contact micro-intent patterns need\n // this snapshot — without it their AND-compound rules eval to\n // false. Anonymous patterns are unaffected.\n if (this.contactId) {\n void this.contactScores.fetchForContact(this.contactId, { contactToken: this.contactToken });\n }\n }\n\n /**\n * Both managers carry contactId. Updates both so the server-side\n * targeting pipeline sees the identity for campaign eligibility AND\n * the widget prefetch includes per-contact segment configs. The\n * widgets call is async (may re-fetch prefetch configs) but we don't\n * wait — callers that care about the fetched state await on the\n * returned promise.\n */\n async updateContactId(contactId: string, contactToken?: string): Promise<void> {\n this.contactId = contactId;\n // C1b: a token is bound to ONE contact — a new id without its own token\n // must not keep the previous contact's proof.\n this.contactToken = contactToken;\n // The token (server-minted proof of this contact, S/C1) lets in-app\n // answers write onto the profile; without it they stay anonymous events.\n this.inApp.updateContactId?.(contactId, contactToken);\n this.chat?.updateContactId(contactId);\n await this.widgets.updateContactId(contactId, contactToken);\n // Re-fetch Layer-1 scores for the new identity. Session-dedup in\n // the fetcher means a repeat call with the same contactId is a\n // no-op, so SPA navigation that re-calls identify() is cheap.\n void this.contactScores.fetchForContact(contactId, { contactToken });\n }\n\n /**\n * Forward a client-side event (product_viewed, cart_idle_90s, etc.)\n * to the in-app manager's client-trigger evaluator. The WidgetManager\n * has its own TriggerEngine wiring (exit-intent, scroll-velocity) so\n * we don't forward there — events it cares about arrive through that\n * channel already.\n */\n onClientEvent(eventName: string, eventData: Record<string, unknown> = {}): void {\n this.inApp.onClientEvent?.(eventName, eventData);\n }\n\n /**\n * Unified reactive context — the GENERAL imperative state API (Plotline-style\n * \"global context state engine\"). The host app feeds named namespaces:\n * runtime.updateContext('cart', { total: 540, items: 3, currency: 'INR' })\n * runtime.updateContext('user', { tier: 'gold', lifecycle: 'active' })\n * runtime.updateContext('session', { idle_s: 60 })\n *\n * Held in PRIVATE runtime state (no `window` pollution). Merged shallowly per\n * namespace and evaluated entirely client-side — no round-trip. This supersedes\n * the single-purpose `setCartState` (kept as a thin wrapper for back-compat).\n *\n * Design note (vs the proposed rewrite): we DELIBERATELY do NOT introduce a\n * parallel rule schema. Our canonical `IntentRule` (shared with the cell-plane\n * Pydantic schema + the editor + seeded campaigns) stays the source of truth.\n * `updateContext` BRIDGES known paths into the existing enum-keyed\n * `IntentSnapshot` (e.g. cart.total → the `cart_value` signal) so today's rules\n * keep firing — fed by the generic API. Arbitrary dot-path leaves\n * (`cart.foo`) are a forward-compatible follow-up that adds an optional `path`\n * to the canonical `IntentLeaf` (schema-mirrored + drift-guarded), NOT a second\n * evaluator. Tri-state (null = unprovable) evaluation is preserved — we do not\n * regress missing paths to `false`.\n */\n updateContext(namespace: string, data: Record<string, unknown>): void {\n if (!namespace || typeof namespace !== 'string' || !data) return;\n // ONE store — the evaluator's namespaces. Path-leaves (cart.total) resolve\n // against this directly; no copy on the runtime.\n this.intentRuleEvaluator.updateContext(namespace, data as Record<string, never>);\n this.bridgeContextToSignals(namespace);\n // Re-arm context-gated campaigns (debounced manager-side).\n this.inApp.refreshOnEvent('context_updated');\n }\n\n /** Read the current client-side context snapshot (for the editor scrubber /\n * preview bridge to mirror live values). Read-only view of the ONE store. */\n getContext(): Readonly<Record<string, Readonly<Record<string, unknown>>>> {\n return this.intentRuleEvaluator.getNamespaces() as Readonly<\n Record<string, Readonly<Record<string, unknown>>>\n >;\n }\n\n /**\n * Bridge well-known context paths into the canonical enum-keyed IntentSnapshot,\n * so existing signal-based rules (cart_value) keep firing off the generic\n * `updateContext`. Reads from the ONE store (the evaluator). Extend here as new\n * signals map to context paths — no parallel engine.\n */\n private bridgeContextToSignals(namespace: string): void {\n if (namespace === 'cart') {\n const total = Number(this.intentRuleEvaluator.getValueByPath('cart.total')\n ?? this.intentRuleEvaluator.getValueByPath('cart.cart_value'));\n if (Number.isFinite(total)) {\n this.intentRuleEvaluator.updateSignal('cart_value', total);\n }\n }\n }\n\n /** Derive the typed cart snapshot for the progress-bar renderer reader from\n * the ONE context store (the `cart` namespace). No separate cart field. */\n private deriveCartState(): CartState | undefined {\n const total = Number(this.intentRuleEvaluator.getValueByPath('cart.total'));\n if (!Number.isFinite(total)) return undefined;\n const items = Number(\n this.intentRuleEvaluator.getValueByPath('cart.items')\n ?? this.intentRuleEvaluator.getValueByPath('cart.itemCount'),\n );\n const currency = this.intentRuleEvaluator.getValueByPath('cart.currency');\n return {\n total,\n itemCount: Number.isFinite(items) ? items : 0,\n currency: typeof currency === 'string' ? currency : 'INR',\n };\n }\n\n /**\n * Back-compat wrapper over `updateContext('cart', …)`. The single-purpose cart\n * feed for headless storefronts; prefer `updateContext` for new code.\n */\n setCartState(state: CartState): void {\n this.applyCart(state);\n }\n\n /**\n * Register the canonical CTA-action handler — the CEP-standard single callback\n * that receives every in-app intent (add_to_cart / open_product / open_cart /\n * open_login / navigate / custom). Return `true` when handled; otherwise the SDK\n * applies the graceful fallback. The construct-time `onAction` config is the\n * equivalent; this lets a host register/replace it once it holds the runtime.\n * See CANONICAL_ACTION_MODEL_TRACKER.md.\n */\n onAction(handler: (action: AegisAction) => boolean | void): void {\n this.inApp.setOnAction(handler);\n }\n\n /**\n * The ONE cart writer. Normalizes any cart-ish shape and funnels it into the\n * single `cart` context namespace (read by the Free-Delivery progress card,\n * the progress-bar widget, and rule eval via the `cart_value` signal). EVERY\n * cart feed routes through here: ecommerce events (`bindEcommerce`), platform\n * auto-detect + `widgets.setCartData` (`onCartDetected`), and the explicit\n * `setCartState` escape hatch. See CART_STORE_UNIFICATION_TRACKER.md.\n */\n applyCart(cart: { total?: number; itemCount?: number; items?: number; currency?: string }): void {\n const total = Number(cart.total) || 0;\n const itemCount = Number(cart.itemCount ?? cart.items) || 0;\n const currency = typeof cart.currency === 'string' && cart.currency ? cart.currency : 'INR';\n this.updateContext('cart', { total, items: itemCount, currency });\n }\n\n /**\n * Wire the analytics ecommerce tracker into the cart store — the CEP-standard\n * path. After this, a host firing `aegis.ecommerce.addToCart(...)` /\n * `cartViewed(...)` automatically drives every cart-reactive surface; no\n * separate `setCartState` call is needed. Call once after constructing both\n * the analytics SDK and the runtime. Idempotent.\n */\n bindEcommerce(tracker: { onCartChange(sink: (c: { total: number; itemCount: number; currency: string }) => void): void }): void {\n tracker.onCartChange((c) => this.applyCart(c));\n }\n\n // --- Qualifying-event refresh facade (DLR Tracker 1 / P1, 2026-05-28) ---\n //\n // `track`, `page`, and `screen` here are thin trigger taps for the\n // in-app subsystem — they tell `AegisInAppManager.refreshOnEvent` that\n // a host-app event happened, and the debounced refresh re-fetches\n // `/v1/in-app/active` so newly armed campaigns become visible without\n // waiting for the next identity flip. They are NOT a substitute for\n // the main analytics SDK (`Aegis` in `core/analytics.ts`) — those\n // methods own the canonical capture path (batch uplink, governance,\n // workspace resolution, etc.). Integrations that already call\n // `aegis.track(...)` should also call `runtime.track(...)` to drive\n // the in-app refresh. A future P-phase will bridge the two so a single\n // call on the public analytics surface drives both behaviors.\n\n /**\n * Notify the in-app subsystem that a tracked event occurred. Schedules\n * a debounced refresh of armed campaigns so newly eligible in-app\n * messages (e.g., trigger-gated on `cart_item_added`) become visible\n * within ~300ms — bursts coalesce. See\n * `AegisInAppManager.refreshOnEvent` for debounce semantics and why\n * this matters under storefront polling.\n */\n track(eventName: string): void {\n if (!eventName) return;\n this.inApp.refreshOnEvent(eventName);\n }\n\n /**\n * Notify the in-app subsystem that a URL-bound page view occurred.\n * Mirrors `aegis.page()` semantics — fires `'page_view'` as the\n * qualifying event so the trailing debounce covers SPA route bursts\n * (history.pushState rapid-fire) without spamming the endpoint.\n */\n page(name?: string): void {\n // isScreenDeclaration=true → the in-app manager captures the raw name as\n // `currentScreen` for free-form targeting (Dynamic Screens P2). The\n // 'page_view' default sentinel is filtered out manager-side.\n this.inApp.refreshOnEvent(name ?? 'page_view', true);\n }\n\n /**\n * Notify the in-app subsystem that a SPA-logical screen view occurred\n * (cart drawer open, checkout step transition — anywhere `aegis.screen()`\n * is called). Refresh-on-screen lets per-screen-targeted campaigns\n * arm without a URL change.\n */\n screen(name: string): void {\n if (!name) return;\n // isScreenDeclaration=true → captured as the raw `currentScreen` for\n // free-form screen targeting (Dynamic Screens P2).\n this.inApp.refreshOnEvent(name, true);\n }\n\n /**\n * Conversion-aware suppression — call when the host app observes a\n * goal event (purchase / order_completed / checkout_completed / custom).\n *\n * Forwards to AegisInAppManager.notifyConversion() which:\n * - Persists the conversion in sessionStorage so subsequent page loads\n * in the same tab keep armed campaigns silenced.\n * - For every armed campaign whose\n * `frequency.suppress_after_conversion_seconds` is set, computes an\n * expiry epoch_ms and silences it until then.\n *\n * Storefronts integrate via the React provider's useCommerceEvents\n * helper which calls this after `ecom.orderCompleted(...)`. Direct SDK\n * users call it themselves.\n *\n * Added by Micro-Intent Engine P0a Task 6 (2026-04-30).\n */\n notifyConversion(goalName: string): void {\n this.inApp.notifyConversion?.(goalName);\n if (goalName) this.microIntent.noteConversion();\n }\n\n /**\n * Tear down both managers. Used by React component unmounts + during\n * identity switches where we want a full reset. If the facade created\n * its own TriggerEngine, WidgetManager.destroy() will stop it;\n * caller-supplied engines are left alone.\n */\n destroy(): void {\n this.unsubscribeCampaignsLoaded();\n this.microIntent.destroy();\n this.inApp.destroy?.();\n this.widgets.destroy();\n this.chat?.destroy();\n this.initialized = false;\n }\n\n /**\n * Current armed campaigns visible to the InAppManager. Accessible for\n * the Prefetch Inspector and for debugging. WidgetManager's prefetched\n * exit-intent popup config is NOT in this list — it lives in\n * `this.widgets` and has its own lifecycle.\n */\n getCampaigns(): InAppCampaign[] {\n // AegisInAppManager keeps `campaigns` as a private field. Expose it\n // via bracket access; the field is stable as of SDK 1.5.0. Once\n // Phase 7c unifies render paths this becomes a first-class getter.\n return ((this.inApp as unknown as { campaigns?: InAppCampaign[] }).campaigns) ?? [];\n }\n}\n","/**\n * SDK Bootstrap\n *\n * Strict origin-assertion handshake against the control-plane. Called once\n * at SDK init — resolves writeKey → propertyId, asserts origin, and returns\n * the VAPID public key + first-party cookie id the SDK needs for the rest\n * of the session.\n *\n * A failed bootstrap (401/403) aborts SDK init — the SDK refuses to emit\n * events or request push permission against an origin it cannot prove it\n * owns.\n *\n * See docs/architecture/SUBSCRIPTION_PROPERTY_ARCHITECTURE.md §5.\n */\n\nconst COOKIE_NAME = 'aegis_fpc';\nconst COOKIE_MAX_AGE_DAYS = 365 * 2;\n\nexport interface BootstrapRequest {\n writeKey: string;\n currentOrigin?: string;\n firstPartyCookieId?: string;\n attestationToken?: string;\n userAgent?: string;\n}\n\nimport type { EventGovernanceHint } from '../governance';\n\nexport interface BootstrapResult {\n propertyId: string;\n organizationId: string;\n workspaceId: string;\n propertyType: 'shopify_store' | 'hosted_commerce' | 'custom_website' | 'mobile_app';\n vapidPublicKey: string | null;\n allowedOrigins: string[];\n pushEnabled: boolean;\n inAppEnabled: boolean;\n transportMode: string;\n firstPartyCookieId: string;\n /**\n * Event-governance hint for SDK-side unique-event-name cap enforcement.\n * Null = Enterprise plan / CP outage / org has no cap. Safe to pass\n * directly to `aegis.ingestGovernanceHint()`.\n */\n eventGovernance: EventGovernanceHint | null;\n /**\n * @deprecated since P15.1 (2026-05-26) — use `locationCodes` instead.\n * Pre-2026-05-26 the path segment after the brand subdomain was a\n * workspace_code; the canonical hierarchy reversal made it a\n * location_code (outlet under a brand). Field name retained for\n * backward compat with SDK consumers that haven't migrated; new\n * consumers MUST read `locationCodes`. Server still populates BOTH\n * with the same array during the deprecation window so a stale\n * SDK keeps working.\n */\n workspaceCodes: string[];\n /**\n * P15.1 — outlet-code allowlist for SDK path-segment detection.\n * Empty array = no outlet codes registered for this org (single-\n * outlet tenant). SDK uses this set to decide whether\n * `location.pathname.split('/')[1]` is a valid outlet code vs a\n * generic route (`/products`, `/collections`). Pass to\n * `aegis.ingestLocationCodes()` after bootstrap completes.\n *\n * Same array contents as `workspaceCodes` during the deprecation\n * window — renaming the field gives semantic clarity (these ARE\n * location codes under the new hierarchy) without breaking\n * existing consumers.\n */\n locationCodes: string[];\n}\n\nexport class BootstrapError extends Error {\n constructor(public readonly status: number, message: string) {\n super(message);\n this.name = 'BootstrapError';\n }\n}\n\n/**\n * Read the first-party cookie id from document.cookie. Returns null if\n * unset — caller should pass undefined so the server issues a fresh one.\n */\nexport function readFirstPartyCookie(): string | null {\n if (typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp(`(?:^|;\\\\s*)${COOKIE_NAME}=([^;]+)`));\n return match ? decodeURIComponent(match[1]) : null;\n}\n\n/**\n * Persist the first-party cookie id returned by the server. Stored as a\n * first-party cookie (readable by JS — it's not a secret, it's a device\n * id) so that the same browser hitting a second origin on the same\n * registrable domain gets the same cookie back.\n */\nexport function writeFirstPartyCookie(cookieId: string): void {\n if (typeof document === 'undefined') return;\n const expires = new Date();\n expires.setDate(expires.getDate() + COOKIE_MAX_AGE_DAYS);\n // Derive the registrable-domain boundary where possible so the cookie is\n // shared across subdomains (e.g., shop.sweettruths.com + www.sweettruths.com).\n // We conservatively use the current host without a leading dot —\n // browsers scope the cookie to that host. For cross-registrable-domain\n // siblings (Shopify myshopify.com + actii.me), each domain gets its own\n // cookie, which is the expected per-origin behavior.\n document.cookie =\n `${COOKIE_NAME}=${encodeURIComponent(cookieId)};` +\n `expires=${expires.toUTCString()};` +\n `path=/;SameSite=Lax`;\n // Also mirror into localStorage so the SDK can read it synchronously at\n // init time even in environments where document.cookie is delayed.\n try {\n window.localStorage.setItem(COOKIE_NAME, cookieId);\n } catch {\n // private mode / storage disabled — cookie alone is sufficient\n }\n}\n\n/**\n * Perform the bootstrap handshake.\n *\n * @throws BootstrapError on 4xx — the SDK should stop init and surface the\n * error to the console. 5xx are retried once.\n */\nexport async function bootstrap(\n apiHost: string,\n req: BootstrapRequest\n): Promise<BootstrapResult> {\n const body: Record<string, unknown> = { writeKey: req.writeKey };\n if (req.currentOrigin) body.currentOrigin = req.currentOrigin;\n if (req.firstPartyCookieId) body.firstPartyCookieId = req.firstPartyCookieId;\n if (req.attestationToken) body.attestationToken = req.attestationToken;\n if (req.userAgent) body.userAgent = req.userAgent;\n\n const url = `${apiHost.replace(/\\/$/, '')}/v1/sdk/bootstrap`;\n\n const doFetch = async (): Promise<Response> =>\n fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n credentials: 'omit',\n });\n\n let resp = await doFetch();\n if (resp.status >= 500) {\n await new Promise((r) => setTimeout(r, 750));\n resp = await doFetch();\n }\n\n if (resp.status === 401) {\n throw new BootstrapError(401, 'writeKey not recognized or revoked');\n }\n if (resp.status === 403) {\n throw new BootstrapError(403, 'Origin validation failed — this writeKey is bound to a different property');\n }\n if (!resp.ok) {\n throw new BootstrapError(resp.status, `Bootstrap failed: HTTP ${resp.status}`);\n }\n\n const json = (await resp.json()) as BootstrapResult;\n writeFirstPartyCookie(json.firstPartyCookieId);\n return json;\n}\n\n/**\n * Derive a deterministic device fingerprint from the first-party cookie id\n * and a few stable UA parts. This is the value sent on every push-subscribe\n * call — its stability lets the backend UPSERT on (property_id, fingerprint)\n * across re-subscribes and VAPID rotations.\n */\nexport async function deriveDeviceFingerprint(firstPartyCookieId: string): Promise<string> {\n const ua = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n const platform = typeof navigator !== 'undefined' ? (navigator.platform || '') : '';\n const language = typeof navigator !== 'undefined' ? (navigator.language || '') : '';\n const input = `${firstPartyCookieId}|${ua}|${platform}|${language}`;\n\n // Prefer subtle.crypto — available in all push-capable browsers.\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n const bytes = new TextEncoder().encode(input);\n const hash = await crypto.subtle.digest('SHA-256', bytes);\n const hex = Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n return hex;\n }\n\n // Last-resort fallback — basic 32-bit FNV hash (non-cryptographic but\n // stable). Push-capable browsers always have subtle.crypto so this path\n // is practically unreachable; included only for completeness.\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;\n }\n return `fnv-${h.toString(16)}`;\n}\n","import { Aegis } from './core/analytics';\nimport { AegisMessageRuntime } from './runtime';\nimport { TriggerEngine } from './triggers';\nimport { AegisChat, openChat, closeChat } from './chat';\nimport { bootstrap } from './core/bootstrap';\n// Single-campaign preview renderer — used by the dashboard editor's iframe so\n// the preview is THE real renderer (zero drift, real interactivity), not a\n// separate static mock. Exposed on window for the sandboxed preview route.\nimport { renderPreview } from './inapp/renderPreview';\n// Surface Canvas P6 — shared web device-pairing entry. Also called by\n// AegisMessageRuntime, so the CDN bundle (Shopify/Magento/Woo) and the npm-SDK\n// consumers (Active Commerce Store + external sites) pair identically.\nimport { maybeStartPairing } from './inapp/devicePairing';\n\ndeclare global {\n interface Window {\n aegis: any;\n Aegis: typeof Aegis;\n AegisMessageRuntime: typeof AegisMessageRuntime;\n TriggerEngine: typeof TriggerEngine;\n /** Customer chat launcher (\"Shopping Assistant\") constructor (Track A).\n * Usually mounted via `new AegisMessageRuntime({ enableChat: true })`,\n * but exposed for host pages that want to construct it standalone. */\n AegisChat: typeof AegisChat;\n /**\n * `/v1/sdk/bootstrap` handshake. Exposed for integrations that embed the\n * CDN bundle and need to retrieve `eventGovernance`, `vapidPublicKey`, etc.\n * Added in SDK 1.4.0 alongside SDK-side event-governance.\n */\n aegisBootstrap: typeof bootstrap;\n }\n}\n\nconst globalName = 'aegis';\nconst existingQueue: any = window[globalName] || [];\n\nconst instance = new Aegis();\n\nif (Array.isArray(existingQueue)) {\n const loadOptions = (existingQueue as any)._loadOptions;\n\n existingQueue.forEach((item: any) => {\n if (!Array.isArray(item)) {\n return;\n }\n\n const [method, ...args] = item;\n\n if (typeof method !== 'string' || method.length === 0) {\n return;\n }\n\n // Resolve dotted paths (e.g. 'user.login') by walking the object\n // graph. Snippet callers can use `aegis.user.login(id)` before the\n // bundle finishes loading; the queue captures ['user.login', id]\n // and we replay it as instance.user.login(id).\n const parts = method.split('.');\n let owner: any = null;\n let fn: any = instance;\n for (let i = 0; i < parts.length; i += 1) {\n if (fn == null) break;\n owner = fn;\n fn = fn[parts[i]];\n }\n\n if (typeof fn === 'function') {\n try {\n fn.apply(owner, args);\n } catch (error) {\n console.error(`[Active Reach SDK] Error executing queued method \"${method}\":`, error);\n }\n } else {\n console.warn(`[Active Reach SDK] Unknown method \"${method}\"`);\n }\n });\n\n if (loadOptions?.key) {\n instance.init(loadOptions.key, loadOptions.options).catch((error) => {\n console.error('[Active Reach SDK] Initialization failed:', error);\n });\n }\n}\n\nwindow[globalName] = instance;\nwindow.Aegis = Aegis;\nwindow.AegisMessageRuntime = AegisMessageRuntime;\nwindow.TriggerEngine = TriggerEngine;\nwindow.AegisChat = AegisChat;\n// Canonical public open API — a host button (any site) opens the chat with a\n// one-liner: `aegis.chat.open()`. Targets the single mounted launcher (campaign\n// or runtime path) via the shared registry; no-op if chat isn't active here.\n(instance as unknown as { chat: { open: typeof openChat; close: typeof closeChat } }).chat = {\n open: openChat,\n close: closeChat,\n};\nwindow.aegisBootstrap = bootstrap;\n(window as unknown as { AegisRenderPreview?: typeof renderPreview }).AegisRenderPreview =\n renderPreview;\n\n// Surface Canvas P6 — web device pairing. A property owner appends\n// `?aegis_pair=CODE` (the code shown in the dashboard) to enter pairing mode:\n// the SDK claims the code + streams this page's element tree to the canvas. The\n// apiHost comes from the snippet's load options (falls back to the prod host).\ntry {\n const lo = (existingQueue as any)?._loadOptions;\n maybeStartPairing(lo?.options?.apiHost);\n} catch {\n /* pairing is best-effort — never block SDK boot */\n}\n\nexport default instance;\n","/**\n * renderPreview — Static Preview Renderer for Dashboard iframe (Critical Fix A)\n *\n * Used exclusively by the sandboxed iframe preview route\n * (`/api/preview/in-app/route.tsx`). Renders a single campaign into the\n * current document body using the same rendering logic as the production SDK.\n *\n * Data flow:\n * editor-sidebar.tsx → onChange → formState → postMessage({type:'preview_update', config})\n * → iframe receives message → calls renderPreview(config)\n *\n * This ensures 100% fidelity between the dashboard editor preview and\n * production rendering — both use the same SDK code.\n */\n\nimport type { InAppCampaign } from './AegisInAppManager';\nimport { AegisInAppManager } from './AegisInAppManager';\nimport { renderStoriesRings, type RenderContext } from './renderers';\nimport type { CartState } from './renderers/types';\n\n/** Optional preview inputs — e.g. a scrubbed cart so cart-reactive widgets\n * (progress_bar) react in the in-page preview exactly as they will live. */\nexport interface RenderPreviewOptions {\n cart?: CartState;\n /** The OTHER campaigns already occupying the same slot (the real current\n * occupants). When present, the inline preview renders the whole slot GROUP\n * through the production `renderIntoSlots` engine — so the operator sees the\n * slot as it really is: single best-match (highest priority) in personalized\n * mode, or the full group cycling when the slot is a carousel\n * (slot_rotate_ms / data-aegis-slot-rotate). */\n coGroup?: Array<InAppCampaign & { interactive_config?: Record<string, unknown> }>;\n}\n\n/**\n * Renders a single in-app campaign into the current document for preview.\n *\n * Creates a temporary AegisInAppManager with preview-only config (no tracking,\n * no SSE, no network requests). Calls the same rendering pipeline as production.\n */\nexport function renderPreview(\n config: InAppCampaign & { interactive_config?: Record<string, unknown> },\n opts?: RenderPreviewOptions,\n): void {\n // Clear previous preview\n document.querySelectorAll(\n '[class^=\"aegis-in-app-\"]'\n ).forEach((el) => {\n if (el.parentNode) {\n el.parentNode.removeChild(el);\n }\n });\n\n // Reset any per-contact \"already seen / completed\" persistence so the\n // preview always renders fresh. Without this, clicking through a\n // coachmark tour (which writes `aegis_coachmark_progress:<resume_key>` to\n // localStorage on completion) would make every subsequent preview render\n // return early — the tour would vanish and never reappear. The preview is\n // a design surface, not a real session, so it must never \"remember\" state.\n try {\n if (typeof localStorage !== 'undefined') {\n for (let i = localStorage.length - 1; i >= 0; i--) {\n const key = localStorage.key(i);\n if (\n key &&\n (key.startsWith('aegis_coachmark_progress:') ||\n key.startsWith('aegis_sticky_dismissed:'))\n ) {\n localStorage.removeItem(key);\n }\n }\n }\n } catch {\n // localStorage blocked — nothing to reset.\n }\n\n if (!config) return;\n\n // Create a lightweight preview-only manager. When a scrubbed cart is supplied,\n // feed it through `getCartState` so cart-reactive widgets (progress_bar) react\n // in the in-page preview the same way they will live.\n const manager = new AegisInAppManager({\n writeKey: 'preview-mode',\n apiHost: '',\n debugMode: false,\n enableSSE: false,\n ...(opts?.cart ? { getCartState: () => opts.cart } : {}),\n });\n\n // Override trackEvent to be a no-op in preview mode\n // Access private via any cast — preview only, not production code\n const m = manager as unknown as {\n displayCampaign: (campaign: InAppCampaign) => void;\n trackEvent: (campaignId: string, eventType: string) => Promise<void>;\n addAnimationStyles: () => void;\n buildRenderContext: (campaign: InAppCampaign) => RenderContext;\n };\n\n // No-op tracking for preview\n m.trackEvent = async () => {};\n\n // Ensure animation styles are injected\n m.addAnimationStyles();\n\n // Inline (embedded_card) preview — render the campaign INTO its real slot on\n // the page (the same renderIntoSlots engine production uses), not as an\n // overlay. This is what makes a footer/inline campaign preview IN its slot\n // instead of as a confusing centered modal. Falls back to the overlay path\n // when the slot isn't on this page.\n const cfgRec = config as unknown as Record<string, unknown>;\n const deliveryModes = Array.isArray(cfgRec.delivery_modes)\n ? (cfgRec.delivery_modes as string[])\n : [];\n const widgetCategory =\n typeof cfgRec.widget_category === 'string' ? (cfgRec.widget_category as string) : '';\n const inline = deliveryModes.includes('embedded_card') && !!widgetCategory;\n const coGroup = opts?.coGroup ?? [];\n\n // PAGE preview — render the REAL page: every [data-aegis-slot] filled with its\n // live campaigns (opts.coGroup = the surface's active slot campaigns) PLUS the\n // authored campaign joining its slot when it's inline. renderIntoSlots fills\n // ALL slots (best-match per slot, or cycling when a slot is a carousel). Runs\n // whenever there's a group to show OR the authored campaign is inline — so the\n // page shows its campaigns even while the authored one is an overlay (rendered\n // on top below). This is the SAME production engine, no parallel renderer.\n if (typeof document !== 'undefined' && (coGroup.length > 0 || inline)) {\n const anchors = document.querySelectorAll('[data-aegis-slot]');\n if (anchors.length > 0) {\n try {\n // Clear prior campaign injections but KEEP each slot's SSR default, so\n // unfilled slots still show their native default and filled slots\n // re-render cleanly (no stacking across preview updates).\n anchors.forEach((slot) => {\n slot\n .querySelectorAll(':scope > :not([data-aegis-slot-default])')\n .forEach((el) => el.remove());\n });\n const mm = manager as unknown as {\n campaigns: InAppCampaign[];\n renderIntoSlots: () => void;\n };\n // Surface-agnostic seed (so the slot gate passes in the preview context),\n // sorted DESC by priority (the production feed arrives pre-sorted). When\n // the authored campaign is inline it joins the group for its slot.\n mm.campaigns = (inline ? [config, ...coGroup] : coGroup)\n .map((c) => ({ ...c, surface: undefined, target_screens: undefined }) as InAppCampaign)\n .sort((a, b) => (b.priority || 0) - (a.priority || 0));\n mm.renderIntoSlots();\n if (inline) return; // authored shown inline in its slot — done\n // else the authored campaign is an overlay → fall through to render it\n // ON TOP of the now-populated page.\n } catch {\n // fall through to the overlay/stories preview below\n }\n }\n }\n\n // Stories' canonical surface is the INLINE launcher tray (the embedded\n // widget), not the full-screen takeover player. Pin the tray to the TOP of the\n // preview — that's where a real stories rail lives (Instagram/app home), and a\n // centered tray floating mid-screen read as \"random\". Full-width row so it\n // adapts to both the narrow mobile frame and the wide desktop frame; the tray\n // itself scrolls horizontally when the cards overflow.\n if (config.type === 'stories' || config.sub_type === 'stories') {\n const host = document.createElement('div');\n host.className = 'aegis-in-app-stories-host';\n host.style.cssText =\n 'position: fixed; left: 0; right: 0; top: 0; z-index: 99998; padding-top: 4px;' +\n // Subtle tray surface so the row reads as a real stories rail in the\n // preview (branded trays paint their own background over this).\n 'background: rgba(255,255,255,0.94); backdrop-filter: blur(8px);' +\n 'box-shadow: 0 2px 12px rgba(0,0,0,0.06);';\n document.body.appendChild(host);\n renderStoriesRings(m.buildRenderContext(config), host);\n return;\n }\n\n // Render the campaign\n m.displayCampaign(config);\n}\n","export interface AegisAPIClient {\n post(\n endpoint: string,\n payload: Record<string, unknown>,\n headers?: Record<string, string>\n ): Promise<unknown>;\n}\n\nexport interface ContactIdentity {\n contactId?: string;\n shopifyCustomerId?: string;\n email?: string;\n}\n\nexport interface AegisWebPushConfig {\n vapidPublicKey: string;\n // SubscriptionProperty binding — supplied by the embedding host (e.g.\n // the storefront SSR via the `sdk.property_id` field on /store/{slug}).\n // Required: every subscribe call sends it back as `X-Property-Id` so\n // the backend pins the row to the right origin.\n propertyId: string;\n // Stable per-browser cookie id (e.g. aegis_fpc). Used as the\n // device-fingerprint salt so a fresh subscribe after VAPID rotation\n // upserts onto the same `web_push_subscriptions` row.\n firstPartyCookieId: string;\n autoPrompt?: boolean;\n promptDelay?: number;\n serviceWorkerPath?: string;\n serviceWorkerScope?: string;\n // Engagement-event reporting credentials. When BOTH `writeKey` and\n // `organizationId` are provided AND the caller does NOT pass an\n // explicit `eventTracker` to the constructor, AegisWebPush auto-wires\n // a built-in tracker that POSTs push lifecycle events\n // (push.delivered / push.dismissed / push.clicked) to\n // `/v1/push/engagement` — closing the long-standing gap where shown\n // + dismissed signals (only the device-side SDK can report them)\n // never reached `aegis.delivery_events`. See backend dual-route at\n // `apps/event-ingress/src/services/event-switchboard.ts`\n // (SDK_ENGAGEMENT_EVENT_TO_DELIVERY_MAPPING).\n //\n // Callers using `AegisMessageRuntime` should keep passing the\n // runtime's analytics adapter as `eventTracker` — these fields are\n // only the standalone-init fallback.\n writeKey?: string;\n organizationId?: string;\n}\n\nexport interface PushEventTracker {\n track(eventName: string, properties: Record<string, unknown>): void;\n}\n\n/**\n * Payload for the `push-tap` lifecycle event. CANCELLABLE — return `false`\n * (or call `evt.preventDefault()`) from a handler to suppress the SDK's\n * default `window.location.href` fallback navigation. Use this to dispatch\n * an in-place state change in a SPA host instead of a hard reload.\n *\n * `action_type` + `action_payload` are author-controlled fields baked into\n * the push template's `data` block:\n *\n * ```json\n * {\n * \"title\": \"Your cart is waiting\",\n * \"body\": \"...\",\n * \"action_url\": \"https://shop.actii.me/?step=cart\",\n * \"data\": {\n * \"action_type\": \"open_cart\",\n * \"action_payload\": { \"step\": \"cart\" }\n * }\n * }\n * ```\n */\nexport interface PushTapEvent {\n /** Author-controlled action discriminator (e.g. `open_cart`,\n * `open_payment_link`, `open_order`). When unset, the SDK runs its\n * default fallback navigation to `action_url`. */\n action_type?: string;\n /** Optional structured args the host SPA forwards to its handler. */\n action_payload?: Record<string, unknown>;\n /** Sanitized destination URL — used as the SDK's default fallback nav\n * target when no host handler cancels. */\n action_url: string;\n /** Action-button id when the user clicked a specific button on a\n * multi-button notification; `'default'` for body taps. */\n action: string;\n /** Source campaign id from the push payload — useful for SPA routing\n * (e.g. open_cart from a cart-recovery campaign vs from a generic\n * catch-all template). */\n campaign_id?: string;\n /** Mutate via `evt.preventDefault()` to signal the SDK to skip its\n * default hard-navigation. Identical semantics to DOM CustomEvent. */\n preventDefault: () => void;\n defaultPrevented: boolean;\n}\n\n/** Payload for `push-shown` — fires after `showNotification` resolves on\n * the SW side. The `degraded` flag is true when the SW had to fall back\n * to its minimal `{title, body}` retry path (broken icon URL etc.). */\nexport interface PushShownEvent {\n campaign_id?: string;\n degraded?: boolean;\n}\n\n/** Payload for `push-dismissed` — fires when the user closes the\n * notification without clicking. */\nexport interface PushDismissEvent {\n campaign_id?: string;\n}\n\n/** Payload for `error` — non-fatal SDK errors (subscribe failures,\n * resubscribe persist failures, host-handler throws). Wire into Sentry\n * / Datadog RUM. */\nexport interface PushErrorEvent {\n message: string;\n error: unknown;\n context: Record<string, unknown>;\n}\n\n/** Map of lifecycle event name → handler signature. Mirrors 1.8.0\n * in-app lifecycle for consistency. */\nexport interface WebPushLifecycleEventMap {\n 'push-tap': (evt: PushTapEvent) => void | false;\n 'push-shown': (evt: PushShownEvent) => void;\n 'push-dismissed': (evt: PushDismissEvent) => void;\n 'error': (evt: PushErrorEvent) => void;\n}\n\n/**\n * AegisWebPush — Web Push notification manager.\n *\n * Handles service worker registration, push subscription via VAPID,\n * token registration with the backend, and push event tracking.\n */\n// 24h subscription liveness re-sync for long-lived tabs (kiosks/dashboards).\nconst HEARTBEAT_INTERVAL_MS = 24 * 60 * 60 * 1000;\n\nexport class AegisWebPush {\n private apiClient: AegisAPIClient;\n private eventTracker: PushEventTracker | null;\n private swRegistration: ServiceWorkerRegistration | null = null;\n private config: AegisWebPushConfig;\n private initialized = false;\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n\n // ── Lifecycle event bus (1.8.5) ────────────────────────────────────────\n // Typed handler registry, parity with AegisInAppManager (1.8.0).\n // Hosts register via the public `on*` methods below; calling the\n // returned unsubscribe fn removes the handler. Multiple subscribers per\n // event are supported. Cancellable events (`push-tap`) suppress the\n // SDK's default behaviour when a handler returns `false` or calls\n // `evt.preventDefault()`.\n //\n // Industry parity: matches CleverTap `notificationCallback`, OneSignal\n // `addEventListener('notificationOpened')`, MoEngage\n // `onSelfHandledClick`, WebEngage `notificationCallback`. The SW posts\n // a `push.clicked` message; this class translates that into a typed\n // PushTapEvent with cancellable default-nav behaviour.\n private hooks = new Map<string, Set<(...args: unknown[]) => void | false | undefined>>();\n\n constructor(\n apiClient: AegisAPIClient,\n config: AegisWebPushConfig,\n eventTracker?: PushEventTracker\n ) {\n this.apiClient = apiClient;\n this.config = {\n serviceWorkerPath: '/aegis-sw.js',\n serviceWorkerScope: '/',\n autoPrompt: false,\n promptDelay: 5000,\n ...config,\n };\n // Explicit eventTracker wins (typical AegisMessageRuntime flow).\n // Otherwise, if both `writeKey` and `organizationId` are configured,\n // wire the built-in tracker that POSTs to `/v1/push/engagement` so\n // standalone push installs don't silently drop lifecycle events.\n if (eventTracker) {\n this.eventTracker = eventTracker;\n } else if (this.config.writeKey && this.config.organizationId) {\n this.eventTracker = this.createBuiltinEventTracker(\n this.config.writeKey,\n this.config.organizationId,\n );\n } else {\n this.eventTracker = null;\n // Loud warning — without a tracker, push delivered/clicked/dismissed\n // beacons are NOT reported, so push delivery analytics will silently\n // undercount. (Previously this was a silent no-op.)\n if (typeof console !== 'undefined' && console.warn) {\n console.warn(\n '[Active Reach WebPush] Push lifecycle tracking is DISABLED — ' +\n 'no eventTracker and missing writeKey/organizationId ' +\n `(hasWriteKey=${!!this.config.writeKey}, hasOrganizationId=${!!this.config.organizationId}). ` +\n 'delivered/clicked/dismissed will not be recorded.',\n );\n }\n }\n }\n\n /** Built-in fallback that POSTs push lifecycle events to\n * `/v1/push/engagement`. The gateway forwards to event-ingress with\n * the canonical `push.<event>` event_type, which the\n * event-switchboard's SDK_ENGAGEMENT_EVENT_TO_DELIVERY_MAPPING\n * dual-routes onto `aegis.delivery_events` as\n * `channel='push', status='shown'|'dismissed'|...`. Best-effort —\n * failures are warned but never thrown (push SW callbacks shouldn't\n * reject because the analytics POST failed). */\n private createBuiltinEventTracker(\n writeKey: string,\n organizationId: string,\n ): PushEventTracker {\n const apiClient = this.apiClient;\n const propertyId = this.config.propertyId;\n const resolveContactId = () => this.resolveContactId();\n return {\n track(eventName: string, properties: Record<string, unknown>): void {\n const contactId =\n (properties.contact_id as string | undefined) ?? resolveContactId();\n const body = {\n campaign_id: (properties.campaign_id as string | undefined) || '',\n message_id: (properties.message_id as string | undefined) || '',\n event_type: eventName, // e.g. 'push.delivered' — gateway tolerates either form\n user_id: (properties.user_id as string | undefined) || contactId,\n contact_id: contactId,\n anonymous_id: (properties.anonymous_id as string | undefined) || '',\n platform: (properties.platform as string | undefined) || 'web',\n property_id: propertyId,\n metadata: (properties.metadata as Record<string, unknown> | undefined) ?? {},\n };\n const headers = {\n 'X-Aegis-Write-Key': writeKey,\n 'X-Organization-ID': organizationId,\n 'X-Property-Id': propertyId,\n };\n void Promise.resolve(\n apiClient.post('/v1/push/engagement', body, headers),\n ).catch((err) => {\n if (typeof console !== 'undefined' && console.warn) {\n console.warn('[Active Reach WebPush] engagement report failed:', err);\n }\n });\n },\n };\n }\n\n // ── Public lifecycle hook API ──────────────────────────────────────────\n\n /** Generic typed subscribe — useful when the host wants to dispatch\n * multiple events through one wrapper. For single-event subscriptions\n * prefer the `on*` sugar methods below. */\n on<E extends keyof WebPushLifecycleEventMap>(\n event: E,\n handler: WebPushLifecycleEventMap[E],\n ): () => void {\n return this.register(event, handler as never);\n }\n\n /** Subscribe to push notification taps. CANCELLABLE — return `false`\n * (or call `evt.preventDefault()`) to suppress the SDK's default\n * `window.location.href = action_url` fallback. Use this to wire a\n * SPA router into the typed `action_type` payload (e.g. open the\n * cart drawer in-place instead of a full reload). */\n onPushTap(\n handler: (evt: PushTapEvent) => void | false,\n ): () => void {\n return this.register('push-tap', handler);\n }\n\n /** Subscribe to push impressions — fires after the OS notification\n * has actually rendered. `degraded=true` when the SW fell back to\n * the minimal `{title, body}` path (broken icon, etc.). */\n onPushShown(\n handler: (evt: PushShownEvent) => void,\n ): () => void {\n return this.register('push-shown', handler);\n }\n\n /** Subscribe to push dismissals (notification closed without a tap). */\n onPushDismiss(\n handler: (evt: PushDismissEvent) => void,\n ): () => void {\n return this.register('push-dismissed', handler);\n }\n\n /** Subscribe to non-fatal SDK errors. Wire into Sentry / Datadog. */\n onError(\n handler: (evt: PushErrorEvent) => void,\n ): () => void {\n return this.register('error', handler);\n }\n\n private register<T>(\n eventName: string,\n handler: (payload: T) => void | false | undefined,\n ): () => void {\n if (!this.hooks.has(eventName)) this.hooks.set(eventName, new Set());\n this.hooks.get(eventName)!.add(handler as (...args: unknown[]) => void | false | undefined);\n return () => {\n this.hooks.get(eventName)?.delete(handler as (...args: unknown[]) => void | false | undefined);\n };\n }\n\n private emit<T>(eventName: string, payload: T): boolean {\n const handlers = this.hooks.get(eventName);\n if (!handlers || handlers.size === 0) return true;\n let proceed = true;\n for (const handler of handlers) {\n try {\n const result = (handler as (p: T) => void | false | undefined)(payload);\n if (result === false) proceed = false;\n } catch (err) {\n this.emitError(err, { event: eventName });\n }\n }\n return proceed;\n }\n\n private emitError(err: unknown, context?: Record<string, unknown>): void {\n const handlers = this.hooks.get('error');\n if (!handlers || handlers.size === 0) {\n console.warn(\n '[Active Reach WebPush] error:',\n err instanceof Error ? err.message : String(err),\n context ?? {}\n );\n return;\n }\n for (const handler of handlers) {\n try {\n (handler as (e: PushErrorEvent) => void)({\n message: err instanceof Error ? err.message : String(err),\n error: err,\n context: context ?? {},\n });\n } catch {\n // already in error path — don't loop\n }\n }\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n if (!('serviceWorker' in navigator) || !('PushManager' in window)) {\n console.warn('[Active Reach WebPush] Push notifications not supported in this browser');\n return;\n }\n\n try {\n this.swRegistration = await navigator.serviceWorker.register(\n this.config.serviceWorkerPath!,\n { scope: this.config.serviceWorkerScope }\n );\n await navigator.serviceWorker.ready;\n this.initialized = true;\n\n // Listen for messages from the service worker (click/dismiss tracking,\n // and pushsubscriptionchange relay — see handleSWMessage).\n navigator.serviceWorker.addEventListener('message', (event) => {\n this.handleSWMessage(event.data);\n });\n\n // Heal endpoint drift on every page load.\n //\n // Background: when the SW updates with skipWaiting+clients.claim\n // (added in 1.7.3), some browsers — Samsung Internet most\n // aggressively, occasionally Chrome too — silently regenerate the\n // push subscription mid-flight. The SW catches the\n // pushsubscriptionchange event and resubscribes locally, but\n // historically the new endpoint never made it back to the\n // backend. Cell-plane kept FCM-pushing to the dead endpoint;\n // FCM 201s on receipt regardless of whether the token is alive,\n // so the silent failure was invisible until users complained.\n //\n // Fix: whenever permission is already granted at init time,\n // re-run subscribeToPush. The pushManager.subscribe() call is\n // idempotent — it returns the existing subscription if the\n // endpoint is still valid, otherwise mints a new one — and we\n // POST the result to /v1/web_push/subscriptions which UPSERTs\n // on (property_id, device_fingerprint). One extra round-trip\n // per page load; cheap, deterministic, never need to debug\n // stale-endpoint mysteries again.\n if (typeof window !== 'undefined' && Notification.permission === 'granted') {\n this.subscribeToPush().catch((err) => {\n console.warn('[Active Reach WebPush] auto-resync subscribeToPush failed:', err);\n });\n // Health heartbeat: long-lived tabs (kiosks, dashboards) can run\n // for days. Re-run the idempotent resync every 24h so a silently\n // dropped endpoint is detected + re-registered without a reload.\n if (!this.heartbeatTimer) {\n this.heartbeatTimer = setInterval(() => {\n if (Notification.permission === 'granted') {\n this.subscribeToPush().catch((err) => {\n console.warn('[Active Reach WebPush] heartbeat resync failed:', err);\n });\n }\n }, HEARTBEAT_INTERVAL_MS);\n }\n }\n\n if (this.config.autoPrompt) {\n setTimeout(() => this.requestPermission(), this.config.promptDelay);\n }\n } catch (err) {\n console.error('[Active Reach WebPush] Service worker registration failed:', err);\n }\n }\n\n async requestPermission(): Promise<boolean> {\n const permission = await Notification.requestPermission();\n\n if (permission === 'granted') {\n await this.subscribeToPush();\n return true;\n }\n\n this.trackPushEvent('push.permission_denied', {});\n return false;\n }\n\n async identify(params: ContactIdentity): Promise<void> {\n // Persist the resolved contact_id so getStableFingerprint /\n // resolveContactId see it on the next subscribe call. Then re-run\n // the subscribe flow — the backend UPSERT on\n // (property_id, device_fingerprint) updates the existing row with\n // the new contact_id in place.\n try {\n if (params.contactId) {\n window.localStorage.setItem('aegis_contact_id', params.contactId);\n }\n } catch {\n // storage blocked — proceed anyway; subscribe() still works\n }\n\n const subscription = await this.swRegistration?.pushManager.getSubscription();\n if (!subscription) return;\n\n const subscriptionJSON = subscription.toJSON();\n const fingerprint = await this.getStableFingerprint();\n await this.retryApiCall(() =>\n this.apiClient.post(\n '/v1/web_push/subscriptions',\n {\n contact_id: params.contactId ?? this.resolveContactId(),\n endpoint: subscriptionJSON.endpoint!,\n p256dh: subscriptionJSON.keys!.p256dh!,\n auth: subscriptionJSON.keys!.auth!,\n device_fingerprint: fingerprint,\n user_agent: navigator.userAgent,\n application_server_key: this.encodeAppServerKey(subscription),\n },\n { 'X-Property-Id': this.config.propertyId }\n )\n );\n }\n\n /**\n * Log out: detach THIS device from the contact. Not a consent statement —\n * the contact's push opt-in / opt-out is left exactly as it was (Aman\n * 2026-09-25), so it calls `/v1/web_push/unbind`, never `/unsubscribe`.\n */\n async logout(): Promise<void> {\n const subscription = await this.swRegistration?.pushManager.getSubscription();\n if (!subscription) return;\n const subscriptionJSON = subscription.toJSON();\n const fingerprint = await this.getStableFingerprint();\n try {\n await this.apiClient.post(\n '/v1/web_push/unbind',\n {\n contact_id: this.resolveContactId(),\n device_fingerprint: fingerprint,\n endpoint: subscriptionJSON.endpoint!,\n },\n { 'X-Property-Id': this.config.propertyId }\n );\n } finally {\n try {\n window.localStorage.removeItem('aegis_contact_id');\n } catch {\n // ignore\n }\n }\n }\n\n /**\n * Turn push back ON for a customer who opted out — only from a VERIFIED\n * storefront session (the `session_code` the store's phone-OTP sign-in\n * minted). The server resolves the customer from that session; this SDK\n * never names who is opted in, so a page cannot opt anyone else in. A\n * registration alone never re-enables push after an opt-out.\n */\n async optIn(params: { slug: string; sessionCode: string }): Promise<void> {\n await this.apiClient.post(\n '/v1/web_push/opt-in',\n { slug: params.slug, session_code: params.sessionCode },\n { 'X-Property-Id': this.config.propertyId }\n );\n }\n\n // ---------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------\n\n private async subscribeToPush(): Promise<void> {\n if (!this.swRegistration) return;\n\n const desiredKey = this.urlBase64ToUint8Array(this.config.vapidPublicKey);\n\n // VAPID-drift heal (2026-07-21): a subscription minted under an older\n // keypair (pre-rotation, or an earlier surface) survives in the\n // browser; re-POSTing it upserts a row the backend then signs with\n // the CURRENT key and FCM 403s (\"credentials do not correspond to\n // the credentials used to create the subscription\"). If the existing\n // subscription's applicationServerKey differs from the config key,\n // drop it and mint a fresh one.\n try {\n const existing = await this.swRegistration.pushManager.getSubscription();\n const existingKey = existing?.options?.applicationServerKey;\n if (existing && existingKey) {\n const a = new Uint8Array(existingKey as ArrayBuffer);\n const matches =\n a.length === desiredKey.length &&\n a.every((b, i) => b === desiredKey[i]);\n if (!matches) {\n await existing.unsubscribe();\n this.trackPushEvent('push.resubscribed', { reason: 'vapid_key_drift' });\n }\n }\n } catch {\n // getSubscription/unsubscribe failure — fall through; subscribe()\n // below will surface a real error if the state is unusable.\n }\n\n const subscription = await this.swRegistration.pushManager.subscribe({\n userVisibleOnly: true,\n // An ArrayBuffer holding exactly the key bytes: TypeScript >= 5.7\n // types `Uint8Array` as possibly SharedArrayBuffer-backed, which\n // `applicationServerKey` (BufferSource) does not accept.\n applicationServerKey: this.toArrayBuffer(desiredKey),\n });\n\n const subscriptionJSON = subscription.toJSON();\n const fingerprint = await this.getStableFingerprint();\n const contactId = this.resolveContactId();\n\n await this.retryApiCall(() =>\n this.apiClient.post(\n '/v1/web_push/subscriptions',\n {\n contact_id: contactId,\n endpoint: subscriptionJSON.endpoint!,\n p256dh: subscriptionJSON.keys!.p256dh!,\n auth: subscriptionJSON.keys!.auth!,\n device_fingerprint: fingerprint,\n user_agent: navigator.userAgent,\n application_server_key: this.encodeAppServerKey(subscription),\n },\n { 'X-Property-Id': this.config.propertyId }\n )\n );\n\n this.trackPushEvent('push.subscribed', {\n browser: this.getBrowserInfo(),\n property_id: this.config.propertyId,\n });\n }\n\n /**\n * Stable SHA-256 device fingerprint over (first_party_cookie_id + UA stable\n * parts). Used as the UPSERT key on `(property_id, device_fingerprint)` —\n * re-subscribes after VAPID rotation or permission reset update the same\n * row instead of leaving dangling duplicates.\n */\n private async getStableFingerprint(): Promise<string> {\n const cookieId = this.config.firstPartyCookieId;\n const ua = navigator.userAgent || '';\n const platform = navigator.platform || '';\n const language = navigator.language || '';\n const input = `${cookieId}|${ua}|${platform}|${language}`;\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n const bytes = new TextEncoder().encode(input);\n const hash = await crypto.subtle.digest('SHA-256', bytes);\n return Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n }\n // Fallback only reachable in environments without Web Crypto —\n // push-capable browsers all have it, so this is defensive.\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;\n }\n return `fnv-${h.toString(16)}`;\n }\n\n /**\n * Resolve the current contact_id. AegisWebPush doesn't own identity, so\n * we look in a few well-known localStorage keys that the identity layer\n * of the SDK writes. If no known contact is set, we use the first-party\n * cookie id — the backend upgrades the row when identify() is called.\n */\n private resolveContactId(): string {\n try {\n const fromStorage =\n window.localStorage.getItem('aegis_contact_id') ||\n window.localStorage.getItem('aegis_user_id');\n if (fromStorage) {\n // Host apps (ACS storefront) write these keys with a TTL\n // envelope: {\"value\":\"ctk_…\",\"expiry\":123}. Using the raw\n // string bound subscriptions to a JSON blob instead of the\n // contact id (found live 2026-07-21, subscription row 12) —\n // unwrap before use.\n if (fromStorage.startsWith('{')) {\n try {\n const parsed = JSON.parse(fromStorage) as { value?: unknown };\n if (typeof parsed.value === 'string' && parsed.value) {\n return parsed.value;\n }\n } catch {\n // not JSON after all — use as-is\n }\n }\n return fromStorage;\n }\n } catch {\n // storage blocked — fall through\n }\n return this.config.firstPartyCookieId;\n }\n\n /**\n * Retry an API call with exponential backoff (max 3 retries: 1s, 2s, 4s).\n */\n private async retryApiCall(fn: () => Promise<unknown>, maxRetries = 3): Promise<void> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n await fn();\n return;\n } catch (err) {\n if (attempt === maxRetries) {\n console.error('[Active Reach WebPush] API call failed after retries:', err);\n return;\n }\n const delay = 1000 * Math.pow(2, attempt);\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n }\n\n /**\n * Handle messages forwarded from the service worker (push click, dismiss).\n */\n private handleSWMessage(data: Record<string, unknown>): void {\n if (!data || typeof data.type !== 'string') return;\n\n switch (data.type) {\n case 'push.clicked': {\n this.trackPushEvent('push.clicked', {\n campaign_id: data.campaign_id,\n // Attribution echoed by the SW → forwarded to the engagement\n // event so it rolls up per-journey / per-template / per-outlet.\n journey_execution_id: data.journey_execution_id,\n lifecycle_fork_id: data.lifecycle_fork_id,\n location_id: data.location_id,\n action_url: data.action_url,\n action_type: data.action_type,\n // Button-level click attribution (delivery_events.button_*).\n button_id: data.button_id,\n button_label: data.button_label,\n });\n this.dispatchPushTap(data);\n break;\n }\n case 'push.dismissed':\n this.trackPushEvent('push.dismissed', {\n campaign_id: data.campaign_id,\n journey_execution_id: data.journey_execution_id,\n lifecycle_fork_id: data.lifecycle_fork_id,\n location_id: data.location_id,\n });\n this.emit('push-dismissed', {\n campaign_id: data.campaign_id as string | undefined,\n });\n break;\n case 'push.delivered':\n this.trackPushEvent('push.delivered', {\n campaign_id: data.campaign_id,\n journey_execution_id: data.journey_execution_id,\n lifecycle_fork_id: data.lifecycle_fork_id,\n location_id: data.location_id,\n });\n this.emit('push-shown', {\n campaign_id: data.campaign_id as string | undefined,\n degraded: data.degraded === true ? true : undefined,\n });\n break;\n case 'push.resubscribed':\n // SW caught a pushsubscriptionchange event and minted a new\n // push subscription. The browser side now has a fresh\n // endpoint; cell-plane still has the old one. Push the\n // updated payload up so the UPSERT keeps the row alive.\n // Without this, FCM pushes go to a dead endpoint with no\n // visible failure (FCM 201s regardless of token validity).\n void this.persistResubscription(\n data.subscription as PushSubscriptionJSON | undefined\n );\n break;\n }\n }\n\n /**\n * Translate a SW `push.clicked` postMessage into a typed PushTapEvent,\n * dispatch through the lifecycle hook bus, and fall back to a hard\n * navigation if no host handler claimed it.\n *\n * The fallback path matters: if a tenant ships a push template with\n * an unknown `action_type` (no SPA handler registered) OR pushes the\n * SDK before the host wires up its `onPushTap` subscriber, we still\n * want the user to land somewhere — `action_url` is always set by the\n * SW's notificationclick handler (it falls back to '/').\n */\n private dispatchPushTap(data: Record<string, unknown>): void {\n if (typeof window === 'undefined') return;\n const actionUrl = (data.action_url as string) || '/';\n let defaultPrevented = false;\n const evt: PushTapEvent = {\n action_type: data.action_type as string | undefined,\n action_payload: data.action_payload as Record<string, unknown> | undefined,\n action_url: actionUrl,\n action: (data.action as string) || 'default',\n campaign_id: data.campaign_id as string | undefined,\n preventDefault: () => {\n defaultPrevented = true;\n evt.defaultPrevented = true;\n },\n defaultPrevented: false,\n };\n const proceed = this.emit('push-tap', evt);\n if (proceed && !defaultPrevented) {\n // No subscriber claimed this tap (or the subscriber returned\n // void / didn't preventDefault). Default to a hard nav so the\n // user always lands somewhere actionable. SPAs that want\n // in-place handling MUST return false from their handler.\n try {\n window.location.href = actionUrl;\n } catch {\n // navigation blocked (sandboxed iframe, etc.) — host can\n // still handle via the hook\n }\n }\n }\n\n private async persistResubscription(\n subscription: PushSubscriptionJSON | undefined\n ): Promise<void> {\n if (!subscription || !subscription.endpoint || !subscription.keys) return;\n try {\n const fingerprint = await this.getStableFingerprint();\n await this.retryApiCall(() =>\n this.apiClient.post(\n '/v1/web_push/subscriptions',\n {\n contact_id: this.resolveContactId(),\n endpoint: subscription.endpoint!,\n p256dh: subscription.keys!.p256dh!,\n auth: subscription.keys!.auth!,\n device_fingerprint: fingerprint,\n user_agent: navigator.userAgent,\n },\n { 'X-Property-Id': this.config.propertyId }\n )\n );\n this.trackPushEvent('push.resubscribed', {});\n } catch (err) {\n console.warn('[Active Reach WebPush] persistResubscription failed:', err);\n }\n }\n\n private trackPushEvent(eventName: string, properties: Record<string, unknown>): void {\n this.eventTracker?.track(eventName, {\n ...properties,\n platform: 'web',\n browser: this.getBrowserInfo(),\n });\n }\n\n private getBrowserInfo(): string {\n const ua = navigator.userAgent;\n if (ua.includes('Edg')) return 'edge';\n if (ua.includes('Chrome')) return 'chrome';\n if (ua.includes('Firefox')) return 'firefox';\n if (ua.includes('Safari')) return 'safari';\n return 'unknown';\n }\n\n /** base64url-encode the subscription's applicationServerKey for the\n * register POST's diagnostic echo (wrong-key mint detection). */\n private encodeAppServerKey(sub: PushSubscription): string | undefined {\n try {\n const k = sub.options?.applicationServerKey;\n if (!k) return undefined;\n const bytes = new Uint8Array(k as ArrayBuffer);\n let bin = '';\n for (const b of bytes) bin += String.fromCharCode(b);\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n } catch {\n return undefined;\n }\n }\n\n private urlBase64ToUint8Array(base64String: string): Uint8Array {\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');\n const rawData = atob(base64);\n const arr = new Uint8Array(rawData.length);\n for (let i = 0; i < rawData.length; ++i) {\n arr[i] = rawData.charCodeAt(i);\n }\n return arr;\n }\n\n /** A fresh ArrayBuffer with exactly these bytes (never a view's larger or\n * shared backing buffer). */\n private toArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n const buf = new ArrayBuffer(bytes.length);\n new Uint8Array(buf).set(bytes);\n return buf;\n }\n}\n"],"names":["DEFAULT_CONFIG","workspace_id","batch_size","batch_interval","capture_utm","capture_referrer","auto_page_view","session_timeout","debug","respect_dnt","cross_domain_tracking","secure_cookie","enable_offline_mode","max_offline_events","retry_failed_requests","max_retries","retry_backoff_multiplier","request_timeout","rate_limit_burst","rate_limit_per_second","auto_region_detection","wait_for_consent","enable_consent_mode","integrate_onetrust","integrate_cookiebot","integrate_google_consent_mode","default_consent","analytics","marketing","functional","initialized","api_host","cell_endpoints","preferred_region","cookie_domain","plugins","active_cell","generateUUID","crypto","randomUUID","replace","c","r","Math","random","toString","generateMessageId","Date","now","slice","logger","constructor","this","enabled","prefix","enable","disable","isEnabled","message","args","info","console","warn","error","Identity","storage","userId","traits","anonymousId","getOrCreateAnonymousId","loadUserIdentity","id","get","set","storedUserId","storedTraits","JSON","parse","getAnonymousId","setUserId","setTraits","getUserId","stringify","getTraits","reset","remove","alias","newUserId","previousId","getState","SessionManager","sessionTimeoutMinutes","eventCount","checkInterval","activityThrottle","lastActivityUpdate","adClickIDs","linkAttribution","sessionTimeout","lastActivityTime","sessionStartTime","session","loadSession","sessionId","startTime","landingPage","createNewSession","startActivityTracking","startExpiryCheck","sessionData","newSessionId","persistSession","activityHandler","onActivity","forEach","event","window","addEventListener","passive","document","visibilityState","setInterval","checkSessionExpiry","getSessionId","incrementEventCount","getSessionDuration","getEventCount","setAdClickIDs","Object","keys","length","getAdClickIDs","durable","parsed","getLandingPage","setLinkAttribution","attribution","aegis_code","getLinkAttribution","destroy","clearInterval","FBP_COOKIE","FBC_COOKIE","readCookie","name","target","parts","cookie","split","i","charAt","substring","indexOf","decodeURIComponent","writeCookie","value","days","expiry","toUTCString","domain","host","location","hostname","test","join","getRootDomain","isHttps","protocol","cookieStr","encodeURIComponent","subdomainIndex","max","ensureFbp","existing","fbp","floor","padStart","ensureFbc","fbclid","readFbclidFromUrl","fbc","URLSearchParams","search","redactMetaCookies","props","properties","masked","EventQueue","config","transport","buffer","flushInterval","isFlushing","offlineQueue","isOnline","navigator","onLine","enableOfflineMode","loadOfflineQueue","setupOnlineListener","startFlushTimer","setupUnloadHandlers","offlineData","events","Array","isArray","maxOfflineEvents","flushOfflineQueue","saveOfflineQueue","data","flush","send","success","push","type","batchSize","addToOfflineQueue","shift","result","unshift","batchInterval","flushBeforeUnload","persistBufferToOfflineQueue","getQueueSize","getOfflineQueueSize","clear","Transport","activeCell","healthCheckInterval","regionLatencyMap","Map","cellEndpoints","initializeCellularArchitecture","cachedCell","loadCachedCell","isCellHealthy","selectOptimalCell","startHealthChecks","cached","saveCachedCell","cell","healthy","some","region","url","preferredRegion","preferredCell","find","autoRegionDetection","detectOptimalRegion","selectCellByPriority","healthyCells","filter","latencyPromises","map","async","latency","measureLatency","results","Promise","all","sort","a","b","optimalCell","performance","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","method","signal","clearTimeout","ok","Infinity","sortedCells","priority","performHealthChecks","healthPromises","isHealthy","checkCellHealth","endpoint","getActiveEndpoint","payload","buildPayload","sendBeacon","sendViaBeacon","resolve","sendViaFetch","apiHost","Error","batch","sentAt","toISOString","writeKey","context","blob","Blob","attempt","requestTimeout","headers","Authorization","body","keepalive","json","errorText","text","status","statusText","shouldRetry","retryRequest","statusCode","retryFailedRequests","maxRetries","delay","pow","retryBackoffMultiplier","getActiveCell","getRegionLatencies","STORAGE_PREFIX","Storage","options","useLocalStorage","isLocalStorageAvailable","localStorage","setItem","removeItem","key","expiryDays","fullKey","item","setCookie","itemStr","getItem","getCookie","deleteCookie","startsWith","trim","date","setTime","getTime","secureCookie","cookieDomain","crossDomain","nameEQ","ca","CREDENTIAL_PARAM_PATTERN","RegExp","redactUrl","LINK_ATTRIBUTION_KEYS","buildContext","utm","searchParams","URL","parseUTMParameters","device","ua","userAgent","detectDevice","os","version","match","detectOS","browser","detectBrowser","network","connection","mozConnection","webkitConnection","effectiveType","downlink","rtt","detectNetworkInfo","storedLandingPage","library","page","path","pathname","referrer","title","href","hash","locale","language","timezone","Intl","DateTimeFormat","resolvedOptions","timeZone","screen","width","height","density","devicePixelRatio","viewport","innerWidth","innerHeight","campaign","PluginRegistry","register","plugin","has","unregister","pluginName","delete","getAll","from","values","executeHook","hookName","hook","apply","String","onError","onErrorError","executeHookChain","initialValue","additionalArgs","executeHookParallel","promises","promise","ConsentManager","listeners","defaultConsent","waitForConsent","consentStorageKey","preferences","loadPreferences","stored","mergeWithDefaults","getDefaultPreferences","necessary","savePreferences","setConsent","updated","notifyListeners","grantAll","denyAll","hasConsent","category","getPreferences","getStatus","isAnalyticsEnabled","isMarketingEnabled","onChange","callback","index","splice","listener","integrateOneTrust","OneTrust","syncWithOneTrust","activeGroups","OnetrustActiveGroups","includes","OptanonWrapper","originalWrapper","integrateCookiebot","cookiebot","Cookiebot","syncWithCookiebot","consent","statistics","integrateGoogleConsentMode","w","gtag","dataLayer","_a","updateGoogleConsent","ad_storage","analytics_storage","ad_user_data","ad_personalization","functionality_storage","personalization_storage","security_storage","sumItems","products","reduce","sum","p","quantity","mapProducts","product_id","sku","price","currency","brand","variant_id","variant_label","position","EcommerceTracker","aegis","_cart","total","itemCount","_cartSink","onCartChange","sink","_emitCart","call","productViewed","product","track","image_url","productListViewed","list","list_id","list_name","items","productClicked","source","section","productImpressed","categoryFiltered","previous_category","result_count","searchPerformed","query","results_count","filters","addToCart","qty","removeFromCart","cartViewed","cart","cart_id","num_items","checkoutStarted","checkout","checkout_id","coupon","shipping","tax","checkoutStep","step","checkoutOption","option","orderCompleted","order","order_id","revenue","discount","payment_method","orderRefunded","orderId","orderShipped","tracking","carrier","tracking_code","tracking_url","couponApplied","couponRemoved","couponDenied","reason","wishlistItemAdded","wishlist","wishlist_id","wishlist_name","productRemovedFromWishlist","productWaitlisted","waitlist","channels","promotionViewed","promo","promotionClicked","RateLimiter","droppedThisWindow","firstDroppedName","windowFlushTimer","capacity","burstCapacity","refillPerSecond","windowMs","dropCoalesceWindowMs","onDropBatch","tokens","lastRefillMs","tryConsume","eventName","refill","flushWindow","getAvailableTokens","elapsedSec","newTokens","min","err","C1","C2","murmurhash3_x86_32","input","seed","bytes","len","nBlocks","h1","offset","k1","imul","tailStart","tailLen","murmurhash3_bytes","TextEncoder","encode","BloomFilter","buf","params","m","mask","fromBase64","bloomB64","b64","atob","bin","out","Uint8Array","charCodeAt","B","globalThis","Buffer","base64ToBytes","seedA","h2","seedB","k","idx","NameGovernor","bloom","remainingNewNames","graceActive","localNovelNames","Set","droppedSinceLastReport","reportWindowStart","hasWarnedThisSession","ingestHint","hint","bloom_algo","bloom_b64","seed_a","seed_b","remaining_new_names","grace_active","shouldSend","add","prev","drainDropReport","size","count","since","_debugState","hasBloom","remaining","localNovel","RESERVED_PREFIXES","CAMEL_BOUNDARY_1","CAMEL_BOUNDARY_2","NON_SNAKE","DUPE_UNDERSCORE","ISO_8601","DATE_KEY_HINTS","normalizeKey","toLowerCase","startsWithReserved","lower","looksLikeDateKey","part","parseDateValue","Number","isFinite","s","ms","isNaN","asNum","_TraitGovernor","warnCounts","process","sanitized","drops","rawKey","originalKey","verdict","normalizedKey","drop","maybeWarn","ws","perVerdict","WARN_CAP","TraitGovernor","VALID_CHANNELS","EMAIL_RE","PHONE_RE","SHA256_HEX_RE","ISO_DATE_RE","UserNamespace","pendingTraits","login","merged","identify","logout","setAttribute","writeTraits","setAttributes","setEmail","email","setPhone","phone","setHashedEmail","sha256Hex","email_sha256","setHashedPhone","phone_sha256","setBirthDate","iso","birth_date","setOptIn","channel","granted","setSecureToken","token","_secure_token","_getPendingTraits","assign","Aegis","queue","initPromise","_ecommerce","_user","rateLimiter","nameGovernor","traitGovernor","_lastPageUrl","_popstateHandler","_originalPushState","_originalReplaceState","_lastEventIds","_workspaceCodes","_runtimeWorkspace","_runtimeLocation","init","_init","shouldRespectDNT","pattern","isBot","buildConfig","identity","captureAdClickIDsOnInit","write_key","sampleName","emitRateLimitMeta","initializePlugins","startSPATracking","onRouteChange","currentUrl","history","pushState","bind","replaceState","stopSPATracking","removeEventListener","dnt","doNotTrack","msDoNotTrack","parseAdClickIDs","hasAdClickIDs","parseLinkAttribution","use","catch","removePlugin","ingestWorkspaceCodes","codes","setWorkspace","codeOrId","sessionStorage","clearWorkspace","ingestLocationCodes","setLocation","clearLocation","getPathLocationCode","getPathWorkspaceCode","getEffectiveLocationId","configured","location_id","loc","pathLoc","segments","Boolean","first","getEffectiveWorkspaceId","pathWs","assertInitialized","messageId","timestamp","captureEvent","wsForGovernor","governedTraits","contactToken","group","groupId","trackEvent","eventLabel","ctx","utm_source","medium","utm_medium","utm_campaign","content","utm_content","term","utm_term","clicks","gclid","gbraid","wbraid","clickId","ctwa_clid","msclkid","ttclid","campaign_click_id","link","entries","meta","transformedEvent","sampleEventName","dropped_count","sample_event_name","burst_capacity","refill_per_second","_b","ingestGovernanceHint","emitGovernanceDropMeta","report","distinct_names","sample_names","window_start","window_end","setContactToken","getRewards","contactId","orgId","organization_id","resp","credentials","lastEventId","setCell","cellEndpoint","existingCell","getCellInfo","latencies","grantConsent","denyConsent","getConsentPreferences","onConsentChange","ecommerce","user","PDP_SKU_RE","productPath","toUpperCase","ICONS","x","d","check","cx","cy","x1","x2","y1","y2","clock","points","play","pause","y","rx","square","truck","gift","tag","fill","percent","star","sparkles","zap","heart","bell","lock","ry","package","flame","crown","ticket","wallet","coins","trophy","timer","plus","store","rocket","escapeAttr","SVG_NS","lucideSvgEl","opts","node","svg","createElementNS","color","strokeWidth","className","ariaLabel","attrs","child","v","appendChild","lucideSvg","a11y","cls","children","IN_APP_FONT_STACK","ensureInAppFont","getElementById","createElement","rel","head","npsBandFromScore","score","computeProgress","threshold","current","pct","complete","milestonesReached","WEIGHT","regular","semibold","bold","ICON_PX","sm","md","lg","TEXT_PX","interpolateTokens","getCartState","_m","ASPECT","DEFAULT_CARD_TEMPLATE","frame","bg","border","radius","elements","aspect","variant","lines","overlay","asLook","resolveGlobalTemplate","ic","t","card_template","legacy","card_style","look","badge","badge_color","hide_price","hide_rating","hide_cta","cta_text","background","border_color","accent","templateFromCardStyle","paintPrice","card","el","metadata","priceText","compareAt","compare_at_price","compare_at","original_price","row","style","cssText","textContent","pillBg","sanitizeColor","pNum","parseFloat","cNum","NaN","orig","off","round","paintRating","avg","avg_rating","review_count","rr","val","toFixed","buildCountdownElement","fg","muted","targetStr","countdown_target","wrap","label","countdown_label","time","started","tick","isConnected","cd","targetMs","nowMs","totalMs","expired","totalSec","hours","minutes","seconds","pad","n","display","computeCountdown","appendRatingThanks","isHigh","appendThanks","rating_high_url","sanitizeUrl","cta","rating_high_cta","element","submitResponse","navigate","renderRatingFollowupStep","stars","firstChild","removeChild","heading","rating_followup_prompt","ta","rows","placeholder","done","finish","withComment","comment","playReaction","btnRow","skip","focus","buildRatingInputElement","background_color","q","scale","rating_scale","iconName","rating_icon","iconWord","submitted","paint","upto","on","btn","lowThreshold","rating_followup_threshold","ceil","rating_followup_enabled","buildProductRatingListElement","slot","slotEl","getAttribute","billCode","reviewsViaPage","reviewsUrl","showThumb","show_thumbnail","rating_layout","scores","itemNames","allowText","allow_review_text","canUpload","organizationId","allowMedia","allow_review_media","reviewText","mediaUrls","uploadOne","file","reject","FileReader","onload","onerror","readAsDataURL","fileToB64","res","content_type","j","submitBtn","branch","innerHTML","box","detractor_headline","prompt","detractor_prompt","selected","chipLabels","detractor_chips","chipRow","sendLabel","detractor_send_label","refreshSend","opacity","pointerEvents","chip","note","sent","detail","chips","bill_code","then","detractor_thanks","renderDetractorForm","ack","thanks_text","submitting","rated","disabled","pid","product_name","review_text","media_urls","rating_result","render","clean","it","hideCard","tile","closest","requestAnimationFrame","rowEl","img","src","alt","loading","mono","ratingThumb","nameEl","onPick","picked","starRow","refreshSubmit","toggle","panel","review_earn_hint","wsQs","workspaceId","points_name","base","photo_bonus","video_bonus","strip","accept","multiple","addTile","click","files","f","thumb","insertBefore","up","vid","buildComposer","rawAttr","loader","ac","fallback","msg","buildPollElement","poll_options","o","querySelectorAll","option_index","option_label","renderNpsReasonStep","promoterThreshold","nps_promoter_threshold","band","nps_follow_up_promoter","nps_follow_up_detractor","nps_follow_up_passive","nps_reason_chips","isPromoter","nps_promoter_url","nps_promoter_cta","appendNpsThanks","buildNpsElement","npsPayload","nps_followup_enabled","lo","hi","buildProgressElement","goal","goal_type","moneyGoal","sym","currency_symbol","code","INR","USD","EUR","GBP","readCur","fromCart","goalType","readGoalFromCart","sub","shell","bar","firedUnlock","seenIncomplete","update","th","threshold_source","freeDeliveryAbove","cur","labeled","rewardText","currencySymbol","pctStr","showPct","cardProgressSubLabel","reward_text","show_pct","fontWeight","stepId","celebrate_on_complete","cartItems","once","buildCtaAction","inline","kind","cta_action","safe","u","cta_url","productId","platformProductId","platform_product_id","platformUrl","platform_url","platformVariantId","platform_variant_id","cta_custom_name","imageUrl","renderCardFromTemplate","action","layout","layoutAccent","tpl","e","badgeEl","applyPerCardOverride","template","raw","h","parseInt","g","contrastFg","cardFont","ff","fontFamily","fLayout","dir","tileGap","gap","sizing","shadow","max_width","min_width","min_height","imageWrap","paintInto","container","els","safeVideo","video_url","loop","autoplay","playsInline","align","cat","px","iconColor","bubble","bubbleTint","icon_name","fs","weight","sp","gdir","overlayOnImage","stopPropagation","dispatchAction","bm","act","celebrate_on_show","reaction","celebrate_reaction","cscope","celebrate_scope","celebrate_lottie","DISMISS_STORAGE_PREFIX","buildProgressBar","log","addAnimationStyles","interactive_config","progress_goal_type","progress_threshold","progress_reward_text","progress_source","accentVar","branded","surface_style","text_color","progress_fill_color","leading","numeric","fillEl","shimmer","footnote","lastFiredUnlock","milestonesFired","fireMilestone","__aegisProgressSSE","rc","win","Shopify","total_price","aegis_cart","cart_items","cart_total","cacheStr","_c","subtotalAmount","readCurrentValue","pollTimer","cleanup","RESUME_PREFIX","writeResumeIdx","resumeKey","renderCoachmarkTour","resume_key","steps","allowSkip","allow_skip","showDots","show_progress_dots","spotlight","highlight_style","spotPad","spotlight_padding","spotRadius","spotlight_shape","spotDim","spotlight_dim","dimMode","spotlight_dim_color","dimBase","spotlight_dim_custom","dimAlpha","dimColor","readResumeIdx","pointerEl","tipEl","highlightEl","cleanupOne","lastStepIdx","skipped","showStep","selector","anchor_web","anchor","querySelector","anchorUnresolved","anchor_resolved","anchor_selector","widget_type","anchorRect","getBoundingClientRect","top","left","placement","titleEl","bodyEl","controls","dots","_","buttons","back","next","isLast","tipRect","bottom","right","aegisResetTour","clearResume","FRAME_GRADIENTS","frameGradient","sheenRing","sheenRingLinear","readConfig","groups","frames","normalizeGroups","stories","ringStyle","stories_ring_style","ringBg","ringBgLinear","stories_ring_color","thumbnailShape","stories_thumbnail_shape","stories_autoplay","stories_loop","showProgress","stories_show_progress","defaultDuration","stories_default_duration_ms","reduceMotion","matchMedia","matches","renderStoriesRings","cfg","injectStoryStyles","floating","labelColor","surfaceBg","rectangle","cover","cover_image_url","firstWithImg","findIndex","groupCover","safeImg","grad","cardWrap","scrim","openViewer","ringBox","ring","initial","buildGroupBubble","dismiss","renderLauncherBar","startGroupIndex","startFrameIndex","gIdx","stage","frameLayer","progressRow","bars","buildBars","replaceChildren","close","rafId","frameStart","elapsedBeforePause","paused","currentVideo","seen","clearTimers","cancelAnimationFrame","completionFired","teardown","onKey","frameDuration","duration_ms","tickProgress","dur","elapsed","scheduleAdvance","advance","show","active","safeImage","bgFill","copy","ctaUrl","ctaLabel","renderFrame","loadGroup","gi","startFrame","resume","pointerDownAt","pointerX","clientX","rect","earnPct","num","str","arr","openGameModal","maxWidth","injectGameStyles","brandAccent","removed","animation","closeIcon","ctaButton","onClick","action_url","gEl","css","fillBar","_gameAudioCtx","playStampThunk","Ctx","AudioContext","webkitAudioContext","state","currentTime","osc","createOscillator","gain","createGain","frequency","setValueAtTime","exponentialRampToValueAtTime","connect","destination","start","stop","GAME_RENDERERS","streak","streak_count","streak_goal","reward","streak_reward_text","createTextNode","hasUrl","streak_cta_text","haptic","milestone","unit","milestone_unit","milestone_current","milestone_target","milestone_steps","st","milestone_cta_text","challenge","tasks","challenge_tasks","challenge_deadline_text","challenge_bonus_text","challenge_cta_text","achievements","achievements_items","got","unlocked","grid","unlockSeen","icon","achievements_next_text","button_text","leaderboard","leaderboard_rows","medal","leaderboard_period_text","you","toLocaleString","leaderboard_gap_text","check_in","daysCfg","checkin_days","today","checkin_cta_text","day","checkin_reset","stamp_card","stamp_total","filled","stamp_filled","logoRaw","stamp_logo_url","logo","rewards","stamp_rewards","rewardAt","rewardIconAt","at","prize_value","stamp_reward","stamp_reward_text","ink","cols","logoHosts","stampOrder","pos","isReward","isFilled","transform","im","Image","nw","naturalWidth","nh","naturalHeight","master","mctx","getContext","drawImage","transparent","getImageData","globalCompositeOperation","fillStyle","fillRect","cv","nextReward","progressTxt","stamp_cta_text","stamp_just_unlocked","banner","coupon_code","mystery_box","boxes","mystery_boxes","prize","mystery_subtitle","opened","fromCharCode","submitP","box_index","won","prizeLabel","prize_label","_d","revealBox","mystery_cta_text","tier_ladder","tiers","curIdx","tier_current_index","curPts","tier_current_points","nextPts","tier_next_points","perks","tier_perks","ladder","tier","tier_cta_text","lucky_draw","draw_entries","actions","draw_actions","draw_deadline_text","draw_pool_text","draw_cta_text","slot_machine","symbols","slot_symbols","pool","rnd","machine","cabinet","reels","period","STRIP","raf","draw","startSpin","transition","residue","final","onEnd","makeSlotReel","winLine","lever","onPull","knob","pull","makeSlotLever","spin","spinning","playBtn","slot_cta_text","fresh","cloneNode","replaceWith","borderColor","boxShadow","prov","provWin","serverPrize","race","slot_win_text","stopReel","GAME_SUB_TYPES","buildVideo","videoUrl","poster","watchedFired","onProgress","duration","duration_s","pill","caption","onCard","readOpts","video_aspect","overlayCaption","video_caption","video_autoplay","video_loop","renderVideo","media","padding","HEX","cardHeadline","headline","cardSub","subhead","cardCta","cta_label","cardUrl","cta_target","renderHeroInline","cards","chrome","_injectHeroResponsiveCss","root","aspect_ratio","overlayCss","justify","text_position","slide","isVideo","media_type","mediaUrl","media_url","cta_style","dotEls","activeIdx","goto","dot","autoplay_ms","STYLE_ID","currentLauncher","AegisChat","sessionResumed","open","unread","renderedIds","pendingEcho","hasInteracted","debugMode","greeting","accentColor","surfaceStyle","logoUrl","displayMode","agentPersona","quickReplies","onSessionStart","onSessionEnd","onMessageSent","readAnonId","readStoredContactId","initialize","injectStyles","mount","scheduleAttention","updateContactId","openPanel","prefill","dismissAttention","classList","clearUnread","resumeSession","startPolling","closePanel","wasOpen","disconnectSSE","stopPolling","appendBubble","sender","role","showTyping","message_text","anonymous_id","contact_id","agent_persona","appendSystem","persistContactId","sseTicket","sse_ticket","connectSSE","refreshHistory","handleFile","fileToBase64","filename","reader","comma","EventSource","origin","eventSource","onopen","appended","messages","hideTyping","bumpUnread","scrollToBottom","bubbleIconHtml","setProperty","header","closeBtn","thread","composer","shiftKey","preventDefault","fileInput","attachBtn","sendBtn","tip","tipText","tipClose","greetingTip","appendSystemGreeting","renderQuickReplies","childElementCount","interactive","inter","renderInteractive","carousel","browse_all_url","more","scrollTop","scrollHeight","typingEl","attentionTimer","CHAT_CSS","TriggerEngine","isStarted","scrollDepthTargets","scrollDepthReached","timeOnPageTargets","exitIntentEnabled","exitIntentFired","inactivityTargets","scrollVelocityEnabled","scrollVelocityFired","scrollVelocityConfig","minScrollPosition","cooldown","lastScrollY","lastScrollTime","visibilityChangeEnabled","backButtonEnabled","backButtonFired","rageClickEnabled","rageClickConfig","rageClickBuffer","rageClickFiredInBurst","hoverEnabled","hoverStartAt","hoverLastMs","hoverThresholds","hoverThresholdsFired","hoverThresholdTimers","mouseVelEnabled","mouseVelConfig","cooldownMs","mouseSamples","mouseVelLast","mouseVelLastFiredAt","handleScroll","pageYOffset","documentElement","scrollPercent","targetDepth","emit","depth_percent","actual_percent","scroll_top","scroll_height","handleExitIntent","clientY","client_y","page_url","time_on_page","pageLoadTime","handleScrollVelocity","currentY","scrollY","timeDiff","distance","velocity","abs","scroll_velocity","scroll_distance","current_position","scrollVelocityCooldownTimer","handleVisibilityChange","hidden","handleBackButton","handleActivity","eventType","callbacks","registerScrollDepth","depthPercent","firstTarget","attachScrollListener","registerTimeOnPage","elapsedMs","timerId","registerExitIntent","attachExitIntentListener","registerInactivity","idleSeconds","idleTime","idle_seconds","actual_idle_time","registerScrollVelocity","attachScrollVelocityListener","registerVisibilityChange","registerBackButton","registerRageClick","noteClick","intent","cutoff","trimStart","recent","window_ms","getRageClickCount","live","ts","registerHoverDwell","thresholdsByIntent","thresholds","noteHoverStart","noteHoverEnd","timers","firedSet","threshold_ms","actual_ms","startAt","dwell","pending","registerMouseVelocityToTop","noteMousePosition","oldest","newest","dt","cooldownOk","samples","getMouseVelocityToTop","getHoverMs","attachVisibilityChangeListener","attachBackButtonListener","attachActivityListeners","startInactivityCheck","removeScrollListener","removeExitIntentListener","removeScrollVelocityListener","removeVisibilityChangeListener","removeBackButtonListener","removeActivityListeners","inactivityCheckInterval","wildcardCallbacks","activeInstance","activeTriggers","resolveChatDisplayMode","rating","verifiedBadge","thumbBtn","reviewId","creds","voted","direction","SORTS","renderReviewsInline","brand_name","withPhotos","fetchReviews","buildUrl","cache","listContainer","reviewCard","li","avatar","reviewer_snapshot","idn","nameRow","verified_purchase","is_incentivized","incentivizedBadge","created_at","toLocaleDateString","month","year","gallery","foot","helpful_count","not_helpful_count","operator_reply","reply","rlabel","renderList","ul","rerender","sel","photosBtn","renderWall","summary","aggregate","ai_summary","spp","aiSummaryCard","ratingBlock","topRow","dist","lbl","distributionPanel","distribution","ld","aggregateRating","ratingValue","reviewCount","review","reviewRating","author","reviewBody","script","injectJsonLd","compact","display_style","line","seeAll","scratchFoilPalette","hex","g0","g1","g2","toward","amt","lum","KEYFRAMES_ID","mountScratchSurface","kf","ensureKeyframes","thresholdPct","brush","brushRadius","pal","palette","tapReveals","tapToReveal","revealed","scratching","dragged","moves","destroyed","prizeLayer","canvas","CW","CH","paintFoil","foil","createLinearGradient","addColorStop","textAlign","textBaseline","globalAlpha","font","fillText","gy","gx","logoCanvas","foilImageUrl","crossOrigin","maxW","maxH","ar","lw","lh","lc","lctx","cells","GX","clearedCells","idleShimmer","clip","dust","spawnDust","py","dx","dy","rearm","onThreshold","scratchAt","beginPath","arc","PI","cw","ch","x0","y0","GY","markCleared","onScratch","downAt","onDown","setPointerCapture","pointerId","onMove","hypot","onUp","wasTap","onCancel","isRevealed","reveal","RETURNING_MARKER_KEY","SESSION_VERDICT_KEY","cachedVerdict","isNewVisitor","sessionVerdict","readSessionVerdict","readMarkerVerdict","writeSessionVerdict","positiveNumber","behaviouralTriggerEvent","trigger","triggerFromEngineEvent","exec","armBehaviouralTriggers","engine","triggers","armed","fire","isMobile","newlyArmed","readAnonIdFromStorage","JOURNEY_ACK_ACTION","impression","clicked","dismissed","svgNode","DOMParser","parseFromString","getElementsByTagName","importNode","productIdentityCache","WeakMap","UUID_SHAPE","productIdentitiesFor","rawId","rawSku","platformId","encoded","_AegisInAppManager","productUrl","campaigns","displayedCampaigns","_displaySizeMul","suppressedUntil","isInitialized","reconnectAttempts","maxReconnectAttempts","hooks","ready","readyResolve","_lastHealOutcome","pendingPreInitRefresh","currentSurface","currentScreen","currentLocationId","_lastDispatch","filledSlots","WeakSet","slotSignatures","slotTimers","slotObserver","slotRenderScheduled","slotUnfilledBeaconed","slotUnfilledSweepTimer","_pushOptInLoading","journeyStepByCampaign","journeyAcks","propertyId","enableSSE","onActionHandler","onAction","getWorkspaceId","handlers","proceed","handler","emitError","onCampaignsLoaded","onCampaignWillShow","onCampaignShown","onCampaignClick","onCampaignDismiss","markReturningVisitor","pv","refreshCampaigns","Notification","permission","alreadyHealed","_subscribeFromPushPrimer","_sendPushBootBeacon","beaconError","optIn","mode","_pushOptInCache","_fetchPushOptInConfig","rej","snoozed","sdk_version","opt_in_mode","prompt_trigger","pv_count","primer_snoozed","heal_outcome","dispatch","primer_present","primer_eligible","user_agent","updateUserId","activeWebChat","refreshOnEvent","isScreenDeclaration","KNOWN_SURFACES","refreshDebounceTimer","REFRESH_DEBOUNCE_MS","locationId","notifyConversion","goalName","CONVERSION_STORAGE_PREFIX","applySuppressionFromCampaigns","convertedAt","suppress_after_conversion_seconds","scope","expiresAt","rehydrateSuppressionFromStorage","earliestTs","isSuppressed","campaignId","hasConvertedThisSession","showTriggeredCampaign","displayCampaign","append","campaign_id","readyState","CLOSED","attemptReconnect","device_type","detectDeviceType","abAssignments","getABAssignments","btoa","processABAssignments","emitServedEvents","mountPersistentLaunchers","tryDisplayNextCampaign","renderIntoSlots","renderIntoAnchors","renderTokenAnchors","SERVED_DEDUP_PREFIX","assigned_variant_id","getVariantId","client_trigger","sub_type","_primerEligibleNow","delivery_modes","primer","canonicalSurfaceFromScreen","norm","matchesCurrentScreenOrSurface","screens","target_screens","screenOk","surface","effective","ensureSlotObserver","MutationObserver","mutations","sawSlot","addedNodes","nodeType","run","observe","childList","subtree","scheduleSlotUnfilledSweep","slots","eligibleByCategory","seenIds","modes","widget_category","anchorSel","element_selector","anchorResolves","sig","oldTimer","attrMs","declaredMs","slot_rotate_ms","rotateMs","hasAttribute","renderRotatingSlot","renderCampaignIntoSlot","_flattenSlotCard","sweepUnfilledSlots","present","panes","pane","deferImpression","impressed","markVisible","darkDots","every","tplBg","isDarkSurface","dotInactive","dotActive","paintDots","fromClick","parentNode","nextSibling","spacing","element_spacing","margin","anchors","submitUrl","submit_url","inlineMul","_sizeMul","sizer","_surfacePalette","_formFields","form_fields","rendered","renderFormSlot","_ensureSurveyTemplate","_isMultiCard","_hasComposedTemplate","renderComposedCardSlot","buildRenderContext","renderSpinWheelSlot","renderCarouselCardsSlot","renderCountdownSlot","renderScratchCardSlot","renderCustomHtmlSlot","renderVideoInline","renderProgressBarInline","rec_layout","isRow","msOverflowStyle","arrow","scrollBy","clientWidth","behavior","syncArrow","atEnd","scrollLeft","scrollWidth","renderProductRecommendationInline","renderQuizSlot","renderGenericCardSlot","question","thank_you_message","rating_low_prompt","rating_review_url","cleaned","fromEntries","_headerImage","header_image_url","card_meta","renderComposedCardOverlay","fullScreen","createOverlay","modal","_addCornerClose","mul","_slotStyle","_wrapInSlotCard","couponCode","buttonText","actionUrl","padY","padX","colL","accentTok","cbody","btnRadius","fields","_buildFormBody","slotStyle","_profileKeys","maps_to","earn","earnHintWs","earnHintQs","inputs","requiredKeys","ctrlCss","ftype","labelText","req","required","ctrl","ph","op","submit","_cAny","collected","submitFormResponse","modalEl","parentElement","overflow","minHeight","alignItems","justifyContent","customThanks","_playReaction","variantId","response_type","user_id","platform","submitWidgetResponse","responseType","shown","labels","isLossSeg","prize_type","alreadyWon","already_won","rawW","probability","sumW","weights","wTotal","spans","spin_slice_sizing","starts","acc","midOf","stops","colorOf","cfgStr","spinMode","idleAnim","winReveal","hapticsOn","spin_haptics_enabled","soundOn","spin_sound_enabled","pointerColor","hubImg","wheelBgImg","pointerImg","pointerCenter","pointerBase","isUrl","rotation","audioCtx","ensureAudio","beep","freq","durMs","vibrate","wheelWrap","pointer","needleH","pim","stem","wheel","wheelFace","wheelRim","wheelShadow","sliceImgFrac","spin_slice_image_size","simg","pts","sin","cos","segEl","ln","mid","icoVal","byThickness","byLength","rRim","fontPx","flip","textRot","lab","em","tx","sheen","idleClip","hub","him","hubDot","soundBtn","resolved","lastTickSlice","stopIdle","spinSpeed","pDefl","pVel","pointerRaf","stepPointer","playTicks","deg","sliceAt","animateTo","finalDeg","durationMs","delta","t0","lastRot","easeOut","renderTakeover","ov","eyebrow","big","clipboard","writeText","validTill","_validTillLine","claim","beginSpin","velocityDegPerMs","turns","provisional","loopSpeed","looping","lastTs","loopFrame","moved","launch","segMid","matched","segment_index","lost","expires_at","lossIndex","dragging","startAngle","startRotation","lastA","lastT","vel","angleAt","atan2","cursor","prevRot","da","endDrag","releasePointerCapture","speed","wonKind","linked","autoplayMs","interval","contains","tiles","scrollTo","evt","digits","mkDigit","hEl","mEl","sEl","sep","deadline","urgent","diff","desc","_scratchFoilPalette","cfgNum","scratch_idle_shimmer","scratch_haptics_enabled","scratch_sound_enabled","revealImg","foilImg","foilPal","prize_pool","sampleWin","lastScrape","chordFx","freqs","sb","backgroundImage","prizeText","_sIco","_sLbl","wonConfigFor","_extractPrizeLabel","showWin","ic2","eb","_fireConfetti","dealOnce","isIdentified","createDealOnce","createBuffer","sampleRate","getChannelData","createBufferSource","hp","createBiquadFilter","scratchOutcome","sampleLabel","scratch_prize_label","_extractCouponCode","wonCfg","winFx","_extractPrizeExpiry","loseFx","cands","getFullYear","clampOpt","wRaw","heading_font_weight","border_radius","padding_x","padding_y","button_radius","borderRadius","onClientEvent","eventData","matchesClientTrigger","wantedRaw","wanted","actual","behavioural","interactiveSubTypes","sheet","xp","renderReviewsOverlay","multiCard2","overlayCardType","renderCustomHtml","renderModal","renderBanner","renderFullScreen","renderHalfInterstitial","renderAlert","renderPIP","renderTooltip","headerText","seenCards","setActive","mkArrow","glyph","stopAutoplay","autoplayTimer","approx","renderCarouselCards","sticky_position","dismissible","sticky_dismissible","autoHide","sticky_auto_hide_ms","sticky_bg_color","positionCss","strong","stickyCtaLabel","autoHideTimer","persist","renderStickyBar","renderProgressBar","handle","renderProductRecommendation","renderStories","renderActiveWebChatLauncher","chat","chat_accent_color","chat_logo_url","chat_icon","chat_title","chat_greeting","chat_position","chat_theme","chat_display_mode","chat_agent_persona","chat_quick_replies","proactive","chat_proactive","wantExit","exit_intent","wantIdle","cart_idle","renderActiveWebChat","ctxObj","card_context","extra","buttonId","navigateToCampaignAction","lottieFile","level","subType","fn","renderGame","isMultiStepFlow","renderCountdownOffer","renderQuiz","renderMultiStepForm","renderPushPrimer","renderGamificationOverlay","display_size","removeModal","_buildCustomHtmlFrame","html","html_height","bst","inAppStyle","brandVars","btnBg","btnText","iframe","srcdoc","onMsg","contentWindow","__aegis","ev","CustomEvent","cancelable","dispatchEvent","includeDays","UNITS","cap","createCTAButton","addCloseButton","formFields","formBody","rawSteps","answers","flow","completed","recordStep","step_id","consolidated","step_count","markCompleted","createFlowAccumulator","viewedSteps","fireStepViewed","progressTrack","progressFill","stepLabel","optionsList","nav","backBtn","nextBtn","renderStep","opt","ob","answer","visibility","needsAnswer","_renderPushPrimerModal","snooze","_promptTriggerSatisfied","fontSize","supported","requestPermission","enableBtn","allow_text","perm","dismissBtn","dismiss_text","cfgRes","vapidPublicKey","vapid_public_key","serviceWorkerPath","service_worker_path","serviceWorkerScope","_e","service_worker_scope","apiClient","post","webPush","AegisWebPush","AegisWebPush$1","firstPartyCookieId","autoPrompt","_runQuizFlow","questions","resultMap","result_mapping","knowledge","quiz_type","tally","maxPts","txt","showResult","outScore","mn","min_score","mx","max_score","best","rid","rTitle","rBody","result_title","result_id","renderQuestion","hd","imgUrl","correctIdx","correct_index","btns","good","option_results","queueMicrotask","firstElementChild","dataset","aegisSized","_wrapScaled","safeUrl","runCampaignActionVerb","typedDefaultPrevented","typedEvent","button_id","defaultPrevented","typedProceed","domEvent","campaign_type","domProceed","setOnAction","_campaignId","handled","fallbackUrlForAction","rest","verb","qs","removeCampaignEl","pdp","parseAction","_contrastText","numOr","mediaHeight","media_height","overlay_opacity","atBottom","banner_position","contentContainer","textContainer","actionsContainer","bannerCtaLabel","removeBanner","closeButton","autoMs","banner_auto_dismiss_ms","mediaWrap","_heroVideo","secondaryText","modal_secondary_text","secondaryUrl","modal_secondary_url","sec","modal_dismiss_on_overlay","coverHost","pillCorner","sync","hasMedia","isBg","fullscreen_layout","contentColor","fsCtaLabel","fullscreen_secondary_text","fullscreen_secondary_url","grabber","halfCtaLabel","half_secondary_text","half_secondary_url","half_dismiss_on_overlay","alert","vspec","warning","alert_variant","buttonContainer","alertCtaLabel","cancelButton","_pipCornerCss","pip_position","pip_size","pip_autoplay","pip_loop","startMuted","pip_muted","expandable","pip_expandable","draggable","pip_draggable","vw","cornerW","standard","large","pip","cornerCss","slideAnim","video","playGlyph","syncGlyph","togglePlay","mutePill","syncMute","accentText","ctrlBtnCss","expanded","backdrop","expandBtn","collapse","expand","theaterW","Event","sx","sy","ox","oy","nx","offsetWidth","ny","offsetHeight","touchAction","_spotlightDimColor","dim","alpha","anchorSelector","tooltip_anchor_selector","preferredPosition","tooltip_position","highlightOn","tooltip_highlight","textColor","tooltip","outsideHandler","cleanupReflow","anim","tooltip_offset","OPP","reposition","tooltipRect","scrollX","vh","room","needV","needH","fits","side","arrowTop","arrowLeft","arrowBottom","arrowRight","ensureAnchor","phase","onReflow","capture","ro","mo","ResizeObserver","attributes","disconnect","colors","ownerDocument","cardAnchor","prevPos","layer","spec","celebrate","festive","affirm","acknowledge","empathize","ringPx","fit","scaled","reactionIcon","_reactionSvg","motion","ra","reaction_animations","_assetBase","_loadLottie","lot","loadAnimation","renderer","lottie","__aegisLottieP","parsedUrl","trackedCampaign","externalEventId","storedAnonId","identifiedUserId","event_type","current_surface","journey_execution_id","lifecycle_fork_id","property_id","idempotency_key","journey_step_execution_id","ackAction","ackJourneyQueueItem","retryable","JOURNEY_ACK_RETRY_MS","styles","clearABState","AegisInAppManager","selectorFor","CSS","escape","attr","tagName","parentEl","sibs","pairWebDevice","noop","device_label","deviceLabel","screenName","nodes","candidates","slotKey","bounds","screen_name","view_tree","captureWebScreen","onNav","_pairingStarted","maybeStartPairing","AegisWidgetManager","widgets","renderedWidgets","isDestroyed","prefetchWidgetConfigs","triggerEngine","ownsTriggerEngine","enablePrefetch","cssCustomization","onEvent","sourcePlatform","onCartDetected","updateCSSCustomization","customization","fetchPrefetchConfigs","preloadWidgetAssets","animationStyle","emitEvent","fetchWidgets","renderImmediateWidgets","setupTriggerListeners","setupExitIntentWithPrefetch","setCartData","cartData","cart_currency","detectPlatformCart","detected","normalizeShopifyCart","normalizeWooCart","normalizeMagentoCart","line_items","product_title","cartJsonEl","mageCacheStr","mageCache","quote_id","subtotal","currencyCode","item_id","product_price_value","exitIntentConfig","getDeviceType","trigger_rules","widget","renderWidget","widget_id","isMobileDevice","mobileConfig","mobile_triggers","widgetId","detectedCart","renderCartRecoveryPopup","sendCartAbandonmentBeacon","renderLeadGenPopup","scroll_threshold","scroll_min_position","scroll_cooldown","idle_timer","visibility_change","back_button","beaconData","user_email","abandoned_at","beaconUrl","renderChatBubble","renderToast","renderFeedbackForm","renderExitIntentPopup","icon_url","link_url","positionStyles","bottom_right","bottom_left","top_right","top_left","textEl","getUTMParams","toast","iconEl","messageEl","description","form","textareaLabel","textarea","submitButton","submitFeedback","descEl","event_data","deviceType","titleText","interpolateCartVariables","accent_color","messageText","show_cart_items","cartTitle","itemDiv","discount_code","discountBox","discount_percentage","show_timer","timer_minutes","checkoutUrl","emailInput","isError","THRESHOLD_SIGNALS","scroll_depth","inactivity","THRESHOLD_EVENT","microIntentRule","fireOn","fire_on","when","suppress_competing","signalThresholds","rules","visit","expr","operands","crossing","rule","hasLeaf","MicroIntentArming","registered","listening","onEngineEvent","arm","armedMicroIntentCampaigns","evaluator","setArmed","listen","noteConversion","updateSignal","getSnapshot","converted_in_session","hasConverted","decide","decision","onSignalChanged","markFired","getValueByPath","namespaces","evaluateExpr","snapshot","isIntentLeaf","leaf","want","isMember","evaluateLeaf","operand","allNull","evaluateRule","_IntentRuleEvaluator","firedThisSession","silencedThisSession","oneInterruptionPerSession","interruptiveBudgetSpent","ttfiMs","sessionStartMs","nowFn","setGovernance","updateSnapshot","partial","updateContext","namespace","getNamespaces","getArmed","sessionElapsedMs","exemptFromTtfi","EXIT_SIGNALS","suppressIds","other","IntentRuleEvaluator","ContactScoresFetcher","lastFetchedAt","latestSnapshot","dedupWindowMs","fetchImpl","updateOrganizationId","fetchForContact","force","lastFetchedContactId","snap","rfm_segment","lifecycle_stage","churn_risk_tier","predicted_ltv_tier","predicted_ltv","price_sensitivity_score","scoring_tier","toIntentSnapshot","ns","HttpClient","slug","errBody","StoreCreditClient","http","listActive","balances","balancePaise","balance_paise","lastActivityAt","last_activity_at","startReload","amount_paise","amountPaise","first_name","firstName","callback_url","callbackUrl","checkout_url","provider","paymentLinkId","payment_link_id","GiftCardsClient","startPurchase","recipientPhone","sender_phone","senderPhone","recipient_phone","recipient_name","recipientName","personal_message","personalMessage","senderContactId","sender_contact_id","giftCardId","gift_card_id","recipientContactId","recipient_contact_id","amountCreditedPaise","amount_credited_paise","newBalancePaise","new_balance_paise","listReceived","gift_cards","deliveredAt","delivered_at","claimedAt","claimed_at","createdAt","AegisLoyaltyManager","storeCredit","giftCards","_AegisMessageRuntime","armedInAppTriggers","__AEGIS_SDK_VERSION__","_bannerPrinted","pairingApiHost","enableWidgetPrefetch","applyCart","inApp","deriveCartState","intentRuleEvaluator","governance","contactScores","loyalty","storefrontSlug","enableChat","proactiveTriggers","hasNoHover","microIntent","unsubscribeCampaignsLoaded","bridgeContextToSignals","setCartState","bindEcommerce","tracker","getCampaigns","AegisMessageRuntime","COOKIE_NAME","BootstrapError","super","globalName","existingQueue","instance","loadOptions","_loadOptions","owner","aegisBootstrap","currentOrigin","attestationToken","doFetch","cookieId","expires","setDate","getDate","writeFirstPartyCookie","AegisRenderPreview","manager","cfgRec","deliveryModes","widgetCategory","coGroup","mm","eventTracker","swRegistration","heartbeatTimer","promptDelay","createBuiltinEventTracker","resolveContactId","message_id","onPushTap","onPushShown","onPushDismiss","serviceWorker","handleSWMessage","subscribeToPush","trackPushEvent","subscription","pushManager","getSubscription","subscriptionJSON","toJSON","fingerprint","getStableFingerprint","retryApiCall","p256dh","auth","device_fingerprint","application_server_key","encodeAppServerKey","session_code","sessionCode","desiredKey","urlBase64ToUint8Array","existingKey","applicationServerKey","unsubscribe","subscribe","userVisibleOnly","toArrayBuffer","getBrowserInfo","subtle","digest","fromStorage","action_type","button_label","dispatchPushTap","degraded","persistResubscription","action_payload","base64String","base64","repeat","rawData","ArrayBuffer"],"mappings":"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"}