@tickboxhq/banner-default 0.1.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -343,13 +343,13 @@ var TICKBOX_STYLES = `
343
343
  .tb-link:hover { text-decoration: underline; }
344
344
 
345
345
  .tb-branding {
346
- /* flex-basis 100% breaks .tb-branding onto its own row inside the
347
- wrapping flex banner so it doesn't sit next to the action buttons.
348
- On .tb-notice (block layout) flex-basis is inert; the explicit
349
- margin-top below handles the spacing there. */
346
+ /* Own row in the wrapping flex banner; negative margins claw back the
347
+ parent's 16px gap + the banner's 16px bottom padding so the credit
348
+ line sits tight to the bottom edge instead of floating in dead space.
349
+ On .tb-notice (block flow) the negative margins are reset below. */
350
350
  flex-basis: 100%;
351
- margin: 0;
352
- padding-top: 4px;
351
+ margin: -12px 0 -10px;
352
+ padding: 0;
353
353
  font-size: 11px;
354
354
  color: var(--tb-fg-muted);
355
355
  text-align: center;
@@ -357,8 +357,7 @@ var TICKBOX_STYLES = `
357
357
  letter-spacing: 0.01em;
358
358
  }
359
359
  .tb-notice .tb-branding {
360
- margin-top: 10px;
361
- padding-top: 0;
360
+ margin: 4px 0 -4px;
362
361
  text-align: right;
363
362
  }
