@freshjuice/zest 0.1.0 → 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.
Files changed (76) hide show
  1. package/README.md +46 -0
  2. package/dist/zest.de.js +104 -1
  3. package/dist/zest.de.js.map +1 -1
  4. package/dist/zest.de.min.js +1 -1
  5. package/dist/zest.en.js +104 -1
  6. package/dist/zest.en.js.map +1 -1
  7. package/dist/zest.en.min.js +1 -1
  8. package/dist/zest.es.js +104 -1
  9. package/dist/zest.es.js.map +1 -1
  10. package/dist/zest.es.min.js +1 -1
  11. package/dist/zest.esm.js +104 -1
  12. package/dist/zest.esm.js.map +1 -1
  13. package/dist/zest.esm.min.js +1 -1
  14. package/dist/zest.fr.js +104 -1
  15. package/dist/zest.fr.js.map +1 -1
  16. package/dist/zest.fr.min.js +1 -1
  17. package/dist/zest.it.js +104 -1
  18. package/dist/zest.it.js.map +1 -1
  19. package/dist/zest.it.min.js +1 -1
  20. package/dist/zest.ja.js +104 -1
  21. package/dist/zest.ja.js.map +1 -1
  22. package/dist/zest.ja.min.js +1 -1
  23. package/dist/zest.js +104 -1
  24. package/dist/zest.js.map +1 -1
  25. package/dist/zest.min.js +1 -1
  26. package/dist/zest.nl.js +104 -1
  27. package/dist/zest.nl.js.map +1 -1
  28. package/dist/zest.nl.min.js +1 -1
  29. package/dist/zest.pl.js +104 -1
  30. package/dist/zest.pl.js.map +1 -1
  31. package/dist/zest.pl.min.js +1 -1
  32. package/dist/zest.pt.js +104 -1
  33. package/dist/zest.pt.js.map +1 -1
  34. package/dist/zest.pt.min.js +1 -1
  35. package/dist/zest.ru.js +104 -1
  36. package/dist/zest.ru.js.map +1 -1
  37. package/dist/zest.ru.min.js +1 -1
  38. package/dist/zest.uk.js +104 -1
  39. package/dist/zest.uk.js.map +1 -1
  40. package/dist/zest.uk.min.js +1 -1
  41. package/dist/zest.zh.js +104 -1
  42. package/dist/zest.zh.js.map +1 -1
  43. package/dist/zest.zh.min.js +1 -1
  44. package/package.json +5 -4
  45. package/src/api/public-api.js +97 -0
  46. package/src/config/defaults.js +150 -0
  47. package/src/config/parser.js +104 -0
  48. package/src/core/categories.js +52 -0
  49. package/src/core/cookie-interceptor.js +116 -0
  50. package/src/core/dnt.js +56 -0
  51. package/src/core/known-trackers.js +168 -0
  52. package/src/core/pattern-matcher.js +96 -0
  53. package/src/core/script-blocker.js +308 -0
  54. package/src/core/storage-interceptor.js +169 -0
  55. package/src/i18n/lang-en.js +54 -0
  56. package/src/i18n/single/lang-de.js +55 -0
  57. package/src/i18n/single/lang-en.js +55 -0
  58. package/src/i18n/single/lang-es.js +55 -0
  59. package/src/i18n/single/lang-fr.js +55 -0
  60. package/src/i18n/single/lang-it.js +55 -0
  61. package/src/i18n/single/lang-ja.js +55 -0
  62. package/src/i18n/single/lang-nl.js +55 -0
  63. package/src/i18n/single/lang-pl.js +55 -0
  64. package/src/i18n/single/lang-pt.js +55 -0
  65. package/src/i18n/single/lang-ru.js +55 -0
  66. package/src/i18n/single/lang-uk.js +55 -0
  67. package/src/i18n/single/lang-zh.js +55 -0
  68. package/src/i18n/translations.js +546 -0
  69. package/src/index.js +377 -0
  70. package/src/integrations/consent-signals.js +71 -0
  71. package/src/storage/consent-store.js +177 -0
  72. package/src/storage/events.js +84 -0
  73. package/src/ui/banner.js +130 -0
  74. package/src/ui/modal.js +211 -0
  75. package/src/ui/styles.js +498 -0
  76. package/src/ui/widget.js +103 -0
