@consenti/ui 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +512 -138
  2. package/dist/angular.d.cts +610 -151
  3. package/dist/angular.d.ts +610 -151
  4. package/dist/angular.js +1 -1
  5. package/dist/angular.js.map +1 -1
  6. package/dist/angular.mjs +1 -1
  7. package/dist/angular.mjs.map +1 -1
  8. package/dist/chunk-3EXCD24H.mjs +2 -0
  9. package/dist/{chunk-K2UN3O67.mjs.map → chunk-3EXCD24H.mjs.map} +1 -1
  10. package/dist/chunk-RDE7CM6T.mjs +2 -0
  11. package/dist/chunk-RDE7CM6T.mjs.map +1 -0
  12. package/dist/http-AQVVUCX6.mjs +2 -0
  13. package/dist/index.css +1 -1
  14. package/dist/index.d.ts +971 -125
  15. package/dist/index.mjs +1 -1
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/index.umd.js +1 -1
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/profiles-5ZLWWN2L.mjs +2 -0
  20. package/dist/profiles-5ZLWWN2L.mjs.map +1 -0
  21. package/dist/react.d.cts +589 -129
  22. package/dist/react.d.ts +589 -129
  23. package/dist/react.js +1 -1
  24. package/dist/react.js.map +1 -1
  25. package/dist/react.mjs +1 -1
  26. package/dist/react.mjs.map +1 -1
  27. package/dist/testing.d.cts +1 -1
  28. package/dist/testing.d.ts +1 -1
  29. package/dist/testing.js +1 -1
  30. package/dist/testing.js.map +1 -1
  31. package/dist/testing.mjs +1 -1
  32. package/dist/testing.mjs.map +1 -1
  33. package/dist/vue.d.cts +562 -79
  34. package/dist/vue.d.ts +562 -79
  35. package/dist/vue.js +1 -1
  36. package/dist/vue.js.map +1 -1
  37. package/dist/vue.mjs +1 -1
  38. package/dist/vue.mjs.map +1 -1
  39. package/package.json +13 -8
  40. package/dist/chunk-K2UN3O67.mjs +0 -2
  41. package/dist/http-UU2NIW6M.mjs +0 -2
  42. /package/dist/{http-UU2NIW6M.mjs.map → http-AQVVUCX6.mjs.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/styles/consenti-css.ts","../src/utils/ssr.ts","../src/utils/console.ts","../src/utils/broadcast.ts","../src/utils/receipt.ts","../src/utils/cookie.ts","../src/utils/storage.ts","../src/core/consent-store.ts","../src/core/event-bus.ts","../src/utils/locale.ts","../src/utils/geo-hints.ts","../../../packages/utils/src/compliance.ts","../src/profiles/general-privacy-consent.ts","../src/profiles/notice-only.ts","../src/profiles/opt-in.ts","../src/profiles/opt-in-brazil.ts","../src/profiles/opt-in-china.ts","../src/profiles/opt-in-dpdpa.ts","../src/profiles/opt-out.ts","../src/profiles/opt-out-strict.ts","../src/core/profile-resolver.ts","../src/core/gpc.ts","../src/ui/buttons.ts","../src/ui/overlay.ts","../src/ui/banner.ts","../src/ui/focus-trap.ts","../src/ui/modal.ts","../src/ui/renderer.ts","../src/core/consenti-setup.ts","../src/plugins/plugin.base.ts","../src/profile/consenti-profile.ts","../src/utils/consent-script.ts","../src/utils/cookie-trigger.ts"],"sourcesContent":["// Auto-generated by scripts/build-css.mjs — do not edit manually.\n// Run `npm run build --workspace=apps/ui` to regenerate.\nexport const CONSENTI_CSS = `#consenti-root{position:relative}.consenti-d-none{display:none}:root{--consenti-color-bg: #ffffff;--consenti-color-text: #1a2e4a;--consenti-color-primary: #1565c0;--consenti-color-primary-text: #ffffff;--consenti-color-secondary: #f0f4f8;--consenti-color-secondary-text: #1a2e4a;--consenti-color-border: #dbe4ee;--consenti-color-secondary-border: #1a2e4a;--consenti-color-overlay: rgba(26, 52, 96, 0.55);--consenti-color-accent: #d32f2f;--consenti-color-accent-text: #ffffff;--consenti-font-family: system-ui, -apple-system, sans-serif;--consenti-font-size-base: 14px;--consenti-font-size-heading: 16px;--consenti-font-weight-heading: 600;--consenti-line-height: 1.5;--consenti-spacing-sm: 8px;--consenti-spacing-md: 16px;--consenti-spacing-lg: 24px;--consenti-border-radius: 8px;--consenti-border-radius-btn: 0;--consenti-shadow: 0 4px 24px rgba(21, 101, 192, 0.14);--consenti-toggle-bg-on: #43a047;--consenti-toggle-bg-off: #9ca3af;--consenti-toggle-knob: #ffffff;--consenti-toggle-width: 52px;--consenti-toggle-height: 28px;--consenti-z-banner: 9999;--consenti-z-overlay: 9998;--consenti-z-modal: 10000}.consenti-root--dark{--consenti-color-bg: #1e2535;--consenti-color-text: #e2e8f0;--consenti-color-primary: #4a9eff;--consenti-color-primary-text: #ffffff;--consenti-color-secondary: #2a3447;--consenti-color-secondary-text: #cbd5e1;--consenti-color-border: #374151;--consenti-color-overlay: rgba(0, 0, 0, 0.65);--consenti-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);--consenti-toggle-bg-off: #6b7280}.consenti-overlay{position:fixed;inset:0;background:var(--consenti-color-overlay);z-index:var(--consenti-z-overlay)}.consenti-btn{display:inline-flex;align-items:center;justify-content:center;padding:var(--consenti-spacing-sm) var(--consenti-spacing-md);border:none;border-radius:var(--consenti-border-radius-btn);font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);font-weight:500;line-height:1;cursor:pointer;text-decoration:none;transition:opacity .15s ease}.consenti-btn:focus-visible{outline:2px solid var(--consenti-color-primary);outline-offset:2px}.consenti-btn:hover{opacity:.9}.consenti-btn--primary{background:var(--consenti-color-primary);color:var(--consenti-color-primary-text)}.consenti-btn--secondary{background:var(--consenti-color-secondary);color:var(--consenti-color-primary);border:1px solid var(--consenti-color-primary)}.consenti-btn--secondary:hover{background:var(--consenti-color-primary);color:var(--consenti-color-secondary);border:1px solid var(--consenti-color-secondary)}.consenti-btn--text{background:rgba(0,0,0,0);color:var(--consenti-color-primary);padding-left:0;padding-right:0;text-decoration:underline;text-underline-offset:4px}.consenti-btn--accent{background:var(--consenti-color-accent);color:var(--consenti-color-accent-text)}.consenti-btn--close{background:rgba(0,0,0,0);color:var(--consenti-color-text);font-size:20px;line-height:1;padding:4px 8px;border-radius:50%}.consenti-btn--close:hover{background:var(--consenti-color-secondary);opacity:1}.consenti-banner{position:fixed;left:0;right:0;z-index:var(--consenti-z-banner);background:var(--consenti-color-bg);color:var(--consenti-color-text);font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);line-height:var(--consenti-line-height);box-shadow:var(--consenti-shadow);padding:var(--consenti-spacing-lg);display:flex;flex-wrap:wrap;align-items:center;gap:var(--consenti-spacing-md)}.consenti-banner--top{top:0;border-bottom:1px solid var(--consenti-color-border)}@media(prefers-reduced-motion: no-preference){.consenti-banner--top{animation:consenti-slide-down .2s ease-out}}.consenti-banner--bottom{bottom:0;border-top:1px solid var(--consenti-color-border)}@media(prefers-reduced-motion: no-preference){.consenti-banner--bottom{animation:consenti-slide-up .2s ease-out}}.consenti-banner--middle{top:50%;transform:translateY(-50%);left:50%;right:auto;transform:translate(-50%, -50%);width:min(600px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner--left-bottom{bottom:var(--consenti-spacing-lg);left:var(--consenti-spacing-lg);right:auto;width:min(400px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner--right-bottom{bottom:var(--consenti-spacing-lg);right:var(--consenti-spacing-lg);left:auto;width:min(400px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner__text-container{flex:1;min-width:500px}.consenti-banner__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0 0 var(--consenti-spacing-sm);width:100%}.consenti-banner__text{margin:0}.consenti-banner__links{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);margin-top:var(--consenti-spacing-sm)}.consenti-banner__link{font-size:.85em}.consenti-banner__controls{position:absolute;top:var(--consenti-spacing-sm);right:var(--consenti-spacing-sm);display:flex;align-items:center;gap:4px}.consenti-banner__buttons{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);align-items:center;margin-block:var(--consenti-spacing-md)}.consenti-banner__powered-by{position:absolute;right:10px;bottom:2px;opacity:.2;color:var(--consenti-color-primary)}@media(max-width: 576px){.consenti-banner{padding:var(--consenti-spacing-md);gap:var(--consenti-spacing-sm)}.consenti-banner--left-bottom,.consenti-banner--right-bottom{left:0;right:0;bottom:0;width:100%;border-radius:0;border-top:1px solid var(--consenti-color-border)}.consenti-banner--middle{width:95vw}.consenti-banner__buttons{width:100%;flex-direction:row;align-items:stretch;margin-bottom:var(--consenti-spacing-md)}.consenti-banner__text-container{min-width:0;width:100%}}@keyframes consenti-slide-up{from{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes consenti-slide-down{from{transform:translateY(-100%)}to{transform:translateY(0)}}.consenti-modal{position:fixed;inset:0;z-index:var(--consenti-z-modal);display:flex;font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);color:var(--consenti-color-text);line-height:var(--consenti-line-height)}.consenti-modal--passthrough{pointer-events:none}.consenti-modal--passthrough .consenti-modal__container{pointer-events:auto}.consenti-modal--center{align-items:center;justify-content:center}.consenti-modal--left{align-items:stretch;justify-content:flex-start}.consenti-modal--left .consenti-modal__container{border-radius:0 var(--consenti-border-radius) var(--consenti-border-radius) 0;height:100%;max-height:100vh}.consenti-modal--right{align-items:stretch;justify-content:flex-end}.consenti-modal--right .consenti-modal__container{border-radius:0;height:100%;max-height:100vh}.consenti-modal--fullscreen{align-items:flex-start;justify-content:flex-start}.consenti-modal--fullscreen .consenti-modal__container{width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;border-radius:0;margin:0}.consenti-modal__overlay{position:absolute;inset:0;background:var(--consenti-color-overlay)}.consenti-modal__container{position:relative;z-index:1;background:var(--consenti-color-bg);border-radius:var(--consenti-border-radius);box-shadow:var(--consenti-shadow);width:min(560px,90vw);max-height:85vh;display:flex;flex-direction:column;overflow:hidden}@media(prefers-reduced-motion: no-preference){.consenti-modal__container{animation:consenti-fade-in .15s ease-out}}.consenti-modal__header{display:flex;justify-content:space-between;padding:var(--consenti-spacing-md) var(--consenti-spacing-lg);border-bottom:1px solid var(--consenti-color-border);flex-shrink:0;position:relative;flex-direction:column}.consenti-modal__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0;flex:1}.consenti-modal__header-controls{display:flex;align-items:center;gap:4px;flex-shrink:0;position:absolute;right:15px;top:10px}.consenti-modal__close{flex-shrink:0}.consenti-modal__links{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg)}.consenti-modal__link{font-size:.85em;text-decoration:underline;text-underline-offset:2px}.consenti-modal__subheading{padding-top:var(--consenti-spacing-sm);margin:0;color:#6b7280;font-size:13px}.consenti-modal__text{padding:var(--consenti-spacing-sm) 0}.consenti-modal__body{overflow-y:auto;flex:1;padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg);position:relative}.consenti-modal__categories{display:flex;flex-direction:column;gap:var(--consenti-spacing-md);padding:var(--consenti-spacing-md) 0}.consenti-modal__buttons{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);padding:var(--consenti-spacing-md);border-top:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__receipt-option{padding-top:var(--consenti-spacing-md);border-top:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__receipt-label{display:flex;align-items:center;gap:var(--consenti-spacing-sm);font-size:var(--consenti-font-size-base);cursor:pointer}.consenti-modal__receipt-checkbox{width:16px;height:16px;flex-shrink:0;cursor:pointer}.consenti-modal__receipt-desc{margin:4px 0 0 24px;font-size:12px;color:#6b7280}.consenti-modal__powered-by{opacity:.5;color:var(--consenti-color-primary)}@keyframes consenti-fade-in{from{opacity:0;transform:scale(0.97)}to{opacity:1;transform:scale(1)}}.consenti-category{padding:var(--consenti-spacing-md) 0;border-bottom:1px solid var(--consenti-color-border)}.consenti-category:last-child{border-bottom:none}.consenti-category--mandatory .consenti-category__toggle{opacity:.6;cursor:default}.consenti-category__header{display:flex;align-items:center;justify-content:space-between;gap:var(--consenti-spacing-md);margin-bottom:var(--consenti-spacing-sm)}.consenti-category__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0}.consenti-category__text{color:#949dab;font-size:13px;margin:0}.consenti-category__li-desc{margin:var(--consenti-spacing-sm) 0 0;font-size:12px;color:#6b7280;font-style:italic}.consenti-category__toggle{flex-shrink:0;position:relative;width:var(--consenti-toggle-width);height:var(--consenti-toggle-height);border-radius:calc(var(--consenti-toggle-height)/2);background:var(--consenti-toggle-bg-off);border:none;cursor:pointer;transition:background .2s ease;padding:0}.consenti-category__toggle:focus-visible{outline:2px solid var(--consenti-color-primary);outline-offset:2px}.consenti-category__toggle[aria-checked=true]{background:var(--consenti-toggle-bg-on)}.consenti-category__toggle--mandatory{cursor:default}.consenti-category__toggle-icon{position:absolute;top:50%;transform:translateY(-50%);width:10px;height:10px;display:flex;align-items:center;justify-content:center;pointer-events:none;transition:opacity .15s ease;color:hsla(0,0%,100%,.9)}.consenti-category__toggle-icon svg{width:10px;height:10px;overflow:visible}.consenti-category__toggle-icon--check{left:8px;opacity:0}[aria-checked=true] .consenti-category__toggle-icon--check{opacity:1}.consenti-category__toggle-icon--cross{right:8px;opacity:1}[aria-checked=true] .consenti-category__toggle-icon--cross{opacity:0}.consenti-category__toggle-knob{position:absolute;top:2px;left:2px;width:calc(var(--consenti-toggle-height) - 4px);height:calc(var(--consenti-toggle-height) - 4px);border-radius:50%;background:var(--consenti-toggle-knob);transition:transform .2s ease;display:block}[aria-checked=true] .consenti-category__toggle-knob{transform:translateX(calc(var(--consenti-toggle-width) - var(--consenti-toggle-height)))}.consenti-locale-switcher{position:relative;display:inline-flex}.consenti-locale-switcher__btn{font-size:1rem;line-height:1;cursor:pointer}.consenti-locale-switcher__list{position:absolute;top:calc(100% + 4px);right:0;z-index:calc(var(--consenti-z-modal) + 1);background:var(--consenti-color-bg);border:1px solid var(--consenti-color-border);border-radius:var(--consenti-border-radius);box-shadow:var(--consenti-shadow);list-style:none;margin:0;padding:4px 0;min-width:80px}.consenti-locale-switcher__list li{display:block}.consenti-locale-switcher__option{display:block;width:100%;padding:6px 12px;text-align:left;font-size:var(--consenti-font-size-base);color:var(--consenti-color-text);background:none;border:none;cursor:pointer}.consenti-locale-switcher__option:hover{background:var(--consenti-color-secondary)}.consenti-locale-switcher__option--active{font-weight:600;color:var(--consenti-color-primary)}.consenti-locale-switcher--modal .consenti-locale-switcher__list{right:0}`\n","/**\n * Server-Side Rendering (SSR) guards.\n *\n * Every access to browser globals (`window`, `document`, `navigator`) must go through\n * these helpers. `new ConsentiSetup(config)` called during SSR — e.g. in a Next.js or\n * Nuxt server render — silently no-ops because `isClient()` returns `false` and the\n * constructor returns early before touching any DOM API.\n */\n\n/**\n * Returns `true` when running in a browser environment with `window` and `document`\n * available. Returns `false` in Node.js, Deno, edge runtimes, and test environments\n * that do not provide a DOM.\n */\nexport const isClient = (): boolean =>\n typeof window !== 'undefined' && typeof document !== 'undefined'\n\n/**\n * Returns the `window` object when running client-side, or `null` in SSR contexts.\n * Prefer this over bare `window` access in code that might run during SSR.\n */\nexport const safeWindow = (): (Window & typeof globalThis) | null =>\n isClient() ? window : null\n\n/**\n * Returns the `document` object when running client-side, or `null` in SSR contexts.\n */\nexport const safeDocument = (): Document | null =>\n isClient() ? document : null\n\n/**\n * Returns the `navigator` object when running client-side and `navigator` is defined,\n * or `null` otherwise. Some non-browser environments expose `window` but not `navigator`.\n */\nexport const safeNavigator = (): Navigator | null =>\n isClient() && typeof navigator !== 'undefined' ? navigator : null\n","type ConsoleLevel = 'info' | 'log' | 'warning' | 'error'\n\nlet _levels: ConsoleLevel[] = ['error']\n\nexport function initConsole(levels: ConsoleLevel[]): void {\n _levels = levels\n}\n\nexport const logger = {\n info: (...args: unknown[]) => { if (_levels.includes('info')) console.info('[Consenti]', ...args) },\n log: (...args: unknown[]) => { if (_levels.includes('log')) console.log('[Consenti]', ...args) },\n warn: (...args: unknown[]) => { if (_levels.includes('warning')) console.warn('[Consenti]', ...args) },\n error: (...args: unknown[]) => { if (_levels.includes('error')) console.error('[Consenti]', ...args) },\n}\n","/**\n * Cross-tab consent synchronisation via the `BroadcastChannel` API.\n *\n * When a user interacts with the consent widget in one tab (accept, deny, delete),\n * `ConsentiChannel` broadcasts the change to all other open tabs on the same origin.\n * Receiving tabs update their in-memory state and hide the banner without requiring\n * a page reload.\n *\n * `BroadcastChannel` is not available in very old browsers or in Safari < 15.4.\n * The `isSupported` guard ensures `ConsentiChannel` fails silently in those cases\n * — consent still works, cross-tab sync is just unavailable.\n */\n\nimport type { ConsentiMessage, ConsentValue } from '../types'\nimport { isClient } from './ssr'\nimport { logger } from './console'\n\nexport type { ConsentiMessage }\n\n/**\n * Wrapper around the native `BroadcastChannel` API for Consenti inter-tab messaging.\n *\n * All tabs with an active `ConsentiSetup` instance for the same origin subscribe\n * on the shared channel name `'consenti'` and receive each other's consent events.\n */\nexport class ConsentiChannel {\n private channel: BroadcastChannel | null = null\n private onMessageCallback: ((msg: ConsentiMessage) => void) | undefined\n\n /** `true` when `BroadcastChannel` is available in the current environment. */\n get isSupported(): boolean {\n return isClient() && typeof BroadcastChannel !== 'undefined'\n }\n\n /**\n * Opens the `BroadcastChannel` and registers a message handler.\n * Silently no-ops if `BroadcastChannel` is unsupported.\n *\n * @param onMessage - Callback invoked with each received `ConsentiMessage`.\n */\n connect(onMessage: (msg: ConsentiMessage) => void): void {\n if (!this.isSupported) return\n this.onMessageCallback = onMessage\n this.channel = new BroadcastChannel('consenti')\n this.channel.onmessage = (event: MessageEvent<ConsentiMessage>) => {\n if (event.data?.type) {\n onMessage(event.data)\n }\n }\n this.channel.onmessageerror = () => {\n logger.warn('BroadcastChannel message error — ignoring')\n }\n }\n\n /**\n * Posts a message to all other tabs connected to the `'consenti'` channel.\n * The sending tab does not receive its own message.\n *\n * @param message - The `ConsentiMessage` to broadcast.\n */\n send(message: ConsentiMessage): void {\n this.channel?.postMessage(message)\n }\n\n /** Closes the channel and removes the message callback. */\n disconnect(): void {\n this.channel?.close()\n this.channel = null\n this.onMessageCallback = void 0\n }\n}\n\n// Re-export so callers can use ConsentValue without going through types\nexport type { ConsentValue }\n","/**\n * Consent receipt generation and download.\n *\n * When `core.allowReceipt: true`, the preference modal shows an opt-in checkbox.\n * If the user checks it before saving, `downloadReceipt()` is called after\n * `submitConsent()` to download a JSON file to their device.\n *\n * The receipt is a portable record of the user's choices at a specific point in time.\n * If `core.signCookies` is enabled, the receipt also includes the HMAC signature as\n * tamper evidence.\n */\n\nimport type { ConsentReceipt, ConsentValue } from '../types'\n\n/** Parameters required to construct a consent receipt. */\nexport interface ReceiptParams {\n /** Visitor UUID from the `consenti_uid` cookie. */\n visitorId: string\n /** Numeric profile ID. */\n profileId: number\n /** Profile schema version at the time of consent. */\n profileVersion: number\n /** Locale active when consent was given. */\n locale: string\n /** The full consent map. */\n consent: ConsentValue\n /** HMAC-SHA256 hex signature, if `core.signCookies` is enabled. */\n signature?: string | undefined\n}\n\n/**\n * Builds a `ConsentReceipt` from the given parameters.\n * Sets `version: '1.0'` and `issuedAt` to the current time.\n *\n * @param params - Receipt content. See `ReceiptParams`.\n */\nexport function generateReceipt(params: ReceiptParams): ConsentReceipt {\n const receipt: ConsentReceipt = {\n version: '1.0',\n issuedAt: new Date().toISOString(),\n visitorId: params.visitorId,\n profileId: params.profileId,\n profileVersion: params.profileVersion,\n locale: params.locale,\n consent: params.consent,\n }\n if (params.signature) receipt.signature = params.signature\n return receipt\n}\n\n/**\n * Triggers a browser file download of the receipt as a pretty-printed JSON file.\n *\n * The filename is `consenti-receipt-{YYYY-MM-DD}.json`.\n * Uses `Blob` + `URL.createObjectURL` + a synthetic anchor click —\n * this approach is supported in all ES2020 target browsers.\n *\n * Silently returns when called in an SSR context.\n *\n * @param receipt - The receipt object returned by `generateReceipt()`.\n */\nexport function downloadReceipt(receipt: ConsentReceipt): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return\n\n const json = JSON.stringify(receipt, null, 2)\n const blob = new Blob([json], { type: 'application/json' })\n const url = URL.createObjectURL(blob)\n\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `consenti-receipt-${receipt.issuedAt.slice(0, 10)}.json`\n anchor.style.display = 'none'\n\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n}\n","/**\n * Low-level `document.cookie` read/write helpers.\n *\n * All functions are SSR-safe — they return early or return `null` when `isClient()`\n * is `false`. Consumers should prefer the `ConsentStorage` class which abstracts\n * over both cookies and `localStorage`.\n */\n\nimport type { CookieOptions } from '../types'\nimport { isClient } from './ssr'\n\n/**\n * Reads a single cookie value by name.\n *\n * @param name - Cookie name (unencoded).\n * @returns The decoded cookie value, or `null` if the cookie is absent or SSR context.\n */\nexport function getCookie(name: string): string | null {\n if (!isClient()) return null\n const match = document.cookie.match(new RegExp(`(?:^|;\\\\s*)${escapeRegExp(name)}=([^;]*)`))\n return match ? decodeURIComponent(match[1] ?? '') : null\n}\n\n/**\n * Writes a cookie to `document.cookie`.\n *\n * The `Secure` flag is auto-detected from `window.location.protocol` when `opts.secure`\n * is not explicitly provided — cookies are set without `Secure` on `http://` to allow\n * local development without HTTPS.\n *\n * @param name - Cookie name (unencoded).\n * @param value - Cookie value (unencoded — will be URI-encoded).\n * @param opts - Optional cookie attributes.\n */\nexport function setCookie(name: string, value: string, opts: CookieOptions = {}): void {\n if (!isClient()) return\n const secure =\n opts.secure ??\n (typeof window !== 'undefined' &&\n typeof window.location !== 'undefined' &&\n window.location.protocol === 'https:')\n const parts = [\n `${encodeURIComponent(name)}=${encodeURIComponent(value)}`,\n `Path=${opts.path ?? '/'}`,\n `SameSite=${opts.sameSite ?? 'Lax'}`,\n opts.maxAge != null ? `Max-Age=${opts.maxAge}` : '',\n secure ? 'Secure' : '',\n opts.domain ? `Domain=${opts.domain}` : '',\n ]\n document.cookie = parts.filter(Boolean).join('; ')\n}\n\n/**\n * Deletes a cookie by setting `Max-Age=0`.\n *\n * @param name - Cookie name (unencoded).\n * @param opts - Optional `path` and `domain` attributes — must match the original `setCookie` call.\n */\nexport function deleteCookie(name: string, opts: Pick<CookieOptions, 'path' | 'domain'> = {}): void {\n setCookie(name, '', { ...opts, maxAge: 0 })\n}\n\n/** Escapes special regex characters in a cookie name so it can be used in a `RegExp`. */\nfunction escapeRegExp(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","/**\n * Unified storage abstraction for consent data.\n *\n * `ConsentStorage` normalises `document.cookie` and `localStorage` behind a single\n * `read / write / remove` interface so the rest of the codebase does not need to\n * know which persistence backend is active.\n *\n * `'cookie'` is the default and recommended mode — consent written to a cookie is\n * visible across subdomains when a `Domain` attribute is set.\n * `'localStorage'` is origin-scoped and cannot be shared with subdomains; it is also\n * incompatible with API mode (which sets consent via `Set-Cookie` on the server).\n */\n\nimport type { CookieOptions } from '../types'\nimport { isClient } from './ssr'\nimport { getCookie, setCookie, deleteCookie } from './cookie'\nimport { logger } from './console'\n\n/** The two supported storage backends. */\nexport type StorageMode = 'cookie' | 'localStorage'\n\n/**\n * Abstraction layer over `document.cookie` and `localStorage`.\n *\n * All operations are SSR-safe — they return early without throwing when `isClient()` is `false`.\n */\nexport class ConsentStorage {\n /**\n * @param mode - `'cookie'` (default) or `'localStorage'`. Falls back to `'cookie'` if\n * `localStorage` is requested but unavailable in the current environment.\n */\n constructor(private mode: StorageMode) {\n if (mode === 'localStorage' && isClient() && typeof localStorage === 'undefined') {\n logger.warn('localStorage is not available, falling back to cookie storage')\n this.mode = 'cookie'\n }\n }\n\n /**\n * Reads a value from storage.\n *\n * @param key - Cookie name or localStorage key.\n * @returns The stored string value, or `null` if absent or SSR context.\n */\n read(key: string): string | null {\n if (!isClient()) return null\n if (this.mode === 'localStorage') {\n return localStorage.getItem(key)\n }\n return getCookie(key)\n }\n\n /**\n * Writes a value to storage.\n *\n * @param key - Cookie name or localStorage key.\n * @param value - String value to persist.\n * @param opts - Cookie attributes (ignored in localStorage mode).\n */\n write(key: string, value: string, opts: CookieOptions = {}): void {\n if (!isClient()) return\n if (this.mode === 'localStorage') {\n localStorage.setItem(key, value)\n } else {\n setCookie(key, value, opts)\n }\n }\n\n /**\n * Removes a value from storage.\n *\n * @param key - Cookie name or localStorage key.\n * @param opts - Cookie `path` / `domain` attributes (must match the original write call).\n */\n remove(key: string, opts: Pick<CookieOptions, 'path' | 'domain'> = {}): void {\n if (!isClient()) return\n if (this.mode === 'localStorage') {\n localStorage.removeItem(key)\n } else {\n deleteCookie(key, opts)\n }\n }\n}\n","/**\n * Consent cookie read/write operations.\n *\n * Cookie name format : `consenti_{userId}_{profileId}`\n * Cookie value format: `t:{epochSeconds}::{cookieId}:{status}|...`\n * Signed value format: `t:{epochSeconds}::{pairs}::sig:{hmac-hex}`\n *\n * The compact `t:{seconds}` timestamp replaces the previous ISO-8601 format to reduce\n * cookie size. Old `ConsentTimestamp:{ISO}` cookies are parsed transparently for\n * backwards compatibility.\n *\n * Visitor identity is derived from the consent cookie name (no separate `consenti_uid`\n * cookie is written). This avoids dropping identifiers before the user has given consent,\n * which is required by TTDSG §25 (Germany) and similar ePrivacy laws.\n *\n * The store supports both `document.cookie` and `localStorage` via `ConsentStorage`.\n * An in-memory cache avoids repeated cookie reads on the hot path (e.g. `hasConsent()`\n * called in render loops by the React/Vue hooks).\n */\n\nimport type { ConsentValue, ParsedConsent, CookieOptions, Cookie } from '../types'\nimport { ConsentStorage } from '../utils/storage'\nimport { logger } from '../utils/console'\n\nexport function resolveConsentMaxAge(cookies: Cookie[]): number {\n const minDays = cookies\n .map(c => c.expiry ?? 365)\n .reduce((min, d) => Math.min(min, d), 365)\n return minDays * 24 * 60 * 60\n}\n\nconst COOKIE_NAME_PREFIX = 'consenti'\n\n/**\n * Builds the full cookie name for a visitor + profile combination.\n *\n * @param userId - UUID assigned to this visitor.\n * @param profileId - Numeric profile ID from `core.profileId`.\n */\nexport function buildConsentCookieName(userId: string, profileId: number): string {\n return `${COOKIE_NAME_PREFIX}_${userId}_${profileId}`\n}\n\n/**\n * Serialises a `ConsentValue` map into the compact cookie value string (without signature).\n *\n * @param consent - The consent map to serialise.\n * @returns A string in the form `t:{epochSeconds}::{id}:{status}|...`\n */\nexport function buildCookieValue(consent: ConsentValue): string {\n const epochSeconds = Math.floor(Date.now() / 1000)\n const pairs = Object.entries(consent)\n .map(([id, status]) => `${id}:${status}`)\n .join('|')\n return `t:${epochSeconds}::${pairs}`\n}\n\n/**\n * Parses a raw cookie string back into a `ParsedConsent` object.\n *\n * Handles both the current compact format (`t:{epochSeconds}`) and the legacy\n * ISO-8601 format (`ConsentTimestamp:{ISO}`) for backwards compatibility.\n *\n * Returns `null` if the value is malformed, missing required segments, or contains\n * an unrecognised consent status. Callers should treat `null` as \"no valid consent\".\n *\n * @param value - Raw string read from the cookie / localStorage.\n */\nexport function parseConsentCookie(value: string): ParsedConsent | null {\n const parts = value.split('::')\n if (parts.length < 2) return null\n\n const tsPart = parts[0] ?? ''\n let timestamp: string\n\n if (tsPart.startsWith('t:')) {\n // Compact format: t:{epochSeconds}\n const epochStr = tsPart.slice(2)\n const epoch = parseInt(epochStr, 10)\n if (!epochStr || isNaN(epoch)) return null\n timestamp = new Date(epoch * 1000).toISOString()\n } else if (tsPart.startsWith('ConsentTimestamp:')) {\n // Legacy ISO-8601 format\n timestamp = tsPart.replace('ConsentTimestamp:', '')\n if (!timestamp) return null\n } else {\n return null\n }\n\n const consentPart = parts[1] ?? ''\n const sigPart = parts[2]\n\n const consent: ConsentValue = {}\n for (const pair of consentPart.split('|')) {\n const colonIdx = pair.indexOf(':')\n if (colonIdx === -1) return null\n const id = pair.slice(0, colonIdx)\n const status = pair.slice(colonIdx + 1)\n if (!id || (status !== 'granted' && status !== 'denied' && status !== 'objected')) {\n return null\n }\n consent[id] = status\n }\n\n const result: ParsedConsent = { timestamp, consent }\n if (sigPart?.startsWith('sig:')) {\n result.signature = sigPart.slice(4)\n }\n\n return result\n}\n\n/**\n * Manages reading and writing the per-profile consent cookie for a single visitor.\n *\n * One `ConsentStore` instance is created per `ConsentiSetup` and lives for the\n * lifetime of the widget. An in-memory cache (`cachedConsent`, `cachedTimestamp`)\n * prevents repeat cookie reads on every call to `hasConsent()` / `getConsent()`.\n */\nexport class ConsentStore {\n private storage: ConsentStorage\n private cachedConsent: ConsentValue | null = null\n private cachedTimestamp: string | null = null\n private cookies: Cookie[] = []\n\n /**\n * @param userId - Visitor UUID (from `consenti_uid` cookie).\n * @param profileId - Numeric profile ID.\n * @param storageMode - `'cookie'` (default, cross-subdomain) or `'localStorage'` (origin-scoped).\n * @param cookieDomains - Comma-separated domain list. First entry used as `Domain` attribute.\n */\n constructor(\n private userId: string,\n private profileId: number,\n storageMode: 'cookie' | 'localStorage',\n private cookieDomains?: string,\n ) {\n this.storage = new ConsentStorage(storageMode)\n }\n\n /** Set profile cookies so max-age can be derived from the shortest expiry. */\n setProfileCookies(cookies: Cookie[]): void {\n this.cookies = cookies\n }\n\n /** The full cookie / localStorage key for this visitor + profile combination. */\n get cookieName(): string {\n return buildConsentCookieName(this.userId, this.profileId)\n }\n\n /**\n * Reads and parses the stored consent record.\n * Updates the in-memory cache on success.\n *\n * @returns The parsed consent or `null` if absent / malformed.\n */\n read(): ParsedConsent | null {\n const raw = this.storage.read(this.cookieName)\n if (!raw) return null\n const parsed = parseConsentCookie(raw)\n if (parsed) {\n this.cachedConsent = parsed.consent\n this.cachedTimestamp = parsed.timestamp\n }\n return parsed\n }\n\n /**\n * Reads, parses, and cryptographically verifies the stored consent record.\n *\n * If the cookie has a `::sig:{hmac}` segment, the signature is verified against\n * the raw value using HMAC-SHA256. A mismatch means the cookie was tampered with —\n * the cookie is deleted and `null` is returned so the widget re-prompts for consent.\n *\n * If the cookie has no signature segment, it is treated as valid (backwards compatible\n * with cookies written before `signCookies` was enabled).\n *\n * @param signingKey - The HMAC key (from profile API response or `core.cookieSigningKey`).\n * @returns The parsed consent, or `null` if absent / malformed / signature invalid.\n */\n async readAndVerify(signingKey: string): Promise<ParsedConsent | null> {\n const raw = this.storage.read(this.cookieName)\n if (!raw) return null\n\n const parts = raw.split('::')\n if (parts.length < 2) return null\n\n const parsed = parseConsentCookie(raw)\n if (!parsed) return null\n\n const hasSig = parts.length === 3 && (parts[2] ?? '').startsWith('sig:')\n if (hasSig) {\n const { verifyValue } = await import('../utils/hmac')\n const valueToVerify = (parts[0] ?? '') + '::' + (parts[1] ?? '')\n const sig = (parts[2] ?? '').slice(4)\n const valid = await verifyValue(valueToVerify, sig, signingKey)\n if (!valid) {\n logger.warn('Cookie signature mismatch — consent cleared and banner will re-appear')\n this.delete()\n return null\n }\n }\n\n this.cachedConsent = parsed.consent\n this.cachedTimestamp = parsed.timestamp\n return parsed\n }\n\n /**\n * Writes a consent record to storage.\n *\n * @param consent - The consent map to persist.\n * @param signature - Optional pre-computed HMAC hex string. Use `writeAndSign()` instead\n * if you need signing — it guarantees the signature covers the exact\n * value that gets written (timestamps match).\n */\n write(consent: ConsentValue, signature?: string): void {\n let value = buildCookieValue(consent)\n if (signature) {\n value = `${value}::sig:${signature}`\n }\n const opts = this.buildCookieOpts()\n this.storage.write(this.cookieName, value, opts)\n this.cachedConsent = consent\n this.cachedTimestamp = new Date().toISOString()\n }\n\n /**\n * Builds the cookie value, signs it with HMAC-SHA256 in a single atomic step,\n * and writes the signed value to storage.\n *\n * Using this instead of `write(consent, signature)` ensures the signature\n * covers exactly the value that is written — there is no timestamp drift\n * between building, signing, and writing.\n *\n * @param consent - The consent map to persist.\n * @param signingKey - The HMAC key string.\n * @returns The hex-encoded signature, stored in `this.lastSignature` by the caller.\n */\n async writeAndSign(consent: ConsentValue, signingKey: string): Promise<string> {\n const { signValue } = await import('../utils/hmac')\n const rawValue = buildCookieValue(consent)\n const sig = await signValue(rawValue, signingKey)\n const finalValue = `${rawValue}::sig:${sig}`\n const opts = this.buildCookieOpts()\n this.storage.write(this.cookieName, finalValue, opts)\n this.cachedConsent = consent\n this.cachedTimestamp = new Date().toISOString()\n return sig\n }\n\n /** Removes the consent record from storage and clears the in-memory cache. */\n delete(): void {\n const opts = this.buildCookieOpts()\n this.storage.remove(this.cookieName, opts)\n this.cachedConsent = null\n this.cachedTimestamp = null\n }\n\n /** Returns `true` if a valid consent record exists (uses cache when available). */\n hasConsent(): boolean {\n if (this.cachedConsent) return true\n return this.read() !== null\n }\n\n /** Returns the stored consent map, or `null` if no consent has been given. */\n getConsent(): ConsentValue | null {\n if (this.cachedConsent) return this.cachedConsent\n return this.read()?.consent ?? null\n }\n\n /** Returns the `Date` of the last consent submission, or `false` if no consent exists. */\n getConsentDate(): Date | false {\n if (this.cachedTimestamp) return new Date(this.cachedTimestamp)\n const parsed = this.read()\n if (!parsed) return false\n return new Date(parsed.timestamp)\n }\n\n /**\n * Updates the in-memory cache without writing to storage.\n * Used by the cross-tab BroadcastChannel handler to keep other tabs in sync.\n */\n setInMemory(consent: ConsentValue): void {\n this.cachedConsent = consent\n this.cachedTimestamp = new Date().toISOString()\n }\n\n /** Clears the in-memory cache. Used when consent is deleted from another tab. */\n clearInMemory(): void {\n this.cachedConsent = null\n this.cachedTimestamp = null\n }\n\n /**\n * Derives the visitor UUID from an existing consent cookie, or generates a new one\n * in memory.\n *\n * No separate identifier cookie is written before consent. Storing a tracking\n * identifier before the user has given consent violates TTDSG §25 (Germany) and\n * equivalent ePrivacy laws in the EU. The UUID is embedded in the consent cookie\n * name and only persisted when consent is first saved (via `write()` / `writeAndSign()`).\n *\n * @param profileId - The active profile ID, used to locate an existing consent cookie.\n * @param storageMode - The configured storage mode (`'cookie'` or `'localStorage'`).\n */\n static getOrCreateUserId(profileId: number, storageMode: 'cookie' | 'localStorage'): string {\n if (typeof document === 'undefined') return crypto.randomUUID()\n\n // Look for an existing consent cookie matching `consenti_*_{profileId}`.\n const prefix = `${COOKIE_NAME_PREFIX}_`\n const suffix = `_${profileId}`\n const all = storageMode === 'localStorage'\n ? ConsentStore._localStorageKeys()\n : ConsentStore._cookieNames()\n\n for (const name of all) {\n if (name.startsWith(prefix) && name.endsWith(suffix)) {\n const inner = name.slice(prefix.length, name.length - suffix.length)\n // Validate it looks like a UUID (8-4-4-4-12 hex groups).\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(inner)) {\n return inner\n }\n }\n }\n\n return crypto.randomUUID()\n }\n\n /** Returns all document.cookie names. */\n private static _cookieNames(): string[] {\n return document.cookie.split(';').map((c) => c.split('=')[0]?.trim() ?? '')\n }\n\n /** Returns all localStorage keys (empty array when unavailable). */\n private static _localStorageKeys(): string[] {\n if (typeof localStorage === 'undefined') return []\n return Object.keys(localStorage)\n }\n\n private buildCookieOpts(): CookieOptions {\n const opts: CookieOptions = {\n path: '/',\n sameSite: 'Lax',\n maxAge: this.cookies.length > 0 ? resolveConsentMaxAge(this.cookies) : 365 * 24 * 60 * 60,\n }\n if (this.cookieDomains) {\n const domain = this.cookieDomains.split(',')[0]?.trim()\n if (domain) opts.domain = domain\n }\n return opts\n }\n}\n","/**\n * Custom DOM event dispatcher with optional Google Tag Manager integration.\n *\n * Every user-visible action in the widget (banner shown, consent saved, modal opened)\n * dispatches a `CustomEvent` on `window` so host applications can react without\n * coupling to Consenti internals.\n *\n * When `config.utils.gtm` is configured, the same events are also pushed to\n * `window.dataLayer` (or a custom variable name) so GTM triggers can fire.\n *\n * Event names all use the `consenti:` namespace prefix to avoid collisions.\n */\n\nimport type { ConsentValue, GtmConfig, ConsentEvent } from '../types'\nimport { isClient, safeWindow } from '../utils/ssr'\n\n/** All event names dispatched by the widget. */\nexport type EventName =\n | 'consenti:bannerInitialized'\n | 'consenti:bannerVisibility'\n | 'consenti:modalVisibility'\n | 'consenti:consentBeingSubmitted'\n | 'consenti:consentSubmitted'\n | 'consenti:closeRequest'\n\n/**\n * Dispatches `CustomEvent` on `window` and optionally pushes to the GTM dataLayer.\n *\n * One `EventBus` instance is created per `ConsentiSetup` and shared across the\n * banner, modal, and consent submission flows.\n */\nexport class EventBus {\n /**\n * @param profileId - Profile ID included in all GTM pushes.\n * @param gtmConfig - Optional GTM integration settings.\n */\n constructor(\n private profileId: number,\n private gtmConfig?: GtmConfig,\n ) {}\n\n /**\n * Dispatches a `consenti:*` custom event on `window` and, if GTM is configured,\n * pushes the event to `window.dataLayer`.\n *\n * Silently no-ops in SSR contexts.\n *\n * @param name - The event name.\n * @param detail - Event payload attached to `event.detail` and included in the GTM push.\n */\n dispatch(name: EventName, detail: ConsentEvent = {}): void {\n if (!isClient()) return\n const event = new CustomEvent(name, { detail, bubbles: true })\n window.dispatchEvent(event)\n this.pushToGTM(name, detail)\n }\n\n private pushToGTM(name: string, detail: ConsentEvent): void {\n const gtm = this.gtmConfig\n if (!gtm) return\n\n const win = safeWindow() as (Window & { [key: string]: unknown }) | null\n if (!win) return\n\n const layerName = gtm.dataLayer ?? 'dataLayer'\n const layer = win[layerName]\n if (!Array.isArray(layer)) return\n\n const shouldPush = !gtm.events || gtm.events.length === 0 || gtm.events.includes(name)\n if (!shouldPush) return\n\n layer.push({\n event: name,\n consenti: {\n profileId: this.profileId,\n consent: detail.consent,\n consentId: detail.consentId,\n consentAction: detail.consentAction,\n },\n })\n\n // Push Google Consent Mode v2 cookieless-ping flags alongside consent submissions.\n // Only emitted when gtag.js or google_tag_manager is detected on the page to\n // prevent no-op pushes that pollute the dataLayer on non-Google sites.\n if (name === 'consenti:consentSubmitted' && this.hasGtag(win)) {\n if (gtm.urlPassthrough) {\n layer.push({ url_passthrough: true })\n }\n if (gtm.adsDataRedaction) {\n // ads_data_redaction: true tells Google to strip identifying data from ad pings\n // when the user has denied ad_storage.\n const adConsentDenied = detail.consent?.['ad_storage'] !== 'granted'\n layer.push({ ads_data_redaction: adConsentDenied })\n }\n }\n }\n\n /**\n * Returns `true` when gtag.js or Google Tag Manager is detected on the page.\n * Used to gate GCM v2 cookieless-ping pushes so they are not emitted when\n * Google services are absent.\n */\n private hasGtag(win: Window & { [key: string]: unknown }): boolean {\n return typeof win['gtag'] === 'function' || typeof win['google_tag_manager'] !== 'undefined'\n }\n}\n\n// Re-export so callers do not need a separate import from types\nexport type { ConsentValue }\n","import { DeepPartial } from '@consenti/types';\n\n/**\n * Locale resolution and deep-merge utilities.\n *\n * Consenti resolves translations in three steps:\n * 1. Exact match — `'fr-CA'` → use `translations['fr-CA']`\n * 2. Language prefix — `'fr-CA'` → try `translations['fr']` if `'fr-CA'` is absent\n * 3. Default locale — fall back to `translations[defaultLocale]`\n *\n * Missing keys in the resolved locale are filled from the default locale via `deepMerge`,\n * so partial translations are safe — only the keys that differ need to be specified.\n */\n\nimport type { ProfileTranslations, LocaleTranslations } from '../types'\n\n/**\n * Resolves the best-matching `LocaleTranslations` for a requested locale.\n *\n * The result is a deep merge of the default locale (base) and the resolved locale\n * (override), so any missing keys in the override are inherited from the base.\n *\n * @param translations - All available locale translations from a `ProfileConfig`.\n * @param requested - The desired locale (e.g. `'fr-CA'`).\n * @param defaultLocale - The fallback locale key (e.g. `'en'`). Must exist in `translations`.\n * @throws `Error` if `defaultLocale` is not found in `translations`.\n */\nexport function resolveLocale(\n translations: ProfileTranslations,\n requested: string,\n defaultLocale: string,\n): LocaleTranslations {\n const base = translations[defaultLocale]\n if (!base) {\n throw new Error(`[Consenti] No translations found for defaultLocale \"${defaultLocale}\"`)\n }\n\n // 1. Exact match\n if (translations[requested]) {\n return deepMerge(base, translations[requested])\n }\n\n // 2. Language prefix (e.g. 'fr' from 'fr-CA')\n const lang = requested.split('-')[0] ?? ''\n if (lang !== requested && translations[lang]) {\n return deepMerge(base, translations[lang])\n }\n\n // 3. Default locale fallback\n return base\n}\n\n/**\n * Recursively merges `override` into `base`, returning a new object.\n *\n * Rules:\n * - `undefined` and `null` values in `override` are skipped (base values are kept).\n * - Arrays are replaced wholesale — individual array items are not merged.\n * - Plain objects are merged recursively.\n * - All other values are replaced by the override value.\n *\n * @param base - The base object (default locale translations).\n * @param override - The partial override (requested locale translations).\n */\nexport function deepMerge<T extends object>(base: T, override: DeepPartial<T>): T {\n const result = { ...base } as Record<string, T[keyof T]>\n\n for (const key in override) {\n const overrideVal = override[key]\n if (overrideVal === undefined || overrideVal === null) continue\n\n const baseVal = result[key]\n\n if (\n typeof overrideVal === 'object' &&\n !Array.isArray(overrideVal) &&\n typeof baseVal === 'object' &&\n baseVal !== null &&\n !Array.isArray(baseVal)\n ) {\n result[key] = deepMerge(\n baseVal as T[keyof T] & object,\n overrideVal as DeepPartial<T[keyof T] & object>,\n ) as T[keyof T]\n } else {\n result[key] = overrideVal as T[keyof T]\n }\n }\n\n return result as T\n}\n","export function getBrowserGeoHints(): { tz: string; lang: string; langs: string[] } {\n return {\n tz: Intl.DateTimeFormat().resolvedOptions().timeZone,\n lang: navigator.language,\n langs: Array.from(navigator.languages),\n }\n}\n","/**\n * Single source of truth for all compliance groups, regulations, and country mappings.\n *\n * This is DATA, not types. The `Compliance` and `ComplianceGroupId` literal types\n * derived from the ID arrays below live in @consenti/types (packages/types/src/compliance.ts),\n * via a type-only import — that keeps @consenti/types free of runtime code.\n *\n * ─── HOW TO UPDATE ────────────────────────────────────────────────────────────\n *\n * ADD A NEW REGULATION:\n * 1. Add regulation ID string to `COMPLIANCE_REGULATION_IDS` below\n * (the `Compliance` type in @consenti/types auto-updates from it).\n * 2. Add it to the appropriate group's `compliances` array below.\n * 3. Add / update the country entry in `EMBEDDED_COMPLIANCE_MAP` below.\n * That is all — no other files need touching.\n *\n * ADD A NEW COMPLIANCE GROUP:\n * 1. Add the kebab-case ID to `COMPLIANCE_GROUP_IDS` below.\n * 2. Add a full group entry to `COMPLIANCE_GROUPS` below (label, compliances, flags).\n * 3. Add a validation rule set to `COMPLIANCE_VALIDATION_RULES` below.\n * 4. Map affected countries in `EMBEDDED_COMPLIANCE_MAP` to the new group.\n *\n * ADD OR UPDATE A COUNTRY:\n * Edit the entry in `EMBEDDED_COMPLIANCE_MAP.countries` below. That is the ONLY\n * place that needs changing for country-level jurisdiction updates.\n */\n\n// ─── Regulation IDs ───────────────────────────────────────────────────────────\n\nexport const COMPLIANCE_REGULATION_IDS = [\n // European Union\n 'gdpr', 'eprivacy',\n // United Kingdom\n 'uk-gdpr', 'pecr',\n // Switzerland\n 'revfadp',\n // Turkey\n 'kvkk',\n // North America — Canada\n 'pipeda', 'law25',\n // North America — US (California)\n 'ccpa', 'cpra',\n // North America — US state laws\n 'vcdpa', 'cpa-co', 'ctdpa', 'ucpa', 'icdpa-ia', 'icdpa-in', 'tipa',\n 'mcdpa-mt', 'mcdpa-mn', 'ocpa', 'tdpsa', 'dpdpa-de', 'njdpa', 'nhpa',\n 'ndpa-ne', 'modpa', 'kcdpa', 'ridtpa',\n // Latin America\n 'lgpd', 'lfpdppp', 'lpdp-ar', 'lpdp-pe', 'law1581-co', 'law18331-uy',\n 'law19628-cl', 'lopdp-ec', 'lpdp-cr', 'data-protection-pa', 'data-protection-do',\n // Asia — India\n 'dpdpa',\n // Asia — China\n 'pipl', 'dsl-cn', 'csl-cn',\n // Asia — Pacific\n 'appi', 'pipa-kr', 'pdpa-sg', 'pdpa-th', 'pdpa-my', 'dpa-ph', 'pdp-id',\n 'pdpd-vn', 'privacy-au', 'privacy-nz', 'pdpl-hk', 'pdpa-mo', 'pdpl-tw',\n 'privacy-bn', 'privacy-lk', 'privacy-np',\n // Middle East\n 'pdpl-sa', 'uae-pdpl', 'difc-dpl', 'adgm-dpr', 'qpdl-qa', 'pdpl-bh',\n 'pdpl-om', 'privacy-kw', 'privacy-il',\n // Africa\n 'popia', 'ndpa-ng', 'dpa-ke', 'dpa-gh', 'dpa-ug', 'dpa-rw', 'dpa-mu',\n 'dpa-bw', 'dpa-zm', 'dpa-zw', 'dpa-eg', 'dpa-ma', 'dpa-tn', 'dpa-dz',\n // Oceania\n 'privacy-fj', 'privacy-pg',\n] as const\n\n// ─── Compliance Group IDs ─────────────────────────────────────────────────────\n\nexport const COMPLIANCE_GROUP_IDS = [\n 'opt-in',\n 'opt-out',\n 'opt-out-strict',\n 'opt-in-dpdpa',\n 'opt-in-china',\n 'opt-in-brazil',\n 'general-privacy-consent',\n 'notice-only',\n] as const\n\n// Local alias for the shape checks below. The public `ComplianceGroupId` type\n// (derived from the same array) is re-exported from @consenti/types.\ntype ComplianceGroupId = typeof COMPLIANCE_GROUP_IDS[number]\n\n// ─── Compliance Groups ────────────────────────────────────────────────────────\n\nexport const COMPLIANCE_GROUPS = {\n 'opt-out-strict': {\n label: 'Opt-out Strict (CPRA / California)',\n description: 'California CPRA: opt-out with sale/sharing/sensitive categories required. GPC must be honored. Stricter than general US state laws.',\n compliances: ['cpra'],\n defaultGpc: 'honor',\n gpcAutoHonor: true,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: true,\n requiresDpdpaDisclosure: false,\n },\n 'opt-out': {\n label: 'Opt-out (US State Laws)',\n description: 'US state privacy laws. Collect first, allow opt-out of sale/share. Covers CCPA and all state-level equivalents.',\n compliances: [\n 'ccpa',\n 'vcdpa', 'cpa-co', 'ctdpa', 'ucpa', 'icdpa-ia', 'icdpa-in', 'tipa',\n 'mcdpa-mt', 'mcdpa-mn', 'ocpa', 'tdpsa', 'dpdpa-de', 'njdpa', 'nhpa',\n 'ndpa-ne', 'modpa', 'kcdpa', 'ridtpa',\n ],\n defaultGpc: 'honor',\n gpcAutoHonor: true,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'notice-only': {\n label: 'Notice Only',\n description: 'A privacy notice is generally sufficient. No dedicated cookie banner law is encoded for this jurisdiction.',\n compliances: ['privacy-fj', 'privacy-pg'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'opt-in': {\n label: 'Standard Opt-in (GDPR)',\n description: 'GDPR / ePrivacy style: prior consent required before non-essential cookies. Granular consent, easy reject, easy withdrawal.',\n compliances: [\n 'gdpr', 'eprivacy', 'uk-gdpr', 'pecr', 'revfadp', 'kvkk', 'pdpa-th',\n 'pdpl-sa', 'uae-pdpl', 'difc-dpl', 'adgm-dpr', 'qpdl-qa', 'pdpl-bh', 'pdpl-om',\n 'law25',\n ],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: true,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'opt-in-dpdpa': {\n label: 'Opt-in (India DPDPA)',\n description: \"India's Digital Personal Data Protection Act. Opt-in consent with data fiduciary disclosure. No legitimate interest basis.\",\n compliances: ['dpdpa'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: true,\n },\n 'opt-in-china': {\n label: 'Opt-in (China PIPL / DSL)',\n description: 'China PIPL + DSL + CSL: strict opt-in with purpose specification, data minimisation, and cross-border transfer controls.',\n compliances: ['pipl', 'dsl-cn', 'csl-cn'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'opt-in-brazil': {\n label: 'Opt-in (Brazil LGPD)',\n description: 'Brazil LGPD: opt-in consent required. Legitimate interest is a valid basis. Analytics and advertising may differ in treatment.',\n compliances: ['lgpd'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'general-privacy-consent': {\n label: 'General Privacy Consent',\n description: 'Privacy notice or consent rules exist but the jurisdiction does not have a GDPR/PECR-style strict cookie banner law. Documenting legal basis is best practice.',\n compliances: [\n 'pipeda',\n 'popia', 'appi', 'pipa-kr',\n 'pdpa-sg', 'pdpa-my', 'dpa-ph', 'pdp-id', 'pdpd-vn',\n 'privacy-au', 'privacy-nz', 'pdpl-hk', 'pdpa-mo', 'pdpl-tw',\n 'privacy-bn', 'privacy-lk', 'privacy-np', 'privacy-kw', 'privacy-il',\n 'ndpa-ng', 'dpa-ke', 'dpa-gh', 'dpa-ug', 'dpa-rw', 'dpa-mu',\n 'dpa-bw', 'dpa-zm', 'dpa-zw', 'dpa-eg', 'dpa-ma', 'dpa-tn', 'dpa-dz',\n 'lfpdppp', 'lpdp-ar', 'lpdp-pe', 'law1581-co', 'law18331-uy',\n 'law19628-cl', 'lopdp-ec', 'lpdp-cr', 'data-protection-pa', 'data-protection-do',\n ],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n} satisfies Record<ComplianceGroupId, {\n label: string\n description: string\n compliances: readonly string[]\n defaultGpc: 'ignore' | 'honor' | 'strict'\n gpcAutoHonor: boolean\n allowsLegitimateInterest: boolean\n allowsTcf: boolean\n requiresCpraCategory: boolean\n requiresDpdpaDisclosure: boolean\n}>\n\n// ─── GPC Options ──────────────────────────────────────────────────────────────\n\nexport const GPC_OPTIONS = [\n { value: 'ignore', label: 'Ignore', description: 'Do not check or respond to the GPC browser signal.' },\n { value: 'honor', label: 'Honor', description: 'Respect GPC as a valid opt-out signal. Show GPC variant banner once.' },\n { value: 'strict', label: 'Strict', description: 'Silently deny all non-mandatory cookies when GPC is detected. No banner shown.' },\n]\n\n// ─── Legal Basis Options ──────────────────────────────────────────────────────\n\nexport const LEGAL_BASIS_OPTIONS = [\n { value: 'mandatory', label: 'Strictly Necessary', description: 'Required for the service to function. No consent needed.' },\n { value: 'consent', label: 'Consent', description: 'Requires explicit user agreement. User can withdraw at any time.' },\n { value: 'legitimate_interest', label: 'Legitimate Interest', description: 'Based on legitimate business interest. User has the right to object.' },\n]\n\n// ─── CPRA Categories ──────────────────────────────────────────────────────────\n\nexport const CPRA_CATEGORIES = [\n { value: 'sale', label: 'Sale', description: 'Sale of personal data to third parties for their own use.' },\n { value: 'sharing', label: 'Sharing', description: 'Sharing for cross-context behavioral advertising (even without payment).' },\n { value: 'sensitive', label: 'Sensitive', description: 'Sensitive personal information (geolocation, health, race, biometrics, etc.). Requires opt-in even in California.' },\n]\n\n// ─── TCF Purposes (IAB TCF 2.2) ──────────────────────────────────────────────\n\nexport const TCF_PURPOSES = [\n { id: 1, name: 'Store and/or access information on a device', description: \"Cookies, device identifiers, or other information can be stored or accessed on your device.\" },\n { id: 2, name: 'Select basic ads', description: \"Ads can be shown based on the content you're viewing, the app you're using, or your approximate location.\" },\n { id: 3, name: 'Create a personalised ads profile', description: \"A profile can be built about you and your interests to show you personalised ads.\" },\n { id: 4, name: 'Select personalised ads', description: \"Personalised ads can be shown to you based on a profile about you.\" },\n { id: 5, name: 'Create a personalised content profile', description: \"A profile can be built about you and your interests to show you personalised content.\" },\n { id: 6, name: 'Select personalised content', description: \"Personalised content can be shown to you based on a profile about you.\" },\n { id: 7, name: 'Measure ad performance', description: \"The performance and effectiveness of ads that you see or interact with can be measured.\" },\n { id: 8, name: 'Measure content performance', description: \"The performance and effectiveness of content that you see or interact with can be measured.\" },\n { id: 9, name: 'Apply market research to generate audience insights', description: \"Market research can be used to learn more about the audiences who visit sites or use apps.\" },\n { id: 10, name: 'Develop and improve products', description: \"Your data can be used to improve existing systems and software, and to develop new products.\" },\n { id: 11, name: 'Use limited data to select content', description: \"Content can be selected based on limited data such as the website you are visiting.\" },\n]\n\nexport const TCF_SPECIAL_FEATURES = [\n { id: 1, name: 'Use precise geolocation data', description: \"Your precise location (within a radius of less than 500 metres) can be used.\" },\n { id: 2, name: 'Actively scan device characteristics for identification', description: \"Your device can be identified based on a scan of your device's unique combination of characteristics.\" },\n]\n\n// ─── Compliance Validation Rules ──────────────────────────────────────────────\n// check() returns true when a violation exists (i.e., the rule is broken)\n\nexport const COMPLIANCE_VALIDATION_RULES: Record<string, {\n errors: Array<{\n rule: string\n field: string\n check: (cookie: Record<string, unknown>) => boolean\n message: (cookieId: string) => string\n }>\n warnings: Array<{\n rule: string\n field: string\n check: (cookie: Record<string, unknown>) => boolean\n message: (cookieId: string) => string\n suggestion: string\n }>\n}> = {\n 'opt-in': {\n errors: [\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Cookie \"${id}\" must have a legal basis set (Consent, Legitimate Interest, or Strictly Necessary) for GDPR compliance.`,\n },\n {\n rule: 'li-description-required',\n field: 'legitimateInterest.description',\n check: (c) => c['legalBasis'] === 'legitimate_interest' && !(c['legitimateInterest'] as Record<string, unknown> | undefined)?.['description'],\n message: (id) => `Cookie \"${id}\" uses Legitimate Interest but has no balancing test description. GDPR requires documenting your LI assessment.`,\n },\n ],\n warnings: [\n {\n rule: 'li-description-recommended',\n field: 'legitimateInterest.description',\n check: (c) => c['legalBasis'] === 'legitimate_interest' && !(c['legitimateInterest'] as Record<string, unknown> | undefined)?.['description'],\n message: (id) => `Cookie \"${id}\": adding a Legitimate Interest description improves transparency and is a GDPR best practice.`,\n suggestion: 'Add a brief description of your legitimate interest balancing test.',\n },\n ],\n },\n\n 'opt-out': {\n errors: [],\n warnings: [\n {\n rule: 'gpc-recommended',\n field: 'listenGpc',\n check: (c) => c['legalBasis'] !== 'mandatory' && !c['listenGpc'],\n message: (id) => `Cookie \"${id}\" does not listen for the GPC signal. US state privacy laws require honoring GPC for opt-out of sale/sharing.`,\n suggestion: 'Enable \"Listen for GPC signal\" on this cookie.',\n },\n ],\n },\n\n 'opt-out-strict': {\n errors: [\n {\n rule: 'cpra-category-required',\n field: 'cpraCategory',\n check: (c) => c['legalBasis'] !== 'mandatory' && !c['cpraCategory'],\n message: (id) => `Cookie \"${id}\" must have a CPRA category (Sale, Sharing, or Sensitive) assigned. Required under CPRA for all non-necessary cookies.`,\n },\n ],\n warnings: [\n {\n rule: 'gpc-strict-required',\n field: 'listenGpc',\n check: (c) => c['legalBasis'] !== 'mandatory' && !c['listenGpc'],\n message: (id) => `Cookie \"${id}\" does not listen for the GPC signal. Under CPRA, non-mandatory cookies must honor GPC as a Do Not Sell/Share signal.`,\n suggestion: 'Enable \"Listen for GPC signal\" on this cookie.',\n },\n ],\n },\n\n 'opt-in-dpdpa': {\n errors: [\n {\n rule: 'no-li-allowed',\n field: 'legalBasis',\n check: (c) => c['legalBasis'] === 'legitimate_interest',\n message: (id) => `Cookie \"${id}\" uses Legitimate Interest, which is not a valid legal basis under India's DPDPA. Use Consent or Strictly Necessary.`,\n },\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Cookie \"${id}\" must have a legal basis set (Consent or Strictly Necessary) for DPDPA compliance.`,\n },\n ],\n warnings: [],\n },\n\n 'opt-in-china': {\n errors: [\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Cookie \"${id}\" must have a legal basis set for China PIPL compliance. Consent is the primary valid basis.`,\n },\n ],\n warnings: [\n {\n rule: 'li-limited-under-pipl',\n field: 'legalBasis',\n check: (c) => c['legalBasis'] === 'legitimate_interest',\n message: (id) => `Cookie \"${id}\" uses Legitimate Interest. Under China's PIPL, LI is narrowly scoped and data processing purposes must be strictly documented.`,\n suggestion: 'Consider using Consent for this cookie to ensure PIPL compliance. Document the specific legitimate interest basis if retaining LI.',\n },\n ],\n },\n\n 'opt-in-brazil': {\n errors: [\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Cookie \"${id}\" must have a legal basis set. Brazil's LGPD requires one of ten legal bases; Consent or Legitimate Interest are most common for cookies.`,\n },\n ],\n warnings: [\n {\n rule: 'li-description-recommended',\n field: 'legitimateInterest.description',\n check: (c) => c['legalBasis'] === 'legitimate_interest' && !(c['legitimateInterest'] as Record<string, unknown> | undefined)?.['description'],\n message: (id) => `Cookie \"${id}\" uses Legitimate Interest. LGPD requires documenting the basis and conducting an impact assessment.`,\n suggestion: 'Add a Legitimate Interest description to document the LGPD basis and your impact assessment.',\n },\n ],\n },\n\n 'general-privacy-consent': {\n errors: [],\n warnings: [\n {\n rule: 'legal-basis-recommended',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Cookie \"${id}\" has no legal basis documented. While a strict cookie banner may not be legally required in this jurisdiction, documenting legal basis is a privacy best practice.`,\n suggestion: 'Set a legal basis to improve compliance documentation and future-proof against stricter enforcement.',\n },\n ],\n },\n\n 'notice-only': {\n errors: [],\n warnings: [],\n },\n}\n\n// ─── Embedded Jurisdiction → Compliance Group Map ─────────────────────────────\n// Version tag: update when regulations or mappings change materially.\n// HOW TO UPDATE: edit or add a country entry below. That is the ONLY file change needed.\n//\n// locales: BCP 47 tags for the most commonly used web/IT locales in that country.\n// Only well-supported, widely-used locales are listed. English-speaking countries\n// use 'en' (no country suffix). No country-specific English variants (en-XX) anywhere.\n\nexport const EMBEDDED_COMPLIANCE_MAP = {\n version: '2026-07',\n countries: {\n // ─── Europe — EU 27 ───────────────────────────────────────────────────────\n AT: { name: 'Austria', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Austria (GDPR)', locales: ['de-AT'] },\n BE: { name: 'Belgium', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Belgium (GDPR)', locales: ['nl-BE', 'fr-BE'] },\n BG: { name: 'Bulgaria', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Bulgaria (GDPR)', locales: ['bg-BG'] },\n HR: { name: 'Croatia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Croatia (GDPR)', locales: ['hr-HR'] },\n CY: { name: 'Cyprus', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Cyprus (GDPR)', locales: ['el-CY'] },\n CZ: { name: 'Czechia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Czechia (GDPR)', locales: ['cs-CZ'] },\n DK: { name: 'Denmark', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Denmark (GDPR)', locales: ['da-DK'] },\n EE: { name: 'Estonia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Estonia (GDPR)', locales: ['et-EE'] },\n FI: { name: 'Finland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Finland (GDPR)', locales: ['fi-FI'] },\n FR: { name: 'France', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'France (GDPR)', locales: ['fr-FR'] },\n DE: { name: 'Germany', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Germany (GDPR)', locales: ['de-DE'] },\n GR: { name: 'Greece', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Greece (GDPR)', locales: ['el-GR'] },\n HU: { name: 'Hungary', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Hungary (GDPR)', locales: ['hu-HU'] },\n IE: { name: 'Ireland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Ireland (GDPR)', locales: ['en'] },\n IT: { name: 'Italy', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Italy (GDPR)', locales: ['it-IT'] },\n LV: { name: 'Latvia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Latvia (GDPR)', locales: ['lv-LV'] },\n LT: { name: 'Lithuania', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Lithuania (GDPR)', locales: ['lt-LT'] },\n LU: { name: 'Luxembourg', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Luxembourg (GDPR)', locales: ['fr-LU', 'de-LU'] },\n MT: { name: 'Malta', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Malta (GDPR)', locales: ['mt-MT', 'en'] },\n NL: { name: 'Netherlands', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Netherlands (GDPR)', locales: ['nl-NL'] },\n PL: { name: 'Poland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Poland (GDPR)', locales: ['pl-PL'] },\n PT: { name: 'Portugal', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Portugal (GDPR)', locales: ['pt-PT'] },\n RO: { name: 'Romania', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Romania (GDPR)', locales: ['ro-RO'] },\n SK: { name: 'Slovakia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Slovakia (GDPR)', locales: ['sk-SK'] },\n SI: { name: 'Slovenia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Slovenia (GDPR)', locales: ['sl-SI'] },\n ES: { name: 'Spain', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Spain (GDPR)', locales: ['es-ES'] },\n SE: { name: 'Sweden', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Sweden (GDPR)', locales: ['sv-SE'] },\n // ─── Europe — EEA non-EU ──────────────────────────────────────────────────\n IS: { name: 'Iceland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Iceland (GDPR via EEA)', locales: ['is-IS'] },\n LI: { name: 'Liechtenstein', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Liechtenstein (GDPR via EEA)', locales: ['de-LI'] },\n NO: { name: 'Norway', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Norway (GDPR via EEA)', locales: ['nb-NO'] },\n // ─── Europe — GDPR-aligned candidate/observer countries ──────────────────\n AD: { name: 'Andorra', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Andorra (GDPR-aligned)', locales: ['ca-AD'] },\n AL: { name: 'Albania', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Albania (GDPR-aligned)', locales: ['sq-AL'] },\n BA: { name: 'Bosnia and Herzegovina', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Bosnia and Herzegovina (GDPR-aligned)', locales: ['bs-BA', 'sr-Latn-BA'] },\n GG: { name: 'Guernsey', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Guernsey (DPA based on GDPR)', locales: ['en'] },\n GI: { name: 'Gibraltar', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Gibraltar (DPA based on UK GDPR)', locales: ['en'] },\n GL: { name: 'Greenland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Greenland (Danish GDPR implementation)', locales: ['da-GL'] },\n MC: { name: 'Monaco', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Monaco (GDPR-aligned)', locales: ['fr-MC'] },\n ME: { name: 'Montenegro', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Montenegro (GDPR-aligned)', locales: ['sr-Latn-ME'] },\n MK: { name: 'North Macedonia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'North Macedonia (GDPR-aligned)', locales: ['mk-MK'] },\n RS: { name: 'Serbia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Serbia (GDPR-aligned)', locales: ['sr-RS'] },\n SM: { name: 'San Marino', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'San Marino (GDPR-aligned)', locales: ['it-SM'] },\n TR: { name: 'Türkiye', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Türkiye (KVKK)', locales: ['tr-TR'] },\n // ─── North America ────────────────────────────────────────────────────────\n CA: {\n name: 'Canada',\n type: 'country',\n complianceGroup: 'general-privacy-consent',\n default: 'general-privacy-consent',\n description: 'Canada (PIPEDA / Law 25)',\n locales: ['en', 'fr-CA'],\n overriddenRegions: {\n QC: { name: 'Quebec', type: 'region', complianceGroup: 'opt-in', description: 'Quebec — Law 25 (GDPR-style opt-in)' },\n },\n },\n US: {\n name: 'United States',\n type: 'country',\n // complianceGroup: base when state known but has no specific law\n // default: used when state CANNOT be detected (use most strict = opt-out-strict)\n complianceGroup: 'opt-out',\n default: 'opt-out-strict',\n description: 'United States — opt-out-strict (CPRA) used when state cannot be detected',\n locales: ['en', 'es-US'],\n overriddenRegions: {\n CA: { name: 'California', type: 'region', complianceGroup: 'opt-out-strict', description: 'California — CPRA/CCPA' },\n VA: { name: 'Virginia', type: 'region', complianceGroup: 'opt-out', description: 'Virginia — VCDPA' },\n CO: { name: 'Colorado', type: 'region', complianceGroup: 'opt-out', description: 'Colorado — CPA' },\n CT: { name: 'Connecticut', type: 'region', complianceGroup: 'opt-out', description: 'Connecticut — CTDPA' },\n UT: { name: 'Utah', type: 'region', complianceGroup: 'opt-out', description: 'Utah — UCPA' },\n TX: { name: 'Texas', type: 'region', complianceGroup: 'opt-out', description: 'Texas — TDPSA' },\n DE: { name: 'Delaware', type: 'region', complianceGroup: 'opt-out', description: 'Delaware — DPDPA' },\n NJ: { name: 'New Jersey', type: 'region', complianceGroup: 'opt-out', description: 'New Jersey — NJDPA' },\n OR: { name: 'Oregon', type: 'region', complianceGroup: 'opt-out', description: 'Oregon — OCPA' },\n MN: { name: 'Minnesota', type: 'region', complianceGroup: 'opt-out', description: 'Minnesota — MCDPA' },\n MD: { name: 'Maryland', type: 'region', complianceGroup: 'opt-out', description: 'Maryland — MODPA' },\n KY: { name: 'Kentucky', type: 'region', complianceGroup: 'opt-out', description: 'Kentucky — KCDPA' },\n TN: { name: 'Tennessee', type: 'region', complianceGroup: 'opt-out', description: 'Tennessee — TIPA' },\n NE: { name: 'Nebraska', type: 'region', complianceGroup: 'opt-out', description: 'Nebraska — NDPA' },\n IA: { name: 'Iowa', type: 'region', complianceGroup: 'opt-out', description: 'Iowa — ICDPA' },\n IN: { name: 'Indiana', type: 'region', complianceGroup: 'opt-out', description: 'Indiana — ICDPA' },\n MT: { name: 'Montana', type: 'region', complianceGroup: 'opt-out', description: 'Montana — MCDPA' },\n NH: { name: 'New Hampshire', type: 'region', complianceGroup: 'opt-out', description: 'New Hampshire — NHPA' },\n RI: { name: 'Rhode Island', type: 'region', complianceGroup: 'opt-out', description: 'Rhode Island — RIDTPA' },\n },\n },\n // ─── Latin America ────────────────────────────────────────────────────────\n AR: { name: 'Argentina', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Argentina (LPDP)', locales: ['es-AR'] },\n BO: { name: 'Bolivia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Bolivia', locales: ['es-BO'] },\n BR: { name: 'Brazil', type: 'country', complianceGroup: 'opt-in-brazil', default: 'opt-in-brazil', description: 'Brazil (LGPD)', locales: ['pt-BR'] },\n CL: { name: 'Chile', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Chile (Law 19.628)', locales: ['es-CL'] },\n CO: { name: 'Colombia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Colombia (Law 1581)', locales: ['es-CO'] },\n CR: { name: 'Costa Rica', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Costa Rica (LPDP)', locales: ['es-CR'] },\n DO: { name: 'Dominican Republic', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Dominican Republic', locales: ['es-DO'] },\n EC: { name: 'Ecuador', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Ecuador (LOPDP)', locales: ['es-EC'] },\n GF: { name: 'French Guiana', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'French Guiana', locales: ['fr-GF'] },\n GT: { name: 'Guatemala', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guatemala', locales: ['es-GT'] },\n GY: { name: 'Guyana', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guyana', locales: ['en'] },\n HN: { name: 'Honduras', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Honduras', locales: ['es-HN'] },\n HT: { name: 'Haiti', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Haiti', locales: ['fr-HT'] },\n JM: { name: 'Jamaica', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Jamaica', locales: ['en'] },\n MX: { name: 'Mexico', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Mexico (LFPDPPP)', locales: ['es-MX'] },\n NI: { name: 'Nicaragua', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Nicaragua', locales: ['es-NI'] },\n PA: { name: 'Panama', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Panama', locales: ['es-PA'] },\n PE: { name: 'Peru', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Peru (LPDP)', locales: ['es-PE'] },\n PY: { name: 'Paraguay', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Paraguay', locales: ['es-PY'] },\n SR: { name: 'Suriname', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Suriname', locales: ['nl-SR'] },\n SV: { name: 'El Salvador', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'El Salvador', locales: ['es-SV'] },\n TT: { name: 'Trinidad and Tobago', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Trinidad and Tobago', locales: ['en'] },\n UY: { name: 'Uruguay', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Uruguay (Law 18.331)', locales: ['es-UY'] },\n VE: { name: 'Venezuela', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Venezuela', locales: ['es-VE'] },\n // ─── Caribbean ────────────────────────────────────────────────────────────\n AG: { name: 'Antigua and Barbuda', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Antigua and Barbuda', locales: ['en'] },\n BB: { name: 'Barbados', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Barbados', locales: ['en'] },\n BL: { name: 'Saint Barthélemy', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Barthélemy', locales: ['fr-BL'] },\n BM: { name: 'Bermuda', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Bermuda', locales: ['en'] },\n BS: { name: 'Bahamas', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Bahamas', locales: ['en'] },\n CU: { name: 'Cuba', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cuba', locales: ['es-CU'] },\n DM: { name: 'Dominica', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Dominica', locales: ['en'] },\n GD: { name: 'Grenada', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Grenada', locales: ['en'] },\n KN: { name: 'Saint Kitts and Nevis', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Kitts and Nevis', locales: ['en'] },\n LC: { name: 'Saint Lucia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Lucia', locales: ['en'] },\n PM: { name: 'Saint Pierre and Miquelon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Pierre and Miquelon', locales: ['fr-PM'] },\n PR: { name: 'Puerto Rico', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Puerto Rico', locales: ['es-PR', 'en'] },\n VC: { name: 'Saint Vincent and the Grenadines', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Vincent and the Grenadines', locales: ['en'] },\n // ─── Asia — India ─────────────────────────────────────────────────────────\n IN: { name: 'India', type: 'country', complianceGroup: 'opt-in-dpdpa', default: 'opt-in-dpdpa', description: 'India (DPDPA)', locales: ['hi-IN', 'en', 'bn-IN', 'ta-IN'] },\n // ─── Asia — China ─────────────────────────────────────────────────────────\n CN: { name: 'China', type: 'country', complianceGroup: 'opt-in-china', default: 'opt-in-china', description: 'China (PIPL + DSL + CSL)', locales: ['zh-CN'] },\n // ─── Asia — Southeast Asia ────────────────────────────────────────────────\n BN: { name: 'Brunei', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Brunei (Personal Data Protection Order)', locales: ['ms-BN', 'en'] },\n ID: { name: 'Indonesia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Indonesia (PDP Law)', locales: ['id-ID'] },\n KH: { name: 'Cambodia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cambodia', locales: ['km-KH'] },\n LA: { name: 'Laos', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Laos', locales: ['lo-LA'] },\n MM: { name: 'Myanmar', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Myanmar', locales: ['my-MM'] },\n MY: { name: 'Malaysia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Malaysia (PDPA)', locales: ['ms-MY', 'zh-MY', 'en'] },\n PH: { name: 'Philippines', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Philippines (DPA)', locales: ['fil-PH', 'en'] },\n SG: { name: 'Singapore', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Singapore (PDPA)', locales: ['zh-SG', 'ms-SG', 'ta-SG', 'en'] },\n TH: { name: 'Thailand', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Thailand (PDPA — GDPR-style opt-in)', locales: ['th-TH'] },\n TL: { name: 'Timor-Leste', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Timor-Leste', locales: ['pt-TL'] },\n VN: { name: 'Vietnam', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Vietnam (PDPD)', locales: ['vi-VN'] },\n // ─── Asia — East Asia ─────────────────────────────────────────────────────\n HK: { name: 'Hong Kong', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Hong Kong SAR (PDPO)', locales: ['zh-HK'] },\n JP: { name: 'Japan', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Japan (APPI)', locales: ['ja-JP'] },\n KP: { name: 'North Korea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'North Korea', locales: ['ko-KP'] },\n KR: { name: 'South Korea', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'South Korea (PIPA)', locales: ['ko-KR'] },\n MN: { name: 'Mongolia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mongolia', locales: ['mn-MN'] },\n MO: { name: 'Macau', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Macau SAR (PDPA)', locales: ['zh-MO', 'pt-MO'] },\n TW: { name: 'Taiwan', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Taiwan (PDPL)', locales: ['zh-TW'] },\n // ─── Asia — South Asia ────────────────────────────────────────────────────\n AF: { name: 'Afghanistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Afghanistan', locales: ['fa-AF'] },\n BD: { name: 'Bangladesh', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Bangladesh', locales: ['bn-BD'] },\n BT: { name: 'Bhutan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Bhutan', locales: ['dz-BT'] },\n IO: { name: 'British Indian Ocean Territory', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'British Indian Ocean Territory', locales: ['en'] },\n LK: { name: 'Sri Lanka', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Sri Lanka', locales: ['si-LK', 'ta-LK'] },\n MV: { name: 'Maldives', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Maldives', locales: ['dv-MV'] },\n NP: { name: 'Nepal', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Nepal', locales: ['ne-NP'] },\n PK: { name: 'Pakistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Pakistan', locales: ['ur-PK', 'en'] },\n // ─── Asia — Central Asia ──────────────────────────────────────────────────\n AM: { name: 'Armenia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Armenia', locales: ['hy-AM'] },\n AZ: { name: 'Azerbaijan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Azerbaijan', locales: ['az-AZ'] },\n GE: { name: 'Georgia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Georgia', locales: ['ka-GE'] },\n KG: { name: 'Kyrgyzstan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Kyrgyzstan', locales: ['ky-KG', 'ru-KG'] },\n KZ: { name: 'Kazakhstan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Kazakhstan', locales: ['kk-KZ', 'ru-KZ'] },\n TJ: { name: 'Tajikistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Tajikistan', locales: ['tg-TJ'] },\n TM: { name: 'Turkmenistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Turkmenistan', locales: ['tk-TM'] },\n UZ: { name: 'Uzbekistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Uzbekistan', locales: ['uz-UZ'] },\n // ─── Middle East ──────────────────────────────────────────────────────────\n AE: { name: 'United Arab Emirates', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'United Arab Emirates (UAE PDPL / DIFC DPL / ADGM DPR)', locales: ['ar-AE'] },\n BH: { name: 'Bahrain', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Bahrain (PDPL)', locales: ['ar-BH'] },\n IL: { name: 'Israel', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Israel (Privacy Protection Law)', locales: ['he-IL', 'ar-IL'] },\n IQ: { name: 'Iraq', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Iraq', locales: ['ar-IQ'] },\n IR: { name: 'Iran', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Iran', locales: ['fa-IR'] },\n JO: { name: 'Jordan', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Jordan', locales: ['ar-JO'] },\n KW: { name: 'Kuwait', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Kuwait', locales: ['ar-KW'] },\n LB: { name: 'Lebanon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Lebanon', locales: ['ar-LB'] },\n OM: { name: 'Oman', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Oman (PDPL)', locales: ['ar-OM'] },\n PS: { name: 'Palestine', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Palestine', locales: ['ar-PS'] },\n QA: { name: 'Qatar', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Qatar (PDPL)', locales: ['ar-QA'] },\n SA: { name: 'Saudi Arabia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Saudi Arabia (PDPL)', locales: ['ar-SA'] },\n SY: { name: 'Syria', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Syria', locales: ['ar-SY'] },\n YE: { name: 'Yemen', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Yemen', locales: ['ar-YE'] },\n // ─── Africa — West ────────────────────────────────────────────────────────\n BF: { name: 'Burkina Faso', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Burkina Faso', locales: ['fr-BF'] },\n BJ: { name: 'Benin', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Benin', locales: ['fr-BJ'] },\n CI: { name: \"Côte d'Ivoire\", type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: \"Côte d'Ivoire\", locales: ['fr-CI'] },\n CM: { name: 'Cameroon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cameroon', locales: ['fr-CM', 'en'] },\n CV: { name: 'Cabo Verde', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cabo Verde', locales: ['pt-CV'] },\n GA: { name: 'Gabon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Gabon', locales: ['fr-GA'] },\n GH: { name: 'Ghana', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Ghana (DPA)', locales: ['en'] },\n GM: { name: 'Gambia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Gambia', locales: ['en'] },\n GN: { name: 'Guinea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guinea', locales: ['fr-GN'] },\n GQ: { name: 'Equatorial Guinea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Equatorial Guinea', locales: ['es-GQ', 'fr-GQ'] },\n GW: { name: 'Guinea-Bissau', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guinea-Bissau', locales: ['pt-GW'] },\n LR: { name: 'Liberia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Liberia', locales: ['en'] },\n ML: { name: 'Mali', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mali', locales: ['fr-ML'] },\n MR: { name: 'Mauritania', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mauritania', locales: ['ar-MR'] },\n NE: { name: 'Niger', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Niger', locales: ['fr-NE'] },\n NG: { name: 'Nigeria', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Nigeria (NDPA)', locales: ['en'] },\n SL: { name: 'Sierra Leone', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Sierra Leone', locales: ['en'] },\n SN: { name: 'Senegal', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Senegal', locales: ['fr-SN'] },\n TG: { name: 'Togo', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Togo', locales: ['fr-TG'] },\n // ─── Africa — East ────────────────────────────────────────────────────────\n BI: { name: 'Burundi', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Burundi', locales: ['fr-BI'] },\n DJ: { name: 'Djibouti', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Djibouti', locales: ['fr-DJ', 'ar-DJ'] },\n ER: { name: 'Eritrea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Eritrea', locales: ['ti-ER'] },\n ET: { name: 'Ethiopia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Ethiopia', locales: ['am-ET'] },\n KE: { name: 'Kenya', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Kenya (DPA)', locales: ['sw-KE', 'en'] },\n KM: { name: 'Comoros', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Comoros', locales: ['ar-KM', 'fr-KM'] },\n MG: { name: 'Madagascar', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Madagascar', locales: ['fr-MG'] },\n MU: { name: 'Mauritius', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Mauritius (DPA)', locales: ['en', 'fr-MU'] },\n MW: { name: 'Malawi', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Malawi', locales: ['en'] },\n MZ: { name: 'Mozambique', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mozambique', locales: ['pt-MZ'] },\n RW: { name: 'Rwanda', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Rwanda (DPA)', locales: ['rw-RW', 'en'] },\n SC: { name: 'Seychelles', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Seychelles', locales: ['fr-SC', 'en'] },\n SO: { name: 'Somalia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Somalia', locales: ['so-SO'] },\n SS: { name: 'South Sudan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'South Sudan', locales: ['en'] },\n ST: { name: 'São Tomé and Príncipe', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'São Tomé and Príncipe', locales: ['pt-ST'] },\n TZ: { name: 'Tanzania', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Tanzania', locales: ['sw-TZ', 'en'] },\n UG: { name: 'Uganda', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Uganda (DPA)', locales: ['en'] },\n // ─── Africa — Southern ────────────────────────────────────────────────────\n AO: { name: 'Angola', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Angola', locales: ['pt-AO'] },\n BW: { name: 'Botswana', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Botswana (DPA)', locales: ['en'] },\n LS: { name: 'Lesotho', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Lesotho', locales: ['en'] },\n NA: { name: 'Namibia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Namibia', locales: ['en'] },\n SZ: { name: 'Eswatini', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Eswatini', locales: ['en'] },\n ZA: { name: 'South Africa', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'South Africa (POPIA)', locales: ['en', 'af-ZA'] },\n ZM: { name: 'Zambia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Zambia (DPA)', locales: ['en'] },\n ZW: { name: 'Zimbabwe', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Zimbabwe (DPA)', locales: ['en'] },\n // ─── Africa — North ───────────────────────────────────────────────────────\n DZ: { name: 'Algeria', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Algeria', locales: ['ar-DZ', 'fr-DZ'] },\n EG: { name: 'Egypt', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Egypt (DPA)', locales: ['ar-EG'] },\n LY: { name: 'Libya', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Libya', locales: ['ar-LY'] },\n MA: { name: 'Morocco', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Morocco', locales: ['ar-MA', 'fr-MA'] },\n SD: { name: 'Sudan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Sudan', locales: ['ar-SD'] },\n TN: { name: 'Tunisia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Tunisia (DPA)', locales: ['ar-TN', 'fr-TN'] },\n // ─── Africa — Central ─────────────────────────────────────────────────────\n CD: { name: 'DR Congo', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'DR Congo', locales: ['fr-CD'] },\n CF: { name: 'Central African Republic', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Central African Republic', locales: ['fr-CF'] },\n CG: { name: 'Republic of Congo', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Republic of Congo', locales: ['fr-CG'] },\n TD: { name: 'Chad', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Chad', locales: ['fr-TD', 'ar-TD'] },\n // ─── Europe — Eastern ─────────────────────────────────────────────────────\n BY: { name: 'Belarus', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Belarus', locales: ['be-BY', 'ru-BY'] },\n MD: { name: 'Moldova', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Moldova', locales: ['ro-MD'] },\n RU: { name: 'Russia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Russia', locales: ['ru-RU'] },\n UA: { name: 'Ukraine', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Ukraine', locales: ['uk-UA'] },\n VA: { name: 'Holy See', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Holy See', locales: ['it-VA'] },\n // ─── Oceania ──────────────────────────────────────────────────────────────\n AU: { name: 'Australia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Australia (Privacy Act)', locales: ['en'] },\n FJ: { name: 'Fiji', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Fiji', locales: ['en'] },\n FM: { name: 'Micronesia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Micronesia', locales: ['en'] },\n KI: { name: 'Kiribati', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Kiribati', locales: ['en'] },\n MH: { name: 'Marshall Islands', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Marshall Islands', locales: ['en'] },\n MP: { name: 'Northern Mariana Islands', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Northern Mariana Islands', locales: ['en'] },\n NC: { name: 'New Caledonia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'New Caledonia', locales: ['fr-NC'] },\n NF: { name: 'Norfolk Island', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Norfolk Island', locales: ['en'] },\n NR: { name: 'Nauru', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Nauru', locales: ['en'] },\n NU: { name: 'Niue', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Niue', locales: ['en'] },\n NZ: { name: 'New Zealand', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'New Zealand (Privacy Act)', locales: ['en'] },\n PF: { name: 'French Polynesia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'French Polynesia', locales: ['fr-PF'] },\n PG: { name: 'Papua New Guinea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Papua New Guinea', locales: ['en'] },\n PW: { name: 'Palau', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Palau', locales: ['en'] },\n SB: { name: 'Solomon Islands', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Solomon Islands', locales: ['en'] },\n TO: { name: 'Tonga', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Tonga', locales: ['en'] },\n TV: { name: 'Tuvalu', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Tuvalu', locales: ['en'] },\n VU: { name: 'Vanuatu', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Vanuatu', locales: ['fr-VU', 'en'] },\n WS: { name: 'Samoa', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Samoa', locales: ['en'] },\n // ─── Europe — GDPR-equivalent / adequacy ─────────────────────────────────\n UK: { name: 'United Kingdom', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'United Kingdom (UK GDPR + PECR)', locales: ['en'] },\n CH: { name: 'Switzerland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Switzerland (revFADP)', locales: ['de-CH', 'fr-CH', 'it-CH'] },\n },\n} satisfies {\n version: string\n countries: Record<string, {\n name: string\n type: 'country'\n complianceGroup: ComplianceGroupId\n default: ComplianceGroupId\n description: string\n locales: readonly string[]\n overriddenRegions?: Record<string, {\n name: string\n type: 'region'\n complianceGroup: ComplianceGroupId\n description: string\n }>\n }>\n}\n\nexport const COUNTRY_CODES = Object.entries(EMBEDDED_COMPLIANCE_MAP.countries).reduce(function (acc, [c, d]) {\n acc[c] = d.name;\n return acc;\n}, {} as Record<string, string>);\n\nexport const LOCALES = Object.values(EMBEDDED_COMPLIANCE_MAP.countries).reduce(function (acc, c) {\n c.locales.forEach(l => {\n acc[l] = c.name;\n })\n return acc;\n}, {} as Record<string, string>);\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'general-privacy-consent',\n gpcMode: 'honor',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'marketing', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'We value your privacy',\n htmlText:\n 'We use cookies and similar technologies to enhance your browsing experience, ' +\n 'personalise content and ads, and analyse our traffic. ' +\n 'By clicking <strong>Accept All</strong>, you consent to our use of these technologies. ' +\n 'You can change your preferences at any time. ' +\n 'See our <a href=\"/privacy-policy\">Privacy Policy</a> for more information.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject Optional', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Your Privacy Preference Has Been Recognized',\n htmlText:\n 'Your browser sent a Global Privacy Control (GPC) signal indicating you prefer to ' +\n 'limit data collection. We have applied your preference. ' +\n 'You can review or update your choices at any time.',\n buttons: [\n { text: 'Review Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Cookie Preferences',\n subheading: 'Choose which types of cookies you allow us to use. You can change these settings at any time.',\n htmlText:\n 'We use different types of cookies to optimise your experience on our website. ' +\n 'Click on the different category headings to find out more and change your default settings. ' +\n 'Note that blocking some types of cookies may impact your experience of the site ' +\n 'and the services we are able to offer.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'These cookies are necessary for the website to function and cannot be switched off. ' +\n 'They are usually only set in response to actions you have taken such as setting your ' +\n 'privacy preferences, logging in, or filling in forms.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies enable enhanced functionality and personalisation. They may be set ' +\n 'by us or by third parties whose services we have added to our pages.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'These cookies help us understand how our website is being used, or how effective ' +\n 'our marketing campaigns are. All information these cookies collect is aggregated ' +\n 'and therefore anonymous.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Marketing',\n htmlText:\n 'These cookies are used by our advertising partners to build a profile of your interests ' +\n 'and show you relevant advertisements on other sites. They work by uniquely identifying ' +\n 'your browser and device. If you do not allow these cookies, you will not experience ' +\n 'targeted advertising.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject Optional', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'notice-only',\n gpcMode: 'ignore',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'legitimate_interest', expiry: 365 },\n { id: 'functional', legalBasis: 'legitimate_interest', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'Cookie Notice',\n htmlText:\n 'This website uses cookies and similar technologies to ensure it works correctly and to ' +\n 'improve your experience. By continuing to browse this site, you acknowledge the use of ' +\n 'these technologies. For more information, please read our ' +\n '<a href=\"/privacy-policy\">Privacy Policy</a> and <a href=\"/cookie-policy\">Cookie Policy</a>.',\n buttons: [\n { text: 'Got It', style: 'primary', action: 'submit' },\n { text: 'Learn More', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Cookie Notice',\n htmlText:\n 'This site uses cookies necessary for its operation and for improving your experience. ' +\n 'No consent is required in this jurisdiction. ' +\n 'See our <a href=\"/privacy-policy\">Privacy Policy</a> for details.',\n buttons: [\n { text: 'Acknowledge', style: 'primary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Cookie Information',\n subheading: 'This notice explains how and why we use cookies on this website.',\n htmlText:\n 'This website uses cookies to function correctly and to improve your experience. ' +\n 'In the jurisdiction where you are located, explicit consent for cookies may not be required. ' +\n 'However, we believe in transparency and provide this information so you understand how we ' +\n 'use cookies. Some cookies are strictly necessary for the site to work.',\n showClose: true,\n overlayOpacity: 40,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'These cookies are required for the website to function correctly. Without these cookies, ' +\n 'some parts of the website will not work as intended. They cannot be disabled.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional and Analytics',\n htmlText:\n 'These cookies help us deliver a better experience by remembering your preferences ' +\n 'and measuring how the site is used. They are used under a legitimate interest basis ' +\n 'and do not require your consent in this jurisdiction.',\n type: 'legitimate_interest',\n legitimateInterest: {\n enabled: true,\n description:\n 'We use functional and analytics cookies under a legitimate interest basis to ' +\n 'improve our service and to understand how users interact with our website. ' +\n 'This data is anonymised and does not identify you personally.',\n },\n cookies: ['functional', 'analytics'],\n },\n ],\n buttons: [\n { text: 'Acknowledge', style: 'primary', action: 'submit' },\n { text: 'Close', style: 'text', action: 'close' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-in',\n gpcMode: 'honor',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 395 },\n { id: 'marketing', legalBasis: 'consent', listenGpc: true, expiry: 395 },\n { id: 'functional', legalBasis: 'consent', expiry: 395 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'We use cookies',\n htmlText:\n 'We and our partners use cookies and similar technologies to provide you with a better browsing experience, ' +\n 'to analyse site traffic, and to personalise content and advertisements. ' +\n 'Please give your consent to our use of cookies as described in our ' +\n '<a href=\"/privacy-policy\">Privacy Policy</a>. ' +\n 'You may withdraw your consent at any time.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject All', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Your Privacy Preference Has Been Detected',\n htmlText:\n 'Your browser or device has sent a Global Privacy Control (GPC) signal indicating you prefer ' +\n 'to limit the sale or sharing of your personal data. As required under applicable law, ' +\n 'we have applied your preference. You can review or update your choices at any time.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Privacy Preferences',\n subheading: 'Manage your cookie preferences below. Strictly necessary cookies cannot be disabled.',\n htmlText:\n 'We use different types of cookies to optimise your experience on our website. ' +\n 'You can choose which categories to allow. Your choices are saved for the duration indicated ' +\n 'for each category and can be changed at any time.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'These cookies are essential for the website to function and cannot be disabled. ' +\n 'They are set in response to actions you take such as logging in, filling in forms, or ' +\n 'setting privacy preferences. They do not store personally identifiable information.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies enable enhanced functionality and personalisation, such as remembering ' +\n 'your language preference or region. They may be set by us or by third-party providers ' +\n 'whose services we have added to our pages.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'These cookies help us understand how visitors interact with our site by collecting and ' +\n 'reporting information anonymously. This data helps us improve our content and navigation. ' +\n 'No personally identifiable information is collected.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Marketing',\n htmlText:\n 'These cookies track your online activity to help advertisers deliver more relevant ' +\n 'advertising to you, or to limit how many times you see an advertisement. They may be ' +\n 'shared with other organisations or advertisers. Declining these cookies will not reduce ' +\n 'the number of advertisements you see.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject All', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-in-brazil',\n gpcMode: 'honor',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'marketing', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'Your Privacy Matters — LGPD Notice',\n htmlText:\n 'In accordance with the Lei Geral de Proteção de Dados (LGPD — Brazilian General Data ' +\n 'Protection Law), we request your consent before collecting and processing personal data ' +\n 'beyond what is strictly necessary. As a data subject (titular), you have the right to ' +\n 'access, correct, delete, and withdraw consent at any time. ' +\n 'Read our <a href=\"/privacy-policy\">Privacy Policy</a> for full details.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject All', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Your Privacy Signal Was Detected',\n htmlText:\n 'A Global Privacy Control (GPC) signal was detected from your browser. We have applied ' +\n 'your preference in accordance with applicable data protection law. ' +\n 'You may manage your choices at any time.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Privacy Preferences — LGPD',\n subheading:\n 'Under the LGPD, you have the right to give, manage, or withdraw consent for each purpose ' +\n 'of personal data processing. You may update your choices at any time.',\n htmlText:\n 'As a titular (data subject) under the Lei Geral de Proteção de Dados (LGPD), you have the ' +\n 'right to: confirm the existence of processing, access your data, correct incomplete or ' +\n 'inaccurate data, anonymise or delete unnecessary data, and withdraw consent. ' +\n 'Use the controls below to set your preferences.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'Processing under this purpose is indispensable for the operation of our services. ' +\n 'This is based on the legal bases of contract performance and legal obligation under ' +\n 'Article 7 of the LGPD.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies allow us to remember your preferences and provide a personalised experience. ' +\n 'Your consent enables this functionality.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'With your consent, we collect statistical data about your use of our services to ' +\n 'improve our offering. Data is anonymised where possible and is not used to identify you.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Marketing',\n htmlText:\n 'With your consent, we and our partners process your personal data to deliver ' +\n 'personalised advertising content. You may withdraw this consent at any time, ' +\n 'and withdrawal will not affect the lawfulness of prior processing.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject All', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-in-china',\n gpcMode: 'ignore',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', expiry: 365 },\n { id: 'marketing', legalBasis: 'consent', expiry: 365 },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'Personal Information Processing Notice',\n htmlText:\n 'In accordance with the Personal Information Protection Law of the People\\'s Republic of ' +\n 'China (PIPL), we hereby inform you of the types of personal information we collect, ' +\n 'the purposes for which it is used, and your rights as a personal information subject. ' +\n 'Your consent is required before we may process your personal information beyond what is ' +\n 'strictly necessary. Please read our <a href=\"/privacy-policy\">Privacy Policy</a> carefully.',\n buttons: [\n { text: 'Agree to All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject Non-Necessary', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Privacy Notice',\n htmlText:\n 'We have detected a privacy preference signal. We have applied your preferences for ' +\n 'the processing of your personal information. You may review and update your choices at any time.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Personal Information Processing Preferences',\n subheading:\n 'Under the PIPL, you have the right to know, decide, access, copy, correct, delete, and ' +\n 'withdraw consent for the processing of your personal information. Please select your preferences.',\n htmlText:\n 'We process personal information lawfully, legitimately, and necessarily in accordance ' +\n 'with the PIPL. Your personal information is stored and processed within the territory of ' +\n 'China. Cross-border transfer of your data, if any, is handled in accordance with Chapter III ' +\n 'of the PIPL. You may withdraw your consent at any time by updating these settings.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'Processing under this purpose is required for the normal functioning of our services ' +\n 'as requested by you. This is processed under the legitimate basis of contract ' +\n 'performance and legal obligation under the PIPL.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies allow us to remember your preferences and settings to provide you with ' +\n 'an enhanced experience. Your consent allows us to personalise our service for you.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Statistical Analysis',\n htmlText:\n 'With your consent, we collect usage data to analyse how our services are used, ' +\n 'enabling us to improve performance and user experience. Data is anonymised where possible.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Marketing and Commercial Purposes',\n htmlText:\n 'With your consent, we use your personal information to deliver personalised ' +\n 'commercial information and advertisements. You may withdraw this consent at any time.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Agree and Continue', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Only Necessary', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-in-dpdpa',\n gpcMode: 'honor',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'marketing', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'We Need Your Consent',\n htmlText:\n 'In accordance with the Digital Personal Data Protection Act, 2023 (DPDPA) of India, ' +\n 'we require your explicit consent before collecting and processing your personal data. ' +\n 'You have the right to withdraw consent at any time and to access, correct, or erase your data. ' +\n 'Please review our <a href=\"/privacy-policy\">Privacy Policy</a> for details.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject All', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Privacy Preference Detected',\n htmlText:\n 'A privacy preference signal was detected from your browser. We have applied your opt-out ' +\n 'preference in accordance with applicable privacy regulations. You may review and update ' +\n 'your consent choices at any time.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Consent Preferences — DPDPA',\n subheading:\n 'Under the Digital Personal Data Protection Act (DPDPA), you have the right to give, ' +\n 'withdraw, or manage your consent for each purpose of data processing. Please select ' +\n 'your preferences below.',\n htmlText:\n 'As a Data Principal under the DPDPA, you have the right to access, correct, erase, and ' +\n 'withdraw consent for your personal data. This notice is provided by the Data Fiduciary ' +\n 'as required under Section 5 of the DPDPA. You can update your preferences at any time.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Necessary Processing',\n htmlText:\n 'Processing your personal data under this purpose is necessary for the delivery of ' +\n 'the service you have requested. This processing is exempt from consent requirements ' +\n 'under Section 7(a) of the DPDPA.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional Enhancements',\n htmlText:\n 'We process limited personal data to remember your preferences and settings, ' +\n 'improving your experience on return visits. Your consent enables this personalisation.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics and Improvement',\n htmlText:\n 'With your consent, we collect data about how you use our services in order to ' +\n 'improve them. The data is anonymised where possible and is not shared with third parties ' +\n 'for commercial purposes.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Marketing and Personalised Advertising',\n htmlText:\n 'With your consent, we and our advertising partners process your personal data to ' +\n 'deliver advertisements tailored to your interests. You may withdraw this consent ' +\n 'at any time without affecting the lawfulness of prior processing.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Give Consent to All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Withdraw All Consent', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-out',\n gpcMode: 'honor',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n { id: 'marketing', legalBasis: 'consent', listenGpc: true, expiry: 365, cpraCategory: 'sale' },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'Your Privacy Choices',\n htmlText:\n 'We use cookies and similar technologies on this site. Some help us operate the site, ' +\n 'while others are used for analytics and advertising purposes. ' +\n 'You may opt out of the sale or sharing of your personal information at any time. ' +\n 'See our <a href=\"/privacy-policy\">Privacy Policy</a> for more information.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Do Not Sell or Share My Personal Information', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Your Opt-Out Preference Has Been Applied',\n htmlText:\n 'We detected a Global Privacy Control (GPC) signal from your browser. ' +\n 'In compliance with applicable privacy law, we have opted you out of the sale ' +\n 'and sharing of your personal information. You can review or update your preferences below.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Privacy Preferences',\n subheading:\n 'You have the right to opt out of the sale and sharing of your personal information. ' +\n 'Toggle the categories below to set your preferences.',\n htmlText:\n 'We use different types of cookies and tracking technologies. Strictly necessary cookies ' +\n 'are always active. For all other categories, you can choose to opt out at any time.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'These cookies are required for the website to operate. They include cookies that ' +\n 'enable you to log in, use shopping carts, and access secure areas of the website.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies allow the website to remember choices you make (such as your user name, ' +\n 'language, or the region you are in) and provide enhanced, more personal features.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'These cookies collect information about how visitors use our website, such as which ' +\n 'pages they visit most often. This data helps us improve the site and your experience.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Sale / Sharing of Personal Information',\n htmlText:\n 'These cookies and trackers are used to deliver targeted advertising to you, and may ' +\n 'constitute a \"sale\" or \"sharing\" of personal information under the California Consumer ' +\n 'Privacy Act (CCPA). You have the right to opt out of this activity.',\n cookies: ['marketing'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Opt Out of Sale/Sharing', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ResolvedProfile } from '../types'\n\nconst profile: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n complianceGroup: 'opt-out-strict',\n gpcMode: 'strict',\n cookies: [\n { id: 'necessary', legalBasis: 'mandatory' },\n { id: 'analytics', legalBasis: 'consent', listenGpc: true, expiry: 365 },\n {\n id: 'marketing',\n legalBasis: 'consent',\n listenGpc: true,\n expiry: 365,\n cpraCategory: 'sale',\n },\n {\n id: 'sensitive',\n legalBasis: 'consent',\n listenGpc: true,\n expiry: 365,\n cpraCategory: 'sensitive',\n },\n { id: 'functional', legalBasis: 'consent', expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'Your Privacy Rights (CPRA)',\n htmlText:\n 'Under the California Privacy Rights Act (CPRA), you have the right to opt out of the ' +\n 'sale, sharing, and use of your sensitive personal information. ' +\n 'We also honor Global Privacy Control (GPC) signals automatically. ' +\n 'See our <a href=\"/privacy-policy\">Privacy Policy</a> and ' +\n '<a href=\"/do-not-sell\">Do Not Sell or Share My Personal Information</a> page.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Opt Out of Sale, Sharing & Sensitive Use', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Manage Preferences', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'GPC Signal Detected — Opt-Out Applied',\n htmlText:\n 'A Global Privacy Control (GPC) opt-out signal was detected. As required by CPRA, ' +\n 'we have automatically applied your opt-out of the sale and sharing of your personal ' +\n 'information and the use of your sensitive personal information. ' +\n 'Your preference has been saved.',\n buttons: [\n { text: 'Manage Preferences', style: 'primary', action: 'manage' },\n { text: 'Acknowledge', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n position: 'right',\n heading: 'Privacy Preferences (CPRA)',\n subheading:\n 'Manage your privacy choices. You may opt out of the sale, sharing, and sensitive use ' +\n 'of your personal information at any time.',\n htmlText:\n 'The California Privacy Rights Act (CPRA) provides you with additional rights over your ' +\n 'personal information. GPC signals are honored automatically. You can update your choices below.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'These cookies are essential for the website to function and are always active. ' +\n 'They are exempt from CPRA opt-out requirements.',\n mandatory: true,\n cookies: ['necessary'],\n },\n {\n id: 'cat-functional',\n heading: 'Functional',\n htmlText:\n 'These cookies personalise your experience (e.g. language, region preferences). ' +\n 'They do not involve the sale or sharing of personal information.',\n cookies: ['functional'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'These cookies measure site performance and visitor behaviour. Data is used internally ' +\n 'to improve our services. Opting out stops this collection.',\n cookies: ['analytics'],\n },\n {\n id: 'cat-marketing',\n heading: 'Sale and Sharing of Personal Information',\n htmlText:\n 'These technologies may constitute a \"sale\" or \"sharing\" of your personal information ' +\n 'under CPRA, including cross-context behavioural advertising. ' +\n 'You have the right to opt out.',\n cookies: ['marketing'],\n },\n {\n id: 'cat-sensitive',\n heading: 'Use of Sensitive Personal Information',\n htmlText:\n 'Some technologies may use sensitive personal information as defined by CPRA ' +\n '(e.g. precise geolocation, health data). You have the right to limit such use.',\n cookies: ['sensitive'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Opt Out of All Non-Necessary', cookies: '!', style: 'secondary', action: 'custom' },\n { text: 'Save My Choices', style: 'text', action: 'submit' },\n ],\n },\n}\n\nexport default profile\n","import type { ConsentiConfig, ResolvedProfile, ProfileConfig, PublicProfileResponse } from '../types'\nimport { httpRequest } from '../utils/http'\nimport { resolveLocale } from '../utils/locale'\nimport { logger } from '../utils/console'\nimport { getBrowserGeoHints } from '../utils/geo-hints'\nimport { COMPLIANCE_GROUP_IDS, EMBEDDED_COMPLIANCE_MAP } from '@consenti/utils'\nimport generalPrivacyConsent from '../profiles/general-privacy-consent'\nimport noticeOnly from '../profiles/notice-only'\nimport optIn from '../profiles/opt-in'\nimport optInBrazil from '../profiles/opt-in-brazil'\nimport optInChina from '../profiles/opt-in-china'\nimport optInDpdpa from '../profiles/opt-in-dpdpa'\nimport optOut from '../profiles/opt-out'\nimport optOutStrict from '../profiles/opt-out-strict'\n\nconst PREBUILT_PROFILE_MAP: Record<string, ResolvedProfile> = {\n 'general-privacy-consent': generalPrivacyConsent,\n 'notice-only': noticeOnly,\n 'opt-in': optIn,\n 'opt-in-brazil': optInBrazil,\n 'opt-in-china': optInChina,\n 'opt-in-dpdpa': optInDpdpa,\n 'opt-out': optOut,\n 'opt-out-strict': optOutStrict,\n}\n\n// ─── Local profile registry ───────────────────────────────────────────────────\n\nconst profileRegistry = new Map<string, ProfileConfig>()\n\nexport function registerProfile(id: number, config: ProfileConfig): void {\n profileRegistry.set(String(id), config)\n}\n\nexport function getRegisteredProfile(id: number): ProfileConfig | undefined {\n return profileRegistry.get(String(id))\n}\n\n// ─── Default fallback profile ─────────────────────────────────────────────────\n\nexport const DEFAULT_PROFILE: ResolvedProfile = {\n id: 0,\n version: 1,\n defaultLocale: 'en',\n gpcMode: 'honor',\n cookies: [\n { id: 'functionality_storage', mandatory: true },\n { id: 'analytics_storage', listenGpc: true, expiry: 365 },\n { id: 'ad_storage', listenGpc: true, expiry: 365 },\n { id: 'ad_user_data', listenGpc: true, expiry: 365 },\n { id: 'ad_personalization', listenGpc: true, expiry: 365 },\n ],\n mainBanner: {\n position: 'bottom',\n heading: 'We value your privacy',\n htmlText:\n 'We use cookies to enhance your browsing experience, serve personalised ads or content, ' +\n 'and analyse our traffic. By clicking <strong>Accept All</strong> you consent to our use ' +\n 'of cookies. You may change your preferences at any time.',\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject Optional', cookies: '!', style: 'primary', action: 'custom' },\n { text: 'Customize', style: 'text', action: 'manage' },\n ],\n },\n gpcBanner: {\n position: 'bottom',\n heading: 'Your Privacy Preference Has Been Recognized',\n htmlText:\n 'Your browser has sent a Global Privacy Control (GPC) signal. ' +\n \"We've applied your privacy preference where required by applicable law. \" +\n 'You can review or update your cookie choices at any time.',\n buttons: [\n { text: 'Review Preferences', style: 'primary', action: 'manage' },\n { text: 'Continue', style: 'secondary', action: 'submit' },\n ],\n },\n preferenceModal: {\n heading: 'Cookie Preferences',\n subheading: 'Choose which cookies you allow us to use.',\n htmlText:\n 'We use different types of cookies to optimise your experience. You can choose to ' +\n 'enable or disable each category below. Necessary cookies cannot be disabled as they ' +\n 'are required for core site functionality.',\n showClose: true,\n overlayOpacity: 50,\n categories: [\n {\n id: 'cat-necessary',\n heading: 'Strictly Necessary',\n htmlText:\n 'These cookies are essential for the website to function correctly. ' +\n 'They include authentication, security, and accessibility features. ' +\n 'They cannot be disabled.',\n mandatory: true,\n cookies: ['functionality_storage'],\n },\n {\n id: 'cat-analytics',\n heading: 'Analytics',\n htmlText:\n 'These cookies help us understand how visitors interact with our website by ' +\n 'collecting and reporting information anonymously. (e.g. Google Analytics)',\n cookies: ['analytics_storage'],\n },\n {\n id: 'cat-advertising',\n heading: 'Advertising',\n htmlText:\n 'These cookies are used to deliver advertisements more relevant to you and your interests. ' +\n '(e.g. Google Ads, Meta Pixel)',\n cookies: ['ad_storage', 'ad_user_data', 'ad_personalization'],\n },\n ],\n buttons: [\n { text: 'Accept All', cookies: '*', style: 'primary', action: 'custom' },\n { text: 'Reject Optional', cookies: '!', style: 'primary', action: 'custom' },\n { text: 'Save Selection', style: 'secondary', action: 'submit' },\n ],\n },\n}\n\n// ─── mapPublicProfileToResolved ───────────────────────────────────────────────\n\nexport function mapPublicProfileToResolved(resp: PublicProfileResponse): ResolvedProfile {\n let gpcMode: 'ignore' | 'honor' | 'strict' | undefined\n if (resp.gpcMode === true) gpcMode = 'honor'\n else if (resp.gpcMode === false) gpcMode = 'ignore'\n else if (resp.gpcMode) gpcMode = resp.gpcMode as 'ignore' | 'honor' | 'strict'\n\n return {\n id: 0,\n profileUuid: resp.id,\n version: resp.version,\n defaultLocale: resp.defaultLocale,\n locales: resp.locales,\n cookies: resp.cookies,\n mainBanner: resp.mainBanner,\n preferenceModal: resp.preferenceModal,\n ...(resp.gpcBanner ? { gpcBanner: resp.gpcBanner } : {}),\n ...(gpcMode ? { gpcMode } : {}),\n ...(resp.darkMode !== undefined ? { darkMode: resp.darkMode } : {}),\n ...(resp.complianceGroup ? { complianceGroup: resp.complianceGroup } : {}),\n ...(resp.complianceConfig ? { complianceConfig: resp.complianceConfig } : {}),\n }\n}\n\n// ─── sessionStorage cache for /resolve-profile ───────────────────────────────\n\nconst CACHE_KEY_PREFIX = '__consenti_resolved_'\nconst CACHE_TTL_MS = 60 * 60 * 1000\n\ninterface ResolvedUrlCache {\n filePath: string\n version: number\n complianceGroup: string\n tenantId: string\n locale: string\n expiresAt: number\n}\n\nexport function getCachedResolution(\n tenantId: string,\n locale: string,\n cacheEnabled: boolean,\n): ResolvedUrlCache | null {\n if (!cacheEnabled) return null\n const key = `${CACHE_KEY_PREFIX}${tenantId}_${locale}`\n try {\n const raw = sessionStorage.getItem(key)\n if (!raw) return null\n const cached = JSON.parse(raw) as ResolvedUrlCache\n if (Date.now() > cached.expiresAt) { sessionStorage.removeItem(key); return null }\n return cached\n } catch { return null }\n}\n\nexport function setCachedResolution(\n tenantId: string,\n locale: string,\n cacheEnabled: boolean,\n data: Omit<ResolvedUrlCache, 'expiresAt'>,\n): void {\n if (!cacheEnabled) return\n const key = `${CACHE_KEY_PREFIX}${tenantId}_${locale}`\n try {\n sessionStorage.setItem(key, JSON.stringify({ ...data, expiresAt: Date.now() + CACHE_TTL_MS }))\n } catch { /* ignore storage failures */ }\n}\n\n// ─── Domain allowlist check ───────────────────────────────────────────────────\n\nfunction isDomainAllowed(profile: PublicProfileResponse, trustDomain: boolean): boolean {\n if (trustDomain) return true\n const origins = profile.allowedOrigins\n if (!origins || origins.length === 0) return true\n const current = window.location.origin\n return origins.some(o => o === current || o === '*')\n}\n\n// ─── Pre-built profile loader ─────────────────────────────────────────────────\n\nfunction loadPrebuiltProfile(complianceGroup: string): ResolvedProfile | null {\n return PREBUILT_PROFILE_MAP[complianceGroup] ?? null\n}\n\nfunction isPrebuiltAllowed(complianceGroup: string, config: ConsentiConfig): boolean {\n const setting = config.core.usePrebuiltProfiles ?? 'all'\n if (setting === 'all') return true\n return (setting as string[]).includes(complianceGroup)\n}\n\n// ─── Geo hint → complianceGroup ───────────────────────────────────────────────\n\nfunction resolveGroupFromHints(tz: string, lang: string): string {\n // Try to extract a country code from the BCP47 language tag (e.g. 'de-DE' → 'DE')\n const parts = lang.replace(/_/g, '-').split('-')\n const tag = parts.length >= 2 ? parts[parts.length - 1]?.toUpperCase() : undefined\n const countryCode = tag && /^[A-Z]{2}$/.test(tag) ? tag : undefined\n\n if (countryCode) {\n const entry = (EMBEDDED_COMPLIANCE_MAP.countries as Record<string, { default: string }>)[countryCode]\n if (entry?.default) return entry.default\n }\n\n // If language has no country subtag, try the timezone IANA region (e.g. 'Europe/Berlin' → 'DE' area)\n const tzParts = tz.split('/')\n if (tzParts[0] === 'Europe') return 'opt-in'\n if (tzParts[0] === 'America') {\n const city = tzParts[1] ?? ''\n if (['Los_Angeles', 'Denver', 'Chicago', 'New_York', 'Phoenix', 'Anchorage', 'Honolulu'].includes(city)) {\n return 'opt-out'\n }\n }\n\n // Default: opt-in (GDPR) — strictest, safe for any unknown jurisdiction\n return 'opt-in'\n}\n\n// ─── Scenario helpers ─────────────────────────────────────────────────────────\n\nfunction effectiveLocale(config: ConsentiConfig): string {\n const l = config.core.locale\n return (!l || l === 'auto') ? navigator.language : l\n}\n\nfunction effectiveTenantId(config: ConsentiConfig): string {\n return config.api?.tenantId ?? config.core.tenantId ?? 'default'\n}\n\nfunction isKnownComplianceGroup(value: string): boolean {\n return (COMPLIANCE_GROUP_IDS as readonly string[]).includes(value)\n}\n\nasync function fetchProfileJson(\n url: string,\n authToken: string | undefined,\n): Promise<PublicProfileResponse | null> {\n try {\n return await httpRequest<PublicProfileResponse>(url, {}, authToken)\n } catch {\n return null\n }\n}\n\n/** Scenario 1A — api disabled, compliance.type = 'auto' (client-side geo) */\nasync function scenario1A(config: ConsentiConfig): Promise<ResolvedProfile> {\n const { tz, lang } = getBrowserGeoHints()\n // Derive complianceGroup from tz + lang.\n // 'opt-in' (GDPR) is the strictest default — safe for any jurisdiction.\n // Full geo-routing via ComplianceMapData is a future enhancement to this path.\n const complianceGroup = resolveGroupFromHints(tz, lang)\n\n if (isPrebuiltAllowed(complianceGroup, config)) {\n const profile = loadPrebuiltProfile(complianceGroup)\n if (profile) return profile\n }\n return DEFAULT_PROFILE\n}\n\n/** Scenario 1B — api disabled, compliance.type = known ComplianceGroupId */\nasync function scenario1B(complianceGroup: string, config: ConsentiConfig): Promise<ResolvedProfile> {\n if (isPrebuiltAllowed(complianceGroup, config)) {\n const profile = loadPrebuiltProfile(complianceGroup)\n if (profile) return profile\n }\n return DEFAULT_PROFILE\n}\n\n/** Scenario 2A — api enabled, compliance.type = 'auto' (server geo-resolve) */\nasync function scenario2A(config: ConsentiConfig): Promise<ResolvedProfile> {\n const locale = effectiveLocale(config)\n const tenantId = effectiveTenantId(config)\n const cacheEnabled = config.core.cacheResolvedProfiles !== false\n const base = config.api?.baseUrl ?? window.location.origin\n const trustDomain = config.api?.trustDomain ?? false\n\n const cached = getCachedResolution(tenantId, locale, cacheEnabled)\n if (cached) {\n const json = await fetchProfileJson(cached.filePath, config.api?.authToken)\n if (json) {\n if (!isDomainAllowed(json, trustDomain)) {\n logger.error(`Domain ${window.location.origin} is not in allowedOrigins for this profile. Falling back to pre-built profile.`)\n return scenario1A(config)\n }\n return mapPublicProfileToResolved(json)\n }\n }\n\n const { tz, lang } = getBrowserGeoHints()\n const resolveUrl =\n `${base}/consenti/api/v1/resolve-profile` +\n `?tz=${encodeURIComponent(tz)}&lang=${encodeURIComponent(lang)}` +\n `&locale=${encodeURIComponent(locale)}&tenantId=${encodeURIComponent(tenantId)}`\n\n let filePath: string\n let complianceGroup: string\n let version: number\n\n try {\n const resolved = await httpRequest<{\n filePath: string\n tenantId: string\n profileId: string\n complianceGroup: string\n locale: string\n version: number\n warning?: string\n }>(resolveUrl, {}, config.api?.authToken)\n\n if (resolved.warning === 'locale_not_found') {\n logger.info('Locale not found; using default locale.')\n }\n\n filePath = resolved.filePath\n complianceGroup = resolved.complianceGroup\n version = resolved.version\n\n setCachedResolution(tenantId, locale, cacheEnabled, {\n filePath,\n version,\n complianceGroup,\n tenantId,\n locale,\n })\n } catch {\n logger.warn('resolve-profile failed; falling back to pre-built profile.')\n return scenario1A(config)\n }\n\n const json = await fetchProfileJson(filePath, config.api?.authToken)\n ?? await fetchProfileJson(\n `${base}/consenti/api/v1/profiles/${tenantId}/${complianceGroup}/default.json`,\n config.api?.authToken,\n )\n\n if (!json) {\n logger.warn('Profile fetch failed; falling back to pre-built profile.')\n return scenario1A(config)\n }\n\n if (!isDomainAllowed(json, trustDomain)) {\n logger.error(`Domain ${window.location.origin} is not in allowedOrigins for this profile. Falling back to pre-built profile.`)\n return scenario1A(config)\n }\n\n return mapPublicProfileToResolved(json)\n}\n\n/** Scenario 2B — api enabled, compliance.type = known ComplianceGroupId */\nasync function scenario2B(complianceGroup: string, config: ConsentiConfig): Promise<ResolvedProfile> {\n const locale = effectiveLocale(config)\n const tenantId = effectiveTenantId(config)\n const base = config.api?.baseUrl ?? window.location.origin\n const trustDomain = config.api?.trustDomain ?? false\n\n const group = config.api?.complianceGroup ?? complianceGroup\n\n const json = await fetchProfileJson(\n `${base}/consenti/api/v1/profiles/${tenantId}/${group}/${encodeURIComponent(locale)}.json`,\n config.api?.authToken,\n ) ?? await fetchProfileJson(\n `${base}/consenti/api/v1/profiles/${tenantId}/${group}/default.json`,\n config.api?.authToken,\n )\n\n if (!json) {\n logger.warn(`Could not fetch profile for ${group}; falling back to pre-built.`)\n return scenario1B(group, config)\n }\n\n if (!isDomainAllowed(json, trustDomain)) {\n logger.error(`Domain ${window.location.origin} is not in allowedOrigins for this profile. Falling back to pre-built profile.`)\n return scenario1B(group, config)\n }\n\n return mapPublicProfileToResolved(json)\n}\n\n/** Scenario Local — locally registered profile */\nasync function scenarioLocal(typeValue: string, config: ConsentiConfig): Promise<ResolvedProfile> {\n if (config.api?.enabled) {\n logger.warn('Local profile used instead of server profile. Check compliance.type config.')\n }\n\n const locale = effectiveLocale(config)\n const local = Array.from(profileRegistry.values()).find(p => p.complianceGroup === typeValue)\n\n if (local) {\n const resolved = resolveLocale(local.translations ?? {}, locale, local.defaultLocale)\n return {\n id: 0,\n version: 1,\n defaultLocale: local.defaultLocale,\n locales: Object.keys(local.translations ?? {}),\n cookies: local.cookies ?? [],\n mainBanner: resolved.mainBanner,\n ...(resolved.gpcBanner ? { gpcBanner: resolved.gpcBanner } : {}),\n preferenceModal: resolved.preferenceModal,\n ...(local.darkMode !== undefined ? { darkMode: local.darkMode } : {}),\n ...(local.complianceGroup ? { complianceGroup: local.complianceGroup } : {}),\n }\n }\n\n // Fall back to auto path for the current api mode\n return config.api?.enabled ? scenario2A(config) : scenario1A(config)\n}\n\n// ─── resolveProfile dispatcher ────────────────────────────────────────────────\n\nexport async function resolveProfile(config: ConsentiConfig): Promise<ResolvedProfile> {\n const complianceType = config.compliance?.type ?? 'auto'\n const apiEnabled = config.api?.enabled ?? false\n\n try {\n if (complianceType === 'auto') {\n return apiEnabled ? await scenario2A(config) : await scenario1A(config)\n }\n\n if (isKnownComplianceGroup(complianceType)) {\n return apiEnabled\n ? await scenario2B(complianceType, config)\n : await scenario1B(complianceType, config)\n }\n\n // Unknown type — treat as local profile key or fall back to auto\n return await scenarioLocal(complianceType, config)\n } catch {\n return DEFAULT_PROFILE\n }\n}\n","/**\n * Global Privacy Control (GPC) detection and consent application.\n *\n * GPC is a browser-level privacy signal defined at https://globalprivacycontrol.org/.\n * When `navigator.globalPrivacyControl === true`, the user has indicated they do\n * not want their personal data sold or shared. CCPA and GDPR both recognise GPC\n * as a legally meaningful signal.\n *\n * Consenti supports three modes via `core.autoHonorGPC`:\n * - `false` → GPC signal ignored entirely (default)\n * - `true` → GPC-flagged cookies pre-denied; GPC banner shown (or main banner)\n * - `'strict'` → GPC-flagged cookies pre-denied; consent written silently; no banner\n *\n * Only cookies with `listenGpc: true` on their `Cookie` config are affected.\n * Mandatory cookies are never denied regardless of GPC.\n */\n\nimport type { Cookie, ConsentValue } from '../types'\nimport { safeNavigator } from '../utils/ssr'\n\n/**\n * Detects whether the Global Privacy Control signal is active in the current browser.\n *\n * Supported natively in Brave and via extensions in Firefox. The property is\n * intentionally not on the standard `Navigator` type yet, so we cast to check it.\n *\n * @returns `true` if `navigator.globalPrivacyControl === true`, `false` in all other cases\n * including SSR environments where `navigator` is unavailable.\n */\nexport function detectGPC(): boolean {\n const nav = safeNavigator()\n if (!nav) return false\n return (\n 'globalPrivacyControl' in nav &&\n (nav as Navigator & { globalPrivacyControl: boolean }).globalPrivacyControl === true\n )\n}\n\n/**\n * Applies the GPC signal to a partial consent object.\n *\n * For every cookie with `listenGpc: true` that is not mandatory:\n * - Consent-basis cookies → `'denied'`\n * - Legitimate-interest cookies (`type: 'legitimate_interest'`) → `'objected'`\n *\n * Mandatory cookies are always forced to `'granted'`.\n *\n * @param cookies - The full cookie list from the resolved profile.\n * @param currentConsent - Existing partial consent to use as a base (typically `{}`).\n * @param mode - The `autoHonorGPC` mode (`true` or `'strict'`). If falsy,\n * returns `currentConsent` unchanged.\n * @returns A new `ConsentValue` with GPC denials applied.\n */\nexport function applyGPCToConsent(\n cookies: Cookie[],\n currentConsent: Partial<ConsentValue>,\n mode: boolean | 'strict',\n): ConsentValue {\n if (!mode || !detectGPC()) return { ...currentConsent } as ConsentValue\n\n const result: ConsentValue = { ...currentConsent } as ConsentValue\n\n for (const cookie of cookies) {\n if (cookie.mandatory) continue\n if (cookie.listenGpc) {\n // LI cookies use 'objected' as the refusal status per the GDPR Art. 21 framework\n result[cookie.id] = cookie.type === 'legitimate_interest' ? 'objected' : 'denied'\n }\n }\n\n // Mandatory cookies always win, regardless of GPC\n for (const cookie of cookies) {\n if (cookie.mandatory) result[cookie.id] = 'granted'\n }\n\n return result\n}\n","/**\n * Button construction and click routing.\n *\n * `style` maps directly to the BEM modifier class (visual only).\n * `action` determines the click handler:\n *\n * | `action` | Effect |\n * |---|---|\n * | `'custom'` | Grant/deny via `cookies`: `'*'` all, `'!'` deny, `string[]` specific |\n * | `'submit'` | Save the current modal toggle state |\n * | `'manage'` | Open the preference modal |\n * | `'close'` | Dismiss the current surface without saving |\n */\n\nimport type { Button } from '../types'\n\n/**\n * Callbacks wired to every interactive button rendered by the widget.\n * Implemented by `ConsentiSetup.buildHandlers()` for each rendering context\n * (`'banner'` or `'modal'`).\n */\nexport interface ButtonClickHandler {\n /** Grant all cookies (non-mandatory and mandatory). */\n onGrantAll: () => void\n /** Deny all non-mandatory cookies; grant mandatory ones. */\n onDenyAll: () => void\n /** Save the current toggle state (modal context) or grant mandatory only (banner context). */\n onSubmit: () => void\n /** Open the preference modal. */\n onManage: () => void\n /** Close the current UI surface without saving. */\n onClose: () => void\n /** Grant only the specified cookie IDs plus all mandatory cookies. */\n onGrantSpecific: (cookieIds: string[]) => void\n}\n\n/**\n * Constructs a `<button>` element from a `Button` config and registers a click handler.\n *\n * @param btn - Button configuration from the resolved profile.\n * @param handlers - Click handler implementations.\n * @returns A fully wired `HTMLButtonElement`.\n */\nexport function buildButton(btn: Button, handlers: ButtonClickHandler): HTMLButtonElement {\n const el = document.createElement('button')\n el.type = 'button'\n el.className = `consenti-btn consenti-btn--${btn.style}`\n el.textContent = btn.text\n\n el.addEventListener('click', () => {\n switch (btn.action) {\n case 'custom': {\n const cookies = btn.cookies\n if (cookies === '*') {\n handlers.onGrantAll()\n } else if (cookies === '!') {\n handlers.onDenyAll()\n } else if (Array.isArray(cookies) && cookies.length > 0) {\n handlers.onGrantSpecific(cookies)\n }\n break\n }\n case 'submit':\n handlers.onSubmit()\n break\n case 'manage':\n handlers.onManage()\n break\n case 'close':\n handlers.onClose()\n break\n }\n })\n\n return el\n}\n","/**\n * Full-page semi-transparent overlay shown behind the banner or modal.\n * Rendered as a sibling element inside `#consenti-root` so it does not interfere\n * with the host page's stacking context.\n *\n * Opacity is driven by an inline style (the only inline style in the widget) because\n * the value is dynamic per-profile config and cannot be expressed as a CSS custom\n * property without JavaScript.\n */\n\n/**\n * Creates a `<div class=\"consenti-overlay\">` element.\n *\n * @param opacity - Overlay opacity as a percentage (0–100). Converted to a 0–1 CSS value.\n * @param onClick - Optional click handler (used by modal to close on backdrop click).\n * @returns The constructed overlay element. Not yet inserted into the DOM.\n */\nexport function createOverlay(opacity: number, onClick?: () => void): HTMLElement {\n const el = document.createElement('div')\n el.className = 'consenti-overlay'\n el.style.opacity = String(opacity / 100)\n el.setAttribute('aria-hidden', 'true')\n if (onClick) {\n el.addEventListener('click', onClick)\n }\n return el\n}\n","/**\n * Cookie consent banner DOM builder.\n *\n * Constructs the banner element from a `MainBanner` config and wires up button handlers.\n * The banner is accessible: it carries `role=\"region\"`, `aria-label`, and `aria-live`\n * so screen reader users are informed when it appears.\n *\n * Position variants (bottom, top, middle, left-bottom, right-bottom) are applied via\n * BEM modifier classes and handled entirely in CSS.\n *\n * Buttons with `action === 'link'` are rendered as `<a>` elements in\n * `.consenti-banner__links` inside the text container, separate from action buttons.\n */\n\nimport type { MainBanner, Button } from '../types'\nimport type { ButtonClickHandler } from './buttons'\nimport { buildButton } from './buttons'\nimport { createOverlay } from './overlay'\n\n/**\n * Builds and manages the consent banner DOM element.\n *\n * One `Banner` instance is created per `ConsentiSetup`. Call `build()` to construct\n * the DOM, then `show()` / `hide()` to toggle visibility without rebuilding.\n */\nexport class Banner {\n private el: HTMLElement | null = null\n private overlayEl: HTMLElement | null = null\n\n /**\n * Constructs the banner DOM from the given config and returns the root element.\n * The element is not yet inserted into the document — the caller appends it to\n * the mount root via `Renderer.mount()`.\n *\n * @param config - Banner layout and content config from the resolved profile.\n * @param handlers - Click handlers wired to each button.\n * @param privacyPolicyUrl - Optional URL appended as a \"Privacy Policy\" link after the body text.\n * @param locales - All locale codes available on the profile (for the locale switcher).\n * @param activeLocale - The currently active locale code.\n * @param onLocaleSwitch - Callback invoked when the user selects a different locale.\n * @returns The constructed banner `<div>` element.\n */\n build(\n config: MainBanner,\n handlers: ButtonClickHandler,\n privacyPolicyUrl?: string,\n locales?: string[],\n activeLocale?: string,\n onLocaleSwitch?: (locale: string) => void,\n hidePoweredBy = false,\n ): HTMLElement {\n const banner = document.createElement('div')\n banner.id = 'consenti-banner'\n banner.className = `consenti-banner consenti-banner--${config.position}`\n banner.setAttribute('role', 'region')\n banner.setAttribute('aria-label', 'Cookie consent')\n banner.setAttribute('aria-live', 'polite')\n\n const textContainer = document.createElement('div')\n textContainer.className = `consenti-banner__text-container`\n const linksContainer = document.createElement('div')\n linksContainer.className = 'consenti-banner__links'\n\n if (config.heading) {\n const headingTag = config.headingTag ?? 'h2'\n const heading = document.createElement(headingTag)\n heading.id = 'consenti-banner-heading'\n heading.className = 'consenti-banner__heading'\n heading.textContent = config.heading\n textContainer.appendChild(heading)\n }\n\n const text = document.createElement('div')\n text.className = 'consenti-banner__text'\n text.innerHTML = config.htmlText\n\n if (privacyPolicyUrl) {\n const link = document.createElement('a')\n link.href = privacyPolicyUrl\n link.className = 'consenti-banner__link consenti-banner__link--privacy consenti-btn consenti-btn--text'\n link.textContent = 'Privacy Policy'\n link.setAttribute('target', '_blank')\n link.setAttribute('rel', 'noopener noreferrer')\n linksContainer.appendChild(link)\n }\n\n textContainer.appendChild(text)\n\n // Render link-action buttons as styled anchors below the body text\n const linkButtons = config.buttons.filter((b): b is Button & { action: 'link' } => b.action === 'link')\n if (linkButtons.length > 0) {\n for (const btn of linkButtons) {\n if (!btn.url) continue\n const anchor = document.createElement('a')\n anchor.href = btn.url\n anchor.className = `consenti-banner__link consenti-btn consenti-btn--${btn.style ?? 'text'}`\n anchor.textContent = btn.text\n anchor.setAttribute('target', '_blank')\n anchor.setAttribute('rel', 'noopener noreferrer')\n linksContainer.appendChild(anchor)\n }\n }\n if (linkButtons.length > 0 || privacyPolicyUrl) {\n textContainer.appendChild(linksContainer)\n }\n\n banner.appendChild(textContainer)\n\n const actionButtons = config.buttons.filter(b => b.action !== 'link')\n if (actionButtons.length > 0) {\n const btns = document.createElement('div')\n btns.className = 'consenti-banner__buttons'\n for (const btn of actionButtons) {\n btns.appendChild(buildButton(btn, handlers))\n }\n banner.appendChild(btns)\n }\n\n // Controls group: locale switcher + close button (both positioned absolute top-right)\n const hasClose = config.showClose\n const hasLocaleSwitcher = config.showLocaleSwitcher && locales && locales.length > 1\n\n if (hasClose || hasLocaleSwitcher) {\n const controls = document.createElement('div')\n controls.className = 'consenti-banner__controls'\n\n if (hasLocaleSwitcher && locales && onLocaleSwitch) {\n controls.appendChild(buildLocaleSwitcher(locales, activeLocale, onLocaleSwitch))\n }\n\n if (hasClose) {\n const close = document.createElement('button')\n close.type = 'button'\n close.className = 'consenti-banner__close consenti-btn consenti-btn--close'\n close.setAttribute('aria-label', 'Close cookie banner')\n close.textContent = '×'\n close.addEventListener('click', handlers.onClose)\n controls.appendChild(close)\n }\n\n banner.appendChild(controls)\n }\n\n if (config.overlayOpacity && config.overlayOpacity > 0) {\n this.overlayEl = createOverlay(config.overlayOpacity)\n }\n\n const pb = document.createElement('a')\n pb.href = 'https://consenti.dev'\n pb.className = 'consenti-banner__powered-by'\n pb.textContent = 'Powered by Consenti'\n pb.setAttribute('target', '_blank')\n pb.setAttribute('rel', 'noopener noreferrer')\n if (hidePoweredBy) {\n pb.classList.add('consenti-d-none')\n }\n banner.appendChild(pb)\n\n this.el = banner\n return banner\n }\n\n /** Returns the overlay element (if `overlayOpacity > 0`), or `null`. */\n getOverlay(): HTMLElement | null {\n return this.overlayEl\n }\n\n /** Makes the banner and its overlay visible after a `hide()` call. */\n show(): void {\n if (this.el) this.el.style.display = ''\n if (this.overlayEl) this.overlayEl.style.display = ''\n }\n\n /** Hides the banner and its overlay without removing them from the DOM. */\n hide(): void {\n if (this.el) this.el.style.display = 'none'\n if (this.overlayEl) this.overlayEl.style.display = 'none'\n }\n\n /** Removes the banner and overlay from the DOM entirely. */\n remove(): void {\n this.el?.remove()\n this.overlayEl?.remove()\n this.el = null\n this.overlayEl = null\n }\n}\n\n/**\n * Builds a locale switcher button + dropdown list element.\n * Clicking the globe button toggles the dropdown; selecting a locale calls `onSwitch`.\n */\nfunction buildLocaleSwitcher(\n locales: string[],\n activeLocale: string | undefined,\n onSwitch: (locale: string) => void,\n): HTMLElement {\n const wrapper = document.createElement('div')\n wrapper.className = 'consenti-locale-switcher'\n\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'consenti-locale-switcher__btn consenti-btn consenti-btn--close'\n btn.setAttribute('aria-label', 'Switch language')\n btn.setAttribute('aria-haspopup', 'listbox')\n btn.textContent = '🌐'\n\n const list = document.createElement('ul')\n list.className = 'consenti-locale-switcher__list'\n list.setAttribute('role', 'listbox')\n list.setAttribute('aria-label', 'Select language')\n list.hidden = true\n\n for (const locale of locales) {\n const item = document.createElement('li')\n item.setAttribute('role', 'option')\n item.setAttribute('aria-selected', String(locale === activeLocale))\n\n const localeBtn = document.createElement('button')\n localeBtn.type = 'button'\n localeBtn.className = `consenti-locale-switcher__option${locale === activeLocale ? ' consenti-locale-switcher__option--active' : ''}`\n localeBtn.textContent = locale\n localeBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = true\n if (locale !== activeLocale) onSwitch(locale)\n })\n\n item.appendChild(localeBtn)\n list.appendChild(item)\n }\n\n btn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = !list.hidden\n })\n\n // Close dropdown when clicking outside\n document.addEventListener('click', () => { list.hidden = true }, { capture: true, once: false })\n\n wrapper.appendChild(btn)\n wrapper.appendChild(list)\n return wrapper\n}\n","/**\n * WCAG 2.1 focus trap for modal dialogs.\n *\n * When a modal is open, keyboard focus must remain within it (WCAG 2.1 SC 2.1.2\n * \"No Keyboard Trap\" requires that keyboard focus can be moved *to* and *from* a\n * component — trapping focus inside a modal while it is open is the correct pattern).\n *\n * Additionally, pressing Escape dispatches a `consenti:closeRequest` CustomEvent on the\n * container so the modal's close handler can react without a direct reference back here.\n */\n\n/** Selector covering all standard interactive focusable elements. */\nconst FOCUSABLE_SELECTORS = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(', ')\n\n/**\n * Traps keyboard focus within `container`.\n *\n * - Tab cycles from last focusable element back to first.\n * - Shift+Tab cycles from first focusable element to last.\n * - Escape dispatches `consenti:closeRequest` on the container.\n * - Focus is moved to the first focusable element immediately on call.\n *\n * @param container - The modal root element to trap focus inside.\n * @returns A cleanup function — call it when the modal closes to remove event listeners.\n */\nexport function trapFocus(container: HTMLElement): () => void {\n const getFocusable = () =>\n Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS)).filter(\n (el) => !el.closest('[aria-hidden=\"true\"]'),\n )\n\n const handleKeydown = (e: KeyboardEvent): void => {\n if (e.key !== 'Tab') return\n const els = getFocusable()\n if (els.length === 0) return\n const first = els[0]!\n const last = els[els.length - 1]!\n\n if (e.shiftKey) {\n if (document.activeElement === first || document.activeElement === container) {\n e.preventDefault()\n last.focus()\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault()\n first.focus()\n }\n }\n }\n\n const handleEscape = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') {\n container.dispatchEvent(new CustomEvent('consenti:closeRequest', { bubbles: true }))\n }\n }\n\n document.addEventListener('keydown', handleKeydown)\n document.addEventListener('keydown', handleEscape)\n\n getFocusable()[0]?.focus()\n\n return () => {\n document.removeEventListener('keydown', handleKeydown)\n document.removeEventListener('keydown', handleEscape)\n }\n}\n","/**\n * Preference modal DOM builder and state manager.\n *\n * The modal renders a list of consent category toggles (`role=\"switch\"`) in an\n * accessible dialog (`role=\"dialog\"`, `aria-modal=\"true\"`). Each toggle reflects\n * the user's current consent for that category and can be flipped before saving.\n *\n * Legitimate-interest (LI) categories use `'objected'` as their off-state rather than\n * `'denied'`, per the GDPR Art. 21 framework for LI objection.\n *\n * A consent receipt checkbox is optionally rendered when `core.allowReceipt: true`.\n * It resets to unchecked each time the modal is opened so the user must opt in per submission.\n */\n\nimport type { PreferenceModal, Category, ConsentValue, Button } from '../types'\nimport type { ButtonClickHandler } from './buttons'\nimport { buildButton } from './buttons'\nimport { createOverlay } from './overlay'\nimport { trapFocus } from './focus-trap'\n\n/**\n * Builds and manages the preference modal DOM.\n * One `Modal` instance is created per `ConsentiSetup` and reused across openings.\n */\nexport class Modal {\n private el: HTMLElement | null = null\n private triggerEl: HTMLElement | null = null\n private trapCleanup: (() => void) | null = null\n /** Map of category ID → toggle button element, used to read state on submit. */\n private toggleMap = new Map<string, HTMLButtonElement>()\n private receiptCheckbox: HTMLInputElement | null = null\n /** Live toggle state — updated on each click so `getToggleConsent()` reads the latest. */\n private toggleState = new Map<string, boolean>()\n /** When `true`, the modal wrapper is passthrough (overlayOpacity === 0). */\n private passthrough = false\n /** Screen width (px) below which the modal goes full-screen. 0 = disabled. */\n private mobileBreakpoint = 576\n /** Saved body overflow value — restored when modal closes. */\n private savedBodyOverflow: string | undefined = undefined\n\n /**\n * Constructs the modal DOM from the given config.\n * The modal is not inserted into the document until `open()` is called.\n *\n * @param config - Modal layout and content config from the resolved profile.\n * @param initialConsent - Current stored consent, used to pre-set toggle states.\n * @param handlers - Click handlers wired to each button.\n * @param allowReceipt - When `true`, renders the consent receipt download checkbox.\n * @param dpdpaConfig - DPDPA grievance notice config.\n * @param locales - All available locale codes (for locale switcher).\n * @param activeLocale - The currently active locale code.\n * @param onLocaleSwitch - Callback invoked when the user selects a different locale.\n */\n build(\n config: PreferenceModal,\n initialConsent: ConsentValue,\n handlers: ButtonClickHandler,\n allowReceipt: boolean,\n dpdpaConfig?: { dataFiduciary: string; grievanceEmail: string; purposeDescription?: string },\n locales?: string[],\n activeLocale?: string,\n onLocaleSwitch?: (locale: string) => void,\n hidePoweredBy = false,\n ): void {\n this.mobileBreakpoint = config.mobileFullScreenBreakpoint ?? 576\n const position = config.position ?? 'center'\n const wrapper = document.createElement('div')\n wrapper.id = 'consenti-modal'\n wrapper.className = `consenti-modal consenti-modal--${position}`\n wrapper.setAttribute('role', 'dialog')\n wrapper.setAttribute('aria-modal', 'true')\n\n const overlayOpacity = config.overlayOpacity ?? 50\n this.passthrough = overlayOpacity === 0\n if (overlayOpacity > 0) {\n // When `persistent: true`, clicking the overlay does not close the modal —\n // the user must use one of the footer buttons.\n const overlayClickHandler = config.persistent ? undefined : () => handlers.onClose()\n const overlay = createOverlay(overlayOpacity, overlayClickHandler)\n overlay.className = 'consenti-modal__overlay'\n wrapper.appendChild(overlay)\n } else {\n // No overlay — let pointer events pass through the wrapper to the page beneath.\n wrapper.classList.add('consenti-modal--passthrough')\n }\n\n const container = document.createElement('div')\n container.className = 'consenti-modal__container'\n\n const header = document.createElement('div')\n header.className = 'consenti-modal__header'\n\n const body = document.createElement('div')\n body.className = 'consenti-modal__body'\n\n if (config.heading) {\n const headingTag = config.headingTag ?? 'h2'\n const heading = document.createElement(headingTag)\n heading.id = 'consenti-modal-heading'\n heading.className = 'consenti-modal__heading'\n heading.textContent = config.heading\n header.appendChild(heading)\n wrapper.setAttribute('aria-labelledby', 'consenti-modal-heading')\n }\n\n if (config.subheading) {\n const sub = document.createElement('p')\n sub.className = 'consenti-modal__subheading'\n sub.textContent = config.subheading\n header.appendChild(sub)\n }\n\n // Header controls: locale switcher + close\n const hasClose = config.showClose !== false\n const hasLocaleSwitcher = config.showLocaleSwitcher && locales && locales.length > 1\n\n if (hasLocaleSwitcher || hasClose) {\n const headerControls = document.createElement('div')\n headerControls.className = 'consenti-modal__header-controls'\n\n if (hasLocaleSwitcher && locales && onLocaleSwitch) {\n headerControls.appendChild(buildModalLocaleSwitcher(locales, activeLocale, onLocaleSwitch))\n }\n\n if (hasClose) {\n const close = document.createElement('button')\n close.type = 'button'\n close.className = 'consenti-modal__close consenti-btn consenti-btn--close'\n close.setAttribute('aria-label', 'Close preference modal')\n close.textContent = '×'\n close.addEventListener('click', handlers.onClose)\n headerControls.appendChild(close)\n }\n\n header.appendChild(headerControls)\n }\n container.appendChild(header)\n\n if (config.htmlText) {\n const desc = document.createElement('div')\n desc.className = 'consenti-modal__text'\n desc.innerHTML = config.htmlText\n body.appendChild(desc)\n }\n\n // Render link-action buttons as styled anchors below the intro text\n const linkButtons = config.buttons.filter((b): b is Button & { action: 'link' } => b.action === 'link')\n if (linkButtons.length > 0) {\n const linksContainer = document.createElement('div')\n linksContainer.className = 'consenti-modal__links'\n for (const btn of linkButtons) {\n if (!btn.url) continue\n const anchor = document.createElement('a')\n anchor.href = btn.url\n anchor.className = `consenti-modal__link consenti-btn consenti-btn--${btn.style ?? 'text'}`\n anchor.textContent = btn.text\n anchor.setAttribute('target', '_blank')\n anchor.setAttribute('rel', 'noopener noreferrer')\n linksContainer.appendChild(anchor)\n }\n container.appendChild(linksContainer)\n }\n\n const categories = document.createElement('div')\n categories.className = 'consenti-modal__categories'\n\n for (const cat of config.categories) {\n const catEl = this.buildCategory(cat, initialConsent)\n categories.appendChild(catEl)\n }\n body.appendChild(categories)\n container.appendChild(body)\n\n if (allowReceipt) {\n body.appendChild(this.buildReceiptOption())\n }\n\n if (dpdpaConfig) {\n const notice = document.createElement('div')\n notice.className = 'consenti-modal__dpdpa-notice'\n const esc = (s: string) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;').replace(/'/g, '&#x27;')\n const safeEmail = dpdpaConfig.grievanceEmail.includes('@') && !dpdpaConfig.grievanceEmail.toLowerCase().startsWith('javascript:')\n ? dpdpaConfig.grievanceEmail : ''\n notice.innerHTML = [\n `<strong>Data Fiduciary:</strong> ${esc(dpdpaConfig.dataFiduciary)}.`,\n dpdpaConfig.purposeDescription ? ` <strong>Purpose:</strong> ${esc(dpdpaConfig.purposeDescription)}.` : '',\n ` You may withdraw consent at any time or contact our Grievance Officer at`,\n safeEmail ? ` <a href=\"mailto:${esc(safeEmail)}\">${esc(safeEmail)}</a>.` : '.',\n ].join('')\n container.appendChild(notice)\n }\n\n const btns = document.createElement('div')\n btns.className = 'consenti-modal__buttons'\n for (const btn of config.buttons.filter(b => b.action !== 'link')) {\n btns.appendChild(buildButton(btn, handlers))\n }\n container.appendChild(btns)\n\n const pb = document.createElement('a')\n pb.href = 'https://consenti.dev'\n pb.className = 'consenti-modal__powered-by'\n pb.textContent = 'Powered by Consenti'\n pb.setAttribute('target', '_blank')\n pb.setAttribute('rel', 'noopener noreferrer')\n if (hidePoweredBy) {\n pb.classList.add('consenti-d-none')\n }\n body.appendChild(pb)\n\n wrapper.appendChild(container)\n\n wrapper.addEventListener('consenti:closeRequest', () => handlers.onClose())\n\n this.el = wrapper\n }\n\n private buildCategory(cat: Category, initialConsent: ConsentValue): HTMLElement {\n const isLI = cat.type === 'legitimate_interest'\n const isMandatory = cat.mandatory === true\n\n let defaultOn: boolean\n if (isMandatory) {\n defaultOn = true\n } else if (isLI) {\n // LI categories are \"on\" unless the user has actively objected\n defaultOn = initialConsent[cat.cookies[0] ?? ''] !== 'objected'\n } else {\n defaultOn = initialConsent[cat.cookies[0] ?? ''] === 'granted'\n }\n this.toggleState.set(cat.id, defaultOn)\n\n const wrapper = document.createElement('div')\n wrapper.className = `consenti-category${isMandatory ? ' consenti-category--mandatory' : ''}`\n\n const header = document.createElement('div')\n header.className = 'consenti-category__header'\n\n const headingTag = cat.headingTag ?? 'h3'\n const heading = document.createElement(headingTag)\n heading.id = `consenti-cat-${cat.id}-heading`\n heading.className = 'consenti-category__heading'\n heading.textContent = cat.heading\n header.appendChild(heading)\n\n const toggle = document.createElement('button')\n toggle.type = 'button'\n toggle.className = `consenti-category__toggle${isMandatory ? ' consenti-category__toggle--mandatory' : ''}`\n toggle.setAttribute('role', 'switch')\n toggle.setAttribute('aria-checked', String(defaultOn))\n toggle.setAttribute('aria-labelledby', `consenti-cat-${cat.id}-heading`)\n if (isMandatory) toggle.setAttribute('aria-disabled', 'true')\n\n const checkIcon = document.createElement('span')\n checkIcon.className = 'consenti-category__toggle-icon consenti-category__toggle-icon--check'\n checkIcon.setAttribute('aria-hidden', 'true')\n checkIcon.innerHTML = '<svg viewBox=\"0 0 10 10\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M1.5 5L4 7.5L8.5 2.5\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'\n toggle.appendChild(checkIcon)\n\n const crossIcon = document.createElement('span')\n crossIcon.className = 'consenti-category__toggle-icon consenti-category__toggle-icon--cross'\n crossIcon.setAttribute('aria-hidden', 'true')\n crossIcon.innerHTML = '<svg viewBox=\"0 0 10 10\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 2L8 8M8 2L2 8\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>'\n toggle.appendChild(crossIcon)\n\n const knob = document.createElement('span')\n knob.className = 'consenti-category__toggle-knob'\n knob.setAttribute('aria-hidden', 'true')\n toggle.appendChild(knob)\n\n if (!isMandatory) {\n toggle.addEventListener('click', () => {\n const current = this.toggleState.get(cat.id) ?? false\n const next = !current\n this.toggleState.set(cat.id, next)\n toggle.setAttribute('aria-checked', String(next))\n })\n }\n\n this.toggleMap.set(cat.id, toggle)\n header.appendChild(toggle)\n wrapper.appendChild(header)\n\n const text = document.createElement('div')\n text.className = 'consenti-category__text'\n text.innerHTML = cat.htmlText\n wrapper.appendChild(text)\n\n if (isLI && cat.legitimateInterest?.description) {\n const li = document.createElement('p')\n li.className = 'consenti-category__li-desc'\n li.textContent = cat.legitimateInterest.description\n wrapper.appendChild(li)\n }\n\n return wrapper\n }\n\n private buildReceiptOption(): HTMLElement {\n const wrapper = document.createElement('div')\n wrapper.className = 'consenti-modal__receipt-option'\n\n const label = document.createElement('label')\n label.className = 'consenti-modal__receipt-label'\n\n const checkbox = document.createElement('input')\n checkbox.type = 'checkbox'\n checkbox.className = 'consenti-modal__receipt-checkbox'\n checkbox.id = 'consenti-receipt-checkbox'\n checkbox.setAttribute('aria-describedby', 'consenti-receipt-desc')\n this.receiptCheckbox = checkbox\n\n label.appendChild(checkbox)\n label.append(' Get a copy of my consent choices (JSON)')\n wrapper.appendChild(label)\n\n const desc = document.createElement('p')\n desc.id = 'consenti-receipt-desc'\n desc.className = 'consenti-modal__receipt-desc'\n desc.textContent = 'A JSON file will be downloaded to your device when you save your preferences.'\n wrapper.appendChild(desc)\n\n return wrapper\n }\n\n /**\n * Appends the modal to `document.body` and activates the focus trap.\n * The receipt checkbox (if present) is reset to unchecked.\n *\n * @param triggerEl - The element that opened the modal. Focus returns here on `close()`.\n */\n open(triggerEl?: HTMLElement): void {\n if (!this.el) return\n this.triggerEl = triggerEl ?? (document.activeElement as HTMLElement)\n if (this.receiptCheckbox) this.receiptCheckbox.checked = false\n const isFullscreen = this.mobileBreakpoint > 0 && window.innerWidth <= this.mobileBreakpoint\n if (isFullscreen) {\n this.el.classList.add('consenti-modal--fullscreen')\n } else {\n this.el.classList.remove('consenti-modal--fullscreen')\n }\n document.body.appendChild(this.el)\n // Lock page scroll when the modal has an overlay or is in mobile full-screen mode.\n // Skip when passthrough and not full-screen — the user can still scroll the page.\n if (!this.passthrough || isFullscreen) {\n this.savedBodyOverflow = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n }\n // Skip focus trap when overlay is transparent — the user can interact with the\n // page behind the modal panel so trapping keyboard focus would be counterproductive.\n if (!this.passthrough) {\n this.trapCleanup = trapFocus(this.el)\n }\n }\n\n /**\n * Removes the modal from `document.body`, deactivates the focus trap,\n * and returns focus to `triggerEl`.\n */\n close(): void {\n this.trapCleanup?.()\n this.trapCleanup = null\n if (this.savedBodyOverflow !== undefined) {\n document.body.style.overflow = this.savedBodyOverflow\n this.savedBodyOverflow = undefined\n }\n this.el?.remove()\n this.triggerEl?.focus()\n this.triggerEl = null\n }\n\n /** Returns `true` if the modal is currently mounted in the document. */\n isOpen(): boolean {\n return this.el?.isConnected === true\n }\n\n /** Returns `true` if the receipt download checkbox is checked at the time of calling. */\n isReceiptRequested(): boolean {\n return this.receiptCheckbox?.checked ?? false\n }\n\n /**\n * Reads the current toggle state map and converts it to a `ConsentValue`.\n *\n * Called by the submit handler to build the consent object before writing to storage.\n *\n * @param categories - The category list from the resolved profile (provides cookie IDs and type).\n */\n getToggleConsent(categories: Category[]): ConsentValue {\n const consent: ConsentValue = {}\n for (const cat of categories) {\n const isLI = cat.type === 'legitimate_interest'\n const isMandatory = cat.mandatory === true\n const on = this.toggleState.get(cat.id) ?? false\n\n for (const cookieId of cat.cookies) {\n if (isMandatory) {\n consent[cookieId] = 'granted'\n } else if (isLI) {\n // LI refusal is 'objected' not 'denied' — distinct status for Art. 21 records\n consent[cookieId] = on ? 'granted' : 'objected'\n } else {\n consent[cookieId] = on ? 'granted' : 'denied'\n }\n }\n }\n return consent\n }\n\n /** Removes the modal element from the DOM and clears all internal state. */\n destroy(): void {\n this.trapCleanup?.()\n if (this.savedBodyOverflow !== undefined) {\n document.body.style.overflow = this.savedBodyOverflow\n this.savedBodyOverflow = undefined\n }\n this.el?.remove()\n this.el = null\n this.toggleMap.clear()\n this.toggleState.clear()\n this.receiptCheckbox = null\n }\n}\n\n/** Builds a compact locale switcher for the modal header. */\nfunction buildModalLocaleSwitcher(\n locales: string[],\n activeLocale: string | undefined,\n onSwitch: (locale: string) => void,\n): HTMLElement {\n const wrapper = document.createElement('div')\n wrapper.className = 'consenti-locale-switcher consenti-locale-switcher--modal'\n\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'consenti-locale-switcher__btn consenti-btn consenti-btn--close'\n btn.setAttribute('aria-label', 'Switch language')\n btn.setAttribute('aria-haspopup', 'listbox')\n btn.textContent = '🌐'\n\n const list = document.createElement('ul')\n list.className = 'consenti-locale-switcher__list'\n list.setAttribute('role', 'listbox')\n list.setAttribute('aria-label', 'Select language')\n list.hidden = true\n\n for (const locale of locales) {\n const item = document.createElement('li')\n item.setAttribute('role', 'option')\n item.setAttribute('aria-selected', String(locale === activeLocale))\n\n const localeBtn = document.createElement('button')\n localeBtn.type = 'button'\n localeBtn.className = `consenti-locale-switcher__option${locale === activeLocale ? ' consenti-locale-switcher__option--active' : ''}`\n localeBtn.textContent = locale\n localeBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = true\n if (locale !== activeLocale) onSwitch(locale)\n })\n\n item.appendChild(localeBtn)\n list.appendChild(item)\n }\n\n btn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = !list.hidden\n })\n\n document.addEventListener('click', () => { list.hidden = true }, { capture: true, once: false })\n\n wrapper.appendChild(btn)\n wrapper.appendChild(list)\n return wrapper\n}\n","/**\n * DOM mount point and screen reader utilities.\n *\n * All Consenti UI elements (banner, modal, overlay) are inserted into a single\n * `<div id=\"consenti-root\">` appended to `document.body`. This keeps the DOM\n * footprint predictable and makes cleanup trivial — `unmount()` removes the root\n * and any injected `<style>` tag in one call.\n *\n * The `announceToScreenReader()` method manages a visually-hidden ARIA live region\n * (`#consenti-announcer`) used to announce non-visual state changes (e.g.\n * \"Your cookie preferences have been saved.\").\n */\n\nimport { isClient } from '../utils/ssr'\n\nconst ROOT_ID = 'consenti-root'\n\n/**\n * Manages the `#consenti-root` mount point and related DOM infrastructure.\n * One `Renderer` instance is created per `ConsentiSetup`.\n */\nexport class Renderer {\n private root: HTMLElement | null = null\n private styleEl: HTMLElement | null = null\n\n /**\n * Returns the mount-point element, creating `#consenti-root` if needed.\n *\n * When `rootEl` is supplied:\n * - `string` → resolved via `document.querySelector`. Throws if no match.\n * - `HTMLElement` → used directly. Throws if not attached to the document.\n * On subsequent calls the previously stored root is returned immediately.\n *\n * @throws `Error` if called in SSR or if a supplied `rootEl` cannot be resolved.\n */\n mount(rootEl?: string | HTMLElement): HTMLElement {\n if (!isClient()) throw new Error('[Consenti] Renderer.mount() called in SSR context')\n\n if (this.root) return this.root\n\n if (rootEl !== undefined) {\n if (typeof rootEl === 'string') {\n const found = document.querySelector<HTMLElement>(rootEl)\n if (!found) throw new Error(`[Consenti] rootEl selector \"${rootEl}\" did not match any element.`)\n this.root = found\n } else {\n if (!document.contains(rootEl)) throw new Error('[Consenti] rootEl element is not attached to the document.')\n this.root = rootEl\n }\n return this.root\n }\n\n let root = document.getElementById(ROOT_ID)\n if (!root) {\n root = document.createElement('div')\n root.id = ROOT_ID\n document.body.appendChild(root)\n }\n this.root = root\n return root\n }\n\n /**\n * Injects a `<style id=\"consenti-styles\">` tag into `<head>` with the given CSS string.\n * No-ops if the style element already exists (idempotent).\n *\n * @param css - The CSS content to inject.\n */\n injectStyles(css: string): void {\n if (!isClient()) return\n if (this.styleEl) return\n\n const style = document.createElement('style')\n style.id = 'consenti-styles'\n style.textContent = css\n document.head.appendChild(style)\n this.styleEl = style\n }\n\n /** Removes `#consenti-root` and the injected `<style>` tag from the document. */\n unmount(): void {\n this.root?.remove()\n this.styleEl?.remove()\n this.root = null\n this.styleEl = null\n }\n\n /**\n * Announces a message to screen readers via a visually-hidden `aria-live=\"polite\"` region.\n *\n * The region is created on first call and reused. The message is cleared then\n * re-set via `requestAnimationFrame` to guarantee the change is detected by\n * AT even if the same string is announced twice in a row.\n *\n * @param message - The message to announce (plain text, not HTML).\n */\n announceToScreenReader(message: string): void {\n if (!isClient()) return\n let announcer = document.getElementById('consenti-announcer')\n if (!announcer) {\n announcer = document.createElement('div')\n announcer.id = 'consenti-announcer'\n announcer.setAttribute('role', 'status')\n announcer.setAttribute('aria-live', 'polite')\n announcer.setAttribute('aria-atomic', 'true')\n // Visually hidden but still in the accessibility tree\n announcer.style.cssText =\n 'position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;'\n document.body.appendChild(announcer)\n }\n announcer.textContent = ''\n requestAnimationFrame(() => {\n if (announcer) announcer.textContent = message\n })\n }\n}\n","/**\n * Main entry point for the Consenti widget.\n *\n * Instantiate one `ConsentiSetup` per page (or per profile ID if you run multiple\n * consent contexts on the same page). The constructor is safe to call during SSR —\n * it silently no-ops when `window` is not defined.\n *\n * @example\n * ```ts\n * const widget = new ConsentiSetup({\n * compliance: { type: 'opt-in' },\n * })\n * await widget.ready\n * console.log(widget.hasConsent())\n * ```\n */\n\nimport type {\n ConsentiConfig,\n ConsentValue,\n ConsentAction,\n ConsentEvent,\n ResolvedProfile,\n ConsentiPlugin,\n ConsentiWidgetAPI,\n ConsentiMessage,\n ConsentiEventName,\n ThemeConfig,\n DeepPartial,\n} from '../types'\nimport { CONSENTI_CSS } from '../styles/consenti-css'\nimport { isClient } from '../utils/ssr'\nimport { ConsentStorage } from '../utils/storage'\nimport { ConsentiChannel } from '../utils/broadcast'\nimport { generateReceipt, downloadReceipt } from '../utils/receipt'\nimport { ConsentStore } from './consent-store'\nimport { EventBus } from './event-bus'\nimport { resolveProfile } from './profile-resolver'\nimport { detectGPC, applyGPCToConsent } from './gpc'\nimport { Banner } from '../ui/banner'\nimport { Modal } from '../ui/modal'\nimport { Renderer } from '../ui/renderer'\nimport type { ButtonClickHandler } from '../ui/buttons'\nimport { deepMerge } from '../utils/locale'\nimport { logger, initConsole } from '../utils/console'\n\nexport class ConsentiSetup implements ConsentiWidgetAPI {\n private initialized = false\n private initializing = false\n private profile: ResolvedProfile | null = null\n private resolvedBase: ResolvedProfile | null = null\n private consentStore: ConsentStore | null = null\n private eventBus: EventBus | null = null\n private banner: Banner | null = null\n private modal: Modal | null = null\n private renderer: Renderer | null = null\n private broadcastChannel = new ConsentiChannel()\n private readyResolve!: () => void\n private readyPromise: Promise<void>\n private plugins: ConsentiPlugin[] = []\n /** Signature from the most recent `writeAndSign` call. Included in consent receipts. */\n private lastSignature: string | undefined\n /** Tracks which banner variant is currently visible, or null when hidden. */\n private _bannerVariant: 'main' | 'gpc' | null = null\n /** Maps original handler functions to their DOM-wrapped listeners for on/off bookkeeping. */\n private _eventHandlers = new Map<\n (data: ConsentEvent) => void,\n Array<{ name: string; wrapped: EventListener }>\n >()\n\n /**\n * Creates and initialises the consent widget.\n *\n * Construction is synchronous; the async `init()` is launched in the background.\n * Use `await widget.ready` or `widget.onReady(cb)` to run code after init completes.\n *\n * @param config - Widget configuration. See `ConsentiConfig` for all options.\n */\n constructor(private config: ConsentiConfig) {\n this.readyPromise = new Promise<void>((resolve) => {\n this.readyResolve = resolve\n })\n\n if (!isClient()) return\n\n initConsole(config.core.console ?? ['error'])\n\n if (config.api?.enabled && config.core.storage === 'localStorage') {\n logger.warn(\n 'localStorage storage mode cannot be used with API mode ' +\n '(API sets cookies via Set-Cookie header). Falling back to cookie storage.',\n )\n config.core.storage = 'cookie'\n }\n\n if (config.autoInit !== false) {\n this.init().catch((err) => logger.warn('Init error:', err))\n }\n }\n\n private profileId(): number {\n return this.profile?.id ?? 0\n }\n\n /**\n * Starts widget initialisation. Called automatically unless `autoInit: false` is set.\n * Can be called explicitly after construction when `autoInit: false`, or after `destroy()`\n * to re-initialise the same instance.\n */\n async init(): Promise<void> {\n if (!isClient()) return\n if (this.initializing) return\n this.initializing = true\n // Reset ready promise so callers can await this init cycle\n this.initialized = false\n this.readyPromise = new Promise<void>((resolve) => { this.readyResolve = resolve })\n\n try {\n const storageMode = this.config.core.storage ?? 'cookie'\n const userId = this.config.core.userId\n ?? ConsentStore.getOrCreateUserId(this.profileId(), storageMode)\n\n const resolvedProfile = await resolveProfile(this.config)\n this.resolvedBase = resolvedProfile\n\n // Apply widget-level config overrides on top of the resolved profile\n if (this.config.darkMode !== undefined) resolvedProfile.darkMode = this.config.darkMode\n\n const profile = this.config.profileOverride\n ? deepMerge<ResolvedProfile>(resolvedProfile, this.config.profileOverride)\n : resolvedProfile\n this.profile = profile\n\n this.eventBus = new EventBus(this.profileId(), this.config.utils?.gtm)\n this.consentStore = new ConsentStore(\n userId,\n this.profileId(),\n storageMode,\n this.config.core.cookieDomains,\n )\n this.consentStore.setProfileCookies(profile.cookies)\n this.renderer = new Renderer()\n if (!this.config.core.disableCssTemplate && CONSENTI_CSS) {\n this.renderer.injectStyles(CONSENTI_CSS)\n }\n this.banner = new Banner()\n this.modal = new Modal()\n\n this.applyTheme()\n this.applyDarkMode()\n this.broadcastChannel.connect((msg) => this.handleBroadcastMessage(msg))\n\n await this.runPlugins()\n\n // Determine signing key: API-returned key takes precedence, then config fallback\n const signingKey = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n\n const existing =\n signingKey\n ? await this.consentStore.readAndVerify(signingKey)\n : this.consentStore.read()\n const hasExisting = existing !== null\n\n this.eventBus.dispatch('consenti:bannerInitialized', {\n profileId: this.profileId(),\n hasExistingConsent: hasExisting,\n })\n\n // GPC handling — widget config overrides profile JSON when explicitly set\n const profileGpcMode = profile.gpcMode\n const gpcMode: boolean | 'strict' =\n this.config.core.autoHonorGPC !== undefined\n ? this.config.core.autoHonorGPC\n : profileGpcMode === 'strict' ? 'strict'\n : profileGpcMode === 'honor' ? true\n : false\n if (gpcMode && detectGPC()) {\n const gpcConsent = applyGPCToConsent(profile.cookies, {}, gpcMode)\n\n if (gpcMode === 'strict') {\n // Silent mode: write consent immediately, dispatch event, skip any banner\n await this.submitConsent(gpcConsent, true)\n this.initialized = true\n this.readyResolve()\n return\n }\n\n // Non-strict: only apply GPC denial on first visit.\n // If the user has already explicitly given consent, respect that choice and\n // fall through to normal banner logic (which will skip the banner since\n // hasExisting is true). Re-running the GPC write on every page load would\n // overwrite the user's explicit consent after they've reviewed via the GPC banner.\n if (!hasExisting) {\n // Persist GPC denial immediately so cookies are dropped now, then show\n // the GPC banner for the user to review/adjust.\n // BroadcastChannel is same-window-exempt, so the banner in this tab is\n // not hidden by the consentUpdated handler received in other tabs.\n const gpcSigningKey = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n if (gpcSigningKey && this.consentStore) {\n this.lastSignature = await this.consentStore.writeAndSign(gpcConsent, gpcSigningKey)\n } else {\n this.consentStore?.write(gpcConsent)\n }\n this.broadcastChannel.send({\n type: 'consentUpdated',\n consent: gpcConsent,\n profileId: this.profileId(),\n })\n this.eventBus?.dispatch('consenti:consentSubmitted', {\n consent: gpcConsent,\n gpcDetected: true,\n consentId: crypto.randomUUID(),\n consentAction: this.deriveAction(gpcConsent, false),\n pageUrl: window.location.href,\n })\n this.showBanner(true)\n this.initialized = true\n this.readyResolve()\n return\n }\n }\n\n // Derive opt-out mode from profile complianceGroup (replaces old core.regulation field)\n const compGroup = profile.complianceGroup ?? ''\n const isOptOut = compGroup === 'opt-out'\n const isOptOutStrict = compGroup === 'opt-out-strict'\n\n // CCPA opt-out: silently write all-granted on first visit; no banner.\n if (isOptOut && !hasExisting) {\n const defaultConsent: ConsentValue = {}\n for (const cookie of profile.cookies) {\n defaultConsent[cookie.id] = 'granted'\n }\n const ccpaSigKey = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n if (ccpaSigKey) {\n await this.consentStore.writeAndSign(defaultConsent, ccpaSigKey)\n } else {\n this.consentStore.write(defaultConsent)\n }\n this.initialized = true\n this.readyResolve()\n return\n }\n\n // CPRA: opt-out for non-sensitive; opt-in required for sensitive data.\n // GPC triggers Do Not Sell + Do Not Share simultaneously (CPRA s.1798.135).\n if (isOptOutStrict && !hasExisting) {\n const gpcActive = this.config.core.autoHonorGPC && detectGPC()\n const defaultConsent: ConsentValue = {}\n for (const cookie of profile.cookies) {\n if (cookie.mandatory) { defaultConsent[cookie.id] = 'granted'; continue }\n if (cookie.cpraCategory === 'sensitive') {\n defaultConsent[cookie.id] = 'denied'\n } else if (gpcActive && (cookie.cpraCategory === 'sale' || cookie.cpraCategory === 'sharing')) {\n defaultConsent[cookie.id] = 'denied'\n } else {\n defaultConsent[cookie.id] = 'granted'\n }\n }\n const sigKey = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n if (sigKey) {\n await this.consentStore.writeAndSign(defaultConsent, sigKey)\n } else {\n this.consentStore.write(defaultConsent)\n }\n this.initialized = true\n this.readyResolve()\n return\n }\n\n // DPDPA: opt-in (same as GDPR — show banner). GPC is not a recognised opt-out under DPDPA.\n // (GDPR and DPDPA both fall through to showBanner below.)\n\n if (!hasExisting) {\n this.showBanner()\n }\n\n this.initialized = true\n this.readyResolve()\n } finally {\n this.initializing = false\n }\n }\n\n /** Applies the `consenti-root--dark` class based on config or profile flag. */\n private applyDarkMode(): void {\n if (!this.renderer) return\n const isDark = this.config.darkMode ?? this.profile?.darkMode ?? false\n const root = this.renderer.mount(this.config.rootEl)\n if (isDark) {\n root.classList.add('consenti-root--dark')\n } else {\n root.classList.remove('consenti-root--dark')\n }\n }\n\n /**\n * Injects CSS custom property overrides supplied via `core.theme` into the\n * Consenti root element so they cascade down to all widget elements.\n */\n private applyTheme(): void {\n const theme = this.config.core.theme\n if (!theme || !this.renderer) return\n\n const map: Record<string, string | undefined> = {\n '--consenti-color-bg': theme.bgColor,\n '--consenti-color-text': theme.textColor,\n '--consenti-color-primary': theme.primaryColor,\n '--consenti-color-primary-text': theme.primaryTextColor,\n '--consenti-color-secondary': theme.secondaryColor,\n '--consenti-color-secondary-text': theme.secondaryTextColor,\n '--consenti-color-border': theme.borderColor,\n '--consenti-font-family': theme.fontFamily,\n '--consenti-font-size-base': theme.fontSizeBase,\n '--consenti-font-size-heading': theme.fontSizeHeading,\n '--consenti-border-radius': theme.borderRadius,\n '--consenti-border-radius-btn': theme.buttonBorderRadius,\n '--consenti-toggle-bg-on': theme.toggleBgOn,\n '--consenti-toggle-bg-off': theme.toggleBgOff,\n '--consenti-color-accent': theme.accentColor,\n '--consenti-color-accent-text': theme.accentTextColor,\n }\n\n const root = this.renderer.mount(this.config.rootEl)\n for (const [prop, value] of Object.entries(map)) {\n if (value !== undefined) root.style.setProperty(prop, value)\n }\n\n if (theme.fontSizeMultiplier) {\n // Multiply base and heading font sizes if a multiplier is given\n const mult = parseFloat(theme.fontSizeMultiplier)\n if (!isNaN(mult)) {\n const baseSize = parseFloat(getComputedStyle(root).getPropertyValue('--consenti-font-size-base') || '14')\n const headingSize = parseFloat(getComputedStyle(root).getPropertyValue('--consenti-font-size-heading') || '16')\n root.style.setProperty('--consenti-font-size-base', `${Math.round(baseSize * mult)}px`)\n root.style.setProperty('--consenti-font-size-heading', `${Math.round(headingSize * mult)}px`)\n }\n }\n }\n\n private async runPlugins(): Promise<void> {\n this.plugins = this.config.plugins ?? []\n for (const plugin of this.plugins) {\n try {\n await plugin.initialize(this)\n } catch (err) {\n logger.warn('Plugin initialize error:', err)\n }\n }\n }\n\n /**\n * Invokes an optional plugin hook on all registered plugins.\n * Async hooks are awaited; errors are caught and logged so a broken plugin\n * never interferes with the consent flow.\n *\n * @param invoke - Function that calls the desired hook method on a single plugin.\n */\n private async callPluginHook(invoke: (plugin: ConsentiPlugin, widget: ConsentiWidgetAPI) => void | Promise<void>): Promise<void> {\n for (const plugin of this.plugins) {\n try {\n await invoke(plugin, this)\n } catch (err) {\n logger.warn('Plugin hook failed:', err)\n }\n }\n }\n\n private _buildGrantAllConsent(onlyMandatory = false): ConsentValue {\n const consent: ConsentValue = {}\n for (const cookie of this.profile?.cookies ?? []) {\n consent[cookie.id] = onlyMandatory && !cookie.mandatory ? 'denied' : 'granted'\n }\n return consent\n }\n\n private _buildDenyAllConsent(includingMandatory = false): ConsentValue {\n const consent: ConsentValue = {}\n for (const cookie of this.profile?.cookies ?? []) {\n consent[cookie.id] = !includingMandatory && cookie.mandatory ? 'granted' : 'denied'\n }\n return consent\n }\n\n private buildHandlers(context: 'banner' | 'modal'): ButtonClickHandler {\n return {\n onGrantAll: () => {\n void this.submitConsent(this._buildGrantAllConsent())\n if (context === 'banner') { this.banner?.hide(); this._bannerVariant = null }\n },\n onDenyAll: () => {\n void this.submitConsent(this._buildDenyAllConsent())\n if (context === 'banner') { this.banner?.hide(); this._bannerVariant = null }\n },\n onSubmit: () => {\n if (context === 'modal' && this.modal && this.profile) {\n const consent = this.modal.getToggleConsent(this.profile.preferenceModal.categories)\n void this.submitConsent(consent)\n this.hideModal()\n } else {\n const consent: ConsentValue = {}\n for (const cookie of this.profile?.cookies ?? []) {\n if (cookie.mandatory) consent[cookie.id] = 'granted'\n }\n void this.submitConsent(consent)\n if (context === 'banner') { this.banner?.hide(); this._bannerVariant = null }\n }\n },\n onManage: () => {\n this.showModal()\n },\n onClose: () => {\n if (context === 'modal') {\n this.hideModal()\n } else {\n this.banner?.hide()\n this._bannerVariant = null\n this.broadcastChannel.send({ type: 'bannerClosed' })\n this.eventBus?.dispatch('consenti:bannerVisibility', { show: false, action: true })\n void this.callPluginHook((p) => p.onBannerHide?.())\n }\n },\n onGrantSpecific: (cookieIds: string[]) => {\n const consent: ConsentValue = {}\n for (const cookie of this.profile?.cookies ?? []) {\n if (cookie.mandatory || cookieIds.includes(cookie.id)) {\n consent[cookie.id] = 'granted'\n } else {\n consent[cookie.id] = 'denied'\n }\n }\n void this.submitConsent(consent)\n if (context === 'banner') this.hideBanner()\n },\n }\n }\n\n /**\n * Classifies a consent submission as `accept_all`, `reject_all`, `custom`, or `update`.\n * Used to enrich events and API payloads with intent metadata.\n */\n private deriveAction(fullConsent: ConsentValue, hadExistingConsent: boolean): ConsentAction {\n if (hadExistingConsent) return 'update'\n const nonMandatory = this.profile?.cookies.filter((c) => !c.mandatory) ?? []\n if (nonMandatory.length === 0) return 'custom'\n const allGranted = nonMandatory.every((c) => fullConsent[c.id] === 'granted')\n const allDenied = nonMandatory.every(\n (c) => fullConsent[c.id] === 'denied' || fullConsent[c.id] === 'objected',\n )\n if (allGranted) return 'accept_all'\n if (allDenied) return 'reject_all'\n return 'custom'\n }\n\n private handleBroadcastMessage(msg: ConsentiMessage): void {\n switch (msg.type) {\n case 'consentUpdated':\n if (msg.profileId !== this.profileId()) return\n this.consentStore?.setInMemory(msg.consent)\n this.banner?.hide()\n if (this.modal?.isOpen()) this.modal.close()\n this.eventBus?.dispatch('consenti:consentSubmitted', {\n consent: msg.consent,\n fromBroadcast: true,\n })\n break\n case 'bannerClosed':\n this.banner?.hide()\n this._bannerVariant = null\n break\n case 'consentDeleted':\n this.consentStore?.clearInMemory()\n break\n }\n }\n\n // ─── Public API ──────────────────────────────────────────────────────────────\n\n /**\n * Returns `true` if the visitor has saved consent for the active profile.\n * Always returns `false` during SSR.\n */\n hasConsent(): boolean {\n if (!isClient()) return false\n return this.consentStore?.hasConsent() ?? false\n }\n\n /**\n * Returns the stored consent map, or `null` if no consent has been saved.\n * Always returns `null` during SSR.\n */\n getConsent(): ConsentValue | null {\n if (!isClient()) return null\n return this.consentStore?.getConsent() ?? null\n }\n\n /**\n * Returns the `Date` when consent was last saved, or `false` if no consent exists.\n * Always returns `false` during SSR.\n */\n getConsentDate(): Date | false {\n if (!isClient()) return false\n return this.consentStore?.getConsentDate() ?? false\n }\n\n /**\n * Returns the consent map in Google Consent Mode v2 format, ready to pass to\n * `gtag('consent', 'update', ...)`. Returns `null` if no consent has been saved.\n *\n * Maps Consenti cookie IDs to the seven Google Consent Mode v2 parameters.\n * Cookies whose IDs exactly match a GCM v2 key are forwarded directly.\n * Non-GCM cookies are ignored by this method (they appear in `getConsent()` only).\n */\n getGTMConsent(): Record<string, string> | null {\n const consent = this.getConsent()\n if (!consent) return null\n\n // The seven Google Consent Mode v2 parameters with their safe defaults.\n const gcmKeys = [\n 'ad_storage',\n 'analytics_storage',\n 'ad_user_data',\n 'ad_personalization',\n 'functionality_storage',\n 'personalization_storage',\n 'security_storage',\n ] as const\n\n const result: Record<string, string> = {}\n\n for (const key of gcmKeys) {\n const raw = consent[key]\n // Map Consenti statuses to GCM values: 'objected' is treated as 'denied' for GCM\n if (raw === 'granted') {\n result[key] = 'granted'\n } else if (raw === 'denied' || raw === 'objected') {\n result[key] = 'denied'\n } else {\n // Key not in consent map — use a safe default\n const safeDefault = (key === 'functionality_storage' || key === 'security_storage')\n ? 'granted'\n : 'denied'\n result[key] = safeDefault\n }\n }\n\n return result\n }\n\n /**\n * Records and persists the user's consent choices.\n *\n * Mandatory cookies are always forced to `'granted'` regardless of what is passed.\n * Non-mandatory cookies not present in `consent` default to `'denied'`.\n *\n * @param consent - Partial or full map of cookie ID → status.\n * @param gpcDerived - Internal flag set to `true` when called from the GPC strict path.\n */\n async submitConsent(consent: Partial<ConsentValue>, gpcDerived = false): Promise<void> {\n if (!isClient() || !this.profile || !this.consentStore) return\n\n const compGroup = this.profile.complianceGroup ?? ''\n const hadExistingConsent = this.consentStore.hasConsent()\n const consentId = crypto.randomUUID()\n const pageUrl = window.location.href\n\n const fullConsent: ConsentValue = {}\n for (const cookie of this.profile.cookies) {\n if (cookie.mandatory) {\n fullConsent[cookie.id] = 'granted'\n } else if (compGroup === 'opt-out-strict') {\n const fallback = cookie.cpraCategory === 'sensitive' ? 'denied' : 'granted'\n fullConsent[cookie.id] = consent[cookie.id] ?? fallback\n } else {\n const defaultDenied = compGroup !== 'opt-out' && compGroup !== 'notice-only'\n fullConsent[cookie.id] = consent[cookie.id] ?? (defaultDenied ? 'denied' : 'granted')\n }\n }\n\n const consentAction = this.deriveAction(fullConsent, hadExistingConsent)\n\n this.eventBus?.dispatch('consenti:consentBeingSubmitted', { consent: fullConsent, consentId })\n\n let apiResponse: unknown\n if (this.config.api?.enabled) {\n try {\n const base = this.config.api.baseUrl ?? window.location.origin\n const { httpRequest } = await import('../utils/http')\n apiResponse = await httpRequest(\n `${base}/consenti/api/v1/consent`,\n {\n method: 'POST',\n body: JSON.stringify({\n profileId: this.profileId(),\n profileUuid: this.profile?.profileUuid,\n consent: fullConsent,\n locale: this.config.core.locale ?? 'en',\n gpcDetected: gpcDerived || detectGPC(),\n consentId,\n action: consentAction,\n pageUrl,\n }),\n },\n this.config.api.authToken,\n )\n } catch (err) {\n logger.warn('API consent submission failed:', err)\n }\n }\n\n // Sign and write atomically so the signature covers the exact persisted value\n const signingKey = this.profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n if (signingKey) {\n this.lastSignature = await this.consentStore.writeAndSign(fullConsent, signingKey)\n } else {\n this.consentStore.write(fullConsent)\n }\n\n const receiptRequested = this.modal?.isReceiptRequested() ?? false\n if (receiptRequested && this.config.core.allowReceipt) {\n // Cookie name format: consenti_{userId}_{profileId} — extract the UUID segment.\n const nameParts = this.consentStore.cookieName.split('_')\n // nameParts: ['consenti', '{uuid-segment1}', ..., '{profileId}'] but UUID contains hyphens\n // so join everything except first and last parts for the UUID.\n const visitorId = nameParts.slice(1, -1).join('_')\n const receiptParams = {\n visitorId,\n profileId: this.profileId(),\n profileVersion: this.profile.version,\n locale: this.config.core.locale ?? 'en',\n consent: fullConsent,\n ...(this.lastSignature ? { signature: this.lastSignature } : {}),\n }\n const receipt = generateReceipt(receiptParams)\n downloadReceipt(receipt)\n }\n\n this.broadcastChannel.send({\n type: 'consentUpdated',\n consent: fullConsent,\n profileId: this.profileId(),\n })\n\n this.eventBus?.dispatch('consenti:consentSubmitted', {\n consent: fullConsent,\n apiResponse,\n gpcDetected: gpcDerived,\n consentId,\n consentAction,\n pageUrl,\n })\n\n await this.callPluginHook((p) => p.onConsentSubmit?.(fullConsent))\n\n this.renderer?.announceToScreenReader('Your cookie preferences have been saved.')\n\n // Hide any visible UI after consent is saved so programmatic submitConsent()\n // calls also dismiss the banner/modal, not just button-triggered submissions.\n this.hideBanner()\n if (this.modal?.isOpen()) this.hideModal()\n }\n\n /**\n * Returns the currently visible banner variant, or `false` if no banner is shown.\n * Useful for programmatically checking state before calling `showBanner` / `hideBanner`.\n */\n bannerVisibility(): 'main' | 'gpc' | false {\n return this._bannerVariant ?? false\n }\n\n /**\n * Returns `'preference'` when the preference modal is open, or `false` when closed.\n */\n modalVisibility(): 'preference' | false {\n return this.modal?.isOpen() ? 'preference' : false\n }\n\n /**\n * Mounts and shows the consent banner.\n *\n * No-ops if the requested variant is already visible. If a different variant is\n * currently shown (e.g. switching from main to GPC), the existing banner is replaced.\n *\n * @param useGpcVariant - When `true` and the profile has a `gpcBanner`, that variant\n * is shown instead of `mainBanner`. Used by the GPC flow.\n */\n showBanner(useGpcVariant = false): void {\n if (!isClient() || !this.profile || !this.banner || !this.renderer) return\n const variant: 'main' | 'gpc' = useGpcVariant && this.profile.gpcBanner ? 'gpc' : 'main'\n\n // Skip if the same variant is already visible.\n if (this._bannerVariant === variant) return\n\n const bannerConfig = variant === 'gpc' ? this.profile.gpcBanner! : this.profile.mainBanner\n const root = this.renderer.mount(this.config.rootEl)\n const handlers = this.buildHandlers('banner')\n\n // Remove any previously rendered banner before building a new one so that we do\n // not accumulate duplicate elements and so new button handlers are correctly wired.\n this.banner.remove()\n\n const locales = this.profile.locales\n const activeLocale = this.config.core.locale ?? 'en'\n const bannerEl = this.banner.build(\n bannerConfig,\n handlers,\n undefined,\n locales,\n activeLocale,\n (locale) => this.switchLocale(locale),\n this.config.hidePoweredBy ?? false,\n )\n root.appendChild(bannerEl)\n const overlay = this.banner.getOverlay()\n if (overlay) root.appendChild(overlay)\n this._bannerVariant = variant\n this.eventBus?.dispatch('consenti:bannerVisibility', { show: true, action: false })\n void this.callPluginHook((p) => p.onBannerShow?.())\n }\n\n /** Hides the banner and notifies other tabs via BroadcastChannel. No-op if already hidden. */\n hideBanner(): void {\n if (this._bannerVariant === null) return\n this.banner?.hide()\n this._bannerVariant = null\n this.broadcastChannel.send({ type: 'bannerClosed' })\n this.eventBus?.dispatch('consenti:bannerVisibility', { show: false, action: true })\n this.renderer?.announceToScreenReader('Cookie banner dismissed.')\n void this.callPluginHook((p) => p.onBannerHide?.())\n }\n\n /**\n * Opens the preference modal and hides the banner while the modal is open.\n * No-ops if the modal is already open.\n * If the modal is closed without saving consent, the banner is re-shown automatically\n * (see `hideModal()`). Only one banner or modal is visible at a time.\n *\n * @param triggerEl - The element that triggered the modal open. Focus returns here on close.\n */\n showModal(triggerEl?: HTMLElement): void {\n if (!isClient() || !this.profile || !this.modal) return\n if (this.modal.isOpen()) return\n\n const handlers = this.buildHandlers('modal')\n const existing = this.consentStore?.getConsent() ?? {}\n const dpdpaCfg = this.profile.complianceGroup === 'opt-in-dpdpa' ? this.profile.dpdpa : undefined\n const locales = this.profile.locales\n const activeLocale = this.config.core.locale ?? 'en'\n this.modal.build(\n this.profile.preferenceModal,\n existing,\n handlers,\n this.config.core.allowReceipt ?? false,\n dpdpaCfg,\n locales,\n activeLocale,\n (locale) => { this.hideModal(); this.switchLocale(locale) },\n !(this.config.hidePoweredBy ?? false),\n )\n // Hide banner while modal is open so only one panel is visible at a time.\n this.banner?.hide()\n this.modal.open(triggerEl)\n // The modal is appended to document.body (outside #consenti-root), so the\n // consenti-root--dark class must be mirrored onto the modal element itself.\n const isDark = this.config.darkMode ?? this.profile?.darkMode ?? false\n document.getElementById('consenti-modal')?.classList.toggle('consenti-root--dark', isDark)\n this.eventBus?.dispatch('consenti:modalVisibility', { show: true, action: true })\n void this.callPluginHook((p) => p.onModalShow?.())\n }\n\n /**\n * Closes the preference modal and returns focus to the element that opened it.\n * No-ops if the modal is already closed.\n * If the user has not yet given consent, the banner is re-shown automatically.\n */\n hideModal(): void {\n if (!this.modal?.isOpen()) return\n this.modal.close()\n this.eventBus?.dispatch('consenti:modalVisibility', { show: false, action: true })\n void this.callPluginHook((p) => p.onModalHide?.())\n\n // If no consent on record, bring the banner back so the user still sees the notice.\n if (!this.consentStore?.hasConsent() && this._bannerVariant !== null) {\n this.banner?.show()\n } else if (!this.consentStore?.hasConsent()) {\n this.showBanner()\n }\n }\n\n /**\n * Registers a callback to run once the widget has finished initialising.\n * If the widget is already initialised, the callback is invoked synchronously.\n *\n * @param callback - Function to call when the widget is ready.\n */\n onReady(callback: () => void): void {\n if (this.initialized) {\n callback()\n } else {\n void this.readyPromise.then(callback)\n }\n }\n\n /**\n * A `Promise` that resolves once the widget has finished initialising.\n * Equivalent to wrapping `onReady()` in a `Promise`.\n *\n * @example\n * ```ts\n * await widget.ready\n * console.log('Widget ready')\n * ```\n */\n get ready(): Promise<void> {\n return this.readyPromise\n }\n\n /**\n * Deletes the stored consent record and notifies other tabs.\n * The banner will reappear on the next page load (or immediately if the\n * receiving tab calls `showBanner()` in response to `consentDeleted`).\n */\n async deleteConsent(): Promise<void> {\n if (!isClient() || !this.consentStore) return\n\n if (this.config.api?.enabled) {\n try {\n const base = this.config.api.baseUrl ?? window.location.origin\n const visitorId = this.consentStore.cookieName\n const { httpRequest } = await import('../utils/http')\n await httpRequest(\n `${base}/consenti/api/v1/consent/${visitorId}`,\n { method: 'DELETE' },\n this.config.api?.authToken,\n )\n } catch (err) {\n logger.warn('API consent deletion failed:', err)\n }\n }\n\n this.consentStore.delete()\n this.broadcastChannel.send({ type: 'consentDeleted' })\n }\n\n /**\n * Removes existing consent and immediately re-shows the consent banner so the user\n * can review and update their preferences.\n *\n * Equivalent to calling `deleteConsent()` followed by `showBanner()`, but in a single\n * method so callers do not need to coordinate the async deletion with the banner display.\n *\n * Use this to power \"Review my consent\" links in footers or privacy dashboards.\n */\n async reConsent(): Promise<void> {\n await this.deleteConsent()\n this.showBanner()\n }\n\n /**\n * Returns the fully resolved profile for the current session, or `null` during the\n * async init phase (before the profile has been fetched/resolved).\n *\n * Plugins can use this to read cookie definitions, banner config, modal categories,\n * and the profile version without accessing internal state.\n */\n getProfile(): ResolvedProfile | null {\n return this.profile\n }\n\n /**\n * Returns the `#consenti-root` container element that wraps all widget DOM,\n * or `null` before the widget has mounted or after `destroy()`.\n *\n * Plugins can use this to append custom elements, apply extra CSS variables,\n * or observe mutations within the widget container.\n */\n getRootElement(): HTMLElement | null {\n if (!isClient()) return null\n if (this.config.rootEl) {\n if (typeof this.config.rootEl === 'string') {\n return document.querySelector<HTMLElement>(this.config.rootEl)\n }\n return this.config.rootEl\n }\n return document.getElementById('consenti-root')\n }\n\n /**\n * Returns the `#consenti-banner` element when it is present in the DOM, or `null`.\n * The element may be hidden (`display: none`) — check `bannerVisibility()` for visibility state.\n *\n * Useful for adding extra content, custom event listeners, or mutation observers on the banner.\n */\n getBannerElement(): HTMLElement | null {\n if (!isClient()) return null\n return document.getElementById('consenti-banner')\n }\n\n /**\n * Returns the `#consenti-modal` element when the modal has been built, or `null`.\n * The element is built on first `showModal()` call and reused on subsequent opens.\n *\n * Useful for injecting extra content into the modal or listening to modal-specific events.\n */\n getModalElement(): HTMLElement | null {\n if (!isClient()) return null\n return document.getElementById('consenti-modal')\n }\n\n /**\n * Switches the active locale, re-resolves the profile, and re-renders the widget.\n * Called by the locale switcher UI when the user selects a different language.\n *\n * @param locale - BCP 47 locale code to switch to (e.g. `'fr'`, `'de-AT'`).\n */\n switchLocale(locale: string): void {\n if (!isClient()) return\n this.config.core.locale = locale\n // Teardown current UI and re-init with new locale\n this.banner?.remove()\n if (this.modal?.isOpen()) this.modal.close()\n this._bannerVariant = null\n this.profile = null\n this.initialized = false\n this.readyPromise = new Promise<void>((resolve) => { this.readyResolve = resolve })\n this.init().catch((err) => logger.warn('Locale switch error:', err))\n }\n\n /**\n * Returns `true` if the named cookie's consent is `'granted'`.\n * When `requestValue` is `true`, returns the raw `ConsentStatus` string instead of a boolean,\n * or `false` if the cookie is not in the consent map.\n */\n isCookieGranted(cookieId: string, requestValue = false): boolean | 'granted' | 'denied' | 'objected' {\n if (!isClient()) return false\n const status = this.getConsent()?.[cookieId]\n if (requestValue) return status ?? false\n return status === 'granted'\n }\n\n /**\n * Checks consent for all cookies belonging to the given category.\n *\n * With `requestValue = false` (default): returns `true` only when every cookie in the\n * category is `'granted'`, `false` otherwise.\n *\n * With `requestValue = true`: returns an array of one-key records — one per cookie in the\n * category — e.g. `[{ analytics: 'granted' }, { pixel: 'denied' }]`.\n *\n * Returns `false` / `[]` if the category is not found or the widget is not yet initialized.\n */\n isCategoryGranted(categoryId: string, requestValue = false): boolean | { [cookieId: string]: 'granted' | 'denied' | 'objected' }[] {\n if (!isClient() || !this.profile) return requestValue ? [] : false\n const category = this.profile.preferenceModal.categories.find((c) => c.id === categoryId)\n if (!category) return requestValue ? [] : false\n\n const consent = this.getConsent()\n\n if (requestValue) {\n return category.cookies.map((cookieId) => ({\n [cookieId]: (consent?.[cookieId] ?? 'denied') as 'granted' | 'denied' | 'objected',\n }))\n }\n\n return category.cookies.every((cookieId) => consent?.[cookieId] === 'granted')\n }\n\n /**\n * Programmatically accepts cookies and hides the banner.\n *\n * @param onlyMandatory - When `true`, only mandatory cookies are granted and all others\n * are set to `'denied'`. Defaults to `false` (grant everything).\n */\n async grantAll(onlyMandatory = false): Promise<void> {\n if (!isClient() || !this.profile) return\n await this.submitConsent(this._buildGrantAllConsent(onlyMandatory))\n }\n\n /**\n * Programmatically denies cookies and hides the banner.\n *\n * @param includingMandatory - When `true`, mandatory cookies are also denied. Defaults to\n * `false` (mandatory cookies remain `'granted'`). Passing `true`\n * logs a warning — ensure this is intentional.\n */\n async denyAll(includingMandatory = false): Promise<void> {\n if (!isClient() || !this.profile) return\n if (includingMandatory) {\n logger.warn('denyAll called with includingMandatory=true — mandatory cookies will be denied. Ensure this is intentional.')\n }\n await this.submitConsent(this._buildDenyAllConsent(includingMandatory))\n }\n\n /**\n * Subscribes to a Consenti widget event. The `consenti:` prefix is optional —\n * both `'consentSubmitted'` and `'consenti:consentSubmitted'` are accepted.\n *\n * The same handler reference must be passed to `off()` to unsubscribe.\n *\n * @example\n * ```ts\n * const handler = (data) => console.log(data)\n * widget.on('consentSubmitted', handler)\n * // later:\n * widget.off('consentSubmitted', handler)\n * ```\n */\n on(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void {\n if (!isClient()) return\n const name = event.startsWith('consenti:') ? event : `consenti:${event}`\n const wrapped: EventListener = (e) => handler((e as CustomEvent<ConsentEvent>).detail)\n const existing = this._eventHandlers.get(handler) ?? []\n existing.push({ name, wrapped })\n this._eventHandlers.set(handler, existing)\n window.addEventListener(name, wrapped)\n }\n\n /**\n * Removes a previously registered event handler. The same function reference passed to\n * `on()` must be used here.\n */\n off(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void {\n if (!isClient()) return\n const name = event.startsWith('consenti:') ? event : `consenti:${event}`\n const entries = this._eventHandlers.get(handler)\n if (!entries) return\n const idx = entries.findIndex((e) => e.name === name)\n if (idx === -1) return\n const entry = entries[idx]\n if (entry) window.removeEventListener(name, entry.wrapped)\n entries.splice(idx, 1)\n if (entries.length === 0) this._eventHandlers.delete(handler)\n }\n\n /**\n * Returns the current version of the package and the active profile/consent versions.\n * Useful for debugging and support diagnostics.\n */\n version(): { package: string; profileVersion: number | null; consentVersion: number } {\n return {\n package: __CONSENTI_VERSION__,\n profileVersion: this.profile?.version ?? null,\n consentVersion: 1,\n }\n }\n\n /**\n * Toggles or sets dark mode at runtime without re-initialising the widget.\n * When `enable` is omitted, the current dark mode state is toggled.\n */\n setDarkMode(enable?: boolean): void {\n if (!isClient()) return\n this.config.darkMode = enable === undefined ? !this.config.darkMode : enable\n this.applyDarkMode()\n const isDark = this.config.darkMode ?? false\n document.getElementById('consenti-modal')?.classList.toggle('consenti-root--dark', isDark)\n }\n\n /**\n * Applies partial theme token overrides at runtime, merging them into the current theme.\n * CSS custom properties on the root element are updated immediately.\n */\n setTheme(theme: Partial<ThemeConfig>): void {\n if (!isClient()) return\n this.config.core.theme = Object.assign({}, this.config.core.theme, theme)\n this.applyTheme()\n }\n\n /**\n * Deep-merges a partial config into the current widget config.\n * Theme and dark mode side-effects are re-applied immediately.\n * Changes to locale or profile require an explicit `switchLocale()` or `init()` call.\n */\n setConfig(config: DeepPartial<ConsentiConfig>): void {\n this.config = deepMerge<ConsentiConfig>(this.config, config)\n if (isClient()) {\n this.applyTheme()\n this.applyDarkMode()\n }\n }\n\n /**\n * Merges a partial profile override into the current profile and re-renders any visible UI.\n * Does not make a network request — the resolved base profile from the last `init()` is reused.\n *\n * No-op before `init()` completes (while `resolvedBase` is null).\n */\n setProfile(override: DeepPartial<ResolvedProfile>): void {\n if (!isClient() || !this.resolvedBase) return\n\n this.config.profileOverride = this.config.profileOverride\n ? deepMerge<DeepPartial<ResolvedProfile>>(this.config.profileOverride, override)\n : override\n\n this.profile = deepMerge<ResolvedProfile>(this.resolvedBase, this.config.profileOverride!)\n\n const modalWasOpen = this.modal?.isOpen() ?? false\n if (modalWasOpen) this.modal?.close()\n\n if (this._bannerVariant !== null) {\n const wasGpc = this._bannerVariant === 'gpc'\n this._bannerVariant = null\n this.showBanner(wasGpc)\n }\n\n if (modalWasOpen) this.showModal()\n }\n\n /**\n * Destroys the widget — removes all DOM elements, disconnects the BroadcastChannel,\n * and calls `destroy()` on all registered plugins.\n *\n * After `destroy()`, the instance should not be used again.\n */\n destroy(): void {\n for (const plugin of this.plugins) {\n try { plugin.destroy() } catch { /* ignore — plugin cleanup must not throw */ }\n }\n this.modal?.destroy()\n this.renderer?.unmount()\n this.broadcastChannel.disconnect()\n if (isClient()) {\n for (const entries of this._eventHandlers.values()) {\n for (const { name, wrapped } of entries) {\n window.removeEventListener(name, wrapped)\n }\n }\n this._eventHandlers.clear()\n }\n this.banner = null\n this.modal = null\n this.renderer = null\n this.consentStore = null\n this.eventBus = null\n this.initialized = false\n }\n}\n","import type { ConsentValue, ConsentiWidgetAPI } from '../types'\n\n/**\n * Abstract base class for Consenti UI plugins.\n *\n * Extend this class to hook into the widget lifecycle. Each plugin receives the\n * full `ConsentiWidgetAPI` in `initialize()` — call any public method, inspect\n * the resolved profile, or manipulate the widget DOM.\n *\n * Errors thrown in `initialize()` or `destroy()` are caught by `ConsentiSetup`\n * and logged as warnings — a broken plugin never blocks the consent flow.\n *\n * @example\n * ```ts\n * import { ConsentiPlugin } from '@consenti/ui'\n * import type { ConsentiWidgetAPI } from '@consenti/ui'\n *\n * class AnalyticsPlugin extends ConsentiPlugin {\n * initialize(widget: ConsentiWidgetAPI) {\n * window.addEventListener('consenti:consentSubmitted', () => {\n * const consent = widget.getConsent()\n * myAnalytics.track('consent_saved', consent)\n * })\n * }\n *\n * destroy() {\n * // remove listeners, clear timers, etc.\n * }\n * }\n *\n * class BannerStylePlugin extends ConsentiPlugin {\n * initialize(widget: ConsentiWidgetAPI) {\n * widget.onReady(() => {\n * const banner = widget.getBannerElement()\n * if (banner) banner.style.setProperty('--my-custom-prop', 'red')\n * })\n * }\n * destroy() {}\n * }\n *\n * const widget = new ConsentiSetup({\n * core: { profileId: 1 },\n * plugins: [new AnalyticsPlugin(), new BannerStylePlugin()],\n * })\n * ```\n */\nexport abstract class ConsentiPlugin {\n /**\n * Called once after `ConsentiSetup` has finished its async `init()`.\n * May be async — the widget awaits the result before continuing.\n *\n * @param widget - The full public API of the initialised widget.\n */\n abstract initialize(widget: ConsentiWidgetAPI): void | Promise<void>\n\n /**\n * Called when `widget.destroy()` is invoked.\n * Use this to remove event listeners, cancel timers, and release resources.\n */\n abstract destroy(): void\n\n /**\n * Called after consent has been saved to storage and dispatched to the API.\n * Receives the full, normalised consent map (mandatory cookies always `'granted'`).\n *\n * @param consent - The complete consent record that was saved.\n */\n onConsentSubmit?(consent: ConsentValue): void | Promise<void>\n\n /** Called after the consent banner has been mounted and made visible. */\n onBannerShow?(): void | Promise<void>\n\n /** Called after the consent banner has been hidden. */\n onBannerHide?(): void | Promise<void>\n\n /** Called after the preference modal has been opened. */\n onModalShow?(): void | Promise<void>\n\n /** Called after the preference modal has been closed. */\n onModalHide?(): void | Promise<void>\n}\n","/**\n * Client-side profile builder for API-less deployments.\n *\n * `ConsentiProfile` provides a type-safe way to define consent profiles directly\n * in application code, without running a backend. Each instance is registered in\n * the module-level profile registry so `ConsentiSetup` can resolve it by ID.\n *\n * IDs are auto-assigned starting from 1000 to avoid conflicts with API-backed profiles\n * (which use small sequential integers assigned by the database).\n *\n * @example\n * ```ts\n * import { ConsentiProfile, ConsentiSetup } from '@consenti/ui'\n *\n * const profile = new ConsentiProfile({\n * cookies: [\n * { id: 'necessary', mandatory: true },\n * { id: 'analytics' },\n * ],\n * defaultLocale: 'en',\n * translations: {\n * en: {\n * mainBanner: {\n * position: 'bottom',\n * htmlText: 'We use cookies.',\n * buttons: [{ text: 'Accept All', type: 'primary', cookies: '*' }],\n * },\n * preferenceModal: {\n * categories: [],\n * buttons: [{ text: 'Save', type: 'submit' }],\n * },\n * },\n * },\n * })\n *\n * const widget = new ConsentiSetup({ core: { profileId: profile.getId() } })\n * ```\n */\n\nimport type { ProfileConfig } from '../types'\nimport { registerProfile } from '../core/profile-resolver'\n\n/** Starting point for auto-assigned local profile IDs. */\nlet nextLocalId = 1000\n\n/**\n * Defines a local consent profile and registers it for use by `ConsentiSetup`.\n */\nexport class ConsentiProfile {\n private id: number\n\n /**\n * @param config - Full profile configuration including cookies, locales, and UI text.\n */\n constructor(private config: ProfileConfig) {\n this.id = nextLocalId++\n registerProfile(this.id, config)\n }\n\n /** Returns the auto-assigned numeric profile ID. Pass this to `core.profileId`. */\n getId(): number {\n return this.id\n }\n\n /** Returns the raw `ProfileConfig` passed to the constructor. */\n toJSON(): ProfileConfig {\n return this.config\n }\n}\n","/**\n * ConsentScript — conditionally loads/unloads a third-party `<script>` tag\n * based on a specific consent cookie's status.\n *\n * Instantiate after the widget is ready. The script is injected into `<head>`\n * when the target cookie's consent is `'granted'` and removed when it is\n * revoked. Ideal for lazy-loading analytics, ad pixels, or chat widgets without\n * manual `consenti:consentSubmitted` listeners.\n *\n * @example\n * ```ts\n * import { ConsentiSetup, ConsentScript } from '@consenti/ui'\n *\n * const widget = new ConsentiSetup({ core: { profileId: 0 } })\n * await widget.ready\n *\n * const cs = new ConsentScript({\n * cookieId: 'analytics_storage',\n * widget,\n * src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX',\n * attributes: { async: '' },\n * onLoad: () => console.log('Analytics loaded'),\n * onRevoke: () => console.log('Analytics removed'),\n * })\n * ```\n */\n\nimport type { ConsentiSetup } from '../core/consenti-setup'\nimport { isClient } from './ssr'\nimport { logger } from './console'\n\n/** Options for constructing a {@link ConsentScript} instance. */\nexport interface ConsentScriptOptions {\n /** The consent cookie ID to watch (e.g. `'analytics_storage'`). */\n cookieId: string\n /** The `ConsentiSetup` widget instance to read consent from. */\n widget: ConsentiSetup\n /** Remote script `src` URL to inject when consent is granted. */\n src?: string\n /**\n * Inline JavaScript to inject when consent is granted (e.g. a GTM snippet).\n * WARNING: this string is assigned directly to `script.innerHTML` — only\n * pass a static, trusted literal. Never pass user-supplied or remote content.\n */\n unsafeInnerHTML?: string\n /**\n * Additional HTML attributes applied to the injected `<script>` element.\n * Set a key to an empty string for boolean attributes (e.g. `{ async: '', defer: '' }`).\n */\n attributes?: Record<string, string>\n /** Called after the `<script>` element has been appended to `<head>`. */\n onLoad?: () => void\n /** Called after the `<script>` element has been removed from the DOM. */\n onRevoke?: () => void\n /**\n * When `true` (default), the script is automatically removed when consent is revoked and\n * re-injected when consent is re-granted. Set to `false` to evaluate consent only once at\n * construction time — no event listener is attached and the script is never auto-removed.\n */\n bind?: boolean\n}\n\n/**\n * Conditionally injects and removes a `<script>` element based on a specific\n * cookie's consent status.\n *\n * The check runs immediately on construction so existing consent is honoured\n * without waiting for the next `consenti:consentSubmitted` event. It then\n * re-evaluates on every future consent change.\n *\n * Call {@link ConsentScript.destroy} when the trigger is no longer needed\n * (e.g. on SPA route unmount) to remove the event listener and the injected\n * element.\n */\nexport class ConsentScript {\n private scriptEl: HTMLScriptElement | null = null\n private readonly handler: () => void\n\n constructor(private readonly options: ConsentScriptOptions) {\n // Assign handler unconditionally to satisfy TypeScript strict property initialisation.\n this.handler = () => this.evaluate()\n\n if (!isClient()) return\n\n if (options.bind !== false) {\n window.addEventListener('consenti:consentSubmitted', this.handler)\n }\n\n // Evaluate immediately so any existing consent is reflected at construction time.\n this.evaluate()\n }\n\n private evaluate(): void {\n const consent = this.options.widget.getConsent()\n const granted = consent?.[this.options.cookieId] === 'granted'\n\n if (granted && !this.scriptEl) {\n this.inject()\n } else if (!granted && this.scriptEl) {\n this.revoke()\n }\n }\n\n private inject(): void {\n const script = document.createElement('script')\n if (this.options.src) {\n // Reject javascript: URLs — only allow http(s)/protocol-relative/relative paths.\n const src = this.options.src.trim()\n if (/^javascript:/i.test(src)) {\n logger.warn('ConsentScript: rejected src with javascript: protocol')\n return\n }\n script.src = src\n }\n // unsafeInnerHTML is an intentional escape hatch for inline scripts (e.g. GTM snippet).\n // The content is the caller's responsibility — only pass trusted, static strings.\n if (this.options.unsafeInnerHTML) script.innerHTML = this.options.unsafeInnerHTML\n for (const [key, value] of Object.entries(this.options.attributes ?? {})) {\n // Strip inline event handler attributes (on*) — they bypass CSP and are never\n // a legitimate need here since the caller can use onLoad/onRevoke callbacks.\n if (/^on/i.test(key)) {\n logger.warn(`ConsentScript: rejected attribute \"${key}\" (inline event handlers are not allowed)`)\n continue\n }\n script.setAttribute(key, value)\n }\n document.head.appendChild(script)\n this.scriptEl = script\n this.options.onLoad?.()\n }\n\n private revoke(): void {\n this.scriptEl?.remove()\n this.scriptEl = null\n this.options.onRevoke?.()\n }\n\n /**\n * Removes the event listener and the injected `<script>` element (if present).\n * Call this when the widget is destroyed or the script is no longer needed.\n */\n destroy(): void {\n if (!isClient()) return\n if (this.options.bind !== false) {\n window.removeEventListener('consenti:consentSubmitted', this.handler)\n }\n this.revoke()\n }\n}\n","/**\n * CookieTrigger — a pre-built accessible UI element that reopens the Consenti\n * preference panel when clicked.\n *\n * Use this to add a \"Cookie Settings\" link in your site footer or cookie policy\n * page without wiring up the click handler manually or importing the widget module\n * into your layout component.\n *\n * @example\n * ```ts\n * import { ConsentiSetup, CookieTrigger } from '@consenti/ui'\n *\n * const widget = new ConsentiSetup({ core: { profileId: 0 } })\n *\n * // Attach to an existing element\n * new CookieTrigger({ widget, el: '#footer-cookie-settings' })\n *\n * // — or — create a new button and place it yourself\n * const trigger = new CookieTrigger({ widget, label: 'Cookie Settings' })\n * document.querySelector('.site-footer')?.appendChild(trigger.getElement())\n * ```\n */\n\nimport type { ConsentiSetup } from '../core/consenti-setup'\nimport { isClient } from './ssr'\n\n/** Options for constructing a {@link CookieTrigger} instance. */\nexport interface CookieTriggerOptions {\n /** The widget instance to control when the trigger is clicked. */\n widget: ConsentiSetup\n /**\n * Element to attach the click handler to.\n * - `HTMLElement` — used directly.\n * - `string` — treated as a CSS selector; the first matching element is used.\n * - Omitted — a new `<button>` is created. Retrieve it with {@link CookieTrigger.getElement}\n * and append it to the DOM at the desired location.\n */\n el?: HTMLElement | string\n /**\n * Which panel opens on click.\n * - `'modal'` (default) — preference modal.\n * - `'banner'` — main consent banner.\n */\n action?: 'modal' | 'banner'\n /**\n * Label for the auto-created button element.\n * Ignored when `el` points to an existing element. Defaults to `'Cookie Settings'`.\n */\n label?: string\n}\n\n/**\n * Attaches a click handler that opens the Consenti preference panel to a DOM\n * element, or creates an accessible `<button>` when no existing element is provided.\n *\n * The created button uses the `.consenti-btn` and `.consenti-btn--text` classes so\n * it inherits the widget's theme automatically.\n */\nexport class CookieTrigger {\n private readonly el: HTMLElement\n private readonly clickHandler: () => void\n /** Whether this instance created the element (used to decide if `destroy()` removes it). */\n private readonly created: boolean\n\n constructor(options: CookieTriggerOptions) {\n const action = options.action ?? 'modal'\n\n this.clickHandler = () => {\n if (action === 'modal') options.widget.showModal()\n else options.widget.showBanner()\n }\n\n if (!options.el) {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'consenti-btn consenti-btn--text consenti-cookie-trigger'\n btn.textContent = options.label ?? 'Cookie Settings'\n btn.setAttribute('aria-label', options.label ?? 'Open cookie settings')\n this.el = btn\n this.created = true\n } else if (typeof options.el === 'string') {\n if (!isClient()) {\n // SSR — create a placeholder that is never appended.\n this.el = document.createElement('span')\n this.created = false\n return\n }\n const found = document.querySelector<HTMLElement>(options.el)\n if (!found) {\n throw new Error(\n `[Consenti] CookieTrigger: element not found for selector \"${options.el}\"`,\n )\n }\n this.el = found\n this.created = false\n } else {\n this.el = options.el\n this.created = false\n }\n\n this.el.addEventListener('click', this.clickHandler)\n }\n\n /**\n * Returns the trigger element.\n * When no `el` was provided, append the returned element to the desired location\n * in the DOM after construction.\n */\n getElement(): HTMLElement {\n return this.el\n }\n\n /**\n * Removes the click handler and, if this instance auto-created the button,\n * removes it from the DOM.\n */\n destroy(): void {\n this.el.removeEventListener('click', this.clickHandler)\n if (this.created) this.el.remove()\n }\n}\n"],"mappings":"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","names":["CONSENTI_CSS","isClient","safeWindow","safeNavigator","isClient","_levels","initConsole","levels","logger","args","ConsentiChannel","isClient","onMessage","event","logger","message","generateReceipt","params","receipt","downloadReceipt","json","blob","url","anchor","getCookie","name","isClient","match","escapeRegExp","setCookie","value","opts","secure","parts","deleteCookie","str","ConsentStorage","mode","isClient","logger","key","getCookie","value","opts","setCookie","deleteCookie","resolveConsentMaxAge","cookies","c","min","d","COOKIE_NAME_PREFIX","buildConsentCookieName","userId","profileId","buildCookieValue","consent","epochSeconds","pairs","id","status","parseConsentCookie","value","parts","tsPart","timestamp","epochStr","epoch","consentPart","sigPart","pair","colonIdx","result","ConsentStore","_ConsentStore","storageMode","cookieDomains","ConsentStorage","raw","parsed","signingKey","verifyValue","valueToVerify","sig","logger","signature","opts","signValue","rawValue","finalValue","prefix","suffix","all","name","inner","domain","EventBus","profileId","gtmConfig","name","detail","isClient","event","gtm","win","safeWindow","layerName","layer","adConsentDenied","resolveLocale","translations","requested","defaultLocale","base","deepMerge","lang","override","result","key","overrideVal","baseVal","getBrowserGeoHints","COMPLIANCE_GROUP_IDS","EMBEDDED_COMPLIANCE_MAP","COUNTRY_CODES","acc","c","d","LOCALES","l","profile","general_privacy_consent_default","profile","notice_only_default","profile","opt_in_default","profile","opt_in_brazil_default","profile","opt_in_china_default","profile","opt_in_dpdpa_default","profile","opt_out_default","profile","opt_out_strict_default","PREBUILT_PROFILE_MAP","general_privacy_consent_default","notice_only_default","opt_in_default","opt_in_brazil_default","opt_in_china_default","opt_in_dpdpa_default","opt_out_default","opt_out_strict_default","profileRegistry","registerProfile","id","config","DEFAULT_PROFILE","mapPublicProfileToResolved","resp","gpcMode","CACHE_KEY_PREFIX","CACHE_TTL_MS","getCachedResolution","tenantId","locale","cacheEnabled","key","raw","cached","setCachedResolution","data","isDomainAllowed","profile","trustDomain","origins","current","loadPrebuiltProfile","complianceGroup","PREBUILT_PROFILE_MAP","isPrebuiltAllowed","config","setting","resolveGroupFromHints","tz","lang","parts","tag","countryCode","entry","EMBEDDED_COMPLIANCE_MAP","tzParts","city","effectiveLocale","l","effectiveTenantId","isKnownComplianceGroup","value","COMPLIANCE_GROUP_IDS","fetchProfileJson","url","authToken","httpRequest","scenario1A","getBrowserGeoHints","scenario1B","scenario2A","base","json","logger","resolveUrl","filePath","version","resolved","scenario2B","group","scenarioLocal","typeValue","local","profileRegistry","p","resolveLocale","resolveProfile","complianceType","apiEnabled","detectGPC","nav","safeNavigator","applyGPCToConsent","cookies","currentConsent","mode","result","cookie","buildButton","btn","handlers","el","cookies","createOverlay","opacity","onClick","el","Banner","config","handlers","privacyPolicyUrl","locales","activeLocale","onLocaleSwitch","hidePoweredBy","banner","textContainer","linksContainer","headingTag","heading","text","link","linkButtons","b","btn","anchor","actionButtons","btns","buildButton","hasClose","hasLocaleSwitcher","controls","buildLocaleSwitcher","close","createOverlay","pb","onSwitch","wrapper","list","locale","item","localeBtn","e","FOCUSABLE_SELECTORS","trapFocus","container","getFocusable","el","handleKeydown","e","els","first","last","handleEscape","Modal","config","initialConsent","handlers","allowReceipt","dpdpaConfig","locales","activeLocale","onLocaleSwitch","hidePoweredBy","position","wrapper","overlayOpacity","overlayClickHandler","overlay","createOverlay","container","header","body","headingTag","heading","sub","hasClose","hasLocaleSwitcher","headerControls","buildModalLocaleSwitcher","close","desc","linkButtons","b","linksContainer","btn","anchor","categories","cat","catEl","notice","esc","s","safeEmail","btns","buildButton","pb","isLI","isMandatory","defaultOn","toggle","checkIcon","crossIcon","knob","next","text","li","label","checkbox","triggerEl","isFullscreen","trapFocus","consent","on","cookieId","onSwitch","list","locale","item","localeBtn","e","ROOT_ID","Renderer","rootEl","isClient","found","root","css","style","message","announcer","ConsentiSetup","config","ConsentiChannel","resolve","isClient","initConsole","logger","err","storageMode","userId","ConsentStore","resolvedProfile","resolveProfile","profile","deepMerge","EventBus","Renderer","CONSENTI_CSS","Banner","Modal","msg","signingKey","hasExisting","profileGpcMode","gpcMode","detectGPC","gpcConsent","applyGPCToConsent","gpcSigningKey","compGroup","isOptOut","isOptOutStrict","defaultConsent","cookie","ccpaSigKey","gpcActive","sigKey","isDark","root","theme","map","prop","value","mult","baseSize","headingSize","plugin","invoke","onlyMandatory","consent","includingMandatory","context","p","cookieIds","fullConsent","hadExistingConsent","nonMandatory","c","allGranted","allDenied","gcmKeys","result","key","raw","safeDefault","gpcDerived","consentId","pageUrl","fallback","defaultDenied","consentAction","apiResponse","base","httpRequest","receiptParams","receipt","generateReceipt","downloadReceipt","useGpcVariant","variant","bannerConfig","handlers","locales","activeLocale","bannerEl","locale","overlay","triggerEl","existing","dpdpaCfg","callback","visitorId","cookieId","requestValue","status","categoryId","category","event","handler","name","wrapped","e","entries","idx","entry","enable","override","modalWasOpen","wasGpc","ConsentiPlugin","nextLocalId","ConsentiProfile","config","registerProfile","ConsentScript","options","isClient","granted","script","src","logger","key","value","CookieTrigger","options","action","isClient","found","btn"]}
1
+ {"version":3,"sources":["../../../packages/utils/src/terminal-color.ts","../../../packages/utils/src/compliance.ts","../../../packages/utils/src/tcf.ts","../../../packages/utils/src/gpp.ts","../../../packages/utils/src/direction.ts","../src/utils/ssr.ts","../src/utils/console.ts","../src/utils/script-injector.ts","../src/utils/consent-script.ts","../src/utils/category-script.ts","../src/utils/script-scanner.ts","../src/utils/consent-mapper.ts","../src/styles/consenti-css.ts","../src/utils/broadcast.ts","../src/utils/receipt.ts","../src/utils/uuid.ts","../src/utils/cookie.ts","../src/utils/storage.ts","../src/utils/visitor-id.ts","../src/core/consent-store.ts","../src/core/event-bus.ts","../src/utils/locale.ts","../src/utils/geo-hints.ts","../src/utils/profile-adapter.ts","../src/core/profile-resolver.ts","../src/core/gpc.ts","../src/core/tcf-stub.ts","../src/core/gpp-stub.ts","../src/ui/buttons.ts","../src/ui/overlay.ts","../src/ui/banner.ts","../src/ui/focus-trap.ts","../src/ui/modal.ts","../src/ui/age-gate.ts","../src/ui/renderer.ts","../src/core/consenti-setup.ts","../src/plugins/plugin.base.ts","../src/profile/consenti-profile.ts","../src/utils/consent-action.ts","../src/utils/category-action.ts","../src/utils/banner-trigger.ts","../src/utils/parental-consent.ts","../src/utils/gpc-sync-snippet.ts"],"sourcesContent":["/**\n * Minimal, dependency-free ANSI helpers for coloring console output — shared by any package/app\n * that prints to a terminal (originally `apps/test-runner`'s internal helper, moved here so\n * `apps/scanner` can reuse the same engine instead of duplicating it). Not worth an external\n * chalk/picocolors dependency for a handful of colors. Disabled automatically when stdout isn't a\n * TTY (CI logs, piping to a file) or `NO_COLOR` is set, or forced on via `FORCE_COLOR`.\n */\n\nfunction colorEnabled(): boolean {\n // Guarded rather than assumed Node-only: this package is also consumed by @consenti/ui in the\n // browser, where `process` doesn't exist at all.\n if (typeof process === 'undefined') return false\n if (process.env.NO_COLOR) return false\n if (process.env.FORCE_COLOR) return true\n return Boolean(process.stdout?.isTTY)\n}\n\nfunction wrap(open: number, close: number): (s: string) => string {\n return (s: string) => (colorEnabled() ? `\\x1b[${open}m${s}\\x1b[${close}m` : s)\n}\n\nexport const color = {\n bold: wrap(1, 22),\n dim: wrap(2, 22),\n red: wrap(31, 39),\n green: wrap(32, 39),\n yellow: wrap(33, 39),\n blue: wrap(34, 39),\n magenta: wrap(35, 39),\n cyan: wrap(36, 39),\n gray: wrap(90, 39),\n}\n\nexport type StatusKind = 'pass' | 'fail' | 'error' | 'not-applicable' | 'new-baseline'\n\nconst STATUS_STYLE: Record<StatusKind, (s: string) => string> = {\n pass: s => color.bold(color.green(s)),\n fail: s => color.bold(color.red(s)),\n error: s => color.bold(color.red(s)),\n 'not-applicable': s => color.gray(s),\n 'new-baseline': s => color.bold(color.blue(s)),\n}\n\n/** A fixed-width, colored status label — e.g. `PASS ` in bold green. */\nexport function statusLabel(status: StatusKind, width = 10): string {\n const text = status.toUpperCase().padEnd(width)\n return STATUS_STYLE[status](text)\n}\n\nexport type LogLevel = 'info' | 'success' | 'warn' | 'error'\n\nconst LEVEL_STYLE: Record<LogLevel, { symbol: string; paint: (s: string) => string }> = {\n info: { symbol: 'ℹ', paint: color.cyan },\n success: { symbol: '✓', paint: color.green },\n warn: { symbol: '⚠', paint: color.yellow },\n error: { symbol: '✗', paint: color.red },\n}\n\n/** Formats `message` with a level-appropriate color and symbol prefix — e.g.\n * `formatLog('warn', 'Blocked with 429')` → a yellow `⚠ Blocked with 429`. Returns a plain string\n * (never writes anywhere itself) so callers can feed it into `console.log`, a spinner, or any\n * other sink, same as the rest of this file's colorizers. */\nexport function formatLog(level: LogLevel, message: string): string {\n const { symbol, paint } = LEVEL_STYLE[level]\n return `${paint(symbol)} ${message}`\n}\n","/**\n * Single source of truth for all compliance groups, regulations, and country mappings.\n *\n * This is DATA, not types. The `Compliance` and `ComplianceGroupId` literal types\n * derived from the ID arrays below live in @consenti/types (packages/types/src/compliance.ts),\n * via a type-only import — that keeps @consenti/types free of runtime code.\n *\n * ─── HOW TO UPDATE ────────────────────────────────────────────────────────────\n *\n * ADD A NEW REGULATION:\n * 1. Add regulation ID string to `COMPLIANCE_REGULATION_IDS` below\n * (the `Compliance` type in @consenti/types auto-updates from it).\n * 2. Add it to the appropriate group's `compliances` array below.\n * 3. Add / update the country entry in `EMBEDDED_COMPLIANCE_MAP` below.\n * That is all — no other files need touching.\n *\n * ADD A NEW COMPLIANCE GROUP:\n * 1. Add the kebab-case ID to `COMPLIANCE_GROUP_IDS` below.\n * 2. Add a full group entry to `COMPLIANCE_GROUPS` below (label, compliances, flags).\n * 3. Add a validation rule set to `COMPLIANCE_VALIDATION_RULES` below.\n * 4. Map affected countries in `EMBEDDED_COMPLIANCE_MAP` to the new group.\n *\n * ADD OR UPDATE A COUNTRY:\n * Edit the entry in `EMBEDDED_COMPLIANCE_MAP.countries` below. That is the ONLY\n * place that needs changing for country-level jurisdiction updates.\n */\n\n// ─── Regulation IDs ───────────────────────────────────────────────────────────\n\n// ─── Compliance Group IDs ─────────────────────────────────────────────────────\n\nexport const COMPLIANCE_GROUP_IDS = [\n 'opt-in',\n 'opt-out',\n 'opt-out-strict',\n 'opt-in-dpdpa',\n 'opt-in-china',\n 'opt-in-brazil',\n 'general-privacy-consent',\n 'notice-only',\n] as const\n\n// Local alias for the shape checks below. The public `ComplianceGroupId` type\n// (derived from the same array) is re-exported from @consenti/types.\ntype ComplianceGroupId = typeof COMPLIANCE_GROUP_IDS[number]\n\n// ─── Compliance Groups ────────────────────────────────────────────────────────\n\nexport const COMPLIANCE_GROUPS = {\n 'opt-out-strict': {\n label: 'Opt-out Strict (CPRA / California)',\n description: 'California CPRA: opt-out with sale/sharing/sensitive categories required. GPC must be honored. Stricter than general US state laws.',\n compliances: ['cpra'],\n defaultGpc: 'honor',\n gpcAutoHonor: true,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: true,\n requiresDpdpaDisclosure: false,\n },\n 'opt-out': {\n label: 'Opt-out (US State Laws)',\n description: 'US state privacy laws. Collect first, allow opt-out of sale/share. Covers CCPA and all state-level equivalents.',\n compliances: [\n 'ccpa',\n 'vcdpa', 'cpa-co', 'ctdpa', 'ucpa', 'icdpa-ia', 'icdpa-in', 'tipa',\n 'mcdpa-mt', 'mcdpa-mn', 'ocpa', 'tdpsa', 'dpdpa-de', 'njdpa', 'nhpa',\n 'ndpa-ne', 'modpa', 'kcdpa', 'ridtpa',\n ],\n defaultGpc: 'honor',\n gpcAutoHonor: true,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'notice-only': {\n label: 'Notice Only',\n description: 'A privacy notice is generally sufficient. No dedicated cookie banner law is encoded for this jurisdiction.',\n compliances: ['privacy-fj', 'privacy-pg'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'opt-in': {\n label: 'Standard Opt-in (GDPR)',\n description: 'GDPR / ePrivacy style: prior consent required before non-essential cookies. Granular consent, easy reject, easy withdrawal.',\n compliances: [\n 'gdpr', 'eprivacy', 'uk-gdpr', 'pecr', 'revfadp', 'kvkk', 'pdpa-th',\n 'pdpl-sa', 'uae-pdpl', 'difc-dpl', 'adgm-dpr', 'qpdl-qa', 'pdpl-bh', 'pdpl-om',\n 'law25',\n ],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: true,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'opt-in-dpdpa': {\n label: 'Opt-in (India DPDPA)',\n description: \"India's Digital Personal Data Protection Act. Opt-in consent with data fiduciary disclosure. No legitimate interest basis.\",\n compliances: ['dpdpa'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: true,\n },\n 'opt-in-china': {\n label: 'Opt-in (China PIPL / DSL)',\n description: 'China PIPL + DSL + CSL: strict opt-in with purpose specification, data minimisation, and cross-border transfer controls.',\n compliances: ['pipl', 'dsl-cn', 'csl-cn'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: false,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'opt-in-brazil': {\n label: 'Opt-in (Brazil LGPD)',\n description: 'Brazil LGPD: opt-in consent required. Legitimate interest is a valid basis. Analytics and advertising may differ in treatment.',\n compliances: ['lgpd'],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n 'general-privacy-consent': {\n label: 'General Privacy Consent',\n description: 'Privacy notice or consent rules exist but the jurisdiction does not have a GDPR/PECR-style strict cookie banner law. Documenting legal basis is best practice.',\n compliances: [\n 'pipeda',\n 'popia', 'appi', 'pipa-kr',\n 'pdpa-sg', 'pdpa-my', 'dpa-ph', 'pdp-id', 'pdpd-vn',\n 'privacy-au', 'privacy-nz', 'pdpl-hk', 'pdpa-mo', 'pdpl-tw',\n 'privacy-bn', 'privacy-lk', 'privacy-np', 'privacy-kw', 'privacy-il',\n 'ndpa-ng', 'dpa-ke', 'dpa-gh', 'dpa-ug', 'dpa-rw', 'dpa-mu',\n 'dpa-bw', 'dpa-zm', 'dpa-zw', 'dpa-eg', 'dpa-ma', 'dpa-tn', 'dpa-dz',\n 'lfpdppp', 'lpdp-ar', 'lpdp-pe', 'law1581-co', 'law18331-uy',\n 'law19628-cl', 'lopdp-ec', 'lpdp-cr', 'data-protection-pa', 'data-protection-do',\n ],\n defaultGpc: 'ignore',\n gpcAutoHonor: false,\n allowsLegitimateInterest: true,\n allowsTcf: false,\n requiresCpraCategory: false,\n requiresDpdpaDisclosure: false,\n },\n} satisfies Record<ComplianceGroupId, {\n label: string\n description: string\n compliances: readonly string[]\n defaultGpc: 'ignore' | 'honor' | 'strict'\n gpcAutoHonor: boolean\n allowsLegitimateInterest: boolean\n allowsTcf: boolean\n requiresCpraCategory: boolean\n requiresDpdpaDisclosure: boolean\n}>\n\n// ─── GPC Options ──────────────────────────────────────────────────────────────\n\nexport const GPC_OPTIONS = [\n { value: 'ignore', label: 'Ignore', description: 'Do not check or respond to the GPC browser signal.' },\n { value: 'honor', label: 'Honor', description: 'Respect GPC as a valid opt-out signal. Show GPC variant banner once.' },\n { value: 'strict', label: 'Strict', description: 'Silently deny all non-mandatory cookies when GPC is detected. No banner shown.' },\n]\n\n// ─── Legal Basis Options ──────────────────────────────────────────────────────\n\nexport const LEGAL_BASIS_OPTIONS = [\n { value: 'mandatory', label: 'Strictly Necessary', description: 'Required for the service to function. No consent needed.' },\n { value: 'consent', label: 'Consent', description: 'Requires explicit user agreement. User can withdraw at any time.' },\n { value: 'legitimate_interest', label: 'Legitimate Interest', description: 'Based on legitimate business interest. User has the right to object.' },\n]\n\n// ─── CPRA Categories ──────────────────────────────────────────────────────────\n\nexport const CPRA_CATEGORIES = [\n { value: 'sale', label: 'Sale', description: 'Sale of personal data to third parties for their own use.' },\n { value: 'sharing', label: 'Sharing', description: 'Sharing for cross-context behavioral advertising (even without payment).' },\n { value: 'sensitive', label: 'Sensitive', description: 'Sensitive personal information (geolocation, health, race, biometrics, etc.). Requires opt-in even in California.' },\n]\n\n// ─── Cookie Purposes ──────────────────────────────────────────────────────────\n// The `CookiePurpose` literal type derived from this array lives in\n// @consenti/types (packages/types/src/ui.ts) via a type-only import.\n\nexport const COOKIE_PURPOSE_IDS = ['necessary', 'functional', 'preferences', 'analytics', 'marketing'] as const\n\ntype CookiePurposeId = typeof COOKIE_PURPOSE_IDS[number]\n\nexport const COOKIE_PURPOSES = [\n { value: 'necessary', label: 'Necessary', description: 'Strictly required to deliver the service or ensure security/compliance. Always granted.' },\n { value: 'functional', label: 'Functional', description: 'Optional features that enhance functionality but are not essential.' },\n { value: 'preferences', label: 'Preferences', description: 'Remember user choices and personalize the experience (\"Remember me\").' },\n { value: 'analytics', label: 'Analytics', description: 'Measure usage and performance to improve the product.' },\n { value: 'marketing', label: 'Marketing', description: 'Advertising, profiling, cross-site tracking, retargeting, ad personalization.' },\n]\n\nexport const COOKIE_PURPOSE_DEFAULTS: Record<CookiePurposeId, {\n legalBasis: 'mandatory' | 'consent' | 'legitimate_interest'\n listenGpc: boolean\n cpraCategory?: 'sale' | 'sharing' | 'sensitive'\n}> = {\n necessary: { legalBasis: 'mandatory', listenGpc: false },\n functional: { legalBasis: 'legitimate_interest', listenGpc: false },\n preferences: { legalBasis: 'legitimate_interest', listenGpc: false },\n analytics: { legalBasis: 'consent', listenGpc: true },\n marketing: { legalBasis: 'consent', listenGpc: true, cpraCategory: 'sharing' },\n}\n\nexport const KNOWN_COOKIE_PURPOSES: Record<string, CookiePurposeId> = {\n security_storage: 'necessary',\n functionality_storage: 'functional',\n personalization_storage: 'preferences',\n analytics_storage: 'analytics',\n ad_storage: 'marketing',\n ad_user_data: 'marketing',\n ad_personalization: 'marketing',\n}\n\n/** Legal basis no longer lives on Cookie (it's derived from category membership), so unknown IDs default to 'functional' rather than guessing from a legal-basis hint. */\nexport function inferCookiePurpose(cookie: { id: string }): CookiePurposeId {\n return KNOWN_COOKIE_PURPOSES[cookie.id] ?? 'functional'\n}\n\n// ─── TCF Purposes (stable across IAB TCF v2.2/v2.3 — same 11 purposes) ───────\n\nexport const TCF_PURPOSES = [\n { id: 1, name: 'Store and/or access information on a device', description: \"Cookies, device identifiers, or other information can be stored or accessed on your device.\" },\n { id: 2, name: 'Select basic ads', description: \"Ads can be shown based on the content you're viewing, the app you're using, or your approximate location.\" },\n { id: 3, name: 'Create a personalised ads profile', description: \"A profile can be built about you and your interests to show you personalised ads.\" },\n { id: 4, name: 'Select personalised ads', description: \"Personalised ads can be shown to you based on a profile about you.\" },\n { id: 5, name: 'Create a personalised content profile', description: \"A profile can be built about you and your interests to show you personalised content.\" },\n { id: 6, name: 'Select personalised content', description: \"Personalised content can be shown to you based on a profile about you.\" },\n { id: 7, name: 'Measure ad performance', description: \"The performance and effectiveness of ads that you see or interact with can be measured.\" },\n { id: 8, name: 'Measure content performance', description: \"The performance and effectiveness of content that you see or interact with can be measured.\" },\n { id: 9, name: 'Apply market research to generate audience insights', description: \"Market research can be used to learn more about the audiences who visit sites or use apps.\" },\n { id: 10, name: 'Develop and improve products', description: \"Your data can be used to improve existing systems and software, and to develop new products.\" },\n { id: 11, name: 'Use limited data to select content', description: \"Content can be selected based on limited data such as the website you are visiting.\" },\n]\n\nexport const TCF_SPECIAL_FEATURES = [\n { id: 1, name: 'Use precise geolocation data', description: \"Your precise location (within a radius of less than 500 metres) can be used.\" },\n { id: 2, name: 'Actively scan device characteristics for identification', description: \"Your device can be identified based on a scan of your device's unique combination of characteristics.\" },\n]\n\n// ─── Category ↔ Cookie legal-basis derivation ─────────────────────────────────\n// Legal basis lives on Category (single source of truth); a Cookie/parameter's\n// effective legal basis is derived from whichever category lists its id in\n// `cookies`. Local, minimal shapes here — packages/utils has no dependency on\n// @consenti/types (types depends on utils for data, not the other way), so\n// these are structurally compatible with `Category` from packages/types/src/ui.ts\n// rather than importing it.\n\ntype LegalBasisValue = 'mandatory' | 'consent' | 'legitimate_interest'\n\ninterface CategoryLike {\n legalBasis: LegalBasisValue\n cookies: string[]\n}\n\n/** Linear scan for a single lookup. For repeated lookups against the same category set, build an index with {@link buildCookieCategoryIndex} once instead. */\nexport function findCategoryForCookie<T extends CategoryLike>(\n cookieId: string,\n categories: Record<string, T>,\n): T | undefined {\n for (const category of Object.values(categories)) {\n if (category.cookies.includes(cookieId)) return category\n }\n return undefined\n}\n\n/** Builds a cookie-id → category lookup once per profile load; reuse it across every consent-engine call rather than rebuilding per call. */\nexport function buildCookieCategoryIndex<T extends CategoryLike>(\n categories: Record<string, T>,\n): Map<string, T> {\n const index = new Map<string, T>()\n for (const category of Object.values(categories)) {\n for (const cookieId of category.cookies) index.set(cookieId, category)\n }\n return index\n}\n\n/** Falls back to 'consent' only if the exactly-one-category-per-parameter authoring rule was somehow bypassed. */\nexport function getCookieLegalBasis(cookieId: string, index: Map<string, CategoryLike>): LegalBasisValue {\n return index.get(cookieId)?.legalBasis ?? 'consent'\n}\n\nexport function isMandatoryCookie(cookieId: string, index: Map<string, CategoryLike>): boolean {\n return getCookieLegalBasis(cookieId, index) === 'mandatory'\n}\n\n// ─── Compliance Validation Rules ──────────────────────────────────────────────\n// check() returns true when a violation exists (i.e., the rule is broken).\n// Legal-basis/LI rules run against Category records (legalBasis now lives there).\n// GPC/CPRA/preGrant rules stay Cookie-level but receive the cookie's *derived*\n// legalBasis (via getCookieLegalBasis/buildCookieCategoryIndex) as a second\n// argument instead of reading a (now nonexistent) cookie.legalBasis field.\n\ninterface RuleEntry<TSubject> {\n rule: string\n field: string\n check: (subject: Record<string, unknown>, legalBasis: TSubject) => boolean\n message: (id: string) => string\n}\ninterface WarningEntry<TSubject> extends RuleEntry<TSubject> {\n suggestion: string\n}\n\nexport const COMPLIANCE_VALIDATION_RULES: Record<string, {\n /** Checked once per category, keyed by category id. */\n categoryErrors: Array<RuleEntry<void>>\n categoryWarnings: Array<WarningEntry<void>>\n /** Checked once per cookie/parameter, keyed by cookie id. Receives the parameter's derived legal basis. */\n cookieErrors: Array<RuleEntry<LegalBasisValue>>\n cookieWarnings: Array<WarningEntry<LegalBasisValue>>\n}> = {\n 'opt-in': {\n categoryErrors: [\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Category \"${id}\" must have a legal basis set (Consent, Legitimate Interest, or Strictly Necessary) for GDPR compliance.`,\n },\n ],\n categoryWarnings: [\n {\n rule: 'legitimate-interest-balancing-test-recommended',\n field: 'legitimateInterestDescription',\n check: (c) => c['legalBasis'] === 'legitimate_interest' && !String(c['legitimateInterestDescription'] ?? '').trim(),\n message: (id) => `Category \"${id}\" relies on Legitimate Interest but has no balancing-test description. GDPR Art. 6(1)(f) requires you to conduct and be able to demonstrate a Legitimate Interests Assessment — note that Art. 5(3) ePrivacy still requires consent (LI cannot substitute) for the cookie storage/access itself, so this basis should only cover the downstream processing purpose, not the placement of a non-essential cookie.`,\n suggestion: 'Document your Legitimate Interests Assessment in the category\\'s \"Legitimate Interest Description\" field.',\n },\n ],\n cookieErrors: [],\n cookieWarnings: [\n {\n rule: 'pre-grant-opt-in-risk',\n field: 'preGrant',\n check: (c, legalBasis) => legalBasis === 'consent' && c['preGrant'] === true,\n message: (id) => `Parameter \"${id}\" is pre-granted, which may violate GDPR's opt-in requirement. Only use \"Pre Grant\" on custom profiles targeting jurisdictions where pre-granted consent is valid.`,\n suggestion: 'Disable \"Pre Grant\" unless this profile specifically targets a jurisdiction that permits pre-granted consent.',\n },\n ],\n },\n\n 'opt-out': {\n categoryErrors: [],\n categoryWarnings: [],\n cookieErrors: [],\n cookieWarnings: [\n {\n rule: 'gpc-recommended',\n field: 'listenGpc',\n check: (c, legalBasis) => legalBasis !== 'mandatory' && !c['listenGpc'],\n message: (id) => `Parameter \"${id}\" does not listen for the GPC signal. US state privacy laws require honoring GPC for opt-out of sale/sharing.`,\n suggestion: 'Enable \"Listen for GPC signal\" on this parameter.',\n },\n ],\n },\n\n 'opt-out-strict': {\n categoryErrors: [],\n categoryWarnings: [],\n cookieErrors: [\n {\n rule: 'cpra-category-required',\n field: 'cpraCategory',\n check: (c, legalBasis) => legalBasis !== 'mandatory' && !c['cpraCategory'],\n message: (id) => `Parameter \"${id}\" must have a CPRA category (Sale, Sharing, or Sensitive) assigned. Required under CPRA for all non-necessary parameters.`,\n },\n {\n rule: 'gpc-strict-required',\n field: 'listenGpc',\n check: (c) => (c['cpraCategory'] === 'sale' || c['cpraCategory'] === 'sharing') && !c['listenGpc'],\n message: (id) => `Parameter \"${id}\" is tagged Sale/Sharing but does not listen for the GPC signal. 11 CCR §7025 requires honoring GPC as a Do Not Sell/Share My Personal Information request for these parameters — this is mandatory, not optional.`,\n },\n ],\n cookieWarnings: [],\n },\n\n 'opt-in-dpdpa': {\n categoryErrors: [\n {\n rule: 'no-li-allowed',\n field: 'legalBasis',\n check: (c) => c['legalBasis'] === 'legitimate_interest',\n message: (id) => `Category \"${id}\" uses Legitimate Interest, which is not a valid legal basis under India's DPDPA. Use Consent or Strictly Necessary.`,\n },\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Category \"${id}\" must have a legal basis set (Consent or Strictly Necessary) for DPDPA compliance.`,\n },\n ],\n categoryWarnings: [],\n cookieErrors: [],\n cookieWarnings: [\n {\n rule: 'pre-grant-opt-in-risk',\n field: 'preGrant',\n check: (c, legalBasis) => legalBasis === 'consent' && c['preGrant'] === true,\n message: (id) => `Parameter \"${id}\" is pre-granted, which may violate DPDPA's opt-in requirement. Only use \"Pre Grant\" on custom profiles targeting jurisdictions where pre-granted consent is valid.`,\n suggestion: 'Disable \"Pre Grant\" unless this profile specifically targets a jurisdiction that permits pre-granted consent.',\n },\n ],\n },\n\n 'opt-in-china': {\n categoryErrors: [\n {\n rule: 'no-li-allowed',\n field: 'legalBasis',\n check: (c) => c['legalBasis'] === 'legitimate_interest',\n message: (id) => `Category \"${id}\" uses Legitimate Interest, which is not one of PIPL Article 13's enumerated legal bases. Use Consent or Strictly Necessary.`,\n },\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Category \"${id}\" must have a legal basis set for China PIPL compliance. Consent is the primary valid basis.`,\n },\n ],\n categoryWarnings: [],\n cookieErrors: [],\n cookieWarnings: [\n {\n rule: 'pre-grant-opt-in-risk',\n field: 'preGrant',\n check: (c, legalBasis) => legalBasis === 'consent' && c['preGrant'] === true,\n message: (id) => `Parameter \"${id}\" is pre-granted, which may violate PIPL's opt-in requirement. Only use \"Pre Grant\" on custom profiles targeting jurisdictions where pre-granted consent is valid.`,\n suggestion: 'Disable \"Pre Grant\" unless this profile specifically targets a jurisdiction that permits pre-granted consent.',\n },\n ],\n },\n\n 'opt-in-brazil': {\n categoryErrors: [\n {\n rule: 'legal-basis-required',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Category \"${id}\" must have a legal basis set. Brazil's LGPD requires one of ten legal bases; Consent or Legitimate Interest are most common for parameters.`,\n },\n ],\n categoryWarnings: [\n {\n rule: 'legitimate-interest-balancing-test-recommended',\n field: 'legitimateInterestDescription',\n check: (c) => c['legalBasis'] === 'legitimate_interest' && !String(c['legitimateInterestDescription'] ?? '').trim(),\n message: (id) => `Category \"${id}\" relies on Legitimate Interest but has no balancing-test description. LGPD Art. 10 requires the legitimate interest basis to be documented and, on request, made available to the ANPD as a Data Protection Impact Report (relatório de impacto).`,\n suggestion: 'Document your legitimate interest assessment in the category\\'s \"Legitimate Interest Description\" field.',\n },\n ],\n cookieErrors: [],\n cookieWarnings: [\n {\n rule: 'pre-grant-opt-in-risk',\n field: 'preGrant',\n check: (c, legalBasis) => legalBasis === 'consent' && c['preGrant'] === true,\n message: (id) => `Parameter \"${id}\" is pre-granted, which may violate LGPD's opt-in requirement. Only use \"Pre Grant\" on custom profiles targeting jurisdictions where pre-granted consent is valid.`,\n suggestion: 'Disable \"Pre Grant\" unless this profile specifically targets a jurisdiction that permits pre-granted consent.',\n },\n ],\n },\n\n 'general-privacy-consent': {\n categoryErrors: [],\n categoryWarnings: [\n {\n rule: 'legal-basis-recommended',\n field: 'legalBasis',\n check: (c) => !c['legalBasis'],\n message: (id) => `Category \"${id}\" has no legal basis documented. While a strict cookie banner may not be legally required in this jurisdiction, documenting legal basis is a privacy best practice.`,\n suggestion: 'Set a legal basis to improve compliance documentation and future-proof against stricter enforcement.',\n },\n {\n rule: 'legitimate-interest-balancing-test-recommended',\n field: 'legitimateInterestDescription',\n check: (c) => c['legalBasis'] === 'legitimate_interest' && !String(c['legitimateInterestDescription'] ?? '').trim(),\n message: (id) => `Category \"${id}\" relies on Legitimate Interest but has no balancing-test description documented. Most legitimate-interest-style bases in this jurisdiction group expect a documented justification, and it future-proofs the profile against stricter enforcement.`,\n suggestion: 'Document your legitimate interest justification in the category\\'s \"Legitimate Interest Description\" field.',\n },\n ],\n cookieErrors: [],\n cookieWarnings: [],\n },\n\n 'notice-only': {\n categoryErrors: [],\n categoryWarnings: [],\n cookieErrors: [],\n cookieWarnings: [],\n },\n}\n\n// ─── Embedded Jurisdiction → Compliance Group Map ─────────────────────────────\n// Version tag: update when regulations or mappings change materially.\n// HOW TO UPDATE: edit or add a country entry below. That is the ONLY file change needed.\n//\n// locales: BCP 47 tags for the most commonly used web/IT locales in that country.\n// Only well-supported, widely-used locales are listed. English-speaking countries\n// use 'en' (no country suffix). No country-specific English variants (en-XX) anywhere.\n\nexport interface ComplianceMap {\n version: string\n countries: Record<string, {\n name: string\n type: 'country'\n complianceGroup: ComplianceGroupId\n default: ComplianceGroupId\n description: string\n locales: readonly string[]\n compliance: readonly Compliance[]\n timezones: readonly string[]\n overriddenRegions?: Record<string, {\n name: string\n type: 'region'\n complianceGroup: ComplianceGroupId\n description: string\n /** When true, cookies tagged `cpraCategory: 'sensitive'` default to denied (opt-in) for\n * this region even though the rest of the group's cookies default to granted (opt-out\n * pre-grant) — a per-region carve-out layered on top of the group, not a group change. */\n requiresSensitiveOptIn?: boolean\n }>\n }>\n}\n\nexport const EMBEDDED_COMPLIANCE_MAP: ComplianceMap = {\n version: '2026-07',\n countries: {\n // ─── Europe — EU 27 ───────────────────────────────────────────────────────\n AT: { name: 'Austria', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Austria (GDPR)', locales: ['de-AT'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Vienna'] },\n BE: { name: 'Belgium', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Belgium (GDPR)', locales: ['nl-BE', 'fr-BE'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Brussels'] },\n BG: { name: 'Bulgaria', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Bulgaria (GDPR)', locales: ['bg-BG'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Sofia'] },\n HR: { name: 'Croatia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Croatia (GDPR)', locales: ['hr-HR'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Zagreb'] },\n CY: { name: 'Cyprus', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Cyprus (GDPR)', locales: ['el-CY'], compliance: ['gdpr', 'eprivacy'], timezones: ['Asia/Nicosia'] },\n CZ: { name: 'Czechia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Czechia (GDPR)', locales: ['cs-CZ'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Prague'] },\n DK: { name: 'Denmark', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Denmark (GDPR)', locales: ['da-DK'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Copenhagen'] },\n EE: { name: 'Estonia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Estonia (GDPR)', locales: ['et-EE'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Tallinn'] },\n FI: { name: 'Finland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Finland (GDPR)', locales: ['fi-FI'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Helsinki'] },\n FR: { name: 'France', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'France (GDPR)', locales: ['fr-FR'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Paris'] },\n DE: { name: 'Germany', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Germany (GDPR)', locales: ['de-DE'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Berlin'] },\n GR: { name: 'Greece', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Greece (GDPR)', locales: ['el-GR'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Athens'] },\n HU: { name: 'Hungary', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Hungary (GDPR)', locales: ['hu-HU'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Budapest'] },\n IE: { name: 'Ireland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Ireland (GDPR)', locales: ['en'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Dublin'] },\n IT: { name: 'Italy', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Italy (GDPR)', locales: ['it-IT'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Rome'] },\n LV: { name: 'Latvia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Latvia (GDPR)', locales: ['lv-LV'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Riga'] },\n LT: { name: 'Lithuania', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Lithuania (GDPR)', locales: ['lt-LT'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Vilnius'] },\n LU: { name: 'Luxembourg', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Luxembourg (GDPR)', locales: ['fr-LU', 'de-LU'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Luxembourg'] },\n MT: { name: 'Malta', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Malta (GDPR)', locales: ['mt-MT', 'en'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Malta'] },\n NL: { name: 'Netherlands', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Netherlands (GDPR)', locales: ['nl-NL'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Amsterdam'] },\n PL: { name: 'Poland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Poland (GDPR)', locales: ['pl-PL'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Warsaw'] },\n PT: { name: 'Portugal', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Portugal (GDPR)', locales: ['pt-PT'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Lisbon', 'Atlantic/Madeira', 'Atlantic/Azores'] },\n RO: { name: 'Romania', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Romania (GDPR)', locales: ['ro-RO'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Bucharest'] },\n SK: { name: 'Slovakia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Slovakia (GDPR)', locales: ['sk-SK'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Bratislava'] },\n SI: { name: 'Slovenia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Slovenia (GDPR)', locales: ['sl-SI'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Ljubljana'] },\n ES: { name: 'Spain', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Spain (GDPR)', locales: ['es-ES'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Madrid', 'Africa/Ceuta', 'Atlantic/Canary'] },\n SE: { name: 'Sweden', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Sweden (GDPR)', locales: ['sv-SE'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Stockholm'] },\n // ─── Europe — EEA non-EU ──────────────────────────────────────────────────\n IS: { name: 'Iceland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Iceland (GDPR via EEA)', locales: ['is-IS'], compliance: ['gdpr', 'eprivacy'], timezones: ['Atlantic/Reykjavik'] },\n LI: { name: 'Liechtenstein', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Liechtenstein (GDPR via EEA)', locales: ['de-LI'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Vaduz'] },\n NO: { name: 'Norway', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Norway (GDPR via EEA)', locales: ['nb-NO'], compliance: ['gdpr', 'eprivacy'], timezones: ['Europe/Oslo', 'Arctic/Longyearbyen'] },\n // ─── Europe — GDPR-aligned candidate/observer countries ──────────────────\n AD: { name: 'Andorra', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Andorra (GDPR-aligned)', locales: ['ca-AD'], compliance: ['gdpr'], timezones: ['Europe/Andorra'] },\n AL: { name: 'Albania', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Albania (GDPR-aligned)', locales: ['sq-AL'], compliance: ['gdpr'], timezones: ['Europe/Tirane'] },\n BA: { name: 'Bosnia and Herzegovina', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Bosnia and Herzegovina (GDPR-aligned)', locales: ['bs-BA', 'sr-Latn-BA'], compliance: ['gdpr'], timezones: ['Europe/Sarajevo'] },\n GG: { name: 'Guernsey', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Guernsey (DPA based on GDPR)', locales: ['en'], compliance: ['gdpr'], timezones: ['Europe/Guernsey'] },\n GI: { name: 'Gibraltar', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Gibraltar (DPA based on UK GDPR)', locales: ['en'], compliance: ['uk-gdpr'], timezones: ['Europe/Gibraltar'] },\n GL: { name: 'Greenland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Greenland (Danish GDPR implementation)', locales: ['da-GL'], compliance: ['gdpr'], timezones: ['America/Nuuk', 'America/Danmarkshavn', 'America/Scoresbysund', 'America/Thule'] },\n MC: { name: 'Monaco', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Monaco (GDPR-aligned)', locales: ['fr-MC'], compliance: ['gdpr'], timezones: ['Europe/Monaco'] },\n ME: { name: 'Montenegro', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Montenegro (GDPR-aligned)', locales: ['sr-Latn-ME'], compliance: ['gdpr'], timezones: ['Europe/Podgorica'] },\n MK: { name: 'North Macedonia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'North Macedonia (GDPR-aligned)', locales: ['mk-MK'], compliance: ['gdpr'], timezones: ['Europe/Skopje'] },\n RS: { name: 'Serbia', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Serbia (GDPR-aligned)', locales: ['sr-RS'], compliance: ['gdpr'], timezones: ['Europe/Belgrade'] },\n SM: { name: 'San Marino', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'San Marino (GDPR-aligned)', locales: ['it-SM'], compliance: ['gdpr'], timezones: ['Europe/San_Marino'] },\n TR: { name: 'Türkiye', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Türkiye (KVKK)', locales: ['tr-TR'], compliance: ['kvkk'], timezones: ['Europe/Istanbul'] },\n // ─── North America ────────────────────────────────────────────────────────\n CA: {\n name: 'Canada',\n type: 'country',\n complianceGroup: 'general-privacy-consent',\n default: 'general-privacy-consent',\n description: 'Canada (PIPEDA / Law 25)',\n locales: ['en', 'fr-CA'],\n compliance: ['pipeda', 'law25'],\n timezones: ['America/St_Johns', 'America/Halifax', 'America/Toronto', 'America/Winnipeg', 'America/Edmonton', 'America/Vancouver'],\n overriddenRegions: {\n QC: { name: 'Quebec', type: 'region', complianceGroup: 'opt-in', description: 'Quebec — Law 25 (GDPR-style opt-in)' },\n },\n },\n US: {\n name: 'United States',\n type: 'country',\n // complianceGroup: base when state known but has no specific law\n // default: used when state CANNOT be detected (use most strict = opt-out-strict)\n complianceGroup: 'opt-out',\n default: 'opt-out-strict',\n description: 'United States — opt-out-strict (CPRA) used when state cannot be detected',\n locales: ['en', 'es-US'],\n compliance: ['ccpa', 'cpra', 'vcdpa', 'cpa-co', 'ctdpa', 'ucpa', 'icdpa-ia', 'icdpa-in', 'tipa', 'mcdpa-mt', 'mcdpa-mn', 'ocpa', 'tdpsa', 'dpdpa-de', 'njdpa', 'nhpa', 'ndpa-ne', 'modpa', 'kcdpa', 'ridtpa'],\n timezones: ['America/New_York', 'America/Chicago', 'America/Denver', 'America/Phoenix', 'America/Los_Angeles', 'America/Anchorage', 'Pacific/Honolulu'],\n overriddenRegions: {\n CA: { name: 'California', type: 'region', complianceGroup: 'opt-out-strict', description: 'California — CPRA/CCPA' },\n VA: { name: 'Virginia', type: 'region', complianceGroup: 'opt-out', description: 'Virginia — VCDPA' },\n // requiresSensitiveOptIn: Colorado's CPA requires opt-in consent (not the group's\n // default opt-out pre-grant) for sensitive data specifically — mirrors CPRA's own\n // cpraCategory:'sensitive' carve-out, scoped to this one region rather than promoting\n // all of Colorado to the stricter 'opt-out-strict' group. Verify against current CPA\n // text/regulations before relying on this for a live deployment.\n CO: { name: 'Colorado', type: 'region', complianceGroup: 'opt-out', description: 'Colorado — CPA', requiresSensitiveOptIn: true },\n CT: { name: 'Connecticut', type: 'region', complianceGroup: 'opt-out', description: 'Connecticut — CTDPA' },\n UT: { name: 'Utah', type: 'region', complianceGroup: 'opt-out', description: 'Utah — UCPA' },\n TX: { name: 'Texas', type: 'region', complianceGroup: 'opt-out', description: 'Texas — TDPSA' },\n DE: { name: 'Delaware', type: 'region', complianceGroup: 'opt-out', description: 'Delaware — DPDPA' },\n NJ: { name: 'New Jersey', type: 'region', complianceGroup: 'opt-out', description: 'New Jersey — NJDPA' },\n OR: { name: 'Oregon', type: 'region', complianceGroup: 'opt-out', description: 'Oregon — OCPA' },\n MN: { name: 'Minnesota', type: 'region', complianceGroup: 'opt-out', description: 'Minnesota — MCDPA' },\n MD: { name: 'Maryland', type: 'region', complianceGroup: 'opt-out', description: 'Maryland — MODPA' },\n KY: { name: 'Kentucky', type: 'region', complianceGroup: 'opt-out', description: 'Kentucky — KCDPA' },\n TN: { name: 'Tennessee', type: 'region', complianceGroup: 'opt-out', description: 'Tennessee — TIPA' },\n NE: { name: 'Nebraska', type: 'region', complianceGroup: 'opt-out', description: 'Nebraska — NDPA' },\n IA: { name: 'Iowa', type: 'region', complianceGroup: 'opt-out', description: 'Iowa — ICDPA' },\n IN: { name: 'Indiana', type: 'region', complianceGroup: 'opt-out', description: 'Indiana — ICDPA' },\n MT: { name: 'Montana', type: 'region', complianceGroup: 'opt-out', description: 'Montana — MCDPA' },\n NH: { name: 'New Hampshire', type: 'region', complianceGroup: 'opt-out', description: 'New Hampshire — NHPA' },\n RI: { name: 'Rhode Island', type: 'region', complianceGroup: 'opt-out', description: 'Rhode Island — RIDTPA' },\n },\n },\n // ─── Latin America ────────────────────────────────────────────────────────\n AR: { name: 'Argentina', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Argentina (LPDP)', locales: ['es-AR'], compliance: ['lpdp-ar'], timezones: ['America/Argentina/Buenos_Aires'] },\n BO: { name: 'Bolivia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Bolivia', locales: ['es-BO'], compliance: [], timezones: ['America/La_Paz'] },\n BR: { name: 'Brazil', type: 'country', complianceGroup: 'opt-in-brazil', default: 'opt-in-brazil', description: 'Brazil (LGPD)', locales: ['pt-BR'], compliance: ['lgpd'], timezones: ['America/Noronha', 'America/Sao_Paulo', 'America/Manaus', 'America/Rio_Branco'] },\n CL: { name: 'Chile', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Chile (Law 19.628)', locales: ['es-CL'], compliance: ['law19628-cl'], timezones: ['America/Santiago', 'Pacific/Easter'] },\n CO: { name: 'Colombia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Colombia (Law 1581)', locales: ['es-CO'], compliance: ['law1581-co'], timezones: ['America/Bogota'] },\n CR: { name: 'Costa Rica', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Costa Rica (LPDP)', locales: ['es-CR'], compliance: ['lpdp-cr'], timezones: ['America/Costa_Rica'] },\n DO: { name: 'Dominican Republic', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Dominican Republic', locales: ['es-DO'], compliance: ['data-protection-do'], timezones: ['America/Santo_Domingo'] },\n EC: { name: 'Ecuador', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Ecuador (LOPDP)', locales: ['es-EC'], compliance: ['lopdp-ec'], timezones: ['America/Guayaquil', 'Pacific/Galapagos'] },\n GF: { name: 'French Guiana', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'French Guiana', locales: ['fr-GF'], compliance: [], timezones: ['America/Cayenne'] },\n GT: { name: 'Guatemala', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guatemala', locales: ['es-GT'], compliance: [], timezones: ['America/Guatemala'] },\n GY: { name: 'Guyana', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guyana', locales: ['en'], compliance: [], timezones: ['America/Guyana'] },\n HN: { name: 'Honduras', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Honduras', locales: ['es-HN'], compliance: [], timezones: ['America/Tegucigalpa'] },\n HT: { name: 'Haiti', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Haiti', locales: ['fr-HT'], compliance: [], timezones: ['America/Port-au-Prince'] },\n JM: { name: 'Jamaica', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Jamaica', locales: ['en'], compliance: [], timezones: ['America/Jamaica'] },\n MX: { name: 'Mexico', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Mexico (LFPDPPP)', locales: ['es-MX'], compliance: ['lfpdppp'], timezones: ['America/Mexico_City', 'America/Cancun', 'America/Chihuahua', 'America/Hermosillo', 'America/Tijuana'] },\n NI: { name: 'Nicaragua', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Nicaragua', locales: ['es-NI'], compliance: [], timezones: ['America/Managua'] },\n PA: { name: 'Panama', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Panama', locales: ['es-PA'], compliance: ['data-protection-pa'], timezones: ['America/Panama'] },\n PE: { name: 'Peru', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Peru (LPDP)', locales: ['es-PE'], compliance: ['lpdp-pe'], timezones: ['America/Lima'] },\n PY: { name: 'Paraguay', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Paraguay', locales: ['es-PY'], compliance: [], timezones: ['America/Asuncion'] },\n SR: { name: 'Suriname', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Suriname', locales: ['nl-SR'], compliance: [], timezones: ['America/Paramaribo'] },\n SV: { name: 'El Salvador', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'El Salvador', locales: ['es-SV'], compliance: [], timezones: ['America/El_Salvador'] },\n TT: { name: 'Trinidad and Tobago', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Trinidad and Tobago', locales: ['en'], compliance: [], timezones: ['America/Port_of_Spain'] },\n UY: { name: 'Uruguay', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Uruguay (Law 18.331)', locales: ['es-UY'], compliance: ['law18331-uy'], timezones: ['America/Montevideo'] },\n VE: { name: 'Venezuela', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Venezuela', locales: ['es-VE'], compliance: [], timezones: ['America/Caracas'] },\n // ─── Caribbean ────────────────────────────────────────────────────────────\n AG: { name: 'Antigua and Barbuda', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Antigua and Barbuda', locales: ['en'], compliance: [], timezones: ['America/Antigua'] },\n BB: { name: 'Barbados', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Barbados', locales: ['en'], compliance: [], timezones: ['America/Barbados'] },\n BL: { name: 'Saint Barthélemy', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Barthélemy', locales: ['fr-BL'], compliance: [], timezones: ['America/St_Barthelemy'] },\n BM: { name: 'Bermuda', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Bermuda', locales: ['en'], compliance: [], timezones: ['Atlantic/Bermuda'] },\n BS: { name: 'Bahamas', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Bahamas', locales: ['en'], compliance: [], timezones: ['America/Nassau'] },\n CU: { name: 'Cuba', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cuba', locales: ['es-CU'], compliance: [], timezones: ['America/Havana'] },\n DM: { name: 'Dominica', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Dominica', locales: ['en'], compliance: [], timezones: ['America/Dominica'] },\n GD: { name: 'Grenada', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Grenada', locales: ['en'], compliance: [], timezones: ['America/Grenada'] },\n KN: { name: 'Saint Kitts and Nevis', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Kitts and Nevis', locales: ['en'], compliance: [], timezones: ['America/St_Kitts'] },\n LC: { name: 'Saint Lucia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Lucia', locales: ['en'], compliance: [], timezones: ['America/St_Lucia'] },\n PM: { name: 'Saint Pierre and Miquelon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Pierre and Miquelon', locales: ['fr-PM'], compliance: [], timezones: ['America/Miquelon'] },\n PR: { name: 'Puerto Rico', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Puerto Rico', locales: ['es-PR', 'en'], compliance: [], timezones: ['America/Puerto_Rico'] },\n VC: { name: 'Saint Vincent and the Grenadines', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Saint Vincent and the Grenadines', locales: ['en'], compliance: [], timezones: ['America/St_Vincent'] },\n // ─── Asia — India ─────────────────────────────────────────────────────────\n IN: { name: 'India', type: 'country', complianceGroup: 'opt-in-dpdpa', default: 'opt-in-dpdpa', description: 'India (DPDPA)', locales: ['hi-IN', 'en', 'bn-IN', 'ta-IN'], compliance: ['dpdpa'], timezones: ['Asia/Kolkata', 'Asia/Calcutta'] },\n // ─── Asia — China ─────────────────────────────────────────────────────────\n CN: { name: 'China', type: 'country', complianceGroup: 'opt-in-china', default: 'opt-in-china', description: 'China (PIPL + DSL + CSL)', locales: ['zh-CN'], compliance: ['pipl', 'dsl-cn', 'csl-cn'], timezones: ['Asia/Shanghai'] },\n // ─── Asia — Southeast Asia ────────────────────────────────────────────────\n BN: { name: 'Brunei', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Brunei (Personal Data Protection Order)', locales: ['ms-BN', 'en'], compliance: ['privacy-bn'], timezones: ['Asia/Brunei'] },\n ID: { name: 'Indonesia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Indonesia (PDP Law)', locales: ['id-ID'], compliance: ['pdp-id'], timezones: ['Asia/Jakarta', 'Asia/Makassar', 'Asia/Jayapura'] },\n KH: { name: 'Cambodia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cambodia', locales: ['km-KH'], compliance: [], timezones: ['Asia/Phnom_Penh'] },\n LA: { name: 'Laos', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Laos', locales: ['lo-LA'], compliance: [], timezones: ['Asia/Vientiane'] },\n MM: { name: 'Myanmar', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Myanmar', locales: ['my-MM'], compliance: [], timezones: ['Asia/Yangon'] },\n MY: { name: 'Malaysia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Malaysia (PDPA)', locales: ['ms-MY', 'zh-MY', 'en'], compliance: ['pdpa-my'], timezones: ['Asia/Kuala_Lumpur', 'Asia/Kuching'] },\n PH: { name: 'Philippines', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Philippines (DPA)', locales: ['fil-PH', 'en'], compliance: ['dpa-ph'], timezones: ['Asia/Manila'] },\n SG: { name: 'Singapore', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Singapore (PDPA)', locales: ['zh-SG', 'ms-SG', 'ta-SG', 'en'], compliance: ['pdpa-sg'], timezones: ['Asia/Singapore'] },\n TH: { name: 'Thailand', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Thailand (PDPA — GDPR-style opt-in)', locales: ['th-TH'], compliance: ['pdpa-th'], timezones: ['Asia/Bangkok'] },\n TL: { name: 'Timor-Leste', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Timor-Leste', locales: ['pt-TL'], compliance: [], timezones: ['Asia/Dili'] },\n VN: { name: 'Vietnam', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Vietnam (PDPD)', locales: ['vi-VN'], compliance: ['pdpd-vn'], timezones: ['Asia/Ho_Chi_Minh'] },\n // ─── Asia — East Asia ─────────────────────────────────────────────────────\n HK: { name: 'Hong Kong', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Hong Kong SAR (PDPO)', locales: ['zh-HK'], compliance: ['pdpl-hk'], timezones: ['Asia/Hong_Kong'] },\n JP: { name: 'Japan', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Japan (APPI)', locales: ['ja-JP'], compliance: ['appi'], timezones: ['Asia/Tokyo'] },\n KP: { name: 'North Korea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'North Korea', locales: ['ko-KP'], compliance: [], timezones: ['Asia/Pyongyang'] },\n KR: { name: 'South Korea', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'South Korea (PIPA)', locales: ['ko-KR'], compliance: ['pipa-kr'], timezones: ['Asia/Seoul'] },\n MN: { name: 'Mongolia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mongolia', locales: ['mn-MN'], compliance: [], timezones: ['Asia/Ulaanbaatar', 'Asia/Hovd'] },\n MO: { name: 'Macau', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Macau SAR (PDPA)', locales: ['zh-MO', 'pt-MO'], compliance: ['pdpa-mo'], timezones: ['Asia/Macau'] },\n TW: { name: 'Taiwan', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Taiwan (PDPL)', locales: ['zh-TW'], compliance: ['pdpl-tw'], timezones: ['Asia/Taipei'] },\n // ─── Asia — South Asia ────────────────────────────────────────────────────\n AF: { name: 'Afghanistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Afghanistan', locales: ['fa-AF'], compliance: [], timezones: ['Asia/Kabul'] },\n BD: { name: 'Bangladesh', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Bangladesh', locales: ['bn-BD'], compliance: [], timezones: ['Asia/Dhaka'] },\n BT: { name: 'Bhutan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Bhutan', locales: ['dz-BT'], compliance: [], timezones: ['Asia/Thimphu'] },\n IO: { name: 'British Indian Ocean Territory', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'British Indian Ocean Territory', locales: ['en'], compliance: [], timezones: ['Indian/Chagos'] },\n LK: { name: 'Sri Lanka', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Sri Lanka', locales: ['si-LK', 'ta-LK'], compliance: ['privacy-lk'], timezones: ['Asia/Colombo'] },\n MV: { name: 'Maldives', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Maldives', locales: ['dv-MV'], compliance: [], timezones: ['Indian/Maldives'] },\n NP: { name: 'Nepal', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Nepal', locales: ['ne-NP'], compliance: ['privacy-np'], timezones: ['Asia/Kathmandu'] },\n PK: { name: 'Pakistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Pakistan', locales: ['ur-PK', 'en'], compliance: [], timezones: ['Asia/Karachi'] },\n // ─── Asia — Central Asia ──────────────────────────────────────────────────\n AM: { name: 'Armenia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Armenia', locales: ['hy-AM'], compliance: [], timezones: ['Asia/Yerevan'] },\n AZ: { name: 'Azerbaijan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Azerbaijan', locales: ['az-AZ'], compliance: [], timezones: ['Asia/Baku'] },\n GE: { name: 'Georgia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Georgia', locales: ['ka-GE'], compliance: [], timezones: ['Asia/Tbilisi'] },\n KG: { name: 'Kyrgyzstan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Kyrgyzstan', locales: ['ky-KG', 'ru-KG'], compliance: [], timezones: ['Asia/Bishkek'] },\n KZ: { name: 'Kazakhstan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Kazakhstan', locales: ['kk-KZ', 'ru-KZ'], compliance: [], timezones: ['Asia/Almaty', 'Asia/Aqtobe'] },\n TJ: { name: 'Tajikistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Tajikistan', locales: ['tg-TJ'], compliance: [], timezones: ['Asia/Dushanbe'] },\n TM: { name: 'Turkmenistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Turkmenistan', locales: ['tk-TM'], compliance: [], timezones: ['Asia/Ashgabat'] },\n UZ: { name: 'Uzbekistan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Uzbekistan', locales: ['uz-UZ'], compliance: [], timezones: ['Asia/Tashkent', 'Asia/Samarkand'] },\n // ─── Middle East ──────────────────────────────────────────────────────────\n AE: { name: 'United Arab Emirates', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'United Arab Emirates (UAE PDPL / DIFC DPL / ADGM DPR)', locales: ['ar-AE'], compliance: ['uae-pdpl', 'difc-dpl', 'adgm-dpr'], timezones: ['Asia/Dubai'] },\n BH: { name: 'Bahrain', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Bahrain (PDPL)', locales: ['ar-BH'], compliance: ['pdpl-bh'], timezones: ['Asia/Bahrain'] },\n IL: { name: 'Israel', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Israel (Privacy Protection Law)', locales: ['he-IL', 'ar-IL'], compliance: ['privacy-il'], timezones: ['Asia/Jerusalem'] },\n IQ: { name: 'Iraq', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Iraq', locales: ['ar-IQ'], compliance: [], timezones: ['Asia/Baghdad'] },\n IR: { name: 'Iran', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Iran', locales: ['fa-IR'], compliance: [], timezones: ['Asia/Tehran'] },\n JO: { name: 'Jordan', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Jordan', locales: ['ar-JO'], compliance: [], timezones: ['Asia/Amman'] },\n KW: { name: 'Kuwait', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Kuwait', locales: ['ar-KW'], compliance: ['privacy-kw'], timezones: ['Asia/Kuwait'] },\n LB: { name: 'Lebanon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Lebanon', locales: ['ar-LB'], compliance: [], timezones: ['Asia/Beirut'] },\n OM: { name: 'Oman', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Oman (PDPL)', locales: ['ar-OM'], compliance: ['pdpl-om'], timezones: ['Asia/Muscat'] },\n PS: { name: 'Palestine', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Palestine', locales: ['ar-PS'], compliance: [], timezones: ['Asia/Gaza', 'Asia/Hebron'] },\n QA: { name: 'Qatar', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Qatar (PDPL)', locales: ['ar-QA'], compliance: ['qpdl-qa'], timezones: ['Asia/Qatar'] },\n SA: { name: 'Saudi Arabia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Saudi Arabia (PDPL)', locales: ['ar-SA'], compliance: ['pdpl-sa'], timezones: ['Asia/Riyadh'] },\n SY: { name: 'Syria', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Syria', locales: ['ar-SY'], compliance: [], timezones: ['Asia/Damascus'] },\n YE: { name: 'Yemen', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Yemen', locales: ['ar-YE'], compliance: [], timezones: ['Asia/Aden'] },\n // ─── Africa — West ────────────────────────────────────────────────────────\n BF: { name: 'Burkina Faso', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Burkina Faso', locales: ['fr-BF'], compliance: [], timezones: ['Africa/Ouagadougou'] },\n BJ: { name: 'Benin', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Benin', locales: ['fr-BJ'], compliance: [], timezones: ['Africa/Porto-Novo'] },\n CI: { name: \"Côte d'Ivoire\", type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: \"Côte d'Ivoire\", locales: ['fr-CI'], compliance: [], timezones: ['Africa/Abidjan'] },\n CM: { name: 'Cameroon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cameroon', locales: ['fr-CM', 'en'], compliance: [], timezones: ['Africa/Douala'] },\n CV: { name: 'Cabo Verde', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Cabo Verde', locales: ['pt-CV'], compliance: [], timezones: ['Atlantic/Cape_Verde'] },\n GA: { name: 'Gabon', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Gabon', locales: ['fr-GA'], compliance: [], timezones: ['Africa/Libreville'] },\n GH: { name: 'Ghana', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Ghana (DPA)', locales: ['en'], compliance: ['dpa-gh'], timezones: ['Africa/Accra'] },\n GM: { name: 'Gambia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Gambia', locales: ['en'], compliance: [], timezones: ['Africa/Banjul'] },\n GN: { name: 'Guinea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guinea', locales: ['fr-GN'], compliance: [], timezones: ['Africa/Conakry'] },\n GQ: { name: 'Equatorial Guinea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Equatorial Guinea', locales: ['es-GQ', 'fr-GQ'], compliance: [], timezones: ['Africa/Malabo'] },\n GW: { name: 'Guinea-Bissau', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Guinea-Bissau', locales: ['pt-GW'], compliance: [], timezones: ['Africa/Bissau'] },\n LR: { name: 'Liberia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Liberia', locales: ['en'], compliance: [], timezones: ['Africa/Monrovia'] },\n ML: { name: 'Mali', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mali', locales: ['fr-ML'], compliance: [], timezones: ['Africa/Bamako'] },\n MR: { name: 'Mauritania', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mauritania', locales: ['ar-MR'], compliance: [], timezones: ['Africa/Nouakchott'] },\n NE: { name: 'Niger', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Niger', locales: ['fr-NE'], compliance: [], timezones: ['Africa/Niamey'] },\n NG: { name: 'Nigeria', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Nigeria (NDPA)', locales: ['en'], compliance: ['ndpa-ng'], timezones: ['Africa/Lagos'] },\n SL: { name: 'Sierra Leone', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Sierra Leone', locales: ['en'], compliance: [], timezones: ['Africa/Freetown'] },\n SN: { name: 'Senegal', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Senegal', locales: ['fr-SN'], compliance: [], timezones: ['Africa/Dakar'] },\n TG: { name: 'Togo', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Togo', locales: ['fr-TG'], compliance: [], timezones: ['Africa/Lome'] },\n // ─── Africa — East ────────────────────────────────────────────────────────\n BI: { name: 'Burundi', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Burundi', locales: ['fr-BI'], compliance: [], timezones: ['Africa/Bujumbura'] },\n DJ: { name: 'Djibouti', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Djibouti', locales: ['fr-DJ', 'ar-DJ'], compliance: [], timezones: ['Africa/Djibouti'] },\n ER: { name: 'Eritrea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Eritrea', locales: ['ti-ER'], compliance: [], timezones: ['Africa/Asmara'] },\n ET: { name: 'Ethiopia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Ethiopia', locales: ['am-ET'], compliance: [], timezones: ['Africa/Addis_Ababa'] },\n KE: { name: 'Kenya', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Kenya (DPA)', locales: ['sw-KE', 'en'], compliance: ['dpa-ke'], timezones: ['Africa/Nairobi'] },\n KM: { name: 'Comoros', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Comoros', locales: ['ar-KM', 'fr-KM'], compliance: [], timezones: ['Indian/Comoro'] },\n MG: { name: 'Madagascar', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Madagascar', locales: ['fr-MG'], compliance: [], timezones: ['Indian/Antananarivo'] },\n MU: { name: 'Mauritius', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Mauritius (DPA)', locales: ['en', 'fr-MU'], compliance: ['dpa-mu'], timezones: ['Indian/Mauritius'] },\n MW: { name: 'Malawi', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Malawi', locales: ['en'], compliance: [], timezones: ['Africa/Blantyre'] },\n MZ: { name: 'Mozambique', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Mozambique', locales: ['pt-MZ'], compliance: [], timezones: ['Africa/Maputo'] },\n RW: { name: 'Rwanda', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Rwanda (DPA)', locales: ['rw-RW', 'en'], compliance: ['dpa-rw'], timezones: ['Africa/Kigali'] },\n SC: { name: 'Seychelles', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Seychelles', locales: ['fr-SC', 'en'], compliance: [], timezones: ['Indian/Mahe'] },\n SO: { name: 'Somalia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Somalia', locales: ['so-SO'], compliance: [], timezones: ['Africa/Mogadishu'] },\n SS: { name: 'South Sudan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'South Sudan', locales: ['en'], compliance: [], timezones: ['Africa/Juba'] },\n ST: { name: 'São Tomé and Príncipe', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'São Tomé and Príncipe', locales: ['pt-ST'], compliance: [], timezones: ['Africa/Sao_Tome'] },\n TZ: { name: 'Tanzania', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Tanzania', locales: ['sw-TZ', 'en'], compliance: [], timezones: ['Africa/Dar_es_Salaam'] },\n UG: { name: 'Uganda', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Uganda (DPA)', locales: ['en'], compliance: ['dpa-ug'], timezones: ['Africa/Kampala'] },\n // ─── Africa — Southern ────────────────────────────────────────────────────\n AO: { name: 'Angola', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Angola', locales: ['pt-AO'], compliance: [], timezones: ['Africa/Luanda'] },\n BW: { name: 'Botswana', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Botswana (DPA)', locales: ['en'], compliance: ['dpa-bw'], timezones: ['Africa/Gaborone'] },\n LS: { name: 'Lesotho', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Lesotho', locales: ['en'], compliance: [], timezones: ['Africa/Maseru'] },\n NA: { name: 'Namibia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Namibia', locales: ['en'], compliance: [], timezones: ['Africa/Windhoek'] },\n SZ: { name: 'Eswatini', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Eswatini', locales: ['en'], compliance: [], timezones: ['Africa/Mbabane'] },\n ZA: { name: 'South Africa', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'South Africa (POPIA)', locales: ['en', 'af-ZA'], compliance: ['popia'], timezones: ['Africa/Johannesburg'] },\n ZM: { name: 'Zambia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Zambia (DPA)', locales: ['en'], compliance: ['dpa-zm'], timezones: ['Africa/Lusaka'] },\n ZW: { name: 'Zimbabwe', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Zimbabwe (DPA)', locales: ['en'], compliance: ['dpa-zw'], timezones: ['Africa/Harare'] },\n // ─── Africa — North ───────────────────────────────────────────────────────\n DZ: { name: 'Algeria', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Algeria', locales: ['ar-DZ', 'fr-DZ'], compliance: ['dpa-dz'], timezones: ['Africa/Algiers'] },\n EG: { name: 'Egypt', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Egypt (DPA)', locales: ['ar-EG'], compliance: ['dpa-eg'], timezones: ['Africa/Cairo'] },\n LY: { name: 'Libya', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Libya', locales: ['ar-LY'], compliance: [], timezones: ['Africa/Tripoli'] },\n MA: { name: 'Morocco', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Morocco', locales: ['ar-MA', 'fr-MA'], compliance: ['dpa-ma'], timezones: ['Africa/Casablanca'] },\n SD: { name: 'Sudan', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Sudan', locales: ['ar-SD'], compliance: [], timezones: ['Africa/Khartoum'] },\n TN: { name: 'Tunisia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Tunisia (DPA)', locales: ['ar-TN', 'fr-TN'], compliance: ['dpa-tn'], timezones: ['Africa/Tunis'] },\n // ─── Africa — Central ─────────────────────────────────────────────────────\n CD: { name: 'DR Congo', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'DR Congo', locales: ['fr-CD'], compliance: [], timezones: ['Africa/Kinshasa', 'Africa/Lubumbashi'] },\n CF: { name: 'Central African Republic', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Central African Republic', locales: ['fr-CF'], compliance: [], timezones: ['Africa/Bangui'] },\n CG: { name: 'Republic of Congo', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Republic of Congo', locales: ['fr-CG'], compliance: [], timezones: ['Africa/Brazzaville'] },\n TD: { name: 'Chad', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Chad', locales: ['fr-TD', 'ar-TD'], compliance: [], timezones: ['Africa/Ndjamena'] },\n // ─── Europe — Eastern ─────────────────────────────────────────────────────\n BY: { name: 'Belarus', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Belarus', locales: ['be-BY', 'ru-BY'], compliance: [], timezones: ['Europe/Minsk'] },\n MD: { name: 'Moldova', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Moldova', locales: ['ro-MD'], compliance: [], timezones: ['Europe/Chisinau'] },\n RU: { name: 'Russia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Russia', locales: ['ru-RU'], compliance: [], timezones: ['Europe/Kaliningrad', 'Europe/Moscow', 'Europe/Samara', 'Asia/Yekaterinburg', 'Asia/Omsk', 'Asia/Krasnoyarsk', 'Asia/Irkutsk', 'Asia/Yakutsk', 'Asia/Vladivostok', 'Asia/Magadan', 'Asia/Kamchatka'] },\n UA: { name: 'Ukraine', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Ukraine', locales: ['uk-UA'], compliance: [], timezones: ['Europe/Kyiv'] },\n VA: { name: 'Holy See', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Holy See', locales: ['it-VA'], compliance: [], timezones: ['Europe/Vatican'] },\n // ─── Oceania ──────────────────────────────────────────────────────────────\n AU: { name: 'Australia', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'Australia (Privacy Act)', locales: ['en'], compliance: ['privacy-au'], timezones: ['Australia/Sydney', 'Australia/Brisbane', 'Australia/Adelaide', 'Australia/Darwin', 'Australia/Perth', 'Australia/Hobart'] },\n FJ: { name: 'Fiji', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Fiji', locales: ['en'], compliance: ['privacy-fj'], timezones: ['Pacific/Fiji'] },\n FM: { name: 'Micronesia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Micronesia', locales: ['en'], compliance: [], timezones: ['Pacific/Chuuk', 'Pacific/Pohnpei', 'Pacific/Kosrae'] },\n KI: { name: 'Kiribati', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Kiribati', locales: ['en'], compliance: [], timezones: ['Pacific/Tarawa', 'Pacific/Enderbury', 'Pacific/Kiritimati'] },\n MH: { name: 'Marshall Islands', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Marshall Islands', locales: ['en'], compliance: [], timezones: ['Pacific/Majuro', 'Pacific/Kwajalein'] },\n MP: { name: 'Northern Mariana Islands', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Northern Mariana Islands', locales: ['en'], compliance: [], timezones: ['Pacific/Saipan'] },\n NC: { name: 'New Caledonia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'New Caledonia', locales: ['fr-NC'], compliance: [], timezones: ['Pacific/Noumea'] },\n NF: { name: 'Norfolk Island', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Norfolk Island', locales: ['en'], compliance: [], timezones: ['Pacific/Norfolk'] },\n NR: { name: 'Nauru', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Nauru', locales: ['en'], compliance: [], timezones: ['Pacific/Nauru'] },\n NU: { name: 'Niue', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Niue', locales: ['en'], compliance: [], timezones: ['Pacific/Niue'] },\n NZ: { name: 'New Zealand', type: 'country', complianceGroup: 'general-privacy-consent', default: 'general-privacy-consent', description: 'New Zealand (Privacy Act)', locales: ['en'], compliance: ['privacy-nz'], timezones: ['Pacific/Auckland', 'Pacific/Chatham'] },\n PF: { name: 'French Polynesia', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'French Polynesia', locales: ['fr-PF'], compliance: [], timezones: ['Pacific/Tahiti', 'Pacific/Marquesas', 'Pacific/Gambier'] },\n PG: { name: 'Papua New Guinea', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Papua New Guinea', locales: ['en'], compliance: ['privacy-pg'], timezones: ['Pacific/Port_Moresby', 'Pacific/Bougainville'] },\n PW: { name: 'Palau', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Palau', locales: ['en'], compliance: [], timezones: ['Pacific/Palau'] },\n SB: { name: 'Solomon Islands', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Solomon Islands', locales: ['en'], compliance: [], timezones: ['Pacific/Guadalcanal'] },\n TO: { name: 'Tonga', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Tonga', locales: ['en'], compliance: [], timezones: ['Pacific/Tongatapu'] },\n TV: { name: 'Tuvalu', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Tuvalu', locales: ['en'], compliance: [], timezones: ['Pacific/Funafuti'] },\n VU: { name: 'Vanuatu', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Vanuatu', locales: ['fr-VU', 'en'], compliance: [], timezones: ['Pacific/Efate'] },\n WS: { name: 'Samoa', type: 'country', complianceGroup: 'notice-only', default: 'notice-only', description: 'Samoa', locales: ['en'], compliance: [], timezones: ['Pacific/Apia'] },\n // ─── Europe — GDPR-equivalent / adequacy ─────────────────────────────────\n UK: { name: 'United Kingdom', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'United Kingdom (UK GDPR + PECR)', locales: ['en'], compliance: ['uk-gdpr', 'pecr'], timezones: ['Europe/London'] },\n CH: { name: 'Switzerland', type: 'country', complianceGroup: 'opt-in', default: 'opt-in', description: 'Switzerland (revFADP)', locales: ['de-CH', 'fr-CH', 'it-CH'], compliance: ['revfadp'], timezones: ['Europe/Zurich'] },\n },\n}\n\nexport const COMPLIANCE_REGULATION_IDS: string[] = [...Object.values(EMBEDDED_COMPLIANCE_MAP.countries).reduce((acc, country) => {\n country.compliance.forEach((c: string) => {\n acc.add(c)\n })\n\n return acc\n}, new Set<string>())]\n\n// Local alias for the shape checks below. The public `Compliance` type\n// (derived from the same array) is re-exported from @consenti/types.\ntype Compliance = typeof COMPLIANCE_REGULATION_IDS[number]\n\nexport const COUNTRY_CODES = Object.entries(EMBEDDED_COMPLIANCE_MAP.countries).reduce(function (acc, [c, d]) {\n acc[c] = d.name;\n return acc;\n}, {} as Record<string, string>);\n\nexport const LOCALES = Object.values(EMBEDDED_COMPLIANCE_MAP.countries).reduce(function (acc, c) {\n c.locales.forEach(l => {\n acc[l] = c.name;\n })\n return acc;\n}, {} as Record<string, string>);\n\n// ─── compliance.complianceMap override resolution ─────────────────────────────\n// Shared by apps/api (GeoResolverService) and apps/ui (profile-resolver, standalone mode\n// only). Defined structurally here (not imported from @consenti/types) to avoid a circular\n// package dependency — @consenti/types already imports its ComplianceGroupId et al. from this\n// file, so this file cannot import back from @consenti/types.\n\n/** `complianceGroup`/`default` are `string` here (not the `ComplianceGroupId` literal union) —\n * that union isn't exported from this module (it lives in `@consenti/types`, which itself\n * imports from this file, so importing it back here would be circular); runtime validation\n * (`isComplianceMapData`) is the actual safety net for operator-supplied data. */\nexport interface ComplianceMapCountryEntry {\n complianceGroup: string\n default?: string\n description?: string\n overriddenRegions?: Record<string, { complianceGroup: string; description?: string; requiresSensitiveOptIn?: boolean }>\n}\n\nexport interface ComplianceMapData {\n version: string\n countries: Record<string, ComplianceMapCountryEntry>\n}\n\nexport interface ComplianceMapResolution {\n map: ComplianceMapData | typeof EMBEDDED_COMPLIANCE_MAP\n /** Set only when `override` was a URL — lets callers inspect `Cache-Control`/`Expires` to\n * schedule their own background refresh. Not meaningful for the `'default'`/object cases. */\n response?: Response\n}\n\nfunction isComplianceMapData(data: unknown): data is ComplianceMapData {\n if (!data || typeof data !== 'object') return false\n const d = data as Record<string, unknown>\n if (typeof d['version'] !== 'string') return false\n const countries = d['countries']\n if (!countries || typeof countries !== 'object') return false\n return Object.values(countries as Record<string, unknown>).every((entry) =>\n !!entry && typeof entry === 'object' && typeof (entry as Record<string, unknown>)['complianceGroup'] === 'string'\n )\n}\n\n/**\n * Resolves a `compliance.complianceMap` config value into actual map data.\n *\n * `'default'`/`undefined` → the embedded map, no network access. A `string` is treated as a\n * URL and fetched as-is (no custom `cache` mode — the platform's own fetch/HTTP-cache semantics\n * apply). A plain object is used directly. Any failure (network error, bad JSON, failed\n * structural validation) reports via `onWarn` and falls back to the embedded map — this never\n * throws. Also warns when a country defines `overriddenRegions` but no explicit `default`,\n * since the effective fallback (`complianceGroup`) may under-comply for regions not listed.\n *\n * Does not do any caching/scheduling — that's platform-specific (Node vs. browser) and is the\n * caller's responsibility.\n */\nexport async function resolveComplianceMapOverride(\n override: 'default' | string | ComplianceMapData | undefined,\n onWarn: (message: string) => void,\n): Promise<ComplianceMapResolution> {\n const fallback: ComplianceMapResolution = { map: EMBEDDED_COMPLIANCE_MAP }\n\n if (override === undefined || override === 'default') return fallback\n\n let candidate: unknown\n let response: Response | undefined\n\n if (typeof override === 'string') {\n try {\n response = await fetch(override)\n if (!response.ok) {\n onWarn(`compliance.complianceMap fetch from \"${override}\" failed (HTTP ${response.status}) — falling back to the embedded map.`)\n return fallback\n }\n candidate = await response.json()\n } catch (err) {\n onWarn(`compliance.complianceMap fetch from \"${override}\" failed (${err instanceof Error ? err.message : String(err)}) — falling back to the embedded map.`)\n return fallback\n }\n } else {\n candidate = override\n }\n\n if (!isComplianceMapData(candidate)) {\n onWarn(`compliance.complianceMap ${typeof override === 'string' ? `at \"${override}\"` : 'object'} failed validation — falling back to the embedded map.`)\n return fallback\n }\n\n for (const [code, entry] of Object.entries(candidate.countries)) {\n if (entry.overriddenRegions && entry.default === undefined) {\n onWarn(`compliance.complianceMap country \"${code}\" has overriddenRegions but no explicit default — falling back to complianceGroup (\"${entry.complianceGroup}\"), which may under-comply for regions not listed.`)\n }\n }\n\n const result: ComplianceMapResolution = { map: candidate }\n if (response) result.response = response\n return result\n}\n","// Simplified TC string encoder/decoder.\n// This implementation stores a base64url-encoded JSON payload for basic compatibility.\n//\n// `@consenti/api` now also ships a spec-correct binary encoder (`apps/api/src/tcf/real-tc-string.ts`)\n// using the optional `@iabtechlabtcf/core` peer dependency (the actively-maintained IAB Tech Lab\n// package — note: the unscoped `iabtcf-core` name referenced in older comments/docs does not exist\n// on npm). That encoder is used automatically when `compliance.tcf.publisherCC` is configured and\n// the dependency is installed; this simplified encoder remains the fallback for everyone else and\n// the only option in `@consenti/ui`'s fully standalone (no-backend) mode, since real encoding needs\n// the multi-megabyte Global Vendor List that only the backend fetches and caches.\n//\n// Implemented with a manual base64url codec over Uint8Array (no `Buffer`) so this file works\n// identically in Node (`@consenti/api`) and the browser (`@consenti/ui`) without a runtime dependency.\n\nconst B64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'\n\nfunction utf8Encode(str: string): Uint8Array {\n return new TextEncoder().encode(str)\n}\n\nfunction utf8Decode(bytes: Uint8Array): string {\n return new TextDecoder().decode(bytes)\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let out = ''\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i] ?? 0\n const b1 = i + 1 < bytes.length ? bytes[i + 1] ?? 0 : 0\n const b2 = i + 2 < bytes.length ? bytes[i + 2] ?? 0 : 0\n out += B64_CHARS[b0 >> 2]\n out += B64_CHARS[((b0 & 0x03) << 4) | (b1 >> 4)]\n if (i + 1 < bytes.length) out += B64_CHARS[((b1 & 0x0f) << 2) | (b2 >> 6)]\n if (i + 2 < bytes.length) out += B64_CHARS[b2 & 0x3f]\n }\n return out\n}\n\nfunction base64UrlDecode(str: string): Uint8Array {\n const lookup = new Map(B64_CHARS.split('').map((c, idx) => [c, idx]))\n const cleaned = str.replace(/[^A-Za-z0-9\\-_]/g, '')\n const bytes: number[] = []\n for (let i = 0; i < cleaned.length; i += 4) {\n const c0 = lookup.get(cleaned[i] ?? '') ?? 0\n const c1 = lookup.get(cleaned[i + 1] ?? '') ?? 0\n const c2 = cleaned[i + 2] !== undefined ? lookup.get(cleaned[i + 2] as string) : undefined\n const c3 = cleaned[i + 3] !== undefined ? lookup.get(cleaned[i + 3] as string) : undefined\n bytes.push((c0 << 2) | (c1 >> 4))\n if (c2 !== undefined) bytes.push(((c1 & 0x0f) << 4) | (c2 >> 2))\n if (c3 !== undefined) bytes.push(((c2 ?? 0) & 0x03) << 6 | c3)\n }\n return new Uint8Array(bytes)\n}\n\nexport interface TcStringInput {\n cmpId: number\n cmpVersion: number\n consentScreen: number\n consentLanguage: string\n vendorListVersion: number\n purposeConsents: number[]\n vendorConsents: number[]\n}\n\nexport function encodeTcString(input: TcStringInput): string {\n const payload = {\n v: 2,\n ts: Math.floor(Date.now() / 100),\n cmpId: input.cmpId,\n cmpV: input.cmpVersion,\n cs: input.consentScreen,\n lang: input.consentLanguage.toUpperCase().slice(0, 2),\n vlV: input.vendorListVersion,\n pc: input.purposeConsents,\n vc: input.vendorConsents,\n }\n return base64UrlEncode(utf8Encode(JSON.stringify(payload)))\n}\n\nexport interface DecodedTcString {\n version: number\n cmpId: number\n consentLanguage: string\n purposeConsents: number[]\n vendorConsents: number[]\n created: number\n}\n\nexport function decodeTcString(tcString: string): DecodedTcString | null {\n try {\n const raw = JSON.parse(utf8Decode(base64UrlDecode(tcString))) as {\n v: number; cmpId: number; lang: string; pc: number[]; vc: number[]; ts: number\n }\n return {\n version: raw.v,\n cmpId: raw.cmpId,\n consentLanguage: raw.lang,\n purposeConsents: Array.isArray(raw.pc) ? raw.pc : [],\n vendorConsents: Array.isArray(raw.vc) ? raw.vc : [],\n created: raw.ts * 100,\n }\n } catch {\n return null\n }\n}\n","// Simplified GPP (Global Privacy Platform) US National (\"usnat\") string encoder/decoder.\n// Stores a base64url-encoded JSON payload, not the real IAB binary bit-string encoding —\n// deliberately, not just unfinished: `@consenti/api` ships a spec-correct encoder using the\n// optional `@iabgpp/cmpapi` peer dependency (see `apps/api/src/gpp/gpp-string.ts`), used\n// automatically when it's installed. This simplified encoder is the fallback for everyone else,\n// and the only option in `@consenti/ui`'s `window.__gpp` stub — unlike TCF, GPP's USNat section\n// doesn't need a multi-megabyte Global Vendor List to encode correctly, but `@iabgpp/cmpapi`\n// itself (all ~20 section encoders, GVL class, etc.) is large enough that bundling it into the\n// widget would defeat \"zero-dependency\" for every site, not just ones using GPP — so it stays a\n// server-only optional dependency, same bundle-size reasoning TCF already established.\n//\n// Implemented with the same manual base64url codec as `tcf.ts` (no `Buffer`), so this file works\n// identically in Node (`@consenti/api`) and the browser (`@consenti/ui`) without a runtime dependency.\n\nconst B64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'\n\nfunction utf8Encode(str: string): Uint8Array {\n return new TextEncoder().encode(str)\n}\n\nfunction utf8Decode(bytes: Uint8Array): string {\n return new TextDecoder().decode(bytes)\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n let out = ''\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i] ?? 0\n const b1 = i + 1 < bytes.length ? bytes[i + 1] ?? 0 : 0\n const b2 = i + 2 < bytes.length ? bytes[i + 2] ?? 0 : 0\n out += B64_CHARS[b0 >> 2]\n out += B64_CHARS[((b0 & 0x03) << 4) | (b1 >> 4)]\n if (i + 1 < bytes.length) out += B64_CHARS[((b1 & 0x0f) << 2) | (b2 >> 6)]\n if (i + 2 < bytes.length) out += B64_CHARS[b2 & 0x3f]\n }\n return out\n}\n\nfunction base64UrlDecode(str: string): Uint8Array {\n const lookup = new Map(B64_CHARS.split('').map((c, idx) => [c, idx]))\n const cleaned = str.replace(/[^A-Za-z0-9\\-_]/g, '')\n const bytes: number[] = []\n for (let i = 0; i < cleaned.length; i += 4) {\n const c0 = lookup.get(cleaned[i] ?? '') ?? 0\n const c1 = lookup.get(cleaned[i + 1] ?? '') ?? 0\n const c2 = cleaned[i + 2] !== undefined ? lookup.get(cleaned[i + 2] as string) : undefined\n const c3 = cleaned[i + 3] !== undefined ? lookup.get(cleaned[i + 3] as string) : undefined\n bytes.push((c0 << 2) | (c1 >> 4))\n if (c2 !== undefined) bytes.push(((c1 & 0x0f) << 4) | (c2 >> 2))\n if (c3 !== undefined) bytes.push(((c2 ?? 0) & 0x03) << 6 | c3)\n }\n return new Uint8Array(bytes)\n}\n\n// IAB's tri-state field convention: 0 = not applicable, 1 = opted out, 2 = did not opt out.\nconst NOT_APPLICABLE = 0\nconst OPTED_OUT = 1\nconst DID_NOT_OPT_OUT = 2\n\nfunction optOutValue(applicable: boolean, optedOut: boolean): number {\n if (!applicable) return NOT_APPLICABLE\n return optedOut ? OPTED_OUT : DID_NOT_OPT_OUT\n}\n\nexport interface GppStringInput {\n cmpId: number\n cmpVersion: number\n mspaCoveredTransaction: boolean\n mspaOptOutOptionMode: 0 | 1 | 2\n mspaServiceProviderMode: 0 | 1 | 2\n /** Whether the visitor opted out of sale-category cookies (cpraCategory: 'sale'). */\n saleOptOut: boolean\n /** Whether any cookie in the profile is tagged cpraCategory: 'sale'. */\n saleApplicable: boolean\n sharingOptOut: boolean\n sharingApplicable: boolean\n gpcDetected?: boolean\n}\n\nexport function encodeGppString(input: GppStringInput): string {\n const payload = {\n v: 1,\n ts: Math.floor(Date.now() / 100),\n cmpId: input.cmpId,\n cmpV: input.cmpVersion,\n sect: 'usnat',\n saleOO: optOutValue(input.saleApplicable, input.saleOptOut),\n shareOO: optOutValue(input.sharingApplicable, input.sharingOptOut),\n // Targeted-advertising opt-out mirrors sharing opt-out — CPRA defines \"sharing\" as\n // cross-context behavioural advertising, which is what USNat's field covers.\n taOO: optOutValue(input.sharingApplicable, input.sharingOptOut),\n mspaCT: input.mspaCoveredTransaction ? 1 : 2,\n mspaOOM: input.mspaOptOutOptionMode,\n mspaSPM: input.mspaServiceProviderMode,\n ...(input.gpcDetected !== undefined ? { gpc: input.gpcDetected } : {}),\n }\n return base64UrlEncode(utf8Encode(JSON.stringify(payload)))\n}\n\nexport interface DecodedGppString {\n version: number\n cmpId: number\n cmpVersion: number\n saleOptOut: number\n sharingOptOut: number\n targetedAdvertisingOptOut: number\n gpc?: boolean\n created: number\n}\n\nexport function decodeGppString(gppString: string): DecodedGppString | null {\n try {\n const raw = JSON.parse(utf8Decode(base64UrlDecode(gppString))) as {\n v: number; cmpId: number; cmpV: number\n saleOO: number; shareOO: number; taOO: number; gpc?: boolean; ts: number\n }\n return {\n version: raw.v,\n cmpId: raw.cmpId,\n cmpVersion: raw.cmpV,\n saleOptOut: raw.saleOO,\n sharingOptOut: raw.shareOO,\n targetedAdvertisingOptOut: raw.taOO,\n ...(raw.gpc !== undefined ? { gpc: raw.gpc } : {}),\n created: raw.ts * 100,\n }\n } catch {\n return null\n }\n}\n","/** ISO 639-1 prefixes of languages conventionally written right-to-left. Fallback for engines\n * without `Intl.Locale(...).getTextInfo()` support — this project's floor is Firefox 74+, and\n * `getTextInfo()` is a recent addition not yet universal. */\nconst RTL_LANGUAGE_PREFIXES = ['ar', 'he', 'fa', 'ur', 'ps', 'sd', 'ug', 'yi']\n\n/**\n * Resolves the text direction for a locale. `override` (an explicit `core.dir` config value)\n * always wins; `'auto'` (or omitted) derives the direction from `Intl.Locale(...).getTextInfo()`\n * when the engine supports it, falling back to a hand-maintained RTL-language-prefix table.\n */\nexport function resolveTextDirection(locale: string, override?: 'ltr' | 'rtl' | 'auto'): 'ltr' | 'rtl' {\n if (override === 'ltr' || override === 'rtl') return override\n try {\n const dir = new Intl.Locale(locale).getTextInfo?.().direction\n if (dir === 'ltr' || dir === 'rtl') return dir\n } catch {\n // Invalid/unparseable locale string, or Intl.Locale/getTextInfo unsupported — fall through.\n }\n const prefix = locale.slice(0, 2).toLowerCase()\n return RTL_LANGUAGE_PREFIXES.includes(prefix) ? 'rtl' : 'ltr'\n}\n","/**\n * Server-Side Rendering (SSR) guards.\n *\n * Every access to browser globals (`window`, `document`, `navigator`) must go through\n * these helpers. `new ConsentiSetup(config)` called during SSR — e.g. in a Next.js or\n * Nuxt server render — silently no-ops because `isClient()` returns `false` and the\n * constructor returns early before touching any DOM API.\n */\n\n/**\n * Returns `true` when running in a browser environment with `window` and `document`\n * available. Returns `false` in Node.js, Deno, edge runtimes, and test environments\n * that do not provide a DOM.\n */\nexport const isClient = (): boolean =>\n typeof window !== 'undefined' && typeof document !== 'undefined'\n\n/**\n * Returns the `window` object when running client-side, or `null` in SSR contexts.\n * Prefer this over bare `window` access in code that might run during SSR.\n */\nexport const safeWindow = (): (Window & typeof globalThis) | null =>\n isClient() ? window : null\n\n/**\n * Returns the `document` object when running client-side, or `null` in SSR contexts.\n */\nexport const safeDocument = (): Document | null =>\n isClient() ? document : null\n\n/**\n * Returns the `navigator` object when running client-side and `navigator` is defined,\n * or `null` otherwise. Some non-browser environments expose `window` but not `navigator`.\n */\nexport const safeNavigator = (): Navigator | null =>\n isClient() && typeof navigator !== 'undefined' ? navigator : null\n","type ConsoleLevel = 'info' | 'log' | 'warning' | 'error'\n\nlet _levels: ConsoleLevel[] = ['error']\n\nexport function initConsole(levels: ConsoleLevel[]): void {\n _levels = levels\n}\n\nexport const logger = {\n info: (...args: unknown[]) => { if (_levels.includes('info')) console.info('[Consenti]', ...args) },\n log: (...args: unknown[]) => { if (_levels.includes('log')) console.log('[Consenti]', ...args) },\n warn: (...args: unknown[]) => { if (_levels.includes('warning')) console.warn('[Consenti]', ...args) },\n error: (...args: unknown[]) => { if (_levels.includes('error')) console.error('[Consenti]', ...args) },\n}\n","/**\n * Shared `<script>` element injection/removal logic used by `ConsentScript` and\n * `CategoryScript`. Centralized so the src/attribute security validation\n * (`javascript:` URL rejection, inline event-handler attribute stripping) can't\n * silently diverge between the two.\n */\n\nimport { logger } from './console'\n\nexport interface ScriptInjectorOptions {\n /** Remote script `src` URL to inject. */\n src?: string\n /**\n * Inline JavaScript to inject (e.g. a GTM snippet).\n * WARNING: assigned directly to `script.innerHTML` — only pass a static, trusted literal.\n */\n unsafeInnerHTML?: string\n /** Additional HTML attributes applied to the injected `<script>` element. */\n attributes?: Record<string, string>\n /** Prefix used in rejection warnings (e.g. `'ConsentScript'`, `'CategoryScript'`). */\n warnPrefix: string\n}\n\n/** Builds and appends a `<script>` element to `<head>`, or returns `null` if rejected (unsafe `src`). */\nexport function injectScript(options: ScriptInjectorOptions): HTMLScriptElement | null {\n const script = document.createElement('script')\n if (options.src) {\n // Reject javascript: URLs — only allow http(s)/protocol-relative/relative paths.\n const src = options.src.trim()\n if (/^javascript:/i.test(src)) {\n logger.warn(`${options.warnPrefix}: rejected src with javascript: protocol`)\n return null\n }\n script.src = src\n }\n // unsafeInnerHTML is an intentional escape hatch for inline scripts (e.g. GTM snippet).\n // The content is the caller's responsibility — only pass trusted, static strings.\n if (options.unsafeInnerHTML) script.innerHTML = options.unsafeInnerHTML\n for (const [key, value] of Object.entries(options.attributes ?? {})) {\n // Strip inline event handler attributes (on*) — they bypass CSP and are never\n // a legitimate need here since the caller can use onLoad/onRevoke callbacks.\n if (/^on/i.test(key)) {\n logger.warn(`${options.warnPrefix}: rejected attribute \"${key}\" (inline event handlers are not allowed)`)\n continue\n }\n script.setAttribute(key, value)\n }\n document.head.appendChild(script)\n return script\n}\n\n/** Removes a previously injected `<script>` element, if present. */\nexport function revokeScript(scriptEl: HTMLScriptElement | null): void {\n scriptEl?.remove()\n}\n","/**\n * ConsentScript — conditionally loads/unloads a third-party `<script>` tag\n * based on a specific consent cookie's status.\n *\n * Instantiate after the widget is ready. The script is injected into `<head>`\n * when the target cookie's consent is `'granted'` and removed when it is\n * revoked. Ideal for lazy-loading analytics, ad pixels, or chat widgets without\n * manual `consenti:consentSubmitted` listeners.\n *\n * @example\n * ```ts\n * import { ConsentiSetup, ConsentScript } from '@consenti/ui'\n *\n * const widget = new ConsentiSetup({ core: { profileId: 0 } })\n * await widget.ready\n *\n * const cs = new ConsentScript({\n * cookieId: 'analytics_storage',\n * widget,\n * src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX',\n * attributes: { async: '' },\n * onLoad: () => console.log('Analytics loaded'),\n * onRevoke: () => console.log('Analytics removed'),\n * })\n * ```\n */\n\nimport type { ConsentiSetup } from '../core/consenti-setup'\nimport { isClient } from './ssr'\nimport { injectScript, revokeScript } from './script-injector'\n\n/** Options for constructing a {@link ConsentScript} instance. */\nexport interface ConsentScriptOptions {\n /** The consent cookie ID to watch (e.g. `'analytics_storage'`). */\n cookieId: string\n /** The `ConsentiSetup` widget instance to read consent from. */\n widget: ConsentiSetup\n /** Remote script `src` URL to inject when consent is granted. */\n src?: string\n /**\n * Inline JavaScript to inject when consent is granted (e.g. a GTM snippet).\n * WARNING: this string is assigned directly to `script.innerHTML` — only\n * pass a static, trusted literal. Never pass user-supplied or remote content.\n */\n unsafeInnerHTML?: string\n /**\n * Additional HTML attributes applied to the injected `<script>` element.\n * Set a key to an empty string for boolean attributes (e.g. `{ async: '', defer: '' }`).\n */\n attributes?: Record<string, string>\n /** Called after the `<script>` element has been appended to `<head>`. */\n onLoad?: () => void\n /** Called after the `<script>` element has been removed from the DOM. */\n onRevoke?: () => void\n /**\n * When `true` (default), the script is automatically removed when consent is revoked and\n * re-injected when consent is re-granted. Set to `false` to evaluate consent only once at\n * construction time — no event listener is attached and the script is never auto-removed.\n */\n bind?: boolean\n}\n\n/**\n * Conditionally injects and removes a `<script>` element based on a specific\n * cookie's consent status.\n *\n * The check runs immediately on construction so existing consent is honoured\n * without waiting for the next `consenti:consentSubmitted` event. It then\n * re-evaluates on every future consent change.\n *\n * Call {@link ConsentScript.destroy} when the trigger is no longer needed\n * (e.g. on SPA route unmount) to remove the event listener and the injected\n * element.\n */\nexport class ConsentScript {\n private scriptEl: HTMLScriptElement | null = null\n private readonly handler: () => void\n\n constructor(private readonly options: ConsentScriptOptions) {\n // Assign handler unconditionally to satisfy TypeScript strict property initialisation.\n this.handler = () => this.evaluate()\n\n if (!isClient()) return\n\n if (options.bind !== false) {\n window.addEventListener('consenti:consentSubmitted', this.handler)\n }\n\n // Evaluate immediately so any existing consent is reflected at construction time.\n this.evaluate()\n }\n\n private evaluate(): void {\n const consent = this.options.widget.getConsent()\n const granted = consent?.[this.options.cookieId] === 'granted'\n\n if (granted && !this.scriptEl) {\n this.inject()\n } else if (!granted && this.scriptEl) {\n this.revoke()\n }\n }\n\n private inject(): void {\n const script = injectScript({\n ...(this.options.src !== undefined ? { src: this.options.src } : {}),\n ...(this.options.unsafeInnerHTML !== undefined ? { unsafeInnerHTML: this.options.unsafeInnerHTML } : {}),\n ...(this.options.attributes !== undefined ? { attributes: this.options.attributes } : {}),\n warnPrefix: 'ConsentScript',\n })\n if (!script) return\n this.scriptEl = script\n this.options.onLoad?.()\n }\n\n private revoke(): void {\n revokeScript(this.scriptEl)\n this.scriptEl = null\n this.options.onRevoke?.()\n }\n\n /**\n * Removes the event listener and the injected `<script>` element (if present).\n * Call this when the widget is destroyed or the script is no longer needed.\n */\n destroy(): void {\n if (!isClient()) return\n if (this.options.bind !== false) {\n window.removeEventListener('consenti:consentSubmitted', this.handler)\n }\n this.revoke()\n }\n}\n","/**\n * CategoryScript — conditionally loads/unloads a third-party `<script>` tag\n * based on a whole category's rollup consent state, instead of one parameter.\n *\n * A category is considered \"granted\" only when *every* parameter it contains is\n * `'granted'` — matching the preference modal's tri-state rollup rule and\n * `CategoryAction`'s behavior. Injection mechanics (src validation, inline-script\n * escape hatch, attribute filtering) are identical to `ConsentScript` — both share\n * the same underlying `injectScript`/`revokeScript` helpers.\n *\n * @example\n * ```ts\n * import { ConsentiSetup, CategoryScript } from '@consenti/ui'\n *\n * const widget = new ConsentiSetup({ core: { profileId: 0 } })\n * await widget.ready\n *\n * new CategoryScript({\n * categoryId: 'marketing',\n * widget,\n * src: 'https://example.com/ad-pixel.js',\n * })\n * ```\n */\n\nimport type { ConsentiSetup } from '../core/consenti-setup'\nimport { isClient } from './ssr'\nimport { injectScript, revokeScript } from './script-injector'\n\n/** Options for constructing a {@link CategoryScript} instance. */\nexport interface CategoryScriptOptions {\n /** The category ID to watch (e.g. `'marketing'`). */\n categoryId: string\n /** The `ConsentiSetup` widget instance to read consent from. */\n widget: ConsentiSetup\n /** Remote script `src` URL to inject when every parameter in the category is granted. */\n src?: string\n /**\n * Inline JavaScript to inject when granted (e.g. a GTM snippet).\n * WARNING: this string is assigned directly to `script.innerHTML` — only\n * pass a static, trusted literal. Never pass user-supplied or remote content.\n */\n unsafeInnerHTML?: string\n /**\n * Additional HTML attributes applied to the injected `<script>` element.\n * Set a key to an empty string for boolean attributes (e.g. `{ async: '', defer: '' }`).\n */\n attributes?: Record<string, string>\n /** Called after the `<script>` element has been appended to `<head>`. */\n onLoad?: () => void\n /** Called after the `<script>` element has been removed from the DOM. */\n onRevoke?: () => void\n /**\n * When `true` (default), the script is automatically removed when the category is no\n * longer fully granted and re-injected when it becomes fully granted again. Set to\n * `false` to evaluate consent only once at construction time — no event listener is\n * attached and the script is never auto-removed.\n */\n bind?: boolean\n}\n\n/**\n * Conditionally injects and removes a `<script>` element based on a category's\n * rollup consent status (every member parameter granted).\n *\n * Call {@link CategoryScript.destroy} when the trigger is no longer needed\n * (e.g. on SPA route unmount) to remove the event listener and the injected element.\n */\nexport class CategoryScript {\n private scriptEl: HTMLScriptElement | null = null\n private readonly handler: () => void\n\n constructor(private readonly options: CategoryScriptOptions) {\n // Assign handler unconditionally to satisfy TypeScript strict property initialisation.\n this.handler = () => this.evaluate()\n\n if (!isClient()) return\n\n if (options.bind !== false) {\n window.addEventListener('consenti:consentSubmitted', this.handler)\n }\n\n // Evaluate immediately so any existing consent is reflected at construction time.\n this.evaluate()\n }\n\n private evaluate(): void {\n const category = this.options.widget.getProfile()?.preferenceModal.categories[this.options.categoryId]\n const cookieIds = category?.cookies ?? []\n const consent = this.options.widget.getConsent()\n const granted = cookieIds.length > 0 && cookieIds.every(id => consent?.[id] === 'granted')\n\n if (granted && !this.scriptEl) {\n this.inject()\n } else if (!granted && this.scriptEl) {\n this.revoke()\n }\n }\n\n private inject(): void {\n const script = injectScript({\n ...(this.options.src !== undefined ? { src: this.options.src } : {}),\n ...(this.options.unsafeInnerHTML !== undefined ? { unsafeInnerHTML: this.options.unsafeInnerHTML } : {}),\n ...(this.options.attributes !== undefined ? { attributes: this.options.attributes } : {}),\n warnPrefix: 'CategoryScript',\n })\n if (!script) return\n this.scriptEl = script\n this.options.onLoad?.()\n }\n\n private revoke(): void {\n revokeScript(this.scriptEl)\n this.scriptEl = null\n this.options.onRevoke?.()\n }\n\n /**\n * Removes the event listener and the injected `<script>` element (if present).\n * Call this when the widget is destroyed or the script is no longer needed.\n */\n destroy(): void {\n if (!isClient()) return\n if (this.options.bind !== false) {\n window.removeEventListener('consenti:consentSubmitted', this.handler)\n }\n this.revoke()\n }\n}\n","/**\n * Declarative DOM-scan bindings — a zero-JS integration path for gating\n * third-party scripts via `data-*` attributes instead of hand-written\n * `ConsentScript`/`CategoryScript` calls.\n *\n * ```html\n * <script type=\"text/plain\" data-consenti-category-script=\"marketing\" src=\"https://example.com/pixel.js\"></script>\n * <script type=\"text/plain\" data-consenti-consent-script=\"analytics_storage\">/* inline snippet *\\/</script>\n * <script type=\"text/plain\" data-consenti-consent-script=\"ad_storage\" data-consenti-bind=\"false\" src=\"...\"></script>\n * ```\n *\n * Scanned automatically once per `ConsentiSetup.init()` cycle (after `init()`\n * completes — so it respects `autoInit: false`, only running once the widget is\n * actually initialized, however `init()` was triggered). The scanned tags are\n * read-only config sources and stay inert (`type=\"text/plain\"`) in the DOM\n * permanently — `ConsentScript`/`CategoryScript` inject their own separate\n * `<script>` element exactly as they do when constructed by hand.\n */\n\nimport type { ConsentiSetup } from '../core/consenti-setup'\nimport { isClient } from './ssr'\nimport { ConsentScript } from './consent-script'\nimport { CategoryScript } from './category-script'\n\nconst CONSENT_ATTR = 'data-consenti-consent-script'\nconst CATEGORY_ATTR = 'data-consenti-category-script'\nconst BIND_ATTR = 'data-consenti-bind'\n/** Marks an element as already scanned so a later manual re-scan doesn't instantiate a second, duplicate watcher (and duplicate-inject) for the same tag. */\nconst SCANNED_ATTR = 'data-consenti-scanned'\n\n/** `bind: false` only when the attribute is present and equals the literal string `\"false\"` — every other case (absent, empty, any other value) defaults to `true`. */\nfunction readBind(el: Element): boolean {\n return el.getAttribute(BIND_ATTR) !== 'false'\n}\n\n/**\n * Scans the document for `<script type=\"text/plain\">` tags carrying\n * `data-consenti-consent-script` or `data-consenti-category-script`, and\n * instantiates `ConsentScript`/`CategoryScript` for each. Called automatically\n * by `ConsentiSetup` at the end of every `init()` cycle.\n *\n * v1 scans once automatically; scripts injected into the DOM after that point\n * (e.g. by a late-loading CMS widget) are not picked up automatically — call this\n * function again manually after adding more tags dynamically if needed. Each element\n * is only ever scanned once (marked with `data-consenti-scanned`), so calling this\n * again is safe — it only processes newly-added tags, never re-instantiates a watcher\n * for one already scanned.\n *\n * Returns the created watchers — `ConsentiSetup` holds onto these and calls\n * `.destroy()` on each when the widget itself is destroyed, since a scanner-created\n * watcher has no other owner to release its `consenti:consentSubmitted` listener.\n */\nexport function scanConsentScripts(widget: ConsentiSetup): Array<ConsentScript | CategoryScript> {\n if (!isClient()) return []\n\n const elements = document.querySelectorAll<HTMLScriptElement>(\n `script[type=\"text/plain\"][${CONSENT_ATTR}]:not([${SCANNED_ATTR}]), script[type=\"text/plain\"][${CATEGORY_ATTR}]:not([${SCANNED_ATTR}])`,\n )\n\n const created: Array<ConsentScript | CategoryScript> = []\n\n for (const el of elements) {\n el.setAttribute(SCANNED_ATTR, '')\n\n const src = el.getAttribute('src') ?? undefined\n const unsafeInnerHTML = src ? undefined : (el.textContent?.trim() || undefined)\n const bind = readBind(el)\n\n const consentId = el.getAttribute(CONSENT_ATTR)\n const categoryId = el.getAttribute(CATEGORY_ATTR)\n\n if (consentId) {\n created.push(new ConsentScript({\n cookieId: consentId,\n widget,\n ...(src !== undefined ? { src } : {}),\n ...(unsafeInnerHTML !== undefined ? { unsafeInnerHTML } : {}),\n bind,\n }))\n } else if (categoryId) {\n created.push(new CategoryScript({\n categoryId,\n widget,\n ...(src !== undefined ? { src } : {}),\n ...(unsafeInnerHTML !== undefined ? { unsafeInnerHTML } : {}),\n bind,\n }))\n }\n }\n\n return created\n}\n","/**\n * Maps raw `ConsentValue` records to vendor-specific output formats.\n *\n * Each exported function corresponds to one `ConsentType` value accepted by `getConsent(type)`.\n * They are pure functions that take the stored consent map and the profile's cookie list,\n * returning a vendor-ready object with no side effects.\n */\n\nimport type { ConsentValue, CookieMap, CookiePurpose, CategoryMap } from '../types'\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\ntype ConsentStatus = 'granted' | 'denied' | 'objected'\n\n/**\n * Returns the most permissive consent status for all cookies with a given purpose.\n * 'granted' wins over 'denied' / 'objected'. Returns 'denied' when no matching cookie found.\n */\nfunction getConsentForPurpose(\n consent: ConsentValue,\n cookies: CookieMap,\n purpose: CookiePurpose,\n): ConsentStatus {\n let result: ConsentStatus = 'denied'\n for (const [cookieId, cookie] of Object.entries(cookies)) {\n if (cookie.purpose !== purpose) continue\n const status = consent[cookieId]\n if (status === 'granted') return 'granted'\n if (status === 'objected' && result === 'denied') result = 'objected'\n }\n return result\n}\n\n// ─── Google Consent Mode v2 ───────────────────────────────────────────────────\n\nconst GCM_KEYS = [\n 'ad_storage',\n 'analytics_storage',\n 'ad_user_data',\n 'ad_personalization',\n 'functionality_storage',\n 'personalization_storage',\n 'security_storage',\n] as const\n\n/**\n * Returns the consent map in Google Consent Mode v2 format.\n * Maps 'objected' → 'denied'; applies safe defaults ('granted') for\n * functionality_storage and security_storage when not present.\n * Also sets `ads_data_redaction` and `url_passthrough` helper flags.\n */\nexport function getGoogleGTMConsent(consent: ConsentValue): Record<string, string> {\n const result: Record<string, string> = {}\n for (const key of GCM_KEYS) {\n const raw = consent[key]\n if (raw === 'granted') {\n result[key] = 'granted'\n } else if (raw === 'denied' || raw === 'objected') {\n result[key] = 'denied'\n } else {\n result[key] = (key === 'functionality_storage' || key === 'security_storage') ? 'granted' : 'denied'\n }\n }\n result['ads_data_redaction'] = result['ad_storage'] === 'denied' ? 'true' : 'false'\n result['url_passthrough'] = 'false'\n return result\n}\n\n// ─── Purpose ──────────────────────────────────────────────────────────────────\n\n/**\n * Returns consent grouped by the fixed CookiePurpose taxonomy.\n * 'necessary' is always 'granted'.\n */\nexport function getPurposeConsent(\n consent: ConsentValue,\n cookies: CookieMap,\n): Record<CookiePurpose, ConsentStatus> {\n return {\n necessary: 'granted',\n functional: getConsentForPurpose(consent, cookies, 'functional'),\n preferences: getConsentForPurpose(consent, cookies, 'preferences'),\n analytics: getConsentForPurpose(consent, cookies, 'analytics'),\n marketing: getConsentForPurpose(consent, cookies, 'marketing'),\n }\n}\n\n// ─── Category ─────────────────────────────────────────────────────────────────\n\n/**\n * Returns consent per the tenant's own authored preference-modal category, keyed by\n * category ID. 'granted' only when every member parameter is granted. Otherwise\n * 'objected' when every member is objected (legitimate_interest categories only —\n * their off-state is 'objected', not 'denied'); 'denied' in every other case\n * (uniformly denied, or a genuine mix of granted/denied/objected members).\n */\nexport function getCategoryConsent(\n consent: ConsentValue,\n categories: CategoryMap,\n): Record<string, ConsentStatus> {\n const result: Record<string, ConsentStatus> = {}\n for (const [categoryId, category] of Object.entries(categories)) {\n const offStatus: ConsentStatus = category.legalBasis === 'legitimate_interest' ? 'objected' : 'denied'\n const statuses = category.cookies.map(id => consent[id])\n if (statuses.length > 0 && statuses.every(s => s === 'granted')) {\n result[categoryId] = 'granted'\n } else if (statuses.every(s => s === offStatus || s === undefined)) {\n result[categoryId] = offStatus\n } else {\n result[categoryId] = 'denied'\n }\n }\n return result\n}\n\n// ─── Adobe ────────────────────────────────────────────────────────────────────\n\nexport function getAdobeConsent(\n consent: ConsentValue,\n cookies: CookieMap,\n): { analytics: ConsentStatus; target: ConsentStatus; manager: ConsentStatus; optimizer: ConsentStatus } {\n return {\n analytics: getConsentForPurpose(consent, cookies, 'analytics'),\n target: getConsentForPurpose(consent, cookies, 'preferences'),\n manager: getConsentForPurpose(consent, cookies, 'marketing'),\n optimizer: getConsentForPurpose(consent, cookies, 'marketing'),\n }\n}\n\n// ─── Meta ─────────────────────────────────────────────────────────────────────\n\nexport function getMetaConsent(\n consent: ConsentValue,\n cookies: CookieMap,\n): { pixel: ConsentStatus; api: ConsentStatus; plugins: ConsentStatus; facebookLogin: ConsentStatus } {\n return {\n pixel: getConsentForPurpose(consent, cookies, 'marketing'),\n api: getConsentForPurpose(consent, cookies, 'marketing'),\n plugins: getConsentForPurpose(consent, cookies, 'functional'),\n facebookLogin: getConsentForPurpose(consent, cookies, 'functional'),\n }\n}\n\n// ─── Microsoft Clarity ────────────────────────────────────────────────────────\n\nexport function getMicrosoftClarityConsent(\n consent: ConsentValue,\n cookies: CookieMap,\n): { session: ConsentStatus; heatmaps: ConsentStatus; performance: ConsentStatus } {\n return {\n session: getConsentForPurpose(consent, cookies, 'analytics'),\n heatmaps: getConsentForPurpose(consent, cookies, 'analytics'),\n performance: getConsentForPurpose(consent, cookies, 'analytics'),\n }\n}\n\n// ─── Twilio Segment ───────────────────────────────────────────────────────────\n\nexport function getTwilioSegmentConsent(\n consent: ConsentValue,\n cookies: CookieMap,\n): { identify: ConsentStatus; page: ConsentStatus; track: ConsentStatus; group: ConsentStatus; alias: ConsentStatus } {\n return {\n identify: getConsentForPurpose(consent, cookies, 'preferences'),\n page: getConsentForPurpose(consent, cookies, 'analytics'),\n track: getConsentForPurpose(consent, cookies, 'analytics'),\n group: getConsentForPurpose(consent, cookies, 'analytics'),\n alias: getConsentForPurpose(consent, cookies, 'analytics'),\n }\n}\n","// Auto-generated by scripts/build-css.mjs — do not edit manually.\n// Run `npm run build --workspace=apps/ui` to regenerate.\nexport const CONSENTI_CSS = `.consenti-root{position:relative}.consenti-d-none{display:none !important}:root{--consenti-color-bg: #ffffff;--consenti-color-text: #1a2e4a;--consenti-color-text-muted: #949dab;--consenti-color-primary: #04111f;--consenti-color-primary-text: #ffffff;--consenti-color-secondary: #f0f4f8;--consenti-color-secondary-text: #1a2e4a;--consenti-color-border: #dbe4ee;--consenti-color-secondary-border: #1a2e4a;--consenti-color-overlay: #04111f;--consenti-color-accent: #d32f2f;--consenti-color-accent-text: #ffffff;--consenti-font-family: system-ui, -apple-system, sans-serif;--consenti-font-family-mono: ui-monospace, monospace;--consenti-font-size-base: 14px;--consenti-font-size-heading: 16px;--consenti-font-weight-heading: 600;--consenti-line-height: 1.5;--consenti-spacing-xs: 5px;--consenti-spacing-sm: 8px;--consenti-spacing-md: 16px;--consenti-spacing-lg: 24px;--consenti-border-radius: 8px;--consenti-border-radius-btn: 0;--consenti-shadow: 0 4px 24px rgba(21, 101, 192, 0.14);--consenti-toggle-bg-on: #43a047;--consenti-toggle-bg-partial: #97c098;--consenti-toggle-bg-off: #9ca3af;--consenti-toggle-knob: #ffffff;--consenti-toggle-width: 52px;--consenti-toggle-height: 28px;--consenti-z-banner: 9999;--consenti-z-overlay: 9998;--consenti-z-modal: 10000}.consenti-root--dark{--consenti-color-bg: #1e2535;--consenti-color-text: #e2e8f0;--consenti-color-text-muted: #a4acb9;--consenti-color-primary: #e2e8f0;--consenti-color-primary-text: #2a3447;--consenti-color-secondary: #2a3447;--consenti-color-secondary-text: #cbd5e1;--consenti-color-border: #374151;--consenti-color-overlay: rgba(0, 0, 0, 0.65);--consenti-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);--consenti-toggle-bg-off: #6b7280}.consenti-overlay{position:fixed;inset:0;background:var(--consenti-color-overlay);z-index:var(--consenti-z-overlay)}.consenti-btn{display:inline-flex;align-items:center;justify-content:center;padding:var(--consenti-spacing-sm) var(--consenti-spacing-md);border:none;border-radius:var(--consenti-border-radius-btn);font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);font-weight:500;line-height:1;cursor:pointer;text-decoration:none;transition:opacity .15s ease}.consenti-btn:focus-visible{outline:2px solid var(--consenti-color-primary);outline-offset:2px}.consenti-btn:hover{opacity:.9}.consenti-btn--primary{background:var(--consenti-color-primary);color:var(--consenti-color-primary-text);border:1px solid var(--consenti-color-primary)}.consenti-btn--secondary{background:var(--consenti-color-secondary);color:var(--consenti-color-primary);border:1px solid var(--consenti-color-primary)}.consenti-btn--secondary:hover{background:var(--consenti-color-primary);color:var(--consenti-color-secondary)}.consenti-btn--text{background:rgba(0,0,0,0);color:var(--consenti-color-primary);padding-left:0;padding-right:0;text-decoration:underline;text-underline-offset:4px;border:1px solid rgba(0,0,0,0)}.consenti-btn--accent{background:var(--consenti-color-accent);color:var(--consenti-color-accent-text)}.consenti-btn--action{background:rgba(0,0,0,0);color:var(--consenti-color-text);font-size:20px;line-height:1;border-radius:50%;height:24px;width:24px;font-size:24px;padding:0}.consenti-banner{position:fixed;left:0;right:0;z-index:var(--consenti-z-banner);background:var(--consenti-color-bg);color:var(--consenti-color-text);font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);line-height:var(--consenti-line-height);box-shadow:var(--consenti-shadow);padding:var(--consenti-spacing-lg);display:flex;flex-wrap:wrap;align-items:center;gap:var(--consenti-spacing-md)}.consenti-banner--top{top:0;border-bottom:1px solid var(--consenti-color-border)}@media(prefers-reduced-motion: no-preference){.consenti-banner--top{animation:consenti-slide-down .2s ease-out}}.consenti-banner--bottom{bottom:0;border-top:1px solid var(--consenti-color-border)}@media(prefers-reduced-motion: no-preference){.consenti-banner--bottom{animation:consenti-slide-up .2s ease-out}}.consenti-banner--top .consenti-banner__text-container,.consenti-banner--bottom .consenti-banner__text-container{min-width:60vw}.consenti-banner--middle{top:50%;transform:translateY(-50%);left:50%;right:auto;transform:translate(-50%, -50%);width:min(600px,90vw);border-radius:var(--consenti-border-radius);flex-direction:column;align-items:flex-start}.consenti-banner--left-bottom{bottom:var(--consenti-spacing-lg);left:var(--consenti-spacing-lg);right:auto;width:min(400px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner--right-bottom{bottom:var(--consenti-spacing-lg);right:var(--consenti-spacing-lg);left:auto;width:min(400px,90vw);border-radius:var(--consenti-border-radius)}.consenti-banner__text-container{flex:1}.consenti-banner__text-container a:not(.consenti-banner__link){color:var(--consenti-color-text)}.consenti-banner__text-container a:not(.consenti-banner__link):hover{color:var(--consenti-color-primary)}.consenti-banner__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0 0 var(--consenti-spacing-sm);width:calc(100% - 56px)}.consenti-banner__text{margin:0}.consenti-banner__links{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);margin-top:var(--consenti-spacing-sm)}.consenti-banner__link{font-size:.87em;padding:var(--consenti-spacing-xs) var(--consenti-spacing-sm);border-radius:var(--consenti-spacing-xs)}.consenti-banner__link:first-child{padding-left:0}.consenti-banner__controls{position:absolute;top:var(--consenti-spacing-sm);right:var(--consenti-spacing-sm);display:flex;align-items:center;gap:4px}.consenti-banner__buttons{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);align-items:center;margin-top:var(--consenti-spacing-sm)}.consenti-banner__close{margin-top:-5px}.consenti-banner .consenti__powered-by{bottom:0}@media(max-width: 576px){.consenti-banner{padding:var(--consenti-spacing-md);gap:var(--consenti-spacing-sm)}.consenti-banner--left-bottom,.consenti-banner--right-bottom{left:0;right:0;bottom:0;width:100%;border-radius:0;border-top:1px solid var(--consenti-color-border)}.consenti-banner--middle{width:95vw}.consenti-banner__buttons{width:100%;flex-direction:column;align-items:stretch}.consenti-banner__text-container{min-width:0;width:100%}}@keyframes consenti-slide-up{from{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes consenti-slide-down{from{transform:translateY(-100%)}to{transform:translateY(0)}}.consenti-modal{position:fixed;inset:0;z-index:var(--consenti-z-modal);display:flex;font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);color:var(--consenti-color-text);line-height:var(--consenti-line-height)}.consenti-modal--passthrough{pointer-events:none}.consenti-modal--passthrough .consenti-modal__container{pointer-events:auto}.consenti-modal--center{align-items:center;justify-content:center}.consenti-modal--left{align-items:stretch;justify-content:flex-start}.consenti-modal--left .consenti-modal__container{border-radius:0 var(--consenti-border-radius) var(--consenti-border-radius) 0;height:100%;max-height:100vh}.consenti-modal--right{align-items:stretch;justify-content:flex-end}.consenti-modal--right .consenti-modal__container{border-radius:0;height:100%;max-height:100vh}.consenti-modal--fullscreen{align-items:flex-start;justify-content:flex-start}.consenti-modal--fullscreen .consenti-modal__container{width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;border-radius:0;margin:0}.consenti-modal__overlay{position:absolute;inset:0;background:var(--consenti-color-overlay)}.consenti-modal__container{position:relative;z-index:1;background:var(--consenti-color-bg);border-radius:var(--consenti-border-radius);box-shadow:var(--consenti-shadow);width:min(560px,90vw);max-height:85vh;display:flex;flex-direction:column;overflow:hidden}@media(prefers-reduced-motion: no-preference){.consenti-modal__container{animation:consenti-fade-in .15s ease-out}}.consenti-modal__header{display:flex;justify-content:space-between;padding:var(--consenti-spacing-md) var(--consenti-spacing-lg);border-bottom:1px solid var(--consenti-color-border);flex-shrink:0;position:relative;flex-direction:column}.consenti-modal__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0;flex:1;width:calc(100% - 56px)}.consenti-modal__header-controls{display:flex;align-items:center;gap:6px;flex-shrink:0;position:absolute;inset-inline-end:15px;top:10px}.consenti-modal__close{flex-shrink:0;margin-top:-5px}.consenti-modal__links{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);margin-top:var(--consenti-spacing-md)}.consenti-modal__link{font-size:.87em;padding:var(--consenti-spacing-xs) var(--consenti-spacing-sm);border-radius:var(--consenti-spacing-xs)}.consenti-modal__link:first-child{padding-left:0}.consenti-modal__subheading{padding-top:var(--consenti-spacing-sm);margin:0;color:var(--consenti-color-text-muted);font-size:13px}.consenti-modal__text{padding:var(--consenti-spacing-sm) 0}.consenti-modal__body{overflow-y:auto;flex:1;padding:var(--consenti-spacing-sm) var(--consenti-spacing-lg);position:relative;display:flex;flex-direction:column}.consenti-modal__body .consenti__powered-by{bottom:0;left:0;position:relative}.consenti-modal__categories{display:flex;flex-grow:1;flex-direction:column;gap:var(--consenti-spacing-md);padding:var(--consenti-spacing-md) 0}.consenti-modal__buttons{display:flex;flex-wrap:wrap;gap:var(--consenti-spacing-sm);padding:var(--consenti-spacing-md);border-top:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__receipt-option{padding-top:var(--consenti-spacing-md);border-top:1px solid var(--consenti-color-border);flex-shrink:0}.consenti-modal__receipt-label{display:flex;align-items:center;gap:var(--consenti-spacing-sm);font-size:var(--consenti-font-size-base);cursor:pointer}.consenti-modal__receipt-checkbox{width:16px;height:16px;flex-shrink:0;cursor:pointer;accent-color:var(--consenti-toggle-bg-on)}.consenti-modal__receipt-desc{margin:4px 0 0 0;margin-inline-start:24px;font-size:12px;color:#6b7280}@keyframes consenti-fade-in{from{opacity:0;transform:scale(0.97)}to{opacity:1;transform:scale(1)}}.consenti-category{padding:var(--consenti-spacing-md) 0;border-bottom:1px solid var(--consenti-color-border)}.consenti-category:last-child{border-bottom:none}.consenti-category--mandatory .consenti-category__toggle{opacity:.6;cursor:default}.consenti-category__header{display:flex;align-items:center;justify-content:space-between;gap:var(--consenti-spacing-md);margin-bottom:var(--consenti-spacing-sm)}.consenti-category__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin:0}.consenti-category__text{color:var(--consenti-color-text-muted);font-size:13px;margin:0}.consenti-category__text--clamped{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.consenti-category__text-wrap{display:flex;flex-direction:column;align-items:flex-start;gap:4px}.consenti-category__show-more{background:none;border:none;padding:0;color:var(--consenti-color-primary);cursor:pointer;font-size:12px;text-decoration:underline;text-underline-offset:2px}.consenti-category__show-more:hover{opacity:.8}.consenti-category__li-desc{margin-bottom:var(--consenti-spacing-sm);font-size:12px;color:#6b7280;font-style:italic}.consenti-category__toggle{flex-shrink:0;position:relative;width:var(--consenti-toggle-width);height:var(--consenti-toggle-height);border-radius:calc(var(--consenti-toggle-height)/2);background:var(--consenti-toggle-bg-off);border:none;cursor:pointer;transition:background .2s ease;padding:0}.consenti-category__toggle:focus-visible{outline:2px solid var(--consenti-color-primary);outline-offset:2px}.consenti-category__toggle[aria-checked=true]{background:var(--consenti-toggle-bg-on)}.consenti-category__toggle--mandatory{cursor:default}.consenti-category__toggle--partial{background:var(--consenti-toggle-bg-partial, var(--consenti-color-accent, #f59e0b))}.consenti-category__toggle--partial .consenti-category__toggle-icon--check,.consenti-category__toggle--partial .consenti-category__toggle-icon--cross,.consenti-category__toggle--partial .consenti-category__toggle-icon--objected{opacity:0}.consenti-category__toggle--partial.consenti-toggle--li .consenti-category__toggle-icon--objected{opacity:0}.consenti-category__toggle--partial .consenti-category__toggle-knob{transform:translateX(calc((var(--consenti-toggle-width) - var(--consenti-toggle-height)) / 2))}.consenti-category__toggle--partial::after{content:\"\";position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:8px;height:2px;border-radius:1px;background:rgba(255,255,255,.9);pointer-events:none;z-index:1}.consenti-category__toggle-icon{position:absolute;top:50%;transform:translateY(-50%);width:10px;height:10px;display:flex;align-items:center;justify-content:center;pointer-events:none;transition:opacity .15s ease;color:rgba(255,255,255,.9)}.consenti-category__toggle-icon svg{width:10px;height:10px;overflow:visible}.consenti-category__toggle-icon--check{left:8px;opacity:0}[aria-checked=true] .consenti-category__toggle-icon--check{opacity:1}.consenti-category__toggle-icon--cross{right:8px;opacity:1}[aria-checked=true] .consenti-category__toggle-icon--cross{opacity:0}.consenti-category__toggle-icon--objected{right:8px;opacity:0}.consenti-category__toggle-knob{position:absolute;top:2px;left:2px;width:calc(var(--consenti-toggle-height) - 4px);height:calc(var(--consenti-toggle-height) - 4px);border-radius:50%;background:var(--consenti-toggle-knob);transition:transform .2s ease;display:block}[aria-checked=true] .consenti-category__toggle-knob{transform:translateX(calc(var(--consenti-toggle-width) - var(--consenti-toggle-height)))}.consenti-category__params{display:flex;flex-direction:column;gap:var(--consenti-spacing-xs, 6px);margin-top:var(--consenti-spacing-sm);padding-top:var(--consenti-spacing-sm);border-top:1px dashed var(--consenti-color-border)}.consenti-category__param{display:flex;align-items:center;justify-content:space-between;gap:var(--consenti-spacing-sm)}.consenti-category__param-info{display:flex;flex-direction:column;gap:2px;min-width:0}.consenti-category__param-label{font-size:12px;font-family:var(--consenti-font-family-mono, monospace);color:var(--consenti-color-text);overflow-wrap:anywhere}.consenti-category__param-meta{font-size:11px;color:var(--consenti-color-text-muted)}.consenti-category__param-toggle{flex-shrink:0;position:relative;width:calc(var(--consenti-toggle-width)*.8);height:calc(var(--consenti-toggle-height)*.8);border-radius:calc(var(--consenti-toggle-height)*.4);background:var(--consenti-toggle-bg-off);border:none;cursor:pointer;transition:background .2s ease;padding:0}.consenti-category__param-toggle:focus-visible{outline:2px solid var(--consenti-color-primary);outline-offset:2px}.consenti-category__param-toggle[aria-checked=true]{background:var(--consenti-toggle-bg-on)}.consenti-category__param-toggle--mandatory{cursor:default;opacity:.6}.consenti-category__param-toggle .consenti-category__toggle-icon{width:8px;height:8px}.consenti-category__param-toggle .consenti-category__toggle-icon svg{width:8px;height:8px}.consenti-category__param-toggle .consenti-category__toggle-icon--check{left:6px}.consenti-category__param-toggle .consenti-category__toggle-icon--cross{right:6px}.consenti-category__param-toggle .consenti-category__toggle-icon--objected{right:6px}.consenti-category__param-toggle .consenti-category__toggle-knob{width:calc(var(--consenti-toggle-height)*.8 - 4px);height:calc(var(--consenti-toggle-height)*.8 - 4px)}.consenti-category__param-toggle[aria-checked=true] .consenti-category__toggle-knob{transform:translateX(calc(var(--consenti-toggle-width) * 0.8 - var(--consenti-toggle-height) * 0.8))}.consenti-toggle--li .consenti-category__toggle-icon--cross{opacity:0}.consenti-toggle--li .consenti-category__toggle-icon--objected{opacity:1}.consenti-toggle--li[aria-checked=true] .consenti-category__toggle-icon--objected{opacity:0}[dir=rtl] .consenti-category__toggle-knob{left:auto;right:2px}[dir=rtl] .consenti-category__toggle[aria-checked=true] .consenti-category__toggle-knob{transform:translateX(calc(-1 * (var(--consenti-toggle-width) - var(--consenti-toggle-height))))}[dir=rtl] .consenti-category__toggle--partial .consenti-category__toggle-knob{transform:translateX(calc(-1 * (var(--consenti-toggle-width) - var(--consenti-toggle-height)) / 2))}[dir=rtl] .consenti-category__param-toggle[aria-checked=true] .consenti-category__toggle-knob{transform:translateX(calc(-1 * (var(--consenti-toggle-width) * 0.8 - var(--consenti-toggle-height) * 0.8)))}[dir=rtl] .consenti-category__toggle-icon--check{left:auto;right:8px}[dir=rtl] .consenti-category__toggle-icon--cross,[dir=rtl] .consenti-category__toggle-icon--objected{right:auto;left:8px}[dir=rtl] .consenti-category__param-toggle .consenti-category__toggle-icon--check{left:auto;right:6px}[dir=rtl] .consenti-category__param-toggle .consenti-category__toggle-icon--cross,[dir=rtl] .consenti-category__param-toggle .consenti-category__toggle-icon--objected{right:auto;left:6px}.consenti-locale-switcher{position:relative;display:inline-flex}.consenti-locale-switcher__btn{font-size:1rem;line-height:1;cursor:pointer}.consenti-locale-switcher__list{position:absolute;top:calc(100% + 4px);inset-inline-end:0;z-index:calc(var(--consenti-z-modal) + 1);background:var(--consenti-color-bg);border:1px solid var(--consenti-color-border);border-radius:var(--consenti-border-radius);box-shadow:var(--consenti-shadow);list-style:none;margin:0;padding:4px 0;min-width:80px}.consenti-locale-switcher__list li{display:block}.consenti-locale-switcher__option{display:block;width:100%;padding:6px 12px;text-align:start;font-size:var(--consenti-font-size-base);color:var(--consenti-color-text);background:none;border:none;cursor:pointer}.consenti-locale-switcher__option:hover{background:var(--consenti-color-secondary)}.consenti-locale-switcher__option--active{font-weight:600;color:var(--consenti-color-primary)}.consenti-locale-switcher--modal .consenti-locale-switcher__list{inset-inline-end:0}.consenti-meta-footer{display:flex;flex-wrap:wrap;gap:.5rem .75rem;font-size:.7rem;color:var(--consenti-color-text);opacity:.6;border-top:1px solid var(--consenti-color-border);padding:var(--consenti-spacing-sm) var(--consenti-spacing-md);justify-content:space-between;margin-top:.75rem;min-width:0}.consenti-meta-footer__item{display:inline-flex;align-items:center;gap:.25rem;min-width:0;max-width:100%}.consenti-meta-footer__id{font-family:monospace;font-size:.65rem;overflow-wrap:anywhere}.consenti-meta-footer__settings{background:none;border:none;padding:0;color:var(--consenti-color-primary);cursor:pointer;font-size:.7rem;text-decoration:underline;text-underline-offset:2px}.consenti-meta-footer__settings:hover{opacity:.8}.consenti--enhanced-a11y .consenti-btn{min-height:44px;min-width:44px}.consenti--enhanced-a11y .consenti-category__toggle{min-width:52px;min-height:28px}.consenti--enhanced-a11y .consenti-category__param-toggle{min-width:44px;min-height:24px}.consenti--enhanced-a11y *:focus-visible{outline:3px solid var(--consenti-color-primary);outline-offset:2px}.consenti-age-gate{position:fixed;inset:0;z-index:var(--consenti-z-modal);margin:auto;width:min(400px,90vw);height:fit-content;background:var(--consenti-color-bg);color:var(--consenti-color-text);border-radius:var(--consenti-border-radius);box-shadow:var(--consenti-shadow);padding:var(--consenti-spacing-lg);font-family:var(--consenti-font-family);font-size:var(--consenti-font-size-base);line-height:var(--consenti-line-height)}.consenti-age-gate__heading{font-size:var(--consenti-font-size-heading);font-weight:var(--consenti-font-weight-heading);margin-bottom:var(--consenti-spacing-sm)}.consenti-age-gate__text{color:var(--consenti-color-text-muted);margin-bottom:var(--consenti-spacing-md)}.consenti-age-gate__buttons{display:flex;gap:var(--consenti-spacing-sm)}.consenti__powered-by{position:absolute;inset-inline-end:var(--consenti-spacing-md);opacity:.3;color:var(--consenti-color-primary);font-size:70%}.consenti-transition-1_2x{transition:transform 300ms ease-in-out}.consenti-transition-1_2x:hover{transform:scale(1.2)}`\n","/**\n * Cross-tab consent synchronisation via the `BroadcastChannel` API.\n *\n * When a user interacts with the consent widget in one tab (accept, deny, delete),\n * `ConsentiChannel` broadcasts the change to all other open tabs on the same origin.\n * Receiving tabs update their in-memory state and hide the banner without requiring\n * a page reload.\n *\n * `BroadcastChannel` is not available in very old browsers or in Safari < 15.4.\n * The `isSupported` guard ensures `ConsentiChannel` fails silently in those cases\n * — consent still works, cross-tab sync is just unavailable.\n */\n\nimport type { ConsentiMessage, ConsentValue } from '../types'\nimport { isClient } from './ssr'\nimport { logger } from './console'\n\nexport type { ConsentiMessage }\n\n/**\n * Wrapper around the native `BroadcastChannel` API for Consenti inter-tab messaging.\n *\n * All tabs with an active `ConsentiSetup` instance for the same origin subscribe\n * on the shared channel name `'consenti'` and receive each other's consent events.\n */\nexport class ConsentiChannel {\n private channel: BroadcastChannel | null = null\n private onMessageCallback: ((msg: ConsentiMessage) => void) | undefined\n\n /** `true` when `BroadcastChannel` is available in the current environment. */\n get isSupported(): boolean {\n return isClient() && typeof BroadcastChannel !== 'undefined'\n }\n\n /**\n * Opens the `BroadcastChannel` and registers a message handler.\n * Silently no-ops if `BroadcastChannel` is unsupported.\n *\n * @param onMessage - Callback invoked with each received `ConsentiMessage`.\n */\n connect(onMessage: (msg: ConsentiMessage) => void): void {\n if (!this.isSupported) return\n this.onMessageCallback = onMessage\n this.channel = new BroadcastChannel('consenti')\n this.channel.onmessage = (event: MessageEvent<ConsentiMessage>) => {\n if (event.data?.type) {\n onMessage(event.data)\n }\n }\n this.channel.onmessageerror = () => {\n logger.warn('BroadcastChannel message error — ignoring')\n }\n }\n\n /**\n * Posts a message to all other tabs connected to the `'consenti'` channel.\n * The sending tab does not receive its own message.\n *\n * @param message - The `ConsentiMessage` to broadcast.\n */\n send(message: ConsentiMessage): void {\n this.channel?.postMessage(message)\n }\n\n /** Closes the channel and removes the message callback. */\n disconnect(): void {\n this.channel?.close()\n this.channel = null\n this.onMessageCallback = void 0\n }\n}\n\n// Re-export so callers can use ConsentValue without going through types\nexport type { ConsentValue }\n","/**\n * Consent receipt generation and download.\n *\n * When `core.allowReceipt: true`, the preference modal shows an opt-in checkbox.\n * If the user checks it before saving, `downloadReceipt()` is called after\n * `submitConsent()` to download a JSON file to their device.\n *\n * The receipt is a portable record of the user's choices at a specific point in time.\n * If `core.signCookies` is enabled, the receipt also includes the HMAC signature as\n * tamper evidence.\n */\n\nimport type { ConsentReceipt, ConsentValue } from '../types'\n\n/** Parameters required to construct a consent receipt. */\nexport interface ReceiptParams {\n /** Censent version for the visitor. */\n version: string\n /** Visitor UUID from the `consenti_uid` cookie. */\n visitorId: string\n /** Numeric profile ID. */\n profileId: string\n /** Locale active when consent was given. */\n locale: string\n /** The full consent map. */\n consent: ConsentValue\n /** HMAC-SHA256 hex signature, if `core.signCookies` is enabled. */\n signature?: string | undefined\n}\n\n/**\n * Builds a `ConsentReceipt` from the given parameters.\n * Sets `version: '1.0'` and `issuedAt` to the current time.\n *\n * @param params - Receipt content. See `ReceiptParams`.\n */\nexport function generateReceipt(params: ReceiptParams): ConsentReceipt {\n const receipt: ConsentReceipt = {\n version: params.version,\n issuedAt: new Date().toISOString(),\n visitorId: params.visitorId,\n profileId: params.profileId,\n locale: params.locale,\n consent: params.consent,\n }\n if (params.signature) receipt.signature = params.signature\n return receipt\n}\n\n/**\n * Triggers a browser file download of the receipt as a pretty-printed JSON file.\n *\n * The filename is `consenti-receipt-{YYYY-MM-DD}.json`.\n * Uses `Blob` + `URL.createObjectURL` + a synthetic anchor click —\n * this approach is supported in all ES2020 target browsers.\n *\n * Silently returns when called in an SSR context.\n *\n * @param receipt - The receipt object returned by `generateReceipt()`.\n */\nexport function downloadReceipt(receipt: ConsentReceipt): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return\n\n const json = JSON.stringify(receipt, null, 2)\n const blob = new Blob([json], { type: 'application/json' })\n const url = URL.createObjectURL(blob)\n\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `consenti-receipt-${receipt.issuedAt.slice(0, 10)}.json`\n anchor.style.display = 'none'\n\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n}\n","export function generateUUID(): string {\n return crypto.randomUUID()\n}\n\n/**\n * Generates a Stripe-style prefixed ID (e.g. `cons_...`, `visi_...`) so the ID's type is\n * readable at a glance in events, receipts, and support tickets — mirrors the prefixes the\n * API already uses for its own record IDs (`randomConsentId`/`randomVisitorId` in\n * apps/api/src/utils/crypto.ts). `pcon` (parental consent token) is client-only — the\n * server just stores it as an opaque string, no matching server-side prefix exists.\n */\nexport function generatePrefixedId(prefix: 'cons' | 'visi' | 'pcon'): string {\n return `${prefix}_${crypto.randomUUID().replace(/-/g, '')}`\n}\n","/**\n * Low-level `document.cookie` read/write helpers.\n *\n * All functions are SSR-safe — they return early or return `null` when `isClient()`\n * is `false`. Consumers should prefer the `ConsentStorage` class which abstracts\n * over both cookies and `localStorage`.\n */\n\nimport type { CookieOptions, ConsentCookieData, ConsentShortValue, ConsentValue } from '../types'\nimport { isClient } from './ssr'\n\n/**\n * Reads a single cookie value by name.\n *\n * @param name - Cookie name (unencoded).\n * @returns The decoded cookie value, or `null` if the cookie is absent or SSR context.\n */\nexport function getCookie(name: string): string | null {\n if (!isClient()) return null\n const match = document.cookie.match(new RegExp(`(?:^|;\\\\s*)${escapeRegExp(name)}=([^;]*)`))\n return match ? decodeURIComponent(match[1] ?? '') : null\n}\n\n/**\n * Writes a cookie to `document.cookie`.\n *\n * The `Secure` flag is auto-detected from `window.location.protocol` when `opts.secure`\n * is not explicitly provided — cookies are set without `Secure` on `http://` to allow\n * local development without HTTPS.\n *\n * @param name - Cookie name (unencoded).\n * @param value - Cookie value (unencoded — will be URI-encoded).\n * @param opts - Optional cookie attributes.\n */\nexport function setCookie(name: string, value: string, opts: CookieOptions = {}): void {\n if (!isClient()) return\n const secure =\n opts.secure ??\n (typeof window !== 'undefined' &&\n typeof window.location !== 'undefined' &&\n window.location.protocol === 'https:')\n const parts = [\n `${encodeURIComponent(name)}=${encodeURIComponent(value)}`,\n `Path=${opts.path ?? '/'}`,\n `SameSite=${opts.sameSite ?? 'Lax'}`,\n opts.maxAge != null ? `Max-Age=${opts.maxAge}` : '',\n secure ? 'Secure' : '',\n opts.domain ? `Domain=${opts.domain}` : '',\n ]\n document.cookie = parts.filter(Boolean).join('; ')\n}\n\n/**\n * Deletes a cookie by setting `Max-Age=0`.\n *\n * @param name - Cookie name (unencoded).\n * @param opts - Optional `path` and `domain` attributes — must match the original `setCookie` call.\n */\nexport function deleteCookie(name: string, opts: Pick<CookieOptions, 'path' | 'domain'> = {}): void {\n setCookie(name, '', { ...opts, maxAge: 0 })\n}\n\n/** Escapes special regex characters in a cookie name so it can be used in a `RegExp`. */\nfunction escapeRegExp(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n// ─── Consent codec ─────────────────────────────────────────────────────────────\n\nfunction isConsentCookieData(obj: unknown): obj is ConsentCookieData {\n if (typeof obj !== 'object' || obj === null) return false\n const d = obj as Record<string, unknown>\n return (\n typeof d['s'] === 'string' &&\n typeof d['v'] === 'number' &&\n typeof d['i'] === 'string' &&\n typeof d['u'] === 'string' &&\n typeof d['t'] === 'number' &&\n (d['g'] === 0 || d['g'] === 1) &&\n typeof d['p'] === 'number' &&\n typeof d['c'] === 'object' && d['c'] !== null &&\n typeof d['l'] === 'string'\n )\n}\n\n/** Serialises a `ConsentCookieData` object to the cookie value string (JSON, no signature). */\nexport function encodeConsent(data: ConsentCookieData): string {\n return JSON.stringify(data)\n}\n\n/**\n * Parses a raw cookie string back into `ConsentCookieData`.\n *\n * Strips a `::sig:{hmac}` suffix if present before parsing. Returns `null` when the\n * JSON is missing required fields or the string is not valid JSON.\n */\nexport function decodeConsent(raw: string): ConsentCookieData | null {\n const sigIdx = raw.lastIndexOf('::sig:')\n const jsonStr = sigIdx !== -1 ? raw.slice(0, sigIdx) : raw\n try {\n const obj = JSON.parse(jsonStr) as unknown\n return isConsentCookieData(obj) ? obj : null\n } catch {\n return null\n }\n}\n\n/** Expands the compact short-key consent map to the full-word `ConsentValue` used by the public API. */\nexport function expandConsent(data: ConsentCookieData): ConsentValue {\n const result: ConsentValue = {}\n for (const [id, short] of Object.entries(data.c)) {\n if (short === 'g') result[id] = 'granted'\n else if (short === 'o') result[id] = 'objected'\n else result[id] = 'denied'\n }\n return result\n}\n\n/** Compresses a full-word `ConsentValue` map into the compact single-letter form for cookie storage. */\nexport function compressConsent(value: ConsentValue): Record<string, ConsentShortValue> {\n const result: Record<string, ConsentShortValue> = {}\n for (const [id, status] of Object.entries(value)) {\n if (status === 'granted') result[id] = 'g'\n else if (status === 'objected') result[id] = 'o'\n else result[id] = 'd'\n }\n return result\n}\n","/**\n * Unified storage abstraction for consent data.\n *\n * `ConsentStorage` normalises `document.cookie` and `localStorage` behind a single\n * `read / write / remove` interface so the rest of the codebase does not need to\n * know which persistence backend is active.\n *\n * `'cookie'` is the default and recommended mode — consent written to a cookie is\n * visible across subdomains when a `Domain` attribute is set.\n * `'localStorage'` is origin-scoped and cannot be shared with subdomains; it is also\n * incompatible with API mode (which sets consent via `Set-Cookie` on the server).\n */\n\nimport type { CookieOptions } from '../types'\nimport { isClient } from './ssr'\nimport { getCookie, setCookie, deleteCookie } from './cookie'\nimport { logger } from './console'\n\n/** The two supported storage backends. */\nexport type StorageMode = 'cookie' | 'localStorage'\n\n/**\n * Abstraction layer over `document.cookie` and `localStorage`.\n *\n * All operations are SSR-safe — they return early without throwing when `isClient()` is `false`.\n */\nexport class ConsentStorage {\n /**\n * @param mode - `'cookie'` (default) or `'localStorage'`. Falls back to `'cookie'` if\n * `localStorage` is requested but unavailable in the current environment.\n */\n constructor(private mode: StorageMode) {\n if (mode === 'localStorage' && isClient() && typeof localStorage === 'undefined') {\n logger.warn('localStorage is not available, falling back to cookie storage')\n this.mode = 'cookie'\n }\n }\n\n /**\n * Reads a value from storage.\n *\n * @param key - Cookie name or localStorage key.\n * @returns The stored string value, or `null` if absent or SSR context.\n */\n read(key: string): string | null {\n if (!isClient()) return null\n if (this.mode === 'localStorage') {\n return localStorage.getItem(key)\n }\n return getCookie(key)\n }\n\n /**\n * Writes a value to storage.\n *\n * @param key - Cookie name or localStorage key.\n * @param value - String value to persist.\n * @param opts - Cookie attributes (ignored in localStorage mode).\n */\n write(key: string, value: string, opts: CookieOptions = {}): void {\n if (!isClient()) return\n if (this.mode === 'localStorage') {\n localStorage.setItem(key, value)\n } else {\n setCookie(key, value, opts)\n }\n }\n\n /**\n * Removes a value from storage.\n *\n * @param key - Cookie name or localStorage key.\n * @param opts - Cookie `path` / `domain` attributes (must match the original write call).\n */\n remove(key: string, opts: Pick<CookieOptions, 'path' | 'domain'> = {}): void {\n if (!isClient()) return\n if (this.mode === 'localStorage') {\n localStorage.removeItem(key)\n } else {\n deleteCookie(key, opts)\n }\n }\n}\n","/**\n * Dedicated visitor-id storage — a cookie/localStorage item, not a generic config blob.\n *\n * Consenti ever persists exactly two client-side items: the `consenti_data` consent record\n * (see `consent-store.ts`) and this one. There is no `consenti_config`/JSON-blob store — the\n * random suffix minted by `generatePrefixedId('visi')` (e.g. `visi_ab12...`) becomes the key\n * itself (`consenti_visi_ab12...`), so the id's own value never needs to be duplicated into a\n * stored value; presence of a `consenti_visi_*` key/cookie *is* the identity. Reading it back\n * means scanning for that prefix, the same idiom `migrateLegacyCookie()` already uses in\n * `consent-store.ts` for its own prefix-matched legacy-cookie lookup.\n *\n * Written only once a consent decision has actually happened (submit, GPC auto-response, or\n * relaying a decision made in another tab) — never eagerly on page load. Nothing is generated\n * or persisted for a visitor who hasn't decided yet.\n */\n\nimport type { CookieOptions } from '../types'\nimport { isClient } from './ssr'\nimport { generatePrefixedId } from './uuid'\nimport { ConsentStorage, type StorageMode } from './storage'\n\nconst KEY_PREFIX = 'consenti_'\nconst ID_PREFIX = 'visi_'\n\n/** Two years, matching the durability visitor-id cookies conventionally get elsewhere (e.g. GA's `_ga`). */\nconst MAX_AGE_SECONDS = 60 * 60 * 24 * 365 * 2\n\nfunction allStorageKeys(mode: StorageMode): string[] {\n if (!isClient()) return []\n if (mode === 'localStorage') {\n return typeof localStorage !== 'undefined' ? Object.keys(localStorage) : []\n }\n return document.cookie.split(';').map((c) => c.split('=')[0]?.trim() ?? '')\n}\n\nfunction findStoredVisitorId(mode: StorageMode): string | null {\n for (const key of allStorageKeys(mode)) {\n if (key.startsWith(KEY_PREFIX + ID_PREFIX)) return key.slice(KEY_PREFIX.length)\n }\n return null\n}\n\n/**\n * Returns the stored visitor ID for this browser, creating and persisting one if absent.\n *\n * @param mode - `'cookie'` (default) or `'localStorage'` — should match `core.storage`.\n * @param cookieDomains - Comma-separated domain list; first entry used as `Domain` (cookie mode only).\n */\nexport function getOrCreateVisitorId(mode: StorageMode = 'cookie', cookieDomains?: string): string {\n const existing = findStoredVisitorId(mode)\n if (existing) return existing\n\n const visitorId = generatePrefixedId('visi')\n const opts: CookieOptions = { path: '/', sameSite: 'Lax', maxAge: MAX_AGE_SECONDS }\n if (cookieDomains) {\n const domain = cookieDomains.split(',')[0]?.trim()\n if (domain) opts.domain = domain\n }\n new ConsentStorage(mode).write(`${KEY_PREFIX}${visitorId}`, '1', opts)\n return visitorId\n}\n\n/** Reads the stored visitor ID without creating one — `null` if no consent decision has\n * happened yet. For callers (like `getVisitor()`) that must never mint an identifier as a\n * side effect of merely being asked about it. */\nexport function peekVisitorId(mode: StorageMode = 'cookie'): string | null {\n return findStoredVisitorId(mode)\n}\n","/**\n * Consent cookie read/write operations.\n *\n * Cookie name : `consenti_data` (fixed single name)\n * Cookie value: compact JSON with single-letter keys — see `ConsentCookieData` in `@consenti/types`.\n *\n * Signing: when a `cookieSigningKey` is configured, the encoded JSON is signed with\n * HMAC-SHA256 and the hex signature is appended as `::sig:{hex}`. On read the signature\n * is stripped before JSON-parsing and verified independently.\n *\n * Migration: on first read after upgrading from v0.x, any legacy `consenti_{userId}_{profileId}`\n * cookie is detected, parsed, and transparently migrated to the new format. The old cookie is\n * then deleted. No consent data is lost.\n *\n * Visitor identity: the `i` field (per-submission UUID) in `ConsentCookieData` identifies a\n * specific consent record. No tracking identifier is written before the user gives consent\n * (TTDSG §25 / ePrivacy compliance).\n */\n\nimport type { ConsentValue, ParsedConsent, CookieOptions, ConsentCookieData } from '../types'\nimport { ConsentStorage } from '../utils/storage'\nimport { encodeConsent, decodeConsent, expandConsent, compressConsent } from '../utils/cookie'\nimport { logger } from '../utils/console'\nimport { generatePrefixedId } from '../utils/uuid'\n\n/** Converts the profile-wide `expiryDays` into a cookie `maxAge` in seconds. */\nexport function resolveConsentMaxAge(expiryDays: number): number {\n return expiryDays * 24 * 60 * 60\n}\n\n/** Fixed cookie name for the compact consent format. */\nexport const CONSENT_COOKIE_NAME = 'consenti_data'\n\n/**\n * Peeks at the locale recorded with an existing consent record, without needing a profile ID\n * (the cookie/localStorage key is fixed regardless of profile). Used to resolve a returning,\n * already-consented visitor's locale before the profile — and hence a full `ConsentStore`\n * instance — is known. Returns `null` if no consent exists or no locale was recorded.\n */\nexport function peekConsentLocale(storageMode: 'cookie' | 'localStorage' = 'cookie', cookieName: string = CONSENT_COOKIE_NAME): string | null {\n const storage = new ConsentStorage(storageMode)\n const raw = storage.read(cookieName)\n if (!raw) return null\n return decodeConsent(raw)?.l || null\n}\n\n/**\n * Peeks at the compliance group recorded with an existing consent record, without needing a\n * profile ID or a resolved profile yet — same \"know the answer before the profile is resolved\"\n * problem `peekConsentLocale` solves for locale. Lets `scenario2A` skip the `/resolve-profile`\n * geo round-trip for a returning, already-consented visitor. Returns `null` if no consent exists\n * or no group was recorded (e.g. a pre-existing cookie from before this field existed).\n */\nexport function peekConsentGroup(storageMode: 'cookie' | 'localStorage' = 'cookie', cookieName: string = CONSENT_COOKIE_NAME): string | null {\n const storage = new ConsentStorage(storageMode)\n const raw = storage.read(cookieName)\n if (!raw) return null\n return decodeConsent(raw)?.k || null\n}\n\nconst LEGACY_PREFIX = 'consenti'\n\n// ─── Legacy parser (migration only) ──────────────────────────────────────────\n\n/** Parses the old `t:{epoch}::{pairs}` cookie value format. Used only during migration. */\nfunction parseLegacyCookieValue(value: string): ParsedConsent | null {\n const parts = value.split('::')\n if (parts.length < 2) return null\n\n const tsPart = parts[0] ?? ''\n let timestamp: string\n\n if (tsPart.startsWith('t:')) {\n const epochStr = tsPart.slice(2)\n const epoch = parseInt(epochStr, 10)\n if (!epochStr || isNaN(epoch)) return null\n timestamp = new Date(epoch * 1000).toISOString()\n } else if (tsPart.startsWith('ConsentTimestamp:')) {\n timestamp = tsPart.replace('ConsentTimestamp:', '')\n if (!timestamp) return null\n } else {\n return null\n }\n\n const consentPart = parts[1] ?? ''\n const sigPart = parts[2]\n const consent: ConsentValue = {}\n for (const pair of consentPart.split('|')) {\n const colonIdx = pair.indexOf(':')\n if (colonIdx === -1) return null\n const id = pair.slice(0, colonIdx)\n const status = pair.slice(colonIdx + 1)\n if (!id || (status !== 'granted' && status !== 'denied' && status !== 'objected')) return null\n consent[id] = status\n }\n\n const result: ParsedConsent = { timestamp, consent }\n if (sigPart?.startsWith('sig:')) result.signature = sigPart.slice(4)\n return result\n}\n\n// ─── ConsentStore ─────────────────────────────────────────────────────────────\n\ntype WriteMeta = { source?: number; gpcDetected?: boolean; consentId?: string; locale?: string; complianceGroup?: string | undefined }\n\n/**\n * Manages reading and writing the `consenti_data` consent cookie for a single visitor.\n *\n * One `ConsentStore` instance is created per `ConsentiSetup` and lives for the\n * lifetime of the widget. An in-memory cache (`cachedData`) prevents repeat cookie\n * reads on the hot path (e.g. `hasConsent()` called in render loops).\n */\nexport class ConsentStore {\n private storage: ConsentStorage\n private cachedData: ConsentCookieData | null = null\n private expiryDays = 365\n /** 0 = unknown (embedded/local profile with no server-side version counter). */\n private profileVersion = 0\n\n /**\n * @param profileId - profile ID.\n * @param storageMode - `'cookie'` (default) or `'localStorage'`.\n * @param cookieDomains - Comma-separated domain list; first entry is used as `Domain` attribute.\n * @param appUserId - Logged-in application user ID; empty string / undefined for anonymous.\n * @param cookieName - Cookie/localStorage key name. Default: `CONSENT_COOKIE_NAME` (`'consenti_data'`).\n */\n constructor(\n private profileId: string,\n private storageMode: 'cookie' | 'localStorage',\n private cookieDomains?: string,\n private appUserId?: string,\n private cookieName: string = CONSENT_COOKIE_NAME,\n ) {\n this.storage = new ConsentStorage(storageMode)\n }\n\n /** Updates the profile-wide expiry (days) used to derive the consent cookie's max-age. */\n setProfileExpiry(expiryDays: number): void {\n this.expiryDays = expiryDays\n }\n\n /** Records which profile version this store is writing consent for (0 = unknown). */\n setProfileVersion(version: number): void {\n this.profileVersion = version\n }\n\n /** Returns the logged-in application user ID recorded with the stored consent (`u` field),\n * or `null` if anonymous/unset/no consent exists yet. Prefers the in-memory cache; falls back\n * to peeking the raw cookie the same way `getConsentLocale()` does. */\n getAppUserId(): string | null {\n if (this.cachedData) return this.cachedData.u || null\n const raw = this.storage.read(this.cookieName)\n if (!raw) return null\n return decodeConsent(raw)?.u || null\n }\n\n /** Updates the user ID used for the *next* cookie write — does not touch storage itself. */\n setAppUserId(id?: string): void {\n this.appUserId = id\n }\n\n /** Returns the configured cookie/localStorage key name for all consent records\n * (`CONSENT_COOKIE_NAME` / `'consenti_data'` unless `core.cookieName` overrides it). */\n getCookieName(): string {\n return this.cookieName\n }\n\n /**\n * Reads and parses the stored consent record.\n *\n * If `consenti_data` is absent but a legacy `consenti_{userId}_{profileId}` cookie\n * exists for this profile, it is automatically migrated to the new format.\n *\n * Updates the in-memory cache on success.\n */\n read(): ParsedConsent | null {\n const raw = this.storage.read(this.cookieName)\n if (raw) {\n const data = decodeConsent(raw)\n if (data) {\n this.cachedData = data\n return this.toParseConsent(data)\n }\n }\n\n const migrated = this.migrateLegacyCookie()\n if (migrated) {\n this.cachedData = migrated\n return this.toParseConsent(migrated)\n }\n\n return null\n }\n\n /**\n * Reads, parses, and cryptographically verifies the stored consent record.\n *\n * If the cookie value has a `::sig:{hmac}` suffix, the HMAC-SHA256 signature is verified\n * against the JSON body. A mismatch means tampering — the cookie is deleted and `null`\n * is returned so the widget re-prompts. Cookies without a signature are accepted as-is\n * (backwards compatible with cookies written before signing was enabled).\n *\n * @param signingKey - HMAC key (from profile API response or `core.cookieSigningKey`).\n */\n async readAndVerify(signingKey: string): Promise<ParsedConsent | null> {\n const raw = this.storage.read(this.cookieName)\n if (!raw) {\n const migrated = this.migrateLegacyCookie()\n if (migrated) {\n this.cachedData = migrated\n return this.toParseConsent(migrated)\n }\n return null\n }\n\n const sigIdx = raw.lastIndexOf('::sig:')\n const jsonStr = sigIdx !== -1 ? raw.slice(0, sigIdx) : raw\n const sigStr = sigIdx !== -1 ? raw.slice(sigIdx + 6) : undefined\n\n const data = decodeConsent(raw)\n if (!data) return null\n\n if (sigStr) {\n const { verifyValue } = await import('../utils/hmac')\n const valid = await verifyValue(jsonStr, sigStr, signingKey)\n if (!valid) {\n logger.warn('Cookie signature mismatch — consent cleared and banner will re-appear')\n this.delete()\n return null\n }\n }\n\n this.cachedData = data\n const parsed = this.toParseConsent(data)\n return sigStr ? { ...parsed, signature: sigStr } : parsed\n }\n\n /**\n * Writes a consent record to storage.\n *\n * @param consent - The consent map to persist.\n * @param meta - Optional metadata: `source` (0=click, 1=widget method), `gpcDetected`, `consentId`.\n * @param signature - Optional pre-computed HMAC hex. Use `writeAndSign()` to sign atomically.\n */\n write(consent: ConsentValue, meta: WriteMeta = {}, signature?: string): void {\n const data = this.buildCookieData(consent, meta)\n let encoded = encodeConsent(data)\n if (signature) encoded = `${encoded}::sig:${signature}`\n this.storage.write(this.cookieName, encoded, this.buildCookieOpts())\n this.cachedData = data\n }\n\n /**\n * Builds the cookie value, signs it with HMAC-SHA256, and writes it atomically.\n *\n * Using this instead of `write(consent, meta, signature)` ensures the signature covers\n * exactly the value that is written (no timestamp drift between building and signing).\n *\n * @param consent - The consent map to persist.\n * @param signingKey - HMAC key string.\n * @param meta - Optional metadata (same as `write()`).\n * @returns The hex-encoded signature.\n */\n async writeAndSign(consent: ConsentValue, signingKey: string, meta: WriteMeta = {}): Promise<string> {\n const { signValue } = await import('../utils/hmac')\n const data = this.buildCookieData(consent, meta)\n const encoded = encodeConsent(data)\n const sig = await signValue(encoded, signingKey)\n this.storage.write(this.cookieName, `${encoded}::sig:${sig}`, this.buildCookieOpts())\n this.cachedData = data\n return sig\n }\n\n /** Removes the consent record from storage and clears the in-memory cache. */\n delete(): void {\n const opts = this.buildCookieOpts()\n this.storage.remove(this.cookieName, opts)\n this.deleteLegacyCookies()\n this.cachedData = null\n }\n\n /** Returns `true` if a valid consent record exists (uses cache when available). */\n hasConsent(): boolean {\n if (this.cachedData) return true\n return this.read() !== null\n }\n\n /** Returns the stored consent map, or `null` if no consent has been given. */\n getConsent(): ConsentValue | null {\n if (this.cachedData) return expandConsent(this.cachedData)\n return this.read()?.consent ?? null\n }\n\n /** Returns the `Date` of the last consent submission, or `false` if no consent exists. */\n getConsentDate(): Date | false {\n if (this.cachedData) return new Date(this.cachedData.t * 1000)\n const parsed = this.read()\n if (!parsed) return false\n return new Date(parsed.timestamp)\n }\n\n /** Returns the per-submission consent UUID (`i` field), or `null` if no consent exists. */\n getConsentId(): string | null {\n if (this.cachedData) return this.cachedData.i\n const raw = this.storage.read(this.cookieName)\n if (!raw) return null\n return decodeConsent(raw)?.i ?? null\n }\n\n /** Returns the locale recorded with the stored consent (`l` field), or `null` if unset/no consent exists. */\n getConsentLocale(): string | null {\n if (this.cachedData) return this.cachedData.l || null\n const raw = this.storage.read(this.cookieName)\n if (!raw) return null\n return decodeConsent(raw)?.l || null\n }\n\n /**\n * Updates the in-memory cache without writing to storage.\n * Used by the cross-tab BroadcastChannel handler to keep other tabs in sync.\n */\n setInMemory(consent: ConsentValue): void {\n this.cachedData = {\n s: this.profileId,\n v: this.profileVersion,\n i: this.cachedData?.i ?? generatePrefixedId('cons'),\n t: Math.round(Date.now() / 1000),\n u: this.appUserId ?? '',\n g: 0,\n p: 0,\n c: compressConsent(consent),\n l: this.cachedData?.l ?? '',\n k: this.cachedData?.k ?? '',\n }\n }\n\n /** Clears the in-memory cache. Used when consent is deleted from another tab. */\n clearInMemory(): void {\n this.cachedData = null\n }\n\n // ─── Private helpers ───────────────────────────────────────────────────────\n\n private toParseConsent(data: ConsentCookieData): ParsedConsent {\n return {\n timestamp: new Date(data.t * 1000).toISOString(),\n consent: expandConsent(data),\n }\n }\n\n private buildCookieData(consent: ConsentValue, meta: WriteMeta = {}): ConsentCookieData {\n return {\n s: this.profileId,\n v: this.profileVersion,\n i: meta.consentId ?? generatePrefixedId('cons'),\n t: Math.round(Date.now() / 1000),\n u: this.appUserId ?? '',\n g: meta.gpcDetected ? 1 : 0,\n p: meta.source ?? 0,\n c: compressConsent(consent),\n l: meta.locale ?? '',\n k: meta.complianceGroup ?? '',\n }\n }\n\n private migrateLegacyCookie(): ConsentCookieData | null {\n if (typeof document === 'undefined') return null\n\n const suffix = `_${this.profileId}`\n const allNames: string[] = this.storageMode === 'localStorage'\n ? (typeof localStorage !== 'undefined' ? Object.keys(localStorage) : [])\n : document.cookie.split(';').map(c => c.split('=')[0]?.trim() ?? '')\n\n for (const name of allNames) {\n if (!name.startsWith(`${LEGACY_PREFIX}_`) || !name.endsWith(suffix)) continue\n\n const raw = this.storage.read(name)\n if (!raw) continue\n const parsed = parseLegacyCookieValue(raw)\n if (!parsed) continue\n\n const inner = name.slice(LEGACY_PREFIX.length + 1, name.length - suffix.length)\n const legacyUserId = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(inner)\n ? inner : ''\n\n const data: ConsentCookieData = {\n s: this.profileId,\n v: 0, // legacy cookie predates the version counter — genuinely unknown\n i: generatePrefixedId('cons'),\n t: Math.round(new Date(parsed.timestamp).getTime() / 1000),\n u: legacyUserId,\n g: 0,\n p: 0,\n c: compressConsent(parsed.consent),\n l: '', // legacy format predates locale tracking — genuinely unknown\n k: '', // legacy format predates compliance-group tracking — genuinely unknown\n }\n\n this.storage.write(this.cookieName, encodeConsent(data), this.buildCookieOpts())\n this.storage.remove(name, this.buildCookieOpts())\n logger.info(`Migrated consent cookie from \"${name}\" to \"${this.cookieName}\"`)\n return data\n }\n return null\n }\n\n private deleteLegacyCookies(): void {\n if (typeof document === 'undefined' || this.storageMode === 'localStorage') return\n const suffix = `_${this.profileId}`\n const opts = this.buildCookieOpts()\n for (const c of document.cookie.split(';')) {\n const name = c.split('=')[0]?.trim() ?? ''\n if (name.startsWith(`${LEGACY_PREFIX}_`) && name.endsWith(suffix)) {\n this.storage.remove(name, opts)\n }\n }\n }\n\n private buildCookieOpts(): CookieOptions {\n const opts: CookieOptions = {\n path: '/',\n sameSite: 'Lax',\n maxAge: resolveConsentMaxAge(this.expiryDays),\n }\n if (this.cookieDomains) {\n const domain = this.cookieDomains.split(',')[0]?.trim()\n if (domain) opts.domain = domain\n }\n return opts\n }\n}\n","/**\n * Custom DOM event dispatcher with optional Google Tag Manager / Google Consent Mode v2\n * integration.\n *\n * Every user-visible action in the widget (banner shown, consent saved, modal opened)\n * dispatches a `CustomEvent` on `window` so host applications can react without\n * coupling to Consenti internals.\n *\n * When `config.utils.gtm` is configured, this also drives real Google Consent Mode v2\n * signalling: a `gtag('consent', 'default', …)` call as soon as the widget initializes\n * (before any tag can fire), and `gtag('consent', 'update', …)` on every consent\n * submission — using the standard `gtag` stub-queue pattern, so it works whether the\n * site's own gtag.js/GTM snippet loads before or after Consenti. Set `gtm.verbose: true`\n * to additionally mirror every `consenti:*` event onto the dataLayer as a generic\n * `{ event, content }` push (for custom, non-Consent-Mode GTM triggers).\n *\n * Event names all use the `consenti:` namespace prefix to avoid collisions.\n */\n\nimport type { ConsentValue, GtmConfig, ConsentEvent, ConsentBeingSubmitted } from '../types'\nimport { isClient, safeWindow } from '../utils/ssr'\nimport { injectScript } from '../utils/script-injector'\nimport { getGoogleGTMConsent } from '../utils/consent-mapper'\n\ntype Gtag = (...args: unknown[]) => void\n\n/** All event names dispatched by the widget. */\nexport type EventName =\n | 'consenti:bannerInitialized'\n | 'consenti:bannerVisibility'\n | 'consenti:modalVisibility'\n | 'consenti:consentBeingSubmitted'\n | 'consenti:consentSubmitted'\n | 'consenti:closeRequest'\n | 'consenti:parentalConsentRequired'\n | 'consenti:parentalConsentResolved'\n | 'consenti:forgetMeRequested'\n | 'consenti:forgotten'\n\n/**\n * Dispatches `CustomEvent` on `window` and optionally pushes to the GTM dataLayer.\n *\n * One `EventBus` instance is created per `ConsentiSetup` and shared across the\n * banner, modal, and consent submission flows.\n */\nexport class EventBus {\n /**\n * @param profileId - Profile ID included in all GTM pushes.\n * @param gtmConfig - Optional GTM integration settings.\n */\n constructor(\n private profileId: string,\n private gtmConfig?: GtmConfig,\n ) {\n if (this.gtmConfig) this.initConsentMode()\n }\n\n /**\n * Sets up Google Consent Mode v2 as soon as the widget initializes — before any tag can\n * fire — following Google's documented pattern: ensure `dataLayer` + a `gtag` stub exist\n * (queuing calls even if the real gtag.js/GTM library hasn't loaded yet), then push the\n * default (denied-by-default) consent state immediately. If `containerId` is set, also\n * injects the GTM library itself so callers don't need a separate GTM snippet.\n */\n private initConsentMode(): void {\n if (!isClient()) return\n const win = safeWindow() as (Window & { [key: string]: unknown }) | null\n if (!win) return\n const gtm = this.gtmConfig!\n const layerName = gtm.dataLayer ?? 'dataLayer'\n\n if (!Array.isArray(win[layerName])) win[layerName] = []\n if (typeof win['gtag'] !== 'function') {\n // Standard Google gtag stub: pushes its arguments onto the dataLayer array so\n // consent commands queue correctly regardless of when gtag.js/GTM actually loads.\n win['gtag'] = ((...args: unknown[]) => { (win[layerName] as unknown[]).push(args) }) satisfies Gtag\n }\n const gtag = win['gtag'] as Gtag\n\n // Consent must be set to a default state before any tag fires. getGoogleGTMConsent({})\n // resolves the library's own safe defaults (denied except functionality/security storage).\n gtag('consent', 'default', getGoogleGTMConsent({}))\n\n if (gtm.containerId) {\n const params = layerName !== 'dataLayer' ? `&l=${encodeURIComponent(layerName)}` : ''\n injectScript({\n src: `https://www.googletagmanager.com/gtm.js?id=${encodeURIComponent(gtm.containerId)}${params}`,\n warnPrefix: 'GTM',\n })\n }\n }\n\n /**\n * Dispatches a `consenti:*` custom event on `window` and, if GTM is configured,\n * pushes the event to `window.dataLayer`.\n *\n * Silently no-ops in SSR contexts.\n *\n * @param name - The event name.\n * @param detail - Event payload attached to `event.detail` and included in the GTM push.\n */\n dispatch(name: EventName, detail: ConsentEvent): void {\n if (!isClient()) return\n const event = new CustomEvent(name, { detail, bubbles: true })\n window.dispatchEvent(event)\n this.pushToGTM(name, detail)\n }\n\n private pushToGTM(name: string, detail: ConsentEvent): void {\n const gtm = this.gtmConfig\n if (!gtm) return\n\n const win = safeWindow() as (Window & { [key: string]: unknown }) | null\n if (!win) return\n\n const layerName = gtm.dataLayer ?? 'dataLayer'\n const layer = win[layerName]\n if (!Array.isArray(layer)) return\n\n // Generic per-event mirroring is opt-in (`verbose: true`) — the primary purpose of `gtm`\n // config is real Consent Mode signalling below, which always runs regardless of verbose.\n // `events`, when set, narrows which named events get mirrored under verbose mode.\n if (gtm.verbose && (!gtm.events || gtm.events.length === 0 || gtm.events.includes(name))) {\n layer.push({ event: name, content: detail })\n }\n\n if (name === 'consenti:consentSubmitted') {\n const consentJson = (detail as ConsentBeingSubmitted).consentJson ?? {}\n const gtag = (typeof win['gtag'] === 'function' ? win['gtag'] : (...args: unknown[]) => layer.push(args)) as Gtag\n\n gtag('consent', 'update', getGoogleGTMConsent(consentJson))\n\n if (gtm.urlPassthrough) gtag('set', 'url_passthrough', true)\n if (gtm.adsDataRedaction) {\n // ads_data_redaction tells Google to strip identifying data from ad pings when the\n // user has denied ad_storage.\n gtag('set', 'ads_data_redaction', consentJson['ad_storage'] !== 'granted')\n }\n }\n }\n\n}\n\n// Re-export so callers do not need a separate import from types\nexport type { ConsentValue }\n","import { DeepPartial } from '@consenti/types';\n\n/**\n * Locale resolution and deep-merge utilities.\n *\n * Consenti resolves translations in three steps:\n * 1. Exact match — `'fr-CA'` → use `translations['fr-CA']`\n * 2. Language prefix — `'fr-CA'` → try `translations['fr']` if `'fr-CA'` is absent\n * 3. Default locale — fall back to `translations[defaultLocale]`\n *\n * Missing keys in the resolved locale are filled from the default locale via `deepMerge`,\n * so partial translations are safe — only the keys that differ need to be specified.\n */\n\nimport type { ProfileTranslations, LocaleTranslations } from '../types'\n\n/**\n * Resolves the best-matching `LocaleTranslations` for a requested locale.\n *\n * The result is a deep merge of the default locale (base) and the resolved locale\n * (override), so any missing keys in the override are inherited from the base.\n *\n * @param translations - All available locale translations from a `ProfileConfig`.\n * @param requested - The desired locale (e.g. `'fr-CA'`).\n * @param defaultLocale - The fallback locale key (e.g. `'en'`). Must exist in `translations`.\n * @throws `Error` if `defaultLocale` is not found in `translations`.\n */\nexport function resolveLocale(\n translations: ProfileTranslations,\n requested: string,\n defaultLocale: string,\n): LocaleTranslations {\n const base = translations[defaultLocale]\n if (!base) {\n throw new Error(`[Consenti] No translations found for defaultLocale \"${defaultLocale}\"`)\n }\n\n // 1. Exact match\n if (translations[requested]) {\n return deepMerge(base, translations[requested])\n }\n\n // 2. Language prefix (e.g. 'fr' from 'fr-CA')\n const lang = requested.split('-')[0] ?? ''\n if (lang !== requested && translations[lang]) {\n return deepMerge(base, translations[lang])\n }\n\n // 3. Default locale fallback\n return base\n}\n\n/**\n * Recursively merges `override` into `base`, returning a new object.\n *\n * Rules:\n * - `undefined` values in `override` are skipped (base values are kept).\n * - `null` values in `override` delete the corresponding key from the merged\n * result (JSON Merge Patch / RFC 7396 semantics) — e.g. `{ marketing: null }`\n * removes the `marketing` entry from a `CategoryMap`/`CookieMap`.\n * - Arrays are merged by index — individual array items are merged, not replaced wholesale.\n * - Plain objects are merged recursively.\n * - All other values are replaced by the override value.\n *\n * @param base - The base object (default locale translations).\n * @param override - The partial override (requested locale translations).\n */\n/**\n * Returns the best locale match from `availableLocales` for the given browser language list.\n * Uses the same 3-step algorithm as `resolveLocale`: exact → language-prefix → undefined.\n */\nexport function detectUserLocale(\n browserLanguages: readonly string[],\n availableLocales: string[],\n): string | undefined {\n for (const lang of browserLanguages) {\n if (availableLocales.includes(lang)) return lang\n const prefix = lang.split('-')[0] ?? ''\n const prefixMatch = availableLocales.find(l => l === prefix || l.startsWith(`${prefix}-`))\n if (prefixMatch) return prefixMatch\n }\n return undefined\n}\n\ntype Primitive = string | number | boolean | bigint | symbol | null | undefined\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value)\n )\n}\n\nfunction mergeValue(base: unknown, override: unknown): unknown {\n if (override === undefined) {\n return base\n }\n\n // JSON Merge Patch (RFC 7396): an explicit `null` deletes the key from the\n // parent object — handled in the object-merge branch below, where the key\n // can actually be removed from the result. Reaching here means `null` was\n // passed as a top-level/array value with no parent key to delete, so it's\n // treated as a plain override value.\n if (override === null) {\n return base\n }\n\n // Merge arrays by index\n if (Array.isArray(base) && Array.isArray(override)) {\n const length = Math.max(base.length, override.length)\n\n return Array.from({ length }, (_, i) => {\n if (i >= override.length) return base[i]\n if (i >= base.length) return override[i]\n\n return mergeValue(base[i], override[i])\n })\n }\n\n // Merge plain objects\n if (isPlainObject(base) && isPlainObject(override)) {\n const result: Record<string, unknown> = { ...base }\n\n for (const key of Object.keys(override)) {\n const overrideValue = override[key]\n if (overrideValue === null) {\n delete result[key]\n continue\n }\n result[key] = mergeValue(base[key], overrideValue)\n }\n\n return result\n }\n\n // Primitive or incompatible types -> override wins\n return override\n}\n\nexport function deepMerge<T>(base: T, override: DeepPartial<T>): T {\n return mergeValue(base, override) as T\n}","export function getBrowserGeoHints(): { tz: string; lang: string; langs: string[] } {\n return {\n tz: Intl.DateTimeFormat().resolvedOptions().timeZone,\n lang: navigator.language,\n langs: Array.from(navigator.languages),\n }\n}\n\n/** Returns a base64-encoded JSON payload of browser geo/locale hints for the `/resolve-profile` API. */\nexport function encodeGeoHints(locale?: string): string {\n const opts = Intl.DateTimeFormat().resolvedOptions()\n return btoa(JSON.stringify({\n timezone: opts.timeZone,\n languages: Array.from(navigator.languages ?? []),\n language: navigator.language ?? '',\n locale: locale ?? opts.locale ?? navigator.language ?? '',\n }))\n}\n","import type { ResolvedProfile, CookieMap, MainBanner, PreferenceModal, CategoryMap, Button } from '../types'\nimport type { EmbeddedProfile, EmbeddedCookie, EmbeddedBanner, EmbeddedModal, EmbeddedCategory, EmbeddedButton, EmbeddedTranslations } from '@consenti/utils/profiles'\n\n// ─── Primitive mappers ────────────────────────────────────────────────────────\n\nfunction mapCookie(c: EmbeddedCookie): CookieMap[string] {\n return {\n purpose: c.purpose,\n ...(c.listenGpc ? { listenGpc: true } : {}),\n ...(c.preGrant ? { preGrant: true } : {}),\n ...(c.cpraCategory ? { cpraCategory: c.cpraCategory } : {}),\n }\n}\n\nfunction mapCookies(cookies: Record<string, EmbeddedCookie>): CookieMap {\n const result: CookieMap = {}\n for (const [id, c] of Object.entries(cookies)) result[id] = mapCookie(c)\n return result\n}\n\nfunction mapButton(b: EmbeddedButton): Button {\n return {\n text: b.text,\n style: b.style,\n action: b.action,\n ...(b.cookies !== undefined ? { cookies: b.cookies } : {}),\n }\n}\n\nfunction mapButtons(buttons: Record<string, EmbeddedButton>): Record<string, Button> {\n const result: Record<string, Button> = {}\n for (const [id, b] of Object.entries(buttons)) result[id] = mapButton(b)\n return result\n}\n\nfunction mapCategory(cat: EmbeddedCategory): CategoryMap[string] {\n return {\n heading: cat.heading,\n htmlText: cat.htmlText,\n legalBasis: cat.legalBasis,\n ...(cat.legitimateInterestDescription !== undefined ? { legitimateInterestDescription: cat.legitimateInterestDescription } : {}),\n cookies: cat.cookies,\n }\n}\n\nfunction mapCategories(categories: Record<string, EmbeddedCategory>): CategoryMap {\n const result: CategoryMap = {}\n for (const [id, cat] of Object.entries(categories)) result[id] = mapCategory(cat)\n return result\n}\n\nfunction mapBanner(b: EmbeddedBanner): MainBanner {\n return {\n position: b.position,\n htmlText: b.htmlText,\n buttons: mapButtons(b.buttons),\n ...(b.heading ? { heading: b.heading } : {}),\n ...(b.showClose !== undefined ? { showClose: b.showClose } : {}),\n ...(b.showLocaleSwitcher !== undefined ? { showLocaleSwitcher: b.showLocaleSwitcher } : {}),\n }\n}\n\nfunction mapModal(m: EmbeddedModal): PreferenceModal {\n return {\n heading: m.heading,\n htmlText: m.htmlText ?? '',\n buttons: mapButtons(m.buttons),\n categories: mapCategories(m.categories),\n ...(m.position ? { position: m.position } : {}),\n ...(m.subheading ? { subheading: m.subheading } : {}),\n ...(m.persistent !== undefined ? { persistent: m.persistent } : {}),\n }\n}\n\n// ─── Locale resolution ────────────────────────────────────────────────────────\n\nfunction pickTranslations(\n translations: Record<string, EmbeddedTranslations>,\n locale: string | undefined,\n defaultLocale: string,\n): EmbeddedTranslations {\n if (locale) {\n if (translations[locale]) return translations[locale]!\n const prefix = locale.split('-')[0] ?? ''\n const prefixMatch = Object.keys(translations).find(k => k === prefix || k.startsWith(`${prefix}-`))\n if (prefixMatch) return translations[prefixMatch]!\n }\n return translations[defaultLocale] ?? Object.values(translations)[0]!\n}\n\n// ─── Public adapter ───────────────────────────────────────────────────────────\n\n/**\n * Converts an `EmbeddedProfile` (from `@consenti/utils/profiles`) to a\n * `ResolvedProfile` suitable for direct use by the widget renderer.\n */\nexport function adaptEmbeddedProfile(\n embedded: EmbeddedProfile,\n locale?: string,\n): ResolvedProfile {\n const tx = pickTranslations(embedded.translations, locale, embedded.defaultLocale)\n return {\n id: \"0\",\n defaultLocale: embedded.defaultLocale,\n locales: Object.keys(embedded.translations),\n cookies: mapCookies(embedded.cookies),\n ...(embedded.expiryDays ? { expiryDays: embedded.expiryDays } : {}),\n mainBanner: mapBanner(tx.mainBanner),\n preferenceModal: mapModal(tx.preferenceModal),\n gpcMode: embedded.gpcMode,\n complianceGroup: embedded.complianceGroup,\n ...(tx.gpcBanner ? { gpcBanner: mapBanner(tx.gpcBanner) } : {}),\n ...(embedded.ageGate ? { ageGate: embedded.ageGate } : {}),\n ...(tx.ageGateModal ? { ageGateModal: tx.ageGateModal } : {}),\n }\n}\n","import type { ConsentiConfig, ResolvedProfile, ProfileConfig, RegisterableProfileConfig, PublicProfileResponse, GpcMode, ComplianceType, ComplianceGroupId, MainBanner, GpcBanner, PreferenceModal, AgeGateModalContent, DeepPartial } from '../types'\nimport { httpRequest } from '../utils/http'\nimport { deepMerge, resolveLocale } from '../utils/locale'\nimport { logger } from '../utils/console'\nimport { getBrowserGeoHints, encodeGeoHints } from '../utils/geo-hints'\nimport { EMBEDDED_COMPLIANCE_MAP, resolveComplianceMapOverride, type ComplianceMapData } from '@consenti/utils'\nimport { adaptEmbeddedProfile } from '../utils/profile-adapter'\nimport { peekConsentLocale, peekConsentGroup } from './consent-store'\n\n// ─── Local profile registry ───────────────────────────────────────────────────\n\nconst profileRegistry = new Map<Symbol, RegisterableProfileConfig>()\n\nexport function registerProfile(type: Symbol, config: RegisterableProfileConfig): void {\n profileRegistry.set(type, config)\n}\n\nexport function getRegisteredProfile(type: Symbol): RegisterableProfileConfig | undefined {\n return profileRegistry.get(type)\n}\n\n/** First registered profile whose `complianceGroup` matches — used to prefer an author's custom profile over the built-in embedded one for that group. */\nfunction findRegisteredProfileForGroup(group: ComplianceGroupId): RegisterableProfileConfig | undefined {\n for (const config of profileRegistry.values()) {\n if (config.complianceGroup === group) return config\n }\n return undefined\n}\n\n// ─── mapPublicProfileToResolved ───────────────────────────────────────────────\n\nexport function mapPublicProfileToResolved(resp: PublicProfileResponse): ResolvedProfile {\n // resp comes from an HTTP response, so it may still carry the legacy boolean gpcMode\n // shape even though the static type says GpcMode — convert defensively.\n const rawGpcMode = resp.gpcMode as GpcMode | boolean | undefined\n let gpcMode: GpcMode = 'ignore'\n if (typeof rawGpcMode === 'boolean') {\n gpcMode = rawGpcMode ? 'honor' : 'ignore'\n } else if (rawGpcMode) {\n gpcMode = rawGpcMode\n }\n\n return {\n id: resp.id,\n ...(resp.version !== undefined ? { version: resp.version } : {}),\n defaultLocale: resp.defaultLocale,\n locales: resp.locales,\n cookies: resp.cookies,\n mainBanner: resp.mainBanner,\n allowReceipt: resp.allowReceipt ?? false,\n preferenceModal: resp.preferenceModal,\n ...(resp.expiryDays ? { expiryDays: resp.expiryDays } : {}),\n ...(resp.gpcBanner ? { gpcBanner: resp.gpcBanner } : {}),\n ...(gpcMode ? { gpcMode } : {}),\n ...(resp.darkMode !== undefined ? { darkMode: resp.darkMode } : {}),\n // `resolvedComplianceGroup` (set by the geo-resolve routes) carries the visitor's actual\n // resolved group — including custom groups, which `complianceGroup`'s strict typing doesn't\n // guarantee. Prefer it; callers that already know the group from their own request path\n // (scenario2A/2B) additionally overwrite this after the call, so this is the fallback.\n ...(resp.resolvedComplianceGroup ?? resp.complianceGroup\n ? { complianceGroup: resp.resolvedComplianceGroup ?? resp.complianceGroup }\n : {}),\n ...(resp.complianceConfig ? { complianceConfig: resp.complianceConfig } : {}),\n ...(resp.dpdpa ? { dpdpa: resp.dpdpa } : {}),\n ...(resp.ageGate ? { ageGate: resp.ageGate } : {}),\n ...(resp.ageGateModal ? { ageGateModal: resp.ageGateModal } : {}),\n ...(resp.hidePoweredBy !== undefined ? { hidePoweredBy: resp.hidePoweredBy } : {}),\n ...(resp.showFooterMetadata !== undefined ? { showFooterMetadata: resp.showFooterMetadata } : {}),\n ...(resp.enhanceAccessibility !== undefined ? { enhanceAccessibility: resp.enhanceAccessibility } : {}),\n }\n}\n\n// ─── sessionStorage cache for /resolve-profile ───────────────────────────────\n\nconst CACHE_KEY_PREFIX = '__consenti_resolved_'\nconst CACHE_TTL_MS = 60 * 60 * 1000\n\ninterface ResolvedUrlCache {\n filePath: string\n version: number\n complianceGroup: string\n tenantId: string\n locale: string\n expiresAt: number\n /** Carried through from `/resolve-profile` so a cache hit doesn't lose the per-region\n * sensitive-data opt-in carve-out that a fresh geo-resolve would otherwise recompute. */\n requiresSensitiveOptIn?: boolean\n}\n\nexport function getCachedResolution(\n tenantId: string,\n locale: string,\n cacheEnabled: boolean,\n): ResolvedUrlCache | null {\n if (!cacheEnabled) return null\n const key = `${CACHE_KEY_PREFIX}${tenantId}_${locale}`\n try {\n const raw = sessionStorage.getItem(key)\n if (!raw) return null\n const cached = JSON.parse(raw) as ResolvedUrlCache\n if (Date.now() > cached.expiresAt) { sessionStorage.removeItem(key); return null }\n return cached\n } catch { return null }\n}\n\nexport function setCachedResolution(\n tenantId: string,\n locale: string,\n cacheEnabled: boolean,\n data: Omit<ResolvedUrlCache, 'expiresAt'>,\n): void {\n if (!cacheEnabled) return\n const key = `${CACHE_KEY_PREFIX}${tenantId}_${locale}`\n try {\n sessionStorage.setItem(key, JSON.stringify({ ...data, expiresAt: Date.now() + CACHE_TTL_MS }))\n } catch { /* ignore storage failures */ }\n}\n\n// ─── Domain allowlist check ───────────────────────────────────────────────────\n\nfunction isDomainAllowed(profile: PublicProfileResponse, trustDomain: boolean): boolean {\n if (trustDomain) return true\n const origins = profile.allowedOrigins\n if (!origins || origins.length === 0) return true\n const current = window.location.origin\n return origins.some(o => o === current || o === '*')\n}\n\n// ─── Pre-built profile loader ─────────────────────────────────────────────────\n\nasync function loadPrebuiltProfile(complianceGroup: ComplianceGroupId, _locale?: string): Promise<ResolvedProfile | null> {\n const { DEFAULT_PROFILES } = await import('@consenti/utils/profiles')\n const embedded = DEFAULT_PROFILES[complianceGroup as keyof typeof DEFAULT_PROFILES]\n if (!embedded) return null\n // Deliberately pinned to English — @consenti/utils/profiles now carries de/es/fr/ja overlays\n // for apps/api (seeding + dashboard \"Load Defaults\"), but the widget's embedded fallback\n // stays English-only until multi-locale prebuilt profiles are explicitly decided on.\n return adaptEmbeddedProfile(embedded, 'en')\n}\n\nfunction isPrebuiltAllowed(complianceGroup: ComplianceGroupId, config: ConsentiConfig): boolean {\n const setting = config.core?.usePrebuiltProfiles ?? 'all'\n if (setting === 'all') return true\n return (setting as string[]).includes(complianceGroup)\n}\n\n// ─── Geo hint → complianceGroup ───────────────────────────────────────────────\n//\n// Timezone is the primary signal: `Intl.DateTimeFormat().resolvedOptions().timeZone`\n// is set by the OS/device and can't be spoofed by a browser locale setting, so a\n// timezone → country match (via `EMBEDDED_COMPLIANCE_MAP`'s `timezones` field) is\n// trusted first. Browser languages are a weaker signal (users travel with their\n// language but not always their timezone override) and are only used to either\n// disambiguate a timezone shared by multiple countries, or as a fallback when the\n// timezone isn't recognized at all.\n\ntype CountryEntry = { default: string; timezones?: readonly string[] }\n\nlet timezoneCountryIndex: Map<string, string[]> | undefined\n\nfunction getTimezoneCountryIndex(): Map<string, string[]> {\n if (timezoneCountryIndex) return timezoneCountryIndex\n const index = new Map<string, string[]>()\n const countries = EMBEDDED_COMPLIANCE_MAP.countries as Record<string, CountryEntry>\n for (const [code, data] of Object.entries(countries)) {\n for (const tz of data.timezones ?? []) {\n const codes = index.get(tz) ?? []\n codes.push(code)\n index.set(tz, codes)\n }\n }\n timezoneCountryIndex = index\n return index\n}\n\n/** Extract a country code from a BCP47 language tag (e.g. 'de-DE' → 'DE'), if valid. */\nfunction countryFromLangTag(lang: string): string | undefined {\n const parts = lang.replace(/_/g, '-').split('-')\n const tag = parts.length >= 2 ? parts[parts.length - 1]?.toUpperCase() : undefined\n return tag && /^[A-Z]{2}$/.test(tag) && tag in EMBEDDED_COMPLIANCE_MAP.countries ? tag : undefined\n}\n\nfunction resolveCountryFromHints(tz: string, langs: readonly string[]): string | undefined {\n const tzCountries = getTimezoneCountryIndex().get(tz) ?? []\n const langCountries = langs.map(countryFromLangTag).filter((c): c is string => !!c)\n\n // Unambiguous timezone match — the strongest signal, use it directly.\n if (tzCountries.length === 1) return tzCountries[0]\n\n // Timezone shared by multiple countries (e.g. a common IANA zone) — join with\n // the browser's language region(s) to pick the right one.\n if (tzCountries.length > 1) {\n return langCountries.find(c => tzCountries.includes(c)) ?? tzCountries[0]\n }\n\n // Timezone not in our map at all — fall back to the language region.\n return langCountries[0]\n}\n\nfunction resolveGroupFromHints(tz: string, langs: readonly string[], overrideMap?: ComplianceMapData): ComplianceGroupId {\n const countryCode = resolveCountryFromHints(tz, langs)\n\n // A custom complianceMap only overrides country→group mapping — country detection itself\n // (above) always uses the embedded geo data, since operator-supplied maps don't carry the\n // timezone/locale index that detection depends on.\n if (overrideMap && countryCode) {\n const overrideEntry = overrideMap.countries[countryCode]\n if (overrideEntry) return (overrideEntry.default ?? overrideEntry.complianceGroup) as ComplianceGroupId\n }\n\n const countries = EMBEDDED_COMPLIANCE_MAP.countries as Record<string, CountryEntry>\n const entry = countryCode ? countries[countryCode] : undefined\n if (entry?.default) return entry.default as ComplianceGroupId\n\n // Last-resort continent-level fallback for timezones we couldn't map at all\n // (legacy/alias IANA zone names, Antarctic research stations, etc.)\n const tzParts = tz.split('/')\n if (tzParts[0] === 'Europe') return 'opt-in'\n if (tzParts[0] === 'America') return 'opt-out'\n\n // Default: opt-in (GDPR) — strictest, safe for any unknown jurisdiction\n return 'opt-in'\n}\n\n/**\n * Resolves a compliance group from an already-known `country`/`region` (as returned by a custom\n * `compliance.geoDataProvider` function), mirroring `GeoResolverService.resolveGroup()`\n * (`apps/api`) region-override-then-country-default precedence — deliberately kept in sync with\n * that method rather than sharing an implementation, since one is server-side (`ComplianceMap`\n * from `@consenti/utils`, always fully typed) and the other is client-standalone (also handles\n * the loosely-typed `ComplianceMapData` override shape `resolveGroupFromHints` above already\n * handles). No country at all → strictest safe default, same \"never under-comply\" rule used\n * throughout this file.\n */\nfunction resolveGroupForCountry(\n country: string | null,\n region: string | null,\n overrideMap?: ComplianceMapData,\n): ComplianceGroupId {\n if (!country) return 'opt-in'\n\n if (overrideMap) {\n const entry = overrideMap.countries[country]\n if (entry) {\n const regionGroup = region ? entry.overriddenRegions?.[region]?.complianceGroup : undefined\n return (regionGroup ?? entry.default ?? entry.complianceGroup) as ComplianceGroupId\n }\n }\n\n const countries = EMBEDDED_COMPLIANCE_MAP.countries as Record<string, {\n default: ComplianceGroupId\n complianceGroup: ComplianceGroupId\n overriddenRegions?: Record<string, { complianceGroup: ComplianceGroupId }>\n }>\n const entry = countries[country]\n if (!entry) return 'opt-in'\n const regionGroup = region ? entry.overriddenRegions?.[region]?.complianceGroup : undefined\n return regionGroup ?? entry.default ?? entry.complianceGroup\n}\n\n// ─── Scenario helpers ─────────────────────────────────────────────────────────\n\nfunction effectiveLocale(config: ConsentiConfig): string {\n const l = config.core?.locale\n if (l && l !== 'auto') return l\n // Locale isn't persisted anywhere until a consent decision exists — an undecided visitor\n // always falls through to the browser's language on every page load. A returning, already\n // -consented visitor gets back the locale recorded with that decision.\n const stored = peekConsentLocale(config.core?.storage ?? 'cookie', config.core?.cookieName)\n if (stored) return stored\n return navigator.language\n}\n\nfunction effectiveTenantId(config: ConsentiConfig): string {\n return config.api?.tenantId ?? config.core?.tenantId ?? 'default'\n}\n\nasync function fetchProfileJson(\n url: string,\n authToken: string | undefined,\n): Promise<PublicProfileResponse | null> {\n try {\n return await httpRequest<PublicProfileResponse>(url, {}, authToken)\n } catch {\n return null\n }\n}\n\n/**\n * Strips the `RegisterableProfileConfig`-only discriminant/authoring fields\n * (`deepMerge`, `translations`, `cookieTemplateId`, `uiTemplateId`, `localeContents`,\n * `regulation`, `regulations`) and returns whatever's left as a `DeepPartial<ResolvedProfile>`\n * overlay — used only for the `deepMerge: true` registry-override path, which is\n * conceptually identical to `profileOverride`: no locale/translations resolution,\n * the provided fields are used exactly as given.\n */\nfunction toDeepPartialResolvedProfile(config: RegisterableProfileConfig): DeepPartial<ResolvedProfile> {\n const c = config as Partial<ProfileConfig>\n return {\n ...(c.id !== undefined ? { id: c.id } : {}),\n ...(c.defaultLocale !== undefined ? { defaultLocale: c.defaultLocale } : {}),\n ...(c.expiryDays !== undefined ? { expiryDays: c.expiryDays } : {}),\n ...(c.cookies !== undefined ? { cookies: c.cookies } : {}),\n ...(c.mainBanner !== undefined ? { mainBanner: c.mainBanner } : {}),\n ...(c.gpcBanner !== undefined ? { gpcBanner: c.gpcBanner } : {}),\n ...(c.preferenceModal !== undefined ? { preferenceModal: c.preferenceModal } : {}),\n ...(c.darkMode !== undefined ? { darkMode: c.darkMode } : {}),\n ...(c.allowedOrigins !== undefined ? { allowedOrigins: c.allowedOrigins } : {}),\n ...(c.dpdpa !== undefined ? { dpdpa: c.dpdpa } : {}),\n ...(c.ageGate !== undefined ? { ageGate: c.ageGate } : {}),\n ...(c.ageGateModal !== undefined ? { ageGateModal: c.ageGateModal } : {}),\n ...(c.complianceGroup !== undefined ? { complianceGroup: c.complianceGroup } : {}),\n ...(c.gpcMode !== undefined ? { gpcMode: c.gpcMode } : {}),\n ...(c.hidePoweredBy !== undefined ? { hidePoweredBy: c.hidePoweredBy } : {}),\n ...(c.allowReceipt !== undefined ? { allowReceipt: c.allowReceipt } : {}),\n ...(c.complianceConfig !== undefined ? { complianceConfig: c.complianceConfig } : {}),\n ...(c.showFooterMetadata !== undefined ? { showFooterMetadata: c.showFooterMetadata } : {}),\n ...(c.enhanceAccessibility !== undefined ? { enhanceAccessibility: c.enhanceAccessibility } : {}),\n } as DeepPartial<ResolvedProfile>\n}\n\n/**\n * If an author has `registerProfile()`-registered a profile whose `complianceGroup`\n * matches, prefer it over the built-in embedded profile: `deepMerge: false` (default)\n * fully replaces the built-in (never even fetched); `deepMerge: true` patches the\n * built-in with the registered fields, reusing the same `deepMerge()` utility\n * `profileOverride`/`setProfile()` already use.\n */\nasync function resolveRegisteredOverride(complianceGroup: ComplianceGroupId, locale: string): Promise<ResolvedProfile | undefined> {\n const override = findRegisteredProfileForGroup(complianceGroup)\n if (!override) return undefined\n if (override.deepMerge) {\n const builtIn = await loadPrebuiltProfile(complianceGroup, locale)\n const partial = toDeepPartialResolvedProfile(override)\n return builtIn ? deepMerge<ResolvedProfile>(builtIn, partial) : (partial as ResolvedProfile)\n }\n return resolveLocalProfileConfig(override as ProfileConfig, locale)\n}\n\n/** Scenario 1A — api disabled, compliance.type = 'auto' (client-side geo) */\nasync function scenario1A(config: ConsentiConfig): Promise<ResolvedProfile> {\n const { map: overrideMap } = await resolveComplianceMapOverride(config.compliance?.complianceMap, (msg) => logger.warn(msg))\n const effectiveOverrideMap = overrideMap === EMBEDDED_COMPLIANCE_MAP ? undefined : overrideMap\n\n const provider = config.compliance?.geoDataProvider\n let complianceGroup: ComplianceGroupId\n\n if (typeof provider === 'function') {\n const result = await provider()\n // A provider-returned `complianceGroup` is authoritative — it skips the country/region map\n // lookup entirely (see `GeoResult.complianceGroup`'s doc comment, `@consenti/types`, for the\n // server-side mirror of this). Otherwise fall back to resolving from whatever country/region\n // the provider did return, against the same map `resolveGroupFromHints` below would use.\n complianceGroup = result.complianceGroup\n ? (result.complianceGroup as ComplianceGroupId)\n : resolveGroupForCountry(result.country, result.region, effectiveOverrideMap)\n } else {\n const { tz, lang, langs } = getBrowserGeoHints()\n complianceGroup = resolveGroupFromHints(tz, langs.length ? langs : [lang], effectiveOverrideMap)\n }\n\n const locale = effectiveLocale(config)\n\n const override = await resolveRegisteredOverride(complianceGroup, locale)\n if (override) return override\n\n if (!isPrebuiltAllowed(complianceGroup, config)) {\n throw new Error(`Pre-built profile for complianceGroup \"${complianceGroup}\" is disallowed by usePrebuiltProfiles config.`)\n }\n const profile = await loadPrebuiltProfile(complianceGroup, locale)\n if (!profile) throw new Error(`No embedded profile found for complianceGroup \"${complianceGroup}\".`)\n return profile\n}\n\n/** Scenario 1B — api disabled, compliance.type = known ComplianceGroupId */\nasync function scenario1B(complianceGroup: ComplianceGroupId, config: ConsentiConfig): Promise<ResolvedProfile> {\n const locale = effectiveLocale(config)\n\n const override = await resolveRegisteredOverride(complianceGroup, locale)\n if (override) return override\n\n if (!isPrebuiltAllowed(complianceGroup, config)) {\n throw new Error(`Pre-built profile for complianceGroup \"${complianceGroup}\" is disallowed by usePrebuiltProfiles config.`)\n }\n const profile = await loadPrebuiltProfile(complianceGroup, locale)\n if (!profile) throw new Error(`No embedded profile found for complianceGroup \"${complianceGroup}\".`)\n return profile\n}\n\n/** Scenario 2A — api enabled, compliance.type = 'auto' (server geo-resolve) */\nasync function scenario2A(config: ConsentiConfig): Promise<ResolvedProfile> {\n const locale = effectiveLocale(config)\n const tenantId = effectiveTenantId(config)\n const cacheEnabled = config.core?.cacheResolvedProfiles !== false\n const base = config.api?.baseUrl ?? window.location.origin\n const trustDomain = config.api?.trustDomain ?? false\n\n const cached = getCachedResolution(tenantId, locale, cacheEnabled)\n if (cached) {\n const json = await fetchProfileJson(cached.filePath, config.api?.authToken)\n if (json) {\n if (!isDomainAllowed(json, trustDomain)) {\n logger.error(`Domain ${window.location.origin} is not in allowedOrigins for this profile. Falling back to pre-built profile.`)\n return scenario1A(config)\n }\n // The cache entry's group is ground truth — it's what was actually requested — so it\n // wins over whatever (possibly absent, for a custom group) `complianceGroup` field the\n // static JSON file itself carries.\n return {\n ...mapPublicProfileToResolved(json),\n complianceGroup: cached.complianceGroup,\n ...(cached.requiresSensitiveOptIn ? { requiresSensitiveOptIn: true } : {}),\n }\n }\n }\n\n // Returning, already-decided visitor — the compliance group is already known from their\n // consent cookie, so skip the /resolve-profile geo round-trip entirely and fetch the group's\n // profile directly (same hot path scenario2B uses). Keyed on complianceGroup, not the stored\n // profile id, so a since-deactivated/replaced profile for that group is never silently served.\n // Falls through to the normal geo-resolve below if the direct fetch fails or no group is known\n // yet (genuinely undecided visitor, or a pre-existing cookie recorded before this field existed).\n const knownGroup = peekConsentGroup(config.core?.storage ?? 'cookie', config.core?.cookieName)\n if (knownGroup) {\n const json = await fetchProfileJson(\n `${base}/consenti/api/v1/profiles/${tenantId}/${knownGroup}/${encodeURIComponent(locale)}`,\n config.api?.authToken,\n ) ?? await fetchProfileJson(\n `${base}/consenti/api/v1/profiles/${tenantId}/${knownGroup}/default`,\n config.api?.authToken,\n )\n if (json && isDomainAllowed(json, trustDomain)) return { ...mapPublicProfileToResolved(json), complianceGroup: knownGroup }\n }\n\n const resolveUrl =\n `${base}/consenti/api/v1/resolve-profile?data=${encodeURIComponent(encodeGeoHints(locale))}`\n\n let path: string | null\n let complianceGroup: ComplianceGroupId\n let resolvedLocale: string\n let requiresSensitiveOptIn: boolean | undefined\n\n try {\n const resolved = await httpRequest<{\n path: string | null\n complianceGroup: string\n locale: string\n found: boolean\n requiresSensitiveOptIn?: boolean\n }>(resolveUrl, {}, config.api?.authToken)\n\n path = resolved.path\n complianceGroup = resolved.complianceGroup as ComplianceGroupId\n resolvedLocale = resolved.locale\n requiresSensitiveOptIn = resolved.requiresSensitiveOptIn\n\n if (resolved.found && path) {\n setCachedResolution(tenantId, locale, cacheEnabled, {\n filePath: `${base}${path}`,\n version: 0,\n complianceGroup,\n tenantId,\n locale: resolvedLocale,\n ...(requiresSensitiveOptIn ? { requiresSensitiveOptIn: true } : {}),\n })\n }\n } catch {\n logger.warn('resolve-profile failed; falling back to pre-built profile.')\n return scenario1A(config)\n }\n\n // Server has a static file — serve it\n if (path) {\n const json = await fetchProfileJson(`${base}${path}`, config.api?.authToken)\n if (json && isDomainAllowed(json, trustDomain)) {\n return {\n ...mapPublicProfileToResolved(json),\n complianceGroup,\n ...(requiresSensitiveOptIn ? { requiresSensitiveOptIn: true } : {}),\n }\n }\n }\n\n // Server resolved the group but has no file (new tenant, seeding pending)\n // Fall back to embedded profile for that group\n if (complianceGroup) {\n const embedded = await loadPrebuiltProfile(complianceGroup, resolvedLocale)\n if (embedded) return { ...embedded, ...(requiresSensitiveOptIn ? { requiresSensitiveOptIn: true } : {}) }\n }\n\n return scenario1A(config)\n}\n\n/** Scenario 2B — api enabled, compliance.type = known ComplianceGroupId */\nasync function scenario2B(complianceGroup: ComplianceGroupId, config: ConsentiConfig): Promise<ResolvedProfile> {\n const locale = effectiveLocale(config)\n const tenantId = effectiveTenantId(config)\n const base = config.api?.baseUrl ?? window.location.origin\n const trustDomain = config.api?.trustDomain ?? false\n\n const group = config.api?.complianceGroup ?? complianceGroup\n\n const json = await fetchProfileJson(\n `${base}/consenti/api/v1/profiles/${tenantId}/${group}/${encodeURIComponent(locale)}`,\n config.api?.authToken,\n ) ?? await fetchProfileJson(\n `${base}/consenti/api/v1/profiles/${tenantId}/${group}/default`,\n config.api?.authToken,\n )\n\n if (!json) {\n logger.warn(`Could not fetch profile for ${group}; falling back to pre-built.`)\n return scenario1B(group, config)\n }\n\n if (!isDomainAllowed(json, trustDomain)) {\n logger.error(`Domain ${window.location.origin} is not in allowedOrigins for this profile. Falling back to pre-built profile.`)\n return scenario1B(group, config)\n }\n\n return { ...mapPublicProfileToResolved(json), complianceGroup: group }\n}\n\n/**\n * Resolves a full `ProfileConfig` (author-registered or a registry-override match)\n * to a `ResolvedProfile` for the given locale — merges the base `mainBanner`/\n * `gpcBanner`/`preferenceModal` with that locale's `translations` entry, falling\n * back to the base (untranslated) content when the locale isn't found.\n */\nfunction resolveLocalProfileConfig(localProfile: ProfileConfig, locale: string, typeDescription?: string): ResolvedProfile {\n const resolved = localProfile.translations?.[locale]\n\n // if translations are not available for the given locale, use the default translations\n if (resolved === undefined) {\n if (locale !== localProfile.defaultLocale) logger.warn(`Unable to resolve requested locale \"${locale}\" of profile \"${typeDescription ?? localProfile.id}\", Using default \"${localProfile.defaultLocale}\".`)\n return {\n id: localProfile.id,\n gpcMode: localProfile.gpcMode ?? \"ignore\",\n allowReceipt: localProfile.allowReceipt ?? false,\n defaultLocale: localProfile.defaultLocale,\n locales: [localProfile.defaultLocale, ...Object.keys(localProfile.translations ?? {})],\n cookies: localProfile.cookies ?? {},\n ...(localProfile.expiryDays ? { expiryDays: localProfile.expiryDays } : {}),\n mainBanner: localProfile.mainBanner,\n ...(localProfile.gpcBanner ? { gpcBanner: localProfile.gpcBanner } : {}),\n preferenceModal: localProfile.preferenceModal,\n ...(localProfile.darkMode !== undefined ? { darkMode: localProfile.darkMode } : {}),\n ...(localProfile.complianceGroup ? { complianceGroup: localProfile.complianceGroup } : {}),\n ...(localProfile.ageGate ? { ageGate: localProfile.ageGate } : {}),\n ...(localProfile.ageGateModal ? { ageGateModal: localProfile.ageGateModal } : {}),\n }\n }\n\n // else deepmerge properties with the translations\n const mainBanner = deepMerge<MainBanner>(localProfile.mainBanner, resolved.mainBanner)\n const gpcBanner = localProfile.gpcBanner ? deepMerge<GpcBanner>(localProfile.gpcBanner, resolved.gpcBanner ?? {}) : false\n const preferenceModal = deepMerge<PreferenceModal>(localProfile.preferenceModal ?? {}, resolved.preferenceModal ?? {})\n // Like `gpcBanner`: a locale override alone (a `DeepPartial`) isn't a complete\n // `AgeGateModalContent` on its own, so it's only used to overlay a base — never standalone.\n const ageGateModal = localProfile.ageGateModal\n ? deepMerge<AgeGateModalContent>(localProfile.ageGateModal, resolved.ageGateModal ?? {})\n : undefined\n\n return {\n id: localProfile.id,\n gpcMode: localProfile.gpcMode ?? \"ignore\",\n allowReceipt: localProfile.allowReceipt ?? false,\n defaultLocale: localProfile.defaultLocale,\n locales: [localProfile.defaultLocale, ...Object.keys(localProfile.translations ?? {})],\n cookies: localProfile.cookies ?? {},\n ...(localProfile.expiryDays ? { expiryDays: localProfile.expiryDays } : {}),\n mainBanner,\n ...(gpcBanner ? { gpcBanner } : {}),\n preferenceModal,\n ...(localProfile.darkMode !== undefined ? { darkMode: localProfile.darkMode } : {}),\n ...(localProfile.complianceGroup ? { complianceGroup: localProfile.complianceGroup } : {}),\n ...(localProfile.ageGate ? { ageGate: localProfile.ageGate } : {}),\n ...(ageGateModal ? { ageGateModal } : {}),\n }\n}\n\n/** Scenario Local — locally registered profile */\nasync function scenarioLocal(typeValue: Symbol, config: ConsentiConfig): Promise<ResolvedProfile> {\n if (config.api?.enabled) {\n logger.warn('Local profile used instead of server profile. Check compliance.type config.')\n }\n\n const locale = effectiveLocale(config)\n const localProfile = getRegisteredProfile(typeValue)\n\n if (localProfile) {\n // Explicitly opted into deepMerge — patch the matching built-in profile rather\n // than treating this registration as a complete, standalone profile.\n if (localProfile.deepMerge && localProfile.complianceGroup) {\n const builtIn = await loadPrebuiltProfile(localProfile.complianceGroup, locale)\n const partial = toDeepPartialResolvedProfile(localProfile)\n return builtIn ? deepMerge<ResolvedProfile>(builtIn, partial) : (partial as ResolvedProfile)\n }\n return resolveLocalProfileConfig(localProfile as ProfileConfig, locale, (typeValue as symbol).description)\n }\n\n logger.error(`Unable to resolve locally registered profile \"${(typeValue as symbol).description}\", Using compliance.type: 'auto'.`)\n\n // Fall back to auto path for the current api mode\n return config.api?.enabled ? scenario2A(config) : scenario1A(config)\n}\n\n// ─── resolveProfile dispatcher ────────────────────────────────────────────────\n\nexport async function resolveProfile(config: ConsentiConfig): Promise<ResolvedProfile> {\n const complianceType = config.compliance?.type ?? 'auto'\n const apiEnabled = config.api?.enabled ?? false\n\n if (apiEnabled && config.compliance?.complianceMap) {\n logger.warn('compliance.complianceMap is ignored when api.enabled is true — the server resolves the compliance group.')\n }\n if (apiEnabled && config.compliance?.geoDataProvider) {\n logger.warn('compliance.geoDataProvider (widget config) is ignored when api.enabled is true — configure compliance.geoDataProvider on the @consenti/api server instead, the server resolves the compliance group.')\n }\n\n if (complianceType === 'auto') {\n return apiEnabled ? scenario2A(config) : scenario1A(config)\n }\n\n // A Symbol targets a client-registered profile (registerProfile()) — never a\n // server-hosted group, built-in or custom.\n if (typeof complianceType === 'symbol') {\n return scenarioLocal(complianceType, config)\n }\n\n // Any other string — one of the 8 built-in groups, or a profile authored against\n // a custom `customComplianceGroup` — resolves identically: scenario2B hot-serve\n // fetches by group name and falls back to scenario1B's pre-built lookup, which\n // simply has nothing to find for a custom group and surfaces that as the same\n // \"No embedded profile found\" error the built-in-group path already throws.\n return apiEnabled\n ? scenario2B(complianceType as ComplianceGroupId, config)\n : scenario1B(complianceType as ComplianceGroupId, config)\n}\n","/**\n * Global Privacy Control (GPC) detection and consent application.\n *\n * GPC is a browser-level privacy signal defined at https://globalprivacycontrol.org/.\n * When `navigator.globalPrivacyControl === true`, the user has indicated they do\n * not want their personal data sold or shared. CCPA and GDPR both recognise GPC\n * as a legally meaningful signal.\n *\n * Consenti supports three modes via `profile.gpcMode`:\n * - `'ignore'` → GPC signal ignored entirely (default)\n * - `'honor'` → GPC-flagged cookies pre-denied; GPC banner shown (or main banner)\n * - `'strict'` → GPC-flagged cookies pre-denied; consent written silently; no banner\n *\n * Only cookies with `listenGpc: true` on their `Cookie` config are affected.\n * Mandatory cookies are never denied regardless of GPC.\n */\n\nimport type { CookieMap, CategoryMap, ConsentValue, GpcMode } from '../types'\nimport { buildCookieCategoryIndex, isMandatoryCookie, getCookieLegalBasis } from '@consenti/utils'\nimport { safeNavigator } from '../utils/ssr'\n\n/**\n * Detects whether the Global Privacy Control signal is active in the current browser.\n *\n * Supported natively in Brave and via extensions in Firefox. The property is\n * intentionally not on the standard `Navigator` type yet, so we cast to check it.\n *\n * @returns `true` if `navigator.globalPrivacyControl === true`, `false` in all other cases\n * including SSR environments where `navigator` is unavailable.\n */\nexport function detectGPC(): boolean {\n const nav = safeNavigator()\n if (!nav) return false\n return (\n 'globalPrivacyControl' in nav &&\n (nav as Navigator & { globalPrivacyControl: boolean }).globalPrivacyControl === true\n )\n}\n\n/**\n * Applies the GPC signal to a partial consent object.\n *\n * For every cookie with `listenGpc: true` that is not mandatory:\n * - Consent-basis cookies → `'denied'`\n * - Legitimate-interest cookies (`type: 'legitimate_interest'`) → `'objected'`\n *\n * Mandatory cookies are always forced to `'granted'`.\n *\n * @param cookies - The full cookie map from the resolved profile.\n * @param categories - The category map from the resolved profile (`preferenceModal.categories`) — legal basis is derived from here.\n * @param currentConsent - Existing partial consent to use as a base (typically `{}`).\n * @param mode - The `gpcMode` mode (`'honor'` or `'strict'`). If 'ignore',\n * returns `currentConsent` unchanged.\n * @returns A new `ConsentValue` with GPC denials applied.\n */\nexport function applyGPCToConsent(\n cookies: CookieMap,\n categories: CategoryMap,\n currentConsent: Partial<ConsentValue>,\n mode: GpcMode,\n): ConsentValue {\n if (mode === 'ignore' || !detectGPC()) return { ...currentConsent } as ConsentValue\n\n const result: ConsentValue = { ...currentConsent } as ConsentValue\n const categoryIndex = buildCookieCategoryIndex(categories)\n\n for (const [cookieId, cookie] of Object.entries(cookies)) {\n if (isMandatoryCookie(cookieId, categoryIndex)) continue\n if (cookie.listenGpc) {\n // LI cookies use 'objected' as the refusal status per the GDPR Art. 21 framework\n result[cookieId] = getCookieLegalBasis(cookieId, categoryIndex) === 'legitimate_interest' ? 'objected' : 'denied'\n }\n }\n\n // Mandatory cookies always win, regardless of GPC\n for (const cookieId of Object.keys(cookies)) {\n if (isMandatoryCookie(cookieId, categoryIndex)) result[cookieId] = 'granted'\n }\n\n return result\n}\n","/**\n * Client-side `window.__tcfapi` stub — the standard IAB TCF v2.3 entry point third-party\n * ad-tech scripts call to read consent (`ping`, `getTCData`, `addEventListener`,\n * `removeEventListener`).\n *\n * Simplified, like the server-side fallback TC string this reads (`@consenti/utils`'s\n * `encodeTcString`): `tcString` is a base64url-encoded JSON payload, not the full IAB binary\n * bitfield encoding. This is intentional here, not just unfinished — the widget never fetches\n * the Global Vendor List itself (multi-megabyte, and IAB policy requires CMPs to cache their own\n * copy rather than have every visitor's browser re-fetch it), so `gvlVersion`/`publisherCC`\n * are operator-supplied via `TcfWidgetConfig` rather than derived, and default to explicit\n * \"not configured\" placeholders when left unset. `gdprApplies` is derived from the resolved\n * profile's compliance group.\n *\n * Spec-correct binary TC-string encoding is available when running with the `@consenti/api`\n * backend (`compliance.tcf.publisherCC` there, via the optional `@iabtechlabtcf/core`\n * dependency — see `apps/api/src/tcf/real-tc-string.ts`), since that's where the GVL is\n * actually fetched and cached. Fully standalone (no-backend) deployments keep this simplified\n * encoding.\n *\n * Only one CMP may own `window.__tcfapi` per page — if it's already set (a real CMP, or another\n * `ConsentiSetup` instance in a multi-profile page), this stub does not overwrite it.\n */\n\nimport type { ConsentValue, ResolvedProfile, TcfWidgetConfig } from '../types'\nimport { encodeTcString } from '@consenti/utils'\nimport { isClient, safeWindow } from '../utils/ssr'\n\ntype TcfCommand = 'ping' | 'getTCData' | 'addEventListener' | 'removeEventListener'\ntype TcfCallback = (data: unknown, success: boolean) => void\ntype QueuedCall = [TcfCommand, number, TcfCallback, unknown?]\n\n// Policy version 5 = TCF v2.3 (mandatory since 2026-03-01; 4 was v2.2, 3 was v2.1, 2 was v2.0).\n// The spec-correct encoder (`real-tc-string.ts`) derives this from the fetched GVL's own\n// `tcfPolicyVersion` instead — this constant only backs the simplified standalone encoding,\n// which has no GVL to read a live value from.\nconst TCF_POLICY_VERSION = 5\nconst API_VERSION = '2.3'\n\nexport interface TcfState {\n profile: ResolvedProfile | null\n consent: ConsentValue | null\n tcfConfig: TcfWidgetConfig | undefined\n}\n\nfunction buildPurposeAndVendorConsents(profile: ResolvedProfile | null, consent: ConsentValue | null) {\n const purposeConsents = new Set<number>()\n const vendorConsents = new Set<number>()\n if (profile && consent) {\n for (const [cookieId, cookie] of Object.entries(profile.cookies)) {\n if (consent[cookieId] !== 'granted' || !cookie.tcfVendorId) continue\n vendorConsents.add(cookie.tcfVendorId)\n for (const p of cookie.tcfPurposes ?? []) purposeConsents.add(p)\n }\n }\n return {\n purposeConsents: Array.from(purposeConsents).sort((a, b) => a - b),\n vendorConsents: Array.from(vendorConsents).sort((a, b) => a - b),\n }\n}\n\nfunction toBoolMap(ids: number[], max: number): Record<number, boolean> {\n const map: Record<number, boolean> = {}\n for (let i = 1; i <= max; i++) map[i] = ids.includes(i)\n return map\n}\n\n/**\n * Installs `window.__tcfapi`. No-ops in SSR, when `tcfConfig.enabled` is false, or when\n * `window.__tcfapi` already exists (another CMP/instance owns the page's TCF surface).\n *\n * @param getState - Called on every command so the stub always reflects current consent —\n * no stale snapshot captured at install time.\n */\nexport function installTcfStub(getState: () => TcfState): void {\n if (!isClient()) return\n const win = safeWindow() as (Window & { __tcfapi?: unknown }) | null\n if (!win || !getState().tcfConfig?.enabled) return\n if (win.__tcfapi) return\n\n const listeners = new Map<number, TcfCallback>()\n let listenerCounter = 0\n\n function buildTcData(listenerId?: number) {\n const { profile, consent, tcfConfig: cfg } = getState()\n const tcfConfig = cfg ?? { enabled: false, cmpId: 0, cmpVersion: 0 }\n const { purposeConsents, vendorConsents } = buildPurposeAndVendorConsents(profile, consent)\n const gdprApplies = profile?.complianceGroup === 'opt-in'\n const tcString = encodeTcString({\n cmpId: tcfConfig.cmpId,\n cmpVersion: tcfConfig.cmpVersion,\n consentScreen: 1,\n consentLanguage: 'en',\n vendorListVersion: tcfConfig.vendorListVersion ?? 0,\n purposeConsents,\n vendorConsents,\n })\n return {\n tcString,\n tcfPolicyVersion: TCF_POLICY_VERSION,\n cmpId: tcfConfig.cmpId,\n cmpVersion: tcfConfig.cmpVersion,\n gdprApplies,\n eventStatus: consent ? 'tcloaded' : 'cmpuishown',\n cmpStatus: 'loaded',\n isServiceSpecific: true,\n useNonStandardStacks: false,\n publisherCC: tcfConfig.publisherCC ?? 'AA',\n purposeOneTreatment: false,\n ...(listenerId !== undefined ? { listenerId } : {}),\n purpose: {\n consents: toBoolMap(purposeConsents, 10),\n legitimateInterests: toBoolMap([], 10),\n },\n vendor: {\n consents: toBoolMap(vendorConsents, Math.max(0, ...vendorConsents)),\n legitimateInterests: toBoolMap([], 0),\n },\n }\n }\n\n function notifyListeners(): void {\n for (const [listenerId, callback] of listeners) {\n callback(buildTcData(listenerId), true)\n }\n }\n\n function handleCommand(command: TcfCommand, _version: number, callback: TcfCallback, parameter?: unknown): void {\n switch (command) {\n case 'ping': {\n const { profile, consent, tcfConfig } = getState()\n callback({\n gdprApplies: profile?.complianceGroup === 'opt-in',\n cmpLoaded: true,\n cmpStatus: 'loaded',\n displayStatus: consent ? 'hidden' : 'visible',\n apiVersion: API_VERSION,\n cmpVersion: tcfConfig?.cmpVersion ?? 0,\n cmpId: tcfConfig?.cmpId ?? 0,\n gvlVersion: tcfConfig?.gvlVersion ?? 0,\n tcfPolicyVersion: TCF_POLICY_VERSION,\n }, true)\n return\n }\n case 'getTCData':\n callback(buildTcData(), true)\n return\n case 'addEventListener': {\n const listenerId = ++listenerCounter\n listeners.set(listenerId, callback)\n callback(buildTcData(listenerId), true)\n return\n }\n case 'removeEventListener': {\n const listenerId = typeof parameter === 'number' ? parameter : undefined\n const removed = listenerId !== undefined && listeners.delete(listenerId)\n callback(removed, removed)\n return\n }\n }\n }\n\n // Standard IAB stub-queue convention: scripts that load before the real stub push their\n // call onto `__tcfapi.a` instead of calling it directly. Drain it once we install for real.\n const queued = ((win.__tcfapi as { a?: QueuedCall[] } | undefined)?.a ?? []) as QueuedCall[]\n\n const api = ((command: TcfCommand, version: number, callback: TcfCallback, parameter?: unknown) => {\n handleCommand(command, version, callback, parameter)\n }) as { (command: TcfCommand, version: number, callback: TcfCallback, parameter?: unknown): void; a: QueuedCall[] }\n api.a = []\n win.__tcfapi = api\n\n window.addEventListener('consenti:consentSubmitted', notifyListeners)\n\n for (const [command, version, callback, parameter] of queued) {\n handleCommand(command, version, callback, parameter)\n }\n}\n","/**\n * Client-side `window.__gpp` stub — the standard IAB GPP CMP API entry point, US National\n * (\"usnat\") section only.\n *\n * Simplified, like the server-side fallback GPP string this reads (`@consenti/utils`'s\n * `encodeGppString`): `gppString` is a base64url-encoded JSON payload, not the real IAB binary\n * bit-string encoding. This is intentional here, not just unfinished — `@iabgpp/cmpapi` (the\n * real encoder, used server-side when installed — see `apps/api/src/gpp/gpp-string.ts`) bundles\n * every GPP section's encoder plus a GVL class, and inlining that into `@consenti/ui`'s bundle\n * would cost every site loading the widget the full library size, not just ones using GPP —\n * the same bundle-size reasoning that already keeps `@iabtechlabtcf/core` server-only for TCF,\n * just for a different underlying cause (there GVL-fetch cost, here plain library size).\n *\n * Only one CMP may own `window.__gpp` per page — if it's already set (a real CMP, or another\n * `ConsentiSetup` instance in a multi-profile page), this stub does not overwrite it.\n */\n\nimport type { ConsentValue, ResolvedProfile, GppWidgetConfig } from '../types'\nimport { encodeGppString } from '@consenti/utils'\nimport { isClient, safeWindow } from '../utils/ssr'\nimport { detectGPC } from './gpc'\n\ntype GppCommand = 'ping' | 'addEventListener' | 'removeEventListener' | 'getSection' | 'hasSection'\ntype GppCallback = (data: unknown, success: boolean) => void\ntype QueuedCall = [GppCommand, GppCallback, unknown?]\n\nconst GPP_VERSION = '1.1'\nconst USNAT_SECTION_ID = 7\n\nexport interface GppState {\n profile: ResolvedProfile | null\n consent: ConsentValue | null\n gppConfig: GppWidgetConfig | undefined\n}\n\nfunction deriveOptOuts(profile: ResolvedProfile | null, consent: ConsentValue | null) {\n let saleApplicable = false\n let sharingApplicable = false\n let saleOptOut = true\n let sharingOptOut = true\n if (profile && consent) {\n for (const [id, cookie] of Object.entries(profile.cookies)) {\n if (cookie.cpraCategory === 'sale') {\n saleApplicable = true\n if (consent[id] === 'granted') saleOptOut = false\n }\n if (cookie.cpraCategory === 'sharing') {\n sharingApplicable = true\n if (consent[id] === 'granted') sharingOptOut = false\n }\n }\n }\n return { saleApplicable, sharingApplicable, saleOptOut, sharingOptOut }\n}\n\n/**\n * Installs `window.__gpp`. No-ops in SSR, when `gppConfig.enabled` is false, or when\n * `window.__gpp` already exists (another CMP/instance owns the page's GPP surface).\n *\n * @param getState - Called on every command so the stub always reflects current consent — no\n * stale snapshot captured at install time.\n */\nexport function installGppStub(getState: () => GppState): void {\n if (!isClient()) return\n const win = safeWindow() as (Window & { __gpp?: unknown }) | null\n if (!win || !getState().gppConfig?.enabled) return\n if (win.__gpp) return\n\n const listeners = new Map<number, GppCallback>()\n let listenerCounter = 0\n\n function currentGppString(): string {\n const { profile, consent, gppConfig: cfg } = getState()\n const gppConfig = cfg ?? { enabled: false, cmpId: 0, cmpVersion: 0, mspaCoveredTransaction: false, mspaOptOutOptionMode: 0 as const, mspaServiceProviderMode: 0 as const }\n const { saleApplicable, sharingApplicable, saleOptOut, sharingOptOut } = deriveOptOuts(profile, consent)\n return encodeGppString({\n cmpId: gppConfig.cmpId,\n cmpVersion: gppConfig.cmpVersion,\n mspaCoveredTransaction: gppConfig.mspaCoveredTransaction,\n mspaOptOutOptionMode: gppConfig.mspaOptOutOptionMode,\n mspaServiceProviderMode: gppConfig.mspaServiceProviderMode,\n saleOptOut,\n saleApplicable,\n sharingOptOut,\n sharingApplicable,\n gpcDetected: detectGPC(),\n })\n }\n\n function buildPingData(): Record<string, unknown> {\n const { consent, gppConfig } = getState()\n return {\n gppVersion: GPP_VERSION,\n cmpStatus: 'loaded',\n cmpDisplayStatus: consent ? 'hidden' : 'visible',\n signalStatus: consent ? 'ready' : 'not ready',\n supportedAPIs: [`${USNAT_SECTION_ID}:usnat`],\n cmpId: gppConfig?.cmpId ?? 0,\n sectionList: [USNAT_SECTION_ID],\n applicableSections: [USNAT_SECTION_ID],\n gppString: currentGppString(),\n parsedSections: {},\n }\n }\n\n function notifyListeners(): void {\n for (const [listenerId, callback] of listeners) {\n callback({ ...buildPingData(), listenerId, pingData: buildPingData() }, true)\n }\n }\n\n function handleCommand(command: GppCommand, callback: GppCallback, parameter?: unknown): void {\n switch (command) {\n case 'ping':\n callback(buildPingData(), true)\n return\n case 'addEventListener': {\n const listenerId = ++listenerCounter\n listeners.set(listenerId, callback)\n callback({ ...buildPingData(), listenerId, pingData: buildPingData() }, true)\n return\n }\n case 'removeEventListener': {\n const listenerId = typeof parameter === 'number' ? parameter : undefined\n const removed = listenerId !== undefined && listeners.delete(listenerId)\n callback(removed, removed)\n return\n }\n case 'getSection':\n callback(parameter === 'usnat' ? currentGppString() : null, parameter === 'usnat')\n return\n case 'hasSection':\n callback(parameter === 'usnat', true)\n return\n }\n }\n\n // Standard IAB stub-queue convention: scripts that load before the real stub push their\n // call onto `__gpp.queue` instead of calling it directly. Drain it once we install for real.\n const queued = ((win.__gpp as { queue?: QueuedCall[] } | undefined)?.queue ?? []) as QueuedCall[]\n\n const api = ((command: GppCommand, callback: GppCallback, parameter?: unknown) => {\n handleCommand(command, callback, parameter)\n }) as { (command: GppCommand, callback: GppCallback, parameter?: unknown): void; queue: QueuedCall[]; gppVersion: string }\n api.queue = []\n api.gppVersion = GPP_VERSION\n win.__gpp = api\n\n window.addEventListener('consenti:consentSubmitted', notifyListeners)\n\n for (const [command, callback, parameter] of queued) {\n handleCommand(command, callback, parameter)\n }\n}\n","/**\n * Button construction and click routing.\n *\n * `style` maps directly to the BEM modifier class (visual only).\n * `action` determines the click handler:\n *\n * | `action` | Effect |\n * |---|---|\n * | `'custom'` | Grant/deny via `cookies`: `'*'` all, `'!'` deny, `string[]` specific |\n * | `'submit'` | Save the current modal toggle state |\n * | `'manage'` | Open the preference modal |\n * | `'close'` | Dismiss the current surface without saving |\n */\n\nimport type { Button } from '../types'\n\n/**\n * Callbacks wired to every interactive button rendered by the widget.\n * Implemented by `ConsentiSetup.buildHandlers()` for each rendering context\n * (`'banner'` or `'modal'`).\n */\nexport interface ButtonClickHandler {\n /** Grant all cookies (non-mandatory and mandatory). */\n onGrantAll: () => void\n /** Deny all non-mandatory cookies; grant mandatory ones. */\n onDenyAll: () => void\n /** Save the current toggle state (modal context) or grant mandatory only (banner context). */\n onSubmit: () => void\n /** Open the preference modal. */\n onManage: () => void\n /** Close the current UI surface without saving. */\n onClose: () => void\n /** Grant only the specified cookie IDs plus all mandatory cookies. */\n onGrantSpecific: (cookieIds: string[]) => void\n /** Erase the stored consent record (GDPR Art. 17 / CCPA-CPRA / LGPD Art. 18 and equivalents)\n * and re-prompt. Wired to the preference modal's \"Forget me\" button. */\n onForgetMe: () => void\n}\n\n/**\n * Constructs a `<button>` element from a `Button` config and registers a click handler.\n *\n * @param id - The button's key in the profile's `ButtonMap`, rendered as the DOM `id`.\n * @param btn - Button configuration from the resolved profile.\n * @param handlers - Click handler implementations.\n * @returns A fully wired `HTMLButtonElement`.\n */\nexport function buildButton(id: string, btn: Button, handlers: ButtonClickHandler): HTMLButtonElement {\n const el = document.createElement('button')\n el.type = 'button'\n if (id) el.id = id\n el.className = `consenti-btn consenti-btn--${btn.style}`\n el.textContent = btn.text\n\n el.addEventListener('click', () => {\n switch (btn.action) {\n case 'custom': {\n const cookies = btn.cookies\n if (cookies === '*') {\n handlers.onGrantAll()\n } else if (cookies === '!') {\n handlers.onDenyAll()\n } else if (Array.isArray(cookies) && cookies.length > 0) {\n handlers.onGrantSpecific(cookies)\n }\n break\n }\n case 'submit':\n handlers.onSubmit()\n break\n case 'manage':\n handlers.onManage()\n break\n case 'close':\n handlers.onClose()\n break\n }\n })\n\n return el\n}\n","/**\n * Full-page semi-transparent overlay shown behind the banner or modal.\n * Rendered as a sibling element inside `#consenti-root` so it does not interfere\n * with the host page's stacking context.\n *\n * Opacity is driven by an inline style (the only inline style in the widget) because\n * the value is dynamic per-profile config and cannot be expressed as a CSS custom\n * property without JavaScript.\n */\n\n/**\n * Creates a `<div class=\"consenti-overlay\">` element.\n *\n * @param opacity - Overlay opacity as a percentage (0–100). Converted to a 0–1 CSS value.\n * @param onClick - Optional click handler (used by modal to close on backdrop click).\n * @returns The constructed overlay element. Not yet inserted into the DOM.\n */\nexport function createOverlay(opacity: number, onClick?: () => void): HTMLElement {\n const el = document.createElement('div')\n el.className = 'consenti-overlay'\n el.style.opacity = String(opacity / 100)\n el.setAttribute('aria-hidden', 'true')\n if (onClick) {\n el.addEventListener('click', onClick)\n }\n return el\n}\n","/**\n * Cookie consent banner DOM builder.\n *\n * Constructs the banner element from a `MainBanner` config and wires up button handlers.\n * The banner is accessible: it carries `role=\"region\"`, `aria-label`, and `aria-live`\n * so screen reader users are informed when it appears.\n *\n * Position variants (bottom, top, middle, left-bottom, right-bottom) are applied via\n * BEM modifier classes and handled entirely in CSS.\n *\n * Buttons with `action === 'link'` are rendered as `<a>` elements in\n * `.consenti-banner__links` inside the text container, separate from action buttons.\n */\n\nimport type { MainBanner, Button } from '../types'\nimport type { ButtonClickHandler } from './buttons'\nimport { buildButton } from './buttons'\nimport { createOverlay } from './overlay'\n\n/**\n * Builds and manages the consent banner DOM element.\n *\n * One `Banner` instance is created per `ConsentiSetup`. Call `build()` to construct\n * the DOM, then `show()` / `hide()` to toggle visibility without rebuilding.\n */\nexport class Banner {\n private el: HTMLElement | null = null\n private overlayEl: HTMLElement | null = null\n\n /**\n * Constructs the banner DOM from the given config and returns the root element.\n * The element is not yet inserted into the document — the caller appends it to\n * the mount root via `Renderer.mount()`.\n *\n * @param config - Banner layout and content config from the resolved profile.\n * @param handlers - Click handlers wired to each button.\n * @param privacyPolicyUrl - Optional URL appended as a \"Privacy Policy\" link after the body text.\n * @param locales - All locale codes available on the profile (for the locale switcher).\n * @param activeLocale - The currently active locale code.\n * @param onLocaleSwitch - Callback invoked when the user selects a different locale.\n * @param variant - The variant of the banner 'main' | 'gpc.\n * @returns The constructed banner `<div>` element.\n */\n build(\n config: MainBanner,\n handlers: ButtonClickHandler,\n privacyPolicyUrl?: string,\n locales?: string[],\n activeLocale?: string,\n onLocaleSwitch?: (locale: string) => void,\n hidePoweredBy = true,\n variant: 'main' | 'gpc' = 'main'\n ): HTMLElement {\n const banner = document.createElement('div')\n banner.id = 'consenti-banner'\n banner.className = `consenti-banner consenti-banner--${variant} consenti-banner--${config.position}`\n banner.setAttribute('role', 'region')\n banner.setAttribute('aria-label', 'Cookie consent')\n banner.setAttribute('aria-live', 'polite')\n\n const textContainer = document.createElement('div')\n textContainer.className = `consenti-banner__text-container`\n const linksContainer = document.createElement('div')\n linksContainer.className = 'consenti-banner__links'\n\n if (config.heading) {\n const headingTag = config.headingTag ?? 'div'\n const heading = document.createElement(headingTag)\n heading.id = 'consenti-banner-heading'\n heading.className = 'consenti-banner__heading'\n heading.textContent = config.heading\n textContainer.appendChild(heading)\n }\n\n const text = document.createElement('div')\n text.className = 'consenti-banner__text'\n text.innerHTML = config.htmlText\n\n if (privacyPolicyUrl) {\n const link = document.createElement('a')\n link.href = privacyPolicyUrl\n link.className = 'consenti-banner__link consenti-banner__link--privacy consenti-btn consenti-btn--text'\n link.textContent = 'Privacy Policy'\n link.setAttribute('target', '_blank')\n link.setAttribute('rel', 'noopener noreferrer')\n linksContainer.appendChild(link)\n }\n\n textContainer.appendChild(text)\n\n // Render link-action buttons as styled anchors below the body text\n const buttonEntries = Object.entries(config.buttons)\n const linkButtons = buttonEntries.filter((e): e is [string, Button & { action: 'link' }] => e[1].action === 'link')\n if (linkButtons.length > 0) {\n for (const [, btn] of linkButtons) {\n if (!btn.url) continue\n const anchor = document.createElement('a')\n anchor.href = btn.url\n anchor.className = `consenti-banner__link consenti-btn consenti-btn--${btn.style ?? 'text'}`\n anchor.textContent = btn.text\n anchor.setAttribute('target', '_blank')\n anchor.setAttribute('rel', 'noopener noreferrer')\n linksContainer.appendChild(anchor)\n }\n }\n if (linkButtons.length > 0 || privacyPolicyUrl) {\n textContainer.appendChild(linksContainer)\n }\n\n banner.appendChild(textContainer)\n\n const actionButtons = buttonEntries.filter(([, b]) => b.action !== 'link')\n if (actionButtons.length > 0) {\n const btns = document.createElement('div')\n btns.className = 'consenti-banner__buttons'\n for (const [id, btn] of actionButtons) {\n btns.appendChild(buildButton(id, btn, handlers))\n }\n banner.appendChild(btns)\n }\n\n // Controls group: locale switcher + close button (both positioned absolute top-right)\n const hasClose = config.showClose\n const hasLocaleSwitcher = config.showLocaleSwitcher && locales && locales.length > 0\n\n if (hasClose || hasLocaleSwitcher) {\n const controls = document.createElement('div')\n controls.className = 'consenti-banner__controls'\n\n if (hasLocaleSwitcher && locales && onLocaleSwitch) {\n controls.appendChild(buildLocaleSwitcher(locales, activeLocale, onLocaleSwitch))\n }\n\n if (hasClose) {\n const close = document.createElement('button')\n close.type = 'button'\n close.className = 'consenti-banner__close consenti-btn consenti-transition-1_2x consenti-btn--action'\n close.setAttribute('aria-label', 'Close cookie banner')\n close.textContent = '×'\n close.addEventListener('click', handlers.onClose)\n controls.appendChild(close)\n }\n\n banner.appendChild(controls)\n }\n\n if (config.overlayOpacity && config.overlayOpacity > 0) {\n this.overlayEl = createOverlay(config.overlayOpacity)\n }\n\n const pb = document.createElement('a')\n pb.href = 'https://consenti.dev/?utm_source=widget&utm_medium=powered-by&utm_campaign=consenti-ui'\n pb.className = 'consenti__powered-by'\n pb.textContent = 'Powered by Consenti ↗'\n pb.setAttribute('target', '_blank')\n pb.setAttribute('rel', 'noopener noreferrer')\n if (hidePoweredBy) {\n pb.classList.add('consenti-d-none')\n }\n banner.appendChild(pb)\n\n this.el = banner\n return banner\n }\n\n /** Returns the overlay element (if `overlayOpacity > 0`), or `null`. */\n getOverlay(): HTMLElement | null {\n return this.overlayEl\n }\n\n /** Makes the banner and its overlay visible after a `hide()` call. */\n show(): void {\n if (this.el) this.el.style.display = ''\n if (this.overlayEl) this.overlayEl.style.display = ''\n }\n\n /** Hides the banner and its overlay without removing them from the DOM. */\n hide(): void {\n if (this.el) this.el.style.display = 'none'\n if (this.overlayEl) this.overlayEl.style.display = 'none'\n }\n\n /** Removes the banner and overlay from the DOM entirely. */\n remove(): void {\n this.el?.remove()\n this.overlayEl?.remove()\n this.el = null\n this.overlayEl = null\n }\n}\n\n/**\n * Builds a locale switcher button + dropdown list element.\n * Clicking the globe button toggles the dropdown; selecting a locale calls `onSwitch`.\n */\nfunction buildLocaleSwitcher(\n locales: string[],\n activeLocale: string | undefined,\n onSwitch: (locale: string) => void,\n): HTMLElement {\n const wrapper = document.createElement('div')\n wrapper.className = 'consenti-locale-switcher'\n\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'consenti-locale-switcher__btn consenti-btn consenti-transition-1_2x consenti-btn--action'\n btn.setAttribute('aria-label', 'Switch language')\n btn.setAttribute('aria-haspopup', 'listbox')\n btn.textContent = '🌐'\n\n const list = document.createElement('ul')\n list.className = 'consenti-locale-switcher__list'\n list.setAttribute('role', 'listbox')\n list.setAttribute('aria-label', 'Select language')\n list.hidden = true\n\n for (const locale of locales) {\n const item = document.createElement('li')\n item.setAttribute('role', 'option')\n item.setAttribute('aria-selected', String(locale === activeLocale))\n\n const localeBtn = document.createElement('button')\n localeBtn.type = 'button'\n localeBtn.className = `consenti-locale-switcher__option${locale === activeLocale ? ' consenti-locale-switcher__option--active' : ''}`\n localeBtn.textContent = locale\n localeBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = true\n if (locale !== activeLocale) onSwitch(locale)\n })\n\n item.appendChild(localeBtn)\n list.appendChild(item)\n }\n\n btn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = !list.hidden\n })\n\n // Close dropdown when clicking outside\n document.addEventListener('click', () => { list.hidden = true }, { capture: true, once: false })\n\n wrapper.appendChild(btn)\n wrapper.appendChild(list)\n return wrapper\n}\n","/**\n * WCAG 2.1 focus trap for modal dialogs.\n *\n * When a modal is open, keyboard focus must remain within it (WCAG 2.1 SC 2.1.2\n * \"No Keyboard Trap\" requires that keyboard focus can be moved *to* and *from* a\n * component — trapping focus inside a modal while it is open is the correct pattern).\n *\n * Additionally, pressing Escape dispatches a `consenti:closeRequest` CustomEvent on the\n * container so the modal's close handler can react without a direct reference back here.\n */\n\n/** Selector covering all standard interactive focusable elements. */\nconst FOCUSABLE_SELECTORS = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(', ')\n\n/**\n * Traps keyboard focus within `container`.\n *\n * - Tab cycles from last focusable element back to first.\n * - Shift+Tab cycles from first focusable element to last.\n * - Escape dispatches `consenti:closeRequest` on the container.\n * - Focus is moved to the first focusable element immediately on call.\n *\n * @param container - The modal root element to trap focus inside.\n * @returns A cleanup function — call it when the modal closes to remove event listeners.\n */\nexport function trapFocus(container: HTMLElement): () => void {\n const getFocusable = () =>\n Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS)).filter(\n (el) => !el.closest('[aria-hidden=\"true\"]'),\n )\n\n const handleKeydown = (e: KeyboardEvent): void => {\n if (e.key !== 'Tab') return\n const els = getFocusable()\n if (els.length === 0) return\n const first = els[0]!\n const last = els[els.length - 1]!\n\n if (e.shiftKey) {\n if (document.activeElement === first || document.activeElement === container) {\n e.preventDefault()\n last.focus()\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault()\n first.focus()\n }\n }\n }\n\n const handleEscape = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') {\n container.dispatchEvent(new CustomEvent('consenti:closeRequest', { bubbles: true }))\n }\n }\n\n document.addEventListener('keydown', handleKeydown)\n document.addEventListener('keydown', handleEscape)\n\n getFocusable()[0]?.focus()\n\n return () => {\n document.removeEventListener('keydown', handleKeydown)\n document.removeEventListener('keydown', handleEscape)\n }\n}\n","/**\n * Preference modal DOM builder and state manager.\n *\n * The modal renders each category as a 3-state master toggle (`role=\"checkbox\"`,\n * `aria-checked` one of `\"true\"`/`\"false\"`/`\"mixed\"`) followed by one binary\n * `role=\"switch\"` per parameter it contains (with purpose/CPRA category/TCF vendor\n * shown where set). The master toggle's state is always *derived* from its\n * parameters' states, never stored independently: `'allowed'` when every parameter\n * is granted, `'denied'` when none are, `'mixed'` otherwise. Clicking the master\n * toggle while `'mixed'` or `'denied'` grants every parameter first; clicking again\n * from `'allowed'` denies every parameter.\n *\n * Legitimate-interest (LI) categories use `'objected'` as their off-state rather than\n * `'denied'`, per the GDPR Art. 21 framework for LI objection.\n *\n * A consent receipt checkbox is optionally rendered when `core.allowReceipt: true`.\n * It resets to unchecked each time the modal is opened so the user must opt in per submission.\n *\n * A \"Forget me\" button is optionally rendered below the categories when the profile's\n * `preferenceModal.showForgetMe` is true — GDPR Art. 17 / CCPA-CPRA / LGPD Art. 18 and\n * equivalent erasure rights, only shown to visitors who already have a stored consent decision.\n * See `ConsentiSetup.forgetMe()`.\n */\n\nimport type { PreferenceModal, Category, CategoryMap, CookieMap, ConsentValue, ConsentStatus, Button } from '../types'\nimport type { ButtonClickHandler } from './buttons'\nimport { buildButton } from './buttons'\nimport { createOverlay } from './overlay'\nimport { trapFocus } from './focus-trap'\n\ntype CategoryRollup = 'allowed' | 'denied' | 'mixed'\n\nexport class Modal {\n private el: HTMLElement | null = null\n private triggerEl: HTMLElement | null = null\n private trapCleanup: (() => void) | null = null\n /** Live per-parameter (cookie id) toggle state — same shape as `ConsentValue`. Source of truth for `getToggleConsent()`. */\n private toggleState = new Map<string, ConsentStatus>()\n /** Per-parameter toggle elements, keyed by cookie id — used to cascade visual updates when the category master toggle is clicked. */\n private paramToggles = new Map<string, HTMLButtonElement>()\n /** Category master toggle elements + their member cookie ids and legal basis, keyed by category id — used to recompute/update the master's rollup state when a member parameter changes. */\n private categoryToggles = new Map<string, { el: HTMLButtonElement; cookieIds: string[]; isLI: boolean }>()\n /** Each category's text-clamp + parameter-list disclosure, keyed by category id — a single \"Show more\" toggle drives both together. `params` is `null` when the category has 0-1 parameters (nothing to disclose there). */\n private categoryDisclosures = new Map<string, { text: HTMLElement; toggle: HTMLButtonElement; params: HTMLElement | null }>()\n private receiptCheckbox: HTMLInputElement | null = null\n /** When `true`, the modal wrapper is passthrough (overlayOpacity === 0). */\n private passthrough = false\n /** Screen width (px) below which the modal goes full-screen. 0 = disabled. */\n private mobileBreakpoint = 576\n /** Saved body overflow value — restored when modal closes. */\n private savedBodyOverflow: string | undefined = undefined\n\n /**\n * Constructs the modal DOM from the given config.\n * The modal is not inserted into the document until `open()` is called.\n *\n * @param config - Modal layout and content config from the resolved profile.\n * @param cookies - The resolved profile's parameter map — supplies purpose/CPRA category/TCF vendor/preGrant per parameter rendered inside each category.\n * @param initialConsent - Current stored consent, used to pre-set toggle states.\n * @param handlers - Click handlers wired to each button.\n * @param allowReceipt - When `true`, renders the consent receipt download checkbox.\n * @param dpdpaConfig - DPDPA grievance notice config.\n * @param locales - All available locale codes (for locale switcher).\n * @param activeLocale - The currently active locale code.\n * @param onLocaleSwitch - Callback invoked when the user selects a different locale.\n */\n build(\n config: PreferenceModal,\n cookies: CookieMap,\n initialConsent: ConsentValue,\n handlers: ButtonClickHandler,\n allowReceipt: boolean,\n dpdpaConfig?: { dataFiduciary: string; grievanceEmail: string; purposeDescription?: string },\n locales?: string[],\n activeLocale?: string,\n onLocaleSwitch?: (locale: string) => void,\n hidePoweredBy = true,\n ): void {\n this.mobileBreakpoint = config.mobileFullScreenBreakpoint ?? 576\n const position = config.position ?? 'center'\n const wrapper = document.createElement('div')\n wrapper.id = 'consenti-modal'\n wrapper.className = `consenti-modal consenti-modal--${position}`\n wrapper.setAttribute('role', 'dialog')\n wrapper.setAttribute('aria-modal', 'true')\n\n const overlayOpacity = config.overlayOpacity ?? 50\n this.passthrough = overlayOpacity === 0\n if (overlayOpacity > 0) {\n // When `persistent: true`, clicking the overlay does not close the modal —\n // the user must use one of the footer buttons.\n const overlayClickHandler = config.persistent ? undefined : () => handlers.onClose()\n const overlay = createOverlay(overlayOpacity, overlayClickHandler)\n overlay.className = 'consenti-modal__overlay'\n wrapper.appendChild(overlay)\n } else {\n // No overlay — let pointer events pass through the wrapper to the page beneath.\n wrapper.classList.add('consenti-modal--passthrough')\n }\n\n const container = document.createElement('div')\n container.className = 'consenti-modal__container'\n\n const header = document.createElement('div')\n header.className = 'consenti-modal__header'\n\n const body = document.createElement('div')\n body.className = 'consenti-modal__body'\n\n if (config.heading) {\n const headingTag = config.headingTag ?? 'div'\n const heading = document.createElement(headingTag)\n heading.id = 'consenti-modal-heading'\n heading.className = 'consenti-modal__heading'\n heading.textContent = config.heading\n header.appendChild(heading)\n wrapper.setAttribute('aria-labelledby', 'consenti-modal-heading')\n }\n\n if (config.subheading) {\n const sub = document.createElement('p')\n sub.className = 'consenti-modal__subheading'\n sub.textContent = config.subheading\n header.appendChild(sub)\n }\n\n // Header controls: locale switcher + close\n const hasClose = config.showClose !== false\n const hasLocaleSwitcher = config.showLocaleSwitcher && locales && locales.length > 0\n\n if (hasLocaleSwitcher || hasClose) {\n const headerControls = document.createElement('div')\n headerControls.className = 'consenti-modal__header-controls'\n\n if (hasLocaleSwitcher && locales && onLocaleSwitch) {\n headerControls.appendChild(buildModalLocaleSwitcher(locales, activeLocale, onLocaleSwitch))\n }\n\n if (hasClose) {\n const close = document.createElement('button')\n close.type = 'button'\n close.className = 'consenti-modal__close consenti-transition-1_2x consenti-btn consenti-btn--action'\n close.setAttribute('aria-label', 'Close preference modal')\n close.textContent = '×'\n close.addEventListener('click', handlers.onClose)\n headerControls.appendChild(close)\n }\n\n header.appendChild(headerControls)\n }\n container.appendChild(header)\n\n if (config.htmlText) {\n const desc = document.createElement('div')\n desc.className = 'consenti-modal__text'\n desc.innerHTML = config.htmlText\n body.appendChild(desc)\n }\n\n // Render link-action buttons as styled anchors below the intro text\n const buttonEntries = Object.entries(config.buttons)\n const linkButtons = buttonEntries.filter((e): e is [string, Button & { action: 'link' }] => e[1].action === 'link')\n if (linkButtons.length > 0) {\n const linksContainer = document.createElement('div')\n linksContainer.className = 'consenti-modal__links'\n for (const [, btn] of linkButtons) {\n if (!btn.url) continue\n const anchor = document.createElement('a')\n anchor.href = btn.url\n anchor.className = `consenti-modal__link consenti-btn consenti-btn--${btn.style ?? 'text'}`\n anchor.textContent = btn.text\n anchor.setAttribute('target', '_blank')\n anchor.setAttribute('rel', 'noopener noreferrer')\n linksContainer.appendChild(anchor)\n }\n header.appendChild(linksContainer)\n }\n\n const categories = document.createElement('div')\n categories.className = 'consenti-modal__categories'\n\n for (const [categoryId, cat] of Object.entries(config.categories)) {\n const catEl = this.buildCategory(categoryId, cat, cookies, initialConsent)\n categories.appendChild(catEl)\n }\n body.appendChild(categories)\n container.appendChild(body)\n if (allowReceipt) {\n body.appendChild(this.buildReceiptOption(config.receiptLabel, config.receiptDescription))\n }\n // Only shown to visitors who already have a stored consent decision — nothing to erase\n // otherwise. `initialConsent` is `{}` for a first-time visitor (see `showModal()`).\n if (config.showForgetMe && Object.keys(initialConsent).length > 0) {\n body.appendChild(this.buildForgetMeOption(config.forgetMeLabel, handlers.onForgetMe))\n }\n\n if (dpdpaConfig) {\n const notice = document.createElement('div')\n notice.className = 'consenti-modal__dpdpa-notice'\n const esc = (s: string) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;').replace(/'/g, '&#x27;')\n const safeEmail = dpdpaConfig.grievanceEmail.includes('@') && !dpdpaConfig.grievanceEmail.toLowerCase().startsWith('javascript:')\n ? dpdpaConfig.grievanceEmail : ''\n notice.innerHTML = [\n `<strong>Data Fiduciary:</strong> ${esc(dpdpaConfig.dataFiduciary)}.`,\n dpdpaConfig.purposeDescription ? ` <strong>Purpose:</strong> ${esc(dpdpaConfig.purposeDescription)}.` : '',\n ` You may withdraw consent at any time or contact our Grievance Officer at`,\n safeEmail ? ` <a href=\"mailto:${esc(safeEmail)}\">${esc(safeEmail)}</a>.` : '.',\n ].join('')\n container.appendChild(notice)\n }\n\n const btns = document.createElement('div')\n btns.className = 'consenti-modal__buttons'\n for (const [id, btn] of buttonEntries.filter(([, b]) => b.action !== 'link')) {\n btns.appendChild(buildButton(id, btn, handlers))\n }\n container.appendChild(btns)\n\n const pb = document.createElement('a')\n pb.href = 'https://consenti.dev/?utm_source=widget&utm_medium=powered-by&utm_campaign=consenti-ui'\n pb.className = 'consenti__powered-by'\n pb.textContent = 'Powered by Consenti ↗'\n pb.setAttribute('target', '_blank')\n pb.setAttribute('rel', 'noopener noreferrer')\n if (hidePoweredBy) {\n pb.classList.add('consenti-d-none')\n }\n body.appendChild(pb)\n\n wrapper.appendChild(container)\n\n wrapper.addEventListener('consenti:closeRequest', () => handlers.onClose())\n\n this.el = wrapper\n }\n\n /**\n * Appends the shared check/cross/objected/knob icon markup to a toggle button (used by both\n * the category master toggle and per-parameter switches). `isLI` renders the off-state as an\n * \"objected\" (circle-slash) icon instead of the plain cross, and marks the toggle with\n * `consenti-toggle--li` so CSS swaps between them — legitimate-interest categories are on by\n * default and the visitor *objects* rather than plainly denying, per GDPR Art. 21.\n */\n private static appendToggleIcons(toggle: HTMLButtonElement, isLI: boolean): void {\n if (isLI) toggle.classList.add('consenti-toggle--li')\n\n const checkIcon = document.createElement('span')\n checkIcon.className = 'consenti-category__toggle-icon consenti-category__toggle-icon--check'\n checkIcon.setAttribute('aria-hidden', 'true')\n checkIcon.innerHTML = '<svg viewBox=\"0 0 10 10\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M1.5 5L4 7.5L8.5 2.5\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'\n toggle.appendChild(checkIcon)\n\n const crossIcon = document.createElement('span')\n crossIcon.className = 'consenti-category__toggle-icon consenti-category__toggle-icon--cross'\n crossIcon.setAttribute('aria-hidden', 'true')\n crossIcon.innerHTML = '<svg viewBox=\"0 0 10 10\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 2L8 8M8 2L2 8\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>'\n toggle.appendChild(crossIcon)\n\n if (isLI) {\n const objectedIcon = document.createElement('span')\n objectedIcon.className = 'consenti-category__toggle-icon consenti-category__toggle-icon--objected'\n objectedIcon.setAttribute('aria-hidden', 'true')\n objectedIcon.innerHTML = '<svg viewBox=\"0 0 10 10\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"5\" cy=\"5\" r=\"3.5\" stroke=\"currentColor\" stroke-width=\"1.4\"/><path d=\"M2.5 2.5L7.5 7.5\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\"/></svg>'\n toggle.appendChild(objectedIcon)\n }\n\n const knob = document.createElement('span')\n knob.className = 'consenti-category__toggle-knob'\n knob.setAttribute('aria-hidden', 'true')\n toggle.appendChild(knob)\n }\n\n /** `'allowed'` when every parameter is granted, `'denied'` when none are, `'mixed'` otherwise. LI categories treat 'objected' as the off-state. */\n private computeRollup(cookieIds: string[], isLI: boolean): CategoryRollup {\n const offStatus: ConsentStatus = isLI ? 'objected' : 'denied'\n const statuses = cookieIds.map(id => this.toggleState.get(id))\n if (statuses.every(s => s === 'granted')) return 'allowed'\n if (statuses.every(s => s === offStatus || s === undefined)) return 'denied'\n return 'mixed'\n }\n\n /** Recomputes and re-renders a category's master toggle from its current member states. */\n private updateCategoryToggleVisual(categoryId: string): void {\n const entry = this.categoryToggles.get(categoryId)\n if (!entry) return\n const rollup = this.computeRollup(entry.cookieIds, entry.isLI)\n const ariaChecked = rollup === 'allowed' ? 'true' : rollup === 'denied' ? 'false' : 'mixed'\n entry.el.setAttribute('aria-checked', ariaChecked)\n entry.el.classList.toggle('consenti-category__toggle--partial', rollup === 'mixed')\n }\n\n /** Builds one parameter row (purpose/CPRA/TCF info + its own switch) inside a category. */\n private buildParamRow(categoryId: string, cookieId: string, cookie: CookieMap[string] | undefined, isMandatory: boolean, isLI: boolean): HTMLElement {\n const row = document.createElement('div')\n row.className = 'consenti-category__param'\n\n const info = document.createElement('div')\n info.className = 'consenti-category__param-info'\n\n const label = document.createElement('span')\n label.className = 'consenti-category__param-label'\n label.textContent = cookieId\n info.appendChild(label)\n\n const meta = document.createElement('span')\n meta.className = 'consenti-category__param-meta'\n const badges: string[] = []\n if (cookie?.purpose) badges.push(cookie.purpose)\n if (cookie?.cpraCategory) badges.push(`CPRA: ${cookie.cpraCategory}`)\n if (cookie?.tcfVendorId != null) badges.push(`TCF Vendor #${cookie.tcfVendorId}`)\n meta.textContent = badges.join(' · ')\n if (badges.length) info.appendChild(meta)\n\n row.appendChild(info)\n\n const offStatus: ConsentStatus = isLI ? 'objected' : 'denied'\n const status = this.toggleState.get(cookieId) ?? offStatus\n const on = status === 'granted'\n\n const toggle = document.createElement('button')\n toggle.type = 'button'\n toggle.className = `consenti-category__param-toggle${isMandatory ? ' consenti-category__param-toggle--mandatory' : ''}`\n toggle.setAttribute('role', 'switch')\n toggle.setAttribute('aria-checked', String(on))\n toggle.setAttribute('aria-label', cookieId)\n if (isMandatory) {\n toggle.setAttribute('aria-disabled', 'true')\n toggle.disabled = true\n }\n Modal.appendToggleIcons(toggle, isLI)\n\n if (!isMandatory) {\n toggle.addEventListener('click', () => {\n const next: ConsentStatus = this.toggleState.get(cookieId) === 'granted' ? offStatus : 'granted'\n this.toggleState.set(cookieId, next)\n toggle.setAttribute('aria-checked', String(next === 'granted'))\n this.updateCategoryToggleVisual(categoryId)\n })\n }\n\n this.paramToggles.set(cookieId, toggle)\n row.appendChild(toggle)\n return row\n }\n\n private buildCategory(categoryId: string, cat: Category, cookies: CookieMap, initialConsent: ConsentValue): HTMLElement {\n const isLI = cat.legalBasis === 'legitimate_interest'\n const isMandatory = cat.legalBasis === 'mandatory'\n const offStatus: ConsentStatus = isLI ? 'objected' : 'denied'\n\n // Seed per-parameter state: stored consent wins; otherwise mandatory→granted,\n // LI→granted (on unless objected), consent-basis→preGrant ? granted : denied.\n for (const cookieId of cat.cookies) {\n const stored = initialConsent[cookieId]\n let status: ConsentStatus\n if (stored !== undefined) {\n status = stored\n } else if (isMandatory) {\n status = 'granted'\n } else if (isLI) {\n status = 'granted'\n } else {\n status = cookies[cookieId]?.preGrant ? 'granted' : 'denied'\n }\n this.toggleState.set(cookieId, status)\n }\n\n const wrapper = document.createElement('div')\n wrapper.className = `consenti-category${isMandatory ? ' consenti-category--mandatory' : ''}`\n\n const header = document.createElement('div')\n header.className = 'consenti-category__header'\n\n const headingTag = cat.headingTag ?? 'div'\n const heading = document.createElement(headingTag)\n heading.id = `consenti-cat-${categoryId}-heading`\n heading.className = 'consenti-category__heading'\n heading.textContent = cat.heading\n header.appendChild(heading)\n\n const rollup = this.computeRollup(cat.cookies, isLI)\n const toggle = document.createElement('button')\n toggle.type = 'button'\n toggle.className = `consenti-category__toggle${isMandatory ? ' consenti-category__toggle--mandatory' : ''}${rollup === 'mixed' ? ' consenti-category__toggle--partial' : ''}`\n // A 3-state master toggle needs `aria-checked=\"mixed\"`, which is only valid ARIA\n // for role=\"checkbox\" — role=\"switch\" is strictly binary per the ARIA spec.\n // Per-parameter toggles below stay role=\"switch\" since they're always binary.\n toggle.setAttribute('role', 'checkbox')\n toggle.setAttribute('aria-checked', rollup === 'allowed' ? 'true' : rollup === 'denied' ? 'false' : 'mixed')\n toggle.setAttribute('aria-labelledby', `consenti-cat-${categoryId}-heading`)\n if (isMandatory) {\n toggle.setAttribute('aria-disabled', 'true')\n toggle.disabled = true\n }\n Modal.appendToggleIcons(toggle, isLI)\n\n this.categoryToggles.set(categoryId, { el: toggle, cookieIds: cat.cookies, isLI })\n\n if (!isMandatory) {\n toggle.addEventListener('click', () => {\n const current = this.computeRollup(cat.cookies, isLI)\n // Matches native tri-state \"select all\" checkbox convention: mixed/denied → grant\n // everything first; a second click from fully-allowed denies everything.\n const next: ConsentStatus = current === 'allowed' ? offStatus : 'granted'\n for (const cookieId of cat.cookies) {\n this.toggleState.set(cookieId, next)\n const paramToggle = this.paramToggles.get(cookieId)\n if (paramToggle && !paramToggle.disabled) paramToggle.setAttribute('aria-checked', String(next === 'granted'))\n }\n this.updateCategoryToggleVisual(categoryId)\n })\n }\n\n header.appendChild(toggle)\n wrapper.appendChild(header)\n\n if (isLI && cat.legitimateInterestDescription) {\n const li = document.createElement('p')\n li.className = 'consenti-category__li-desc'\n li.textContent = cat.legitimateInterestDescription\n wrapper.appendChild(li)\n }\n\n const textWrap = document.createElement('div')\n textWrap.className = 'consenti-category__text-wrap'\n\n const text = document.createElement('div')\n text.className = 'consenti-category__text consenti-category__text--clamped'\n text.innerHTML = cat.htmlText\n textWrap.appendChild(text)\n\n const showMore = document.createElement('button')\n showMore.type = 'button'\n showMore.className = 'consenti-category__show-more consenti-d-none'\n showMore.setAttribute('aria-expanded', 'false')\n showMore.textContent = 'Show more'\n textWrap.appendChild(showMore)\n\n wrapper.appendChild(textWrap)\n\n const params = document.createElement('div')\n params.className = 'consenti-category__params'\n for (const cookieId of cat.cookies) {\n params.appendChild(this.buildParamRow(categoryId, cookieId, cookies[cookieId], isMandatory, isLI))\n }\n const hasMultipleParams = cat.cookies.length > 1\n params.classList.add('consenti-d-none')\n wrapper.appendChild(params)\n\n // One toggle drives both disclosures together: unclamping the text and revealing the\n // parameter list happen as a single \"Show more\" action, not two independent controls.\n showMore.addEventListener('click', () => {\n const expanded = !text.classList.toggle('consenti-category__text--clamped')\n if (hasMultipleParams) params.classList.toggle('consenti-d-none', !expanded)\n showMore.setAttribute('aria-expanded', String(expanded))\n showMore.textContent = expanded ? 'Show less' : 'Show more'\n })\n this.categoryDisclosures.set(categoryId, { text, toggle: showMore, params: hasMultipleParams ? params : null })\n\n return wrapper\n }\n\n private buildReceiptOption(label_?: string, description?: string): HTMLElement {\n const wrapper = document.createElement('div')\n wrapper.className = 'consenti-modal__receipt-option'\n\n const label = document.createElement('label')\n label.className = 'consenti-modal__receipt-label'\n\n const checkbox = document.createElement('input')\n checkbox.type = 'checkbox'\n checkbox.className = 'consenti-modal__receipt-checkbox'\n checkbox.id = 'consenti-receipt-checkbox'\n checkbox.setAttribute('aria-describedby', 'consenti-receipt-desc')\n this.receiptCheckbox = checkbox\n\n label.appendChild(checkbox)\n label.append(' ' + (label_ ?? 'Get a copy of my consent choices (JSON)'))\n wrapper.appendChild(label)\n\n const desc = document.createElement('p')\n desc.id = 'consenti-receipt-desc'\n desc.className = 'consenti-modal__receipt-desc'\n desc.textContent = description ?? 'A JSON file will be downloaded to your device when you save your preferences.'\n wrapper.appendChild(desc)\n\n return wrapper\n }\n\n /**\n * Builds the \"Forget me\" control — GDPR Art. 17 / CCPA-CPRA / LGPD Art. 18 and equivalent\n * erasure rights, exercised from the widget itself (see `ConsentiSetup.forgetMe()`). Confirms\n * before erasing since the action is destructive and cannot be undone.\n */\n private buildForgetMeOption(label_: string | undefined, onForgetMe: () => void): HTMLElement {\n const wrapper = document.createElement('div')\n wrapper.className = 'consenti-modal__forget-me'\n\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.id = 'consenti-forget-me'\n btn.className = 'consenti-btn consenti-btn--text consenti-modal__forget-me-btn'\n btn.textContent = label_ ?? 'Forget me'\n btn.addEventListener('click', () => {\n if (window.confirm('Erase your stored consent record? This cannot be undone.')) {\n onForgetMe()\n }\n })\n\n wrapper.appendChild(btn)\n return wrapper\n }\n\n /**\n * Appends the modal to `mountEl` (falling back to `document.body` if omitted) and activates\n * the focus trap. The receipt checkbox (if present) is reset to unchecked.\n *\n * @param triggerEl - The element that opened the modal. Focus returns here on `close()`.\n * @param mountEl - Element to append the modal into — normally `#consenti-root`, so the\n * whole widget lives under one root and inherits its theme/a11y classes\n * via normal CSS descendant inheritance instead of needing them mirrored.\n */\n open(triggerEl?: HTMLElement, mountEl?: HTMLElement): void {\n if (!this.el) return\n this.triggerEl = triggerEl ?? (document.activeElement as HTMLElement)\n if (this.receiptCheckbox) this.receiptCheckbox.checked = false\n const isFullscreen = this.mobileBreakpoint > 0 && window.innerWidth <= this.mobileBreakpoint\n if (isFullscreen) {\n this.el.classList.add('consenti-modal--fullscreen')\n } else {\n this.el.classList.remove('consenti-modal--fullscreen')\n }\n ;(mountEl ?? document.body).appendChild(this.el)\n // Lock page scroll when the modal has an overlay or is in mobile full-screen mode.\n // Skip when passthrough and not full-screen — the user can still scroll the page.\n if (!this.passthrough || isFullscreen) {\n this.savedBodyOverflow = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n }\n // Skip focus trap when overlay is transparent — the user can interact with the\n // page behind the modal panel so trapping keyboard focus would be counterproductive.\n if (!this.passthrough) {\n this.trapCleanup = trapFocus(this.el)\n }\n this.resetCategoryDisclosures()\n }\n\n /**\n * Re-evaluates whether each category's text overflows its 3-line clamp and/or it has more\n * than one parameter, and shows/hides the single \"Show more\" toggle accordingly. Resets\n * every category back to clamped-text + hidden-params first, so re-opening the modal\n * always starts collapsed.\n */\n private resetCategoryDisclosures(): void {\n for (const { text, toggle, params } of this.categoryDisclosures.values()) {\n text.classList.add('consenti-category__text--clamped')\n params?.classList.add('consenti-d-none')\n toggle.setAttribute('aria-expanded', 'false')\n toggle.textContent = 'Show more'\n const isTextOverflowing = text.scrollHeight > text.clientHeight + 1\n toggle.classList.toggle('consenti-d-none', !(isTextOverflowing || params !== null))\n }\n }\n\n /**\n * Removes the modal from `document.body`, deactivates the focus trap,\n * and returns focus to `triggerEl`.\n */\n close(): void {\n this.trapCleanup?.()\n this.trapCleanup = null\n if (this.savedBodyOverflow !== undefined) {\n document.body.style.overflow = this.savedBodyOverflow\n this.savedBodyOverflow = undefined\n }\n this.el?.remove()\n this.triggerEl?.focus()\n this.triggerEl = null\n }\n\n /** Returns `true` if the modal is currently mounted in the document. */\n isOpen(): boolean {\n return this.el?.isConnected === true\n }\n\n /** Returns `true` if the receipt download checkbox is checked at the time of calling. */\n isReceiptRequested(): boolean {\n return this.receiptCheckbox?.checked ?? false\n }\n\n /**\n * Reads the current per-parameter toggle state and converts it to a `ConsentValue`.\n * The internal state is already keyed by cookie id (built up in `buildCategory`/\n * `buildParamRow` at construction time), so this is close to a direct read —\n * the fallback branch only matters if `categories` lists a parameter that was\n * never rendered (shouldn't happen in practice).\n *\n * Called by the submit handler to build the consent object before writing to storage.\n *\n * @param categories - The category map from the resolved profile (provides cookie IDs and legal basis for the fallback).\n */\n getToggleConsent(categories: CategoryMap): ConsentValue {\n const consent: ConsentValue = {}\n for (const cat of Object.values(categories)) {\n const isLI = cat.legalBasis === 'legitimate_interest'\n const isMandatory = cat.legalBasis === 'mandatory'\n const fallback: ConsentStatus = isMandatory ? 'granted' : isLI ? 'objected' : 'denied'\n for (const cookieId of cat.cookies) {\n consent[cookieId] = isMandatory ? 'granted' : (this.toggleState.get(cookieId) ?? fallback)\n }\n }\n return consent\n }\n\n /** Removes the modal element from the DOM and clears all internal state. */\n destroy(): void {\n this.trapCleanup?.()\n if (this.savedBodyOverflow !== undefined) {\n document.body.style.overflow = this.savedBodyOverflow\n this.savedBodyOverflow = undefined\n }\n this.el?.remove()\n this.el = null\n this.paramToggles.clear()\n this.categoryToggles.clear()\n this.categoryDisclosures.clear()\n this.toggleState.clear()\n this.receiptCheckbox = null\n }\n}\n\n/** Builds a compact locale switcher for the modal header. */\nfunction buildModalLocaleSwitcher(\n locales: string[],\n activeLocale: string | undefined,\n onSwitch: (locale: string) => void,\n): HTMLElement {\n const wrapper = document.createElement('div')\n wrapper.className = 'consenti-locale-switcher consenti-locale-switcher--modal'\n\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'consenti-locale-switcher__btn consenti-btn consenti-btn--action consenti-transition-1_2x'\n btn.setAttribute('aria-label', 'Switch language')\n btn.setAttribute('aria-haspopup', 'listbox')\n btn.textContent = '🌐'\n\n const list = document.createElement('ul')\n list.className = 'consenti-locale-switcher__list'\n list.setAttribute('role', 'listbox')\n list.setAttribute('aria-label', 'Select language')\n list.hidden = true\n\n for (const locale of locales) {\n const item = document.createElement('li')\n item.setAttribute('role', 'option')\n item.setAttribute('aria-selected', String(locale === activeLocale))\n\n const localeBtn = document.createElement('button')\n localeBtn.type = 'button'\n localeBtn.className = `consenti-locale-switcher__option${locale === activeLocale ? ' consenti-locale-switcher__option--active' : ''}`\n localeBtn.textContent = locale\n localeBtn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = true\n if (locale !== activeLocale) onSwitch(locale)\n })\n\n item.appendChild(localeBtn)\n list.appendChild(item)\n }\n\n btn.addEventListener('click', (e) => {\n e.stopPropagation()\n list.hidden = !list.hidden\n })\n\n document.addEventListener('click', () => { list.hidden = true }, { capture: true, once: false })\n\n wrapper.appendChild(btn)\n wrapper.appendChild(list)\n return wrapper\n}\n","/**\n * Age-gate prompt shown before any other consent UI when the resolved profile's `ageGate.enabled`.\n *\n * Two screens, built on demand (never both mounted at once):\n * - `buildPrompt()` — \"Are you {minimumAge} or older?\" Yes/No.\n * - `buildParentalConsentMessage()` — shown after a \"No\" answer when\n * `requireParentalConsent` is true; static message, no further input collected here\n * (see `AGE_GATE.md` in docs — verification itself is left to the site owner).\n *\n * Text is translatable, per-locale profile content (`profile.ageGateModal`) — this is\n * consumer-facing legal-notice copy, regulated per-locale, unlike the widget's own hardcoded UI\n * chrome (banner close button, locale switcher). A profile that enables `ageGate` without\n * authoring `ageGateModal` text falls back to the English defaults below, so nothing silently\n * breaks for an existing profile that predates this option.\n */\n\nimport { createOverlay } from './overlay'\nimport type { AgeGateModalContent } from '../types'\n\nexport class AgeGate {\n private el: HTMLElement | null = null\n private overlayEl: HTMLElement | null = null\n\n /** Builds the \"are you old enough\" prompt. Not yet inserted into the document. */\n buildPrompt(\n minimumAge: number,\n onYes: () => void,\n onNo: () => void,\n content?: AgeGateModalContent,\n ): HTMLElement {\n const root = document.createElement('div')\n root.id = 'consenti-age-gate'\n root.className = 'consenti-age-gate'\n root.setAttribute('role', 'dialog')\n root.setAttribute('aria-modal', 'true')\n root.setAttribute('aria-label', 'Age verification')\n\n const heading = document.createElement('div')\n heading.className = 'consenti-age-gate__heading'\n heading.textContent = content?.heading || `Are you ${minimumAge} or older?`\n root.appendChild(heading)\n\n const text = document.createElement('div')\n text.className = 'consenti-age-gate__text'\n text.innerHTML = content?.htmlText || 'Please confirm your age before we show you cookie preferences.'\n root.appendChild(text)\n\n const buttons = document.createElement('div')\n buttons.className = 'consenti-age-gate__buttons'\n\n const yes = document.createElement('button')\n yes.type = 'button'\n yes.className = 'consenti-btn consenti-btn--primary'\n yes.textContent = content?.confirmButtonLabel || 'Yes'\n yes.addEventListener('click', onYes)\n buttons.appendChild(yes)\n\n const no = document.createElement('button')\n no.type = 'button'\n no.className = 'consenti-btn consenti-btn--secondary'\n no.textContent = content?.denyButtonLabel || 'No'\n no.addEventListener('click', onNo)\n buttons.appendChild(no)\n\n root.appendChild(buttons)\n\n this.overlayEl = createOverlay(60)\n this.el = root\n return root\n }\n\n /** Builds the static \"parental consent required\" message (no overlay change needed — reuses the existing one). */\n buildParentalConsentMessage(onAcknowledge: () => void, content?: AgeGateModalContent): HTMLElement {\n const root = document.createElement('div')\n root.id = 'consenti-age-gate'\n root.className = 'consenti-age-gate'\n root.setAttribute('role', 'dialog')\n root.setAttribute('aria-modal', 'true')\n root.setAttribute('aria-label', 'Parental consent required')\n\n const heading = document.createElement('div')\n heading.className = 'consenti-age-gate__heading'\n heading.textContent = content?.parentalConsent?.heading || 'Parental consent required'\n root.appendChild(heading)\n\n const text = document.createElement('div')\n text.className = 'consenti-age-gate__text'\n text.innerHTML = content?.parentalConsent?.htmlText || 'Only strictly necessary cookies will be used until a parent or guardian verifies consent on your behalf.'\n root.appendChild(text)\n\n const buttons = document.createElement('div')\n buttons.className = 'consenti-age-gate__buttons'\n\n const ok = document.createElement('button')\n ok.type = 'button'\n ok.className = 'consenti-btn consenti-btn--primary'\n ok.textContent = content?.parentalConsent?.confirmButtonLabel || 'OK'\n ok.addEventListener('click', onAcknowledge)\n buttons.appendChild(ok)\n\n root.appendChild(buttons)\n\n this.el = root\n return root\n }\n\n getOverlay(): HTMLElement | null {\n return this.overlayEl\n }\n\n remove(): void {\n this.el?.remove()\n this.overlayEl?.remove()\n this.el = null\n this.overlayEl = null\n }\n}\n","/**\n * DOM mount point and screen reader utilities.\n *\n * All Consenti UI elements (banner, modal, overlay) are inserted into a single\n * `<div id=\"consenti-root\">` appended to `document.body`. This keeps the DOM\n * footprint predictable and makes cleanup trivial — `unmount()` removes the root\n * and any injected `<style>` tag in one call.\n *\n * The `announceToScreenReader()` method manages a visually-hidden ARIA live region\n * (`#consenti-announcer`) used to announce non-visual state changes (e.g.\n * \"Your cookie preferences have been saved.\").\n */\n\nimport { isClient } from '../utils/ssr'\n\nconst ROOT_ID = 'consenti-root'\n\n/**\n * Manages the `#consenti-root` mount point and related DOM infrastructure.\n * One `Renderer` instance is created per `ConsentiSetup`.\n */\nexport class Renderer {\n private root: HTMLElement | null = null\n private styleEl: HTMLElement | null = null\n\n /**\n * Returns the mount-point element, creating `#consenti-root` if needed.\n *\n * When `rootEl` is supplied:\n * - `string` → resolved via `document.querySelector`. Throws if no match.\n * - `HTMLElement` → used directly. Throws if not attached to the document.\n * On subsequent calls the previously stored root is returned immediately.\n *\n * @throws `Error` if called in SSR or if a supplied `rootEl` cannot be resolved.\n */\n mount(rootEl?: string | HTMLElement): HTMLElement {\n if (!isClient()) throw new Error('[Consenti] Renderer.mount() called in SSR context')\n\n if (this.root) return this.root\n\n if (rootEl !== undefined) {\n if (typeof rootEl === 'string') {\n const found = document.querySelector<HTMLElement>(rootEl)\n if (!found) throw new Error(`[Consenti] rootEl selector \"${rootEl}\" did not match any element.`)\n this.root = found\n } else {\n if (!document.contains(rootEl)) throw new Error('[Consenti] rootEl element is not attached to the document.')\n this.root = rootEl\n }\n this.root.classList.add('consenti-root')\n return this.root\n }\n\n let root = document.getElementById(ROOT_ID)\n if (!root) {\n root = document.createElement('div')\n root.id = ROOT_ID\n document.body.appendChild(root)\n }\n root.classList.add('consenti-root')\n this.root = root\n return root\n }\n\n /**\n * Injects a `<style id=\"consenti-styles\">` tag into `<head>` with the given CSS string.\n * No-ops if the style element already exists (idempotent).\n *\n * @param css - The CSS content to inject.\n */\n injectStyles(css: string): void {\n if (!isClient()) return\n if (this.styleEl) return\n\n const style = document.createElement('style')\n style.id = 'consenti-styles'\n style.textContent = css\n document.head.appendChild(style)\n this.styleEl = style\n }\n\n /** Removes `#consenti-root` and the injected `<style>` tag from the document. */\n unmount(): void {\n this.root?.remove()\n this.styleEl?.remove()\n this.root = null\n this.styleEl = null\n }\n\n /**\n * Announces a message to screen readers via a visually-hidden `aria-live=\"polite\"` region.\n *\n * The region is created on first call and reused. The message is cleared then\n * re-set via `requestAnimationFrame` to guarantee the change is detected by\n * AT even if the same string is announced twice in a row.\n *\n * @param message - The message to announce (plain text, not HTML).\n */\n announceToScreenReader(message: string): void {\n if (!isClient()) return\n let announcer = document.getElementById('consenti-announcer')\n if (!announcer) {\n announcer = document.createElement('div')\n announcer.id = 'consenti-announcer'\n announcer.setAttribute('role', 'status')\n announcer.setAttribute('aria-live', 'polite')\n announcer.setAttribute('aria-atomic', 'true')\n // Visually hidden but still in the accessibility tree\n announcer.style.cssText =\n 'position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;'\n document.body.appendChild(announcer)\n }\n announcer.textContent = ''\n requestAnimationFrame(() => {\n if (announcer) announcer.textContent = message\n })\n }\n}\n","/**\n * Main entry point for the Consenti widget.\n *\n * Instantiate one `ConsentiSetup` per page (or per profile ID if you run multiple\n * consent contexts on the same page). The constructor is safe to call during SSR —\n * it silently no-ops when `window` is not defined.\n *\n * @example\n * ```ts\n * const widget = new ConsentiSetup({\n * compliance: { type: 'opt-in' },\n * })\n * await widget.ready\n * console.log(widget.hasConsent())\n * ```\n */\n\nimport type {\n ConsentiConfig,\n ConsentValue,\n ConsentAction,\n ConsentEvent,\n ResolvedProfile,\n Category,\n ConsentiPlugin,\n ConsentiWidgetAPI,\n ConsentiMessage,\n ConsentiEventName,\n ThemeConfig,\n CoreConfig,\n DeepPartial,\n ConsentType,\n GpcMode,\n ConsentDbRecord,\n AgeGateConfig,\n IdentifyEventDetail,\n VisitorIdentity,\n} from '../types'\nimport { buildCookieCategoryIndex, isMandatoryCookie, resolveTextDirection } from '@consenti/utils'\nimport { scanConsentScripts } from '../utils/script-scanner'\nimport type { ConsentScript } from '../utils/consent-script'\nimport type { CategoryScript } from '../utils/category-script'\nimport {\n getGoogleGTMConsent,\n getPurposeConsent,\n getCategoryConsent,\n getAdobeConsent,\n getMetaConsent,\n getMicrosoftClarityConsent,\n getTwilioSegmentConsent,\n} from '../utils/consent-mapper'\nimport { CONSENTI_CSS } from '../styles/consenti-css'\nimport { isClient } from '../utils/ssr'\nimport { ConsentStorage } from '../utils/storage'\nimport { ConsentiChannel } from '../utils/broadcast'\nimport { generateReceipt, downloadReceipt } from '../utils/receipt'\nimport { generatePrefixedId } from '../utils/uuid'\nimport { getOrCreateVisitorId, peekVisitorId } from '../utils/visitor-id'\nimport { ConsentStore } from './consent-store'\nimport { EventBus } from './event-bus'\nimport { resolveProfile } from './profile-resolver'\nimport { detectGPC, applyGPCToConsent } from './gpc'\nimport { installTcfStub } from './tcf-stub'\nimport { installGppStub } from './gpp-stub'\nimport { Banner } from '../ui/banner'\nimport { Modal } from '../ui/modal'\nimport { AgeGate } from '../ui/age-gate'\nimport { Renderer } from '../ui/renderer'\nimport type { ButtonClickHandler } from '../ui/buttons'\nimport { deepMerge } from '../utils/locale'\nimport { logger, initConsole } from '../utils/console'\n\nexport class ConsentiSetup implements ConsentiWidgetAPI {\n private initialized = false\n private initializing = false\n private profile: ResolvedProfile | null = null\n private resolvedBase: ResolvedProfile | null = null\n /** cookieId → Category lookup, rebuilt whenever `profile` changes. Avoids rescanning categories on every consent action. */\n private categoryIndex: Map<string, Category> = new Map()\n /** Watchers created by the declarative `data-consenti-*` DOM scan — destroyed alongside the widget since they have no other owner. */\n private scannedScripts: Array<ConsentScript | CategoryScript> = []\n private consentStore: ConsentStore | null = null\n /**\n * Stable per-browser identifier. Created lazily the first time a consent decision actually\n * happens (manual submit, GPC auto-response, or relaying a decision already made in another\n * tab) — never minted before that, so an undecided visitor leaves nothing in storage. See\n * `getOrCreateVisitorId()`.\n */\n private visitorId(): string {\n return getOrCreateVisitorId(this.config.core.storage ?? 'cookie', this.config.core.cookieDomains)\n }\n private eventBus: EventBus | null = null\n private banner: Banner | null = null\n private modal: Modal | null = null\n private ageGate: AgeGate | null = null\n private renderer: Renderer | null = null\n /** Set once the age gate (if enabled) resolves; threaded onto every `submitConsent` call thereafter. */\n private _ageVerified: boolean | undefined = undefined\n private _parentalConsentToken: string | undefined = undefined\n private broadcastChannel = new ConsentiChannel()\n private readyResolve!: () => void\n private readyPromise: Promise<void>\n private plugins: ConsentiPlugin[] = []\n /** Signature from the most recent `writeAndSign` call. Included in consent receipts. */\n private lastSignature: string | undefined\n /** Tracks which banner variant is currently visible, or null when hidden. */\n private _bannerVariant: 'main' | 'gpc' | null = null\n /** Tracks which banner variant is currently mounted in the DOM (persists when banner is hidden). */\n private _bannerMounted: 'main' | 'gpc' | null = null\n /** Active focus trap keydown listener — removed when modal closes. */\n private _focusTrapListener: ((e: KeyboardEvent) => void) | null = null\n /** Inbound `consenti:listener:identify` window listener — installed once per init(), removed on destroy(). */\n private _identifyListener: EventListener | null = null\n /** Maps original handler functions to their DOM-wrapped listeners for on/off bookkeeping. */\n private _eventHandlers = new Map<\n (data: ConsentEvent) => void,\n Array<{ name: string; wrapped: EventListener }>\n >()\n /** `core` defaulted to `{}` in the constructor below — every one of its own fields is\n * optional, so this narrows back to non-optional for every other method in this class. */\n private config: ConsentiConfig & { core: CoreConfig }\n\n /**\n * Creates and initialises the consent widget.\n *\n * Construction is synchronous; the async `init()` is launched in the background.\n * Use `await widget.ready` or `widget.onReady(cb)` to run code after init completes.\n *\n * @param config - Widget configuration. See `ConsentiConfig` for all options. `core` may be\n * omitted entirely — every one of its own fields is optional too.\n */\n constructor(config: ConsentiConfig) {\n this.config = { ...config, core: config.core ?? {} }\n this.readyPromise = new Promise<void>((resolve) => {\n this.readyResolve = resolve\n })\n\n if (!isClient()) return\n\n if (config.verbose) {\n initConsole(['error', 'info', 'warning', 'log'])\n } else {\n initConsole(this.config.core.console ?? ['error'])\n }\n\n\n if (config.api?.enabled && this.config.core.storage === 'localStorage') {\n logger.warn(\n 'localStorage storage mode cannot be used with API mode ' +\n '(API sets cookies via Set-Cookie header). Falling back to cookie storage.',\n )\n this.config.core.storage = 'cookie'\n }\n\n if (config.autoInit !== false) {\n this.init().catch((err) => logger.warn('Init error:', err))\n }\n }\n\n private profileId(): string {\n return this.profile?.id ?? \"0\"\n }\n\n /** Patches `base` with `complianceGroupsOverride[base.complianceGroup]`, if both exist — the\n * per-group counterpart to `profileOverride`. Lets an author supply different text/buttons/\n * cookies per compliance group (built-in or custom) without authoring a separate profile for\n * each one. Applied before `profileOverride`, which still always wins as the final catch-all. */\n private applyComplianceGroupOverride(base: ResolvedProfile): ResolvedProfile {\n const override = base.complianceGroup\n ? this.config.complianceGroupsOverride?.[base.complianceGroup]\n : undefined\n return override ? deepMerge<ResolvedProfile>(base, override) : base\n }\n\n /**\n * Whether `window.__tcfapi`/`window.__gpp` should stay uninstalled because the corresponding\n * server-side registration (`compliance.tcf`/`compliance.gpp`) is unconfirmed (cmpId/cmpVersion\n * changed since the dashboard's confirmation checkbox was last checked, or never confirmed).\n * Standalone (no-backend) deployments have no governance layer to check against, so this always\n * resolves `false` there — the client-side config is used as configured, same as before this\n * feature existed. A network failure fails closed (`true`) rather than assuming the\n * unverifiable state is fine.\n */\n private async isRegistrationBlocked(enabled: boolean | undefined, statusPath: 'tcf' | 'gpp'): Promise<boolean> {\n if (!enabled || !this.config.api?.enabled) return false\n try {\n const { httpRequest } = await import('../utils/http')\n const base = this.config.api.baseUrl ?? window.location.origin\n const status = await httpRequest<{ blocked: boolean }>(\n `${base}/consenti/api/v1/${statusPath}/status`,\n {},\n this.config.api.authToken,\n )\n return status.blocked\n } catch {\n return true\n }\n }\n\n /**\n * Starts widget initialisation. Called automatically unless `autoInit: false` is set.\n * Can be called explicitly after construction when `autoInit: false`, or after `destroy()`\n * to re-initialise the same instance.\n */\n async init(): Promise<void> {\n if (!isClient()) return\n if (this.initializing) return\n this.initializing = true\n // Reset ready promise so callers can await this init cycle\n this.initialized = false\n this.readyPromise = new Promise<void>((resolve) => { this.readyResolve = resolve })\n\n try {\n const storageMode = this.config.core.storage ?? 'cookie'\n\n // Kicked off in parallel with profile resolution (both are network calls when api.enabled)\n // so an already-confirmed registration — the overwhelmingly common case — never adds\n // sequential latency to the banner's first paint.\n const tcfBlockedPromise = this.isRegistrationBlocked(this.config.compliance?.tcf?.enabled, 'tcf')\n const gppBlockedPromise = this.isRegistrationBlocked(this.config.compliance?.gpp?.enabled, 'gpp')\n\n const resolvedProfile = await resolveProfile(this.config)\n this.resolvedBase = resolvedProfile\n\n // Apply widget-level config overrides on top of the resolved profile\n if (this.config.darkMode !== undefined) resolvedProfile.darkMode = this.config.darkMode\n\n const withGroupOverride = this.applyComplianceGroupOverride(resolvedProfile)\n\n const profile = this.config.profileOverride\n ? deepMerge<ResolvedProfile>(withGroupOverride, this.config.profileOverride)\n : withGroupOverride\n\n const gpcActive = detectGPC();\n\n // GPC handling — profileOverrides can override profile JSON when explicitly set\n profile.gpcMode = profile.gpcMode ?? \"ignore\"\n\n this.profile = profile\n this.categoryIndex = buildCookieCategoryIndex(profile.preferenceModal.categories)\n\n this.eventBus = new EventBus(this.profileId(), this.config.utils?.gtm)\n this.consentStore = new ConsentStore(\n this.profileId(),\n storageMode,\n this.config.core.cookieDomains,\n this.config.core.userId,\n this.config.core.cookieName,\n )\n this.consentStore.setProfileExpiry(profile.expiryDays ?? 365)\n this.consentStore.setProfileVersion(profile.version ?? 0)\n if (!(await tcfBlockedPromise)) {\n installTcfStub(() => ({\n profile: this.profile,\n consent: this.getConsent(),\n tcfConfig: this.config.compliance?.tcf,\n }))\n } else {\n logger.error(\n 'TCF is configured but registration is unconfirmed (or unverifiable) — window.__tcfapi ' +\n 'was not installed. Confirm the current cmpId/cmpVersion in the dashboard\\'s TCF Registration panel.',\n )\n }\n if (!(await gppBlockedPromise)) {\n installGppStub(() => ({\n profile: this.profile,\n consent: this.getConsent(),\n gppConfig: this.config.compliance?.gpp,\n }))\n } else {\n logger.error(\n 'GPP is configured but registration is unconfirmed (or unverifiable) — window.__gpp ' +\n 'was not installed. Confirm the current cmpId/cmpVersion in the dashboard\\'s GPP Registration panel.',\n )\n }\n this.renderer = new Renderer()\n if (!this.config.core.disableCssTemplate && CONSENTI_CSS) {\n this.renderer.injectStyles(CONSENTI_CSS)\n }\n this.banner = new Banner()\n this.modal = new Modal()\n\n this.applyTheme()\n this.applyDarkMode()\n this.applyDirection()\n this.applyAccessibility()\n this.applyStackButtonsBreakpoint()\n this.broadcastChannel.connect((msg) => this.handleBroadcastMessage(msg))\n this._installIdentifyListener()\n\n await this.runPlugins()\n\n // Determine signing key: API-returned key takes precedence, then config fallback\n const signingKey = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n\n const existing =\n signingKey\n ? await this.consentStore.readAndVerify(signingKey)\n : this.consentStore.read()\n const hasExisting = existing !== null\n\n const ageGateConfig = profile.ageGate\n if (ageGateConfig?.enabled && !hasExisting) {\n // Blocks the rest of init() (GPC/CCPA/CPRA/banner below) until the visitor answers,\n // resumed from `resolveAgeGate()` via `continueInit()`. `finishInit()` is deliberately\n // not called here — `ready` should only resolve once a determinate outcome is reached\n // (confirmed → continueInit's own finishInit; declined → the deny-all branches in\n // `resolveAgeGate()`), matching every other exit path in this method.\n this.showAgeGate(ageGateConfig, profile)\n return\n }\n\n await this.continueInit(profile, gpcActive, hasExisting)\n } finally {\n this.initializing = false\n }\n }\n\n /**\n * Everything after the \"does consent already exist\" check — GPC/CCPA/CPRA silent paths,\n * then the normal opt-in banner. Extracted from `init()` so the age gate (when enabled and\n * no consent exists yet) can defer this whole sequence until the visitor answers it via\n * `resolveAgeGate()`, then resume here with `hasExisting` fixed at `false`.\n */\n private async continueInit(profile: ResolvedProfile, gpcActive: boolean, hasExisting: boolean): Promise<void> {\n if (!this.consentStore || !this.eventBus) return\n\n this.eventBus.dispatch('consenti:bannerInitialized', {\n profileId: this.profileId(),\n hasExistingConsent: hasExisting,\n complianceGroup: profile.complianceGroup ?? \"auto\",\n gpcDetected: gpcActive,\n willShow: !hasExisting && !(gpcActive && profile.gpcMode === 'strict'),\n })\n\n if (profile.gpcMode !== 'ignore' && gpcActive) {\n const gpcConsent = applyGPCToConsent(profile.cookies, profile.preferenceModal.categories, {}, profile.gpcMode as GpcMode)\n\n if (profile.gpcMode === 'strict') {\n // Silent mode: write consent immediately, dispatch event, skip any banner\n await this.submitConsent(gpcConsent, true)\n this.finishInit()\n return\n }\n\n // Non-strict: only apply GPC denial on first visit.\n // If the user has already explicitly given consent, respect that choice and\n // fall through to normal banner logic (which will skip the banner since\n // hasExisting is true). Re-running the GPC write on every page load would\n // overwrite the user's explicit consent after they've reviewed via the GPC banner.\n if (!hasExisting) {\n // Persist GPC denial immediately so cookies are dropped now, then show\n // the GPC banner for the user to review/adjust.\n // BroadcastChannel is same-window-exempt, so the banner in this tab is\n // not hidden by the consentUpdated handler received in other tabs.\n const gpcSigningKey = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n const gpcMeta = { gpcDetected: true, locale: this.config.core.locale ?? 'en', complianceGroup: profile.complianceGroup }\n if (gpcSigningKey) {\n this.lastSignature = await this.consentStore.writeAndSign(gpcConsent, gpcSigningKey, gpcMeta)\n } else {\n this.consentStore.write(gpcConsent, gpcMeta)\n }\n this.broadcastChannel.send({\n type: 'consentUpdated',\n consent: gpcConsent,\n profileId: this.profileId(),\n })\n this.eventBus.dispatch('consenti:consentSubmitted', {\n consentJson: gpcConsent,\n gpcDetected: true,\n consentId: generatePrefixedId('cons'),\n consentAction: this.deriveAction(gpcConsent, false),\n pageUrl: window.location.href,\n visitorId: this.visitorId(),\n timestamp: Math.round(Date.now() / 1000),\n profileId: this.profileId(),\n })\n this.showBanner(true)\n this.finishInit()\n return\n }\n }\n\n // Derive opt-out mode from profile complianceGroup (replaces old core.regulation field)\n const compGroup = profile.complianceGroup ?? ''\n const isOptOut = compGroup === 'opt-out'\n const isOptOutStrict = compGroup === 'opt-out-strict'\n\n this.getRootElement()?.setAttribute(\"data-consenti-cg\", compGroup)\n if (this.config.verbose) {\n this.getRootElement()?.setAttribute(\"data-consenti-gpc\", profile.gpcMode ?? \"\")\n }\n\n // CCPA opt-out: silently write all-granted on first visit; no banner. Regions with a\n // `requiresSensitiveOptIn` carve-out (e.g. Colorado) deny cpraCategory:'sensitive' cookies\n // instead — same silent-no-banner treatment, just opt-in for that one category, mirroring\n // how the opt-out-strict (CPRA) branch below already treats sensitive data.\n if (isOptOut && !hasExisting) {\n const defaultConsent: ConsentValue = {}\n for (const [cookieId, cookie] of Object.entries(profile.cookies)) {\n if (profile.requiresSensitiveOptIn && cookie.cpraCategory === 'sensitive' && !isMandatoryCookie(cookieId, this.categoryIndex)) {\n defaultConsent[cookieId] = 'denied'\n } else {\n defaultConsent[cookieId] = 'granted'\n }\n }\n const ccpaSigKey = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n const ccpaMeta = { locale: this.config.core.locale ?? 'en', complianceGroup: profile.complianceGroup }\n if (ccpaSigKey) {\n await this.consentStore.writeAndSign(defaultConsent, ccpaSigKey, ccpaMeta)\n } else {\n this.consentStore.write(defaultConsent, ccpaMeta)\n }\n this.finishInit()\n return\n }\n\n // CPRA: GPC active → treat it as if \"Do Not Sell/Share\" had been clicked (CPRA Regs\n // §7025): deny sale/sharing + sensitive, write immediately, show the GPC banner so the\n // visitor sees what was applied. GPC inactive → no silent default; show the main banner\n // and let the visitor decide, same as opt-in groups.\n if (isOptOutStrict && !hasExisting) {\n if (!gpcActive) {\n this.showBanner()\n this.finishInit()\n return\n }\n\n const defaultConsent: ConsentValue = {}\n for (const [cookieId, cookie] of Object.entries(profile.cookies)) {\n if (isMandatoryCookie(cookieId, this.categoryIndex)) { defaultConsent[cookieId] = 'granted'; continue }\n const denyByDefault = cookie.cpraCategory === 'sensitive' || cookie.cpraCategory === 'sale' || cookie.cpraCategory === 'sharing'\n defaultConsent[cookieId] = denyByDefault ? 'denied' : 'granted'\n }\n const sigKey = profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n const cpraMeta = { gpcDetected: true, locale: this.config.core.locale ?? 'en', complianceGroup: profile.complianceGroup }\n if (sigKey) {\n this.lastSignature = await this.consentStore.writeAndSign(defaultConsent, sigKey, cpraMeta)\n } else {\n this.consentStore.write(defaultConsent, cpraMeta)\n }\n this.broadcastChannel.send({ type: 'consentUpdated', consent: defaultConsent, profileId: this.profileId() })\n this.eventBus.dispatch('consenti:consentSubmitted', {\n consentJson: defaultConsent,\n gpcDetected: true,\n consentId: generatePrefixedId('cons'),\n consentAction: this.deriveAction(defaultConsent, false),\n pageUrl: window.location.href,\n visitorId: this.visitorId(),\n timestamp: Math.round(Date.now() / 1000),\n profileId: this.profileId(),\n })\n this.showBanner(true)\n this.finishInit()\n return\n }\n\n // DPDPA: opt-in (same as GDPR — show banner). GPC is not a recognised opt-out under DPDPA.\n // (GDPR and DPDPA both fall through to showBanner below.)\n\n if (!hasExisting) {\n this.showBanner()\n }\n\n this.finishInit()\n }\n\n /** Mounts the age-gate prompt (`profile.ageGate`) in place of the normal banner flow. */\n private showAgeGate(ageGateConfig: AgeGateConfig, profile: ResolvedProfile): void {\n if (!this.renderer) return\n const minimumAge = ageGateConfig.minimumAge\n const root = this.renderer.mount(this.config.rootEl)\n this.ageGate = new AgeGate()\n const promptEl = this.ageGate.buildPrompt(\n minimumAge,\n () => void this.resolveAgeGate(true, ageGateConfig, profile),\n () => void this.resolveAgeGate(false, ageGateConfig, profile),\n profile.ageGateModal,\n )\n const overlayEl = this.ageGate.getOverlay()\n if (overlayEl) root.appendChild(overlayEl)\n root.appendChild(promptEl)\n }\n\n /**\n * Handles the visitor's age-gate answer.\n * - Confirmed → records `ageVerified: true` for all future `submitConsent` calls and\n * resumes the normal init flow via `continueInit()`.\n * - Declined, no parental consent required → immediately submits a deny-all consent\n * (mandatory cookies still granted), mirroring the GPC-strict silent-write pattern.\n * - Declined, parental consent required → same deny-all submission, plus a\n * `parentalConsentToken` and a `consenti:parentalConsentRequired` event so the site\n * owner can wire their own out-of-band verification flow (no email infra exists in\n * this package to do that here).\n */\n private async resolveAgeGate(\n confirmed: boolean,\n ageGateConfig: AgeGateConfig,\n profile: ResolvedProfile,\n ): Promise<void> {\n this.ageGate?.remove()\n this.ageGate = null\n this._ageVerified = confirmed\n\n if (confirmed) {\n await this.continueInit(profile, detectGPC(), false)\n return\n }\n\n if (ageGateConfig.requireParentalConsent) {\n await this.submitConsent(this._buildDenyAllConsent())\n\n // Server-issued, potentially signed token when a backend is configured — the ownership\n // cookie the request route checks was just set by submitConsent() above. Falls back to a\n // client-generated, unsigned token (the old behavior) when there's no backend to ask.\n let token = generatePrefixedId('pcon')\n if (this.config.api?.enabled) {\n try {\n const base = this.config.api.baseUrl ?? window.location.origin\n const { httpRequest } = await import('../utils/http')\n const response = await httpRequest<{ token: string }>(\n `${base}/consenti/api/v1/consent/${this.visitorId()}/parental-consent-request`,\n { method: 'POST', body: JSON.stringify({ profileId: this.profileId() }) },\n this.config.api.authToken,\n )\n token = response.token\n } catch (err) {\n logger.warn('Parental-consent request failed; using local unsigned token:', err)\n }\n }\n this._parentalConsentToken = token\n this.eventBus?.dispatch('consenti:parentalConsentRequired', {\n parentalConsentToken: this._parentalConsentToken,\n profileId: this.profileId(),\n visitorId: this.visitorId(),\n timestamp: Math.round(Date.now() / 1000),\n })\n if (this.renderer) {\n const root = this.renderer.mount(this.config.rootEl)\n this.ageGate = new AgeGate()\n const msgEl = this.ageGate.buildParentalConsentMessage(() => {\n this.ageGate?.remove()\n this.ageGate = null\n }, profile.ageGateModal)\n root.appendChild(msgEl)\n }\n } else {\n await this.submitConsent(this._buildDenyAllConsent())\n }\n this.finishInit()\n }\n\n /**\n * Marks the widget ready and runs the declarative DOM-scan for\n * `data-consenti-consent-script`/`data-consenti-category-script` tags. Called from\n * every exit path of `init()` so the scan always runs exactly once per init cycle,\n * whether `init()` was triggered automatically (`autoInit !== false`) or explicitly\n * by the caller (`autoInit: false`) — it never runs before the widget actually has\n * a resolved profile to read consent from.\n */\n private finishInit(): void {\n this.initialized = true\n this.readyResolve()\n this.scannedScripts.push(...scanConsentScripts(this))\n }\n\n /** Installs the inbound `consenti:listener:identify` window listener — the event-driven\n * counterpart to `setUserId()`. Removes any previously-installed listener first so repeated\n * `init()` calls (e.g. from `switchLocale()`) don't stack up duplicate listeners. */\n private _installIdentifyListener(): void {\n if (!isClient()) return\n if (this._identifyListener) window.removeEventListener('consenti:listener:identify', this._identifyListener)\n const listener: EventListener = (e) => {\n const detail = (e as CustomEvent<IdentifyEventDetail>).detail\n void this.setUserId(detail.userId, detail.reConsent ?? true)\n }\n this._identifyListener = listener\n window.addEventListener('consenti:listener:identify', listener)\n }\n\n /** Returns the current logged-in application user ID, or `null` for an anonymous visitor. */\n getUserId(): string | null {\n return this.consentStore?.getAppUserId() ?? this.config.core.userId ?? null\n }\n\n /**\n * Sets the logged-in application user ID (`null` for anonymous/logout). If a stored consent\n * record exists and its user differs from `userId`, this is treated as a different data\n * subject on a shared device: by default it reconsents (deletes the existing record and\n * re-shows the banner) and logs a warning. Pass `reConsent: false` to just update the identity\n * without prompting again (still logs the warning). No-ops entirely — no warning, no\n * reconsent — when there is no prior consent record to compare against (true first-time visitor).\n */\n async setUserId(userId: string | null, reConsent = true): Promise<void> {\n if (!isClient() || !this.consentStore) return\n const previous = this.consentStore.getAppUserId() ?? ''\n const next = userId ?? ''\n if (previous === next) return\n\n if (userId === null) delete this.config.core.userId\n else this.config.core.userId = userId\n this.consentStore.setAppUserId(userId ?? undefined)\n\n if (!this.consentStore.hasConsent()) return // no prior consent record — nothing to reconcile\n\n logger.warn('User identity changed — ' + (reConsent ? 'reconsenting.' : 'consent record left as-is (reConsent: false).'))\n if (reConsent) await this.reConsent()\n }\n\n /**\n * Snapshot of the current visitor's identity. `visitorId` is the stable per-browser ID —\n * `null` until a consent decision has actually happened, since this never mints one just to\n * answer the call (see `getOrCreateVisitorId()`). `type` is `'authenticated'` whenever a\n * `userId` is set (via `setUserId()` or `core.userId`), `'anonymous'` otherwise.\n */\n getVisitor(): VisitorIdentity {\n const userId = this.getUserId()\n return {\n visitorId: peekVisitorId(this.config.core.storage ?? 'cookie'),\n type: userId ? 'authenticated' : 'anonymous',\n userId,\n }\n }\n\n /** Sets the `dir` attribute on the root element from `core.dir` (default `'auto'`, derived from `core.locale`). */\n private applyDirection(): void {\n if (!this.renderer) return\n const root = this.renderer.mount(this.config.rootEl)\n root.setAttribute('dir', resolveTextDirection(this.config.core.locale ?? 'en', this.config.core.dir))\n }\n\n /** Applies the `consenti-root--dark` class based on config or profile flag. */\n private applyDarkMode(): void {\n if (!this.renderer) return\n const isDark = this.config.darkMode ?? this.profile?.darkMode ?? false\n const root = this.renderer.mount(this.config.rootEl)\n if (isDark) {\n root.classList.add('consenti-root--dark')\n } else {\n root.classList.remove('consenti-root--dark')\n }\n }\n\n /** Applies `consenti--enhanced-a11y` class when `profile.enhanceAccessibility` is set. */\n private applyAccessibility(): void {\n if (!this.renderer) return\n const root = this.renderer.mount(this.config.rootEl)\n if (this.profile?.enhanceAccessibility) {\n root.classList.add('consenti--enhanced-a11y')\n } else {\n root.classList.remove('consenti--enhanced-a11y')\n }\n }\n\n /** Builds and appends the metadata footer strip to modal container. */\n private _appendMetaFooter(container: HTMLElement): void {\n const footer = document.createElement('div')\n footer.className = 'consenti-meta-footer'\n\n const consentId = this.consentStore?.getConsentId()\n if (consentId) {\n const idEl = document.createElement('span')\n idEl.className = 'consenti-meta-footer__item'\n idEl.title = consentId\n idEl.innerHTML = `ID: <code class=\"consenti-meta-footer__id\">${consentId.slice(0, 10)}&hellip;</code>`\n footer.appendChild(idEl)\n }\n\n if (this.hasConsent()) {\n const visitorId = this.visitorId();\n const visitorEl = document.createElement('span')\n visitorEl.className = 'consenti-meta-footer__item'\n visitorEl.title = visitorId;\n visitorEl.innerHTML = `Visitor: <code class=\"consenti-meta-footer__id\">${visitorId.slice(0, 10)}&hellip;</code>`\n footer.appendChild(visitorEl)\n }\n\n if (this.profile?.id) {\n const profileEl = document.createElement('span')\n profileEl.className = 'consenti-meta-footer__item'\n profileEl.title = this.profile.id;\n profileEl.innerHTML = `Profile: <code class=\"consenti-meta-footer__id\">${this.profile.id.slice(0, 10)}&hellip; (v${this.profile?.version ?? 1})</code>`\n footer.appendChild(profileEl)\n }\n\n const consentDate = this.consentStore?.getConsentDate()\n if (consentDate) {\n const dateEl = document.createElement('span')\n dateEl.className = 'consenti-meta-footer__item'\n dateEl.innerHTML = `Date: <code class=\"consenti-meta-footer__id\">${consentDate.toLocaleDateString()}</code>`\n footer.appendChild(dateEl)\n }\n\n container.appendChild(footer)\n }\n\n /**\n * Injects a `<style>` element into the root for `stackButtonsOnBreakpoint`.\n * Called once per profile load; a new style replaces the previous one.\n */\n private applyStackButtonsBreakpoint(): void {\n if (!this.renderer || !this.profile) return\n let bp = this.profile.mainBanner?.stackButtonsOnBreakpoint\n if (!bp || bp <= 0) return\n const root = this.renderer.mount(this.config.rootEl)\n const existing = root.querySelector('#consenti-stack-btn-style')\n existing?.remove()\n const style = document.createElement('style')\n style.id = 'consenti-stack-btn-style'\n style.textContent =\n `@media(max-width:${bp}px){` +\n `.consenti-banner__buttons{flex-direction:column;}` +\n `.consenti-banner__btn{width:100%;}` +\n `}`\n root.appendChild(style)\n }\n\n /** Installs a keyboard focus trap on `container` while the modal is open. */\n private _installFocusTrap(container: HTMLElement): void {\n this._removeFocusTrap()\n const FOCUSABLE = 'button:not([disabled]),[href],input:not([disabled]),select,textarea,[tabindex]:not([tabindex=\"-1\"])'\n const listener = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return\n const focusable = Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE))\n .filter(el => !el.closest('[aria-hidden=\"true\"]'))\n if (focusable.length === 0) return\n const first = focusable[0]!\n const last = focusable[focusable.length - 1]!\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault()\n last.focus()\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault()\n first.focus()\n }\n }\n }\n this._focusTrapListener = listener\n document.addEventListener('keydown', listener)\n }\n\n /** Removes the active focus trap keyboard listener. */\n private _removeFocusTrap(): void {\n if (this._focusTrapListener) {\n document.removeEventListener('keydown', this._focusTrapListener)\n this._focusTrapListener = null\n }\n }\n\n /**\n * Injects CSS custom property overrides supplied via `core.theme` into the\n * Consenti root element so they cascade down to all widget elements.\n */\n private applyTheme(): void {\n const theme = this.config.core.theme\n if (!theme || !this.renderer) return\n\n const map: Record<string, string | undefined> = {\n '--consenti-color-bg': theme.colorBg,\n '--consenti-color-text': theme.colorText,\n '--consenti-color-text-muted': theme.colorTextMuted,\n '--consenti-color-primary': theme.colorPrimary,\n '--consenti-color-primary-text': theme.colorPrimaryText,\n '--consenti-color-secondary': theme.colorSecondary,\n '--consenti-color-secondary-text': theme.colorSecondaryText,\n '--consenti-color-border': theme.colorBorder,\n '--consenti-color-secondary-border': theme.colorSecondaryBorder,\n '--consenti-color-overlay': theme.colorOverlay,\n '--consenti-color-accent': theme.colorAccent,\n '--consenti-color-accent-text': theme.colorAccentText,\n '--consenti-font-family': theme.fontFamily,\n '--consenti-font-family-mono': theme.fontFamilyMono,\n '--consenti-font-size-base': theme.fontSizeBase,\n '--consenti-font-size-heading': theme.fontSizeHeading,\n '--consenti-font-weight-heading': theme.fontWeightHeading,\n '--consenti-line-height': theme.lineHeight,\n '--consenti-spacing-xs': theme.spacingXs,\n '--consenti-spacing-sm': theme.spacingSm,\n '--consenti-spacing-md': theme.spacingMd,\n '--consenti-spacing-lg': theme.spacingLg,\n '--consenti-border-radius': theme.borderRadius,\n '--consenti-border-radius-btn': theme.borderRadiusBtn,\n '--consenti-shadow': theme.shadow,\n '--consenti-toggle-bg-on': theme.toggleBgOn,\n '--consenti-toggle-bg-partial': theme.toggleBgPartial,\n '--consenti-toggle-bg-off': theme.toggleBgOff,\n '--consenti-toggle-knob': theme.toggleKnob,\n '--consenti-toggle-width': theme.toggleWidth,\n '--consenti-toggle-height': theme.toggleHeight,\n '--consenti-z-banner': theme.zBanner,\n '--consenti-z-overlay': theme.zOverlay,\n '--consenti-z-modal': theme.zModal,\n }\n\n const root = this.renderer.mount(this.config.rootEl)\n for (const [prop, value] of Object.entries(map)) {\n if (value !== undefined) root.style.setProperty(prop, value)\n }\n\n if (theme.fontSizeMultiplier) {\n // Multiply base and heading font sizes if a multiplier is given\n const mult = parseFloat(theme.fontSizeMultiplier)\n if (!isNaN(mult)) {\n const baseSize = parseFloat(getComputedStyle(root).getPropertyValue('--consenti-font-size-base') || '14')\n const headingSize = parseFloat(getComputedStyle(root).getPropertyValue('--consenti-font-size-heading') || '16')\n root.style.setProperty('--consenti-font-size-base', `${Math.round(baseSize * mult)}px`)\n root.style.setProperty('--consenti-font-size-heading', `${Math.round(headingSize * mult)}px`)\n }\n }\n }\n\n private async runPlugins(): Promise<void> {\n this.plugins = this.config.plugins ?? []\n for (const plugin of this.plugins) {\n try {\n await plugin.initialize(this)\n } catch (err) {\n logger.warn('Plugin initialize error:', err)\n }\n }\n }\n\n /**\n * Invokes an optional plugin hook on all registered plugins.\n * Async hooks are awaited; errors are caught and logged so a broken plugin\n * never interferes with the consent flow.\n *\n * @param invoke - Function that calls the desired hook method on a single plugin.\n */\n private async callPluginHook(invoke: (plugin: ConsentiPlugin, widget: ConsentiWidgetAPI) => void | Promise<void>): Promise<void> {\n for (const plugin of this.plugins) {\n try {\n await invoke(plugin, this)\n } catch (err) {\n logger.warn('Plugin hook failed:', err)\n }\n }\n }\n\n private _buildGrantAllConsent(onlyMandatory = false): ConsentValue {\n const consent: ConsentValue = {}\n for (const cookieId of Object.keys(this.profile?.cookies ?? {})) {\n consent[cookieId] = onlyMandatory && !isMandatoryCookie(cookieId, this.categoryIndex) ? 'denied' : 'granted'\n }\n return consent\n }\n\n private _buildDenyAllConsent(includingMandatory = false): ConsentValue {\n const consent: ConsentValue = {}\n for (const cookieId of Object.keys(this.profile?.cookies ?? {})) {\n consent[cookieId] = !includingMandatory && isMandatoryCookie(cookieId, this.categoryIndex) ? 'granted' : 'denied'\n }\n return consent\n }\n\n private buildHandlers(context: 'banner' | 'modal'): ButtonClickHandler {\n return {\n onGrantAll: () => {\n void this.submitConsent(this._buildGrantAllConsent())\n if (context === 'banner') { this.banner?.hide(); this._bannerVariant = null }\n },\n onDenyAll: () => {\n void this.submitConsent(this._buildDenyAllConsent())\n if (context === 'banner') { this.banner?.hide(); this._bannerVariant = null }\n },\n onSubmit: () => {\n if (context === 'modal' && this.modal && this.profile) {\n const consent = this.modal.getToggleConsent(this.profile.preferenceModal.categories)\n void this.submitConsent(consent)\n this.hideModal()\n } else {\n const consent: ConsentValue = {}\n for (const cookieId of Object.keys(this.profile?.cookies ?? {})) {\n if (isMandatoryCookie(cookieId, this.categoryIndex)) consent[cookieId] = 'granted'\n }\n void this.submitConsent(consent)\n if (context === 'banner') { this.banner?.hide(); this._bannerVariant = null }\n }\n },\n onManage: () => {\n this.showModal()\n },\n onClose: () => {\n if (context === 'modal') {\n this.hideModal()\n } else {\n this.banner?.hide()\n this.eventBus?.dispatch('consenti:bannerVisibility', { visible: false, action: true, variant: this._bannerVariant ?? 'main' })\n this._bannerVariant = null\n this.broadcastChannel.send({ type: 'bannerClosed' })\n void this.callPluginHook((p) => p.onBannerHide?.())\n }\n },\n onGrantSpecific: (cookieIds: string[]) => {\n const consent: ConsentValue = {}\n for (const cookieId of Object.keys(this.profile?.cookies ?? {})) {\n if (isMandatoryCookie(cookieId, this.categoryIndex) || cookieIds.includes(cookieId)) {\n consent[cookieId] = 'granted'\n } else {\n consent[cookieId] = 'denied'\n }\n }\n void this.submitConsent(consent)\n if (context === 'banner') this.hideBanner()\n },\n onForgetMe: () => {\n if (context === 'modal') this.hideModal()\n void this.forgetMe()\n },\n }\n }\n\n /**\n * Classifies a consent submission as `accept_all`, `reject_all`, `custom`, or `update`.\n * Used to enrich events and API payloads with intent metadata.\n */\n private deriveAction(fullConsent: ConsentValue, hadExistingConsent: boolean): ConsentAction {\n if (hadExistingConsent) return 'update'\n const nonMandatory = Object.keys(this.profile?.cookies ?? {}).filter((id) => !isMandatoryCookie(id, this.categoryIndex))\n if (nonMandatory.length === 0) return 'custom'\n const allGranted = nonMandatory.every((id) => fullConsent[id] === 'granted')\n const allDenied = nonMandatory.every(\n (id) => fullConsent[id] === 'denied' || fullConsent[id] === 'objected',\n )\n if (allGranted) return 'accept_all'\n if (allDenied) return 'reject_all'\n return 'custom'\n }\n\n private handleBroadcastMessage(msg: ConsentiMessage): void {\n switch (msg.type) {\n case 'consentUpdated':\n if (msg.profileId !== this.profileId()) return\n this.consentStore?.setInMemory(msg.consent)\n this.banner?.hide()\n if (this.modal?.isOpen()) this.modal.close()\n this.eventBus?.dispatch('consenti:consentSubmitted', {\n consentJson: msg.consent,\n fromBroadcast: true,\n gpcDetected: detectGPC(),\n consentId: generatePrefixedId('cons'),\n consentAction: this.deriveAction(msg.consent, false),\n pageUrl: window.location.href,\n visitorId: this.visitorId(),\n timestamp: Math.round(Date.now() / 1000),\n profileId: this.profileId(),\n })\n break\n case 'bannerClosed':\n this.banner?.hide()\n this._bannerVariant = null\n break\n case 'consentDeleted':\n this.consentStore?.clearInMemory()\n break\n }\n }\n\n // ─── Public API ──────────────────────────────────────────────────────────────\n\n /**\n * Returns `true` if the visitor has saved consent for the active profile.\n * Always returns `false` during SSR.\n */\n hasConsent(): boolean {\n if (!isClient()) return false\n return this.consentStore?.hasConsent() ?? false\n }\n\n /**\n * Returns the stored consent map, optionally transformed for a specific vendor.\n * Pass `type` to get a vendor-ready object instead of the raw Consenti format.\n * Always returns `null` during SSR or when no consent has been saved.\n *\n * @param type - Output format:\n * - omit / `'default'` → raw ConsentValue, keyed by every cookie parameter ID in the profile\n * - `'purpose'` → consent per the fixed CookiePurpose taxonomy (necessary/functional/preferences/analytics/marketing)\n * - `'category'` → consent per the tenant's own authored preference-modal category, keyed by category ID\n * ('granted' only when every member parameter is granted; 'denied'/'objected' otherwise)\n * - `'google-gtm'` → Google Consent Mode v2 keys + ads_data_redaction, url_passthrough\n * - `'adobe'` → { analytics, target, manager, optimizer }\n * - `'meta'` → { pixel, api, plugins, facebookLogin }\n * - `'microsoft-clarity'` → { session, heatmaps, performance }\n * - `'twilio-segment'` → { identify, page, track, group, alias }\n */\n getConsent(): ConsentValue | null\n getConsent(type: ConsentType): Record<string, string> | null\n getConsent(type?: ConsentType): ConsentValue | Record<string, string> | null {\n if (!isClient()) return null\n const consent = this.consentStore?.getConsent() ?? null\n if (!consent) return null\n const cookies = this.profile?.cookies ?? {}\n\n switch (type) {\n case 'google-gtm': return getGoogleGTMConsent(consent)\n case 'purpose': return getPurposeConsent(consent, cookies) as unknown as Record<string, string>\n case 'category': return getCategoryConsent(consent, this.profile?.preferenceModal?.categories ?? {}) as unknown as Record<string, string>\n case 'adobe': return getAdobeConsent(consent, cookies) as unknown as Record<string, string>\n case 'meta': return getMetaConsent(consent, cookies) as unknown as Record<string, string>\n case 'microsoft-clarity': return getMicrosoftClarityConsent(consent, cookies) as unknown as Record<string, string>\n case 'twilio-segment': return getTwilioSegmentConsent(consent, cookies) as unknown as Record<string, string>\n default: return consent\n }\n }\n\n /**\n * Returns the `Date` when consent was last saved, or `false` if no consent exists.\n * Always returns `false` during SSR.\n */\n getConsentDate(): Date | false {\n if (!isClient()) return false\n return this.consentStore?.getConsentDate() ?? false\n }\n\n /**\n * @deprecated Use `getConsent('google-gtm')` instead.\n * Returns the consent map in Google Consent Mode v2 format.\n */\n getGTMConsent(): Record<string, string> | null {\n return this.getConsent('google-gtm') as Record<string, string> | null\n }\n\n /**\n * Records and persists the user's consent choices.\n *\n * Mandatory cookies are always forced to `'granted'` regardless of what is passed.\n * Non-mandatory cookies not present in `consent` default to `'denied'`.\n *\n * @param consent - Partial or full map of cookie ID → status.\n * @param gpcDerived - Internal flag set to `true` when called from the GPC strict path.\n */\n async submitConsent(consent: Partial<ConsentValue>, gpcDerived = false, source = 0): Promise<ConsentDbRecord | void> {\n if (!isClient() || !this.profile || !this.consentStore) return\n\n const compGroup = this.profile.complianceGroup ?? ''\n const hadExistingConsent = this.consentStore.hasConsent()\n // Reuse the existing local consent record id across resubmissions (rather than minting a\n // new one every time) so the cookie's `i` field keeps identifying the same logical\n // consent record. `this.visitorId()` (not this id) is what identifies the visitor to the\n // server and drives the DELETE/PUT ownership check — it's stable independent of consent.\n const consentId = this.consentStore.getConsentId() ?? generatePrefixedId('cons')\n const pageUrl = window.location.href\n\n const fullConsent: ConsentValue = {}\n for (const [cookieId, cookie] of Object.entries(this.profile.cookies)) {\n if (isMandatoryCookie(cookieId, this.categoryIndex)) {\n fullConsent[cookieId] = 'granted'\n } else if (compGroup === 'opt-out-strict') {\n const fallback = cookie.cpraCategory === 'sensitive' ? 'denied' : 'granted'\n fullConsent[cookieId] = consent[cookieId] ?? fallback\n } else {\n // preGrant only ever loosens the default toward 'granted' — it never forces denial,\n // and only applies when there's no stored decision for this parameter yet.\n const defaultDenied = compGroup !== 'opt-out' && compGroup !== 'notice-only'\n const fallback = cookie.preGrant ? 'granted' : (defaultDenied ? 'denied' : 'granted')\n fullConsent[cookieId] = consent[cookieId] ?? fallback\n }\n }\n\n const consentAction = this.deriveAction(fullConsent, hadExistingConsent)\n\n this.eventBus?.dispatch('consenti:consentBeingSubmitted', {\n consentJson: fullConsent,\n consentId,\n fromBroadcast: false,\n gpcDetected: detectGPC(),\n consentAction: this.deriveAction(fullConsent, false),\n pageUrl: window.location.href,\n visitorId: this.visitorId(),\n timestamp: Math.round(Date.now() / 1000),\n profileId: this.profileId(),\n })\n\n let apiResponse = null as unknown as ConsentDbRecord;\n if (this.config.api?.enabled) {\n try {\n const base = this.config.api.baseUrl ?? window.location.origin\n const { httpRequest } = await import('../utils/http')\n apiResponse = await httpRequest(\n `${base}/consenti/api/v1/consent`,\n {\n method: 'POST',\n body: JSON.stringify({\n profileId: this.profileId(),\n visitorId: this.visitorId(),\n consentJson: fullConsent,\n locale: this.config.core.locale ?? 'en',\n gpcDetected: gpcDerived || detectGPC(),\n consentId,\n action: consentAction,\n pageUrl,\n ...(this._ageVerified !== undefined ? { ageVerified: this._ageVerified } : {}),\n ...(this._parentalConsentToken ? { parentalConsentToken: this._parentalConsentToken } : {}),\n }),\n },\n this.config.api.authToken,\n )\n } catch (err) {\n logger.warn('API consent submission failed:', err)\n }\n }\n\n // Sign and write atomically so the signature covers the exact persisted value\n const signingKey = this.profile.cookieSigningKey ?? this.config.core.cookieSigningKey\n const writeMeta = { source, gpcDetected: gpcDerived, consentId, locale: this.config.core.locale ?? 'en', complianceGroup: this.profile?.complianceGroup }\n if (signingKey) {\n this.lastSignature = await this.consentStore.writeAndSign(fullConsent, signingKey, writeMeta)\n } else {\n this.consentStore.write(fullConsent, writeMeta)\n }\n\n const receiptRequested = this.modal?.isReceiptRequested() ?? false\n if (receiptRequested && this.profile.allowReceipt) {\n const visitorId = this.visitorId()\n const currentConsentVersion = this.consentStore.getConsent();\n console.log({ currentConsentVersion });\n const receiptParams = {\n version: '1.0',\n visitorId,\n profileId: this.profileId(),\n locale: this.config.core.locale ?? 'en',\n consent: fullConsent,\n ...(this.lastSignature ? { signature: this.lastSignature } : {}),\n }\n const receipt = generateReceipt(receiptParams)\n downloadReceipt(receipt)\n }\n\n this.broadcastChannel.send({\n type: 'consentUpdated',\n consent: fullConsent,\n profileId: this.profileId(),\n })\n\n this.eventBus?.dispatch('consenti:consentSubmitted', {\n consentJson: fullConsent,\n apiResponse,\n gpcDetected: gpcDerived,\n consentId,\n consentAction,\n pageUrl,\n fromBroadcast: false,\n visitorId: this.visitorId(),\n timestamp: Math.round(Date.now() / 1000),\n profileId: this.profileId(),\n })\n\n await this.callPluginHook((p) => p.onConsentSubmit?.(fullConsent))\n\n this.renderer?.announceToScreenReader('Your cookie preferences have been saved.')\n\n // Hide any visible UI after consent is saved so programmatic submitConsent()\n // calls also dismiss the banner/modal, not just button-triggered submissions.\n this.hideBanner()\n if (this.modal?.isOpen()) this.hideModal()\n\n return apiResponse;\n }\n\n /**\n * Returns the currently visible banner variant, or `false` if no banner is shown.\n * Useful for programmatically checking state before calling `showBanner` / `hideBanner`.\n */\n bannerVisibility(): 'main' | 'gpc' | false {\n return this._bannerVariant ?? false\n }\n\n /**\n * Returns `'preference'` when the preference modal is open, or `false` when closed.\n */\n modalVisibility(): 'preference' | false {\n return this.modal?.isOpen() ? 'preference' : false\n }\n\n /**\n * Mounts and shows the consent banner.\n *\n * No-ops if the requested variant is already visible. If a different variant is\n * currently shown (e.g. switching from main to GPC), the existing banner is replaced.\n *\n * @param useGpcVariant - When `true` and the profile has a `gpcBanner`, that variant\n * is shown instead of `mainBanner`. Used by the GPC flow.\n */\n showBanner(useGpcVariant = false): void {\n if (!isClient() || !this.profile || !this.banner || !this.renderer) return\n const variant: 'main' | 'gpc' = useGpcVariant && this.profile.gpcBanner ? 'gpc' : 'main'\n\n // Skip if the same variant is already visible.\n if (this._bannerVariant === variant) return\n\n // If the same variant is already mounted (just hidden), reuse the DOM element.\n if (this._bannerMounted === variant) {\n this.banner.show()\n this._bannerVariant = variant\n this.eventBus?.dispatch('consenti:bannerVisibility', { visible: true, action: false, variant })\n void this.callPluginHook((p) => p.onBannerShow?.())\n return\n }\n\n const bannerConfig = variant === 'gpc' ? this.profile.gpcBanner! : this.profile.mainBanner\n const root = this.renderer.mount(this.config.rootEl)\n const handlers = this.buildHandlers('banner')\n\n // Remove any previously rendered banner before building a new one so that we do\n // not accumulate duplicate elements and so new button handlers are correctly wired.\n this.banner.remove()\n\n const locales = this.profile.locales\n const activeLocale = this.config.core.locale ?? 'en'\n const bannerEl = this.banner.build(\n bannerConfig,\n handlers,\n undefined,\n locales,\n activeLocale,\n (locale) => this.switchLocale(locale),\n this.config.hidePoweredBy ?? this.profile?.hidePoweredBy ?? true,\n variant,\n )\n\n root.appendChild(bannerEl)\n const overlay = this.banner.getOverlay()\n if (overlay) root.appendChild(overlay)\n this._bannerVariant = variant\n this._bannerMounted = variant\n this.eventBus?.dispatch('consenti:bannerVisibility', { visible: true, action: false, variant })\n void this.callPluginHook((p) => p.onBannerShow?.())\n }\n\n /** Hides the banner and notifies other tabs via BroadcastChannel. No-op if already hidden. */\n hideBanner(): void {\n if (this._bannerVariant === null) return\n this.banner?.hide()\n this.eventBus?.dispatch('consenti:bannerVisibility', { visible: false, action: true, variant: this._bannerVariant })\n this._bannerVariant = null\n this.broadcastChannel.send({ type: 'bannerClosed' })\n this.renderer?.announceToScreenReader('Cookie banner dismissed.')\n void this.callPluginHook((p) => p.onBannerHide?.())\n }\n\n /**\n * Opens the preference modal and hides the banner while the modal is open.\n * No-ops if the modal is already open.\n * If the modal is closed without saving consent, the banner is re-shown automatically\n * (see `hideModal()`). Only one banner or modal is visible at a time.\n *\n * @param triggerEl - The element that triggered the modal open. Focus returns here on close.\n */\n showModal(triggerEl?: HTMLElement): void {\n if (!isClient() || !this.profile || !this.modal) return\n if (this.modal.isOpen()) return\n\n const handlers = this.buildHandlers('modal')\n const existing = this.consentStore?.getConsent() ?? {}\n const dpdpaCfg = this.profile.complianceGroup === 'opt-in-dpdpa' ? this.profile.dpdpa : undefined\n const locales = this.profile.locales\n const activeLocale = this.config.core.locale ?? 'en'\n this.modal.build(\n this.profile.preferenceModal,\n this.profile.cookies,\n existing,\n handlers,\n this.profile.allowReceipt ?? false,\n dpdpaCfg,\n locales,\n activeLocale,\n (locale) => { this.hideModal(); this.switchLocale(locale) },\n (this.config.hidePoweredBy ?? this.profile?.hidePoweredBy ?? true),\n )\n // Hide banner while modal is open so only one panel is visible at a time.\n this.banner?.hide()\n this.modal.open(triggerEl, this.renderer?.mount(this.config.rootEl))\n const modalEl = document.getElementById('consenti-modal')\n if (this.profile.showFooterMetadata && modalEl) {\n const container = modalEl.querySelector<HTMLElement>('.consenti-modal__container')\n if (container) this._appendMetaFooter(container)\n }\n if (this.profile.preferenceModal?.trapFocus && modalEl) {\n this._installFocusTrap(modalEl)\n }\n if (this._bannerVariant !== null) {\n this.eventBus?.dispatch('consenti:bannerVisibility', { visible: false, action: triggerEl ? true : false, variant: this._bannerVariant })\n }\n this.eventBus?.dispatch('consenti:modalVisibility', { visible: true, action: triggerEl ? true : false })\n void this.callPluginHook((p) => p.onModalShow?.())\n }\n\n /**\n * Closes the preference modal and returns focus to the element that opened it.\n * No-ops if the modal is already closed.\n * If the user has not yet given consent, the banner is re-shown automatically.\n */\n hideModal(): void {\n if (!this.modal?.isOpen()) return\n this._removeFocusTrap()\n this.modal.close()\n this.eventBus?.dispatch('consenti:modalVisibility', { visible: false, action: true })\n void this.callPluginHook((p) => p.onModalHide?.())\n\n // If no consent on record, bring the banner back so the user still sees the notice.\n if (!this.consentStore?.hasConsent() && this._bannerVariant !== null) {\n this.eventBus?.dispatch('consenti:bannerVisibility', { visible: true, action: true, variant: this._bannerVariant })\n this.banner?.show()\n } else if (!this.consentStore?.hasConsent()) {\n this.showBanner()\n }\n }\n\n /**\n * Registers a callback to run once the widget has finished initialising.\n * If the widget is already initialised, the callback is invoked synchronously.\n *\n * @param callback - Function to call when the widget is ready.\n */\n onReady(callback: () => void): void {\n if (this.initialized) {\n callback()\n } else {\n void this.readyPromise.then(callback)\n }\n }\n\n /**\n * A `Promise` that resolves once the widget has finished initialising.\n * Equivalent to wrapping `onReady()` in a `Promise`.\n *\n * @example\n * ```ts\n * await widget.ready\n * console.log('Widget ready')\n * ```\n */\n get ready(): Promise<void> {\n return this.readyPromise\n }\n\n /**\n * Deletes the stored consent record and notifies other tabs.\n * The banner will reappear on the next page load (or immediately if the\n * receiving tab calls `showBanner()` in response to `consentDeleted`).\n */\n async deleteConsent(): Promise<void> {\n if (!isClient() || !this.consentStore) return\n\n if (this.config.api?.enabled) {\n try {\n const base = this.config.api.baseUrl ?? window.location.origin\n const { httpRequest } = await import('../utils/http')\n await httpRequest(\n `${base}/consenti/api/v1/consent/${this.visitorId()}`,\n { method: 'DELETE' },\n this.config.api?.authToken,\n )\n } catch (err) {\n logger.warn('API consent deletion failed:', err)\n }\n }\n\n this.consentStore.delete()\n this.broadcastChannel.send({ type: 'consentDeleted' })\n }\n\n /**\n * Removes existing consent and immediately re-shows the consent banner so the user\n * can review and update their preferences.\n *\n * Equivalent to calling `deleteConsent()` followed by `showBanner()`, but in a single\n * method so callers do not need to coordinate the async deletion with the banner display.\n *\n * Use this to power \"Review my consent\" links in footers or privacy dashboards.\n *\n * @param resetAgeGate - When `true` (default) and the profile has `ageGate.enabled`, the\n * age-gate prompt is shown again instead of jumping straight to the\n * banner — mirroring the original first-visit flow. Pass `false` to\n * skip it and go straight to the banner, keeping the visitor's prior\n * age-gate answer.\n */\n async reConsent(resetAgeGate = true): Promise<void> {\n await this.deleteConsent()\n if (resetAgeGate && this.profile?.ageGate?.enabled) {\n this._ageVerified = undefined\n this.showAgeGate(this.profile.ageGate, this.profile)\n return\n }\n this.showBanner()\n }\n\n /**\n * Self-service erasure entry point — GDPR Art. 17 / CCPA-CPRA / LGPD Art. 18 and equivalent\n * \"right to be forgotten\" rights, exercised from the widget itself. Wraps `reConsent()` (same\n * erase-then-reprompt behavior) with `consenti:forgetMeRequested` / `consenti:forgotten`\n * dispatched around it, so a host app can hook its own identity-verified erasure workflow\n * across other systems (CRM, DMP, analytics) — the CMP itself only ever erases its own consent\n * record. See the \"Right to erasure\" guide for the full picture, including the server-side\n * `consent:erased` eventBus event fired by `DELETE /consent/:visitorId`.\n *\n * Powers the preference modal's \"Forget me\" button (`preferenceModal.showForgetMe`, only\n * rendered when the visitor already has a stored consent decision). Also callable directly —\n * e.g. `widget.forgetMe()` from a custom footer link or privacy-settings page.\n *\n * @param resetAgeGate - Forwarded to `reConsent()` — see its docs.\n */\n async forgetMe(resetAgeGate = true): Promise<void> {\n if (!isClient()) return\n const visitorId = this.visitorId()\n const profileId = this.profileId()\n this.eventBus?.dispatch('consenti:forgetMeRequested', { visitorId, profileId, timestamp: Math.round(Date.now() / 1000) })\n await this.reConsent(resetAgeGate)\n this.eventBus?.dispatch('consenti:forgotten', { visitorId, profileId, timestamp: Math.round(Date.now() / 1000) })\n }\n\n /**\n * Returns the fully resolved profile for the current session, or `null` during the\n * async init phase (before the profile has been fetched/resolved).\n *\n * Plugins can use this to read cookie definitions, banner config, modal categories,\n * and the profile version without accessing internal state.\n */\n getProfile(): ResolvedProfile | null {\n return this.profile\n }\n\n /**\n * Returns the `#consenti-root` container element that wraps all widget DOM,\n * or `null` before the widget has mounted or after `destroy()`.\n *\n * Plugins can use this to append custom elements, apply extra CSS variables,\n * or observe mutations within the widget container.\n */\n getRootElement(): HTMLElement | null {\n if (!isClient()) return null\n if (this.config.rootEl) {\n if (typeof this.config.rootEl === 'string') {\n return document.querySelector<HTMLElement>(this.config.rootEl)\n }\n return this.config.rootEl\n }\n return document.getElementById('consenti-root')\n }\n\n /**\n * Returns the `#consenti-banner` element when it is present in the DOM, or `null`.\n * The element may be hidden (`display: none`) — check `bannerVisibility()` for visibility state.\n *\n * Useful for adding extra content, custom event listeners, or mutation observers on the banner.\n */\n getBannerElement(): HTMLElement | null {\n if (!isClient()) return null\n return document.getElementById('consenti-banner')\n }\n\n /**\n * Returns the `#consenti-modal` element when the modal has been built, or `null`.\n * The element is built on first `showModal()` call and reused on subsequent opens.\n *\n * Useful for injecting extra content into the modal or listening to modal-specific events.\n */\n getModalElement(): HTMLElement | null {\n if (!isClient()) return null\n return document.getElementById('consenti-modal')\n }\n\n /**\n * Switches the active locale, re-resolves the profile, and re-renders the widget.\n * Called by the locale switcher UI when the user selects a different language.\n *\n * @param locale - BCP 47 locale code to switch to (e.g. `'fr'`, `'de-AT'`).\n */\n switchLocale(locale: string): void {\n if (!isClient()) return\n // In-memory only for this page session — resets to the configured default on refresh.\n // Only becomes persistent once a consent decision writes it into the `l` field of the\n // consent record itself (see submitConsent()).\n this.config.core.locale = locale\n // Teardown current UI and re-init with new locale\n this.banner?.remove()\n if (this.modal?.isOpen()) this.modal.close()\n this._bannerVariant = null\n this._bannerMounted = null\n this.profile = null\n this.initialized = false\n this.readyPromise = new Promise<void>((resolve) => { this.readyResolve = resolve })\n this.init().catch((err) => logger.warn('Locale switch error:', err))\n }\n\n /**\n * Returns `true` if the named cookie's consent is `'granted'`.\n * When `requestValue` is `true`, returns the raw `ConsentStatus` string instead of a boolean,\n * or `false` if the cookie is not in the consent map.\n */\n isCookieGranted(cookieId: string, requestValue = false): boolean | 'granted' | 'denied' | 'objected' {\n if (!isClient()) return false\n const status = this.getConsent()?.[cookieId]\n if (requestValue) return status ?? false\n return status === 'granted'\n }\n\n /**\n * Checks consent for all cookies belonging to the given category.\n *\n * With `requestValue = false` (default): returns `true` only when every cookie in the\n * category is `'granted'`, `false` otherwise.\n *\n * With `requestValue = true`: returns an array of one-key records — one per cookie in the\n * category — e.g. `[{ analytics: 'granted' }, { pixel: 'denied' }]`.\n *\n * Returns `false` / `[]` if the category is not found or the widget is not yet initialized.\n */\n isCategoryGranted(categoryId: string, requestValue = false): boolean | { [cookieId: string]: 'granted' | 'denied' | 'objected' }[] {\n if (!isClient() || !this.profile) return requestValue ? [] : false\n const category = this.profile.preferenceModal.categories[categoryId]\n if (!category) return requestValue ? [] : false\n\n const consent = this.getConsent()\n\n if (requestValue) {\n return category.cookies.map((cookieId) => ({\n [cookieId]: (consent?.[cookieId] ?? 'denied') as 'granted' | 'denied' | 'objected',\n }))\n }\n\n return category.cookies.every((cookieId) => consent?.[cookieId] === 'granted')\n }\n\n /**\n * Programmatically accepts cookies and hides the banner.\n *\n * @param onlyMandatory - When `true`, only mandatory cookies are granted and all others\n * are set to `'denied'`. Defaults to `false` (grant everything).\n */\n async grantAll(onlyMandatory = false): Promise<void> {\n if (!isClient() || !this.profile) return\n await this.submitConsent(this._buildGrantAllConsent(onlyMandatory), false, 1)\n }\n\n /**\n * Programmatically denies cookies and hides the banner.\n *\n * @param includingMandatory - When `true`, mandatory cookies are also denied. Defaults to\n * `false` (mandatory cookies remain `'granted'`). Passing `true`\n * logs a warning — ensure this is intentional.\n */\n async denyAll(includingMandatory = false): Promise<void> {\n if (!isClient() || !this.profile) return\n if (includingMandatory) {\n logger.warn('denyAll called with includingMandatory=true — mandatory cookies will be denied. Ensure this is intentional.')\n }\n await this.submitConsent(this._buildDenyAllConsent(includingMandatory), false, 1)\n }\n\n /**\n * Subscribes to a Consenti widget event. The `consenti:` prefix is optional —\n * both `'consentSubmitted'` and `'consenti:consentSubmitted'` are accepted.\n *\n * The same handler reference must be passed to `off()` to unsubscribe.\n *\n * @example\n * ```ts\n * const handler = (data) => console.log(data)\n * widget.on('consentSubmitted', handler)\n * // later:\n * widget.off('consentSubmitted', handler)\n * ```\n */\n on(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void {\n if (!isClient()) return\n const name = event.startsWith('consenti:') ? event : `consenti:${event}`\n const wrapped: EventListener = (e) => handler((e as CustomEvent<ConsentEvent>).detail)\n const existing = this._eventHandlers.get(handler) ?? []\n existing.push({ name, wrapped })\n this._eventHandlers.set(handler, existing)\n window.addEventListener(name, wrapped)\n }\n\n /**\n * Removes a previously registered event handler. The same function reference passed to\n * `on()` must be used here.\n */\n off(event: ConsentiEventName, handler: (data: ConsentEvent) => void): void {\n if (!isClient()) return\n const name = event.startsWith('consenti:') ? event : `consenti:${event}`\n const entries = this._eventHandlers.get(handler)\n if (!entries) return\n const idx = entries.findIndex((e) => e.name === name)\n if (idx === -1) return\n const entry = entries[idx]\n if (entry) window.removeEventListener(name, entry.wrapped)\n entries.splice(idx, 1)\n if (entries.length === 0) this._eventHandlers.delete(handler)\n }\n\n /**\n * Returns the current version of the package and the active profile/consent versions.\n * Useful for debugging and support diagnostics.\n */\n version(): { package: string; profileVersion: string; consentVersion: string | null } {\n return {\n package: __CONSENTI_VERSION__,\n profileVersion: this.profile?.id ?? \"0\",\n consentVersion: null,\n }\n }\n\n /**\n * Toggles or sets dark mode at runtime without re-initialising the widget.\n * When `enable` is omitted, the current dark mode state is toggled.\n */\n setDarkMode(enable?: boolean): void {\n if (!isClient()) return\n this.config.darkMode = enable === undefined ? !this.config.darkMode : enable\n this.applyDarkMode()\n }\n\n /**\n * Applies partial theme token overrides at runtime, merging them into the current theme.\n * CSS custom properties on the root element are updated immediately.\n */\n setTheme(theme: Partial<ThemeConfig>): void {\n if (!isClient()) return\n this.config.core.theme = Object.assign({}, this.config.core.theme, theme)\n this.applyTheme()\n }\n\n /**\n * Deep-merges a partial config into the current widget config.\n * Theme and dark mode side-effects are re-applied immediately.\n * Changes to locale or profile require an explicit `switchLocale()` or `init()` call.\n */\n setConfig(config: DeepPartial<ConsentiConfig>): void {\n const merged = deepMerge<ConsentiConfig>(this.config, config)\n this.config = { ...merged, core: merged.core ?? {} }\n if (isClient()) {\n this.applyTheme()\n this.applyDarkMode()\n }\n }\n\n /**\n * Merges a partial profile override into the current profile and re-renders any visible UI.\n * Does not make a network request — the resolved base profile from the last `init()` is reused.\n *\n * No-op before `init()` completes (while `resolvedBase` is null).\n */\n setProfile(override: DeepPartial<ResolvedProfile>): void {\n if (!isClient() || !this.resolvedBase) return\n\n this.config.profileOverride = this.config.profileOverride\n ? deepMerge<DeepPartial<ResolvedProfile>>(this.config.profileOverride, override)\n : override\n\n const withGroupOverride = this.applyComplianceGroupOverride(this.resolvedBase)\n this.profile = deepMerge<ResolvedProfile>(withGroupOverride, this.config.profileOverride!)\n this.categoryIndex = buildCookieCategoryIndex(this.profile.preferenceModal.categories)\n\n const modalWasOpen = this.modal?.isOpen() ?? false\n if (modalWasOpen) this.modal?.close()\n\n if (this._bannerVariant !== null) {\n const wasGpc = this._bannerVariant === 'gpc'\n this._bannerVariant = null\n this._bannerMounted = null\n this.showBanner(wasGpc)\n }\n\n if (modalWasOpen) this.showModal()\n }\n\n /**\n * Destroys the widget — removes all DOM elements, disconnects the BroadcastChannel,\n * and calls `destroy()` on all registered plugins.\n *\n * After `destroy()`, the instance should not be used again.\n */\n destroy(): void {\n this._removeFocusTrap()\n if (isClient() && this._identifyListener) {\n window.removeEventListener('consenti:listener:identify', this._identifyListener)\n this._identifyListener = null\n }\n for (const plugin of this.plugins) {\n try { plugin.destroy() } catch { /* ignore — plugin cleanup must not throw */ }\n }\n for (const script of this.scannedScripts) script.destroy()\n this.scannedScripts = []\n this.modal?.destroy()\n this.renderer?.unmount()\n this.broadcastChannel.disconnect()\n if (isClient()) {\n for (const entries of this._eventHandlers.values()) {\n for (const { name, wrapped } of entries) {\n window.removeEventListener(name, wrapped)\n }\n }\n this._eventHandlers.clear()\n }\n this.banner = null\n this.modal = null\n this.renderer = null\n this.consentStore = null\n this.eventBus = null\n this.initialized = false\n }\n}\n","import type { ConsentValue, ConsentiWidgetAPI } from '../types'\n\n/**\n * Abstract base class for Consenti UI plugins.\n *\n * Extend this class to hook into the widget lifecycle. Each plugin receives the\n * full `ConsentiWidgetAPI` in `initialize()` — call any public method, inspect\n * the resolved profile, or manipulate the widget DOM.\n *\n * Errors thrown in `initialize()` or `destroy()` are caught by `ConsentiSetup`\n * and logged as warnings — a broken plugin never blocks the consent flow.\n *\n * @example\n * ```ts\n * import { ConsentiPlugin } from '@consenti/ui'\n * import type { ConsentiWidgetAPI } from '@consenti/ui'\n *\n * class AnalyticsPlugin extends ConsentiPlugin {\n * initialize(widget: ConsentiWidgetAPI) {\n * window.addEventListener('consenti:consentSubmitted', () => {\n * const consent = widget.getConsent()\n * myAnalytics.track('consent_saved', consent)\n * })\n * }\n *\n * destroy() {\n * // remove listeners, clear timers, etc.\n * }\n * }\n *\n * class BannerStylePlugin extends ConsentiPlugin {\n * initialize(widget: ConsentiWidgetAPI) {\n * widget.onReady(() => {\n * const banner = widget.getBannerElement()\n * if (banner) banner.style.setProperty('--my-custom-prop', 'red')\n * })\n * }\n * destroy() {}\n * }\n *\n * const widget = new ConsentiSetup({\n * core: { profileId: 1 },\n * plugins: [new AnalyticsPlugin(), new BannerStylePlugin()],\n * })\n * ```\n */\nexport abstract class ConsentiPlugin {\n /**\n * Called once after `ConsentiSetup` has finished its async `init()`.\n * May be async — the widget awaits the result before continuing.\n *\n * @param widget - The full public API of the initialised widget.\n */\n abstract initialize(widget: ConsentiWidgetAPI): void | Promise<void>\n\n /**\n * Called when `widget.destroy()` is invoked.\n * Use this to remove event listeners, cancel timers, and release resources.\n */\n abstract destroy(): void\n\n /**\n * Called after consent has been saved to storage and dispatched to the API.\n * Receives the full, normalised consent map (mandatory cookies always `'granted'`).\n *\n * @param consent - The complete consent record that was saved.\n */\n onConsentSubmit?(consent: ConsentValue): void | Promise<void>\n\n /** Called after the consent banner has been mounted and made visible. */\n onBannerShow?(): void | Promise<void>\n\n /** Called after the consent banner has been hidden. */\n onBannerHide?(): void | Promise<void>\n\n /** Called after the preference modal has been opened. */\n onModalShow?(): void | Promise<void>\n\n /** Called after the preference modal has been closed. */\n onModalHide?(): void | Promise<void>\n}\n","/**\n * Client-side profile builder for API-less deployments.\n *\n * `ConsentiProfile` provides a type-safe way to define consent profiles directly\n * in application code, without running a backend. Each instance is registered in\n * the module-level profile registry so `ConsentiSetup` can resolve it by ID.\n *\n * IDs are auto-assigned starting from 1000 to avoid conflicts with API-backed profiles\n * (which use small sequential integers assigned by the database).\n *\n * @example\n * ```ts\n * import { ConsentiProfile, ConsentiSetup } from '@consenti/ui'\n *\n * const profile = new ConsentiProfile({\n * cookies: {\n * necessary: {},\n * analytics: {},\n * },\n * defaultLocale: 'en',\n * translations: {\n * en: {\n * mainBanner: {\n * position: 'bottom',\n * htmlText: 'We use cookies.',\n * buttons: { 'accept-all': { text: 'Accept All', style: 'primary', action: 'submit', cookies: '*' } },\n * },\n * preferenceModal: {\n * categories: {\n * necessary: { heading: 'Necessary', htmlText: '...', legalBasis: 'mandatory', cookies: ['necessary'] },\n * },\n * buttons: { save: { text: 'Save', style: 'primary', action: 'submit' } },\n * },\n * },\n * },\n * })\n *\n * const widget = new ConsentiSetup({ core: { profileId: profile.getType() } })\n * ```\n *\n * To instead patch one of Consenti's built-in profiles for a given `complianceGroup`\n * (rather than defining a full standalone profile), pass `complianceGroup` and\n * `deepMerge: true` with only the fields you want to override — see\n * `RegisterableProfileConfig` and the `complianceGroup`/`deepMerge` resolver behavior\n * in `profile-resolver.ts`.\n */\n\nimport type { ProfileConfig, RegisterableProfileConfig } from '../types'\nimport { registerProfile } from '../core/profile-resolver'\n\n/** Starting point for auto-assigned local profile IDs. */\nlet nextLocalId = 1000\n\n/**\n * Defines a local consent profile and registers it for use by `ConsentiSetup`.\n */\nexport class ConsentiProfile {\n private type: Symbol\n\n /**\n * @param config - Full profile configuration including cookies, locales, and UI text,\n * or a `{ complianceGroup, deepMerge: true, ... }` partial overlay to\n * patch a built-in profile instead of defining a standalone one.\n */\n constructor(private config: RegisterableProfileConfig) {\n this.type = Symbol((config as ProfileConfig).id ?? nextLocalId++)\n registerProfile(this.type, config)\n }\n\n /** Returns the auto-assigned numeric profile ID. Pass this to `core.profileId`. */\n getType(): Symbol {\n return this.type\n }\n\n /** Returns the raw config passed to the constructor. */\n toJSON(): RegisterableProfileConfig {\n return this.config\n }\n}\n","/**\n * ConsentAction — runs a callback based on a specific consent parameter's grant status.\n *\n * Same lifecycle as `ConsentScript` (evaluates immediately on construction so\n * existing consent is honoured, re-evaluates on `consenti:consentSubmitted`,\n * `destroy()` to unbind) but fires `onGrant`/`onDeny` callbacks instead of\n * injecting a `<script>` element — for integrations that expose their own\n * `sdk.optIn()`/`sdk.optOut()` API (Segment, Mixpanel, Amplitude, Sentry, etc.)\n * rather than needing a script tag toggled. Use `ConsentScript` for that case instead.\n *\n * @example\n * ```ts\n * import { ConsentiSetup, ConsentAction } from '@consenti/ui'\n *\n * const widget = new ConsentiSetup({ core: { profileId: 0 } })\n * await widget.ready\n *\n * new ConsentAction({\n * id: 'analytics_storage',\n * widget,\n * onGrant: () => analyticsSdk.optIn(),\n * onDeny: () => analyticsSdk.optOut(),\n * })\n * ```\n */\n\nimport type { ConsentiSetup } from '../core/consenti-setup'\nimport type { ConsentValue, ConsentStatus, Cookie } from '../types'\nimport { isClient } from './ssr'\n\n/** Passed to `onGrant`/`onDeny` — the resolved parameter's metadata plus the full current consent map. */\nexport interface ConsentActionParams {\n cookieId: string\n status: ConsentStatus\n cookie: Cookie | undefined\n consent: ConsentValue | null\n}\n\n/** Options for constructing a {@link ConsentAction} instance. */\nexport interface ConsentActionOptions {\n /** The consent parameter ID to watch (e.g. `'analytics_storage'`). */\n id: string\n /** The `ConsentiSetup` widget instance to read consent from. */\n widget: ConsentiSetup\n /**\n * When `true` (default), the callbacks re-fire automatically on every future consent\n * change. Set to `false` to evaluate consent only once at construction time — no\n * event listener is attached.\n */\n bind?: boolean\n /** Called when the parameter's status transitions to `'granted'`. */\n onGrant?: (params: ConsentActionParams) => void\n /** Called when the parameter's status transitions away from `'granted'` (`'denied'` or `'objected'`). */\n onDeny?: (params: ConsentActionParams) => void\n}\n\n/**\n * Watches a single consent parameter's grant status and fires `onGrant`/`onDeny`\n * callbacks on transitions. Call {@link ConsentAction.destroy} when no longer\n * needed to remove the event listener.\n */\nexport class ConsentAction {\n private granted: boolean | null = null\n private readonly handler: () => void\n\n constructor(private readonly options: ConsentActionOptions) {\n // Assign handler unconditionally to satisfy TypeScript strict property initialisation.\n this.handler = () => this.evaluate()\n\n if (!isClient()) return\n\n if (options.bind !== false) {\n window.addEventListener('consenti:consentSubmitted', this.handler)\n }\n\n // Evaluate immediately so any existing consent is reflected at construction time.\n this.evaluate()\n }\n\n private evaluate(): void {\n const consent = this.options.widget.getConsent()\n const status: ConsentStatus = consent?.[this.options.id] ?? 'denied'\n const granted = status === 'granted'\n\n if (granted === this.granted) return\n this.granted = granted\n\n const cookie = this.options.widget.getProfile()?.cookies[this.options.id]\n const params: ConsentActionParams = { cookieId: this.options.id, status, cookie, consent }\n\n if (granted) this.options.onGrant?.(params)\n else this.options.onDeny?.(params)\n }\n\n /** Removes the event listener (if bound). */\n destroy(): void {\n if (!isClient()) return\n if (this.options.bind !== false) {\n window.removeEventListener('consenti:consentSubmitted', this.handler)\n }\n }\n}\n","/**\n * CategoryAction — runs a callback based on a category's rollup consent state.\n *\n * A category is considered \"granted\" only when *every* parameter it contains is\n * `'granted'` — matching the preference modal's tri-state rollup rule. `onDeny`\n * fires for both a fully-denied category and a partially-granted (\"mixed\") one,\n * since neither represents full consent for the category.\n *\n * Same lifecycle as `ConsentAction`: evaluates immediately on construction,\n * re-evaluates on `consenti:consentSubmitted`, `destroy()` to unbind.\n *\n * @example\n * ```ts\n * import { ConsentiSetup, CategoryAction } from '@consenti/ui'\n *\n * const widget = new ConsentiSetup({ core: { profileId: 0 } })\n * await widget.ready\n *\n * new CategoryAction({\n * id: 'marketing',\n * widget,\n * onGrant: () => adSdk.enableAll(),\n * onDeny: () => adSdk.disableAll(),\n * })\n * ```\n */\n\nimport type { ConsentiSetup } from '../core/consenti-setup'\nimport type { ConsentValue } from '../types'\nimport { isClient } from './ssr'\n\n/** Passed to `onGrant`/`onDeny` — the category's member parameter IDs plus the full current consent map. */\nexport interface CategoryActionParams {\n categoryId: string\n cookieIds: string[]\n consent: ConsentValue | null\n}\n\n/** Options for constructing a {@link CategoryAction} instance. */\nexport interface CategoryActionOptions {\n /** The category ID to watch (e.g. `'marketing'`). */\n id: string\n /** The `ConsentiSetup` widget instance to read consent from. */\n widget: ConsentiSetup\n /**\n * When `true` (default), the callbacks re-fire automatically on every future consent\n * change. Set to `false` to evaluate consent only once at construction time — no\n * event listener is attached.\n */\n bind?: boolean\n /** Called when every parameter in the category transitions to `'granted'`. */\n onGrant?: (params: CategoryActionParams) => void\n /** Called when the category transitions away from fully-granted (any parameter denied/objected). */\n onDeny?: (params: CategoryActionParams) => void\n}\n\n/**\n * Watches a category's rollup grant status and fires `onGrant`/`onDeny` callbacks\n * on transitions. Call {@link CategoryAction.destroy} when no longer needed to\n * remove the event listener.\n */\nexport class CategoryAction {\n private granted: boolean | null = null\n private readonly handler: () => void\n\n constructor(private readonly options: CategoryActionOptions) {\n // Assign handler unconditionally to satisfy TypeScript strict property initialisation.\n this.handler = () => this.evaluate()\n\n if (!isClient()) return\n\n if (options.bind !== false) {\n window.addEventListener('consenti:consentSubmitted', this.handler)\n }\n\n // Evaluate immediately so any existing consent is reflected at construction time.\n this.evaluate()\n }\n\n private evaluate(): void {\n const category = this.options.widget.getProfile()?.preferenceModal.categories[this.options.id]\n const cookieIds = category?.cookies ?? []\n const consent = this.options.widget.getConsent()\n const granted = cookieIds.length > 0 && cookieIds.every(id => consent?.[id] === 'granted')\n\n if (granted === this.granted) return\n this.granted = granted\n\n const params: CategoryActionParams = { categoryId: this.options.id, cookieIds, consent }\n\n if (granted) this.options.onGrant?.(params)\n else this.options.onDeny?.(params)\n }\n\n /** Removes the event listener (if bound). */\n destroy(): void {\n if (!isClient()) return\n if (this.options.bind !== false) {\n window.removeEventListener('consenti:consentSubmitted', this.handler)\n }\n }\n}\n","/**\n * BannerTrigger — a pre-built accessible UI element that reopens the Consenti\n * banner or preference modal when clicked.\n *\n * Use this to add a \"Cookie Settings\" link in your site footer or cookie policy\n * page without wiring up the click handler manually or importing the widget module\n * into your layout component.\n *\n * (Renamed from `CookieTrigger` — it was never scoped to a specific cookie, it\n * just opens the banner or modal.)\n *\n * @example\n * ```ts\n * import { ConsentiSetup, BannerTrigger } from '@consenti/ui'\n *\n * const widget = new ConsentiSetup({ core: { profileId: 0 } })\n *\n * // Attach to an existing element\n * new BannerTrigger({ widget, el: '#footer-cookie-settings' })\n *\n * // — or — create a new button and place it yourself\n * const trigger = new BannerTrigger({ widget, label: 'Cookie Settings' })\n * document.querySelector('.site-footer')?.appendChild(trigger.getElement())\n * ```\n */\n\nimport type { ConsentiSetup } from '../core/consenti-setup'\nimport { isClient } from './ssr'\n\n/** Options for constructing a {@link BannerTrigger} instance. */\nexport interface BannerTriggerOptions {\n /** The widget instance to control when the trigger is clicked. */\n widget: ConsentiSetup\n /**\n * Element to attach the click handler to.\n * - `HTMLElement` — used directly.\n * - `string` — treated as a CSS selector; the first matching element is used.\n * - Omitted — a new `<button>` is created. Retrieve it with {@link BannerTrigger.getElement}\n * and append it to the DOM at the desired location.\n */\n el?: HTMLElement | string\n /**\n * Which panel opens on click.\n * - `'modal'` (default) — preference modal.\n * - `'banner'` — main consent banner.\n */\n action?: 'modal' | 'banner'\n /**\n * Label for the auto-created button element.\n * Ignored when `el` points to an existing element. Defaults to `'Cookie Settings'`.\n */\n label?: string\n}\n\n/**\n * Attaches a click handler that opens the Consenti preference panel to a DOM\n * element, or creates an accessible `<button>` when no existing element is provided.\n *\n * The created button uses the `.consenti-btn` and `.consenti-btn--text` classes so\n * it inherits the widget's theme automatically.\n */\nexport class BannerTrigger {\n private readonly el: HTMLElement\n private readonly clickHandler: () => void\n /** Whether this instance created the element (used to decide if `destroy()` removes it). */\n private readonly created: boolean\n\n constructor(options: BannerTriggerOptions) {\n const action = options.action ?? 'modal'\n\n this.clickHandler = () => {\n if (action === 'modal') options.widget.showModal()\n else options.widget.showBanner()\n }\n\n if (!options.el) {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = 'consenti-btn consenti-btn--text consenti-banner-trigger'\n btn.textContent = options.label ?? 'Cookie Settings'\n btn.setAttribute('aria-label', options.label ?? 'Open cookie settings')\n this.el = btn\n this.created = true\n } else if (typeof options.el === 'string') {\n if (!isClient()) {\n // SSR — create a placeholder that is never appended.\n this.el = document.createElement('span')\n this.created = false\n return\n }\n const found = document.querySelector<HTMLElement>(options.el)\n if (!found) {\n throw new Error(\n `[Consenti] BannerTrigger: element not found for selector \"${options.el}\"`,\n )\n }\n this.el = found\n this.created = false\n } else {\n this.el = options.el\n this.created = false\n }\n\n this.el.addEventListener('click', this.clickHandler)\n }\n\n /**\n * Returns the trigger element.\n * When no `el` was provided, append the returned element to the desired location\n * in the DOM after construction.\n */\n getElement(): HTMLElement {\n return this.el\n }\n\n /**\n * Removes the click handler and, if this instance auto-created the button,\n * removes it from the DOM.\n */\n destroy(): void {\n this.el.removeEventListener('click', this.clickHandler)\n if (this.created) this.el.remove()\n }\n}\n","/**\n * Standalone resolver for the parental-consent hook (see `AgeGate`/`ConsentiSetup.resolveAgeGate`).\n * Not a `ConsentiSetup` instance method — the parent typically opens the token link (emailed by\n * the host's own eventBus listener, see `consenti:parentalConsentRequired`) on a fresh page or\n * even a different device, with no live widget instance around at all.\n */\n\nimport type { ParentalConsentGrantedDetail } from '../types'\nimport { httpRequest } from './http'\nimport { isClient } from './ssr'\n\nexport interface ParentalConsentApiConfig {\n baseUrl?: string\n authToken?: string\n}\n\n/**\n * POSTs a parental-consent token to `POST /consent/parental-consent-resolve`. On success,\n * dispatches `consenti:parentalConsentResolved` on `window` (same mechanism `EventBus.dispatch`\n * uses) so any page with a live widget instance can react — what \"resolved\" should actually do to\n * consent state is left to that listener, matching the \"we provide the hook, not the behavior\"\n * scope of this whole feature. Throws on an invalid/expired token or network failure.\n */\nexport async function resolveParentalConsent(\n token: string,\n apiConfig: ParentalConsentApiConfig = {},\n): Promise<ParentalConsentGrantedDetail> {\n const base = apiConfig.baseUrl ?? (isClient() ? window.location.origin : '')\n const result = await httpRequest<ParentalConsentGrantedDetail>(\n `${base}/consenti/api/v1/consent/parental-consent-resolve`,\n { method: 'POST', body: JSON.stringify({ token }) },\n apiConfig.authToken,\n )\n\n if (isClient()) {\n window.dispatchEvent(new CustomEvent('consenti:parentalConsentResolved', { detail: result, bubbles: true }))\n }\n\n return result\n}\n","/**\n * Builds a synchronous, dependency-free `<head>` snippet that freezes Google Consent Mode\n * to denied-by-default the instant a visitor's browser sends the Global Privacy Control (GPC)\n * signal — before any tag-loading script runs, including GTM/gtag.js itself and Consenti's own\n * bundle (which only applies GPC once it has loaded, parsed, and initialized asynchronously).\n *\n * This is a documentation helper, not something the widget loads itself: paste the returned\n * string as the very first `<script>` in `<head>`, ahead of GTM/gtag.js and the Consenti bundle.\n * It only touches `window[dataLayerName]`/`window.gtag` (the same stub-queue pattern\n * `EventBus.initConsentMode()` uses), so it's safe to run before those exist and harmless to\n * run again after they do — Consenti's own `gtag('consent', 'default', …)` call on init just\n * pushes another entry onto the same queue.\n *\n * The default values pushed here mirror `getGoogleGTMConsent({})` in `consent-mapper.ts` (safe\n * defaults for an empty consent map) — keep the two in sync if either changes.\n */\nexport function buildSyncGpcSnippet(options?: { dataLayerName?: string }): string {\n const dl = (options?.dataLayerName ?? 'dataLayer').replace(/[^a-zA-Z0-9_$]/g, '')\n return `<script>(function(){if(typeof navigator!==\"undefined\"&&navigator.globalPrivacyControl===true){window.${dl}=window.${dl}||[];if(typeof window.gtag!==\"function\"){window.gtag=function(){window.${dl}.push(arguments)}}window.gtag(\"consent\",\"default\",{ad_storage:\"denied\",analytics_storage:\"denied\",ad_user_data:\"denied\",ad_personalization:\"denied\",functionality_storage:\"granted\",personalization_storage:\"denied\",security_storage:\"granted\",ads_data_redaction:\"true\",url_passthrough:\"false\"});window.__consentiGpcPreFrozen=true}})();</script>`\n}\n"],"mappings":"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","names":["colorEnabled","wrap","open","close","s","color","LEVEL_STYLE","color","buildCookieCategoryIndex","categories","index","category","cookieId","getCookieLegalBasis","isMandatoryCookie","EMBEDDED_COMPLIANCE_MAP","COMPLIANCE_REGULATION_IDS","acc","country","c","COUNTRY_CODES","d","LOCALES","l","isComplianceMapData","data","countries","entry","resolveComplianceMapOverride","override","onWarn","fallback","candidate","response","err","code","result","B64_CHARS","utf8Encode","str","base64UrlEncode","bytes","out","i","b0","b1","b2","B64_CHARS","encodeTcString","input","payload","base64UrlEncode","utf8Encode","B64_CHARS","utf8Encode","str","base64UrlEncode","bytes","out","i","b0","b1","b2","B64_CHARS","NOT_APPLICABLE","OPTED_OUT","DID_NOT_OPT_OUT","optOutValue","applicable","optedOut","encodeGppString","input","payload","base64UrlEncode","utf8Encode","RTL_LANGUAGE_PREFIXES","resolveTextDirection","locale","override","dir","prefix","isClient","safeWindow","safeNavigator","isClient","_levels","initConsole","levels","logger","args","injectScript","options","script","src","logger","key","value","revokeScript","scriptEl","ConsentScript","options","isClient","granted","script","injectScript","revokeScript","CategoryScript","options","isClient","cookieIds","consent","granted","id","script","injectScript","revokeScript","CONSENT_ATTR","CATEGORY_ATTR","BIND_ATTR","SCANNED_ATTR","readBind","el","scanConsentScripts","widget","isClient","elements","created","src","unsafeInnerHTML","bind","consentId","categoryId","ConsentScript","CategoryScript","getConsentForPurpose","consent","cookies","purpose","result","cookieId","cookie","status","GCM_KEYS","getGoogleGTMConsent","key","raw","getPurposeConsent","getCategoryConsent","categories","categoryId","category","offStatus","statuses","id","getAdobeConsent","getMetaConsent","getMicrosoftClarityConsent","getTwilioSegmentConsent","CONSENTI_CSS","ConsentiChannel","isClient","onMessage","event","logger","message","generateReceipt","params","receipt","downloadReceipt","json","blob","url","anchor","generatePrefixedId","prefix","getCookie","name","isClient","match","escapeRegExp","setCookie","value","opts","secure","parts","deleteCookie","str","isConsentCookieData","obj","d","encodeConsent","data","decodeConsent","raw","sigIdx","jsonStr","expandConsent","result","id","short","compressConsent","status","ConsentStorage","mode","isClient","logger","key","getCookie","value","opts","setCookie","deleteCookie","KEY_PREFIX","ID_PREFIX","MAX_AGE_SECONDS","allStorageKeys","mode","isClient","c","findStoredVisitorId","key","getOrCreateVisitorId","cookieDomains","existing","visitorId","generatePrefixedId","opts","domain","ConsentStorage","peekVisitorId","resolveConsentMaxAge","expiryDays","CONSENT_COOKIE_NAME","peekConsentLocale","storageMode","cookieName","raw","ConsentStorage","decodeConsent","peekConsentGroup","LEGACY_PREFIX","parseLegacyCookieValue","value","parts","tsPart","timestamp","epochStr","epoch","consentPart","sigPart","consent","pair","colonIdx","id","status","result","ConsentStore","profileId","cookieDomains","appUserId","version","data","migrated","signingKey","sigIdx","jsonStr","sigStr","verifyValue","logger","parsed","meta","signature","encoded","encodeConsent","signValue","sig","opts","expandConsent","generatePrefixedId","compressConsent","suffix","allNames","c","name","inner","legacyUserId","domain","EventBus","profileId","gtmConfig","isClient","win","safeWindow","gtm","layerName","args","gtag","getGoogleGTMConsent","params","injectScript","name","detail","event","layer","consentJson","isPlainObject","value","mergeValue","base","override","length","_","result","key","overrideValue","deepMerge","getBrowserGeoHints","encodeGeoHints","locale","opts","mapCookie","c","mapCookies","cookies","result","id","mapButton","b","mapButtons","buttons","mapCategory","cat","mapCategories","categories","mapBanner","mapModal","m","pickTranslations","translations","locale","defaultLocale","prefix","prefixMatch","k","adaptEmbeddedProfile","embedded","tx","profileRegistry","registerProfile","type","config","getRegisteredProfile","findRegisteredProfileForGroup","group","mapPublicProfileToResolved","resp","rawGpcMode","gpcMode","CACHE_KEY_PREFIX","CACHE_TTL_MS","getCachedResolution","tenantId","locale","cacheEnabled","key","raw","cached","setCachedResolution","data","isDomainAllowed","profile","trustDomain","origins","current","o","loadPrebuiltProfile","complianceGroup","_locale","DEFAULT_PROFILES","embedded","adaptEmbeddedProfile","isPrebuiltAllowed","setting","timezoneCountryIndex","getTimezoneCountryIndex","index","countries","EMBEDDED_COMPLIANCE_MAP","code","tz","codes","countryFromLangTag","lang","parts","tag","resolveCountryFromHints","langs","tzCountries","langCountries","c","resolveGroupFromHints","overrideMap","countryCode","overrideEntry","entry","tzParts","resolveGroupForCountry","country","region","effectiveLocale","l","stored","peekConsentLocale","effectiveTenantId","fetchProfileJson","url","authToken","httpRequest","toDeepPartialResolvedProfile","resolveRegisteredOverride","override","builtIn","partial","deepMerge","resolveLocalProfileConfig","scenario1A","resolveComplianceMapOverride","msg","logger","effectiveOverrideMap","provider","result","getBrowserGeoHints","scenario1B","scenario2A","base","json","knownGroup","peekConsentGroup","resolveUrl","encodeGeoHints","path","resolvedLocale","requiresSensitiveOptIn","resolved","scenario2B","localProfile","typeDescription","mainBanner","gpcBanner","preferenceModal","ageGateModal","scenarioLocal","typeValue","resolveProfile","complianceType","apiEnabled","detectGPC","nav","safeNavigator","applyGPCToConsent","cookies","categories","currentConsent","mode","result","categoryIndex","buildCookieCategoryIndex","cookieId","cookie","isMandatoryCookie","getCookieLegalBasis","TCF_POLICY_VERSION","API_VERSION","buildPurposeAndVendorConsents","profile","consent","purposeConsents","vendorConsents","cookieId","cookie","p","a","b","toBoolMap","ids","max","map","i","installTcfStub","getState","isClient","win","safeWindow","listeners","listenerCounter","buildTcData","listenerId","cfg","tcfConfig","gdprApplies","encodeTcString","notifyListeners","callback","handleCommand","command","_version","parameter","removed","queued","api","version","GPP_VERSION","USNAT_SECTION_ID","deriveOptOuts","profile","consent","saleApplicable","sharingApplicable","saleOptOut","sharingOptOut","id","cookie","installGppStub","getState","isClient","win","safeWindow","listeners","listenerCounter","currentGppString","cfg","gppConfig","encodeGppString","detectGPC","buildPingData","notifyListeners","listenerId","callback","handleCommand","command","parameter","removed","queued","api","buildButton","id","btn","handlers","el","cookies","createOverlay","opacity","onClick","el","Banner","config","handlers","privacyPolicyUrl","locales","activeLocale","onLocaleSwitch","hidePoweredBy","variant","banner","textContainer","linksContainer","headingTag","heading","text","link","buttonEntries","linkButtons","e","btn","anchor","actionButtons","b","btns","id","buildButton","hasClose","hasLocaleSwitcher","controls","buildLocaleSwitcher","close","createOverlay","pb","onSwitch","wrapper","list","locale","item","localeBtn","FOCUSABLE_SELECTORS","trapFocus","container","getFocusable","el","handleKeydown","e","els","first","last","handleEscape","Modal","_Modal","config","cookies","initialConsent","handlers","allowReceipt","dpdpaConfig","locales","activeLocale","onLocaleSwitch","hidePoweredBy","position","wrapper","overlayOpacity","overlayClickHandler","overlay","createOverlay","container","header","body","headingTag","heading","sub","hasClose","hasLocaleSwitcher","headerControls","buildModalLocaleSwitcher","close","desc","buttonEntries","linkButtons","e","linksContainer","btn","anchor","categories","categoryId","cat","catEl","notice","esc","s","safeEmail","btns","id","b","buildButton","pb","toggle","isLI","checkIcon","crossIcon","objectedIcon","knob","cookieIds","offStatus","statuses","entry","rollup","ariaChecked","cookieId","cookie","isMandatory","row","info","label","meta","badges","on","next","stored","status","paramToggle","li","textWrap","text","showMore","params","hasMultipleParams","expanded","label_","description","checkbox","onForgetMe","triggerEl","mountEl","isFullscreen","trapFocus","isTextOverflowing","consent","fallback","onSwitch","list","locale","item","localeBtn","AgeGate","minimumAge","onYes","onNo","content","root","heading","text","buttons","yes","no","createOverlay","onAcknowledge","ok","ROOT_ID","Renderer","rootEl","isClient","found","root","css","style","message","announcer","ConsentiSetup","config","ConsentiChannel","resolve","isClient","initConsole","logger","err","getOrCreateVisitorId","base","override","deepMerge","enabled","statusPath","httpRequest","storageMode","tcfBlockedPromise","gppBlockedPromise","resolvedProfile","resolveProfile","withGroupOverride","profile","gpcActive","detectGPC","buildCookieCategoryIndex","EventBus","ConsentStore","installTcfStub","installGppStub","Renderer","CONSENTI_CSS","Banner","Modal","msg","signingKey","hasExisting","ageGateConfig","gpcConsent","applyGPCToConsent","gpcSigningKey","gpcMeta","generatePrefixedId","compGroup","isOptOut","isOptOutStrict","defaultConsent","cookieId","cookie","isMandatoryCookie","ccpaSigKey","ccpaMeta","denyByDefault","sigKey","cpraMeta","minimumAge","root","AgeGate","promptEl","overlayEl","confirmed","token","msgEl","scanConsentScripts","listener","e","detail","userId","reConsent","peekVisitorId","resolveTextDirection","isDark","container","footer","consentId","idEl","visitorId","visitorEl","profileEl","consentDate","dateEl","bp","style","FOCUSABLE","focusable","el","first","last","theme","map","prop","value","mult","baseSize","headingSize","plugin","invoke","onlyMandatory","consent","includingMandatory","context","p","cookieIds","fullConsent","hadExistingConsent","nonMandatory","id","allGranted","allDenied","type","cookies","getGoogleGTMConsent","getPurposeConsent","getCategoryConsent","getAdobeConsent","getMetaConsent","getMicrosoftClarityConsent","getTwilioSegmentConsent","gpcDerived","source","pageUrl","fallback","defaultDenied","consentAction","apiResponse","writeMeta","currentConsentVersion","receiptParams","receipt","generateReceipt","downloadReceipt","useGpcVariant","variant","bannerConfig","handlers","locales","activeLocale","bannerEl","locale","overlay","triggerEl","existing","dpdpaCfg","modalEl","callback","resetAgeGate","profileId","requestValue","status","categoryId","category","event","handler","name","wrapped","entries","idx","entry","enable","merged","modalWasOpen","wasGpc","script","ConsentiPlugin","nextLocalId","ConsentiProfile","config","registerProfile","ConsentAction","options","isClient","consent","status","granted","cookie","params","CategoryAction","options","isClient","cookieIds","consent","granted","id","params","BannerTrigger","options","action","isClient","found","btn","resolveParentalConsent","token","apiConfig","base","isClient","result","httpRequest","buildSyncGpcSnippet","options","dl"]}