364
363
  .tb-branding a {
@@ -641,5 +640,5 @@ function mergeCopy(base, remote) {
641
640
  }
642
641
 
643
642
  export { applyThemeColors, injectStyles, locales, mergeCopy, readCachedTheme, refreshTheme, resolveLocalePack };
644
- //# sourceMappingURL=chunk-SV4PBHBY.js.map
645
- //# sourceMappingURL=chunk-SV4PBHBY.js.map
643
+ //# sourceMappingURL=chunk-GYKO2N23.js.map
644
+ //# sourceMappingURL=chunk-GYKO2N23.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/locales/de.ts","../src/shared/locales/en.ts","../src/shared/locales/es.ts","../src/shared/locales/fr.ts","../src/shared/locales/it.ts","../src/shared/locales/nl.ts","../src/shared/locales/pl.ts","../src/shared/locales/pt.ts","../src/shared/locales/uk.ts","../src/shared/locales/index.ts","../src/shared/styles.ts","../src/shared/theme.ts"],"names":["banner","notice"],"mappings":"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","file":"chunk-GYKO2N23.js","sourcesContent":["import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies und Tracking',\n description:\n 'Wir verwenden Cookies, damit diese Website funktioniert, und mit Ihrer Einwilligung, um die Nutzung zu messen. Sie können selbst wählen, was Sie zulassen.',\n acceptLabel: 'Alle akzeptieren',\n rejectLabel: 'Alle ablehnen',\n customiseLabel: 'Anpassen',\n saveLabel: 'Einstellungen speichern',\n closeLabel: 'Schließen',\n policyLinkLabel: 'Datenschutz',\n requiredBadge: 'Erforderlich',\n}\n\nexport const notice: NoticeCopy = {\n title: 'Hinweis zur Analyse',\n description:\n 'Wir verwenden datenschutzfreundliche Analyse-Tools, um zu verstehen, wie diese Website genutzt wird. Es werden keine personenbezogenen Daten erhoben und keine Werbeprofile erstellt.',\n acknowledgeLabel: 'Verstanden',\n optOutLabel: 'Ablehnen',\n policyLinkLabel: 'Datenschutz',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies and tracking',\n description:\n 'We use cookies to make this site work and, with your consent, to measure usage. You can choose what to allow.',\n acceptLabel: 'Accept all',\n rejectLabel: 'Reject all',\n customiseLabel: 'Customise',\n saveLabel: 'Save preferences',\n closeLabel: 'Close',\n policyLinkLabel: 'Privacy policy',\n requiredBadge: 'Required',\n}\n\nexport const notice: NoticeCopy = {\n title: 'A note about analytics',\n description:\n 'We use privacy-friendly analytics to understand how this site is used. No personal data is collected and no advertising profiles are built.',\n acknowledgeLabel: 'Got it',\n optOutLabel: 'Opt out',\n policyLinkLabel: 'Privacy policy',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies y seguimiento',\n description:\n 'Utilizamos cookies para que este sitio funcione y, con tu consentimiento, para medir su uso. Tú eliges lo que permites.',\n acceptLabel: 'Aceptar todo',\n rejectLabel: 'Rechazar todo',\n customiseLabel: 'Personalizar',\n saveLabel: 'Guardar preferencias',\n closeLabel: 'Cerrar',\n policyLinkLabel: 'Política de privacidad',\n requiredBadge: 'Obligatorio',\n}\n\nexport const notice: NoticeCopy = {\n title: 'Sobre la analítica',\n description:\n 'Utilizamos analítica respetuosa con la privacidad para entender cómo se usa este sitio. No recopilamos datos personales ni creamos perfiles publicitarios.',\n acknowledgeLabel: 'Entendido',\n optOutLabel: 'Rechazar',\n policyLinkLabel: 'Política de privacidad',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies et suivi',\n description:\n 'Nous utilisons des cookies pour faire fonctionner ce site et, avec votre consentement, pour mesurer son utilisation. Vous choisissez ce que vous autorisez.',\n acceptLabel: 'Tout accepter',\n rejectLabel: 'Tout refuser',\n customiseLabel: 'Personnaliser',\n saveLabel: 'Enregistrer les préférences',\n closeLabel: 'Fermer',\n policyLinkLabel: 'Politique de confidentialité',\n requiredBadge: 'Requis',\n}\n\nexport const notice: NoticeCopy = {\n title: \"À propos de l'analyse\",\n description:\n \"Nous utilisons une analyse respectueuse de la vie privée pour comprendre comment ce site est utilisé. Aucune donnée personnelle n'est collectée et aucun profil publicitaire n'est constitué.\",\n acknowledgeLabel: 'Compris',\n optOutLabel: 'Refuser',\n policyLinkLabel: 'Politique de confidentialité',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookie e tracciamento',\n description:\n \"Utilizziamo i cookie per far funzionare il sito e, con il tuo consenso, per misurarne l'utilizzo. Puoi scegliere cosa consentire.\",\n acceptLabel: 'Accetta tutto',\n rejectLabel: 'Rifiuta tutto',\n customiseLabel: 'Personalizza',\n saveLabel: 'Salva preferenze',\n closeLabel: 'Chiudi',\n policyLinkLabel: 'Informativa sulla privacy',\n requiredBadge: 'Obbligatorio',\n}\n\nexport const notice: NoticeCopy = {\n title: \"Nota sull'analisi\",\n description:\n 'Utilizziamo strumenti di analisi rispettosi della privacy per capire come viene usato questo sito. Non raccogliamo dati personali né creiamo profili pubblicitari.',\n acknowledgeLabel: 'Ho capito',\n optOutLabel: 'Rifiuta',\n policyLinkLabel: 'Informativa sulla privacy',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies en tracking',\n description:\n 'Wij gebruiken cookies om deze site te laten werken en, met uw toestemming, om het gebruik te meten. U kiest wat u toestaat.',\n acceptLabel: 'Alles accepteren',\n rejectLabel: 'Alles weigeren',\n customiseLabel: 'Aanpassen',\n saveLabel: 'Voorkeuren opslaan',\n closeLabel: 'Sluiten',\n policyLinkLabel: 'Privacybeleid',\n requiredBadge: 'Vereist',\n}\n\nexport const notice: NoticeCopy = {\n title: 'Over analytics',\n description:\n 'Wij gebruiken privacyvriendelijke analytics om te begrijpen hoe deze site wordt gebruikt. Er worden geen persoonsgegevens verzameld en er worden geen advertentieprofielen opgebouwd.',\n acknowledgeLabel: 'Begrepen',\n optOutLabel: 'Afmelden',\n policyLinkLabel: 'Privacybeleid',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Pliki cookie i śledzenie',\n description:\n 'Używamy plików cookie, aby ta strona działała, oraz – za Twoją zgodą – aby mierzyć jej użycie. To Ty decydujesz, na co pozwolić.',\n acceptLabel: 'Zaakceptuj wszystkie',\n rejectLabel: 'Odrzuć wszystkie',\n customiseLabel: 'Dostosuj',\n saveLabel: 'Zapisz preferencje',\n closeLabel: 'Zamknij',\n policyLinkLabel: 'Polityka prywatności',\n requiredBadge: 'Wymagane',\n}\n\nexport const notice: NoticeCopy = {\n title: 'O analityce',\n description:\n 'Używamy analityki przyjaznej prywatności, aby zrozumieć, jak korzysta się z tej strony. Nie zbieramy danych osobowych ani nie tworzymy profili reklamowych.',\n acknowledgeLabel: 'Rozumiem',\n optOutLabel: 'Zrezygnuj',\n policyLinkLabel: 'Polityka prywatności',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies e rastreio',\n description:\n 'Utilizamos cookies para que este site funcione e, com o seu consentimento, para medir a sua utilização. Pode escolher o que permitir.',\n acceptLabel: 'Aceitar tudo',\n rejectLabel: 'Rejeitar tudo',\n customiseLabel: 'Personalizar',\n saveLabel: 'Guardar preferências',\n closeLabel: 'Fechar',\n policyLinkLabel: 'Política de privacidade',\n requiredBadge: 'Obrigatório',\n}\n\nexport const notice: NoticeCopy = {\n title: 'Sobre a análise',\n description:\n 'Utilizamos análises respeitadoras da privacidade para perceber como este site é utilizado. Não recolhemos dados pessoais nem criamos perfis publicitários.',\n acknowledgeLabel: 'Entendi',\n optOutLabel: 'Recusar',\n policyLinkLabel: 'Política de privacidade',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Файли cookie та відстеження',\n description:\n 'Ми використовуємо файли cookie, щоб цей сайт працював, а за вашою згодою — щоб вимірювати використання. Ви самі обираєте, що дозволити.',\n acceptLabel: 'Прийняти всі',\n rejectLabel: 'Відхилити всі',\n customiseLabel: 'Налаштувати',\n saveLabel: 'Зберегти налаштування',\n closeLabel: 'Закрити',\n policyLinkLabel: 'Політика конфіденційності',\n requiredBadge: \"Обов'язково\",\n}\n\nexport const notice: NoticeCopy = {\n title: 'Про аналітику',\n description:\n 'Ми використовуємо аналітику, що поважає приватність, щоб зрозуміти, як використовується цей сайт. Ми не збираємо персональні дані й не створюємо рекламні профілі.',\n acknowledgeLabel: 'Зрозуміло',\n optOutLabel: 'Відмовитися',\n policyLinkLabel: 'Політика конфіденційності',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\nimport * as de from './de.js'\nimport * as en from './en.js'\nimport * as es from './es.js'\nimport * as fr from './fr.js'\nimport * as it from './it.js'\nimport * as nl from './nl.js'\nimport * as pl from './pl.js'\nimport * as pt from './pt.js'\nimport * as uk from './uk.js'\n\nexport type LocalePack = {\n banner: BannerCopy\n notice: NoticeCopy\n}\n\nexport const locales: Record<string, LocalePack> = {\n en: { banner: en.banner, notice: en.notice },\n de: { banner: de.banner, notice: de.notice },\n fr: { banner: fr.banner, notice: fr.notice },\n es: { banner: es.banner, notice: es.notice },\n it: { banner: it.banner, notice: it.notice },\n nl: { banner: nl.banner, notice: nl.notice },\n pt: { banner: pt.banner, notice: pt.notice },\n pl: { banner: pl.banner, notice: pl.notice },\n uk: { banner: uk.banner, notice: uk.notice },\n}\n\nexport type LocaleCode = keyof typeof locales\n\n/**\n * Resolve a BCP-47 locale tag to a built-in locale pack. Falls back from\n * the full tag (`en-GB`) to the language prefix (`en`), then to English.\n *\n * Pass `'auto'` to read `navigator.language` at call time. Anywhere that\n * `navigator` is missing (SSR, Node), `'auto'` falls back to English.\n */\nexport function resolveLocalePack(locale: string | undefined): LocalePack {\n if (!locale) return locales.en as LocalePack\n const tag = locale === 'auto' ? readNavigatorLanguage() : locale\n if (!tag) return locales.en as LocalePack\n const lower = tag.toLowerCase()\n if (locales[lower]) return locales[lower] as LocalePack\n const prefix = lower.split('-')[0]\n if (prefix && locales[prefix]) return locales[prefix] as LocalePack\n return locales.en as LocalePack\n}\n\nfunction readNavigatorLanguage(): string | undefined {\n if (typeof navigator === 'undefined') return undefined\n return navigator.language\n}\n","/**\n * Inline CSS for the default banner / notice / modal components.\n *\n * Uses CSS custom properties so users can re-theme without forking. Light\n * and dark themes are wired through `prefers-color-scheme` and the\n * `[data-tb-theme]` attribute.\n *\n * Visual style: GitHub-ish — system font, 6px corners, subtle border + soft\n * shadow, equal-prominence accept/reject buttons.\n */\nexport const TICKBOX_STYLES = `\n:where(.tb-root) {\n --tb-bg: #ffffff;\n --tb-fg: #1f2328;\n --tb-fg-muted: #59636e;\n --tb-border: #d1d9e0;\n --tb-shadow: 0 8px 24px rgba(140, 149, 159, 0.2);\n --tb-primary-bg: #1f2328;\n --tb-primary-fg: #ffffff;\n --tb-primary-bg-hover: #000000;\n --tb-secondary-bg: #ffffff;\n --tb-secondary-fg: #1f2328;\n --tb-secondary-bg-hover: #f6f8fa;\n --tb-link: #0969da;\n --tb-radius: 6px;\n --tb-z: 2147483000;\n font-family:\n -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Noto Sans\", Helvetica,\n Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\";\n color: var(--tb-fg);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n}\n@media (prefers-color-scheme: dark) {\n :where(.tb-root:not([data-tb-theme=\"light\"])) {\n --tb-bg: #0d1117;\n --tb-fg: #f0f6fc;\n --tb-fg-muted: #9198a1;\n --tb-border: #30363d;\n --tb-shadow: 0 8px 24px rgba(1, 4, 9, 0.85);\n --tb-primary-bg: #f0f6fc;\n --tb-primary-fg: #0d1117;\n --tb-primary-bg-hover: #ffffff;\n --tb-secondary-bg: #15191f;\n --tb-secondary-fg: #f0f6fc;\n --tb-secondary-bg-hover: #1f2328;\n --tb-link: #4493f8;\n }\n}\n:where(.tb-root[data-tb-theme=\"dark\"]) {\n --tb-bg: #0d1117;\n --tb-fg: #f0f6fc;\n --tb-fg-muted: #9198a1;\n --tb-border: #30363d;\n --tb-shadow: 0 8px 24px rgba(1, 4, 9, 0.85);\n --tb-primary-bg: #f0f6fc;\n --tb-primary-fg: #0d1117;\n --tb-primary-bg-hover: #ffffff;\n --tb-secondary-bg: #15191f;\n --tb-secondary-fg: #f0f6fc;\n --tb-secondary-bg-hover: #1f2328;\n --tb-link: #4493f8;\n}\n\n.tb-banner {\n position: fixed;\n left: 16px;\n right: 16px;\n bottom: 16px;\n max-width: 1100px;\n margin-inline: auto;\n z-index: var(--tb-z);\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n padding: 16px 20px;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-banner-text {\n flex: 1 1 320px;\n min-width: 0;\n}\n.tb-banner-title {\n font-weight: 600;\n margin: 0 0 2px;\n font-size: 14px;\n}\n.tb-banner-desc {\n margin: 0;\n color: var(--tb-fg-muted);\n}\n.tb-banner-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.tb-notice {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: var(--tb-z);\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n padding: 14px 16px;\n max-width: 360px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-notice-title {\n font-weight: 600;\n margin: 0 0 4px;\n font-size: 14px;\n}\n.tb-notice-desc {\n margin: 0 0 10px;\n color: var(--tb-fg-muted);\n font-size: 13px;\n}\n.tb-notice-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n justify-content: flex-end;\n flex-wrap: wrap;\n}\n\n.tb-link {\n color: var(--tb-link);\n text-decoration: none;\n font-size: 13px;\n margin-right: auto;\n}\n.tb-link:hover { text-decoration: underline; }\n\n.tb-branding {\n /* Own row in the wrapping flex banner; negative margins claw back the\n parent's 16px gap + the banner's 16px bottom padding so the credit\n line sits tight to the bottom edge instead of floating in dead space.\n On .tb-notice (block flow) the negative margins are reset below. */\n flex-basis: 100%;\n margin: -12px 0 -10px;\n padding: 0;\n font-size: 11px;\n color: var(--tb-fg-muted);\n text-align: center;\n opacity: 0.55;\n letter-spacing: 0.01em;\n}\n.tb-notice .tb-branding {\n margin: 4px 0 -4px;\n text-align: right;\n}\n.tb-branding a {\n color: inherit;\n text-decoration: none;\n}\n.tb-branding a:hover { text-decoration: underline; opacity: 1; }\n\n.tb-btn {\n appearance: none;\n border: 1px solid transparent;\n border-radius: var(--tb-radius);\n padding: 6px 14px;\n font-size: 13px;\n font-weight: 500;\n font-family: inherit;\n cursor: pointer;\n line-height: 1.5;\n transition: background-color 80ms ease;\n white-space: nowrap;\n}\n.tb-btn:focus-visible {\n outline: 2px solid var(--tb-link);\n outline-offset: 2px;\n}\n.tb-btn-primary {\n background: var(--tb-primary-bg);\n color: var(--tb-primary-fg);\n}\n.tb-btn-primary:hover { background: var(--tb-primary-bg-hover); }\n.tb-btn-secondary {\n background: var(--tb-secondary-bg);\n color: var(--tb-secondary-fg);\n border-color: var(--tb-border);\n}\n.tb-btn-secondary:hover { background: var(--tb-secondary-bg-hover); }\n/*\n * Style for Accept All and Reject All on the first banner layer. They MUST\n * look identical — UK ICO and EU EDPB treat unequal visual weight on those\n * buttons as a dark pattern. Customisation should not break this symmetry;\n * if you need to apply brand colours, apply them here, not to one button.\n */\n.tb-btn-equal {\n background: var(--tb-secondary-bg);\n color: var(--tb-secondary-fg);\n border-color: var(--tb-border);\n}\n.tb-btn-equal:hover { background: var(--tb-secondary-bg-hover); }\n.tb-btn-ghost {\n background: transparent;\n color: var(--tb-fg-muted);\n padding: 6px 10px;\n}\n.tb-btn-ghost:hover { color: var(--tb-fg); }\n\n.tb-modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(15, 18, 24, 0.5);\n z-index: var(--tb-z);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-modal {\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n width: 100%;\n max-width: 520px;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n}\n.tb-modal-head {\n padding: 14px 16px;\n border-bottom: 1px solid var(--tb-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n.tb-modal-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n}\n.tb-modal-body {\n padding: 12px 16px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.tb-modal-foot {\n padding: 12px 16px;\n border-top: 1px solid var(--tb-border);\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n flex-wrap: wrap;\n}\n\n.tb-cat {\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n padding: 12px;\n display: flex;\n gap: 12px;\n align-items: flex-start;\n}\n.tb-cat-text { flex: 1; min-width: 0; }\n.tb-cat-name {\n font-weight: 600;\n margin: 0 0 2px;\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.tb-cat-desc {\n margin: 0;\n color: var(--tb-fg-muted);\n font-size: 13px;\n}\n.tb-badge {\n display: inline-block;\n font-size: 11px;\n font-weight: 500;\n color: var(--tb-fg-muted);\n background: var(--tb-secondary-bg-hover);\n border: 1px solid var(--tb-border);\n border-radius: 999px;\n padding: 1px 8px;\n}\n\n.tb-switch {\n position: relative;\n display: inline-block;\n width: 32px;\n height: 18px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n.tb-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n position: absolute;\n}\n.tb-switch-track {\n position: absolute;\n inset: 0;\n background: var(--tb-border);\n border-radius: 999px;\n transition: background-color 100ms ease;\n cursor: pointer;\n}\n.tb-switch-thumb {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 14px;\n height: 14px;\n background: var(--tb-bg);\n border-radius: 50%;\n transition: transform 100ms ease;\n}\n.tb-switch input:checked + .tb-switch-track {\n background: var(--tb-primary-bg);\n}\n.tb-switch input:checked + .tb-switch-track .tb-switch-thumb {\n transform: translateX(14px);\n}\n.tb-switch input:disabled + .tb-switch-track {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.tb-switch input:focus-visible + .tb-switch-track {\n outline: 2px solid var(--tb-link);\n outline-offset: 2px;\n}\n\n@keyframes tb-fade-in {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@media (max-width: 640px) {\n .tb-banner {\n flex-direction: column;\n align-items: stretch;\n }\n .tb-banner-actions {\n flex-direction: column;\n }\n .tb-banner-actions .tb-btn { width: 100%; }\n}\n`\n\nconst STYLE_ID = 'tickbox-default-styles'\n\nlet injected = false\n\n/**\n * Insert the stylesheet into `<head>` exactly once per page. Safe to call\n * from every component mount — subsequent calls are no-ops. No-op on the\n * server (no `document`).\n */\nexport function injectStyles(): void {\n if (injected) return\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ID)) {\n injected = true\n return\n }\n const el = document.createElement('style')\n el.id = STYLE_ID\n el.textContent = TICKBOX_STYLES\n document.head.appendChild(el)\n injected = true\n}\n","/**\n * Runtime theme fetcher + cache for @tickboxhq/banner-default.\n *\n * The compliance contract (jurisdiction, categories, vendors) stays in the\n * customer's consent.config.ts under PR review. Presentation only — copy,\n * colours, locale — can be edited remotely in the dashboard and fetched\n * by the SDK at runtime so marketing teams can iterate without a deploy.\n *\n * Fetch strategy: read cached theme from localStorage and apply\n * synchronously on mount (no flicker), then refresh in the background.\n * Updated cache is written back; the *next* page load picks up the change.\n * Failed fetches are silent — the banner keeps rendering with whatever\n * cache or code defaults it had.\n */\n\nconst DEFAULT_ENDPOINT = 'https://api.tickbox.dev'\nconst STORAGE_KEY_PREFIX = '__tb_theme_'\n/** Treat cache older than 24h as suspect. We still render with it (no\n * flicker), but we always refetch — same as a CDN with stale-while-revalidate. */\nconst STALE_AFTER_MS = 24 * 60 * 60 * 1000\n\nexport type RemoteTheme = {\n copy?: {\n title?: string\n body?: string\n buttonAccept?: string\n buttonReject?: string\n buttonCustomise?: string\n privacyLink?: string\n }\n colors?: {\n accent?: string\n background?: string\n text?: string\n }\n locale?: string\n}\n\ntype CachedTheme = {\n theme: RemoteTheme\n fetchedAt: number\n}\n\n/** Synchronous read of the last cached theme. Used on mount to render\n * immediately without waiting for a network round-trip. */\nexport function readCachedTheme(siteId: string): RemoteTheme | null {\n if (typeof localStorage === 'undefined') return null\n try {\n const raw = localStorage.getItem(STORAGE_KEY_PREFIX + siteId)\n if (!raw) return null\n const parsed = JSON.parse(raw) as CachedTheme\n if (parsed && typeof parsed === 'object' && parsed.theme) {\n return parsed.theme\n }\n return null\n } catch {\n return null\n }\n}\n\n/** Fetch the latest theme from the API, write to cache. Returns the fresh\n * theme on success, `null` on any failure (caller already rendered the\n * cached one). */\nexport async function refreshTheme(\n siteId: string,\n endpoint: string = DEFAULT_ENDPOINT,\n): Promise<RemoteTheme | null> {\n if (typeof fetch === 'undefined') return null\n const url = `${endpoint.replace(/\\/+$/, '')}/v1/theme/${encodeURIComponent(siteId)}`\n try {\n const res = await fetch(url, { method: 'GET' })\n if (!res.ok) return null\n const body = (await res.json()) as { theme?: RemoteTheme }\n const theme = body?.theme ?? {}\n if (typeof localStorage !== 'undefined') {\n try {\n localStorage.setItem(\n STORAGE_KEY_PREFIX + siteId,\n JSON.stringify({ theme, fetchedAt: Date.now() } satisfies CachedTheme),\n )\n } catch {\n // Storage quota or disabled — fail silently. The banner still\n // renders with whatever's currently in memory.\n }\n }\n return theme\n } catch {\n return null\n }\n}\n\n/** Indicates whether the cached theme is older than the stale-after window.\n * Currently informational; we refetch on every mount regardless. Exposed\n * so future call sites can decide to skip the refetch (e.g. SSR). */\nexport function isStale(siteId: string): boolean {\n if (typeof localStorage === 'undefined') return true\n try {\n const raw = localStorage.getItem(STORAGE_KEY_PREFIX + siteId)\n if (!raw) return true\n const parsed = JSON.parse(raw) as CachedTheme\n return Date.now() - parsed.fetchedAt > STALE_AFTER_MS\n } catch {\n return true\n }\n}\n\n/**\n * Apply a theme's colour overrides as CSS custom properties on the banner\n * root element. The banner's own stylesheet reads these variables (or falls\n * back to defaults if unset), so theming is a matter of setting the right\n * --tb-* values.\n *\n * sanitise() drops anything outside the CSS-colour character set as defence\n * in depth against attribute injection — the values arrive via fetch from\n * api.tickbox.dev, so a compromise of the dashboard could only inject\n * benign CSS at worst.\n */\nexport function applyThemeColors(root: HTMLElement, theme: RemoteTheme | null): void {\n if (!theme?.colors) return\n if (theme.colors.accent)\n root.style.setProperty('--tb-primary-bg', sanitiseColor(theme.colors.accent))\n if (theme.colors.background)\n root.style.setProperty('--tb-bg', sanitiseColor(theme.colors.background))\n if (theme.colors.text) root.style.setProperty('--tb-fg', sanitiseColor(theme.colors.text))\n}\n\nfunction sanitiseColor(v: string): string {\n // Permit common CSS-colour characters: hex digits, color() / rgb() /\n // hsl() / oklch() function tokens. Block <, >, &, \", ' and newlines.\n return v.replace(/[<>&\"'\\n\\r\\\\]/g, '').slice(0, 64)\n}\n\n/**\n * Merge a RemoteTheme's `copy` block into a partial BannerCopy passed by\n * the caller (the customer's `copy` prop on <ConsentBannerDefault>). The\n * remote theme wins where set; code defaults fill in the rest at render\n * time via resolveLocalePack.\n */\nexport function mergeCopy<T extends Record<string, string>>(\n base: Partial<T>,\n remote: RemoteTheme['copy'] | undefined,\n): Partial<T> {\n if (!remote) return base\n const out: Record<string, string> = { ...(base as Record<string, string>) }\n if (remote.title) out.title = remote.title\n if (remote.body) out.description = remote.body\n if (remote.buttonAccept) out.acceptLabel = remote.buttonAccept\n if (remote.buttonReject) out.rejectLabel = remote.buttonReject\n if (remote.buttonCustomise) out.customiseLabel = remote.buttonCustomise\n if (remote.privacyLink) out.policyLinkLabel = remote.privacyLink\n return out as Partial<T>\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { readCachedTheme, injectStyles, refreshTheme, mergeCopy, resolveLocalePack, applyThemeColors } from '../chunk-SV4PBHBY.js';
2
- export { locales, resolveLocalePack } from '../chunk-SV4PBHBY.js';
1
+ import { readCachedTheme, injectStyles, refreshTheme, mergeCopy, resolveLocalePack, applyThemeColors } from '../chunk-GYKO2N23.js';
2
+ export { locales, resolveLocalePack } from '../chunk-GYKO2N23.js';
3
3
  import { ConsentBanner, ConsentNotice } from '@tickboxhq/react';
4
4
  import { useState, useEffect, useRef, useId } from 'react';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
package/dist/vue/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { readCachedTheme, injectStyles, refreshTheme, mergeCopy, resolveLocalePack, applyThemeColors } from '../chunk-SV4PBHBY.js';
2
- export { locales, resolveLocalePack } from '../chunk-SV4PBHBY.js';
1
+ import { readCachedTheme, injectStyles, refreshTheme, mergeCopy, resolveLocalePack, applyThemeColors } from '../chunk-GYKO2N23.js';
2
+ export { locales, resolveLocalePack } from '../chunk-GYKO2N23.js';
3
3
  import { ConsentBanner, ConsentNotice } from '@tickboxhq/vue';
4
4
  import { defineComponent, ref, onMounted, h, watch } from 'vue';
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tickboxhq/banner-default",
3
- "version": "0.1.2",
3
+ "version": "1.0.0",
4
4
  "description": "Drop-in styled consent banner and notice components for Tickbox",
5
5
  "license": "MIT",
6
6
  "homepage": "https://tickbox.dev",
@@ -29,13 +29,13 @@
29
29
  "access": "public"
30
30
  },