@@ -1 +1 @@
1
- var Zest=function(){"use strict";const t={essential:[/^zest_/,/^csrf/i,/^xsrf/i,/^session/i,/^__host-/i,/^__secure-/i],functional:[/^lang/i,/^locale/i,/^theme/i,/^preferences/i,/^ui_/i],analytics:[/^_ga/,/^_gid/,/^_gat/,/^_utm/,/^__utm/,/^plausible/i,/^_pk_/,/^matomo/i,/^_hj/,/^ajs_/],marketing:[/^_fbp/,/^_fbc/,/^_gcl/,/^_ttp/,/^ads/i,/^doubleclick/i,/^__gads/,/^__gpi/,/^_pin_/,/^li_/]};let e={...t};function n(t){for(const[n,o]of Object.entries(e))if(o.some(e=>e.test(t)))return n;return"marketing"}let o=null;const a=[];let s=()=>!1;function r(){return o}let i=null,c=null;const l=[],d=[];let u=()=>!1;function p(t,e,o){return new Proxy(t,{get(t,o){if("setItem"===o)return(o,a)=>{const s=n(o);u(s)?t.setItem(o,a):e.push({key:o,value:a,category:s,timestamp:Date.now()})};const a=t[o];return"function"==typeof a?a.bind(t):a}})}const m={analytics:["google-analytics.com","www.google-analytics.com","analytics.google.com","googletagmanager.com","www.googletagmanager.com","plausible.io","cloudflareinsights.com","static.cloudflareinsights.com"],marketing:["connect.facebook.net","www.facebook.com/tr","ads.google.com","www.googleadservices.com","googleads.g.doubleclick.net","pagead2.googlesyndication.com"]},f={analytics:[...m.analytics,"analytics.tiktok.com","matomo.","hotjar.com","static.hotjar.com","script.hotjar.com","clarity.ms","www.clarity.ms","heapanalytics.com","cdn.heapanalytics.com","mixpanel.com","cdn.mxpnl.com","segment.com","cdn.segment.com","api.segment.io","fullstory.com","rs.fullstory.com","amplitude.com","cdn.amplitude.com","mouseflow.com","cdn.mouseflow.com","luckyorange.com","cdn.luckyorange.net","crazyegg.com","script.crazyegg.com"],marketing:[...m.marketing,"snap.licdn.com","px.ads.linkedin.com","ads.linkedin.com","analytics.twitter.com","static.ads-twitter.com","t.co","analytics.tiktok.com","ads.tiktok.com","sc-static.net","tr.snapchat.com","ct.pinterest.com","pintrk.com","s.pinimg.com","widgets.pinterest.com","bat.bing.com","ads.yahoo.com","sp.analytics.yahoo.com","amazon-adsystem.com","z-na.amazon-adsystem.com","criteo.com","static.criteo.net","dis.criteo.com","taboola.com","cdn.taboola.com","trc.taboola.com","outbrain.com","widgets.outbrain.com","adroll.com","s.adroll.com"],functional:["cdn.onesignal.com","onesignal.com","pusher.com","js.pusher.com","intercom.io","widget.intercom.io","js.intercomcdn.com","crisp.chat","client.crisp.chat","cdn.livechatinc.com","livechatinc.com","tawk.to","embed.tawk.to","zendesk.com","static.zdassets.com"]};function g(t,e){try{const n=new URL(t).hostname.toLowerCase(),o=t.toLowerCase();for(const t of e)if(t.endsWith(".")){if(n.includes(t.slice(0,-1)))return!0}else{if(n===t||n.endsWith("."+t))return!0;if(o.includes(t))return!0}}catch(t){}return!1}function b(t,e="safe"){const n="strict"===e?f:m;for(const[e,o]of Object.entries(n))if(g(t,o))return e;return null}const z=[];let y=null,h="safe",x=[],w=()=>!1;function v(t){const e=t.getAttribute("data-consent-category");if(e)return e;if(t.hasAttribute("data-zest-allow"))return null;const n=t.src;if(!n)return null;const o=function(t){if(!t||0===x.length)return null;try{const e=new URL(t).hostname.toLowerCase();for(const t of x){const n="string"==typeof t?t:t.domain,o="string"==typeof t?"marketing":t.category||"marketing";if(e===n||e.endsWith("."+n))return o}}catch(t){}return null}(n);if(o)return o;switch(h){case"manual":default:return null;case"safe":case"strict":return b(n,h);case"doomsday":return function(t){try{const e=new URL(t).hostname,n=window.location.hostname,o=t=>t.replace(/^www\./,"");return o(e)!==o(n)}catch(t){return!1}}(n)?b(n,"strict")||"marketing":null}}function k(t){if(t.hasAttribute("data-zest-processed"))return!1;const e=v(t);if(!e)return t.setAttribute("data-zest-processed","allowed"),!1;if(w(e))return t.setAttribute("data-zest-processed","allowed"),!1;const n={category:e,src:t.src,inline:t.textContent,type:t.type,async:t.async,defer:t.defer,timestamp:Date.now()};return t.setAttribute("data-zest-processed","blocked"),t.setAttribute("data-consent-category",e),t.type="text/plain",t.src&&(t.setAttribute("data-blocked-src",t.src),t.removeAttribute("src")),z.push(n),!0}function _(t){const e=document.createElement("script");t.src?e.src=t.src:t.inline&&(e.textContent=t.inline),t.async&&(e.async=!0),t.defer&&(e.defer=!0),e.setAttribute("data-zest-processed","executed"),e.setAttribute("data-consent-executed","true"),document.head.appendChild(e)}function A(t){for(const e of t)for(const t of e.addedNodes)if("SCRIPT"!==t.nodeName||t.hasAttribute("data-zest-processed")||k(t),t.querySelectorAll){t.querySelectorAll("script:not([data-zest-processed])").forEach(k)}}function C(t="safe",e=[]){return h=t,x=e,document.querySelectorAll("script:not([data-zest-processed])").forEach(k),y=new MutationObserver(A),y.observe(document.documentElement,{childList:!0,subtree:!0}),!0}const S={essential:{id:"essential",label:"Essential",description:"Required for the website to function properly. Cannot be disabled.",required:!0,default:!0},functional:{id:"functional",label:"Functional",description:"Enable personalized features like language preferences and themes.",required:!1,default:!1},analytics:{id:"analytics",label:"Analytics",description:"Help us understand how visitors interact with our website.",required:!1,default:!1},marketing:{id:"marketing",label:"Marketing",description:"Used to deliver relevant advertisements and track campaign performance.",required:!1,default:!1}};function E(){if("undefined"==typeof navigator)return!1;const t=navigator.doNotTrack||window.doNotTrack||navigator.msDoNotTrack;return"1"===t||"yes"===t||!0===t||!0===navigator.globalPrivacyControl}const j={labels:{banner:{title:"Valoramos tu privacidad",description:'Utilizamos cookies para mejorar tu experiencia de navegación, ofrecer contenido personalizado y analizar nuestro tráfico. Al hacer clic en "Aceptar todo", consientes el uso de cookies.',acceptAll:"Aceptar todo",rejectAll:"Rechazar todo",settings:"Configuración"},modal:{title:"Configuración de privacidad",description:"Gestiona tus preferencias de cookies. Puedes activar o desactivar diferentes tipos de cookies a continuación.",save:"Guardar preferencias",acceptAll:"Aceptar todo",rejectAll:"Rechazar todo"},widget:{label:"Configuración de cookies"}},categories:{essential:{label:"Esenciales",description:"Necesarias para el funcionamiento del sitio web. No se pueden desactivar."},functional:{label:"Funcionales",description:"Permiten funciones personalizadas como preferencias de idioma y temas."},analytics:{label:"Analíticas",description:"Nos ayudan a entender cómo los visitantes interactúan con nuestro sitio web."},marketing:{label:"Marketing",description:"Se utilizan para mostrar anuncios relevantes y medir el rendimiento de las campañas."}}};const R={lang:"auto",position:"bottom",theme:"auto",accentColor:"#0071e3",categories:S,labels:{banner:{title:"We value your privacy",description:'We use cookies to enhance your browsing experience, serve personalized content, and analyze our traffic. By clicking "Accept All", you consent to our use of cookies.',acceptAll:"Accept All",rejectAll:"Reject All",settings:"Settings"},modal:{title:"Privacy Settings",description:"Manage your cookie preferences. You can enable or disable different types of cookies below.",save:"Save Preferences",acceptAll:"Accept All",rejectAll:"Reject All"},widget:{label:"Cookie Settings"}},autoInit:!0,showWidget:!0,expiration:365,respectDNT:!0,dntBehavior:"reject",customStyles:"",mode:"safe",blockedDomains:[],policyUrl:null,imprintUrl:null,callbacks:{onAccept:null,onReject:null,onChange:null,onReady:null}};function N(t){const e={...R};t||(t={});const n=["lang","position","theme","accentColor","autoInit","showWidget","expiration","policyUrl","imprintUrl","customStyles","mode","blockedDomains","respectDNT","dntBehavior"];for(const o of n)void 0!==t[o]&&(e[o]=t[o]);e.lang="es";const o=j,a=o.labels||{},s=t.labels||{};e.labels={banner:{...R.labels.banner,...a.banner,...s.banner},modal:{...R.labels.modal,...a.modal,...s.modal},widget:{...R.labels.widget,...a.widget,...s.widget}};const r=o.categories||{},i=t.categories||{};e.categories={...R.categories};for(const t of Object.keys(R.categories))e.categories[t]={...R.categories[t],...r[t],...i[t]};return t.callbacks&&(e.callbacks={...R.callbacks,...t.callbacks}),t.patterns&&(e.patterns=t.patterns),e}function $(){const t="undefined"!=typeof window&&window.ZestConfig?window.ZestConfig:{},e=function(){const t=document.currentScript||document.querySelector("script[data-zest]")||document.querySelector('script[src*="zest"]');if(!t)return{};const e={},n=t.getAttribute("data-position");n&&(e.position=n);const o=t.getAttribute("data-theme");o&&(e.theme=o);const a=t.getAttribute("data-accent")||t.getAttribute("data-accent-color");a&&(e.accentColor=a);const s=t.getAttribute("data-policy-url")||t.getAttribute("data-privacy-url");s&&(e.policyUrl=s);const r=t.getAttribute("data-imprint-url");r&&(e.imprintUrl=r);const i=t.getAttribute("data-show-widget");null!==i&&(e.showWidget="false"!==i);const c=t.getAttribute("data-auto-init");null!==c&&(e.autoInit="false"!==c);const l=t.getAttribute("data-expiration");return l&&(e.expiration=parseInt(l,10)),e}();return N({...t,...e})}let D=null;function T(){return D||(D=$()),D}const O="zest_consent";let q=null;function L(t){const e=r();e?.set?e.set.call(document,t):document.cookie=t}function M(){const t=r();return t?.get?t.get.call(document):document.cookie}function I(){try{const t=M().match(RegExp(O+"=([^;]+)"));if(t){const e=JSON.parse(decodeURIComponent(t[1]));return q=e.categories||{essential:!0,functional:!1,analytics:!1,marketing:!1},{...q}}}catch(t){}return q={essential:!0,functional:!1,analytics:!1,marketing:!1},{...q}}function U(){return q||(q=I()),{...q}}function P(t,e=365){const n=q?{...q}:{essential:!0,functional:!1,analytics:!1,marketing:!1};return q={essential:!0,functional:!!t.functional,analytics:!!t.analytics,marketing:!!t.marketing},function(t=365){q||(q={essential:!0,functional:!1,analytics:!1,marketing:!1});const e={version:"1.0",timestamp:Date.now(),categories:q},n=new Date(Date.now()+24*t*60*60*1e3).toUTCString();L(`${O}=${encodeURIComponent(JSON.stringify(e))}; expires=${n}; path=/; SameSite=Lax`)}(e),{current:{...q},previous:n}}function W(t){return q||(q=I()),!0===q[t]}function Z(t=365){return P({functional:!1,analytics:!1,marketing:!1},t)}function Y(){try{return M().includes(O)}catch(t){return!1}}const H={READY:"zest:ready",CONSENT:"zest:consent",REJECT:"zest:reject",CHANGE:"zest:change",SHOW:"zest:show",HIDE:"zest:hide"};function B(t,e={}){const n=new CustomEvent(t,{detail:e,bubbles:!0,cancelable:!0});return document.dispatchEvent(n),n}function F(t,e){return B(H.CONSENT,{consent:t,previous:e})}function J(t){return B(H.REJECT,{consent:t})}function G(t,e){return B(H.CHANGE,{consent:t,previous:e})}function X(t="banner"){return B(H.SHOW,{type:t})}function V(t="banner"){return B(H.HIDE,{type:t})}function K(t){const e=t.accentColor||"#4F46E5";return`\n:host {\n --zest-accent: ${e};\n --zest-accent-hover: ${function(t,e){const n=parseInt(t.replace("#",""),16),o=Math.round(2.55*e);return"#"+(16777216+65536*Math.min(255,Math.max(0,(n>>16)+o))+256*Math.min(255,Math.max(0,(n>>8&255)+o))+Math.min(255,Math.max(0,(255&n)+o))).toString(16).slice(1)}(e,-15)};\n --zest-bg: #ffffff;\n --zest-bg-secondary: #f3f4f6;\n --zest-text: #1f2937;\n --zest-text-secondary: #6b7280;\n --zest-border: #e5e7eb;\n --zest-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);\n --zest-radius: 12px;\n --zest-radius-sm: 8px;\n --zest-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n\n font-family: var(--zest-font);\n font-size: 14px;\n line-height: 1.5;\n color: var(--zest-text);\n box-sizing: border-box;\n}\n\n:host([data-theme="dark"]) {\n --zest-bg: #1f2937;\n --zest-bg-secondary: #374151;\n --zest-text: #f9fafb;\n --zest-text-secondary: #9ca3af;\n --zest-border: #4b5563;\n --zest-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.4), 0 8px 10px -6px rgba(0, 0, 0, 0.3);\n}\n\n@media (prefers-color-scheme: dark) {\n :host([data-theme="auto"]) {\n --zest-bg: #1f2937;\n --zest-bg-secondary: #374151;\n --zest-text: #f9fafb;\n --zest-text-secondary: #9ca3af;\n --zest-border: #4b5563;\n --zest-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.4), 0 8px 10px -6px rgba(0, 0, 0, 0.3);\n }\n}\n\n*, *::before, *::after {\n box-sizing: border-box;\n}\n\n/* Banner */\n.zest-banner {\n position: fixed;\n z-index: 999999;\n max-width: 480px;\n padding: 20px;\n background: var(--zest-bg);\n border-radius: var(--zest-radius);\n box-shadow: var(--zest-shadow);\n animation: zest-slide-in 0.3s ease-out;\n}\n\n.zest-banner--bottom {\n bottom: 20px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.zest-banner--bottom-left {\n bottom: 20px;\n left: 20px;\n}\n\n.zest-banner--bottom-right {\n bottom: 20px;\n right: 20px;\n}\n\n.zest-banner--top {\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n@keyframes zest-slide-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n\n.zest-banner--bottom-left {\n animation-name: zest-slide-in-left;\n}\n\n@keyframes zest-slide-in-left {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.zest-banner--bottom-right {\n animation-name: zest-slide-in-right;\n}\n\n@keyframes zest-slide-in-right {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .zest-banner,\n .zest-modal {\n animation: none;\n }\n}\n\n.zest-banner__title {\n margin: 0 0 8px 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--zest-text);\n}\n\n.zest-banner__description {\n margin: 0 0 16px 0;\n font-size: 14px;\n color: var(--zest-text-secondary);\n}\n\n.zest-banner__buttons {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n/* Buttons */\n.zest-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 10px 16px;\n font-size: 14px;\n font-weight: 500;\n font-family: inherit;\n border: none;\n border-radius: var(--zest-radius-sm);\n cursor: pointer;\n transition: background-color 0.15s ease, transform 0.1s ease;\n}\n\n.zest-btn:hover {\n transform: translateY(-1px);\n}\n\n.zest-btn:active {\n transform: translateY(0);\n}\n\n.zest-btn:focus-visible {\n outline: 2px solid var(--zest-accent);\n outline-offset: 2px;\n}\n\n.zest-btn--primary {\n background: var(--zest-accent);\n color: #ffffff;\n}\n\n.zest-btn--primary:hover {\n background: var(--zest-accent-hover);\n}\n\n.zest-btn--secondary {\n background: var(--zest-bg-secondary);\n color: var(--zest-text);\n}\n\n.zest-btn--secondary:hover {\n background: var(--zest-border);\n}\n\n.zest-btn--ghost {\n background: transparent;\n color: var(--zest-text-secondary);\n}\n\n.zest-btn--ghost:hover {\n background: var(--zest-bg-secondary);\n color: var(--zest-text);\n}\n\n/* Modal */\n.zest-modal-overlay {\n position: fixed;\n inset: 0;\n z-index: 999998;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n background: rgba(0, 0, 0, 0.5);\n animation: zest-fade-in 0.2s ease-out;\n}\n\n@keyframes zest-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.zest-modal {\n width: 100%;\n max-width: 500px;\n max-height: 90vh;\n overflow-y: auto;\n background: var(--zest-bg);\n border-radius: var(--zest-radius);\n box-shadow: var(--zest-shadow);\n animation: zest-modal-in 0.3s ease-out;\n}\n\n@keyframes zest-modal-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n.zest-modal__header {\n padding: 20px 20px 0;\n}\n\n.zest-modal__title {\n margin: 0 0 8px 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--zest-text);\n}\n\n.zest-modal__description {\n margin: 0;\n font-size: 14px;\n color: var(--zest-text-secondary);\n}\n\n.zest-modal__body {\n padding: 20px;\n}\n\n.zest-modal__footer {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n padding: 0 20px 20px;\n}\n\n/* Categories */\n.zest-category {\n padding: 16px;\n margin-bottom: 12px;\n background: var(--zest-bg-secondary);\n border-radius: var(--zest-radius-sm);\n}\n\n.zest-category:last-child {\n margin-bottom: 0;\n}\n\n.zest-category__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.zest-category__info {\n flex: 1;\n}\n\n.zest-category__label {\n display: block;\n font-size: 14px;\n font-weight: 600;\n color: var(--zest-text);\n}\n\n.zest-category__description {\n margin: 4px 0 0;\n font-size: 13px;\n color: var(--zest-text-secondary);\n}\n\n/* Toggle Switch */\n.zest-toggle {\n position: relative;\n width: 44px;\n height: 24px;\n flex-shrink: 0;\n}\n\n.zest-toggle__input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n cursor: pointer;\n margin: 0;\n}\n\n.zest-toggle__input:disabled {\n cursor: not-allowed;\n}\n\n.zest-toggle__slider {\n position: absolute;\n inset: 0;\n background: var(--zest-border);\n border-radius: 12px;\n transition: background-color 0.2s ease;\n pointer-events: none;\n}\n\n.zest-toggle__slider::before {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: 20px;\n height: 20px;\n background: #ffffff;\n border-radius: 50%;\n transition: transform 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n}\n\n.zest-toggle__input:checked + .zest-toggle__slider {\n background: var(--zest-accent);\n}\n\n.zest-toggle__input:checked + .zest-toggle__slider::before {\n transform: translateX(20px);\n}\n\n.zest-toggle__input:focus-visible + .zest-toggle__slider {\n outline: 2px solid var(--zest-accent);\n outline-offset: 2px;\n}\n\n.zest-toggle__input:disabled + .zest-toggle__slider {\n opacity: 0.6;\n}\n\n/* Widget */\n.zest-widget {\n position: fixed;\n z-index: 999997;\n bottom: 20px;\n left: 20px;\n}\n\n.zest-widget__btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n padding: 0;\n background: var(--zest-bg);\n border: 1px solid var(--zest-border);\n border-radius: 50%;\n box-shadow: var(--zest-shadow);\n cursor: pointer;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n}\n\n.zest-widget__btn:hover {\n transform: scale(1.05);\n box-shadow: 0 12px 28px -5px rgba(0, 0, 0, 0.15);\n}\n\n.zest-widget__btn:focus-visible {\n outline: 2px solid var(--zest-accent);\n outline-offset: 2px;\n}\n\n.zest-widget__icon {\n width: 24px;\n height: 24px;\n fill: var(--zest-text);\n}\n\n/* Link */\n.zest-link {\n color: var(--zest-accent);\n text-decoration: none;\n}\n\n.zest-link:hover {\n text-decoration: underline;\n}\n\n/* Mobile */\n@media (max-width: 480px) {\n .zest-banner {\n left: 10px;\n right: 10px;\n max-width: none;\n transform: none;\n }\n\n .zest-banner--bottom,\n .zest-banner--bottom-left,\n .zest-banner--bottom-right {\n bottom: 10px;\n }\n\n .zest-banner--top {\n top: 10px;\n transform: none;\n }\n\n @keyframes zest-slide-in {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n .zest-banner__buttons {\n flex-direction: column;\n }\n\n .zest-btn {\n width: 100%;\n }\n\n .zest-modal-overlay {\n padding: 10px;\n }\n\n .zest-widget {\n bottom: 10px;\n left: 10px;\n }\n}\n\n/* Hidden utility */\n.zest-hidden {\n display: none !important;\n}\n${t.customStyles||""}\n`}let Q=null,tt=null;function et(t={}){Q?Q.classList.remove("zest-hidden"):function(t={}){if(Q)return Q;const e=T();Q=document.createElement("zest-banner"),Q.setAttribute("data-theme",e.theme||"light"),tt=Q.attachShadow({mode:"open"});const n=document.createElement("style");n.textContent=K(e),tt.appendChild(n);const o=document.createElement("div");o.innerHTML=function(t){const e=t.labels.banner;return`\n <div class="zest-banner zest-banner--${t.position||"bottom"}" role="dialog" aria-modal="false" aria-label="${e.title}">\n <h2 class="zest-banner__title">${e.title}</h2>\n <p class="zest-banner__description">${e.description}</p>\n <div class="zest-banner__buttons">\n <button type="button" class="zest-btn zest-btn--primary" data-action="accept-all">\n ${e.acceptAll}\n </button>\n <button type="button" class="zest-btn zest-btn--secondary" data-action="reject-all">\n ${e.rejectAll}\n </button>\n <button type="button" class="zest-btn zest-btn--ghost" data-action="settings">\n ${e.settings}\n </button>\n </div>\n </div>\n `}(e),tt.appendChild(o.firstElementChild);const a=tt.querySelector(".zest-banner");a.addEventListener("click",e=>{const n=e.target.dataset.action;if(n)switch(n){case"accept-all":t.onAcceptAll?.();break;case"reject-all":t.onRejectAll?.();break;case"settings":t.onSettings?.()}}),a.addEventListener("keydown",e=>{"Escape"===e.key&&t.onSettings?.()}),document.body.appendChild(Q),requestAnimationFrame(()=>{const t=tt.querySelector("button");t?.focus()})}(t)}function nt(){Q&&(Q.remove(),Q=null,tt=null)}let ot=null,at=null,st={};function rt(){if(!at)return st;const t=at.querySelectorAll(".zest-toggle__input"),e={essential:!0};return t.forEach(t=>{const n=t.dataset.category;n&&"essential"!==n&&(e[n]=t.checked)}),e}function it(t={},e={}){if(ot)return ot;const n=T();st={...t},ot=document.createElement("zest-modal"),ot.setAttribute("data-theme",n.theme||"light"),at=ot.attachShadow({mode:"open"});const o=document.createElement("style");o.textContent=K(n),at.appendChild(o);const a=document.createElement("div");a.innerHTML=function(t,e){const n=t.labels.modal,o=Object.values(t.categories||S).map(t=>{return`\n <div class="zest-category">\n <div class="zest-category__header">\n <div class="zest-category__info">\n <span class="zest-category__label">${(n=t).label}</span>\n <p class="zest-category__description">${n.description}</p>\n </div>\n <label class="zest-toggle">\n <input\n type="checkbox"\n class="zest-toggle__input"\n data-category="${n.id}"\n ${e[t.id]??t.default?"checked":""}\n ${t.required?"disabled":""}\n aria-label="${n.label}"\n >\n <span class="zest-toggle__slider"></span>\n </label>\n </div>\n </div>\n `;var n}).join("");return`\n <div class="zest-modal-overlay" role="dialog" aria-modal="true" aria-label="${n.title}">\n <div class="zest-modal">\n <div class="zest-modal__header">\n <h2 class="zest-modal__title">${n.title}</h2>\n <p class="zest-modal__description">${n.description} ${t.policyUrl?`<a href="${t.policyUrl}" class="zest-link" target="_blank" rel="noopener">Privacy Policy</a>`:""}</p>\n </div>\n <div class="zest-modal__body">\n ${o}\n </div>\n <div class="zest-modal__footer">\n <button type="button" class="zest-btn zest-btn--primary" data-action="save">\n ${n.save}\n </button>\n <button type="button" class="zest-btn zest-btn--secondary" data-action="accept-all">\n ${n.acceptAll}\n </button>\n <button type="button" class="zest-btn zest-btn--ghost" data-action="reject-all">\n ${n.rejectAll}\n </button>\n </div>\n </div>\n </div>\n `}(n,t),at.appendChild(a.firstElementChild);const s=at.querySelector(".zest-modal-overlay");return s.addEventListener("click",t=>{const n=t.target.dataset.action;if(n)switch(n){case"save":e.onSave?.(rt());break;case"accept-all":e.onAcceptAll?.();break;case"reject-all":e.onRejectAll?.()}else t.target===s&&e.onClose?.()}),s.addEventListener("keydown",t=>{"Escape"===t.key&&e.onClose?.()}),at.querySelectorAll(".zest-toggle__input").forEach(t=>{t.addEventListener("change",()=>{st=rt()})}),document.body.appendChild(ot),requestAnimationFrame(()=>{const t=at.querySelector("button");t?.focus()}),ot}function ct(){ot&&(ot.remove(),ot=null,at=null)}let lt=null,dt=null;function ut(t={}){lt?lt.style.display="":function(t={}){if(lt)return lt;const e=T();lt=document.createElement("zest-widget"),lt.setAttribute("data-theme",e.theme||"light"),dt=lt.attachShadow({mode:"open"});const n=document.createElement("style");n.textContent=K(e),dt.appendChild(n);const o=document.createElement("div");o.innerHTML=function(t){const e=t.labels.widget;return`\n <div class="zest-widget">\n <button type="button" class="zest-widget__btn" aria-label="${e.label}" title="${e.label}">\n <span class="zest-widget__icon"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10c0-.728-.078-1.437-.225-2.12a1 1 0 0 0-1.482-.63 3 3 0 0 1-4.086-3.72 1 1 0 0 0-.793-1.263A10.05 10.05 0 0 0 12 2zm0 2c.178 0 .354.006.528.017a5 5 0 0 0 5.955 5.955c.011.174.017.35.017.528 0 4.418-3.582 8-8 8s-8-3.582-8-8 3.582-8 8-8zm-4 6a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zm5 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm-2 4a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z"/></svg></span>\n </button>\n </div>\n `}(e),dt.appendChild(o.firstElementChild),dt.querySelector(".zest-widget__btn").addEventListener("click",()=>{t.onClick?.()}),document.body.appendChild(lt)}(t)}function pt(){lt&&(lt.style.display="none")}let mt=!1,ft=null;function gt(t){return W(t)}function bt(t){!function(t){const e=[];for(const n of a)t.includes(n.category)?o?.set&&o.set.call(document,n.value):e.push(n);a.length=0,a.push(...e)}(t),function(t){const e=[];for(const n of l)t.includes(n.category)?i?.setItem(n.key,n.value):e.push(n);l.length=0,l.push(...e);const n=[];for(const e of d)t.includes(e.category)?c?.setItem(e.key,e.value):n.push(e);d.length=0,d.push(...n)}(t),function(t){const e=[];for(const n of z)t.includes(n.category)?_(n):e.push(n);z.length=0,z.push(...e),document.querySelectorAll('script[data-zest-processed="blocked"]').forEach(e=>{const n=e.getAttribute("data-consent-category");if(t.includes(n)){const t=document.createElement("script"),n=e.getAttribute("data-blocked-src");n?t.src=n:t.textContent=e.textContent,e.async&&(t.async=!0),e.defer&&(t.defer=!0),t.setAttribute("data-zest-processed","executed"),t.setAttribute("data-consent-executed","true"),e.parentNode?.replaceChild(t,e)}})}(t)}function zt(){const t=function(t=365){return P({functional:!0,analytics:!0,marketing:!0},t)}(ft.expiration),e=Object.keys(S);nt(),ct(),bt(e),ft.showWidget&&ut({onClick:xt}),F(t.current,t.previous),G(t.current,t.previous),ft.callbacks?.onAccept?.(t.current),ft.callbacks?.onChange?.(t.current)}function yt(){const t=Z(ft.expiration);nt(),ct(),ft.showWidget&&ut({onClick:xt}),J(t.current),G(t.current,t.previous),ft.callbacks?.onReject?.(),ft.callbacks?.onChange?.(t.current)}function ht(t){const e=P(t,ft.expiration),n=Object.keys(e.current).filter(t=>e.current[t]&&!e.previous[t]);n.length>0&&bt(n),ct(),ft.showWidget&&ut({onClick:xt});Object.entries(t).some(([t,e])=>"essential"!==t&&e)?F(e.current,e.previous):J(e.current),G(e.current,e.previous),ft.callbacks?.onChange?.(e.current)}function xt(){nt(),pt(),it(U(),{onSave:ht,onAcceptAll:zt,onRejectAll:yt,onClose:wt}),X("modal")}function wt(){ct(),V("modal"),Y()&&ft.showWidget?ut({onClick:xt}):et({onAcceptAll:zt,onRejectAll:yt,onSettings:xt})}function vt(r={}){if(mt)return console.warn("[Zest] Already initialized"),kt;ft=function(t){return D=N(t),D}(r),ft.patterns&&function(n){e={...t};for(const[t,o]of Object.entries(n))Array.isArray(o)&&(e[t]=o.map(t=>t instanceof RegExp?t:RegExp(t)))}(ft.patterns),s=gt,function(t){u=t}(gt),function(t){w=t}(gt),o=Object.getOwnPropertyDescriptor(Document.prototype,"cookie"),o?Object.defineProperty(document,"cookie",{get:()=>o.get.call(document),set(t){const e=function(t){const e=t.match(/^([^=]+)/);return e?e[1].trim():null}(t);if(!e)return;const r=n(e);s(r)?o.set.call(document,t):a.push({value:t,name:e,category:r,timestamp:Date.now()})},configurable:!0}):console.warn("[Zest] Could not get cookie descriptor"),function(){try{return i=window.localStorage,c=window.sessionStorage,Object.defineProperty(window,"localStorage",{value:p(i,l),configurable:!0,writable:!1}),Object.defineProperty(window,"sessionStorage",{value:p(c,d),configurable:!0,writable:!1}),!0}catch(t){return console.warn("[Zest] Could not intercept storage APIs:",t),!1}}(),C(ft.mode,ft.blockedDomains);const m=I();mt=!0;let f=!1;if(E()&&ft.respectDNT&&"ignore"!==ft.dntBehavior&&"reject"===ft.dntBehavior&&!Y()){const t=Z(ft.expiration);f=!0,J(t.current),G(t.current,t.previous),ft.callbacks?.onReject?.(),ft.callbacks?.onChange?.(t.current)}return function(t){B(H.READY,{consent:t})}(m),ft.callbacks?.onReady?.(m),Y()||f?ft.showWidget&&ut({onClick:xt}):(et({onAcceptAll:zt,onRejectAll:yt,onSettings:xt}),X("banner")),kt}const kt={init:vt,show(){mt?(ct(),pt(),et({onAcceptAll:zt,onRejectAll:yt,onSettings:xt}),X("banner")):console.warn("[Zest] Not initialized. Call Zest.init() first.")},hide(){nt(),V("banner")},showSettings(){mt?xt():console.warn("[Zest] Not initialized. Call Zest.init() first.")},hideSettings(){ct(),V("modal")},getConsent:U,hasConsent:W,hasConsentDecision:Y,getConsentProof:function(){try{const t=M().match(RegExp(O+"=([^;]+)"));if(t)return JSON.parse(decodeURIComponent(t[1]))}catch(t){}return null},isDoNotTrackEnabled:E,getDNTDetails:function(){if("undefined"==typeof navigator)return{enabled:!1,source:null};const t=navigator.doNotTrack||window.doNotTrack||navigator.msDoNotTrack;return"1"===t||"yes"===t||!0===t?{enabled:!0,source:"dnt"}:!0===navigator.globalPrivacyControl?{enabled:!0,source:"gpc"}:{enabled:!1,source:null}},acceptAll(){mt?zt():console.warn("[Zest] Not initialized. Call Zest.init() first.")},rejectAll(){mt?yt():console.warn("[Zest] Not initialized. Call Zest.init() first.")},reset(){L(O+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/"),q=null,ct(),lt&&(lt.remove(),lt=null,dt=null),mt&&(et({onAcceptAll:zt,onRejectAll:yt,onSettings:xt}),X("banner"))},getConfig:T,EVENTS:H};if("undefined"!=typeof window){window.Zest=kt;const t=()=>{!1!==$().autoInit&&vt(window.ZestConfig)};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",t):t()}return kt}();
1
+ var Zest=function(){"use strict";const t={essential:[/^zest_/,/^csrf/i,/^xsrf/i,/^session/i,/^__host-/i,/^__secure-/i],functional:[/^lang/i,/^locale/i,/^theme/i,/^preferences/i,/^ui_/i],analytics:[/^_ga/,/^_gid/,/^_gat/,/^_utm/,/^__utm/,/^plausible/i,/^_pk_/,/^matomo/i,/^_hj/,/^ajs_/],marketing:[/^_fbp/,/^_fbc/,/^_gcl/,/^_ttp/,/^ads/i,/^doubleclick/i,/^__gads/,/^__gpi/,/^_pin_/,/^li_/]};let n={...t};function e(t){for(const[e,o]of Object.entries(n))if(o.some(n=>n.test(t)))return e;return"marketing"}let o=null;const a=[];let s=()=>!1;function r(){return o}let i=null,c=null;const l=[],d=[];let u=()=>!1;function p(t,n,o){return new Proxy(t,{get(t,o){if("setItem"===o)return(o,a)=>{const s=e(o);u(s)?t.setItem(o,a):n.push({key:o,value:a,category:s,timestamp:Date.now()})};const a=t[o];return"function"==typeof a?a.bind(t):a}})}const g={analytics:["google-analytics.com","www.google-analytics.com","analytics.google.com","googletagmanager.com","www.googletagmanager.com","plausible.io","cloudflareinsights.com","static.cloudflareinsights.com"],marketing:["connect.facebook.net","www.facebook.com/tr","ads.google.com","www.googleadservices.com","googleads.g.doubleclick.net","pagead2.googlesyndication.com"]},m={analytics:[...g.analytics,"analytics.tiktok.com","matomo.","hotjar.com","static.hotjar.com","script.hotjar.com","clarity.ms","www.clarity.ms","heapanalytics.com","cdn.heapanalytics.com","mixpanel.com","cdn.mxpnl.com","segment.com","cdn.segment.com","api.segment.io","fullstory.com","rs.fullstory.com","amplitude.com","cdn.amplitude.com","mouseflow.com","cdn.mouseflow.com","luckyorange.com","cdn.luckyorange.net","crazyegg.com","script.crazyegg.com"],marketing:[...g.marketing,"snap.licdn.com","px.ads.linkedin.com","ads.linkedin.com","analytics.twitter.com","static.ads-twitter.com","t.co","analytics.tiktok.com","ads.tiktok.com","sc-static.net","tr.snapchat.com","ct.pinterest.com","pintrk.com","s.pinimg.com","widgets.pinterest.com","bat.bing.com","ads.yahoo.com","sp.analytics.yahoo.com","amazon-adsystem.com","z-na.amazon-adsystem.com","criteo.com","static.criteo.net","dis.criteo.com","taboola.com","cdn.taboola.com","trc.taboola.com","outbrain.com","widgets.outbrain.com","adroll.com","s.adroll.com"],functional:["cdn.onesignal.com","onesignal.com","pusher.com","js.pusher.com","intercom.io","widget.intercom.io","js.intercomcdn.com","crisp.chat","client.crisp.chat","cdn.livechatinc.com","livechatinc.com","tawk.to","embed.tawk.to","zendesk.com","static.zdassets.com"]};function f(t,n){try{const e=new URL(t).hostname.toLowerCase(),o=t.toLowerCase();for(const t of n)if(t.endsWith(".")){if(e.includes(t.slice(0,-1)))return!0}else{if(e===t||e.endsWith("."+t))return!0;if(o.includes(t))return!0}}catch(t){}return!1}function b(t,n="safe"){const e="strict"===n?m:g;for(const[n,o]of Object.entries(e))if(f(t,o))return n;return null}const y=[];let z=null,h="safe",w=[],x=()=>!1;function v(t){const n=t.getAttribute("data-consent-category");if(n)return n;if(t.hasAttribute("data-zest-allow"))return null;const e=t.src;if(!e)return null;const o=function(t){if(!t||0===w.length)return null;try{const n=new URL(t).hostname.toLowerCase();for(const t of w){const e="string"==typeof t?t:t.domain,o="string"==typeof t?"marketing":t.category||"marketing";if(n===e||n.endsWith("."+e))return o}}catch(t){}return null}(e);if(o)return o;switch(h){case"manual":default:return null;case"safe":case"strict":return b(e,h);case"doomsday":return function(t){try{const n=new URL(t).hostname,e=window.location.hostname,o=t=>t.replace(/^www\./,"");return o(n)!==o(e)}catch(t){return!1}}(e)?b(e,"strict")||"marketing":null}}function k(t){if(t.hasAttribute("data-zest-processed"))return!1;const n=v(t);if(!n)return t.setAttribute("data-zest-processed","allowed"),!1;if(x(n))return t.setAttribute("data-zest-processed","allowed"),!1;const e={category:n,src:t.src,inline:t.textContent,type:t.type,async:t.async,defer:t.defer,timestamp:Date.now()};return t.setAttribute("data-zest-processed","blocked"),t.setAttribute("data-consent-category",n),t.type="text/plain",t.src&&(t.setAttribute("data-blocked-src",t.src),t.removeAttribute("src")),y.push(e),!0}function _(t){const n=document.createElement("script");t.src?n.src=t.src:t.inline&&(n.textContent=t.inline),t.async&&(n.async=!0),t.defer&&(n.defer=!0),n.setAttribute("data-zest-processed","executed"),n.setAttribute("data-consent-executed","true"),document.head.appendChild(n)}function A(t){for(const n of t)for(const t of n.addedNodes)if("SCRIPT"!==t.nodeName||t.hasAttribute("data-zest-processed")||k(t),t.querySelectorAll){t.querySelectorAll("script:not([data-zest-processed])").forEach(k)}}function C(t="safe",n=[]){return h=t,w=n,document.querySelectorAll("script:not([data-zest-processed])").forEach(k),z=new MutationObserver(A),z.observe(document.documentElement,{childList:!0,subtree:!0}),!0}const S={essential:{id:"essential",label:"Essential",description:"Required for the website to function properly. Cannot be disabled.",required:!0,default:!0},functional:{id:"functional",label:"Functional",description:"Enable personalized features like language preferences and themes.",required:!1,default:!1},analytics:{id:"analytics",label:"Analytics",description:"Help us understand how visitors interact with our website.",required:!1,default:!1},marketing:{id:"marketing",label:"Marketing",description:"Used to deliver relevant advertisements and track campaign performance.",required:!1,default:!1}};function E(){if("undefined"==typeof navigator)return!1;const t=navigator.doNotTrack||window.doNotTrack||navigator.msDoNotTrack;return"1"===t||"yes"===t||!0===t||!0===navigator.globalPrivacyControl}function j(t,n,e){const o=e?"default":"update";n.consentModeGoogle&&function(t,n){if(window.dataLayer=window.dataLayer||[],"function"==typeof window.gtag)window.gtag("consent",t,n);else{function e(){window.dataLayer.push(arguments)}e("consent",t,n)}}(o,function(t){const n=t=>t?"granted":"denied";return{ad_storage:n(t.marketing),ad_user_data:n(t.marketing),ad_personalization:n(t.marketing),analytics_storage:n(t.analytics),functionality_storage:"granted",personalization_storage:n(t.functional)}}(t)),n.consentModeMicrosoft&&function(t,n){window.uetq=window.uetq||[],window.uetq.push("consent",t,n)}(o,function(t){return{ad_storage:t.marketing?"granted":"denied"}}(t))}const R={labels:{banner:{title:"Valoramos tu privacidad",description:'Utilizamos cookies para mejorar tu experiencia de navegación, ofrecer contenido personalizado y analizar nuestro tráfico. Al hacer clic en "Aceptar todo", consientes el uso de cookies.',acceptAll:"Aceptar todo",rejectAll:"Rechazar todo",settings:"Configuración"},modal:{title:"Configuración de privacidad",description:"Gestiona tus preferencias de cookies. Puedes activar o desactivar diferentes tipos de cookies a continuación.",save:"Guardar preferencias",acceptAll:"Aceptar todo",rejectAll:"Rechazar todo"},widget:{label:"Configuración de cookies"}},categories:{essential:{label:"Esenciales",description:"Necesarias para el funcionamiento del sitio web. No se pueden desactivar."},functional:{label:"Funcionales",description:"Permiten funciones personalizadas como preferencias de idioma y temas."},analytics:{label:"Analíticas",description:"Nos ayudan a entender cómo los visitantes interactúan con nuestro sitio web."},marketing:{label:"Marketing",description:"Se utilizan para mostrar anuncios relevantes y medir el rendimiento de las campañas."}}};const M={lang:"auto",position:"bottom",theme:"auto",accentColor:"#0071e3",categories:S,labels:{banner:{title:"We value your privacy",description:'We use cookies to enhance your browsing experience, serve personalized content, and analyze our traffic. By clicking "Accept All", you consent to our use of cookies.',acceptAll:"Accept All",rejectAll:"Reject All",settings:"Settings"},modal:{title:"Privacy Settings",description:"Manage your cookie preferences. You can enable or disable different types of cookies below.",save:"Save Preferences",acceptAll:"Accept All",rejectAll:"Reject All"},widget:{label:"Cookie Settings"}},autoInit:!0,showWidget:!0,expiration:365,respectDNT:!0,dntBehavior:"reject",customStyles:"",consentModeGoogle:!1,consentModeMicrosoft:!1,mode:"safe",blockedDomains:[],policyUrl:null,imprintUrl:null,callbacks:{onAccept:null,onReject:null,onChange:null,onReady:null}};function N(t){const n={...M};t||(t={});const e=["lang","position","theme","accentColor","autoInit","showWidget","expiration","policyUrl","imprintUrl","customStyles","mode","blockedDomains","respectDNT","dntBehavior","consentModeGoogle","consentModeMicrosoft"];for(const o of e)void 0!==t[o]&&(n[o]=t[o]);n.lang="es";const o=R,a=o.labels||{},s=t.labels||{};n.labels={banner:{...M.labels.banner,...a.banner,...s.banner},modal:{...M.labels.modal,...a.modal,...s.modal},widget:{...M.labels.widget,...a.widget,...s.widget}};const r=o.categories||{},i=t.categories||{};n.categories={...M.categories};for(const t of Object.keys(M.categories))n.categories[t]={...M.categories[t],...r[t],...i[t]};return t.callbacks&&(n.callbacks={...M.callbacks,...t.callbacks}),t.patterns&&(n.patterns=t.patterns),n}function $(){const t="undefined"!=typeof window&&window.ZestConfig?window.ZestConfig:{},n=function(){const t=document.currentScript||document.querySelector("script[data-zest]")||document.querySelector('script[src*="zest"]');if(!t)return{};const n={},e=t.getAttribute("data-position");e&&(n.position=e);const o=t.getAttribute("data-theme");o&&(n.theme=o);const a=t.getAttribute("data-accent")||t.getAttribute("data-accent-color");a&&(n.accentColor=a);const s=t.getAttribute("data-policy-url")||t.getAttribute("data-privacy-url");s&&(n.policyUrl=s);const r=t.getAttribute("data-imprint-url");r&&(n.imprintUrl=r);const i=t.getAttribute("data-show-widget");null!==i&&(n.showWidget="false"!==i);const c=t.getAttribute("data-auto-init");null!==c&&(n.autoInit="false"!==c);const l=t.getAttribute("data-expiration");l&&(n.expiration=parseInt(l,10));const d=t.getAttribute("data-consent-mode-google");null!==d&&(n.consentModeGoogle="false"!==d);const u=t.getAttribute("data-consent-mode-microsoft");return null!==u&&(n.consentModeMicrosoft="false"!==u),n}();return N({...t,...n})}let D=null;function q(){return D||(D=$()),D}const L="zest_consent";let T=null;function O(t){const n=r();n?.set?n.set.call(document,t):document.cookie=t}function I(){const t=r();return t?.get?t.get.call(document):document.cookie}function U(){try{const t=I().match(RegExp(L+"=([^;]+)"));if(t){const n=JSON.parse(decodeURIComponent(t[1]));return T=n.categories||{essential:!0,functional:!1,analytics:!1,marketing:!1},{...T}}}catch(t){}return T={essential:!0,functional:!1,analytics:!1,marketing:!1},{...T}}function P(){return T||(T=U()),{...T}}function W(t,n=365){const e=T?{...T}:{essential:!0,functional:!1,analytics:!1,marketing:!1};return T={essential:!0,functional:!!t.functional,analytics:!!t.analytics,marketing:!!t.marketing},function(t=365){T||(T={essential:!0,functional:!1,analytics:!1,marketing:!1});const n={version:"1.0",timestamp:Date.now(),categories:T},e=new Date(Date.now()+24*t*60*60*1e3).toUTCString();O(`${L}=${encodeURIComponent(JSON.stringify(n))}; expires=${e}; path=/; SameSite=Lax`)}(n),{current:{...T},previous:e}}function Z(t){return T||(T=U()),!0===T[t]}function Y(t=365){return W({functional:!1,analytics:!1,marketing:!1},t)}function H(){try{return I().includes(L)}catch(t){return!1}}const B={READY:"zest:ready",CONSENT:"zest:consent",REJECT:"zest:reject",CHANGE:"zest:change",SHOW:"zest:show",HIDE:"zest:hide"};function G(t,n={}){const e=new CustomEvent(t,{detail:n,bubbles:!0,cancelable:!0});return document.dispatchEvent(e),e}function F(t,n){return G(B.CONSENT,{consent:t,previous:n})}function J(t){return G(B.REJECT,{consent:t})}function X(t,n){return G(B.CHANGE,{consent:t,previous:n})}function V(t="banner"){return G(B.SHOW,{type:t})}function K(t="banner"){return G(B.HIDE,{type:t})}function Q(t){const n=t.accentColor||"#4F46E5";return`\n:host {\n --zest-accent: ${n};\n --zest-accent-hover: ${function(t,n){const e=parseInt(t.replace("#",""),16),o=Math.round(2.55*n);return"#"+(16777216+65536*Math.min(255,Math.max(0,(e>>16)+o))+256*Math.min(255,Math.max(0,(e>>8&255)+o))+Math.min(255,Math.max(0,(255&e)+o))).toString(16).slice(1)}(n,-15)};\n --zest-bg: #ffffff;\n --zest-bg-secondary: #f3f4f6;\n --zest-text: #1f2937;\n --zest-text-secondary: #6b7280;\n --zest-border: #e5e7eb;\n --zest-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);\n --zest-radius: 12px;\n --zest-radius-sm: 8px;\n --zest-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n\n font-family: var(--zest-font);\n font-size: 14px;\n line-height: 1.5;\n color: var(--zest-text);\n box-sizing: border-box;\n}\n\n:host([data-theme="dark"]) {\n --zest-bg: #1f2937;\n --zest-bg-secondary: #374151;\n --zest-text: #f9fafb;\n --zest-text-secondary: #9ca3af;\n --zest-border: #4b5563;\n --zest-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.4), 0 8px 10px -6px rgba(0, 0, 0, 0.3);\n}\n\n@media (prefers-color-scheme: dark) {\n :host([data-theme="auto"]) {\n --zest-bg: #1f2937;\n --zest-bg-secondary: #374151;\n --zest-text: #f9fafb;\n --zest-text-secondary: #9ca3af;\n --zest-border: #4b5563;\n --zest-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.4), 0 8px 10px -6px rgba(0, 0, 0, 0.3);\n }\n}\n\n*, *::before, *::after {\n box-sizing: border-box;\n}\n\n/* Banner */\n.zest-banner {\n position: fixed;\n z-index: 999999;\n max-width: 480px;\n padding: 20px;\n background: var(--zest-bg);\n border-radius: var(--zest-radius);\n box-shadow: var(--zest-shadow);\n animation: zest-slide-in 0.3s ease-out;\n}\n\n.zest-banner--bottom {\n bottom: 20px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n.zest-banner--bottom-left {\n bottom: 20px;\n left: 20px;\n}\n\n.zest-banner--bottom-right {\n bottom: 20px;\n right: 20px;\n}\n\n.zest-banner--top {\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n}\n\n@keyframes zest-slide-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n\n.zest-banner--bottom-left {\n animation-name: zest-slide-in-left;\n}\n\n@keyframes zest-slide-in-left {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.zest-banner--bottom-right {\n animation-name: zest-slide-in-right;\n}\n\n@keyframes zest-slide-in-right {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .zest-banner,\n .zest-modal {\n animation: none;\n }\n}\n\n.zest-banner__title {\n margin: 0 0 8px 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--zest-text);\n}\n\n.zest-banner__description {\n margin: 0 0 16px 0;\n font-size: 14px;\n color: var(--zest-text-secondary);\n}\n\n.zest-banner__buttons {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n/* Buttons */\n.zest-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 10px 16px;\n font-size: 14px;\n font-weight: 500;\n font-family: inherit;\n border: none;\n border-radius: var(--zest-radius-sm);\n cursor: pointer;\n transition: background-color 0.15s ease, transform 0.1s ease;\n}\n\n.zest-btn:hover {\n transform: translateY(-1px);\n}\n\n.zest-btn:active {\n transform: translateY(0);\n}\n\n.zest-btn:focus-visible {\n outline: 2px solid var(--zest-accent);\n outline-offset: 2px;\n}\n\n.zest-btn--primary {\n background: var(--zest-accent);\n color: #ffffff;\n}\n\n.zest-btn--primary:hover {\n background: var(--zest-accent-hover);\n}\n\n.zest-btn--secondary {\n background: var(--zest-bg-secondary);\n color: var(--zest-text);\n}\n\n.zest-btn--secondary:hover {\n background: var(--zest-border);\n}\n\n.zest-btn--ghost {\n background: transparent;\n color: var(--zest-text-secondary);\n}\n\n.zest-btn--ghost:hover {\n background: var(--zest-bg-secondary);\n color: var(--zest-text);\n}\n\n/* Modal */\n.zest-modal-overlay {\n position: fixed;\n inset: 0;\n z-index: 999998;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n background: rgba(0, 0, 0, 0.5);\n animation: zest-fade-in 0.2s ease-out;\n}\n\n@keyframes zest-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.zest-modal {\n width: 100%;\n max-width: 500px;\n max-height: 90vh;\n overflow-y: auto;\n background: var(--zest-bg);\n border-radius: var(--zest-radius);\n box-shadow: var(--zest-shadow);\n animation: zest-modal-in 0.3s ease-out;\n}\n\n@keyframes zest-modal-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n.zest-modal__header {\n padding: 20px 20px 0;\n}\n\n.zest-modal__title {\n margin: 0 0 8px 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--zest-text);\n}\n\n.zest-modal__description {\n margin: 0;\n font-size: 14px;\n color: var(--zest-text-secondary);\n}\n\n.zest-modal__body {\n padding: 20px;\n}\n\n.zest-modal__footer {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n padding: 0 20px 20px;\n}\n\n/* Categories */\n.zest-category {\n padding: 16px;\n margin-bottom: 12px;\n background: var(--zest-bg-secondary);\n border-radius: var(--zest-radius-sm);\n}\n\n.zest-category:last-child {\n margin-bottom: 0;\n}\n\n.zest-category__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.zest-category__info {\n flex: 1;\n}\n\n.zest-category__label {\n display: block;\n font-size: 14px;\n font-weight: 600;\n color: var(--zest-text);\n}\n\n.zest-category__description {\n margin: 4px 0 0;\n font-size: 13px;\n color: var(--zest-text-secondary);\n}\n\n/* Toggle Switch */\n.zest-toggle {\n position: relative;\n width: 44px;\n height: 24px;\n flex-shrink: 0;\n}\n\n.zest-toggle__input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n cursor: pointer;\n margin: 0;\n}\n\n.zest-toggle__input:disabled {\n cursor: not-allowed;\n}\n\n.zest-toggle__slider {\n position: absolute;\n inset: 0;\n background: var(--zest-border);\n border-radius: 12px;\n transition: background-color 0.2s ease;\n pointer-events: none;\n}\n\n.zest-toggle__slider::before {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: 20px;\n height: 20px;\n background: #ffffff;\n border-radius: 50%;\n transition: transform 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n}\n\n.zest-toggle__input:checked + .zest-toggle__slider {\n background: var(--zest-accent);\n}\n\n.zest-toggle__input:checked + .zest-toggle__slider::before {\n transform: translateX(20px);\n}\n\n.zest-toggle__input:focus-visible + .zest-toggle__slider {\n outline: 2px solid var(--zest-accent);\n outline-offset: 2px;\n}\n\n.zest-toggle__input:disabled + .zest-toggle__slider {\n opacity: 0.6;\n}\n\n/* Widget */\n.zest-widget {\n position: fixed;\n z-index: 999997;\n bottom: 20px;\n left: 20px;\n}\n\n.zest-widget__btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n padding: 0;\n background: var(--zest-bg);\n border: 1px solid var(--zest-border);\n border-radius: 50%;\n box-shadow: var(--zest-shadow);\n cursor: pointer;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n}\n\n.zest-widget__btn:hover {\n transform: scale(1.05);\n box-shadow: 0 12px 28px -5px rgba(0, 0, 0, 0.15);\n}\n\n.zest-widget__btn:focus-visible {\n outline: 2px solid var(--zest-accent);\n outline-offset: 2px;\n}\n\n.zest-widget__icon {\n width: 24px;\n height: 24px;\n fill: var(--zest-text);\n}\n\n/* Link */\n.zest-link {\n color: var(--zest-accent);\n text-decoration: none;\n}\n\n.zest-link:hover {\n text-decoration: underline;\n}\n\n/* Mobile */\n@media (max-width: 480px) {\n .zest-banner {\n left: 10px;\n right: 10px;\n max-width: none;\n transform: none;\n }\n\n .zest-banner--bottom,\n .zest-banner--bottom-left,\n .zest-banner--bottom-right {\n bottom: 10px;\n }\n\n .zest-banner--top {\n top: 10px;\n transform: none;\n }\n\n @keyframes zest-slide-in {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n .zest-banner__buttons {\n flex-direction: column;\n }\n\n .zest-btn {\n width: 100%;\n }\n\n .zest-modal-overlay {\n padding: 10px;\n }\n\n .zest-widget {\n bottom: 10px;\n left: 10px;\n }\n}\n\n/* Hidden utility */\n.zest-hidden {\n display: none !important;\n}\n${t.customStyles||""}\n`}let tt=null,nt=null;function et(t={}){tt?tt.classList.remove("zest-hidden"):function(t={}){if(tt)return tt;const n=q();tt=document.createElement("zest-banner"),tt.setAttribute("data-theme",n.theme||"light"),nt=tt.attachShadow({mode:"open"});const e=document.createElement("style");e.textContent=Q(n),nt.appendChild(e);const o=document.createElement("div");o.innerHTML=function(t){const n=t.labels.banner;return`\n <div class="zest-banner zest-banner--${t.position||"bottom"}" role="dialog" aria-modal="false" aria-label="${n.title}">\n <h2 class="zest-banner__title">${n.title}</h2>\n <p class="zest-banner__description">${n.description}</p>\n <div class="zest-banner__buttons">\n <button type="button" class="zest-btn zest-btn--primary" data-action="accept-all">\n ${n.acceptAll}\n </button>\n <button type="button" class="zest-btn zest-btn--secondary" data-action="reject-all">\n ${n.rejectAll}\n </button>\n <button type="button" class="zest-btn zest-btn--ghost" data-action="settings">\n ${n.settings}\n </button>\n </div>\n </div>\n `}(n),nt.appendChild(o.firstElementChild);const a=nt.querySelector(".zest-banner");a.addEventListener("click",n=>{const e=n.target.dataset.action;if(e)switch(e){case"accept-all":t.onAcceptAll?.();break;case"reject-all":t.onRejectAll?.();break;case"settings":t.onSettings?.()}}),a.addEventListener("keydown",n=>{"Escape"===n.key&&t.onSettings?.()}),document.body.appendChild(tt),requestAnimationFrame(()=>{const t=nt.querySelector("button");t?.focus()})}(t)}function ot(){tt&&(tt.remove(),tt=null,nt=null)}let at=null,st=null,rt={};function it(){if(!st)return rt;const t=st.querySelectorAll(".zest-toggle__input"),n={essential:!0};return t.forEach(t=>{const e=t.dataset.category;e&&"essential"!==e&&(n[e]=t.checked)}),n}function ct(t={},n={}){if(at)return at;const e=q();rt={...t},at=document.createElement("zest-modal"),at.setAttribute("data-theme",e.theme||"light"),st=at.attachShadow({mode:"open"});const o=document.createElement("style");o.textContent=Q(e),st.appendChild(o);const a=document.createElement("div");a.innerHTML=function(t,n){const e=t.labels.modal,o=Object.values(t.categories||S).map(t=>{return`\n <div class="zest-category">\n <div class="zest-category__header">\n <div class="zest-category__info">\n <span class="zest-category__label">${(e=t).label}</span>\n <p class="zest-category__description">${e.description}</p>\n </div>\n <label class="zest-toggle">\n <input\n type="checkbox"\n class="zest-toggle__input"\n data-category="${e.id}"\n ${n[t.id]??t.default?"checked":""}\n ${t.required?"disabled":""}\n aria-label="${e.label}"\n >\n <span class="zest-toggle__slider"></span>\n </label>\n </div>\n </div>\n `;var e}).join("");return`\n <div class="zest-modal-overlay" role="dialog" aria-modal="true" aria-label="${e.title}">\n <div class="zest-modal">\n <div class="zest-modal__header">\n <h2 class="zest-modal__title">${e.title}</h2>\n <p class="zest-modal__description">${e.description} ${t.policyUrl?`<a href="${t.policyUrl}" class="zest-link" target="_blank" rel="noopener">Privacy Policy</a>`:""}</p>\n </div>\n <div class="zest-modal__body">\n ${o}\n </div>\n <div class="zest-modal__footer">\n <button type="button" class="zest-btn zest-btn--primary" data-action="save">\n ${e.save}\n </button>\n <button type="button" class="zest-btn zest-btn--secondary" data-action="accept-all">\n ${e.acceptAll}\n </button>\n <button type="button" class="zest-btn zest-btn--ghost" data-action="reject-all">\n ${e.rejectAll}\n </button>\n </div>\n </div>\n </div>\n `}(e,t),st.appendChild(a.firstElementChild);const s=st.querySelector(".zest-modal-overlay");return s.addEventListener("click",t=>{const e=t.target.dataset.action;if(e)switch(e){case"save":n.onSave?.(it());break;case"accept-all":n.onAcceptAll?.();break;case"reject-all":n.onRejectAll?.()}else t.target===s&&n.onClose?.()}),s.addEventListener("keydown",t=>{"Escape"===t.key&&n.onClose?.()}),st.querySelectorAll(".zest-toggle__input").forEach(t=>{t.addEventListener("change",()=>{rt=it()})}),document.body.appendChild(at),requestAnimationFrame(()=>{const t=st.querySelector("button");t?.focus()}),at}function lt(){at&&(at.remove(),at=null,st=null)}let dt=null,ut=null;function pt(t={}){dt?dt.style.display="":function(t={}){if(dt)return dt;const n=q();dt=document.createElement("zest-widget"),dt.setAttribute("data-theme",n.theme||"light"),ut=dt.attachShadow({mode:"open"});const e=document.createElement("style");e.textContent=Q(n),ut.appendChild(e);const o=document.createElement("div");o.innerHTML=function(t){const n=t.labels.widget;return`\n <div class="zest-widget">\n <button type="button" class="zest-widget__btn" aria-label="${n.label}" title="${n.label}">\n <span class="zest-widget__icon"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10c0-.728-.078-1.437-.225-2.12a1 1 0 0 0-1.482-.63 3 3 0 0 1-4.086-3.72 1 1 0 0 0-.793-1.263A10.05 10.05 0 0 0 12 2zm0 2c.178 0 .354.006.528.017a5 5 0 0 0 5.955 5.955c.011.174.017.35.017.528 0 4.418-3.582 8-8 8s-8-3.582-8-8 3.582-8 8-8zm-4 6a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zm5 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm-2 4a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z"/></svg></span>\n </button>\n </div>\n `}(n),ut.appendChild(o.firstElementChild),ut.querySelector(".zest-widget__btn").addEventListener("click",()=>{t.onClick?.()}),document.body.appendChild(dt)}(t)}function gt(){dt&&(dt.style.display="none")}let mt=!1,ft=null;function bt(t){return Z(t)}function yt(t){!function(t){const n=[];for(const e of a)t.includes(e.category)?o?.set&&o.set.call(document,e.value):n.push(e);a.length=0,a.push(...n)}(t),function(t){const n=[];for(const e of l)t.includes(e.category)?i?.setItem(e.key,e.value):n.push(e);l.length=0,l.push(...n);const e=[];for(const n of d)t.includes(n.category)?c?.setItem(n.key,n.value):e.push(n);d.length=0,d.push(...e)}(t),function(t){const n=[];for(const e of y)t.includes(e.category)?_(e):n.push(e);y.length=0,y.push(...n),document.querySelectorAll('script[data-zest-processed="blocked"]').forEach(n=>{const e=n.getAttribute("data-consent-category");if(t.includes(e)){const t=document.createElement("script"),e=n.getAttribute("data-blocked-src");e?t.src=e:t.textContent=n.textContent,n.async&&(t.async=!0),n.defer&&(t.defer=!0),t.setAttribute("data-zest-processed","executed"),t.setAttribute("data-consent-executed","true"),n.parentNode?.replaceChild(t,n)}})}(t)}function zt(){const t=function(t=365){return W({functional:!0,analytics:!0,marketing:!0},t)}(ft.expiration),n=Object.keys(S);j(t.current,ft,!1),ot(),lt(),yt(n),ft.showWidget&&pt({onClick:xt}),F(t.current,t.previous),X(t.current,t.previous),ft.callbacks?.onAccept?.(t.current),ft.callbacks?.onChange?.(t.current)}function ht(){const t=Y(ft.expiration);j(t.current,ft,!1),ot(),lt(),ft.showWidget&&pt({onClick:xt}),J(t.current),X(t.current,t.previous),ft.callbacks?.onReject?.(),ft.callbacks?.onChange?.(t.current)}function wt(t){const n=W(t,ft.expiration);j(n.current,ft,!1);const e=Object.keys(n.current).filter(t=>n.current[t]&&!n.previous[t]);e.length>0&&yt(e),lt(),ft.showWidget&&pt({onClick:xt});Object.entries(t).some(([t,n])=>"essential"!==t&&n)?F(n.current,n.previous):J(n.current),X(n.current,n.previous),ft.callbacks?.onChange?.(n.current)}function xt(){ot(),gt(),ct(P(),{onSave:wt,onAcceptAll:zt,onRejectAll:ht,onClose:vt}),V("modal")}function vt(){lt(),K("modal"),H()&&ft.showWidget?pt({onClick:xt}):et({onAcceptAll:zt,onRejectAll:ht,onSettings:xt})}function kt(r={}){if(mt)return console.warn("[Zest] Already initialized"),_t;ft=function(t){return D=N(t),D}(r),j({functional:!1,analytics:!1,marketing:!1},ft,!0),ft.patterns&&function(e){n={...t};for(const[t,o]of Object.entries(e))Array.isArray(o)&&(n[t]=o.map(t=>t instanceof RegExp?t:RegExp(t)))}(ft.patterns),s=bt,function(t){u=t}(bt),function(t){x=t}(bt),o=Object.getOwnPropertyDescriptor(Document.prototype,"cookie"),o?Object.defineProperty(document,"cookie",{get:()=>o.get.call(document),set(t){const n=function(t){const n=t.match(/^([^=]+)/);return n?n[1].trim():null}(t);if(!n)return;const r=e(n);s(r)?o.set.call(document,t):a.push({value:t,name:n,category:r,timestamp:Date.now()})},configurable:!0}):console.warn("[Zest] Could not get cookie descriptor"),function(){try{return i=window.localStorage,c=window.sessionStorage,Object.defineProperty(window,"localStorage",{value:p(i,l),configurable:!0,writable:!1}),Object.defineProperty(window,"sessionStorage",{value:p(c,d),configurable:!0,writable:!1}),!0}catch(t){return console.warn("[Zest] Could not intercept storage APIs:",t),!1}}(),C(ft.mode,ft.blockedDomains);const g=U();mt=!0,H()&&j(g,ft,!1);let m=!1;if(E()&&ft.respectDNT&&"ignore"!==ft.dntBehavior&&"reject"===ft.dntBehavior&&!H()){const t=Y(ft.expiration);m=!0,j(t.current,ft,!1),J(t.current),X(t.current,t.previous),ft.callbacks?.onReject?.(),ft.callbacks?.onChange?.(t.current)}return function(t){G(B.READY,{consent:t})}(g),ft.callbacks?.onReady?.(g),H()||m?ft.showWidget&&pt({onClick:xt}):(et({onAcceptAll:zt,onRejectAll:ht,onSettings:xt}),V("banner")),_t}const _t={init:kt,show(){mt?(lt(),gt(),et({onAcceptAll:zt,onRejectAll:ht,onSettings:xt}),V("banner")):console.warn("[Zest] Not initialized. Call Zest.init() first.")},hide(){ot(),K("banner")},showSettings(){mt?xt():console.warn("[Zest] Not initialized. Call Zest.init() first.")},hideSettings(){lt(),K("modal")},getConsent:P,hasConsent:Z,hasConsentDecision:H,getConsentProof:function(){try{const t=I().match(RegExp(L+"=([^;]+)"));if(t)return JSON.parse(decodeURIComponent(t[1]))}catch(t){}return null},isDoNotTrackEnabled:E,getDNTDetails:function(){if("undefined"==typeof navigator)return{enabled:!1,source:null};const t=navigator.doNotTrack||window.doNotTrack||navigator.msDoNotTrack;return"1"===t||"yes"===t||!0===t?{enabled:!0,source:"dnt"}:!0===navigator.globalPrivacyControl?{enabled:!0,source:"gpc"}:{enabled:!1,source:null}},acceptAll(){mt?zt():console.warn("[Zest] Not initialized. Call Zest.init() first.")},rejectAll(){mt?ht():console.warn("[Zest] Not initialized. Call Zest.init() first.")},reset(){O(L+"=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/"),T=null,lt(),dt&&(dt.remove(),dt=null,ut=null),mt&&(et({onAcceptAll:zt,onRejectAll:ht,onSettings:xt}),V("banner"))},getConfig:q,EVENTS:B};if("undefined"!=typeof window){window.Zest=_t;const t=()=>{!1!==$().autoInit&&kt(window.ZestConfig)};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",t):t()}return _t}();
package/dist/zest.esm.js CHANGED
@@ -839,6 +839,78 @@ function getDNTDetails() {
839
839
  return { enabled: false, source: null };
840
840
  }
841
841
 
842
+ /**
843
+ * Consent Signals - Optional vendor consent mode integrations
844
+ *
845
+ * Pushes consent state to Google Consent Mode v2 and/or Microsoft UET
846
+ * Consent Mode when enabled via config.
847
+ */
848
+
849
+ /**
850
+ * Map Zest consent state to Google Consent Mode v2 signals
851
+ */
852
+ function toGoogleSignals(consent) {
853
+ const g = (val) => val ? 'granted' : 'denied';
854
+ return {
855
+ ad_storage: g(consent.marketing),
856
+ ad_user_data: g(consent.marketing),
857
+ ad_personalization: g(consent.marketing),
858
+ analytics_storage: g(consent.analytics),
859
+ functionality_storage: 'granted', // essential is always true
860
+ personalization_storage: g(consent.functional)
861
+ };
862
+ }
863
+
864
+ /**
865
+ * Push consent signal to Google via gtag or dataLayer fallback.
866
+ * Uses a local function to preserve the `arguments` object shape
867
+ * that gtag/dataLayer expects (not an array).
868
+ */
869
+ function pushGoogle(type, signals) {
870
+ window.dataLayer = window.dataLayer || [];
871
+ if (typeof window.gtag === 'function') {
872
+ window.gtag('consent', type, signals);
873
+ } else {
874
+ function gtagFallback() { window.dataLayer.push(arguments); }
875
+ gtagFallback('consent', type, signals);
876
+ }
877
+ }
878
+
879
+ /**
880
+ * Map Zest consent state to Microsoft UET signal.
881
+ * Microsoft UET only exposes ad_storage.
882
+ */
883
+ function toMicrosoftSignals(consent) {
884
+ return { ad_storage: consent.marketing ? 'granted' : 'denied' };
885
+ }
886
+
887
+ /**
888
+ * Push consent signal to Microsoft UET
889
+ */
890
+ function pushMicrosoft(type, signals) {
891
+ window.uetq = window.uetq || [];
892
+ window.uetq.push('consent', type, signals);
893
+ }
894
+
895
+ /**
896
+ * Apply consent signals to enabled vendor integrations.
897
+ *
898
+ * @param {Object} consent Current Zest consent state
899
+ * @param {Object} config Merged Zest config
900
+ * @param {boolean} isDefault true on first call (pushes 'default'), false for updates
901
+ */
902
+ function applyConsentSignals(consent, config, isDefault) {
903
+ const type = isDefault ? 'default' : 'update';
904
+
905
+ if (config.consentModeGoogle) {
906
+ pushGoogle(type, toGoogleSignals(consent));
907
+ }
908
+
909
+ if (config.consentModeMicrosoft) {
910
+ pushMicrosoft(type, toMicrosoftSignals(consent));
911
+ }
912
+ }
913
+
842
914
  /**
843
915
  * Built-in translations for Zest
844
916
  * Language is auto-detected from <html lang=""> or navigator.language
@@ -1438,6 +1510,10 @@ const DEFAULTS = {
1438
1510
  // Custom styles to inject into Shadow DOM
1439
1511
  customStyles: '',
1440
1512
 
1513
+ // Vendor consent mode integrations (optional)
1514
+ consentModeGoogle: false,
1515
+ consentModeMicrosoft: false,
1516
+
1441
1517
  // Blocking mode: 'manual' | 'safe' | 'strict' | 'doomsday'
1442
1518
  mode: 'safe',
1443
1519
 
@@ -1468,7 +1544,7 @@ function mergeConfig(userConfig) {
1468
1544
  }
1469
1545
 
1470
1546
  // Simple properties
1471
- const simpleKeys = ['lang', 'position', 'theme', 'accentColor', 'autoInit', 'showWidget', 'expiration', 'policyUrl', 'imprintUrl', 'customStyles', 'mode', 'blockedDomains', 'respectDNT', 'dntBehavior'];
1547
+ const simpleKeys = ['lang', 'position', 'theme', 'accentColor', 'autoInit', 'showWidget', 'expiration', 'policyUrl', 'imprintUrl', 'customStyles', 'mode', 'blockedDomains', 'respectDNT', 'dntBehavior', 'consentModeGoogle', 'consentModeMicrosoft'];
1472
1548
  for (const key of simpleKeys) {
1473
1549
  if (userConfig[key] !== undefined) {
1474
1550
  config[key] = userConfig[key];
@@ -1578,6 +1654,13 @@ function parseDataAttributes() {
1578
1654
  const expiration = script.getAttribute('data-expiration');
1579
1655
  if (expiration) config.expiration = parseInt(expiration, 10);
1580
1656
 
1657
+ // Consent mode integrations
1658
+ const consentModeGoogle = script.getAttribute('data-consent-mode-google');
1659
+ if (consentModeGoogle !== null) config.consentModeGoogle = consentModeGoogle !== 'false';
1660
+
1661
+ const consentModeMicrosoft = script.getAttribute('data-consent-mode-microsoft');
1662
+ if (consentModeMicrosoft !== null) config.consentModeMicrosoft = consentModeMicrosoft !== 'false';
1663
+
1581
1664
  return config;
1582
1665
  }
1583
1666
 
@@ -2809,6 +2892,8 @@ function handleAcceptAll() {
2809
2892
  const result = acceptAll(config.expiration);
2810
2893
  const categories = getCategoryIds();
2811
2894
 
2895
+ applyConsentSignals(result.current, config, false);
2896
+
2812
2897
  hideBanner();
2813
2898
  hideModal();
2814
2899
 
@@ -2830,6 +2915,8 @@ function handleAcceptAll() {
2830
2915
  function handleRejectAll() {
2831
2916
  const result = rejectAll(config.expiration);
2832
2917
 
2918
+ applyConsentSignals(result.current, config, false);
2919
+
2833
2920
  hideBanner();
2834
2921
  hideModal();
2835
2922
 
@@ -2849,6 +2936,8 @@ function handleRejectAll() {
2849
2936
  function handleSavePreferences(selections) {
2850
2937
  const result = updateConsent(selections, config.expiration);
2851
2938
 
2939
+ applyConsentSignals(result.current, config, false);
2940
+
2852
2941
  // Find newly allowed categories
2853
2942
  const newlyAllowed = Object.keys(result.current).filter(
2854
2943
  cat => result.current[cat] && !result.previous[cat]
@@ -2927,6 +3016,13 @@ function init(userConfig = {}) {
2927
3016
  // Merge config
2928
3017
  config = setConfig(userConfig);
2929
3018
 
3019
+ // Push default denied state to vendor consent mode APIs (must happen before scripts load)
3020
+ applyConsentSignals(
3021
+ { functional: false, analytics: false, marketing: false },
3022
+ config,
3023
+ true
3024
+ );
3025
+
2930
3026
  // Set patterns if provided
2931
3027
  if (config.patterns) {
2932
3028
  setPatterns(config.patterns);
@@ -2947,6 +3043,11 @@ function init(userConfig = {}) {
2947
3043
 
2948
3044
  initialized = true;
2949
3045
 
3046
+ // Push update for returning visitors with saved consent
3047
+ if (hasConsentDecision()) {
3048
+ applyConsentSignals(consent, config, false);
3049
+ }
3050
+
2950
3051
  // Check Do Not Track / Global Privacy Control
2951
3052
  const dntEnabled = isDoNotTrackEnabled();
2952
3053
  let dntApplied = false;
@@ -2957,6 +3058,8 @@ function init(userConfig = {}) {
2957
3058
  const result = rejectAll(config.expiration);
2958
3059
  dntApplied = true;
2959
3060
 
3061
+ applyConsentSignals(result.current, config, false);
3062
+
2960
3063
  // Emit events
2961
3064
  emitReject(result.current);
2962
3065
  emitChange(result.current, result.previous);