PageSourceSearch

https://www.83-629.fr/reglementation/api/quiz-widget.js

js 83-629.fr collected 2026-10-02 11:58:59 UTC 50,739 bytes, 592 lines download raw bytes

1(function(){
2  if (window.__bq83629WidgetLoaded) return;
3  window.__bq83629WidgetLoaded = true;
4
5  var placeholder = document.getElementById('mini-qcm-widget');
6  if (!placeholder) return;
7  var postIdent = placeholder.getAttribute('data-post-slug') || placeholder.getAttribute('data-post-uuid');
8  if (!postIdent) return;
9
10  var API_BASE = 'https://www.83-629.fr';
11
12  // CSS injecté une seule fois
13  var CSS = "\n.mqcm-container{font-family:'Inter',system-ui,-apple-system,sans-serif;background:#FFFFFF;border:1px solid #D8D0BF;border-radius:18px;padding:32px 28px;margin:40px 0;color:#2C3A4E;line-height:1.55;box-shadow:0 20px 40px -24px rgba(30,58,138,.25),0 4px 8px -4px rgba(30,58,138,.08);position:relative;overflow:hidden;animation:mqcmFadeIn .5s ease-out}\n.mqcm-container::before{content:'';position:absolute;top:0;left:0;right:0;height:4px;background:linear-gradient(90deg,#1e3a8a 0%,#3b82f6 50%,#1e3a8a 100%)}\n.mqcm-intro{background:linear-gradient(135deg,#FFFFFF 0%,#F0F4FA 100%)}\n@keyframes mqcmFadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}\n.mqcm-icon-wrap{display:flex;justify-content:center;margin-bottom:14px}\n.mqcm-icon{display:inline-flex;align-items:center;justify-content:center;width:56px;height:56px;background:linear-gradient(135deg,#dbeafe,#FFFFFF);border:1px solid #dbeafe;border-radius:50%;font-size:1.8rem;box-shadow:0 8px 16px -8px rgba(30,58,138,.2)}\n.mqcm-header{margin-bottom:22px;text-align:center}\n.mqcm-eyebrow{font-family:'JetBrains Mono',monospace;font-size:.74rem;color:#1e3a8a;text-transform:uppercase;letter-spacing:.08em;font-weight:700;margin-bottom:10px}\n.mqcm-h{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:1.35rem;font-weight:700;color:#0E1A2B;margin:0 0 14px;line-height:1.25;letter-spacing:-.01em}\n.mqcm-pills{display:flex;justify-content:center;gap:8px;margin:0 0 14px;flex-wrap:wrap}\n.mqcm-pill{display:inline-flex;align-items:center;font-family:'JetBrains Mono',monospace;font-size:.72rem;color:#1e3a8a;background:#dbeafe;padding:5px 12px;border-radius:999px;font-weight:600;letter-spacing:.03em}\n.mqcm-pill-soft{background:#FAF8F2;color:#5A6A7D;border:1px solid #D8D0BF}\n.mqcm-sub{font-size:.92rem;color:#6B7A8F;margin:0 auto;text-align:center;max-width:520px}\n.mqcm-progress-bar{height:4px;background:#EFEAE0;border-radius:999px;overflow:hidden;margin-bottom:10px}\n.mqcm-progress-bar-fill{height:100%;background:linear-gradient(90deg,#1e3a8a,#3b82f6);border-radius:999px;transition:width .4s ease-out}\n.mqcm-progress{font-family:'JetBrains Mono',monospace;font-size:.78rem;color:#6B7A8F;margin-bottom:14px;letter-spacing:.06em}\n.mqcm-question{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:1.1rem;font-weight:600;color:#0E1A2B;margin-bottom:18px;line-height:1.4}\n.mqcm-options{display:flex;flex-direction:column;gap:9px;margin-bottom:18px}\n.mqcm-option{background:#FAF8F2;border:2px solid #D8D0BF;border-radius:8px;padding:12px 16px;cursor:pointer;font-size:.94rem;color:#2C3A4E;text-align:left;font-family:inherit;width:100%;transition:all .15s;line-height:1.4}\n.mqcm-option:hover:not(:disabled){border-color:#1e3a8a;background:#dbeafe}\n.mqcm-option.mqcm-selected{border-color:#1e3a8a;background:#dbeafe;color:#0f1e4f;font-weight:600;animation:mqcm-pulse .35s ease-out}\n.mqcm-option:disabled{cursor:default;opacity:.55}\n.mqcm-option.mqcm-selected:disabled{opacity:1}\n@keyframes mqcm-pulse{0%{transform:scale(1)}50%{transform:scale(1.015);box-shadow:0 0 0 4px rgba(30,58,138,.18)}100%{transform:scale(1);box-shadow:none}}\n.mqcm-nav{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:14px}\n.mqcm-nav-hint{font-size:.78rem;color:#6B7A8F;font-style:italic;flex:1;text-align:right}\n.mqcm-btn{padding:12px 22px;border:none;border-radius:10px;font-weight:600;cursor:pointer;font-size:.92rem;font-family:inherit;transition:all .2s;text-decoration:none;display:inline-flex;align-items:center;justify-content:center;gap:8px;text-align:center}\n.mqcm-btn-primary{background:linear-gradient(135deg,#1e3a8a,#0f1e4f);color:white;box-shadow:0 6px 14px -6px rgba(30,58,138,.6),inset 0 1px 0 rgba(255,255,255,.15)}\n.mqcm-btn-primary:hover{transform:translateY(-1px);box-shadow:0 10px 20px -8px rgba(30,58,138,.7),inset 0 1px 0 rgba(255,255,255,.18)}\n.mqcm-btn-primary:active{transform:translateY(0)}\n.mqcm-btn-secondary{background:#FFFFFF;color:#1e3a8a;border:1px solid #D8D0BF}\n.mqcm-btn-secondary:hover{border-color:#1e3a8a;background:#FAF8F2}\n.mqcm-btn-large{padding:14px 28px;font-size:1rem;display:flex;width:100%;max-width:340px;margin:0 auto}\n.mqcm-btn-arrow{font-size:1.1em;transition:transform .2s}\n.mqcm-btn-primary:hover .mqcm-btn-arrow{transform:translateX(3px)}\n.mqcm-cta-row{display:flex;flex-wrap:wrap;gap:10px;margin:18px 0;justify-content:center}\n.mqcm-signup-wall{background:linear-gradient(135deg,#EEF4FF 0%,#FFFFFF 100%);border:2px solid #1e3a8a;border-radius:12px;padding:18px 20px;margin:16px 0;text-align:center}\n.mqcm-signup-wall__title{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:1.02rem;font-weight:700;color:#1e3a8a;margin-bottom:8px}\n.mqcm-signup-wall__body{font-size:.9rem;line-height:1.55;color:#334155;margin-bottom:14px}\n.mqcm-signup-wall__body strong{color:#1e3a8a}\n.mqcm-signup-wall__foot{font-size:.76rem;color:#94a3b8;margin-top:10px;letter-spacing:.02em}\n.mqcm-btn-attest{background:linear-gradient(135deg,#16a34a 0%,#0f5132 100%);color:#fff;box-shadow:0 6px 14px -6px rgba(22,163,74,.5),inset 0 1px 0 rgba(255,255,255,.15)}\n.mqcm-btn-attest:hover{transform:translateY(-1px);box-shadow:0 10px 20px -8px rgba(22,163,74,.6)}\n.mqcm-btn-attest:disabled{opacity:.6;cursor:default}\n.mqcm-btn-attest.mqcm-vig-loading{cursor:progress}\n.mqcm-attest-hint{font-size:.84rem;color:#92400e;background:#fef3c7;border-left:3px solid #d97706;padding:8px 14px;border-radius:0 6px 6px 0;margin:6px 0;font-style:italic;text-align:left;width:100%}\n.mqcm-attest-result{background:linear-gradient(135deg,#D1FAE5 0%,#FFFFFF 100%);border:2px solid #16a34a;border-radius:12px;padding:18px 20px;margin:14px 0;text-align:center;animation:mqcmFadeIn .35s ease both}\n.mqcm-attest-level{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:1.05rem;color:#0f5132;margin-bottom:12px;font-weight:600}\n.mqcm-attest-level strong{color:#16a34a;text-transform:uppercase;letter-spacing:.03em}\n.mqcm-attest-note{font-size:.82rem;color:#6B7A8F;font-style:italic;margin-top:10px;line-height:1.45}\n.mqcm-share-prompt{margin-top:14px;padding-top:14px;border-top:1px dashed #16a34a}\n.mqcm-share-title{font-family:'Bricolage Grotesque','Inter',sans-serif;font-weight:700;color:#0f5132;font-size:.95rem;margin-bottom:10px}\n.mqcm-share-row{display:flex;flex-wrap:wrap;gap:8px;justify-content:center}\n.mqcm-share-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:9999px;font-family:'Inter',sans-serif;font-weight:600;font-size:.82rem;text-decoration:none;border:1.5px solid transparent;cursor:pointer;color:#fff;transition:transform .15s}\n.mqcm-share-btn:hover{transform:translateY(-1px)}\n.mqcm-share-li{background:#0A66C2}\n.mqcm-share-wa{background:#25D366;color:#063}\n.mqcm-share-dl{background:#fff;color:#1e3a8a;border-color:rgba(30,58,138,.35)}\n.mqcm-vigilance-panel{background:linear-gradient(135deg,#F0F9F4 0%,#FFFFFF 100%);border:1px solid #D8D0BF;border-radius:12px;padding:18px 20px;margin-top:18px}\n.mqcm-vigilance-loading{text-align:center;color:#6B7A8F;font-family:'JetBrains Mono',monospace;font-size:.85rem;padding:12px}\n.mqcm-vig-head{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:.95rem;color:#0E1A2B;margin-bottom:14px;padding-bottom:10px;border-bottom:1px dotted #D8D0BF}\n.mqcm-vig-head strong{color:#16a34a}\n.mqcm-vig-row{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid #EFEAE0}\n.mqcm-vig-row:last-child{border-bottom:none}\n.mqcm-vig-ic{font-size:1.6rem;flex-shrink:0;width:32px;text-align:center}\n.mqcm-vig-body{flex:1;min-width:0}\n.mqcm-vig-label{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:.92rem;color:#0E1A2B;margin-bottom:5px;font-weight:600}\n.mqcm-vig-count{font-family:'JetBrains Mono',monospace;font-size:.78rem;color:#6B7A8F;font-weight:500;margin-left:6px}\n.mqcm-vig-bar{height:5px;background:#EFEAE0;border-radius:999px;overflow:hidden}\n.mqcm-vig-bar-fill{height:100%;background:linear-gradient(90deg,#1e3a8a,#3b82f6);border-radius:999px;transition:width .4s ease-out}\n.mqcm-vig-row--ok .mqcm-vig-bar-fill{background:linear-gradient(90deg,#16a34a,#22c55e)}\n.mqcm-vig-row--ok .mqcm-vig-label{color:#0f5132}\n.mqcm-vig-row--ok .mqcm-vig-count{color:#16a34a;font-weight:700}\n.mqcm-vig-row--todo{opacity:.55}\n.mqcm-vig-remaining{font-family:'JetBrains Mono',monospace;font-size:.74rem;color:#6B7A8F;flex-shrink:0;white-space:nowrap}\n.mqcm-vig-btn{padding:7px 14px;font-size:.82rem;flex-shrink:0;white-space:nowrap;min-height:0}\n.mqcm-vig-next{margin-top:14px;padding:12px 14px;background:#FEF9E7;border:1px solid #F6E05E;border-radius:8px;font-size:.88rem;line-height:1.5;color:#5C4408}\n.mqcm-vig-next strong{color:#3D2C04}\n.mqcm-vig-next--max{background:#D1FAE5;border-color:#16a34a;color:#0f5132}\n.mqcm-vig-next--max strong{color:#15803d}\n.mqcm-vig-foot{margin-top:12px;text-align:center;font-size:.85rem}\n.mqcm-vig-foot a{color:#1e3a8a;font-weight:600;text-decoration:none;border-bottom:1px dotted #1e3a8a;transition:color .15s}\n.mqcm-vig-foot a:hover{color:#0f1e4f;border-bottom-style:solid}\n.mqcm-cta{margin:0 4px}\n.mqcm-meta{font-size:.84rem;color:#6B7A8F;text-align:center;margin-top:14px}\n.mqcm-link{color:#1e3a8a;text-decoration:none;font-weight:600;border-bottom:1px solid #1e3a8a}\n.mqcm-link:hover{color:#0f1e4f}\n.mqcm-disclaimer{font-size:.7rem;color:#9ca3af;text-align:center;font-style:italic;margin-top:18px;padding-top:10px;border-top:1px dotted #D8D0BF}\n.mqcm-loading{padding:60px 20px;text-align:center;color:#6B7A8F;font-family:'JetBrains Mono',monospace;font-size:.85rem}\n.mqcm-error{padding:18px;background:#FEE2E2;
13color:#B91C1C;border-radius:10px;text-align:center;font-weight:600;font-size:.9rem;margin:24px 0}\n.mqcm-score-big{font-family:'Anton','Inter',sans-serif;font-size:3.5rem;color:#0f1e4f;line-height:1;text-align:center;margin-bottom:4px}\n.mqcm-score-pct{font-family:'JetBrains Mono',monospace;font-size:.85rem;color:#6B7A8F;text-align:center;text-transform:uppercase;letter-spacing:.08em;margin-bottom:18px}\n.mqcm-score-msg{font-size:.95rem;line-height:1.55;color:#2C3A4E;background:#FAF8F2;border-left:3px solid #1e3a8a;border-radius:0 8px 8px 0;padding:14px 18px;margin-bottom:14px}\n.mqcm-band-perfect{border-color:#16a34a;background:linear-gradient(135deg,#D1FAE5,#FFFFFF)}\n.mqcm-band-perfect .mqcm-score-big{color:#16a34a}\n.mqcm-band-high{border-color:#1e3a8a;background:linear-gradient(135deg,#dbeafe,#FFFFFF)}\n.mqcm-band-mid{border-color:#d97706;background:linear-gradient(135deg,#FEF3C7,#FFFFFF)}\n.mqcm-band-mid .mqcm-score-big{color:#92400E}\n.mqcm-band-low{border-color:#dc2626;background:linear-gradient(135deg,#FEE2E2,#FFFFFF)}\n.mqcm-band-low .mqcm-score-big{color:#dc2626}\n.mqcm-hint-box{background:#FEF3C7;border:2px dashed #d97706;border-radius:10px;padding:16px;margin-top:16px}\n.mqcm-hint-label{font-family:'JetBrains Mono',monospace;font-size:.74rem;color:#d97706;text-transform:uppercase;letter-spacing:.06em;font-weight:700;margin-bottom:6px}\n.mqcm-hint-q{font-size:.9rem;color:#6B7A8F;margin-bottom:8px}\n.mqcm-hint-text{color:#1A2A40;font-size:.94rem;line-height:1.5;font-weight:500}\n.mqcm-corrections{margin-top:20px;border-top:1px solid #D8D0BF;padding-top:14px}\n.mqcm-corrections-summary{cursor:pointer;font-weight:600;color:#1e3a8a;font-size:.95rem;padding:8px 0}\n.mqcm-corrections-summary:hover{color:#0f1e4f}\n.mqcm-correction-item{background:#FAF8F2;border:1px solid #D8D0BF;border-radius:8px;padding:16px;margin:12px 0}\n.mqcm-correction-q{font-weight:600;color:#0E1A2B;margin-bottom:8px;font-size:.95rem}\n.mqcm-correction-answer{font-family:'JetBrains Mono',monospace;font-size:.82rem;color:#16a34a;background:#D1FAE5;padding:6px 10px;border-radius:5px;display:inline-block;margin-bottom:8px}\n.mqcm-correction-wrong{color:#dc2626;font-size:.82rem;margin-bottom:8px}\n.mqcm-correction-intuition{background:#FEF0B4;border-left:3px solid #b45309;padding:10px 14px;border-radius:0 6px 6px 0;font-size:.88rem;margin-bottom:8px;color:#78350f}\n.mqcm-correction-intuition-label{display:block;font-family:'JetBrains Mono',monospace;font-size:.7rem;color:#92400e;text-transform:uppercase;letter-spacing:.06em;font-weight:700;margin-bottom:4px}\n.mqcm-correction-exp{font-size:.9rem;color:#2C3A4E;line-height:1.55;margin-bottom:10px}\n.mqcm-correction-mingeau{background:#dbeafe;border-left:3px solid #1e3a8a;padding:10px 14px;border-radius:0 6px 6px 0;font-size:.88rem;margin-bottom:8px;font-style:italic}\n.mqcm-correction-mingeau-label{display:block;font-family:'JetBrains Mono',monospace;font-size:.7rem;color:#1e3a8a;text-transform:uppercase;letter-spacing:.06em;font-weight:700;margin-bottom:4px;font-style:normal}\n.mqcm-correction-meta{font-family:'JetBrains Mono',monospace;font-size:.78rem;color:#6B7A8F;margin-top:6px}\n.mqcm-correction-meta a{color:#1e3a8a;font-weight:600}\n/* Correction ALLÉGÉE anon/FREE (encart neutre bleu pâle, distinct du crème EXPERT) */\n.mqcm-corrections-light{margin-top:16px;background:#F3F6FB;border:1px solid #C7D2E3;border-radius:12px;padding:16px 18px;text-align:left}\n.mqcm-cl-title{font-weight:700;color:#1e3a8a;font-size:.95rem;margin-bottom:12px}\n.mqcm-cl-item{background:#FFFFFF;border:1px solid #DDE5F0;border-radius:8px;padding:12px 14px;margin:10px 0}\n.mqcm-cl-item.mqcm-cl-wrong{border-left:3px solid #dc2626}\n.mqcm-cl-item.mqcm-cl-ok{border-left:3px solid #16a34a}\n.mqcm-cl-q{font-weight:600;color:#0E1A2B;margin-bottom:6px;font-size:.92rem}\n.mqcm-cl-your{color:#dc2626;font-size:.82rem;margin-bottom:4px}\n.mqcm-cl-good{color:#16a34a;font-size:.85rem;margin-bottom:6px}\n.mqcm-cl-exp{font-size:.88rem;color:#2C3A4E;line-height:1.55}\n.mqcm-cl-foot{font-size:.78rem;color:#6B7A8F;margin-top:10px;font-style:italic}\n@media (max-width: 600px){\n  .mqcm-container{padding:18px;margin:24px 0}\n  .mqcm-h{font-size:1.1rem}\n  .mqcm-question{font-size:1rem}\n  .mqcm-option{font-size:.9rem;padding:10px 14px}\n  .mqcm-score-big{font-size:2.8rem}\n}\n.mqcm-sticky-card{display:none;position:fixed;bottom:24px;left:24px;z-index:9000;width:240px;font-family:'Inter',system-ui,-apple-system,sans-serif;background:linear-gradient(135deg,#FFFFFF 0%,#F0F4FA 100%);border:1px solid #D8D0BF;border-radius:16px;padding:18px 16px;box-shadow:0 18px 36px -16px rgba(30,58,138,.3),0 4px 8px -4px rgba(30,58,138,.1);animation:mqcmStickyIn .55s ease-out;text-align:center;color:#2C3A4E;line-height:1.4;overflow:hidden}\n.mqcm-sticky-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,#1e3a8a 0%,#3b82f6 50%,#1e3a8a 100%)}\n.mqcm-sticky-close{position:absolute;top:8px;right:8px;width:24px;height:24px;border:none;background:rgba(30,58,138,.08);color:#1e3a8a;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .15s;padding:0;z-index:1}\n.mqcm-sticky-close:hover{background:rgba(30,58,138,.2);transform:rotate(90deg)}\n.mqcm-sticky-icon-wrap{display:flex;justify-content:center;margin-bottom:10px}\n.mqcm-sticky-icon{display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;background:linear-gradient(135deg,#dbeafe,#FFFFFF);border:1px solid #dbeafe;border-radius:50%;font-size:1.4rem;box-shadow:0 6px 12px -6px rgba(30,58,138,.25);color:#1e3a8a}\n.mqcm-sticky-eyebrow{font-family:'JetBrains Mono',monospace;font-size:.66rem;color:#1e3a8a;text-transform:uppercase;letter-spacing:.08em;font-weight:700;margin-bottom:6px}\n.mqcm-sticky-title{font-family:'Bricolage Grotesque','Inter',sans-serif;font-size:1rem;font-weight:700;color:#0E1A2B;margin-bottom:10px;line-height:1.25;letter-spacing:-.01em}\n.mqcm-sticky-pills{display:flex;justify-content:center;gap:4px;margin-bottom:14px;flex-wrap:wrap}\n.mqcm-sticky-pill{display:inline-flex;align-items:center;font-family:'JetBrains Mono',monospace;font-size:.62rem;color:#1e3a8a;background:#dbeafe;padding:3px 8px;border-radius:999px;font-weight:600;letter-spacing:.03em}\n.mqcm-sticky-pill-soft{background:#FAF8F2;color:#6B7A8F;border:1px solid #D8D0BF}\n.mqcm-sticky-btn{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:11px 14px;background:linear-gradient(135deg,#1e3a8a,#0f1e4f);color:white;border:none;border-radius:10px;cursor:pointer;font-family:inherit;font-weight:600;font-size:.82rem;text-align:center;box-shadow:0 6px 14px -6px rgba(30,58,138,.5),inset 0 1px 0 rgba(255,255,255,.15);transition:all .2s}\n.mqcm-sticky-btn:hover{transform:translateY(-1px);box-shadow:0 10px 18px -6px rgba(30,58,138,.6),inset 0 1px 0 rgba(255,255,255,.18)}\n.mqcm-sticky-arrow{font-size:1.05em;transition:transform .2s}\n.mqcm-sticky-btn:hover .mqcm-sticky-arrow{transform:translateX(2px)}\n@keyframes mqcmStickyIn{from{opacity:0;transform:translateY(-12px)}to{opacity:1;transform:translateY(0)}}\n.mqcm-sticky-pill-collapsed{display:none;position:fixed;bottom:20px;right:16px;z-index:9000;width:54px;height:54px;animation:mqcmStickyIn .4s ease-out}\n.mqcm-sticky-pill-btn{width:54px;height:54px;border:none;background:linear-gradient(135deg,#1e3a8a 0%,#0f1e4f 100%);border-radius:50%;cursor:pointer;box-shadow:0 12px 24px -10px rgba(30,58,138,.55),0 2px 6px -2px rgba(30,58,138,.25);display:flex;align-items:center;justify-content:center;transition:transform .2s;padding:0;position:relative}\n.mqcm-sticky-pill-btn:hover{transform:scale(1.06)}\n.mqcm-sticky-pill-collapsed .mqcm-sticky-icon{width:auto;height:auto;margin:0;background:none;border:none;border-radius:0;box-shadow:none;color:#FFFFFF}\n.mqcm-sticky-pill-close{position:absolute;top:-4px;right:-4px;width:20px;height:20px;border:1px solid #D8D0BF;background:#FFFFFF;color:#6B7A8F;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;z-index:2;box-shadow:0 2px 6px -2px rgba(0,0,0,.2);transition:all .15s}\n.mqcm-sticky-pill-close:hover{background:#FEE2E2;
13color:#dc2626;border-color:#dc2626}\n@media (min-width: 1280px){\n  .mqcm-sticky-card.mqcm-sticky--show{display:block}\n}\n@media (max-width: 1279px){\n  .mqcm-sticky-pill-collapsed.mqcm-sticky--show{display:block}\n}\n";
14  if (!document.getElementById('mqcm-styles')) {
15    var styleEl = document.createElement('style');
16    styleEl.id = 'mqcm-styles';
17    styleEl.textContent = CSS;
18    document.head.appendChild(styleEl);
19  }
20
21  var state = {
22    data: null,
23    currentIdx: 0,
24    answers: [],
25    score: null,
26    hintRevealed: false,
27    startedAt: Date.now(),
28    advancing: false,
29    impressionTracked: false,
30  };
31
32  function gtag_safe(name, params) {
33    if (typeof window.gtag === 'function') {
34      try { window.gtag('event', name, params || {}); } catch(_) {}
35    }
36  }
37
38  function esc(s) {
39    return String(s == null ? '' : s)
40      .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
41      .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
42  }
43
44  function render(html) { placeholder.innerHTML = html; }
45
46  function init() {
47    render('<div class="mqcm-loading">Chargement du test de veille…</div>');
48    fetch(API_BASE + '/reglementation/api/blog-quiz/' + encodeURIComponent(postIdent) + '/data', { credentials: 'same-origin' })
49      .then(function(r){
50        if (r.status === 404) throw new Error('not_found');
51        if (!r.ok) throw new Error('http_' + r.status);
52        return r.json();
53      })
54      .then(function(data){
55        state.data = data;
56        state.answers = new Array(data.questions.length).fill(null);
57        renderIntro();
58        injectStickyCta(data);
59        trackImpressionWhenVisible(data);
60      })
61      .catch(function(e){
62        if (e.message === 'not_found') {
63          placeholder.style.display = 'none';
64          return;
65        }
66        render('<div class="mqcm-error">Test de veille indisponible (' + esc(e.message) + ')</div>');
67      });
68  }
69
70  // Émet l'impression « widget réellement VU » (et non « page chargée ») via
71  // IntersectionObserver. Avant : l'event partait au chargement → surcomptage massif.
72  // Fallback : émission immédiate si l'API n'existe pas. Tunnel : started(vu) > start_clicked > completed.
73  function trackImpressionWhenVisible(data) {
74    function fire() {
75      if (state.impressionTracked) return;
76      state.impressionTracked = true;
77      gtag_safe('blog_quiz_started_' + (data.tier || 'anonymous'), {
78        post_slug: postIdent, question_count: data.questions.length,
79      });
80    }
81    if ('IntersectionObserver' in window && placeholder) {
82      var io = new IntersectionObserver(function(entries){
83        for (var i = 0; i < entries.length; i++) {
84          if (entries[i].isIntersecting) { fire(); io.disconnect(); break; }
85        }
86      }, { threshold: 0.25 });
87      io.observe(placeholder);
88    } else {
89      fire();
90    }
91  }
92
93  function renderIntro() {
94    var d = state.data;
95    // Matrice tier × visibility (3 profils explicites) :
96    //   - tier user vient du backend : 'anonymous' | 'free' | 'expert'
97    //   - article visibility 'public' vs autre (paywallé EXPERT)
98    // Cas spécial : anon/FREE sur article EXPERT → ton « intuition challenge » (preserve l'angle dissonance).
99    // Tous les autres cas : ton « vérification de veille » adapté au profil.
100    var articleIsExpert = (d.post_visibility !== 'public');
101    var tier = d.tier || 'anonymous';
102    var eyebrow, title, sub, btnLabel, icon;
103
104    if (articleIsExpert && tier !== 'expert') {
105      icon = '\ud83d\udd0d'; // 🔍
106      eyebrow = 'Pas (encore) abonné EXPERT ?';
107      title = 'Vérifie si tu maîtrises ce sujet - sans avoir besoin du compte EXPERT';
108      sub = 'En ' + d.questions.length + ' questions chrono. Si tu as tout bon, tu connais déjà l\'essentiel.';
109      btnLabel = 'Vérifier mon intuition';
110    } else if (tier === 'expert') {
111      icon = '\u2696\ufe0f'; // ⚖️
112      eyebrow = 'Veille continue · R.631-7 CSI';
113      title = 'Confirmer ma veille sur ce sujet';
114      sub = 'Test rapide (~2 min) pour valider ta compréhension. Comptabilisé pour ton <strong>attestation de veille datée</strong> (Qualiopi RNQ critère 6).';
115      btnLabel = 'Démarrer le test';
116    } else if (tier === 'free') {
117      icon = '\ud83c\udfaf'; // 🎯
118      eyebrow = 'Vérification de veille';
119      title = 'Tester ma compréhension + débloquer mon palier';
120      sub = 'En 2 min chrono. Comptabilisé pour ton palier de <strong>veille</strong> (R.631-7 CSI, Qualiopi RNQ critère 6).';
121      btnLabel = 'Démarrer le test';
122    } else {
123      icon = '\ud83c\udfaf'; // 🎯
124      eyebrow = 'Vérification de veille';
125      title = 'Tester ma compréhension en 2 min';
126      sub = 'Vérifie ta compréhension de l\'article. Aucun compte requis - crée-toi un compte gratuit pour suivre tes paliers de veille.';
127      btnLabel = 'Démarrer le test';
128    }
129    render(
130      '<div class="mqcm-container mqcm-intro">' +
131      '  <div class="mqcm-icon-wrap"><span class="mqcm-icon">' + icon + '</span></div>' +
132      '  <div class="mqcm-header">' +
133      '    <div class="mqcm-eyebrow">' + eyebrow + '</div>' +
134      '    <h3 class="mqcm-h">' + title + '</h3>' +
135      '    <div class="mqcm-pills">' +
136      '      <span class="mqcm-pill">' + d.questions.length + ' questions</span>' +
137      '      <span class="mqcm-pill mqcm-pill-soft">~2 min</span>' +
138      '    </div>' +
139      '    <p class="mqcm-sub">' + sub + '</p>' +
140      '  </div>' +
141      '  <button class="mqcm-btn mqcm-btn-primary mqcm-btn-large" id="mqcm-start">' + btnLabel + ' <span class="mqcm-btn-arrow">&rarr;</span></button>' +
142      '  <p class="mqcm-meta"><a href="' + API_BASE + '/blog-quiz/' + esc(d.post_slug) + '/" class="mqcm-link">Ouvrir en plein écran &rarr;</a></p>' +
143      renderDisclaimer(d) +
144      '</div>'
145    );
146    var btn = document.getElementById('mqcm-start');
147    if (btn) btn.onclick = function(){
148      gtag_safe('blog_quiz_start_clicked', { post_slug: postIdent, tier: (state.data && state.data.tier) || 'anonymous' });
149      state.startedAt = Date.now();
150      renderQuestion();
151    };
152  }
153
154  function renderDisclaimer(d) {
155    var text = d.needs_review
156      ? 'Aperçu en relecture'
157      : 'Aperçu validé par M. Mingeau';
158    return '<p class="mqcm-disclaimer">
158' + text + '</p>';
159  }
160
161  function renderQuestion() {
162    var d = state.data;
163    var q = d.questions[state.currentIdx];
164    var total = d.questions.length;
165    var optsHtml = q.options.map(function(opt, i){
166      var sel = state.answers[state.currentIdx] === i ? ' mqcm-selected' : '';
167      return '<button class="mqcm-option' + sel + '" data-idx="' + i + '">' + esc(opt) + '</button>';
168    }).join('');
169    var isLast = state.currentIdx === total - 1;
170    var answered = state.answers.filter(function(a){ return a !== null && a !== undefined; }).length;
171    var progressPct = Math.round(answered * 100 / total);
172    var navHint = isLast
173      ? '\u2728 Dernière question - clique pour voir ton score'
174      : 'Clique sur ta réponse pour passer à la suivante';
175    render(
176      '<div class="mqcm-container">' +
177      '  <div class="mqcm-progress-bar"><div class="mqcm-progress-bar-fill" style="width:' + progressPct + '%"></div></div>' +
178      '  <div class="mqcm-progress">Question ' + (state.currentIdx + 1) + ' / ' + total + (answered > 0 ? ' \u00b7 ' + answered + ' répondue' + (answered > 1 ? 's' : '') : '') + '</div>' +
179      '  <div class="mqcm-question">' + esc(q.question) + '</div>' +
180      '  <div class="mqcm-options">' + optsHtml + '</div>' +
181      '  <div class="mqcm-nav">' +
182      '    <button class="mqcm-btn mqcm-btn-secondary" id="mqcm-prev"' + (state.currentIdx === 0 ? ' disabled' : '') + '>&larr; Précédent</button>' +
183      '    <div class="mqcm-nav-hint">' + navHint + '</div>' +
184      '  </div>' +
185      '</div>'
186    );
187    Array.prototype.forEach.call(placeholder.querySelectorAll('.mqcm-option'), function(el){
188      el.onclick = function(){
189        if (state.advancing) return;
190        state.answers[state.currentIdx] = parseInt(el.getAttribute('data-idx'), 10);
191        Array.prototype.forEach.call(placeholder.querySelectorAll('.mqcm-option'), function(opt){ opt.classList.remove('mqcm-selected'); });
192        el.classList.add('mqcm-selected');
193        Array.prototype.forEach.call(placeholder.querySelectorAll('.mqcm-option'), function(opt){ opt.disabled = true; });
194        state.advancing = true;
195        setTimeout(function(){
196          state.advancing = false;
197          if (isLast) submitQcm();
198          else { state.currentIdx++; renderQuestion(); }
199        }, 400);
200      };
201    });
202    var prev = document.getElementById('mqcm-prev');
203    if (prev) prev.onclick = function(){
204      if (state.advancing) return;
205      if (state.currentIdx > 0) { state.currentIdx--; renderQuestion(); }
206    };
207  }
208
209  function submitQcm() {
210    render('<div class="mqcm-loading">Calcul du score…</div>');
211    fetch(API_BASE + '/reglementation/api/blog-quiz/' + encodeURIComponent(state.data.post_uuid) + '/attempt', {
212      method: 'POST', credentials: 'same-origin',
213      headers: { 'Content-Type': 'application/json' },
214      body: JSON.stringify({
215        answers: state.answers,
216        duration_sec: Math.round((Date.now() - state.startedAt) / 1000),
217        started_at: state.startedAt,
218        finished_at: Date.now(),
219      }),
220    })
221      .then(function(r){ return r.json(); })
222      .then(function(result){
223        state.score = result;
224        gtag_safe('blog_quiz_completed_' + (result.tier || 'anonymous'), {
225          post_slug: state.data.post_slug, score: result.score, total: result.total,
226        });
227        renderScore();
228      })
229      .catch(function(e){ render('<div class="mqcm-error">Erreur soumission : ' + esc(e.message) + '</div>'); });
230  }
231
232  function renderScore() {
233    var d = state.data;
234    var s = state.score;
235    var pct = s.total > 0 ? Math.round(s.score * 100 / s.total) : 0;
236    var bandClass = pct === 100 ? 'mqcm-band-perfect' : pct >= 80 ? 'mqcm-band-high' : pct >= 60 ? 'mqcm-band-mid' : 'mqcm-band-low';
237
238    var isConnected = s.is_expert || s.is_free;
239    var passed = pct >= 60;
240
241    // ANONYME : mur signup léger (ne bloque pas le score, posé juste sous le résultat).
242    // 62 % des parties sont anonymes et ne comptent pour AUCUNE attestation tant qu'il
243    // n'y a pas de compte → on transforme le résultat en accroche d'inscription.
244    var signupWall = '';
245    var ctaHtml = '';
246    if (!isConnected) {
247      var sUrl = s.cta_url || '/signup/?from=blog-quiz';
248      signupWall =
249        '<div class="mqcm-signup-wall">' +
250        '  <div class="mqcm-signup-wall__title">' + (passed ? '✅ Test réussi - mais il n\'est pas encore compté' : '📌 Ton score n\'est pas encore enregistré') + '</div>' +
251        '  <div class="mqcm-signup-wall__body">Crée ton <strong>compte gratuit</strong> pour suivre ta veille : <strong>2 tests réussis cette semaine = ta 1ʳᵉ attestation de veille datée</strong>, opposable employeur / OF (critère Qualiopi). Sans compte, rien n\'est comptabilisé.</div>' +
252        '  <a href="' + esc(sUrl) + '" class="mqcm-btn mqcm-btn-primary mqcm-btn-large mqcm-cta" data-track="cta" data-cta-kind="signup">Créer mon compte gratuit →</a>' +
253        '  <div class="mqcm-signup-wall__foot">Gratuit · sans carte bancaire · paliers hebdo → annuel</div>' +
254        '</div>';
255    } else if (s.cta_url) {
256      // FREE : CTA upgrade EXPERT (EXPERT ne reçoit pas de cta_url).
257      ctaHtml = '<a href="' + esc(s.cta_url) + '" class="mqcm-btn mqcm-btn-primary mqcm-cta" data-track="cta" data-cta-kind="upgrade">' + esc(s.cta_label || 'Découvrir') + '</a>';
258    }
259    var hintCta = '';
260    if (s.is_free && !state.hintRevealed) {
261      hintCta = '<button class="mqcm-btn mqcm-btn-secondary mqcm-hint-btn" id="mqcm-hint">Révéler 1 indice (FREE)</button>';
262    }
263    // Panneau attestation : chargé séparément (GET /me/vigilance) car nécessite cookie Ghost
264    // → on insère juste un placeholder ici, et on lance le fetch après render.
265    var attestPanel = isConnected ? '<div id="mqcm-vigilance-panel" class="mqcm-vigilance-panel"><div class="mqcm-vigilance-loading">📜 Chargement de tes paliers de veille…</div></div>' : '';
266
267    // Correction allégée (anon/FREE, systematique) : fondement pour tous les items, pas seulement les echecs.
268    var lightHtml = (Array.isArray(s.corrections_light) && s.corrections_light.length)
269      ? renderLightCorrections(s.corrections_light, s.answers, (state.data && state.data.questions) || [])
270      : '';
271
272    render(
273      '<div class="mqcm-container ' + bandClass + '">' +
274      '  <div class="mqcm-score-big">' + s.score + '/' + s.total + '</div>' +
275      '  <div class="mqcm-score-pct">' + pct + '% de bonnes réponses</div>' +
276      '  <div class="mqcm-score-msg">' + esc(s.message || '') + '</div>' +
277      lightHtml +
278      ((ctaHtml || hintCta) ? '  <div class="mqcm-cta-row">' + ctaHtml + hintCta + '</div>' : '') +
279      signupWall +
280      '  <div id="mqcm-hint-zone"></div>' +
281      attestPanel +
282      '  <div id="mqcm-attest-zone"></div>' +
283      (Array.isArray(s.questions) && s.questions.length ? renderExpertCorrections(s.questions, s.answers) : '') +
284      '  <p class="mqcm-meta"><a href="' + API_BASE + '/blog-quiz/' + esc(d.post_slug) + '/" class="mqcm-link">Voir le test en plein écran &rarr;</a></p>' +
285      '</div>'
286    );
287    if (lightHtml) gtag_safe('blog_quiz_correction_shown', { post_slug: d.post_slug, tier: s.tier, score: s.score });
288    var hintBtn = document.getElementById('mqcm-hint');
289    if (hintBtn) hintBtn.onclick = revealHint;
290    // Charge dynamiquement les paliers de veille pour les FREE+EXPERT
291    if (isConnected) loadVigilancePanel();
292    Array.prototype.forEach.call(placeholder.querySelectorAll('[data-track]'), function(el){
293      el.addEventListener('click', function(){
294        var kind = el.getAttribute('data-cta-kind') || 'signup';
295        gtag_safe('blog_quiz_cta_' + kind + '_clicked', { post_slug: d.post_slug, tier: s.tier, score: s.score });
296        // Persiste le clic en base (cta_signup_clicked / cta_upgrade_clicked).
297        // keepalive : la requête survit à la navigation déclenchée par le lien.
298        try {
299          fetch(API_BASE + '/reglementation/api/blog-quiz/' + encodeURIComponent(d.post_uuid) + '/cta', {
300            method: 'POST', credentials: 'include', keepalive: true,
301            headers: { 'Content-Type': 'application/json' },
302            body: JSON.stringify({ kind: kind }),
303          }).catch(function(){});
304        } catch (_e) {}
305      });
306    });
307  }
308
309  function renderExpertCorrections(questions, answers) {
310    return '<details class="mqcm-corrections" open><summary class="mqcm-corrections-summary">Voir les corrections détaillées (' + questions.length + ')</summary>' +
311      questions.map(function(q, i){
312        var userAns = answers[i];
313        var isOk = userAns === q.correct;
314        var jurisp = q.jurisprudence_ref && q.jurisprudence_ref.label
315          ? '<div class="mqcm-correction-meta">\u2696\ufe0f Jurisprudence : ' + (q.jurisprudence_ref.url ? '<a href="' + esc(q.jurisprudence_ref.url) + '" target="_blank" rel="noopener">' + esc(q.jurisprudence_ref.label) + '</a>' : esc(q.jurisprudence_ref.label)) + '</div>'
316          : '';
317        var legifrance = q.legifrance_url
318          ? '<div class="mqcm-correction-meta">\ud83d\udcdc <a href="' + esc(q.legifrance_url) + '" target="_blank" rel="noopener">Texte officiel sur Légifrance &rarr;</a></div>'
319          : '';
320        var intuition = q.common_intuition
321          ? '<div class="mqcm-correction-intuition"><span class="mqcm-correction-intuition-label">\ud83d\udcad Ce qu\'on croit souvent</span>' + esc(q.common_intuition) + '</div>'
322          : '';
323        var mingeauShort = q.mingeau_comment_short && !q.mingeau_comment_full
324          ? '<div class="mqcm-correction-mingeau"><span class="mqcm-correction-mingeau-label">\u270d\ufe0f M. Mingeau</span>' + esc(q.mingeau_comment_short) + '</div>'
325          : '';
326        var mingeauFull = q.mingeau_comment_full
327          ? '<div class="mqcm-correction-mingeau"><span class="mqcm-correction-mingeau-label">\u270d\ufe0f M. Mingeau</span>' + esc(q.mingeau_comment_full) + '</div>'
328          : mingeauShort;
329        return '<div class="mqcm-correction-item">' +
330          '  <div class="mqcm-correction-q">Q' + (i + 1) + '. ' + esc(q.question) + '</div>' +
331          '  <div class="mqcm-correction-answer">Bonne réponse : ' + String.fromCharCode(65 + q.correct) + '. ' + esc(q.options[q.correct]) + '</div>' +
332          (isOk ? '' : '<div class="mqcm-correction-wrong">Votre réponse : ' + (userAns >= 0 ? String.fromCharCode(65 + userAns) + '. ' + esc(q.options[userAns] || '') : '(non répondue)') + '</div>') +
333          intuition +
334          '  <div class="mqcm-correction-exp">' + esc(q.explanation_expert || '') + '</div>' +
335          mingeauFull + jurisp + legifrance +
336          '</div>';
337      }).join('') +
338    '</details>';
339  }
340
341  // Correction ALLÉGÉE pour anon/FREE en échec : bonne réponse + explication grand public,
342  // SANS les champs premium (jurisprudence/legifrance/mingeau, réservés EXPERT).
343  // dq = state.data.questions (pour retrouver le libellé de la réponse de l'utilisateur).
344  // Rendu pour TOUS les items : bonne reponse + fondement, que ce soit un echec ou une reussite.
345  function renderLightCorrections(items, answers, dq) {
346    var hasAnyWrong = items.some(function(c){ return !!c.was_wrong; });
347    var blockTitle = hasAnyWrong ? '\ud83d\udcd8 Fondements des réponses' : '\u2705 Toutes les réponses - fondements';
348    return '<div class="mqcm-corrections-light">' +
349      '<div class="mqcm-cl-title">' + blockTitle + '</div>' +
350      items.map(function(c, i){
351        var wrong = !!c.was_wrong;
352        var correct = !!c.was_correct;
353        var ua = answers ? answers[i] : null;
354        var uaLabel = (wrong && ua != null && ua >= 0 && dq && dq[i] && dq[i].options) ? dq[i].options[ua] : null;
355        return '<div class="mqcm-cl-item' + (wrong ? ' mqcm-cl-wrong' : ' mqcm-cl-ok') + '">' +
356          '<div class="mqcm-cl-q">' + esc(c.question) + '</div>' +
357          (uaLabel ? '<div class="mqcm-cl-your">\u2717 Ta réponse : ' + esc(uaLabel) + '</div>' : '') +
358          (correct && !uaLabel ? '<div class="mqcm-cl-correct-badge">\u2713 Bonne réponse</div>' : '') +
359          '<div class="mqcm-cl-good">' + (correct ? 'Fondement : <strong>' : '\u2713 Bonne réponse : <strong>') + esc(c.correct_label) + '</strong></div>' +
360          (c.explanation_simple ? '<div class="mqcm-cl-exp">' + esc(c.explanation_simple) + '</div>' : '') +
361          '</div>';
362      }).join('') +
363      '<div class="mqcm-cl-foot">Corrections complètes (fondement juridique, jurisprudence, commentaire du formateur) réservées aux membres EXPERT.</div>' +
364      '</div>';
365  }
366
367  // Charge dynamiquement les 5 paliers de veille pour l'utilisateur connecté.
368  function loadVigilancePanel() {
369    var panel = document.getElementById('mqcm-vigilance-panel');
370    if (!panel) return;
371    fetch(API_BASE + '/reglementation/api/blog-quiz/me/vigilance', {
372      method: 'GET', credentials: 'include',
373    })
374      .then(function(r){ return r.json(); })
375      .then(function(j){
376        if (j.error || !j.levels) {
377          panel.innerHTML = '<div class="mqcm-error">Impossible de charger tes paliers (' + esc(j.error || 'erreur') + ')</div>';
378          return;
379        }
380        var rowsHtml = j.levels.map(function(L){
381          var stateCls = L.eligible ? ' mqcm-vig-row--ok' : (L.n_played > 0 ? ' mqcm-vig-row--prog' : ' mqcm-vig-row--todo');
382          var rightCol = L.eligible
383            ? '<button class="mqcm-btn mqcm-btn-attest mqcm-vig-btn" data-type="' + esc(L.type) + '">📜 Générer</button>'
384            : '<span class="mqcm-vig-remaining">' + L.remaining + ' test' + (L.remaining > 1 ? 's' : '') + ' restant' + (L.remaining > 1 ? 's' : '') + '</span>';
385          return '<div class="mqcm-vig-row' + stateCls + '">' +
386            '<span class="mqcm-vig-ic">' + L.icon + '</span>' +
387            '<div class="mqcm-vig-body">' +
388            '<div class="mqcm-vig-label">' + esc(L.label) + ' <span class="mqcm-vig-count">' + L.n_played + '/' + L.min + '</span></div>' +
389            '<div class="mqcm-vig-bar"><div class="mqcm-vig-bar-fill" style="width:' + L.progress + '%"></div></div>' +
390            '</div>' +
391            rightCol +
392            '</div>';
393        }).join('');
394        // Prochain palier atteignable : 1er non-eligible avec n_played > 0, sinon hebdo.
395        var next = null;
396        for (var i = 0; i < j.levels.length; i++) {
397          if (!j.levels[i].eligible) { next = j.levels[i]; break; }
398        }
399        var nextHtml = '';
400        if (next) {
401          var freshWord = next.type === 'hebdo' ? 'frais cette semaine' : 'récents';
402          var goalWord = next.type === 'hebdo' ? ' pour ta <strong>1ʳᵉ attestation</strong>' : ' pour le palier <strong>' + esc(next.label) + '</strong>';
403          nextHtml = '<div class="mqcm-vig-next">🎯 Plus que <strong>' + next.remaining + ' QCM ' + freshWord + '</strong> (réussi ≥ 60 %)' + goalWord + ' - articles publiés ces <strong>' + next.days + ' derniers jours</strong>.</div>';
404        } else {
405          nextHtml = '<div class="mqcm-vig-next mqcm-vig-next--max">🏆 <strong>Tous les paliers débloqués !</strong> Continue ta veille pour maintenir tes attestations à jour.</div>';
406        }
407        panel.innerHTML =
408          '<div class="mqcm-vig-head">📜 <strong>Tes paliers de veille</strong> - un test compte si <em>score ≥ 3/5 (60 %)</em> sur un article publié dans la période.</div>' +
409          rowsHtml +
410          nextHtml +
411          '<div class="mqcm-vig-foot"><a href="https://www.83-629.fr/blog-quiz/#paliers" target="_blank" rel="noopener">📚 Comprendre les paliers et voir un modèle d\'attestation →</a></div>';
412        Array.prototype.forEach.call(panel.querySelectorAll('.mqcm-vig-btn'), function(b){
413          b.addEventListener('click', function(){ generateAttestation(b.getAttribute('data-type'), b); });
414        });
415      })
416      .catch(function(e){
417        panel.innerHTML = '<div class="mqcm-error">Erreur réseau : ' + esc(e.message) + '</div>';
418      });
419  }
420
421  function generateAttestation(type, btn) {
422    var zone = document.getElementById('mqcm-attest-zone');
423    if (btn) { btn.disabled = true; btn.textContent = '⏳…'; btn.classList.add('mqcm-vig-loading'); }
424    fetch(API_BASE + '/reglementation/api/blog-quiz/me/attestation', {
425      method: 'POST', credentials: 'include',
426      headers: { 'Content-Type': 'application/json' },
427      body: JSON.stringify({ type: type }),
428    })
429      .then(function(r){ return r.json(); })
430      .then(function(j){
431        if (j.error) {
432          if (zone) zone.innerHTML = '<div class="mqcm-error">' + esc(j.error) + (j.cta_url ? ' <a href="' + esc(j.cta_url) + '" target="_blank">→</a>' : '') + '</div>';
433          if (btn) { btn.disabled = false; btn.textContent = '📜 Générer'; btn.classList.remove('mqcm-vig-loading'); }
434          return;
435        }
436        gtag_safe('blog_quiz_attestation_generated', { post_slug: state.data.post_slug, t
436ype: j.type });
437        if (zone) zone.innerHTML =
438          '<div class="mqcm-attest-result">' +
439          '  <div class="mqcm-attest-level">' + esc(j.icon || '📜') + ' <strong>' + esc(j.label || 'Attestation') + '</strong> - ' + esc(j.period_label || '') + '</div>' +
440          '  <a href="' + API_BASE + '/attestation/' + esc(j.public_token) + '/" target="_blank" class="mqcm-btn mqcm-btn-primary mqcm-btn-large">Voir mon attestation →</a>' +
441          '  <div class="mqcm-share-prompt">' +
442          '    <div class="mqcm-share-title">🎉 Bravo ! Partage ta veille :</div>' +
443          '    <div class="mqcm-share-row">' +
444          '      <a class="mqcm-share-btn mqcm-share-li" target="_blank" rel="noopener" href="https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(API_BASE + '/attestation/' + j.public_token + '/') + '">LinkedIn</a>' +
445          '      <a class="mqcm-share-btn mqcm-share-wa" target="_blank" rel="noopener" href="https://wa.me/?text=' + encodeURIComponent('Veille validee en sécurité privée sur 83-629.fr. Mon attestation : ' + API_BASE + '/attestation/' + j.public_token + '/') + '">WhatsApp</a>' +
446          '      <a class="mqcm-share-btn mqcm-share-dl" target="_blank" rel="noopener" download href="' + API_BASE + '/attestation/' + esc(j.public_token) + '/card-carre.png">⬇ Image</a>' +
447          '    </div>' +
448          '  </div>' +
449          '  <p class="mqcm-attest-note">Lien permanent, partageable (employeur, donneur d\'ordre, auditeur). ' + j.n_played + ' test' + (j.n_played > 1 ? 's' : '') + ' validés sur la période · moyenne ' + j.avg_score_pct + '%.</p>' +
450          '</div>';
451        if (btn) { btn.disabled = true; btn.textContent = '✓ Générée'; btn.classList.remove('mqcm-vig-loading'); }
452        // Le résultat s'affiche dans #mqcm-attest-zone, sous le panneau des paliers
453        // (souvent hors écran sur ce résumé) : on amène l'attestation à l'utilisateur.
454        if (zone && zone.scrollIntoView) {
455          try { zone.scrollIntoView({ behavior: 'smooth', block: 'center' }); }
456          catch (e) { zone.scrollIntoView(); }
457        }
458      })
459      .catch(function(e){
460        if (zone) zone.innerHTML = '<div class="mqcm-error">Erreur réseau : ' + esc(e.message) + '</div>';
461        if (btn) { btn.disabled = false; btn.textContent = '📜 Générer'; btn.classList.remove('mqcm-vig-loading'); }
462      });
463  }
464
465  function revealHint() {
466    fetch(API_BASE + '/reglementation/api/blog-quiz/' + encodeURIComponent(state.data.post_uuid) + '/hint', {
467      method: 'POST', credentials: 'same-origin',
468    })
469      .then(function(r){ return r.json(); })
470      .then(function(j){
471        if (j.error) {
472          if (j.cta_url) { window.location.href = j.cta_url; return; }
473          alert('Erreur : ' + j.error); return;
474        }
475        state.hintRevealed = true;
476        gtag_safe('blog_quiz_hint_revealed', { post_slug: state.data.post_slug, teaser_question_id: j.teaser_question_id });
477        var zone = document.getElementById('mqcm-hint-zone');
478        if (zone) zone.innerHTML =
479          '<div class="mqcm-hint-box">' +
480          '  <div class="mqcm-hint-label">\ud83d\udca1 Indice débloqué</div>' +
481          '  <div class="mqcm-hint-q"><em>' + esc(j.teaser_question || '') + '</em></div>' +
482          '  <div class="mqcm-hint-text">' + esc(j.hint_free || '') + '</div>' +
483          '</div>';
484        var btn = document.getElementById('mqcm-hint');
485        if (btn) btn.style.display = 'none';
486      })
487      .catch(function(e){ alert('Erreur : ' + e.message); });
488  }
489
490  // Sticky CTA card desktop (cards à droite) + pill mobile, scroll-driven
491  function injectStickyCta(d) {
492    if (document.getElementById('mqcm-sticky')) return;
493    var articleIsExpert = (d.post_visibility !== 'public');
494    var tier = d.tier || 'anonymous';
495    var iconSvg = '<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.1 9a2.9 2.9 0 1 1 4 2.7c-1 .45-1.7 1.15-1.7 2.55"></path><circle cx="11.4" cy="17.6" r="1.15" fill="currentColor" stroke="none"></circle></svg>';
496    var eyebrow, titleText, btnLabel;
497    if (articleIsExpert && tier !== 'expert') {
498      eyebrow = 'Vous croyez maîtriser ?';
499      titleText = 'Vérifiez votre intuition';
500      btnLabel = 'Vérifier mon intuition';
501    } else if (tier === 'expert') {
502      eyebrow = 'Veille R.631-7 CSI';
503      titleText = 'Confirmer ma veille';
504      btnLabel = 'Démarrer le test';
505    } else if (tier === 'free') {
506      eyebrow = 'Vérification de veille';
507      titleText = 'Tester + débloquer mon palier';
508      btnLabel = 'Démarrer le test';
509    } else {
510      eyebrow = 'Vérification de veille';
511      titleText = 'Tester ma compréhension';
512      btnLabel = 'Démarrer le test';
513    }
514    var card = document.createElement('aside');
515    card.id = 'mqcm-sticky';
516    card.className = 'mqcm-sticky-card';
517    card.setAttribute('aria-label', 'Test de veille disponible pour cet article');
518    card.innerHTML =
519      '<button type="button" class="mqcm-sticky-close" aria-label="Fermer">' +
520      '  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>' +
521      '</button>' +
522      '<div class="mqcm-sticky-icon-wrap"><span class="mqcm-sticky-icon">' + iconSvg + '</span></div>' +
523      '<div class="mqcm-sticky-eyebrow">' + eyebrow + '</div>' +
524      '<div class="mqcm-sticky-title">' + titleText + '</div>' +
525      '<div class="mqcm-sticky-pills">' +
526      '  <span class="mqcm-sticky-pill">' + d.questions.length + ' questions</span>' +
527      '  <span class="mqcm-sticky-pill mqcm-sticky-pill-soft">~2 min</span>' +
528      '</div>' +
529      '<button type="button" class="mqcm-sticky-btn" aria-label="Aller au test de veille">' + btnLabel + ' <span class="mqcm-sticky-arrow">&rarr;</span></button>';
530    var pill = document.createElement('div');
531    pill.id = 'mqcm-sticky-pill';
532    pill.className = 'mqcm-sticky-pill-collapsed';
533    pill.innerHTML =
534      '<button type="button" class="mqcm-sticky-pill-btn" aria-label="Aller au test de veille"><span class="mqcm-sticky-icon">' + iconSvg + '</span></button>' +
535      '<button type="button" class="mqcm-sticky-pill-close" aria-label="Fermer">' +
536      '  <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>' +
537      '</button>';
538    document.body.appendChild(card);
539    document.body.appendChild(pill);
540    var dismissed = false;
541    // sessionStorage (et non localStorage) : la fermeture ne vaut que pour la session
542    // courante → la pastille réapparaît à la prochaine visite (fermeture non permanente).
543    try { dismissed = sessionStorage.getItem('bq83629_sticky_dismissed') === '1'; } catch (_) {}
544    function goToQcm() {
545      if (placeholder && placeholder.scrollIntoView) {
546        placeholder.scrollIntoView({ behavior: 'smooth', block: 'start' });
547        gtag_safe('blog_quiz_sticky_clicked', { post_slug: postIdent, tier: d.tier });
548      }
549    }
550    function dismiss() {
551      dismissed = true;
552      try { sessionStorage.setItem('bq83629_sticky_dismissed', '1'); } catch (_) {}
553      applyState();
554      gtag_safe('blog_quiz_sticky_dismissed', { post_slug: postIdent });
555    }
556    card.querySelector('.mqcm-sticky-btn').addEventListener('click', goToQcm);
557    card.querySelector('.mqcm-sticky-close').addEventListener('click', dismiss);
558    pill.querySelector('.mqcm-sticky-pill-btn').addEventListener('click', goToQcm);
559    pill.querySelector('.mqcm-sticky-pill-close').addEventListener('click', function(e){ e.stopPropagation(); dismiss(); });
560    var visible = false;
561    function applyState() {
562      var show = visible && !dismissed;
563      card.classList.toggle('mqcm-sticky--show', show);
564      pill.classList.toggle('mqcm-sticky--show', show);
565    }
566    var heroImg = document.querySelector('.gh-post-page__featured-img, .gh-post-page__featured-img-wrapper, .gh-post-page__header, article header, .post-full-image');
567    var morePosts = document.querySelector('.gh-post-page__more, .post-full-footer, .read-next');
568    function checkVisibility() {
569      var heroOut = true;
570      if (heroImg) {
571        var r = heroImg.getBoundingClientRect();
572        heroOut = r.bottom < 80;
573      }
574      var morePostsIn = false;
575      if (morePosts) {
576        var m = morePosts.getBoundingClientRect();
577        morePostsIn = m.top < window.innerHeight * 0.85;
578      }
579      visible = heroOut && !morePostsIn;
580      applyState();
581    }
582    window.addEventListener('scroll', checkVisibility, { passive: true });
583    window.addEventListener('resize', checkVisibility, { passive: true });
584    setTimeout(checkVisibility, 100);
585  }
586
587  if (document.readyState === 'loading') {
588    document.addEventListener('DOMContentLoaded', init);
589  } else {
590    init();
591  }
592})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.