31
31
  "dependencies": {
32
- "@tickboxhq/core": "0.1.2"
32
+ "@tickboxhq/core": "1.0.0"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "react": "^18.0.0 || ^19.0.0",
36
36
  "vue": "^3.4.0",
37
- "@tickboxhq/react": "0.1.2",
38
- "@tickboxhq/vue": "0.1.2"
37
+ "@tickboxhq/react": "1.0.0",
38
+ "@tickboxhq/vue": "1.0.0"
39
39
  },
40
40
  "peerDependenciesMeta": {
41
41
  "@tickboxhq/react": {
@@ -61,8 +61,8 @@
61
61
  "typescript": "^5.7.2",
62
62
  "vitest": "^2.1.8",
63
63
  "vue": "^3.5.13",
64
- "@tickboxhq/react": "0.1.2",
65
- "@tickboxhq/vue": "0.1.2"
64
+ "@tickboxhq/vue": "1.0.0",
65
+ "@tickboxhq/react": "1.0.0"
66
66
  },
67
67
  "scripts": {
68
68
  "build": "tsup",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shared/locales/de.ts","../src/shared/locales/en.ts","../src/shared/locales/es.ts","../src/shared/locales/fr.ts","../src/shared/locales/it.ts","../src/shared/locales/nl.ts","../src/shared/locales/pl.ts","../src/shared/locales/pt.ts","../src/shared/locales/uk.ts","../src/shared/locales/index.ts","../src/shared/styles.ts","../src/shared/theme.ts"],"names":["banner","notice"],"mappings":";AAEO,IAAM,MAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,sBAAA;AAAA,EACP,WAAA,EACE,kKAAA;AAAA,EACF,WAAA,EAAa,kBAAA;AAAA,EACb,WAAA,EAAa,eAAA;AAAA,EACb,cAAA,EAAgB,UAAA;AAAA,EAChB,SAAA,EAAW,yBAAA;AAAA,EACX,UAAA,EAAY,cAAA;AAAA,EACZ,eAAA,EAAiB,aAAA;AAAA,EACjB,aAAA,EAAe;AACjB,CAAA;AAEO,IAAM,MAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,qBAAA;AAAA,EACP,WAAA,EACE,uLAAA;AAAA,EACF,gBAAA,EAAkB,YAAA;AAAA,EAClB,WAAA,EAAa,UAAA;AAAA,EACb,eAAA,EAAiB;AACnB,CAAA;;;ACpBO,IAAMA,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,sBAAA;AAAA,EACP,WAAA,EACE,+GAAA;AAAA,EACF,WAAA,EAAa,YAAA;AAAA,EACb,WAAA,EAAa,YAAA;AAAA,EACb,cAAA,EAAgB,WAAA;AAAA,EAChB,SAAA,EAAW,kBAAA;AAAA,EACX,UAAA,EAAY,OAAA;AAAA,EACZ,eAAA,EAAiB,gBAAA;AAAA,EACjB,aAAA,EAAe;AACjB,CAAA;AAEO,IAAMC,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,wBAAA;AAAA,EACP,WAAA,EACE,6IAAA;AAAA,EACF,gBAAA,EAAkB,QAAA;AAAA,EAClB,WAAA,EAAa,SAAA;AAAA,EACb,eAAA,EAAiB;AACnB,CAAA;;;ACpBO,IAAMD,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,uBAAA;AAAA,EACP,WAAA,EACE,4HAAA;AAAA,EACF,WAAA,EAAa,cAAA;AAAA,EACb,WAAA,EAAa,eAAA;AAAA,EACb,cAAA,EAAgB,cAAA;AAAA,EAChB,SAAA,EAAW,sBAAA;AAAA,EACX,UAAA,EAAY,QAAA;AAAA,EACZ,eAAA,EAAiB,2BAAA;AAAA,EACjB,aAAA,EAAe;AACjB,CAAA;AAEO,IAAMC,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,uBAAA;AAAA,EACP,WAAA,EACE,kKAAA;AAAA,EACF,gBAAA,EAAkB,WAAA;AAAA,EAClB,WAAA,EAAa,UAAA;AAAA,EACb,eAAA,EAAiB;AACnB,CAAA;;;ACpBO,IAAMD,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,kBAAA;AAAA,EACP,WAAA,EACE,6JAAA;AAAA,EACF,WAAA,EAAa,eAAA;AAAA,EACb,WAAA,EAAa,cAAA;AAAA,EACb,cAAA,EAAgB,eAAA;AAAA,EAChB,SAAA,EAAW,mCAAA;AAAA,EACX,UAAA,EAAY,QAAA;AAAA,EACZ,eAAA,EAAiB,iCAAA;AAAA,EACjB,aAAA,EAAe;AACjB,CAAA;AAEO,IAAMC,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,0BAAA;AAAA,EACP,WAAA,EACE,8MAAA;AAAA,EACF,gBAAA,EAAkB,SAAA;AAAA,EAClB,WAAA,EAAa,SAAA;AAAA,EACb,eAAA,EAAiB;AACnB,CAAA;;;ACpBO,IAAMD,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,uBAAA;AAAA,EACP,WAAA,EACE,mIAAA;AAAA,EACF,WAAA,EAAa,eAAA;AAAA,EACb,WAAA,EAAa,eAAA;AAAA,EACb,cAAA,EAAgB,cAAA;AAAA,EAChB,SAAA,EAAW,kBAAA;AAAA,EACX,UAAA,EAAY,QAAA;AAAA,EACZ,eAAA,EAAiB,2BAAA;AAAA,EACjB,aAAA,EAAe;AACjB,CAAA;AAEO,IAAMC,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,mBAAA;AAAA,EACP,WAAA,EACE,uKAAA;AAAA,EACF,gBAAA,EAAkB,WAAA;AAAA,EAClB,WAAA,EAAa,SAAA;AAAA,EACb,eAAA,EAAiB;AACnB,CAAA;;;ACpBO,IAAMD,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,qBAAA;AAAA,EACP,WAAA,EACE,6HAAA;AAAA,EACF,WAAA,EAAa,kBAAA;AAAA,EACb,WAAA,EAAa,gBAAA;AAAA,EACb,cAAA,EAAgB,WAAA;AAAA,EAChB,SAAA,EAAW,oBAAA;AAAA,EACX,UAAA,EAAY,SAAA;AAAA,EACZ,eAAA,EAAiB,eAAA;AAAA,EACjB,aAAA,EAAe;AACjB,CAAA;AAEO,IAAMC,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,gBAAA;AAAA,EACP,WAAA,EACE,uLAAA;AAAA,EACF,gBAAA,EAAkB,UAAA;AAAA,EAClB,WAAA,EAAa,UAAA;AAAA,EACb,eAAA,EAAiB;AACnB,CAAA;;;ACpBO,IAAMD,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,+BAAA;AAAA,EACP,WAAA,EACE,uLAAA;AAAA,EACF,WAAA,EAAa,sBAAA;AAAA,EACb,WAAA,EAAa,uBAAA;AAAA,EACb,cAAA,EAAgB,UAAA;AAAA,EAChB,SAAA,EAAW,oBAAA;AAAA,EACX,UAAA,EAAY,SAAA;AAAA,EACZ,eAAA,EAAiB,2BAAA;AAAA,EACjB,aAAA,EAAe;AACjB,CAAA;AAEO,IAAMC,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,aAAA;AAAA,EACP,WAAA,EACE,iLAAA;AAAA,EACF,gBAAA,EAAkB,UAAA;AAAA,EAClB,WAAA,EAAa,WAAA;AAAA,EACb,eAAA,EAAiB;AACnB,CAAA;;;ACpBO,IAAMD,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,oBAAA;AAAA,EACP,WAAA,EACE,6IAAA;AAAA,EACF,WAAA,EAAa,cAAA;AAAA,EACb,WAAA,EAAa,eAAA;AAAA,EACb,cAAA,EAAgB,cAAA;AAAA,EAChB,SAAA,EAAW,yBAAA;AAAA,EACX,UAAA,EAAY,QAAA;AAAA,EACZ,eAAA,EAAiB,4BAAA;AAAA,EACjB,aAAA,EAAe;AACjB,CAAA;AAEO,IAAMC,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,oBAAA;AAAA,EACP,WAAA,EACE,wKAAA;AAAA,EACF,gBAAA,EAAkB,SAAA;AAAA,EAClB,WAAA,EAAa,SAAA;AAAA,EACb,eAAA,EAAiB;AACnB,CAAA;;;ACpBO,IAAMD,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,uHAAA;AAAA,EACP,WAAA,EACE,ipBAAA;AAAA,EACF,WAAA,EAAa,qEAAA;AAAA,EACb,WAAA,EAAa,2EAAA;AAAA,EACb,cAAA,EAAgB,oEAAA;AAAA,EAChB,SAAA,EAAW,2HAAA;AAAA,EACX,UAAA,EAAY,4CAAA;AAAA,EACZ,eAAA,EAAiB,mJAAA;AAAA,EACjB,aAAA,EAAe;AACjB,CAAA;AAEO,IAAMC,OAAAA,GAAqB;AAAA,EAChC,KAAA,EAAO,2EAAA;AAAA,EACP,WAAA,EACE,40BAAA;AAAA,EACF,gBAAA,EAAkB,wDAAA;AAAA,EAClB,WAAA,EAAa,oEAAA;AAAA,EACb,eAAA,EAAiB;AACnB,CAAA;;;ACNO,IAAM,OAAA,GAAsC;AAAA,EACjD,EAAA,EAAI,EAAE,MAAA,EAAWD,OAAAA,EAAQ,QAAWC,OAAAA,EAAO;AAAA,EAC3C,EAAA,EAAI,EAAE,MAAA,EAAmB,MAAA,EAAkB;AAAA,EAC3C,EAAA,EAAI,EAAE,MAAA,EAAWD,OAAAA,EAAQ,QAAWC,OAAAA,EAAO;AAAA,EAC3C,EAAA,EAAI,EAAE,MAAA,EAAWD,OAAAA,EAAQ,QAAWC,OAAAA,EAAO;AAAA,EAC3C,EAAA,EAAI,EAAE,MAAA,EAAWD,OAAAA,EAAQ,QAAWC,OAAAA,EAAO;AAAA,EAC3C,EAAA,EAAI,EAAE,MAAA,EAAWD,OAAAA,EAAQ,QAAWC,OAAAA,EAAO;AAAA,EAC3C,EAAA,EAAI,EAAE,MAAA,EAAWD,OAAAA,EAAQ,QAAWC,OAAAA,EAAO;AAAA,EAC3C,EAAA,EAAI,EAAE,MAAA,EAAWD,OAAAA,EAAQ,QAAWC,OAAAA,EAAO;AAAA,EAC3C,EAAA,EAAI,EAAE,MAAA,EAAWD,OAAAA,EAAQ,QAAWC,OAAAA;AACtC;AAWO,SAAS,kBAAkB,MAAA,EAAwC;AACxE,EAAA,IAAI,CAAC,MAAA,EAAQ,OAAO,OAAA,CAAQ,EAAA;AAC5B,EAAA,MAAM,GAAA,GAAM,MAAA,KAAW,MAAA,GAAS,qBAAA,EAAsB,GAAI,MAAA;AAC1D,EAAA,IAAI,CAAC,GAAA,EAAK,OAAO,OAAA,CAAQ,EAAA;AACzB,EAAA,MAAM,KAAA,GAAQ,IAAI,WAAA,EAAY;AAC9B,EAAA,IAAI,OAAA,CAAQ,KAAK,CAAA,EAAG,OAAO,QAAQ,KAAK,CAAA;AACxC,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,KAAA,CAAM,GAAG,EAAE,CAAC,CAAA;AACjC,EAAA,IAAI,UAAU,OAAA,CAAQ,MAAM,CAAA,EAAG,OAAO,QAAQ,MAAM,CAAA;AACpD,EAAA,OAAO,OAAA,CAAQ,EAAA;AACjB;AAEA,SAAS,qBAAA,GAA4C;AACnD,EAAA,IAAI,OAAO,SAAA,KAAc,WAAA,EAAa,OAAO,MAAA;AAC7C,EAAA,OAAO,SAAA,CAAU,QAAA;AACnB;;;ACzCO,IAAM,cAAA,GAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAqW9B,IAAM,QAAA,GAAW,wBAAA;AAEjB,IAAI,QAAA,GAAW,KAAA;AAOR,SAAS,YAAA,GAAqB;AACnC,EAAA,IAAI,QAAA,EAAU;AACd,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACrC,EAAA,IAAI,QAAA,CAAS,cAAA,CAAe,QAAQ,CAAA,EAAG;AACrC,IAAA,QAAA,GAAW,IAAA;AACX,IAAA;AAAA,EACF;AACA,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AACzC,EAAA,EAAA,CAAG,EAAA,GAAK,QAAA;AACR,EAAA,EAAA,CAAG,WAAA,GAAc,cAAA;AACjB,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,EAAE,CAAA;AAC5B,EAAA,QAAA,GAAW,IAAA;AACb;;;ACrXA,IAAM,gBAAA,GAAmB,yBAAA;AACzB,IAAM,kBAAA,GAAqB,aAAA;AA6BpB,SAAS,gBAAgB,MAAA,EAAoC;AAClE,EAAA,IAAI,OAAO,YAAA,KAAiB,WAAA,EAAa,OAAO,IAAA;AAChD,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,YAAA,CAAa,OAAA,CAAQ,kBAAA,GAAqB,MAAM,CAAA;AAC5D,IAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,OAAO,KAAA,EAAO;AACxD,MAAA,OAAO,MAAA,CAAO,KAAA;AAAA,IAChB;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAKA,eAAsB,YAAA,CACpB,MAAA,EACA,QAAA,GAAmB,gBAAA,EACU;AAC7B,EAAA,IAAI,OAAO,KAAA,KAAU,WAAA,EAAa,OAAO,IAAA;AACzC,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,QAAA,CAAS,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAC,CAAA,UAAA,EAAa,kBAAA,CAAmB,MAAM,CAAC,CAAA,CAAA;AAClF,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,MAAM,KAAA,CAAM,KAAK,EAAE,MAAA,EAAQ,OAAO,CAAA;AAC9C,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,OAAO,IAAA;AACpB,IAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,IAAA,MAAM,KAAA,GAAQ,IAAA,EAAM,KAAA,IAAS,EAAC;AAC9B,IAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,MAAA,IAAI;AACF,QAAA,YAAA,CAAa,OAAA;AAAA,UACX,kBAAA,GAAqB,MAAA;AAAA,UACrB,IAAA,CAAK,UAAU,EAAE,KAAA,EAAO,WAAW,IAAA,CAAK,GAAA,IAA6B;AAAA,SACvE;AAAA,MACF,CAAA,CAAA,MAAQ;AAAA,MAGR;AAAA,IACF;AACA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AA4BO,SAAS,gBAAA,CAAiB,MAAmB,KAAA,EAAiC;AACnF,EAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AACpB,EAAA,IAAI,MAAM,MAAA,CAAO,MAAA;AACf,IAAA,IAAA,CAAK,MAAM,WAAA,CAAY,iBAAA,EAAmB,cAAc,KAAA,CAAM,MAAA,CAAO,MAAM,CAAC,CAAA;AAC9E,EAAA,IAAI,MAAM,MAAA,CAAO,UAAA;AACf,IAAA,IAAA,CAAK,MAAM,WAAA,CAAY,SAAA,EAAW,cAAc,KAAA,CAAM,MAAA,CAAO,UAAU,CAAC,CAAA;AAC1E,EAAA,IAAI,KAAA,CAAM,MAAA,CAAO,IAAA,EAAM,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,SAAA,EAAW,aAAA,CAAc,KAAA,CAAM,MAAA,CAAO,IAAI,CAAC,CAAA;AAC3F;AAEA,SAAS,cAAc,CAAA,EAAmB;AAGxC,EAAA,OAAO,EAAE,OAAA,CAAQ,gBAAA,EAAkB,EAAE,CAAA,CAAE,KAAA,CAAM,GAAG,EAAE,CAAA;AACpD;AAQO,SAAS,SAAA,CACd,MACA,MAAA,EACY;AACZ,EAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AACpB,EAAA,MAAM,GAAA,GAA8B,EAAE,GAAI,IAAA,EAAgC;AAC1E,EAAA,IAAI,MAAA,CAAO,KAAA,EAAO,GAAA,CAAI,KAAA,GAAQ,MAAA,CAAO,KAAA;AACrC,EAAA,IAAI,MAAA,CAAO,IAAA,EAAM,GAAA,CAAI,WAAA,GAAc,MAAA,CAAO,IAAA;AAC1C,EAAA,IAAI,MAAA,CAAO,YAAA,EAAc,GAAA,CAAI,WAAA,GAAc,MAAA,CAAO,YAAA;AAClD,EAAA,IAAI,MAAA,CAAO,YAAA,EAAc,GAAA,CAAI,WAAA,GAAc,MAAA,CAAO,YAAA;AAClD,EAAA,IAAI,MAAA,CAAO,eAAA,EAAiB,GAAA,CAAI,cAAA,GAAiB,MAAA,CAAO,eAAA;AACxD,EAAA,IAAI,MAAA,CAAO,WAAA,EAAa,GAAA,CAAI,eAAA,GAAkB,MAAA,CAAO,WAAA;AACrD,EAAA,OAAO,GAAA;AACT","file":"chunk-SV4PBHBY.js","sourcesContent":["import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies und Tracking',\n description:\n 'Wir verwenden Cookies, damit diese Website funktioniert, und mit Ihrer Einwilligung, um die Nutzung zu messen. Sie können selbst wählen, was Sie zulassen.',\n acceptLabel: 'Alle akzeptieren',\n rejectLabel: 'Alle ablehnen',\n customiseLabel: 'Anpassen',\n saveLabel: 'Einstellungen speichern',\n closeLabel: 'Schließen',\n policyLinkLabel: 'Datenschutz',\n requiredBadge: 'Erforderlich',\n}\n\nexport const notice: NoticeCopy = {\n title: 'Hinweis zur Analyse',\n description:\n 'Wir verwenden datenschutzfreundliche Analyse-Tools, um zu verstehen, wie diese Website genutzt wird. Es werden keine personenbezogenen Daten erhoben und keine Werbeprofile erstellt.',\n acknowledgeLabel: 'Verstanden',\n optOutLabel: 'Ablehnen',\n policyLinkLabel: 'Datenschutz',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies and tracking',\n description:\n 'We use cookies to make this site work and, with your consent, to measure usage. You can choose what to allow.',\n acceptLabel: 'Accept all',\n rejectLabel: 'Reject all',\n customiseLabel: 'Customise',\n saveLabel: 'Save preferences',\n closeLabel: 'Close',\n policyLinkLabel: 'Privacy policy',\n requiredBadge: 'Required',\n}\n\nexport const notice: NoticeCopy = {\n title: 'A note about analytics',\n description:\n 'We use privacy-friendly analytics to understand how this site is used. No personal data is collected and no advertising profiles are built.',\n acknowledgeLabel: 'Got it',\n optOutLabel: 'Opt out',\n policyLinkLabel: 'Privacy policy',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies y seguimiento',\n description:\n 'Utilizamos cookies para que este sitio funcione y, con tu consentimiento, para medir su uso. Tú eliges lo que permites.',\n acceptLabel: 'Aceptar todo',\n rejectLabel: 'Rechazar todo',\n customiseLabel: 'Personalizar',\n saveLabel: 'Guardar preferencias',\n closeLabel: 'Cerrar',\n policyLinkLabel: 'Política de privacidad',\n requiredBadge: 'Obligatorio',\n}\n\nexport const notice: NoticeCopy = {\n title: 'Sobre la analítica',\n description:\n 'Utilizamos analítica respetuosa con la privacidad para entender cómo se usa este sitio. No recopilamos datos personales ni creamos perfiles publicitarios.',\n acknowledgeLabel: 'Entendido',\n optOutLabel: 'Rechazar',\n policyLinkLabel: 'Política de privacidad',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies et suivi',\n description:\n 'Nous utilisons des cookies pour faire fonctionner ce site et, avec votre consentement, pour mesurer son utilisation. Vous choisissez ce que vous autorisez.',\n acceptLabel: 'Tout accepter',\n rejectLabel: 'Tout refuser',\n customiseLabel: 'Personnaliser',\n saveLabel: 'Enregistrer les préférences',\n closeLabel: 'Fermer',\n policyLinkLabel: 'Politique de confidentialité',\n requiredBadge: 'Requis',\n}\n\nexport const notice: NoticeCopy = {\n title: \"À propos de l'analyse\",\n description:\n \"Nous utilisons une analyse respectueuse de la vie privée pour comprendre comment ce site est utilisé. Aucune donnée personnelle n'est collectée et aucun profil publicitaire n'est constitué.\",\n acknowledgeLabel: 'Compris',\n optOutLabel: 'Refuser',\n policyLinkLabel: 'Politique de confidentialité',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookie e tracciamento',\n description:\n \"Utilizziamo i cookie per far funzionare il sito e, con il tuo consenso, per misurarne l'utilizzo. Puoi scegliere cosa consentire.\",\n acceptLabel: 'Accetta tutto',\n rejectLabel: 'Rifiuta tutto',\n customiseLabel: 'Personalizza',\n saveLabel: 'Salva preferenze',\n closeLabel: 'Chiudi',\n policyLinkLabel: 'Informativa sulla privacy',\n requiredBadge: 'Obbligatorio',\n}\n\nexport const notice: NoticeCopy = {\n title: \"Nota sull'analisi\",\n description:\n 'Utilizziamo strumenti di analisi rispettosi della privacy per capire come viene usato questo sito. Non raccogliamo dati personali né creiamo profili pubblicitari.',\n acknowledgeLabel: 'Ho capito',\n optOutLabel: 'Rifiuta',\n policyLinkLabel: 'Informativa sulla privacy',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies en tracking',\n description:\n 'Wij gebruiken cookies om deze site te laten werken en, met uw toestemming, om het gebruik te meten. U kiest wat u toestaat.',\n acceptLabel: 'Alles accepteren',\n rejectLabel: 'Alles weigeren',\n customiseLabel: 'Aanpassen',\n saveLabel: 'Voorkeuren opslaan',\n closeLabel: 'Sluiten',\n policyLinkLabel: 'Privacybeleid',\n requiredBadge: 'Vereist',\n}\n\nexport const notice: NoticeCopy = {\n title: 'Over analytics',\n description:\n 'Wij gebruiken privacyvriendelijke analytics om te begrijpen hoe deze site wordt gebruikt. Er worden geen persoonsgegevens verzameld en er worden geen advertentieprofielen opgebouwd.',\n acknowledgeLabel: 'Begrepen',\n optOutLabel: 'Afmelden',\n policyLinkLabel: 'Privacybeleid',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Pliki cookie i śledzenie',\n description:\n 'Używamy plików cookie, aby ta strona działała, oraz – za Twoją zgodą – aby mierzyć jej użycie. To Ty decydujesz, na co pozwolić.',\n acceptLabel: 'Zaakceptuj wszystkie',\n rejectLabel: 'Odrzuć wszystkie',\n customiseLabel: 'Dostosuj',\n saveLabel: 'Zapisz preferencje',\n closeLabel: 'Zamknij',\n policyLinkLabel: 'Polityka prywatności',\n requiredBadge: 'Wymagane',\n}\n\nexport const notice: NoticeCopy = {\n title: 'O analityce',\n description:\n 'Używamy analityki przyjaznej prywatności, aby zrozumieć, jak korzysta się z tej strony. Nie zbieramy danych osobowych ani nie tworzymy profili reklamowych.',\n acknowledgeLabel: 'Rozumiem',\n optOutLabel: 'Zrezygnuj',\n policyLinkLabel: 'Polityka prywatności',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Cookies e rastreio',\n description:\n 'Utilizamos cookies para que este site funcione e, com o seu consentimento, para medir a sua utilização. Pode escolher o que permitir.',\n acceptLabel: 'Aceitar tudo',\n rejectLabel: 'Rejeitar tudo',\n customiseLabel: 'Personalizar',\n saveLabel: 'Guardar preferências',\n closeLabel: 'Fechar',\n policyLinkLabel: 'Política de privacidade',\n requiredBadge: 'Obrigatório',\n}\n\nexport const notice: NoticeCopy = {\n title: 'Sobre a análise',\n description:\n 'Utilizamos análises respeitadoras da privacidade para perceber como este site é utilizado. Não recolhemos dados pessoais nem criamos perfis publicitários.',\n acknowledgeLabel: 'Entendi',\n optOutLabel: 'Recusar',\n policyLinkLabel: 'Política de privacidade',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\n\nexport const banner: BannerCopy = {\n title: 'Файли cookie та відстеження',\n description:\n 'Ми використовуємо файли cookie, щоб цей сайт працював, а за вашою згодою — щоб вимірювати використання. Ви самі обираєте, що дозволити.',\n acceptLabel: 'Прийняти всі',\n rejectLabel: 'Відхилити всі',\n customiseLabel: 'Налаштувати',\n saveLabel: 'Зберегти налаштування',\n closeLabel: 'Закрити',\n policyLinkLabel: 'Політика конфіденційності',\n requiredBadge: \"Обов'язково\",\n}\n\nexport const notice: NoticeCopy = {\n title: 'Про аналітику',\n description:\n 'Ми використовуємо аналітику, що поважає приватність, щоб зрозуміти, як використовується цей сайт. Ми не збираємо персональні дані й не створюємо рекламні профілі.',\n acknowledgeLabel: 'Зрозуміло',\n optOutLabel: 'Відмовитися',\n policyLinkLabel: 'Політика конфіденційності',\n}\n","import type { BannerCopy, NoticeCopy } from '../copy.js'\nimport * as de from './de.js'\nimport * as en from './en.js'\nimport * as es from './es.js'\nimport * as fr from './fr.js'\nimport * as it from './it.js'\nimport * as nl from './nl.js'\nimport * as pl from './pl.js'\nimport * as pt from './pt.js'\nimport * as uk from './uk.js'\n\nexport type LocalePack = {\n banner: BannerCopy\n notice: NoticeCopy\n}\n\nexport const locales: Record<string, LocalePack> = {\n en: { banner: en.banner, notice: en.notice },\n de: { banner: de.banner, notice: de.notice },\n fr: { banner: fr.banner, notice: fr.notice },\n es: { banner: es.banner, notice: es.notice },\n it: { banner: it.banner, notice: it.notice },\n nl: { banner: nl.banner, notice: nl.notice },\n pt: { banner: pt.banner, notice: pt.notice },\n pl: { banner: pl.banner, notice: pl.notice },\n uk: { banner: uk.banner, notice: uk.notice },\n}\n\nexport type LocaleCode = keyof typeof locales\n\n/**\n * Resolve a BCP-47 locale tag to a built-in locale pack. Falls back from\n * the full tag (`en-GB`) to the language prefix (`en`), then to English.\n *\n * Pass `'auto'` to read `navigator.language` at call time. Anywhere that\n * `navigator` is missing (SSR, Node), `'auto'` falls back to English.\n */\nexport function resolveLocalePack(locale: string | undefined): LocalePack {\n if (!locale) return locales.en as LocalePack\n const tag = locale === 'auto' ? readNavigatorLanguage() : locale\n if (!tag) return locales.en as LocalePack\n const lower = tag.toLowerCase()\n if (locales[lower]) return locales[lower] as LocalePack\n const prefix = lower.split('-')[0]\n if (prefix && locales[prefix]) return locales[prefix] as LocalePack\n return locales.en as LocalePack\n}\n\nfunction readNavigatorLanguage(): string | undefined {\n if (typeof navigator === 'undefined') return undefined\n return navigator.language\n}\n","/**\n * Inline CSS for the default banner / notice / modal components.\n *\n * Uses CSS custom properties so users can re-theme without forking. Light\n * and dark themes are wired through `prefers-color-scheme` and the\n * `[data-tb-theme]` attribute.\n *\n * Visual style: GitHub-ish — system font, 6px corners, subtle border + soft\n * shadow, equal-prominence accept/reject buttons.\n */\nexport const TICKBOX_STYLES = `\n:where(.tb-root) {\n --tb-bg: #ffffff;\n --tb-fg: #1f2328;\n --tb-fg-muted: #59636e;\n --tb-border: #d1d9e0;\n --tb-shadow: 0 8px 24px rgba(140, 149, 159, 0.2);\n --tb-primary-bg: #1f2328;\n --tb-primary-fg: #ffffff;\n --tb-primary-bg-hover: #000000;\n --tb-secondary-bg: #ffffff;\n --tb-secondary-fg: #1f2328;\n --tb-secondary-bg-hover: #f6f8fa;\n --tb-link: #0969da;\n --tb-radius: 6px;\n --tb-z: 2147483000;\n font-family:\n -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Noto Sans\", Helvetica,\n Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\";\n color: var(--tb-fg);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n}\n@media (prefers-color-scheme: dark) {\n :where(.tb-root:not([data-tb-theme=\"light\"])) {\n --tb-bg: #0d1117;\n --tb-fg: #f0f6fc;\n --tb-fg-muted: #9198a1;\n --tb-border: #30363d;\n --tb-shadow: 0 8px 24px rgba(1, 4, 9, 0.85);\n --tb-primary-bg: #f0f6fc;\n --tb-primary-fg: #0d1117;\n --tb-primary-bg-hover: #ffffff;\n --tb-secondary-bg: #15191f;\n --tb-secondary-fg: #f0f6fc;\n --tb-secondary-bg-hover: #1f2328;\n --tb-link: #4493f8;\n }\n}\n:where(.tb-root[data-tb-theme=\"dark\"]) {\n --tb-bg: #0d1117;\n --tb-fg: #f0f6fc;\n --tb-fg-muted: #9198a1;\n --tb-border: #30363d;\n --tb-shadow: 0 8px 24px rgba(1, 4, 9, 0.85);\n --tb-primary-bg: #f0f6fc;\n --tb-primary-fg: #0d1117;\n --tb-primary-bg-hover: #ffffff;\n --tb-secondary-bg: #15191f;\n --tb-secondary-fg: #f0f6fc;\n --tb-secondary-bg-hover: #1f2328;\n --tb-link: #4493f8;\n}\n\n.tb-banner {\n position: fixed;\n left: 16px;\n right: 16px;\n bottom: 16px;\n max-width: 1100px;\n margin-inline: auto;\n z-index: var(--tb-z);\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n padding: 16px 20px;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-banner-text {\n flex: 1 1 320px;\n min-width: 0;\n}\n.tb-banner-title {\n font-weight: 600;\n margin: 0 0 2px;\n font-size: 14px;\n}\n.tb-banner-desc {\n margin: 0;\n color: var(--tb-fg-muted);\n}\n.tb-banner-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.tb-notice {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: var(--tb-z);\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n padding: 14px 16px;\n max-width: 360px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-notice-title {\n font-weight: 600;\n margin: 0 0 4px;\n font-size: 14px;\n}\n.tb-notice-desc {\n margin: 0 0 10px;\n color: var(--tb-fg-muted);\n font-size: 13px;\n}\n.tb-notice-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n justify-content: flex-end;\n flex-wrap: wrap;\n}\n\n.tb-link {\n color: var(--tb-link);\n text-decoration: none;\n font-size: 13px;\n margin-right: auto;\n}\n.tb-link:hover { text-decoration: underline; }\n\n.tb-branding {\n /* flex-basis 100% breaks .tb-branding onto its own row inside the\n wrapping flex banner so it doesn't sit next to the action buttons.\n On .tb-notice (block layout) flex-basis is inert; the explicit\n margin-top below handles the spacing there. */\n flex-basis: 100%;\n margin: 0;\n padding-top: 4px;\n font-size: 11px;\n color: var(--tb-fg-muted);\n text-align: center;\n opacity: 0.55;\n letter-spacing: 0.01em;\n}\n.tb-notice .tb-branding {\n margin-top: 10px;\n padding-top: 0;\n text-align: right;\n}\n.tb-branding a {\n color: inherit;\n text-decoration: none;\n}\n.tb-branding a:hover { text-decoration: underline; opacity: 1; }\n\n.tb-btn {\n appearance: none;\n border: 1px solid transparent;\n border-radius: var(--tb-radius);\n padding: 6px 14px;\n font-size: 13px;\n font-weight: 500;\n font-family: inherit;\n cursor: pointer;\n line-height: 1.5;\n transition: background-color 80ms ease;\n white-space: nowrap;\n}\n.tb-btn:focus-visible {\n outline: 2px solid var(--tb-link);\n outline-offset: 2px;\n}\n.tb-btn-primary {\n background: var(--tb-primary-bg);\n color: var(--tb-primary-fg);\n}\n.tb-btn-primary:hover { background: var(--tb-primary-bg-hover); }\n.tb-btn-secondary {\n background: var(--tb-secondary-bg);\n color: var(--tb-secondary-fg);\n border-color: var(--tb-border);\n}\n.tb-btn-secondary:hover { background: var(--tb-secondary-bg-hover); }\n/*\n * Style for Accept All and Reject All on the first banner layer. They MUST\n * look identical — UK ICO and EU EDPB treat unequal visual weight on those\n * buttons as a dark pattern. Customisation should not break this symmetry;\n * if you need to apply brand colours, apply them here, not to one button.\n */\n.tb-btn-equal {\n background: var(--tb-secondary-bg);\n color: var(--tb-secondary-fg);\n border-color: var(--tb-border);\n}\n.tb-btn-equal:hover { background: var(--tb-secondary-bg-hover); }\n.tb-btn-ghost {\n background: transparent;\n color: var(--tb-fg-muted);\n padding: 6px 10px;\n}\n.tb-btn-ghost:hover { color: var(--tb-fg); }\n\n.tb-modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(15, 18, 24, 0.5);\n z-index: var(--tb-z);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-modal {\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n width: 100%;\n max-width: 520px;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n}\n.tb-modal-head {\n padding: 14px 16px;\n border-bottom: 1px solid var(--tb-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n.tb-modal-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n}\n.tb-modal-body {\n padding: 12px 16px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.tb-modal-foot {\n padding: 12px 16px;\n border-top: 1px solid var(--tb-border);\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n flex-wrap: wrap;\n}\n\n.tb-cat {\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n padding: 12px;\n display: flex;\n gap: 12px;\n align-items: flex-start;\n}\n.tb-cat-text { flex: 1; min-width: 0; }\n.tb-cat-name {\n font-weight: 600;\n margin: 0 0 2px;\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.tb-cat-desc {\n margin: 0;\n color: var(--tb-fg-muted);\n font-size: 13px;\n}\n.tb-badge {\n display: inline-block;\n font-size: 11px;\n font-weight: 500;\n color: var(--tb-fg-muted);\n background: var(--tb-secondary-bg-hover);\n border: 1px solid var(--tb-border);\n border-radius: 999px;\n padding: 1px 8px;\n}\n\n.tb-switch {\n position: relative;\n display: inline-block;\n width: 32px;\n height: 18px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n.tb-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n position: absolute;\n}\n.tb-switch-track {\n position: absolute;\n inset: 0;\n background: var(--tb-border);\n border-radius: 999px;\n transition: background-color 100ms ease;\n cursor: pointer;\n}\n.tb-switch-thumb {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 14px;\n height: 14px;\n background: var(--tb-bg);\n border-radius: 50%;\n transition: transform 100ms ease;\n}\n.tb-switch input:checked + .tb-switch-track {\n background: var(--tb-primary-bg);\n}\n.tb-switch input:checked + .tb-switch-track .tb-switch-thumb {\n transform: translateX(14px);\n}\n.tb-switch input:disabled + .tb-switch-track {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.tb-switch input:focus-visible + .tb-switch-track {\n outline: 2px solid var(--tb-link);\n outline-offset: 2px;\n}\n\n@keyframes tb-fade-in {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@media (max-width: 640px) {\n .tb-banner {\n flex-direction: column;\n align-items: stretch;\n }\n .tb-banner-actions {\n flex-direction: column;\n }\n .tb-banner-actions .tb-btn { width: 100%; }\n}\n`\n\nconst STYLE_ID = 'tickbox-default-styles'\n\nlet injected = false\n\n/**\n * Insert the stylesheet into `<head>` exactly once per page. Safe to call\n * from every component mount — subsequent calls are no-ops. No-op on the\n * server (no `document`).\n */\nexport function injectStyles(): void {\n if (injected) return\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ID)) {\n injected = true\n return\n }\n const el = document.createElement('style')\n el.id = STYLE_ID\n el.textContent = TICKBOX_STYLES\n document.head.appendChild(el)\n injected = true\n}\n","/**\n * Runtime theme fetcher + cache for @tickboxhq/banner-default.\n *\n * The compliance contract (jurisdiction, categories, vendors) stays in the\n * customer's consent.config.ts under PR review. Presentation only — copy,\n * colours, locale — can be edited remotely in the dashboard and fetched\n * by the SDK at runtime so marketing teams can iterate without a deploy.\n *\n * Fetch strategy: read cached theme from localStorage and apply\n * synchronously on mount (no flicker), then refresh in the background.\n * Updated cache is written back; the *next* page load picks up the change.\n * Failed fetches are silent — the banner keeps rendering with whatever\n * cache or code defaults it had.\n */\n\nconst DEFAULT_ENDPOINT = 'https://api.tickbox.dev'\nconst STORAGE_KEY_PREFIX = '__tb_theme_'\n/** Treat cache older than 24h as suspect. We still render with it (no\n * flicker), but we always refetch — same as a CDN with stale-while-revalidate. */\nconst STALE_AFTER_MS = 24 * 60 * 60 * 1000\n\nexport type RemoteTheme = {\n copy?: {\n title?: string\n body?: string\n buttonAccept?: string\n buttonReject?: string\n buttonCustomise?: string\n privacyLink?: string\n }\n colors?: {\n accent?: string\n background?: string\n text?: string\n }\n locale?: string\n}\n\ntype CachedTheme = {\n theme: RemoteTheme\n fetchedAt: number\n}\n\n/** Synchronous read of the last cached theme. Used on mount to render\n * immediately without waiting for a network round-trip. */\nexport function readCachedTheme(siteId: string): RemoteTheme | null {\n if (typeof localStorage === 'undefined') return null\n try {\n const raw = localStorage.getItem(STORAGE_KEY_PREFIX + siteId)\n if (!raw) return null\n const parsed = JSON.parse(raw) as CachedTheme\n if (parsed && typeof parsed === 'object' && parsed.theme) {\n return parsed.theme\n }\n return null\n } catch {\n return null\n }\n}\n\n/** Fetch the latest theme from the API, write to cache. Returns the fresh\n * theme on success, `null` on any failure (caller already rendered the\n * cached one). */\nexport async function refreshTheme(\n siteId: string,\n endpoint: string = DEFAULT_ENDPOINT,\n): Promise<RemoteTheme | null> {\n if (typeof fetch === 'undefined') return null\n const url = `${endpoint.replace(/\\/+$/, '')}/v1/theme/${encodeURIComponent(siteId)}`\n try {\n const res = await fetch(url, { method: 'GET' })\n if (!res.ok) return null\n const body = (await res.json()) as { theme?: RemoteTheme }\n const theme = body?.theme ?? {}\n if (typeof localStorage !== 'undefined') {\n try {\n localStorage.setItem(\n STORAGE_KEY_PREFIX + siteId,\n JSON.stringify({ theme, fetchedAt: Date.now() } satisfies CachedTheme),\n )\n } catch {\n // Storage quota or disabled — fail silently. The banner still\n // renders with whatever's currently in memory.\n }\n }\n return theme\n } catch {\n return null\n }\n}\n\n/** Indicates whether the cached theme is older than the stale-after window.\n * Currently informational; we refetch on every mount regardless. Exposed\n * so future call sites can decide to skip the refetch (e.g. SSR). */\nexport function isStale(siteId: string): boolean {\n if (typeof localStorage === 'undefined') return true\n try {\n const raw = localStorage.getItem(STORAGE_KEY_PREFIX + siteId)\n if (!raw) return true\n const parsed = JSON.parse(raw) as CachedTheme\n return Date.now() - parsed.fetchedAt > STALE_AFTER_MS\n } catch {\n return true\n }\n}\n\n/**\n * Apply a theme's colour overrides as CSS custom properties on the banner\n * root element. The banner's own stylesheet reads these variables (or falls\n * back to defaults if unset), so theming is a matter of setting the right\n * --tb-* values.\n *\n * sanitise() drops anything outside the CSS-colour character set as defence\n * in depth against attribute injection — the values arrive via fetch from\n * api.tickbox.dev, so a compromise of the dashboard could only inject\n * benign CSS at worst.\n */\nexport function applyThemeColors(root: HTMLElement, theme: RemoteTheme | null): void {\n if (!theme?.colors) return\n if (theme.colors.accent)\n root.style.setProperty('--tb-primary-bg', sanitiseColor(theme.colors.accent))\n if (theme.colors.background)\n root.style.setProperty('--tb-bg', sanitiseColor(theme.colors.background))\n if (theme.colors.text) root.style.setProperty('--tb-fg', sanitiseColor(theme.colors.text))\n}\n\nfunction sanitiseColor(v: string): string {\n // Permit common CSS-colour characters: hex digits, color() / rgb() /\n // hsl() / oklch() function tokens. Block <, >, &, \", ' and newlines.\n return v.replace(/[<>&\"'\\n\\r\\\\]/g, '').slice(0, 64)\n}\n\n/**\n * Merge a RemoteTheme's `copy` block into a partial BannerCopy passed by\n * the caller (the customer's `copy` prop on <ConsentBannerDefault>). The\n * remote theme wins where set; code defaults fill in the rest at render\n * time via resolveLocalePack.\n */\nexport function mergeCopy<T extends Record<string, string>>(\n base: Partial<T>,\n remote: RemoteTheme['copy'] | undefined,\n): Partial<T> {\n if (!remote) return base\n const out: Record<string, string> = { ...(base as Record<string, string>) }\n if (remote.title) out.title = remote.title\n if (remote.body) out.description = remote.body\n if (remote.buttonAccept) out.acceptLabel = remote.buttonAccept\n if (remote.buttonReject) out.rejectLabel = remote.buttonReject\n if (remote.buttonCustomise) out.customiseLabel = remote.buttonCustomise\n if (remote.privacyLink) out.policyLinkLabel = remote.privacyLink\n return out as Partial<T>\n}\n"]}