<style id="cxt-consent-front-bridge-css">#cxt-consent-banner,#cxt-consent-modal,#cxt-consent-fab,.cxt-consent-embed-placeholder{box-sizing:border-box;font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
#cxt-consent-banner *,#cxt-consent-modal *,#cxt-consent-fab *,.cxt-consent-embed-placeholder *{box-sizing:border-box}
.cxt-consent-banner{position:fixed;z-index:2147483000;left:18px;right:18px;bottom:18px;max-width:920px;margin:auto;padding:20px 22px;border-radius:22px;background:#170037;color:#fff;border:1px solid rgba(255,255,255,.18);box-shadow:0 24px 70px rgba(0,0,0,.42);display:grid;gap:12px}
.cxt-consent-banner.is-necessary{max-width:760px}.cxtc-banner-status{display:flex;align-items:center;gap:9px}.cxtc-banner-status>span{width:12px;height:12px;border-radius:99px;background:#49dd78;box-shadow:0 0 0 5px rgba(73,221,120,.14)}.cxt-consent-banner p{margin:0;color:#e4dbff;line-height:1.5}.cxt-consent-banner>div:last-child,.cxtc-banner-actions{display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap}.cxt-consent-banner button,.cxt-consent-dialog button{min-height:42px;border-radius:999px;border:1px solid rgba(255,255,255,.18);padding:10px 16px;font:inherit;font-weight:800;cursor:pointer}.cxtc-details,.cxtc-reject{background:transparent;color:#fff}.cxt-consent-dialog .cxtc-reject{background:#f4effc;color:#2a0b4d;border-color:#ded1ed!important}.cxtc-understood,.cxtc-accept,.cxtc-save{background:#ffd84d;color:#20004d;border-color:#ffd84d!important}
.cxt-consent-modal[hidden]{display:none!important}.cxt-consent-modal{position:fixed;inset:0;z-index:2147483100;display:grid;place-items:center;padding:18px}.cxt-consent-backdrop{position:absolute;inset:0;background:rgba(10,0,27,.72);backdrop-filter:blur(4px)}.cxt-consent-dialog{position:relative;width:min(760px,100%);max-height:min(86vh,780px);overflow:hidden;border-radius:26px;padding:0;background:#fff;color:#20152d;box-shadow:0 30px 100px rgba(0,0,0,.46);display:grid;grid-template-rows:minmax(0,1fr) auto}.cxtc-scroll{min-height:0;overflow:auto;padding:26px 26px 18px;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}.cxtc-close{position:absolute;right:16px;top:14px;width:42px;padding:0!important;font-size:26px;background:#f4effc;color:#2d1355}.cxtc-status-line{display:flex;align-items:center;gap:9px;padding-right:54px;color:#5b4a69;font-weight:700;font-size:13px}.cxtc-status-dot{width:11px;height:11px;border-radius:99px}.cxtc-status-dot.is-green{background:#26bf5b;box-shadow:0 0 0 4px rgba(38,191,91,.12)}.cxtc-status-dot.is-red{background:#e95058;box-shadow:0 0 0 4px rgba(233,80,88,.12)}.cxt-consent-dialog h2{margin:12px 0 6px;color:#2a0b4d;font-size:30px;line-height:1.05}.cxtc-intro{margin:0 0 20px;color:#665873}.cxtc-categories{display:grid;gap:10px}.cxtc-front-row{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:15px;border:1px solid #e6def0;border-radius:16px;background:#fbf9fd}.cxtc-front-copy{display:grid;gap:3px}.cxtc-front-copy strong{color:#2a0b4d}.cxtc-front-copy small{color:#74677e;line-height:1.45}.cxtc-switch{position:relative;flex:0 0 52px}.cxtc-switch input{position:absolute;opacity:0}.cxtc-switch i{display:block;width:50px;height:29px;border-radius:99px;background:#d9d2df;position:relative;transition:.2s}.cxtc-switch i:after{content:"";position:absolute;left:4px;top:4px;width:21px;height:21px;border-radius:99px;background:#fff;box-shadow:0 2px 5px rgba(0,0,0,.18);transition:.2s}.cxtc-switch input:checked+i{background:#7643de}.cxtc-switch input:checked+i:after{transform:translateX(21px)}.cxtc-switch input:disabled+i{background:#2db960;opacity:.75}.cxtc-services{margin-top:18px;padding-top:16px;border-top:1px solid #ebe4f1}.cxtc-services h3{margin:0 0 10px;color:#2a0b4d}.cxtc-service-group{margin:0 0 7px;border:1px solid #e7dff0;border-radius:13px;overflow:hidden;background:#fbf9fd}.cxtc-service-toggle{width:100%;min-height:40px!important;border:0!important;border-radius:0!important;padding:8px 10px!important;background:#fbf9fd!important;color:#2a0b4d!important;display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:8px;text-align:left}.cxtc-service-title{font-size:13px;font-weight:900}.cxtc-service-count{min-width:24px;height:24px;padding:0 7px;border-radius:999px;background:#eee6f8;color:#6337b7!important;font-size:11px!important;font-weight:900;display:grid;place-items:center}.cxtc-service-chevron{color:#7643de!important;font-size:17px!important;line-height:1;transition:transform .18s ease}.cxtc-service-toggle[aria-expanded="true"] .cxtc-service-chevron{transform:rotate(180deg)}.cxtc-service-list{padding:0 10px 5px;border-top:1px solid #eee7f3;background:#fff}.cxtc-service-list[hidden]{display:none!important}.cxtc-service-list p{display:grid;grid-template-columns:minmax(120px,.75fr) 1.5fr;gap:10px;margin:0;padding:8px 0;border-bottom:1px solid #f0eaf4}.cxtc-service-list p:last-child{border-bottom:0}.cxtc-service-list strong{font-size:12px;color:#2a0b4d}.cxtc-service-list span{color:#766a7e;font-size:12px;line-height:1.35}.cxtc-actions{position:relative;z-index:3;display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap;margin:0;padding:12px 26px 14px;background:#fff;border-top:1px solid #ebe4f1;box-shadow:0 -10px 24px rgba(42,11,77,.06)}.cxtc-privacy{display:inline-block;margin-top:14px;color:#5c35a8;font-size:13px;font-weight:700}
.cxt-consent-fab{position:fixed;z-index:2147482900;left:18px;right:auto;top:auto;bottom:78px;transform:none;width:60px;height:60px;border-radius:16px;border:1px solid rgba(43,11,78,.16);display:grid;place-items:center;cursor:pointer;box-shadow:0 12px 34px rgba(39,31,29,.16);background:rgba(255,253,251,.98);backdrop-filter:blur(10px)}.cxt-consent-fab svg{width:28px;height:28px;fill:none;stroke:#2b0b4e;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}.cxtc-fab-dot{position:absolute;right:1px;top:1px;width:13px;height:13px;border:2px solid #fff;border-radius:99px}.cxt-consent-fab.is-green .cxtc-fab-dot{background:#24bd59}.cxt-consent-fab.is-red .cxtc-fab-dot{background:#e94b55}
/* v0.1.21 — le dock CXT Navigation prend uniquement la position du FAB. */
.cxt-consent-fab.is-cxt-navigation-docked{position:relative!important;left:auto!important;right:auto!important;top:auto!important;bottom:auto!important;transform:none!important;margin:0!important}
.cxt-consent-embed-placeholder{min-height:180px;padding:24px;border-radius:18px;border:1px dashed #bca9ce;background:#f7f3fb;color:#2e1a3d;display:grid;place-items:center;text-align:center;gap:7px}.cxt-consent-embed-placeholder strong{font-size:18px}.cxt-consent-embed-placeholder span{color:#6e6076;max-width:620px}.cxt-consent-embed-placeholder button{border:0;border-radius:999px;background:#ffd84d;color:#20004d;padding:10px 16px;font-weight:900;cursor:pointer}
html.cxt-consent-open{overflow:hidden}

/* v0.1.28 — vue PC compacte : tout le niveau principal tient d’un coup d’œil.
   Les 4 catégories et les services identifiés passent en deux colonnes,
   tandis que le bandeau d’actions reste fixe en bas du panneau. */
@media(min-width:701px) and (pointer:fine){
  .cxt-consent-dialog{width:min(980px,calc(100vw - 36px));max-height:min(92vh,760px)}
  .cxtc-scroll{display:grid;grid-template-columns:minmax(0,1.08fr) minmax(300px,.92fr);grid-template-areas:"status status" "title title" "intro intro" "categories services" "privacy services";column-gap:18px;row-gap:0;padding:20px 22px 14px;overflow:auto}
  .cxtc-status-line{grid-area:status}
  .cxt-consent-dialog h2{grid-area:title;margin:9px 0 4px;font-size:27px}
  .cxtc-intro{grid-area:intro;margin:0 0 12px;font-size:14px;line-height:1.4}
  .cxtc-categories{grid-area:categories;gap:7px;align-self:start}
  .cxtc-front-row{gap:12px;padding:10px 12px;border-radius:13px}
  .cxtc-front-copy{gap:2px}
  .cxtc-front-copy strong{font-size:14px}
  .cxtc-front-copy small{font-size:11.5px;line-height:1.28}
  .cxtc-switch{flex-basis:46px}
  .cxtc-switch i{width:44px;height:26px}
  .cxtc-switch i:after{left:3px;top:3px;width:20px;height:20px}
  .cxtc-switch input:checked+i:after{transform:translateX(18px)}
  .cxtc-services{grid-area:services;align-self:start;margin:0;padding:0 0 0 18px;border-top:0;border-left:1px solid #ebe4f1}
  .cxtc-services h3{margin:0 0 7px;font-size:16px}
  .cxtc-service-group{margin-bottom:6px;border-radius:11px}
  .cxtc-service-toggle{min-height:36px!important;padding:6px 9px!important}
  .cxtc-service-list{max-height:180px;overflow:auto}
  .cxtc-service-list p{grid-template-columns:minmax(100px,.72fr) 1.4fr;gap:8px;padding:6px 0}
  .cxtc-service-list strong,.cxtc-service-list span{font-size:11px}
  .cxtc-privacy{grid-area:privacy;align-self:end;margin-top:10px;font-size:12px}
  .cxtc-actions{gap:8px;padding:10px 22px 12px}
  .cxtc-actions button{min-height:38px;padding:8px 14px}
}

@media(max-width:700px),(pointer:coarse) and (max-height:700px){
  .cxt-consent-banner{left:10px;right:10px;bottom:10px;padding:17px;border-radius:18px}.cxt-consent-banner>div:last-child,.cxtc-banner-actions{display:grid;grid-template-columns:1fr}.cxt-consent-banner button{width:100%}
  .cxt-consent-modal{z-index:2147482400;padding:12px 12px 12px 72px;place-items:center start}
  .cxt-consent-dialog{width:min(320px,calc(100vw - 84px));max-width:320px;height:min(440px,calc(100vh - 24px));height:min(440px,calc(100svh - 24px));max-height:none;overflow:hidden;border-radius:18px;padding:0}.cxtc-scroll{padding:14px 12px 10px}
  .cxtc-close{right:10px;top:9px;width:32px;min-height:32px!important;height:32px;padding:0!important;font-size:20px}.cxtc-status-line{gap:7px;padding-right:38px;font-size:11px}.cxtc-status-dot{width:9px;height:9px}
  .cxt-consent-dialog h2{margin:9px 0 4px;font-size:20px}.cxtc-intro{margin:0 0 11px;font-size:12px;line-height:1.35}.cxtc-categories{gap:6px}.cxtc-front-row{align-items:flex-start;gap:10px;padding:9px;border-radius:11px}.cxtc-front-copy{gap:2px}.cxtc-front-copy strong{font-size:12px}.cxtc-front-copy small{font-size:10px;line-height:1.3}.cxtc-switch{flex-basis:42px}.cxtc-switch i{width:40px;height:24px}.cxtc-switch i:after{left:3px;top:3px;width:18px;height:18px}.cxtc-switch input:checked+i:after{transform:translateX(16px)}
  .cxtc-services{margin-top:9px;padding-top:8px}.cxtc-services h3{margin-bottom:5px;font-size:12px}.cxtc-service-group{margin-bottom:5px;border-radius:10px}.cxtc-service-toggle{min-height:34px!important;padding:5px 8px!important;gap:6px}.cxtc-service-title{font-size:11px}.cxtc-service-count{min-width:20px;height:20px;padding:0 6px;font-size:9px!important}.cxtc-service-chevron{font-size:15px!important}.cxtc-service-list{padding:0 8px 3px}.cxtc-service-list p{grid-template-columns:1fr;gap:2px;padding:5px 0}.cxtc-service-list strong{font-size:10px}.cxtc-service-list span{font-size:9px;line-height:1.25}
  .cxtc-actions{display:grid;grid-template-columns:1fr;gap:6px;margin:0;padding:8px 12px 10px}.cxtc-actions button{width:100%;min-height:36px;padding:7px 10px}.cxtc-privacy{margin-top:9px;margin-bottom:0;font-size:11px}.cxt-consent-fab{left:12px;right:auto;top:auto;bottom:72px;transform:none;width:60px;height:60px;border-radius:16px}
}
/* 0.1.29 — emplacement mobile commun avec CXT Live et CXT Pulse. */
@media(max-width:700px),(pointer:coarse) and (max-height:700px){
  .cxt-consent-modal.is-cxt-navigation-utility-panel{padding:0!important;place-items:start!important}
  .cxt-consent-modal.is-cxt-navigation-utility-panel .cxt-consent-dialog{position:fixed!important;left:var(--cxt-consent-panel-left,72px)!important;top:var(--cxt-consent-panel-top,12px)!important;right:auto!important;bottom:auto!important;margin:0!important;transform:none!important}
}
@media(prefers-reduced-motion:reduce){#cxt-consent-banner *,#cxt-consent-modal *,#cxt-consent-fab *{transition:none!important;animation:none!important}}

/* 0.1.31 — symétrie visuelle accepter/refuser : même niveau de proéminence. */
.cxt-consent-banner .cxtc-reject,.cxt-consent-banner .cxtc-accept{background:#fff!important;color:#2a0b4d!important;border-color:#fff!important;box-shadow:0 2px 10px rgba(0,0,0,.08)}
.cxt-consent-dialog .cxtc-reject,.cxt-consent-dialog .cxtc-accept{background:#f4effc!important;color:#2a0b4d!important;border-color:#d8cae8!important;box-shadow:inset 0 0 0 1px rgba(42,11,77,.04)}
</style><?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="https://www.carrextriangle.fr/wp-sitemap-index.xsl" ?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><sitemap><loc>https://www.carrextriangle.fr/wp-sitemap-posts-page-1.xml</loc></sitemap></sitemapindex>
<script id="cxt-consent-front-bridge-config" data-cookieconsent="ignore" data-cxt-consent-runtime="config">window.CXTConsentConfig={"ajaxUrl":"\/wp-admin\/admin-ajax.php","nonce":"404c184664","mode":"consent","engineEnabled":true,"site":"formation","syncGroup":"formation","cookieName":"cxt_consent_formation","cookieDomain":"","legacyCookieNames":["cxt_consent_formation","cxt_consent"],"lifetimeDays":180,"policyRevision":1,"configVersion":22,"showFloating":true,"floatingPosition":{"x":1,"y":92.5,"model":"viewport-track-v2"},"privacyUrl":"","text":{"title":"Vos choix de confidentialit\u00e9","description":"Nous utilisons uniquement les traceurs n\u00e9cessaires au fonctionnement du site tant que vous n\u2019autorisez pas de services optionnels.","necessaryTitle":"N\u00e9cessaires","necessaryDescription":"Connexion, panier, s\u00e9curit\u00e9 et fonctions indispensables demand\u00e9es par l\u2019utilisateur.","preferencesTitle":"Pr\u00e9f\u00e9rences","preferencesDescription":"Fonctions de confort ou contenus externes qui ne sont pas indispensables au service demand\u00e9.","statisticsTitle":"Statistiques","statisticsDescription":"Mesure d\u2019audience et compr\u00e9hension de l\u2019utilisation du site lorsque ces outils n\u00e9cessitent un consentement.","marketingTitle":"Marketing","marketingDescription":"Publicit\u00e9, reciblage, pixels sociaux et suivi marketing."},"services":[{"key":"stripe","name":"Stripe","category":"necessary","purpose":"Paiement et s\u00e9curisation du parcours de paiement."},{"key":"youtube","name":"YouTube","category":"preferences","purpose":"Affichage de vid\u00e9os h\u00e9berg\u00e9es par un service tiers."},{"key":"cxt-cloudfront-assets","name":"Ressources m\u00e9dia CXT","category":"necessary","purpose":"Images et ressources statiques utilis\u00e9es par les pages Carr\u00e9 X Triangle."},{"key":"cookie-cxt_consent_choice","name":"Choix de confidentialit\u00e9 CXT","category":"necessary","purpose":"M\u00e9morise les choix de confidentialit\u00e9 afin de ne pas redemander le consentement \u00e0 chaque page."},{"key":"cookie-cxt_auth_global","name":"Connexion CXT multi-sous-domaines","category":"necessary","purpose":"Maintient la session WordPress sign\u00e9e entre les univers Carr\u00e9 X Triangle apr\u00e8s connexion."},{"key":"cookie-cxt_ref","name":"Attribution d\u2019affiliation","category":"marketing","purpose":"Optionnel : m\u00e9morise le Click-ID opaque d\u2019une recommandation pour un retour diff\u00e9r\u00e9. L\u2019achat du parcours courant reste attribuable c\u00f4t\u00e9 serveur m\u00eame si Marketing est refus\u00e9."},{"key":"cookie-cxt_assistant_session","name":"Session temporaire CXT Assistant","category":"necessary","purpose":"Conserve pendant 24 h la continuit\u00e9 de la conversation apr\u00e8s que le visiteur a choisi d\u2019utiliser CXT Assistant."},{"key":"cookie-cxt_cart_global","name":"Panier global Carr\u00e9 X Triangle","category":"necessary","purpose":"Permet de conserver le m\u00eame panier entre les pages et sous-domaines Carr\u00e9 X Triangle."},{"key":"cookie-cxt_cart","name":"Ancien panier CXT (migration)","category":"necessary","purpose":"Ancien identifiant de panier migr\u00e9 automatiquement vers le panier global."}],"choice":{"id":"","version":0,"necessary":true,"preferences":false,"statistics":false,"marketing":false,"saved":false}};</script><script id="cxt-consent-front-bridge-js" data-cookieconsent="ignore" data-cxt-consent-runtime="frontend">(function(){
  'use strict';
  const cfg = window.CXTConsentConfig || null;
  const doc = document;
  const PANEL_EVENT = 'cxt:utility-panel:open';
  const CLOSE_ALL_EVENT = 'cxt:utility-panel:close-all';
  const mobilePanelMode = () => window.matchMedia ? window.matchMedia('(max-width: 700px), (pointer: coarse) and (max-height: 700px)').matches : window.innerWidth <= 700;

  function consentFabDocked(){
    const btn=doc.getElementById('cxt-consent-fab');
    return !!(btn && (btn.dataset.cxtNavigationDocked==='1' || btn.classList.contains('is-cxt-navigation-docked') || btn.closest('[data-cxt-navigation-slot="consent"]')));
  }

  function sharedUtilityPlacement(panelWidth,panelHeight){
    const api=window.CXTNavigationDock;
    if(api&&typeof api.getUtilityPanelPlacement==='function'){
      try{return api.getUtilityPanelPlacement(panelWidth,panelHeight);}catch(e){}
    }
    const viewport=window.visualViewport;
    const vw=Math.max(1,viewport?viewport.width:window.innerWidth);
    const vh=Math.max(1,viewport?viewport.height:window.innerHeight);
    const ox=Math.max(0,viewport?viewport.offsetLeft:0);
    const oy=Math.max(0,viewport?viewport.offsetTop:0);
    const safe=12;
    const dock=doc.getElementById('cxt-navigation-dock');
    const dockRect=dock?dock.getBoundingClientRect():{right:ox+64};
    const width=Math.max(1,Math.min(Number(panelWidth)||320,vw-safe*2));
    const height=Math.max(1,Math.min(Number(panelHeight)||440,vh-safe*2));
    const minLeft=ox+safe,maxLeft=Math.max(minLeft,ox+vw-width-safe);
    const left=Math.max(minLeft,Math.min(dockRect.right+8,maxLeft));
    const minTop=oy+safe,maxTop=Math.max(minTop,oy+vh-height-safe);
    const top=Math.max(minTop,Math.min(oy+(vh-height)/2,maxTop));
    return {left:left,top:top,width:width,height:height};
  }

  function placeOpenModal(){
    const modal=doc.getElementById('cxt-consent-modal');
    if(!modal||modal.hidden) return;
    if(!mobilePanelMode()||!consentFabDocked()){
      modal.classList.remove('is-cxt-navigation-utility-panel');
      modal.style.removeProperty('--cxt-consent-panel-left');
      modal.style.removeProperty('--cxt-consent-panel-top');
      return;
    }
    const dialog=modal.querySelector('.cxt-consent-dialog');
    if(!dialog) return;
    const rect=dialog.getBoundingClientRect();
    const placement=sharedUtilityPlacement(rect.width||320,rect.height||440);
    modal.classList.add('is-cxt-navigation-utility-panel');
    modal.style.setProperty('--cxt-consent-panel-left',Math.round(placement.left)+'px');
    modal.style.setProperty('--cxt-consent-panel-top',Math.round(placement.top)+'px');
  }

  function decodeOriginal(encoded){
    try {
      const base64 = String(encoded || '').replace(/-/g,'+').replace(/_/g,'/');
      const padded = base64 + '='.repeat((4 - base64.length % 4) % 4);
      return decodeURIComponent(Array.prototype.map.call(atob(padded), function(c){ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join(''));
    } catch(e) { return ''; }
  }

  function replaceWithOriginal(node){
    const original = decodeOriginal(node.getAttribute('data-cxt-original'));
    if(!original || !/^\s*<(iframe|script)\b/i.test(original)) return false;
    const tpl = doc.createElement('template');
    tpl.innerHTML = original.trim();
    const restored = tpl.content.firstElementChild;
    if(!restored) return false;

    // Un <script> créé via template/innerHTML n'est pas garanti d'être exécuté.
    // On recrée donc explicitement le script pour restaurer son comportement.
    if(restored.tagName === 'SCRIPT'){
      const activeScript = doc.createElement('script');
      Array.from(restored.attributes || []).forEach(function(attr){
        activeScript.setAttribute(attr.name, attr.value);
      });
      activeScript.text = restored.textContent || '';
      node.replaceWith(activeScript);
      return true;
    }

    node.replaceWith(restored);
    return true;
  }

  function restoreBlockedMarkup(){
    doc.querySelectorAll('[data-cxt-original]').forEach(function(node){ replaceWithOriginal(node); });
  }

  // STOP/Audit reste un vrai bypass, y compris si un document tunnel mis en cache
  // contient encore un ancien runtime CXT Consent.
  if (!cfg || cfg.engineEnabled === false || cfg.mode === 'audit') {
    if(doc.readyState === 'loading') doc.addEventListener('DOMContentLoaded', restoreBlockedMarkup); else restoreBlockedMarkup();
    return;
  }

  const optional = ['preferences','statistics','marketing'];
  const emptyChoice = {id:'',version:0,necessary:true,preferences:false,statistics:false,marketing:false,saved:false};
  let current = Object.assign({}, emptyChoice);
  let saveInProgress = false;
  let currentSignature = '';
  let modalOpenSequence = 0;

  const uuid = () => {
    if (window.crypto && crypto.randomUUID) return crypto.randomUUID();
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c){
      const r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8); return v.toString(16);
    });
  };

  function policyRevision(){
    return Math.max(1, Number(cfg.policyRevision || 1));
  }

  function cookieDomain(){
    const d = String(cfg.cookieDomain || '').trim();
    return d ? '; Domain=' + d : '';
  }

  function normalizeChoice(parsed, strictRevision){
    if(!parsed || typeof parsed !== 'object') return null;
    const rawVersion = Number(parsed.version || 0);
    if(strictRevision && rawVersion !== policyRevision()) return null;
    return {
      id:String(parsed.id||''),
      version:policyRevision(),
      necessary:true,
      preferences:!!parsed.preferences,
      statistics:!!parsed.statistics,
      marketing:!!parsed.marketing,
      saved:true,
      _legacyVersion:rawVersion
    };
  }

  function readCookieByName(cookieName, strictRevision){
    const cleanName = String(cookieName || '').trim();
    if(!cleanName) return Object.assign({}, emptyChoice);
    const prefix = cleanName + '=';
    const parts = String(doc.cookie || '').split(';');
    let best = null;

    for(let i=0;i<parts.length;i++){
      const part = parts[i].trim();
      if(part.indexOf(prefix)!==0) continue;
      try {
        const parsed = JSON.parse(decodeURIComponent(part.slice(prefix.length)));
        const normalized = normalizeChoice(parsed, !!strictRevision);
        if(!normalized) continue;
        // En migration, si plusieurs anciens cookies du même nom coexistent,
        // on préfère celui dont la version technique était la plus élevée.
        if(!best || Number(normalized._legacyVersion||0) >= Number(best._legacyVersion||0)) best = normalized;
      } catch(e){}
    }

    if(!best) return Object.assign({}, emptyChoice);
    delete best._legacyVersion;
    return best;
  }

  function writeCookie(choice){
    const maxAge = Math.max(1, Number(cfg.lifetimeDays || 180)) * 86400;
    const normalized = Object.assign({}, emptyChoice, choice, {
      version:policyRevision(),
      necessary:true,
      saved:true
    });
    const value = encodeURIComponent(JSON.stringify(normalized));
    doc.cookie = String(cfg.cookieName || 'cxt_consent_main') + '=' + value
      + '; Path=/; Max-Age=' + maxAge + '; SameSite=Lax'
      + cookieDomain()
      + (location.protocol === 'https:' ? '; Secure' : '');
    return normalized;
  }

  function readBrowserChoice(){
    const canonical = readCookieByName(cfg.cookieName || '', true);
    if(canonical.saved) return canonical;

    // Migration unique depuis les cookies v0.1.10-v0.1.13. Le nouveau cookie
    // canonique a un nom différent : aucune collision host-only/domaine parent.
    const legacyNames = Array.isArray(cfg.legacyCookieNames) ? cfg.legacyCookieNames : [];
    for(let i=0;i<legacyNames.length;i++){
      const legacyName = String(legacyNames[i] || '').trim();
      if(!legacyName || legacyName === String(cfg.cookieName || '')) continue;
      const legacy = readCookieByName(legacyName, false);
      if(legacy.saved) return writeCookie(legacy);
    }

    return Object.assign({}, emptyChoice);
  }

  function signature(choice){
    const c = choice || emptyChoice;
    return [String(c.id||''),c.saved?'1':'0',c.preferences?'1':'0',c.statistics?'1':'0',c.marketing?'1':'0'].join('|');
  }

  function restoreAllowedMarkup(){
    if(!current.saved) return;
    doc.querySelectorAll('[data-cxt-original][data-cxt-category]').forEach(function(node){
      const category = String(node.getAttribute('data-cxt-category') || '');
      if(optional.includes(category) && current[category]) replaceWithOriginal(node);
    });
  }

  function saveChoice(choice, eventType, reload){
    if(saveInProgress) return;
    saveInProgress = true;

    const previous = Object.assign({}, current);
    choice.id = choice.id || current.id || uuid();
    choice.version = policyRevision();
    choice.necessary = true;
    choice.preferences = !!choice.preferences;
    choice.statistics = !!choice.statistics;
    choice.marketing = !!choice.marketing;

    // Le choix navigateur est écrit AVANT l'AJAX et reste la source de vérité.
    // Si un tunnel contient un nonce périmé dans un cache, l'échec du journal AJAX
    // ne doit jamais annuler le choix visuel du visiteur.
    current = writeCookie(choice);
    currentSignature = signature(current);

    // Un rechargement n'est nécessaire que lorsqu'une catégorie déjà active est
    // retirée : un script tiers déjà exécuté ne peut pas être "dés-exécuté".
    // Premier choix / refus / activation de catégories : application immédiate
    // sans recharger le tunnel, ce qui évite de retomber sur un vieux rendu caché.
    const revoked = optional.some(function(key){ return !!previous.saved && !!previous[key] && !current[key]; });
    const mustReload = !!reload && revoked;

    const body = new URLSearchParams({
      action:'cxt_consent_save', nonce:String(cfg.nonce || ''), consent_id:current.id,
      preferences:current.preferences?'1':'', statistics:current.statistics?'1':'', marketing:current.marketing?'1':'', event_type:eventType||'updated'
    });

    fetch(String(cfg.ajaxUrl || '/wp-admin/admin-ajax.php'),{
      method:'POST',
      credentials:'same-origin',
      headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},
      body:body.toString()
    })
      .then(function(response){ return response.json().catch(function(){ return null; }); })
      .then(function(payload){
        const serverChoice = payload && payload.success && payload.data && payload.data.choice ? payload.data.choice : null;
        if(serverChoice){
          current = writeCookie(Object.assign({}, current, serverChoice));
          currentSignature = signature(current);
        }
      })
      .catch(function(){ /* Le cookie local reste valide même si le journal échoue. */ })
      .finally(function(){
        if(mustReload){
          window.setTimeout(function(){ location.reload(); }, 80);
          return;
        }

        saveInProgress = false;
        const banner = doc.getElementById('cxt-consent-banner');
        if(banner) banner.remove();
        const modal = doc.getElementById('cxt-consent-modal');
        if(modal) modal.hidden = true;
        doc.documentElement.classList.remove('cxt-consent-open');
        restoreAllowedMarkup();
        renderFloating(current);
      });
  }

  function categoryRow(key, title, desc, fixed){
    const row = doc.createElement('label'); row.className='cxtc-front-row';
    row.innerHTML = '<span class="cxtc-front-copy"><strong></strong><small></small></span><span class="cxtc-switch"><input type="checkbox"><i></i></span>';
    row.querySelector('strong').textContent = title; row.querySelector('small').textContent = desc;
    const input = row.querySelector('input'); input.dataset.category=key; input.checked = fixed || !!current[key]; input.disabled=!!fixed || cfg.mode==='necessary';
    return row;
  }

  function updateStatus(root, choice){
    const active=optional.filter(k=>!!choice[k]);
    const dot=root.querySelector('.cxtc-status-dot'), text=root.querySelector('.cxtc-status-text');
    if(!dot||!text)return;
    dot.className='cxtc-status-dot '+(active.length?'is-red':'is-green');
    text.textContent=active.length ? ('Traceurs optionnels autorisés : '+active.length+' catégorie(s)') : 'Aucun traceur optionnel autorisé';
  }

  function syncModalInputs(modal){
    optional.forEach(function(key){
      const input = modal.querySelector('input[data-category="'+key+'"]');
      if(input) input.checked = !!current[key];
    });
    updateStatus(modal,current);
  }

  function closeModal(){
    modalOpenSequence += 1;
    const modal=doc.getElementById('cxt-consent-modal');
    if(modal){
      modal.hidden=true;
      modal.classList.remove('is-cxt-navigation-utility-panel');
      modal.style.removeProperty('--cxt-consent-panel-left');
      modal.style.removeProperty('--cxt-consent-panel-top');
    }
    doc.documentElement.classList.remove('cxt-consent-open');
  }

  function announceOpen(){
    if(!mobilePanelMode()) return;
    try { window.dispatchEvent(new CustomEvent(PANEL_EVENT,{detail:{tool:'consent'}})); } catch(e) {}
  }

  function rejectAll(){
    saveChoice({id:current.id,preferences:false,statistics:false,marketing:false},'rejected',true);
  }

  function buildModal(){
    let modal = doc.getElementById('cxt-consent-modal');
    if(modal){ syncModalInputs(modal); return modal; }
    modal = doc.createElement('div'); modal.id='cxt-consent-modal'; modal.className='cxt-consent-modal'; modal.hidden=true;
    modal.innerHTML='<div class="cxt-consent-backdrop" data-close="1"></div><section class="cxt-consent-dialog" role="dialog" aria-modal="true" aria-labelledby="cxtc-title"><div class="cxtc-scroll"><button class="cxtc-close" type="button" aria-label="Fermer">×</button><div class="cxtc-status-line"><span class="cxtc-status-dot"></span><span class="cxtc-status-text"></span></div><h2 id="cxtc-title"></h2><p class="cxtc-intro"></p><div class="cxtc-categories"></div><div class="cxtc-services"></div><a class="cxtc-privacy" target="_self" rel="nofollow">Politique de confidentialité et cookies</a></div><div class="cxtc-actions"><button type="button" class="cxtc-reject">Tout refuser</button><button type="button" class="cxtc-save">Enregistrer mes choix</button><button type="button" class="cxtc-accept">Tout accepter</button></div></section>';
    modal.querySelector('#cxtc-title').textContent=cfg.text.title;
    modal.querySelector('.cxtc-intro').textContent=cfg.text.description;
    const cats=modal.querySelector('.cxtc-categories');
    cats.appendChild(categoryRow('necessary',cfg.text.necessaryTitle,cfg.text.necessaryDescription,true));
    cats.appendChild(categoryRow('preferences',cfg.text.preferencesTitle,cfg.text.preferencesDescription,false));
    cats.appendChild(categoryRow('statistics',cfg.text.statisticsTitle,cfg.text.statisticsDescription,false));
    cats.appendChild(categoryRow('marketing',cfg.text.marketingTitle,cfg.text.marketingDescription,false));
    const services=modal.querySelector('.cxtc-services');
    const list=Array.isArray(cfg.services)?cfg.services:[];
    if(list.length){
      const title=doc.createElement('h3'); title.textContent='Services identifiés'; services.appendChild(title);

      const serviceGroups={necessary:[],preferences:[],statistics:[],marketing:[],other:[]};
      list.forEach(function(service){
        const category=String(service&&service.category||'').toLowerCase();
        const key=Object.prototype.hasOwnProperty.call(serviceGroups,category)?category:'other';
        serviceGroups[key].push(service);
      });

      const serviceLabels={
        necessary:(cfg.text&&cfg.text.necessaryTitle)||'Nécessaires',
        preferences:(cfg.text&&cfg.text.preferencesTitle)||'Préférences',
        statistics:(cfg.text&&cfg.text.statisticsTitle)||'Statistiques',
        marketing:(cfg.text&&cfg.text.marketingTitle)||'Marketing',
        other:'Autres'
      };

      ['necessary','preferences','statistics','marketing','other'].forEach(function(category){
        const items=serviceGroups[category];
        if(!items.length) return;

        const group=doc.createElement('section');
        group.className='cxtc-service-group';
        group.dataset.category=category;

        const toggle=doc.createElement('button');
        toggle.type='button';
        toggle.className='cxtc-service-toggle';
        toggle.setAttribute('aria-expanded','false');
        toggle.innerHTML='<span class="cxtc-service-title"></span><span class="cxtc-service-count"></span><span class="cxtc-service-chevron" aria-hidden="true">⌄</span>';
        toggle.querySelector('.cxtc-service-title').textContent=serviceLabels[category];
        toggle.querySelector('.cxtc-service-count').textContent=String(items.length);

        const body=doc.createElement('div');
        body.className='cxtc-service-list';
        body.hidden=true;
        items.forEach(function(service){
          const row=doc.createElement('p');
          row.innerHTML='<strong></strong><span></span>';
          row.querySelector('strong').textContent=String(service&&service.name||'Service');
          row.querySelector('span').textContent=String(service&&service.purpose||'');
          body.appendChild(row);
        });

        toggle.addEventListener('click',function(){
          const willOpen=toggle.getAttribute('aria-expanded')!=='true';
          services.querySelectorAll('.cxtc-service-toggle[aria-expanded="true"]').forEach(function(openToggle){
            if(openToggle===toggle) return;
            openToggle.setAttribute('aria-expanded','false');
            const openGroup=openToggle.closest('.cxtc-service-group');
            const openBody=openGroup?openGroup.querySelector('.cxtc-service-list'):null;
            if(openBody) openBody.hidden=true;
          });
          toggle.setAttribute('aria-expanded',willOpen?'true':'false');
          body.hidden=!willOpen;
        });

        group.appendChild(toggle);
        group.appendChild(body);
        services.appendChild(group);
      });
    }
    const privacy=modal.querySelector('.cxtc-privacy'); if(cfg.privacyUrl){ privacy.href=cfg.privacyUrl; } else { privacy.hidden=true; }
    if(cfg.mode==='necessary'){
      // v0.1.25 : même en mode « Nécessaires uniquement », le visiteur doit
      // toujours disposer d'une action explicite « Tout refuser » sur PC et mobile.
      // Le bouton confirme l'absence de catégories optionnelles et ferme le panneau.
      modal.querySelector('.cxtc-reject').hidden=false;
      modal.querySelector('.cxtc-accept').hidden=true;
      modal.querySelector('.cxtc-save').textContent='Fermer';
    }
    const close=closeModal;
    modal.querySelector('.cxtc-close').addEventListener('click',close);
    modal.querySelector('.cxt-consent-backdrop').addEventListener('click',close);
    modal.querySelector('.cxtc-reject').addEventListener('click',rejectAll);
    modal.querySelector('.cxtc-accept').addEventListener('click',function(){ saveChoice({id:current.id,preferences:true,statistics:true,marketing:true},'accepted',true); });
    modal.querySelector('.cxtc-save').addEventListener('click',function(){
      if(cfg.mode==='necessary'){ close(); return; }
      const choice={id:current.id};
      optional.forEach(function(k){ choice[k]=!!modal.querySelector('input[data-category="'+k+'"]').checked; });
      saveChoice(choice,'customized',true);
    });
    doc.body.appendChild(modal);
    syncModalInputs(modal);
    return modal;
  }

  function openModal(){
    announceOpen();
    const sequence=++modalOpenSequence;
    const m=buildModal();
    syncModalInputs(m);
    m.hidden=false;
    doc.documentElement.classList.add('cxt-consent-open');
    // 0.1.29 — le panneau mobile docké reprend le rectangle commun Live/Pulse.
    placeOpenModal();
    window.requestAnimationFrame(function(){
      if(sequence!==modalOpenSequence||m.hidden) return;
      placeOpenModal();
    });
    // 0.1.30 — un clic très rapide vers Live/Pulse ne doit pas laisser un ancien
    // timeout Consent reprendre le focus après la fermeture du panneau.
    setTimeout(function(){
      if(sequence!==modalOpenSequence||m.hidden) return;
      const closeButton=m.querySelector('.cxtc-close');
      if(closeButton) closeButton.focus({preventScroll:true});
    },20);
  }

  // API publique CXT Consent : ouvre/ferme le centre de préférences depuis une page CXT.
  window.CXTConsent = window.CXTConsent || {};
  window.CXTConsent.openPreferences = openModal;
  window.CXTConsent.open = openModal;
  window.CXTConsent.close = closeModal;
  window.CXTConsent.rejectAll = rejectAll;
  window.addEventListener('cxt-consent:open', openModal);
  window.addEventListener(PANEL_EVENT,function(event){
    const tool=event&&event.detail?String(event.detail.tool||''):'';
    if(mobilePanelMode()&&tool&&tool!=='consent') closeModal();
  });
  window.addEventListener(CLOSE_ALL_EVENT,closeModal);
  window.addEventListener('resize',placeOpenModal,{passive:true});
  window.addEventListener('orientationchange',function(){ window.requestAnimationFrame(placeOpenModal); },{passive:true});
  if(window.visualViewport){
    window.visualViewport.addEventListener('resize',placeOpenModal,{passive:true});
    window.visualViewport.addEventListener('scroll',placeOpenModal,{passive:true});
  }
  window.addEventListener('cxt-navigation:docked',function(event){
    if(!event.detail||event.detail.tool==='consent') window.requestAnimationFrame(placeOpenModal);
  });

  function renderBanner(){
    if(current.saved) return renderFloating(current);
    if(doc.getElementById('cxt-consent-banner')) return;
    const banner=doc.createElement('section'); banner.id='cxt-consent-banner'; banner.className='cxt-consent-banner '+(cfg.mode==='necessary'?'is-necessary':'is-consent');
    if(cfg.mode==='necessary'){
      // v0.1.25 : dans ce mode, « Tout refuser » remplace le vague « Compris ».
      // « Paramètres » ouvre le centre de transparence, comme en consentement complet.
      banner.innerHTML='<div class="cxtc-banner-status"><span></span><strong>Aucun traceur optionnel actif</strong></div><p></p><div class="cxtc-banner-actions"><button type="button" class="cxtc-reject">Tout refuser</button><button type="button" class="cxtc-details">Paramètres</button></div>';
      banner.querySelector('p').textContent='Les traceurs strictement nécessaires au compte, au panier, à la sécurité et au service demandé restent actifs. CXT Consent n’autorise aucun suivi optionnel.';
      banner.querySelector('.cxtc-reject').addEventListener('click',rejectAll);
      banner.querySelector('.cxtc-details').addEventListener('click',openModal);
    } else {
      banner.innerHTML='<div class="cxtc-banner-status"><span></span><strong>Vos choix de confidentialité</strong></div><p></p><div class="cxtc-banner-actions"><button type="button" class="cxtc-reject">Tout refuser</button><button type="button" class="cxtc-details">Paramètres</button><button type="button" class="cxtc-accept">Tout accepter</button></div>';
      banner.querySelector('p').textContent=cfg.text.description;
      banner.querySelector('.cxtc-reject').addEventListener('click',rejectAll);
      banner.querySelector('.cxtc-details').addEventListener('click',openModal);
      banner.querySelector('.cxtc-accept').addEventListener('click',function(){ saveChoice({id:current.id,preferences:true,statistics:true,marketing:true},'accepted',true); });
    }
    doc.body.appendChild(banner);
  }

  function renderFloating(choice){
    if(!cfg.showFloating) return;
    const active=optional.filter(k=>!!choice[k]);
    let btn=doc.getElementById('cxt-consent-fab');
    const pos=(cfg.floatingPosition&&typeof cfg.floatingPosition==='object')?cfg.floatingPosition:{x:0,y:100,model:'viewport-track-v2'};
    const rawX=Number(pos.x),rawY=Number(pos.y);
    // v0.1.16 : position libre. Les valeurs hors 0–100 % sont
    // intentionnelles et ne doivent plus être ramenées dans le viewport.
    const x=Number.isFinite(rawX)?rawX:0, y=Number.isFinite(rawY)?rawY:100;
    const model=String(pos.model||'legacy-center-v1');

    const navigationSlot=function(){ return btn ? btn.closest('[data-cxt-navigation-slot="consent"]') : null; };
    const isDocked=function(){ return !!navigationSlot() || (btn && btn.dataset.cxtNavigationDocked==='1'); };
    const mountInNavigation=function(){
      if(!btn || !window.CXTNavigationDock || typeof window.CXTNavigationDock.mount!=='function') return false;
      return !!window.CXTNavigationDock.mount('consent',btn);
    };
    const place=function(){
      if(!btn) return;
      if(isDocked()){
        btn.dataset.cxtNavigationDocked='1';
        btn.classList.add('is-cxt-navigation-docked');
        btn.style.left='auto';btn.style.top='auto';btn.style.right='auto';btn.style.bottom='auto';btn.style.transform='none';
        return;
      }
      delete btn.dataset.cxtNavigationDocked;
      btn.classList.remove('is-cxt-navigation-docked');
      if(model==='viewport-track-v2'){
        const viewport=window.visualViewport;
        const vw=Math.max(0,viewport?viewport.width:window.innerWidth);
        const vh=Math.max(0,viewport?viewport.height:window.innerHeight);
        const r=btn.getBoundingClientRect();
        const maxX=Math.max(0,vw-r.width),maxY=Math.max(0,vh-r.height);
        btn.style.left=((maxX*x)/100)+'px';
        btn.style.top=((maxY*y)/100)+'px';
        btn.style.right='auto';btn.style.bottom='auto';btn.style.transform='none';
      }else{
        // Compatibilité avec une configuration injectée par une ancienne page mise en cache.
        btn.style.left='clamp(28px,'+x+'vw,calc(100vw - 28px))';
        btn.style.top='clamp(28px,'+y+'vh,calc(100vh - 28px))';
        btn.style.right='auto';btn.style.bottom='auto';btn.style.transform='translate(-50%,-50%)';
      }
    };

    if(btn){
      btn.className='cxt-consent-fab '+(active.length?'is-red':'is-green');
      btn.title=active.length?'Traceurs optionnels autorisés':'Aucun traceur optionnel autorisé';
      mountInNavigation();
      place();
      return;
    }

    btn=doc.createElement('button'); btn.id='cxt-consent-fab'; btn.className='cxt-consent-fab '+(active.length?'is-red':'is-green'); btn.type='button'; btn.setAttribute('aria-label','Gérer mes choix de confidentialité'); btn.title=active.length?'Traceurs optionnels autorisés':'Aucun traceur optionnel autorisé';
    btn.innerHTML='<span class="cxtc-fab-dot"></span><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3l7 3v5c0 4.8-2.8 8.1-7 10-4.2-1.9-7-5.2-7-10V6l7-3z"></path><path d="M9 12l2 2 4-4"></path></svg>';
    btn.addEventListener('click',openModal);
    doc.body.appendChild(btn);
    mountInNavigation();
    place();

    window.addEventListener('resize',place,{passive:true});
    if(window.visualViewport) window.visualViewport.addEventListener('resize',place,{passive:true});
    window.addEventListener('cxt-navigation:docked',function(event){
      if(!event.detail || event.detail.tool==='consent') place();
    });
    try { window.dispatchEvent(new CustomEvent('cxt-consent:ready',{detail:{element:btn}})); } catch(e) {}
  }

  function checkSharedChoiceOnReturn(){
    if(saveInProgress || String(cfg.syncGroup || '') !== 'main') return;
    const browser = readCookieByName(cfg.cookieName || '', true);
    if(!browser.saved) return;
    const next = signature(browser);
    if(currentSignature && next !== currentSignature) location.reload();
  }

  doc.addEventListener('click',function(e){
    const b=e.target.closest('.cxt-consent-allow-embed'); if(!b)return;
    const category=b.dataset.category; if(!optional.includes(category))return;
    const choice={id:current.id,preferences:!!current.preferences,statistics:!!current.statistics,marketing:!!current.marketing};
    choice[category]=true;
    saveChoice(choice,'customized',true);
  });

  function boot(){
    // Si un ancien runtime a déjà eu le temps de créer une interface (HTML tunnel
    // historique), la version courante reprend la main et retire cette UI avant
    // de relire le cookie canonique. Cela rend l'initialisation idempotente.
    ['cxt-consent-banner','cxt-consent-modal','cxt-consent-fab'].forEach(function(id){
      const oldNode = doc.getElementById(id);
      if(oldNode) oldNode.remove();
    });
    doc.documentElement.classList.remove('cxt-consent-open');

    // Source de vérité n°1 : cookie navigateur canonique. Cela rend l'interface
    // indépendante d'un ancien cfg.choice injecté dans une page tunnel mise en cache.
    const browserChoice = readBrowserChoice();
    if(browserChoice.saved){
      current = browserChoice;
    } else {
      // Aucun état serveur injecté n'est utilisé pour décider de l'interface.
      // Les pages tunnel peuvent contenir un HTML mis en cache ; document.cookie
      // est la seule source de vérité côté navigateur pour l'affichage du choix.
      current = Object.assign({}, emptyChoice, {version:policyRevision()});
    }

    currentSignature = signature(current);
    restoreAllowedMarkup();
    renderBanner();

    // Synchronisation fiable sans polling permanent : le cookie partagé est relu
    // lorsque l'utilisateur revient sur un autre onglet/sous-domaine CXT.
    window.addEventListener('focus', checkSharedChoiceOnReturn, {passive:true});
    doc.addEventListener('visibilitychange', function(){ if(!doc.hidden) checkSharedChoiceOnReturn(); }, {passive:true});
  }

  if(doc.readyState==='loading') doc.addEventListener('DOMContentLoaded',boot); else boot();
})();
</script>