{"id":8720,"date":"2026-04-02T14:49:47","date_gmt":"2026-04-02T12:49:47","guid":{"rendered":"https:\/\/epurbiosolution.eu\/reminder-calendar\/"},"modified":"2026-04-16T15:22:48","modified_gmt":"2026-04-16T13:22:48","slug":"reminder-calendar","status":"publish","type":"page","link":"https:\/\/epurbiosolution.eu\/en\/reminder-calendar\/","title":{"rendered":"Your maintenance schedule &#8211; Uw onderhoudsagenda"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"8720\" class=\"elementor elementor-8720 elementor-8712\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-de2a9b9 e-flex e-con-boxed e-con e-parent\" data-id=\"de2a9b9\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-aae5867 elementor-widget elementor-widget-html\" data-id=\"aae5867\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<style>\n*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   PALETTE EPUR \u2014 th\u00e8me unique, DA du site epurbiosolution.eu\n   Vert primaire CTA : #4BAA4A   Vert fonc\u00e9 nav : #1E5C1B\n   Fond blanc site   : #FFFFFF   Texte corps     : #1A2B1A\n   Police            : Poppins (Google Fonts, th\u00e8me Blocksy)\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n:root {\n  --e9:#0D2B0D; --e8:#1E5C1B; --e7:#2E7D32; --e6:#388E3C;\n  --e5:#4BAA4A; --e4:#66BB6A; --e3:#81C784; --e2:#A5D6A7;\n  --e1:#C8E6C9; --e0:#E8F5E9;\n\n  --g3:var(--e3); --g4:var(--e4); --g5:var(--e5); --g6:var(--e6);\n\n  \/* Fond & surfaces \u2014 blanc pur comme le site Epur *\/\n  --bg:    #FFFFFF;\n  --card:  #F6FBF6;\n  --card2: #ECF7EC;\n\n  \/* Bordures *\/\n  --bdr:   #D6EBD6;\n  --bdr2:  #BBDABB;\n  --bdr3:  #8EC98E;\n  --bdr4:  #4BAA4A;\n\n  \/* Typographie *\/\n  --t1:    #1A2B1A;   \/* quasi-noir verd\u00e2tre \u2014 lisibilit\u00e9 maximale *\/\n  --t2:    #2E5C2E;   \/* secondaire vert fonc\u00e9 *\/\n  --t3:    #4BAA4A;   \/* tertiaire vert primaire *\/\n  --t4:    #6AB96A;   \/* hint *\/\n\n  \/* Amber (avertissements) *\/\n  --amber: #B8830A;\n  --amber-bg:  rgba(184,131,10,.07);\n  --amber-bdr: rgba(184,131,10,.22);\n\n  --rad:14px; --radsm:9px;\n  --font:'Poppins',system-ui,sans-serif;\n}\n\nhtml,body{\n  font-family:var(--font);background:var(--bg);color:var(--t1);\n  line-height:1.5;-webkit-font-smoothing:antialiased;\n  font-size:16px;\n}\n\n\/* PAGE \u2014 wrapper neutre pour Elementor *\/\n.page{\n  width:100%;\n  display:block;\n  padding:100px 0 0.5rem;\n  background:transparent;\n}\n\n\/* MODAL \u2014 large sur desktop, 100% sur mobile *\/\n.modal{\n  position:relative;z-index:1;\n  width:100%;max-width:860px;\n  margin:0 auto;\n  background:var(--bg);border:1px solid var(--bdr);border-radius:16px;overflow:hidden;\n  box-shadow:0 0 0 1px rgba(75,170,74,.08),0 8px 32px rgba(0,0,0,.08),0 2px 8px rgba(0,0,0,.05);\n}\n\n\/* Desktop \u2014 plus de respiration et grille 4 colonnes *\/\n@media(min-width:521px){\n  .hdr{padding:1.2rem 2rem 0}\n  .body{padding:1.5rem 2rem 2rem}\n  .pgrid{grid-template-columns:repeat(4,1fr);gap:12px}\n  .brand-footer{padding:.8rem 2rem}\n  .hh-row{gap:12px}\n  .sum-row{padding:9px 16px}\n  .export-banner,.warn-box,.preview-banner{padding:13px 16px}\n  .email-hero{padding:15px 16px}\n}\n\n\/* HEADER \u2014 vert fonc\u00e9 Epur comme le nav du site *\/\n.hdr{\n  background:linear-gradient(160deg, var(--e8) 0%, var(--e7) 60%, var(--e6) 100%);\n  border-bottom:1px solid rgba(0,0,0,.1);\n  padding:1.1rem 1.5rem 0;\n}\n.hdr-top{\n  display:flex;align-items:center;justify-content:space-between;\n  gap:12px;margin-bottom:1rem;\n}\n.hdr-title{font-size:21px;font-weight:700;color:#fff;letter-spacing:-.01em;line-height:1.3}\n.hdr-sub{font-size:17px;color:rgba(255,255,255,.75);margin-top:2px;letter-spacing:.01em}\n\n\/* Progress segments \u2014 hauteur accrue et coins arrondis *\/\n.prog-wrap{display:flex;gap:5px;padding-bottom:0}\n.prog-seg{flex:1;height:4px;border-radius:3px;background:rgba(255,255,255,.2);transition:background .4s ease,transform .2s;position:relative;overflow:hidden}\n.prog-seg.active{background:#fff}\n.prog-seg.active::after{\n  content:'';position:absolute;top:0;left:-100%;width:80px;height:100%;\n  background:linear-gradient(90deg,transparent,rgba(255,255,255,.5),transparent);\n  animation:shimmer .8s ease forwards;\n}\n@keyframes shimmer{to{left:200%}}\n\n\/* BODY \/ STEPS *\/\n.body{padding:1.3rem 1.5rem 1.5rem}\n.step{display:none}\n.step.active{display:block;animation:fadeUp .22s ease both}\n@keyframes fadeUp{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}\n.sec-lbl{\n  font-size:14.5px;font-weight:600;letter-spacing:.12em;color:var(--t3);\n  text-transform:uppercase;font-family:var(--font);margin-bottom:1rem;\n}\n\n\/* PRODUCT GRID *\/\n.pgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:1.2rem}\n.pcard{\n  background:var(--card);border:1px solid var(--bdr);border-radius:var(--rad);\n  padding:12px 8px 11px;cursor:pointer;\n  transition:border-color .18s,background .18s,transform .15s,box-shadow .18s;\n  display:flex;flex-direction:column;align-items:center;text-align:center;\n  position:relative;user-select:none;\n}\n.pcard:hover{border-color:var(--e5);background:var(--card2);transform:translateY(-1px);box-shadow:0 4px 16px rgba(75,170,74,.12)}\n.pcard:focus-visible{outline:2px solid var(--e5);outline-offset:2px}\n.pcard.sel{border-color:var(--e5);background:var(--card2);border-width:1.5px;box-shadow:0 0 0 3px rgba(75,170,74,.1)}\n.pcard.sel::after{\n  content:'\u2713';position:absolute;top:6px;right:7px;font-size:9px;\n  width:16px;height:16px;border-radius:50%;background:var(--e5);color:#fff;\n  display:flex;align-items:center;justify-content:center;font-weight:700;\n}\n.pimg-wrap{width:100%;display:flex;align-items:center;justify-content:center;margin-bottom:10px;flex-shrink:0;min-height:100px}\n.pimg-wrap img{height:100px;width:auto;object-fit:contain;border-radius:5px;transition:opacity .2s,transform .18s}\n.pcard:hover .pimg-wrap img{transform:scale(1.04)}\n.pimg-wrap .fallback{font-size:36px;line-height:1;display:none}\n.pcard-name{font-size:18px;font-weight:600;color:var(--t1);line-height:1.35;margin-bottom:2px}\n.pcard-variant{font-size:15px;color:var(--t3);line-height:1.3;font-family:var(--font)}\n.vbadge{\n  display:inline-block;font-size:11px;font-weight:700;padding:3px 8px;border-radius:4px;margin-top:5px;\n  background:#1E5C1B;color:#fff;border:none;letter-spacing:.05em;text-transform:uppercase;\n}\n\n\/* STEP 2 \u2014 FOYER *\/\n.prod-hero{display:flex;align-items:center;gap:12px;background:var(--card);border:0.5px solid var(--bdr2);border-radius:var(--radsm);padding:10px 13px;margin-bottom:1.1rem}\n.prod-hero img{width:46px;height:46px;object-fit:contain;border-radius:5px;flex-shrink:0}\n.prod-hero .ph-name{font-size:18px;font-weight:600;color:var(--t1);line-height:1.3}\n.prod-hero .ph-meta{font-size:17px;color:var(--t3);margin-top:2px}\n.hh-q{font-size:19px;font-weight:600;color:var(--t1);margin-bottom:.4rem;line-height:1.4}\n.hh-hint{font-size:17px;color:var(--t2);margin-bottom:1rem;line-height:1.55}\n.hh-row{display:flex;gap:8px;margin-bottom:1.1rem}\n.hh-opt{\n  flex:1;background:var(--card);border:1px solid var(--bdr);border-radius:var(--radsm);\n  padding:14px 6px;cursor:pointer;text-align:center;transition:all .2s;user-select:none;\n}\n.hh-opt:hover{border-color:var(--e5);background:var(--card2);transform:translateY(-2px);box-shadow:0 4px 12px rgba(75,170,74,.15)}\n.hh-opt:focus-visible{outline:2px solid var(--e5);outline-offset:2px}\n.hh-opt.sel{border-color:var(--e5);background:var(--card2);border-width:2px;box-shadow:0 0 0 3px rgba(75,170,74,.12);transform:translateY(-1px)}\n.hh-ico{font-size:28px;display:block;margin-bottom:6px;line-height:1}\n.hh-lbl{font-size:17px;color:var(--t2);line-height:1.3;font-weight:700;display:inline}\n.hh-dpw{font-size:15px;color:var(--t4);display:inline}\n.hh-opt.sel .hh-lbl{color:var(--e2)}.hh-opt.sel .hh-dpw{color:var(--e3)}\n.preview-banner{\n  background:rgba(75,170,74,.08);border:0.5px solid rgba(75,170,74,.3);\n  border-radius:var(--radsm);padding:11px 14px;margin-bottom:1.1rem;\n  font-size:16px;color:var(--t2);line-height:1.75;\n}\n.preview-banner strong{color:var(--e3);font-weight:600}\n\n\/* STEP 3 *\/\n.field-lbl{font-size:17px;font-weight:500;color:var(--t2);margin-bottom:5px;display:block}\ninput[type=\"date\"]{\n  width:100%;background:var(--card);border:1px solid var(--bdr2);border-radius:var(--radsm);\n  padding:9px 13px;color:var(--t1);font-size:17px;font-family:var(--font);\n  margin-bottom:1.1rem;outline:none;color-scheme:light;transition:border-color .18s;\n}\ninput[type=\"date\"]:focus{border-color:var(--e5)}\n.sum-card{background:var(--card);border:0.5px solid var(--bdr2);border-radius:var(--radsm);overflow:hidden;margin-bottom:1rem}\n.sum-row{\n  display:flex;justify-content:space-between;align-items:baseline;\n  padding:8px 13px;border-bottom:0.5px solid var(--bdr);font-size:17px;gap:8px;\n}\n.sum-row:last-child{border-bottom:none}\n.sum-k{color:var(--t3);flex-shrink:0;font-weight:400}\n.sum-v{color:var(--t1);font-weight:600;text-align:right}\n.sum-v.hi{color:var(--e4)}\n.warn-box{\n  display:flex;gap:10px;align-items:flex-start;background:var(--amber-bg);\n  border:0.5px solid var(--amber-bdr);border-radius:var(--radsm);\n  padding:10px 13px;margin-bottom:1.1rem;font-size:17px;\n  color:rgba(212,168,71,.9);line-height:1.55;\n}\n.warn-icon{font-size:16px;flex-shrink:0;margin-top:1px}\n\n\/* STEP 4 *\/\n.export-banner{\n  background:rgba(75,170,74,.08);border:0.5px solid rgba(75,170,74,.3);\n  border-radius:var(--radsm);padding:12px 14px;margin-bottom:1.1rem;\n  font-size:18px;color:var(--t2);line-height:1.75;\n}\n.export-banner strong{color:var(--e3);font-weight:600}\n.compat-note{font-size:16px;color:var(--t4);text-align:center;margin:.35rem 0 .9rem;line-height:1.5}\n\n\/* BUTTONS *\/\n.btn-p{\n  width:100%;padding:11px;\n  background:var(--e5);   \/* vert CTA Epur #4BAA4A *\/\n  color:#fff;border:none;border-radius:var(--radsm);\n  font-size:17px;font-weight:600;font-family:var(--font);\n  cursor:pointer;margin-bottom:8px;letter-spacing:.01em;\n  transition:background .18s,transform .12s,box-shadow .18s;\n  position:relative;overflow:hidden;\n}\n.btn-p::before{\n  content:'';position:absolute;inset:0;\n  background:linear-gradient(180deg,rgba(255,255,255,.1) 0%,transparent 100%);\n  pointer-events:none;\n}\n.btn-p:hover:not(:disabled){background:var(--e6);transform:translateY(-1px);box-shadow:0 6px 20px rgba(75,170,74,.3)}\n.btn-p:active:not(:disabled){transform:translateY(0);box-shadow:none}\n.btn-p:disabled{background:var(--bdr2);color:var(--t4);cursor:not-allowed}\n\n.btn-s{\n  width:100%;padding:10.5px;background:transparent;\n  color:var(--e5);border:1.5px solid var(--e5);\n  border-radius:var(--radsm);font-size:17px;font-weight:600;\n  font-family:var(--font);cursor:pointer;margin-bottom:8px;transition:all .18s;\n}\n.btn-s:hover{background:rgba(75,170,74,.08);border-color:var(--e6);color:var(--e6)}\n\n.btn-g{\n  width:100%;padding:10px;background:transparent;color:var(--t2);\n  border:0.5px solid var(--bdr3);border-radius:var(--radsm);\n  font-size:16px;font-family:var(--font);cursor:pointer;\n  display:flex;align-items:center;justify-content:center;gap:9px;transition:all .18s;\n}\n.btn-g:hover{background:var(--card2);color:var(--t1);border-color:var(--e5)}\n\n.divider{border:none;border-top:0.5px solid var(--bdr);margin:.9rem 0}\n.back-btn{\n  background:none;border:none;font-size:15px;color:var(--t4);cursor:pointer;\n  padding:0 0 .95rem;display:flex;align-items:center;gap:5px;\n  font-family:var(--font);transition:color .15s;\n}\n.back-btn:hover{color:var(--e5)}.back-btn::before{content:'\u2190';font-size:15px}\n\n\/* STEP 5 \u2014 EMAIL *\/\n.email-hero{\n  display:flex;gap:13px;align-items:flex-start;\n  background:rgba(75,170,74,.07);border:0.5px solid rgba(75,170,74,.2);\n  border-radius:var(--radsm);padding:13px 14px;margin-bottom:1rem;\n}\n.email-hero-icon{\n  width:40px;height:40px;background:rgba(75,170,74,.12);\n  border:0.5px solid rgba(75,170,74,.25);border-radius:50%;\n  display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:1px;\n}\n.email-hero-title{font-size:20px;font-weight:700;color:var(--t1);margin-bottom:3px}\n.email-hero-sub{font-size:18px;color:var(--t2);line-height:1.5}\n.email-hero-sub strong{color:var(--e4);font-weight:600}\n.stock-fin-card{\n  background:var(--card);border:0.5px solid var(--bdr2);border-radius:var(--radsm);\n  padding:10px 13px;margin-bottom:1.1rem;font-size:17px;color:var(--t2);line-height:1.7;\n}\n.stock-fin-card strong{color:var(--e3);font-weight:600}\n.stock-fin-card .sfdate{color:var(--amber);font-weight:600}\n.email-field-wrap{position:relative;margin-bottom:1rem}\n.email-field-wrap input[type=\"email\"]{\n  width:100%;background:var(--card);border:1px solid var(--bdr2);border-radius:var(--radsm);\n  padding:9px 38px 9px 13px;color:var(--t1);font-size:17px;font-family:var(--font);\n  outline:none;color-scheme:light;transition:border-color .18s;margin-bottom:0;\n}\n.email-field-wrap input[type=\"email\"]:focus{border-color:var(--e5)}\n.email-field-wrap input[type=\"email\"].valid{border-color:var(--e5)}\n.email-field-wrap input[type=\"email\"].invalid{border-color:rgba(212,168,71,.5)}\n.email-valid-dot{position:absolute;right:12px;top:50%;transform:translateY(-50%);width:8px;height:8px;border-radius:50%;background:transparent;transition:background .2s}\n.email-valid-dot.ok{background:var(--e5)}.email-valid-dot.bad{background:var(--amber)}\n.email-opts{display:flex;flex-direction:column;gap:8px;margin-bottom:1.1rem}\n.chk-row{display:flex;align-items:flex-start;gap:10px;cursor:pointer;user-select:none}\n.chk-row input[type=\"checkbox\"]{display:none}\n.chk-box{\n  width:16px;height:16px;border-radius:4px;border:1.5px solid var(--bdr3);\n  background:var(--card);flex-shrink:0;display:flex;align-items:center;\n  justify-content:center;transition:all .15s;margin-top:1px;\n}\n.chk-row input:checked+.chk-box{background:var(--e5);border-color:var(--e5)}\n.chk-row input:checked+.chk-box::after{\n  content:'';width:5px;height:9px;border:1.5px solid #fff;\n  border-top:none;border-left:none;transform:rotate(40deg) translateY(-1px);display:block;\n}\n.chk-lbl{font-size:17px;color:var(--t2);line-height:1.5}\n.feedback-ok{\n  display:flex;align-items:flex-start;gap:10px;background:rgba(75,170,74,.1);\n  border:0.5px solid rgba(75,170,74,.35);border-radius:var(--radsm);\n  padding:12px 14px;margin-bottom:.8rem;font-size:18px;color:var(--e3);\n  line-height:1.6;animation:fadeUp .25s ease;\n}\n.feedback-ok .fk-icon{\n  width:22px;height:22px;background:rgba(75,170,74,.2);border-radius:50%;\n  display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0;margin-top:1px;\n}\n.feedback-err{\n  display:flex;align-items:center;gap:10px;background:var(--amber-bg);\n  border:0.5px solid var(--amber-bdr);border-radius:var(--radsm);\n  padding:10px 13px;margin-bottom:.8rem;font-size:17px;\n  color:rgba(212,168,71,.9);line-height:1.5;animation:fadeUp .25s ease;\n}\n.rgpd-note{font-size:15px;color:var(--t4);text-align:center;line-height:1.5;margin-top:.5rem}\n\n\/* FOOTER *\/\n.brand-footer{\n  border-top:1px solid var(--bdr);padding:.7rem 1.5rem;\n  display:flex;align-items:center;justify-content:center;\n  background:var(--e0);\n}\n.brand-tag{font-size:13px;color:var(--t4);letter-spacing:.04em}\n\n\/* LANG SWITCHER *\/\n.lang-switcher{display:flex;gap:4px;align-items:center;flex-shrink:0}\n.lang-btn{\n  font-size:15px;font-weight:700;padding:3px 9px;border-radius:5px;\n  border:1.5px solid rgba(255,255,255,.35);color:rgba(255,255,255,.65);\n  background:transparent;cursor:pointer;font-family:var(--font);\n  letter-spacing:.04em;transition:all .15s;\n}\n.lang-btn.active{background:rgba(255,255,255,.22);color:#fff;border-color:rgba(255,255,255,.7)}\n.lang-btn:hover:not(.active){color:#fff;border-color:rgba(255,255,255,.5)}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   RESPONSIVE \u2014 mobile\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n@media(max-width:520px){\n  .page{padding:.25rem 0}\n  .modal{border-radius:10px}\n  .hdr{padding:.85rem 1rem 0}\n  .body{padding:1rem 1rem 1.25rem}\n  .hdr-title{font-size:18px}\n  .hdr-sub{font-size:13px}\n  .pgrid{grid-template-columns:repeat(2,1fr);gap:7px}\n  .pimg-wrap{min-height:70px}\n  .pimg-wrap img{height:70px}\n  .pcard-name{font-size:12px}\n  .pcard-variant{font-size:10.5px}\n  .hh-row{flex-wrap:wrap}\n  .hh-opt{min-width:calc(50% - 4px)}\n  .hh-lbl{font-size:13px}\n  .hh-dpw{font-size:11px;display:inline}\n  .sum-row{flex-direction:column;gap:2px;align-items:flex-start}\n  .sum-v{text-align:left}\n  .btn-p,.btn-s{font-size:16px;padding:14px 11px}\n  .hh-q{font-size:16px}\n  .hh-hint{font-size:14px}\n  .field-lbl{font-size:14px}\n  .warn-box{font-size:14px}\n  .export-banner{font-size:15px}\n  .email-hero-title{font-size:17px}\n  .email-hero-sub{font-size:15px}\n  .stock-fin-card{font-size:14px}\n  .chk-lbl{font-size:14px}\n  .feedback-ok{font-size:15px}\n  .brand-footer{padding:.6rem 1rem}\n}\n\n@media(max-width:360px){\n  .hdr-title{font-size:16px}\n  .hh-row{flex-direction:column}\n  .hh-opt{min-width:100%}\n  .btn-p,.btn-s{font-size:15px}\n}\n<\/style>\n<div class=\"page\">\n\n<div class=\"modal\">\n  <div class=\"hdr\">\n    <div class=\"hdr-top\">\n      <div>\n        <div class=\"hdr-title\" id=\"hdrTitle\">Your maintenance schedule<\/div>\n        <div class=\"hdr-sub\" id=\"hdrSub\">Plan, export and never miss a dose again.<\/div>\n      <\/div>\n      <div class=\"lang-switcher\">\n        <button class=\"lang-btn active\" onclick=\"setLang('fr')\" id=\"btnFR\">FR<\/button>\n        <button class=\"lang-btn\" onclick=\"setLang('nl')\" id=\"btnNL\">NL<\/button>\n      <\/div>\n    <\/div>\n    <div class=\"prog-wrap\">\n      <div class=\"prog-seg active\" id=\"ps1\"><\/div>\n      <div class=\"prog-seg\" id=\"ps2\"><\/div>\n      <div class=\"prog-seg\" id=\"ps3\"><\/div>\n      <div class=\"prog-seg\" id=\"ps4\"><\/div>\n      <div class=\"prog-seg\" id=\"ps5\"><\/div>\n    <\/div>\n  <\/div>\n  <div class=\"body\">\n    <!-- STEP 1 -->\n    <div class=\"step active\" id=\"s1\">\n      <p class=\"sec-lbl\" data-i18n=\"step1_lbl\">01 \/ Select your product<\/p>\n      <div class=\"pgrid\" id=\"prodGrid\"><\/div>\n    <\/div>\n    <!-- STEP 2 -->\n    <div class=\"step\" id=\"s2\">\n      <button class=\"back-btn\" onclick=\"go(1)\" data-i18n=\"back\">Back<\/button>\n      <p class=\"sec-lbl\" data-i18n=\"step2_lbl\">02 \/ Household size<\/p>\n      <div class=\"prod-hero\" id=\"heroCard\"><\/div>\n      <p class=\"hh-q\" data-i18n=\"step2_q\">How many people use this product?<\/p>\n      <p class=\"hh-hint\" id=\"hhHint\"><\/p>\n      <div class=\"hh-row\" id=\"hhOpts\"><\/div>\n      <div id=\"previewWrap\" style=\"display:none\">\n        <div class=\"preview-banner\" id=\"previewBanner\"><\/div>\n      <\/div>\n      <button class=\"btn-p\" id=\"btnHH\" disabled onclick=\"go(3)\" data-i18n=\"step2_confirm\" style=\"display:none\">Confirm \u2192<\/button>\n    <\/div>\n    <!-- STEP 3 -->\n    <div class=\"step\" id=\"s3\">\n      <button class=\"back-btn\" onclick=\"goBackS3()\" data-i18n=\"back\">Back<\/button>\n      <p class=\"sec-lbl\" data-i18n=\"step3_lbl\">03 \/ Start date<\/p>\n      <label class=\"field-lbl\" for=\"startDate\" data-i18n=\"step3_q\">Date of your first application :<\/label>\n      <input type=\"date\" id=\"startDate\" onchange=\"onDate()\">\n      <div class=\"sum-card\" id=\"sumCard\" style=\"display:none\">\n        <div class=\"sum-row\"><span class=\"sum-k\" data-i18n=\"sum_product\">Product<\/span><span class=\"sum-v\" id=\"sv1\"><\/span><\/div>\n        <div class=\"sum-row\"><span class=\"sum-k\" data-i18n=\"sum_foyer\">Home<\/span><span class=\"sum-v\" id=\"sv2\"><\/span><\/div>\n        <div class=\"sum-row\"><span class=\"sum-k\" data-i18n=\"sum_freq\">Frequency<\/span><span class=\"sum-v hi\" id=\"sv3\"><\/span><\/div>\n        <div class=\"sum-row\"><span class=\"sum-k\" data-i18n=\"sum_reminders\">Recalls generated<\/span><span class=\"sum-v\" id=\"sv4\"><\/span><\/div>\n        <div class=\"sum-row\"><span class=\"sum-k\" data-i18n=\"sum_stockend\">Stock sold out on<\/span><span class=\"sum-v hi\" id=\"sv5\"><\/span><\/div>\n      <\/div>\n      <div class=\"warn-box\" id=\"warnBox\" style=\"display:none\">\n        <span class=\"warn-icon\">\u26a0<\/span>\n        <span data-i18n=\"warn_last\">The <strong>last reminder<\/strong> will contain a direct link to renew your stock on <strong>epurbiosolution.eu<\/strong>.<\/span>\n      <\/div>\n      <button class=\"btn-p\" id=\"btnGoExport\" disabled onclick=\"go(4)\" data-i18n=\"step3_cta\">Generate my calendar \u2192<\/button>\n    <\/div>\n    <!-- STEP 4 -->\n    <div class=\"step\" id=\"s4\">\n      <button class=\"back-btn\" onclick=\"go(3)\" data-i18n=\"back\">Back<\/button>\n      <p class=\"sec-lbl\" data-i18n=\"step4_lbl\">04 \/ Export to your calendar<\/p>\n      <div class=\"export-banner\" id=\"exportBanner\"><\/div>\n      <button class=\"btn-p\" id=\"btnICS\" onclick=\"dlICS()\" data-i18n=\"step4_ics\">\ud83d\udcc5 Add to my calendar<\/button>\n      <p class=\"compat-note\" data-i18n=\"step4_compat\">Compatible with iPhone - Android - Outlook - Apple Calendar<br>All reminders can be imported in a single action.<\/p>\n      <div class=\"divider\"><\/div>\n      <button class=\"btn-s\" onclick=\"go(5)\" data-i18n=\"step4_cta\">Receive an alert before my stock lasts \u2192<\/button>\n    <\/div>\n    <!-- STEP 5 -->\n    <div class=\"step\" id=\"s5\">\n      <button class=\"back-btn\" onclick=\"go(4)\" data-i18n=\"back\">Back<\/button>\n      <p class=\"sec-lbl\" data-i18n=\"step5_lbl\">05 \/ Renewal alert<\/p>\n      <div class=\"email-hero\">\n        <div class=\"email-hero-icon\">\n          <svg width=\"22\" height=\"22\" viewbox=\"0 0 24 24\" fill=\"none\">\n            <path d=\"M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z\" stroke=\"#3DBD93\" stroke-width=\"1.5\" stroke-linejoin=\"round\"\/>\n            <polyline points=\"22,6 12,13 2,6\" stroke=\"#3DBD93\" stroke-width=\"1.5\" stroke-linecap=\"round\"\/>\n          <\/svg>\n        <\/div>\n        <div>\n          <div class=\"email-hero-title\" data-i18n=\"step5_title\">Be alert before you run out!<\/div>\n          <div class=\"email-hero-sub\" data-i18n=\"step5_sub\">Receive an email alert <strong>7 days before<\/strong> your stock runs out.<\/div>\n        <\/div>\n      <\/div>\n      <div class=\"stock-fin-card\" id=\"stockFinCard\"><\/div>\n      <label class=\"field-lbl\" for=\"emailInput\" data-i18n=\"email_lbl\">Your email address<\/label>\n      <div class=\"email-field-wrap\">\n        <input type=\"email\" id=\"emailInput\" placeholder=\"vous@email.com\" oninput=\"onEmailInput()\" autocomplete=\"email\">\n        <div class=\"email-valid-dot\" id=\"emailDot\"><\/div>\n      <\/div>\n      <div class=\"email-opts\">\n        <label class=\"chk-row\" id=\"chkConsentRow\">\n          <input type=\"checkbox\" id=\"chkConsent\" onchange=\"onEmailInput()\">\n          <span class=\"chk-box\"><\/span>\n          <span class=\"chk-lbl\" data-i18n=\"consent_lbl\">\n  I agree that my email address will be used only to receive this renewal reminder.\n  No newsletter. In accordance with\n <a href=\"https:\/\/epurbiosolution.eu\/en\/privacy-policy\/\" target=\"_blank\" rel=\"noopener\" style=\"color:var(--g4);text-decoration:underline;\" data-i18n=\"consent_link\">Epur privacy policy<\/a>. \n          <\/span>\n        <\/label>\n      <\/div>\n      <button class=\"btn-p\" id=\"btnEmail\" disabled onclick=\"envoyerRappelEmail()\" data-i18n=\"email_btn\">Alert me by Email<\/button>\n      <div id=\"emailFeedback\" style=\"display:none\"><\/div>\n    <\/div>\n  <\/div>\n  <div class=\"brand-footer\">\n    <span class=\"brand-tag\" data-i18n=\"footer_tag\">Copyright \u00a9 2026 Epur Biosolution<\/span>\n  <\/div>\n<\/div>\n<\/div>\n\n<script>\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   i18n \u2014 FR \/ NL\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nconst I18N = {\n  fr: {\n    hdr_title:      \"Votre agenda d'entretien\",\n    hdr_sub:        \"Planifiez, exportez et ne manquez plus aucune dose.\",\n    step1_lbl:      \"01 \/ S\u00e9lectionnez votre produit\",\n    back:           \"Retour\",\n    step2_lbl:      \"02 \/ Taille de votre foyer\",\n    step2_q:        \"Combien de personnes utilisent ce produit ?\",\n    step2_lbl_D:    \"02 \/ Votre cuve de r\u00e9cup\u00e9ration\",\n    step2_q_D:      \"Quelle est la contenance de votre cuve ?\",\n    step2_confirm:  \"Confirmer \u2192\",\n    step3_lbl:      \"03 \/ Date de d\u00e9but\",\n    step3_q:        \"Date de votre premi\u00e8re application :\",\n    sum_product:    \"Produit\",\n    sum_foyer:      \"Foyer\",\n    sum_cuve:       \"Cuve\",\n    sum_freq:       \"Fr\u00e9quence\",\n    sum_reminders:  \"Rappels g\u00e9n\u00e9r\u00e9s\",\n    sum_stockend:   \"Stock \u00e9puis\u00e9 le\",\n    warn_last:      \"Le <strong>dernier rappel<\/strong> contiendra un lien direct pour renouveler votre stock sur <strong>epurbiosolution.eu<\/strong>.\",\n    step3_cta:      \"G\u00e9n\u00e9rer mon calendrier \u2192\",\n    step4_lbl:      \"04 \/ Exporter vers votre agenda\",\n    step4_ics:      \"\ud83d\udcc5 \\u00a0Ajouter \u00e0 mon calendrier\",\n    step4_compat:   \"Compatible iPhone \u00b7 Android \u00b7 Outlook \u00b7 Apple Calendar<br>Tous les rappels s'importent en une seule action.\",\n    step4_cta:      \"Recevoir une alerte avant la fin de mon stock \u2192\",\n    step5_lbl:      \"05 \/ Alerte de renouvellement\",\n    step5_title:    \"Soyez alert\u00e9 avant d'\u00eatre \u00e0 court !\",\n    step5_sub:      \"Recevez une alerte email <strong>7 jours avant<\/strong> l'\u00e9puisement de votre stock.\",\n    email_lbl:      \"Votre adresse email\",\n    email_placeholder: \"vous@email.com\",\n    consent_lbl:    \"J'accepte que mon adresse email soit utilis\u00e9e uniquement pour recevoir ce rappel de renouvellement. Aucune newsletter. Conform\u00e9ment \u00e0 la \",\n    consent_link:   \"politique de confidentialit\u00e9 Epur\",\n    email_btn:      \"M'alerter par Email\",\n    email_sending:  \"Envoi en cours\u2026\",\n    email_sent:     \"\u2713 Alerte enregistr\u00e9e\",\n    email_err:      \"\u26a1 Erreur ({msg}). V\u00e9rifiez votre connexion et r\u00e9essayez.\",\n    feedback_ok:    \"Alerte programm\u00e9e le <strong>{date}<\/strong> (J-7).<br>Un email sera envoy\u00e9 \u00e0 <strong>{email}<\/strong> avec le lien de r\u00e9achat.\",\n    footer_tag:     \"Copyright \u00a9 2026 Epur Biosolution\",\n    hh_hint_A:      \"La fr\u00e9quence d'utilisation d\u00e9pend du nombre de personnes dans votre foyer.\",\n    hh_hint_B:      \"1 dose\/semaine pour 1-4 pers. \u00b7 2 doses\/semaine au-del\u00e0.\",\n    hh_1_4:        \"1 \u2013 4 pers.\",\n    hh_5_8:        \"5 \u2013 8 pers.\",\n    hh_9p:         \"9+ pers.\",\n    hh_1_4_sub:    \"1 {unit}\/semaine\",\n    hh_5_8_sub:    \"2 {unit}s\/semaine\",\n    hh_9p_sub:     \"3 {unit}s\/semaine\",\n    hh_1_4b:       \"1 \u2013 4 pers.\",\n    hh_5pb:        \"5+ pers.\",\n    hh_1_4b_sub:   \"1 {unit}\/semaine\",\n    hh_5pb_sub:    \"2 {unit}s\/semaine\",\n    preview_A:     \"Fr\u00e9quence : <strong>{dpw} {unit}{s}\/semaine<\/strong> \u00b7 Stock estim\u00e9 : <strong>{sem} semaines<\/strong>\",\n    preview_B:     \"Fr\u00e9quence : <strong>{dpw} {unit}{s}\/semaine<\/strong> \u00b7 Stock estim\u00e9 : <strong>{sem} semaines<\/strong>\",\n    export_banner: \"\ud83d\udcc5 <strong>{n} rappels<\/strong> g\u00e9n\u00e9r\u00e9s pour <strong>{name}<\/strong><br>Du {start} au {end}\",\n    stock_card:    \"Produit : <strong>{name}<\/strong> \u00b7 {variant}<br>Traitement : <strong>{sem} semaine{sems}<\/strong> ({n} rappels)<br>Stock \u00e9puis\u00e9 le : <span class=\\\"sfdate\\\">{fin}<\/span><br>Alerte J-7 : <strong>{j7}<\/strong> \u00b7 Alerte J-1 : <strong>{j1}<\/strong>\",\n    gcal_title:    \"EPUR : Verser {lbl} ({num}\/{n})\",\n    gcal_details:  \"Produit Epur - {name}. Dose a verser.\\nRachat : {url}\\n\\nImportez le .ics pour tous les {n} rappels d'un coup.\",\n    email_subj:    \"Votre stock {name} arrive bient\u00f4t \u00e0 sa fin\",\n    email_body:    \"Bonjour,\\n\\nVotre stock de {name} ({variant}) sera \u00e9puis\u00e9 le {fin}.\\n\\nRecommandez d\u00e8s maintenant :\\n{url}\\n\\nL'\u00e9quipe Epur Bio Solution\",\n    ics_summary:   \"EPUR : Verser {lbl} ({num}\/{total})\",\n    ics_dose:      \"Produit Epur - {name}. Dose a verser.\",\n    ics_foyer:     \"Foyer : {foyer}.\",\n    ics_cuve:      \"Cuve : {cuve}.\",\n    ics_dosenum:   \"Dose {num} sur {total}.\",\n    ics_lastdose:  \"DERNIERE DOSE - Pensez a renouveler votre stock !\",\n    ics_reorder:   \"Pour racheter votre produit :\",\n    ics_alarm:     \"EPUR : {lbl} a verser aujourd'hui\",\n    ics_alarm_last:\"EPUR - Derniere dose demain. Pensez a renouveler !\",\n    locale:        \"fr-FR\",\n    fmtLong:       {weekday:\"long\",day:\"numeric\",month:\"long\",year:\"numeric\"},\n    \/* Produits \u2014 noms & variantes *\/\n    p_fosse_4m_sach_name:    \"Activateur Fosses Septiques\",\n    p_fosse_4m_sach_variant: \"4 mois \u00b7 18 sachets\",\n    p_fosse_5m_sach_name:    \"Activateur Fosses Septiques\",\n    p_fosse_5m_sach_variant: \"5 mois \u00b7 22 sachets\",\n    p_fosse_6m_dose_name:    \"Activateur Fosses Septiques\",\n    p_fosse_6m_dose_variant: \"6 mois \u00b7 30 doses\",\n    p_fosse_12m_sach_name:   \"Activateur Fosses Septiques\",\n    p_fosse_12m_sach_variant:\"12 mois \u00b7 54 sachets\",\n    p_fosse_12m_dose_name:   \"Activateur Fosses Septiques\",\n    p_fosse_12m_dose_variant:\"12 mois \u00b7 54 doses\",\n    p_micro_station_name:    \"Activateur Micro-station\",\n    p_micro_station_variant: \"6 mois \u00b7 26 doses\",\n    p_bac_graisses_name:     \"Entretien Bac \u00e0 Graisses\",\n    p_bac_graisses_variant:  \"6 mois \u00b7 26 doses\",\n    p_entretien_fosse_12m_name:    \"Entretien Fosses Microbilles\",\n    p_entretien_fosse_12m_variant: \"12 mois \u00b7 2 sachets\",\n    p_entretien_fosse_12m_fixed:   \"1 sachet tous les 6 mois\",\n    p_batons_cana_name:    \"B\u00e2tonnets Canalisations\",\n    p_batons_cana_variant: \"12 mois \u00b7 12 b\u00e2tonnets\",\n    p_batons_cana_fixed:   \"1 b\u00e2tonnet par mois\",\n\n    \/* Formats badges *\/\n    fmt_Sachet:   \"Sachets\",\n    fmt_Dose:     \"Doses\",\n    fmt_Batonnet: \"B\u00e2tonnets\",\n    fmt_fixed:    \"Fixe\",\n    fmt_variable: \"Selon foyer\",\n    \/* Consommation fixe *\/\n    fixed_consumption: \"Consommation fixe\",\n    \/* Fr\u00e9quence r\u00e9sum\u00e9 *\/\n    freq_per_week: \"{n} {unit}{s} \/ semaine\",\n    freq_per_days: \"1 \/ {days} j\",\n    \/* Rappels *\/\n    reminders_count: \"{n} rappel{s} ({sem} sem.)\",\n    \/* Groupe D \u2014 Cuve eaux de pluie *\/\n    hh_hint_D:      \"Choisissez la taille de votre cuve.\",\n    cuve_small:     \"Cuve jusqu'\u00e0 500 L\",\n    cuve_large:     \"Cuve \u00e0 partir de 1 000 L\",\n    cuve_small_sub: \"1 dose tous les 2 mois\",\n    cuve_large_sub: \"5 doses tous les 2 mois\",\n    preview_D:      \"{n} rappels sur <strong>{months} mois<\/strong> \u00b7 <strong>{dpw} {unit}{s}<\/strong> \u00e0 chaque application\",\n    freq_per_interval: \"{n} {unit}{s} tous les {days} jours\",\n    \/* Produits eaux de pluie *\/\n    p_eaux_pluie_name:    \"Traitement Eaux de Pluie\",\n    p_eaux_pluie_variant: \"33 doses\",\n    p_eaux_pluie_conc_name:    \"Traitement Eaux de Pluie Concentr\u00e9\",\n    p_eaux_pluie_conc_variant: \"14 sachets\",\n    p_eaux_pluie_conc_fixed:   \"1 sachet \/ 2 m\u00b3 \u00b7 tous les 2 mois\",\n  },\n  nl: {\n    hdr_title:      \"Uw onderhoudsagenda\",\n    hdr_sub:        \"Plan, exporteer en mis nooit meer een dosis.\",\n    step1_lbl:      \"01 \/ Selecteer uw product\",\n    back:           \"Terug\",\n    step2_lbl:      \"02 \/ Grootte van uw huishouden\",\n    step2_q:        \"Hoeveel personen gebruiken dit product?\",\n    step2_lbl_D:    \"02 \/ Uw regenwaterreservoir\",\n    step2_q_D:      \"Wat is de inhoud van uw reservoir?\",\n    step2_confirm:  \"Bevestigen \u2192\",\n    step3_lbl:      \"03 \/ Startdatum\",\n    step3_q:        \"Datum van uw eerste toediening :\",\n    sum_product:    \"Product\",\n    sum_foyer:      \"Huishouden\",\n    sum_cuve:       \"Reservoir\",\n    sum_freq:       \"Frequentie\",\n    sum_reminders:  \"Herinneringen aangemaakt\",\n    sum_stockend:   \"Voorraad op\",\n    warn_last:      \"De <strong>laatste herinnering<\/strong> bevat een directe link om uw voorraad te vernieuwen op <strong>epurbiosolution.eu<\/strong>.\",\n    step3_cta:      \"Mijn kalender aanmaken \u2192\",\n    step4_lbl:      \"04 \/ Exporteren naar uw agenda\",\n    step4_ics:      \"\ud83d\udcc5 \\u00a0Toevoegen aan mijn kalender\",\n    step4_compat:   \"Compatibel met iPhone \u00b7 Android \u00b7 Outlook \u00b7 Apple Calendar<br>Alle herinneringen worden in \u00e9\u00e9n klik ge\u00efmporteerd.\",\n    step4_cta:      \"Ontvang een melding voor het einde van mijn voorraad \u2192\",\n    step5_lbl:      \"05 \/ Vernieuwingsmelding\",\n    step5_title:    \"Ontvang een waarschuwing voor uw voorraad op is!\",\n    step5_sub:      \"Ontvang een e-mailwaarschuwing <strong>7 dagen voor<\/strong> het einde van uw voorraad.\",\n    email_lbl:      \"Uw e-mailadres\",\n    email_placeholder: \"u@email.com\",\n    consent_lbl:    \"Ik aanvaard dat mijn e-mailadres uitsluitend wordt gebruikt voor deze herinnering. Geen nieuwsbrief. Conform het \",\n    consent_link:   \"privacybeleid van Epur\",\n    email_btn:      \"Mij per e-mail waarschuwen\",\n    email_sending:  \"Bezig met verzenden\u2026\",\n    email_sent:     \"\u2713 Melding geregistreerd\",\n    email_err:      \"\u26a1 Fout ({msg}). Controleer uw verbinding en probeer opnieuw.\",\n    feedback_ok:    \"Melding gepland op <strong>{date}<\/strong> (J-7).<br>Een e-mail wordt verzonden naar <strong>{email}<\/strong> met de bestelkoppeling.\",\n    footer_tag:     \"Copyright \u00a9 2026 Epur Biosolution\",\n    hh_hint_A:      \"De gebruiksfrequentie hangt af van het aantal personen in uw huishouden.\",\n    hh_hint_B:      \"1 dosis\/week voor 1-4 pers. \u00b7 2 doses\/week daarboven.\",\n    hh_1_4:        \"1 \u2013 4 pers.\",\n    hh_5_8:        \"5 \u2013 8 pers.\",\n    hh_9p:         \"9+ pers.\",\n    hh_1_4_sub:    \"1 {unit}\/week\",\n    hh_5_8_sub:    \"2 {unit}s\/week\",\n    hh_9p_sub:     \"3 {unit}s\/week\",\n    hh_1_4b:       \"1 \u2013 4 pers.\",\n    hh_5pb:        \"5+ pers.\",\n    hh_1_4b_sub:   \"1 {unit}\/week\",\n    hh_5pb_sub:    \"2 {unit}s\/week\",\n    preview_A:     \"Frequentie : <strong>{dpw} {unit}{s}\/week<\/strong> \u00b7 Geschatte stock : <strong>{sem} weken<\/strong>\",\n    preview_B:     \"Frequentie : <strong>{dpw} {unit}{s}\/week<\/strong> \u00b7 Geschatte stock : <strong>{sem} weken<\/strong>\",\n    export_banner: \"\ud83d\udcc5 <strong>{n} herinneringen<\/strong> aangemaakt voor <strong>{name}<\/strong><br>Van {start} tot {end}\",\n    stock_card:    \"Product : <strong>{name}<\/strong> \u00b7 {variant}<br>Behandeling : <strong>{sem} week{sems}<\/strong> ({n} herinneringen)<br>Voorraad op : <span class=\\\"sfdate\\\">{fin}<\/span><br>Melding J-7 : <strong>{j7}<\/strong> \u00b7 Melding J-1 : <strong>{j1}<\/strong>\",\n    gcal_title:    \"EPUR : Doseer {lbl} ({num}\/{n})\",\n    gcal_details:  \"Epur product - {name}. Te doseren.\\nBestellen : {url}\\n\\nImporteer het .ics-bestand voor alle {n} herinneringen tegelijk.\",\n    email_subj:    \"Uw voorraad {name} raakt binnenkort op\",\n    email_body:    \"Goedendag,\\n\\nUw voorraad {name} ({variant}) is op {fin} uitgeput.\\n\\nBestel nu opnieuw:\\n{url}\\n\\nHet Epur Bio Solution team\",\n    ics_summary:   \"EPUR : Doseer {lbl} ({num}\/{total})\",\n    ics_dose:      \"Epur product - {name}. Te doseren.\",\n    ics_foyer:     \"Huishouden : {foyer}.\",\n    ics_cuve:      \"Reservoir : {cuve}.\",\n    ics_dosenum:   \"Dosis {num} van {total}.\",\n    ics_lastdose:  \"LAATSTE DOSIS - Vergeet uw voorraad niet te vernieuwen!\",\n    ics_reorder:   \"Uw product opnieuw bestellen:\",\n    ics_alarm:     \"EPUR : {lbl} vandaag doseren\",\n    ics_alarm_last:\"EPUR - Laatste dosis morgen. Vergeet niet te vernieuwen!\",\n    locale:        \"nl-BE\",\n    fmtLong:       {weekday:\"long\",day:\"numeric\",month:\"long\",year:\"numeric\"},\n    \/* Produits \u2014 noms & variantes *\/\n    p_fosse_4m_sach_name:    \"Activator Septische Putten\",\n    p_fosse_4m_sach_variant: \"4 maanden \u00b7 18 zakjes\",\n    p_fosse_5m_sach_name:    \"Activator Septische Putten\",\n    p_fosse_5m_sach_variant: \"5 maanden \u00b7 22 zakjes\",\n    p_fosse_6m_dose_name:    \"Activator Septische Putten\",\n    p_fosse_6m_dose_variant: \"6 maanden \u00b7 30 doses\",\n    p_fosse_12m_sach_name:   \"Activator Septische Putten\",\n    p_fosse_12m_sach_variant:\"12 maanden \u00b7 54 zakjes\",\n    p_fosse_12m_dose_name:   \"Activator Septische Putten\",\n    p_fosse_12m_dose_variant:\"12 maanden \u00b7 54 doses\",\n    p_micro_station_name:    \"Activator Ministation\",\n    p_micro_station_variant: \"6 maanden \u00b7 26 doses\",\n    p_bac_graisses_name:     \"Onderhoud Vetafscheider\",\n    p_bac_graisses_variant:  \"6 maanden \u00b7 26 doses\",\n    p_entretien_fosse_12m_name:    \"Onderhoud Putten Microkorrels\",\n    p_entretien_fosse_12m_variant: \"12 maanden \u00b7 2 zakjes\",\n    p_entretien_fosse_12m_fixed:   \"1 zakje om de 6 maanden\",\n    p_batons_cana_name:    \"Sticks Afvoerleidingen\",\n    p_batons_cana_variant: \"12 maanden \u00b7 12 sticks\",\n    p_batons_cana_fixed:   \"1 stick per maand\",\n\n    \/* Formats badges *\/\n    fmt_Sachet:   \"Zakjes\",\n    fmt_Dose:     \"Doses\",\n    fmt_Batonnet: \"Sticks\",\n    fmt_fixed:    \"Vast\",\n    fmt_variable: \"Volgens huishouden\",\n    \/* Consommation fixe *\/\n    fixed_consumption: \"Vaste consumptie\",\n    \/* Fr\u00e9quence r\u00e9sum\u00e9 *\/\n    freq_per_week: \"{n} {unit}{s} \/ week\",\n    freq_per_days: \"1 \/ {days} d\",\n    \/* Rappels *\/\n    reminders_count: \"{n} herinnering{s} ({sem} wk.)\",\n    \/* Groep D \u2014 Regenwaterreservoir *\/\n    hh_hint_D:      \"Kies de grootte van uw reservoir.\",\n    cuve_small:     \"Reservoir tot 500 L\",\n    cuve_large:     \"Reservoir vanaf 1 000 L\",\n    cuve_small_sub: \"1 dosis om de 2 maanden\",\n    cuve_large_sub: \"5 doses om de 2 maanden\",\n    preview_D:      \"{n} herinneringen over <strong>{months} maanden<\/strong> \u00b7 <strong>{dpw} {unit}{s}<\/strong> per toediening\",\n    freq_per_interval: \"{n} {unit}{s} om de {days} dagen\",\n    \/* Producten regenwater *\/\n    p_eaux_pluie_name:    \"Behandeling Regenwater\",\n    p_eaux_pluie_variant: \"33 doses\",\n    p_eaux_pluie_conc_name:    \"Geconcentreerde behandeling van regenwater\",\n    p_eaux_pluie_conc_variant: \"14 zakjes\",\n    p_eaux_pluie_conc_fixed:   \"1 zakje \/ 2 m\u00b3 \u00b7 om de 2 maanden\",\n  }\n};\n\nlet LANG = 'fr';\n\nfunction setLang(l){\n  LANG = l;\n  document.getElementById('btnFR').classList.toggle('active', l==='fr');\n  document.documentElement.lang = l==='nl' ? 'nl' : 'fr';\n  document.getElementById('btnNL').classList.toggle('active', l==='nl');\n  const t = I18N[l];\n  \/\/ Header\n  document.getElementById('hdrTitle').textContent = t.hdr_title;\n  document.getElementById('hdrSub').textContent   = t.hdr_sub;\n  \/\/ Tous les \u00e9l\u00e9ments data-i18n statiques\n  document.querySelectorAll('[data-i18n]').forEach(el => {\n    const key = el.getAttribute('data-i18n');\n    if (t[key] !== undefined) el.innerHTML = t[key];\n  });\n  \/\/ Placeholder email + reset formulaire\n  const ei = document.getElementById('emailInput');\n  if (ei){ ei.placeholder = t.email_placeholder; ei.value=''; ei.className=''; }\n  \/\/ Consent label (reconstruit avec lien)\n  const cl = document.querySelector('.chk-lbl');\n  if (cl) cl.innerHTML = t.consent_lbl +\n    `<a href=\"https:\/\/epurbiosolution.eu\/en\/privacy-policy\/\" target=\"_blank\" rel=\"noopener\" style=\"color:var(--g4);text-decoration:underline;\">${t.consent_link}<\/a>.`;\n  \/\/ Reconstruire la grille produits traduite\n  rebuildGrid();\n  \/\/ Reset formulaire email\n  const dot = document.getElementById('emailDot');\n  if(dot){ dot.className='email-valid-dot'; }\n  const fb = document.getElementById('emailFeedback');\n  if(fb){ fb.style.display='none'; }\n  const btnE = document.getElementById('btnEmail');\n  if(btnE){ btnE.disabled=true; btnE.textContent=I18N[l].email_btn||''; }\n  const chk = document.getElementById('chkConsent');\n  if(chk){ chk.checked=false; }\n  \/\/ Re-render dynamic zones si d\u00e9j\u00e0 g\u00e9n\u00e9r\u00e9s\n  if (ST.prodId) {\n    const p = PRODUCTS.find(x=>x.id===ST.prodId);\n    if (p && (p.groupe===\"A\"||p.groupe===\"B\")) buildHH(p);\n    if (ST.dates && ST.dates.length) {\n      renderSummary(p);\n      buildExportBanner();\n      buildStep5();\n    }\n    if (ST.hhKey) buildPreview();\n  }\n}\n\nfunction T(key, vars){\n  let s = (I18N[LANG][key] || I18N['fr'][key] || key);\n  if (vars) Object.entries(vars).forEach(([k,v])=>{ s=s.replaceAll('{'+k+'}',v); });\n  return s;\n}\n\/* Traduit un champ produit (name, variant, fixedLabel) *\/\nfunction pT(p, field){\n  const key = `p_${p.id}_${field}`;\n  return I18N[LANG][key] || I18N['fr'][key] || p[field] || '';\n}\n\/* Retourne l'URL de rachat selon la langue active *\/\nfunction pUrl(p){\n  return (LANG === 'nl' && p.lienRachatNL) ? p.lienRachatNL : p.lienRachat;\n}\nfunction fmtLong(d){ return d.toLocaleDateString(I18N[LANG].locale, I18N[LANG].fmtLong); }\nfunction fmtShort(d){ return d.toLocaleDateString(I18N[LANG].locale,{day:'2-digit',month:'2-digit',year:'numeric'}); }\n<\/script>\n\n<script>\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   CATALOGUE PRODUITS \u2014 Source de v\u00e9rit\u00e9 unique\n   groupe A : Fosses septiques  (1-4 \/ 5-8 \/ +8 pers)\n   groupe B : Micro-station & Bac \u00e0 graisses  (1-4 \/ +4 pers)\n   groupe C : Produits fixes  (pas de s\u00e9lecteur foyer)\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nconst PRODUCTS = [\n\n  \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n     GROUPE A \u2014 Activateur Fosses Septiques\n     R\u00e8gle foyer : 1-4 pers = 1\/sem | 5-8 pers = 2\/sem | +8 pers = 3\/sem\n     DEUX FORMATS distincts pour le 12 mois \u2014 images et liens diff\u00e9rents\n  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n\n  \/* Format SACHET \u2014 4 mois \u00b7 lien et image : gamme sachets *\/\n  {\n    id:         \"fosse_4m_sach\",\n    groupe:     \"A\",\n    name:       \"Activateur Fosses Septiques\",\n    variant:    \"4 mois \u00b7 18 sachets\",\n    format:     \"Sachet\",\n    stock:      18,\n    unit:       \"sachet\",\n    imageURL:   \"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2024\/03\/1006800-Activateur-fossses-4-mois-1.png\",\n    shopURL:    \"https:\/\/epurbiosolution.eu\/produit\/activateur-fosses-en-sachets\/\",\n    lienRachat: \"https:\/\/epurbiosolution.eu\/produit\/activateur-fosses-en-sachets\/\",\n    lienRachatNL: \"https:\/\/epurbiosolution.eu\/nl\/product\/activator-septische-putten-zakjes\/\"\n  },\n\n  \/* Format SACHET \u2014 5 mois \u00b7 lien et image : gamme sachets *\/\n  {\n    id:         \"fosse_5m_sach\",\n    groupe:     \"A\",\n    name:       \"Activateur Fosses Septiques\",\n    variant:    \"5 mois \u00b7 22 sachets\",\n    format:     \"Sachet\",\n    stock:      22,\n    unit:       \"sachet\",\n    imageURL:   \"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2024\/04\/1006850-Activateur-fossses-4-mois-1-gratuit.png\",\n    shopURL:    \"https:\/\/epurbiosolution.eu\/produit\/activateur-fosses-en-sachets\/\",\n    lienRachat: \"https:\/\/epurbiosolution.eu\/produit\/activateur-fosses-en-sachets\/\",\n    lienRachatNL: \"https:\/\/epurbiosolution.eu\/nl\/product\/activator-septische-putten-zakjes\/\"\n  },\n\n  \/* Format DOSE \u2014 6 mois \u00b7 lien et image : gamme doses *\/\n  {\n    id:         \"fosse_6m_dose\",\n    groupe:     \"A\",\n    name:       \"Activateur Fosses Septiques\",\n    variant:    \"6 mois \u00b7 30 doses\",\n    format:     \"Dose\",\n    stock:      30,\n    unit:       \"dose\",\n    imageURL:   \"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2024\/03\/1002800-Activateur-fosses-6-mois-1.png\",\n    shopURL:    \"https:\/\/epurbiosolution.eu\/produit\/activateur-fosses\/\",\n    lienRachat: \"https:\/\/epurbiosolution.eu\/produit\/activateur-fosses\/\",\n    lienRachatNL: \"https:\/\/epurbiosolution.eu\/nl\/product\/activator-septische-putten\/\"\n  },\n\n  \/* Format SACHET \u2014 12 mois \u00b7 54 sachets \u00b7 lien et image : gamme sachets (pot 1,5 kg) *\/\n  {\n    id:         \"fosse_12m_sach\",\n    groupe:     \"A\",\n    name:       \"Activateur Fosses Septiques\",\n    variant:    \"12 mois \u00b7 54 sachets\",\n    format:     \"Sachet\",\n    stock:      54,\n    unit:       \"sachet\",\n    imageURL:   \"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2024\/04\/1007800-Activateur-fosses-12-mois-54-sachets.png\",\n    shopURL:    \"https:\/\/epurbiosolution.eu\/produit\/activateur-fosses-en-sachets\/\",\n    lienRachat: \"https:\/\/epurbiosolution.eu\/produit\/activateur-fosses-en-sachets\/\",\n    lienRachatNL: \"https:\/\/epurbiosolution.eu\/nl\/product\/activator-septische-putten-zakjes\/\"\n  },\n\n  \/* Format DOSE \u2014 12 mois \u00b7 54 doses \u00b7 image pot doses *\/\n  {\n    id:         \"fosse_12m_dose\",\n    groupe:     \"A\",\n    name:       \"Activateur Fosses Septiques\",\n    variant:    \"12 mois \u00b7 54 doses\",\n    format:     \"Dose\",\n    stock:      54,\n    unit:       \"dose\",\n    imageURL:   \"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2024\/04\/1003800-Activateur-fossses-12-mois-15Kg.png\",\n    shopURL:    \"https:\/\/epurbiosolution.eu\/produit\/activateur-fosses\/\",\n    lienRachat: \"https:\/\/epurbiosolution.eu\/produit\/activateur-fosses\/\",\n    lienRachatNL: \"https:\/\/epurbiosolution.eu\/nl\/product\/activator-septische-putten\/\"\n  },\n\n  \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n     GROUPE B \u2014 Micro-station & Bac \u00e0 graisses   (Format DOSE)\n     R\u00e8gle foyer : 1-4 pers = 1 dose\/sem | +4 pers = 2 doses\/sem\n  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n\n  {\n    id:         \"micro_station\",\n    groupe:     \"B\",\n    name:       \"Activateur Micro-station\",\n    variant:    \"6 mois \u00b7 26 doses\",\n    format:     \"Dose\",\n    stock:      26,\n    unit:       \"dose\",\n    imageURL:   \"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2024\/03\/1009050-Activateur-micro-station-900g-1.png\",\n    shopURL:    \"https:\/\/epurbiosolution.eu\/produit\/activateur-micro-station\/\",\n    lienRachat: \"https:\/\/epurbiosolution.eu\/produit\/activateur-micro-station\/\",\n    lienRachatNL: \"https:\/\/epurbiosolution.eu\/nl\/product\/activator-ministation\/\"\n  },\n\n  {\n    id:         \"bac_graisses\",\n    groupe:     \"B\",\n    name:       \"Entretien Bac \u00e0 Graisses\",\n    variant:    \"6 mois \u00b7 26 doses\",\n    format:     \"Dose\",\n    stock:      26,\n    unit:       \"dose\",\n    imageURL:   \"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2024\/03\/1007950-Entretien-bac-a-graisses-750g-1.png\",\n    shopURL:    \"https:\/\/epurbiosolution.eu\/produit\/entretien-bac-a-graisses\/\",\n    lienRachat: \"https:\/\/epurbiosolution.eu\/produit\/entretien-bac-a-graisses\/\",\n    lienRachatNL: \"https:\/\/epurbiosolution.eu\/nl\/product\/onderhoud-vetafscheider\/\"\n  },\n\n  \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n     GROUPE C \u2014 Produits fixes (pas de s\u00e9lecteur foyer)\n  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n\n  \/* Entretien Fosses Microbilles 12 mois \u2014 2 sachets, 1 tous les 6 mois *\/\n  {\n    id:           \"entretien_fosse_12m\",\n    groupe:       \"C\",\n    name:         \"Entretien Fosses Microbilles\",\n    variant:      \"12 mois \u00b7 2 sachets\",\n    format:       \"Sachet\",\n    stock:        2,\n    unit:         \"sachet\",\n    intervalDays: 182,\n    fixedLabel:   \"1 sachet tous les 6 mois\",\n    imageURL:     \"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2024\/03\/1007850-Entretien-fosses-microbilles-1-an-2-x-200g-1.png\",\n    shopURL:      \"https:\/\/epurbiosolution.eu\/produit\/entretien-fosses-microbilles-1-an-d-entretien\/\",\n    lienRachat:   \"https:\/\/epurbiosolution.eu\/produit\/entretien-fosses-microbilles-1-an-d-entretien\/\",\n    lienRachatNL: \"https:\/\/epurbiosolution.eu\/nl\/product\/onderhoud-putten-microkorrels\/\"\n  },\n\n  \/* B\u00e2tonnets Canalisations \u2014 12 b\u00e2tonnets, 1 par mois *\/\n  {\n    id:           \"batons_cana\",\n    groupe:       \"C\",\n    name:         \"B\u00e2tonnets Canalisations\",\n    variant:      \"12 mois \u00b7 12 b\u00e2tonnets\",\n    format:       \"Batonnet\",\n    stock:        12,\n    unit:         \"batonnet\",\n    intervalDays: 30,\n    fixedLabel:   \"1 b\u00e2tonnet par mois\",\n    imageURL:     \"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2024\/03\/1008900-Batonnets-canalisations-12x2g-1.png\",\n    shopURL:      \"https:\/\/epurbiosolution.eu\/produit\/batonnets-canalisations\/\",\n    lienRachat:   \"https:\/\/epurbiosolution.eu\/produit\/batonnets-canalisations\/\",\n    lienRachatNL: \"https:\/\/epurbiosolution.eu\/nl\/product\/sticks-afvoerleidingen\/\"\n  },\n\n  \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n     GROUPE D \u2014 Traitement Eaux de Pluie (s\u00e9lecteur taille cuve)\n     Cuve \u2264500L  : 1 dose tous les 2 mois (dpw = 7\/60 \u2248 0.117)\n     Cuve \u22651000L : 5 doses tous les 2 mois \u2192 intervalDays=60, stock\/doses par rappel=5\n  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n  {\n    id:           \"eaux_pluie\",\n    groupe:       \"D\",\n    name:         \"Traitement Eaux de Pluie\",\n    variant:      \"33 doses\",\n    format:       \"Dose\",\n    stock:        33,\n    unit:         \"dose\",\n    intervalDays: 60,\n    imageURL:     \"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2024\/03\/1009010-Traitement-eaux-de-pluie-1Kg-1.png\",\n    shopURL:      \"https:\/\/epurbiosolution.eu\/produit\/traitement-eaux-de-pluie\/\",\n    lienRachat:   \"https:\/\/epurbiosolution.eu\/produit\/traitement-eaux-de-pluie\/\",\n    lienRachatNL: \"https:\/\/epurbiosolution.eu\/nl\/product\/behandeling-regenwater\/\"\n  },\n\n  \/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n     GROUPE C \u2014 Traitement Eaux de Pluie Formule Concentr\u00e9e (fixe)\n     1 sachet tous les 2 mois \u2014 consommation fixe\n  \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n  {\n    id:           \"eaux_pluie_conc\",\n    groupe:       \"C\",\n    name:         \"Traitement Eaux de Pluie Concentr\u00e9\",\n    variant:      \"14 sachets\",\n    format:       \"Sachet\",\n    stock:        14,\n    unit:         \"sachet\",\n    intervalDays: 60,\n    fixedLabel:   \"1 sachet \/ 2 m\u00b3 \u00b7 tous les 2 mois\",\n    imageURL:     \"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2024\/03\/1009020-Traitement-eaux-de-pluie-14-x-40g-1.png\",\n    shopURL:      \"https:\/\/epurbiosolution.eu\/produit\/traitement-eaux-de-pluie-formule-concentree\/\",\n    lienRachat:   \"https:\/\/epurbiosolution.eu\/produit\/traitement-eaux-de-pluie-formule-concentree\/\",\n    lienRachatNL: \"https:\/\/epurbiosolution.eu\/nl\/product\/geconcentreerde-behandeling-van-regenwater\/\"\n  }\n\n];\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   R\u00c8GLES DE FR\u00c9QUENCE PAR GROUPE\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nconst SCHEDULE_A = [\n  {key:\"1-4\", icon:\"\ud83c\udfe0\", dpw:1},\n  {key:\"5-8\", icon:\"\ud83c\udfe1\", dpw:2},\n  {key:\"+8\",  icon:\"\ud83c\udfd8\ufe0f\", dpw:3}\n];\nconst SCHEDULE_B = [\n  {key:\"1-4\", icon:\"\ud83c\udfe0\", dpw:1},\n  {key:\"+4\",  icon:\"\ud83c\udfe1\", dpw:2}\n];\n\/* Groupe D \u2014 Eaux de Pluie : taille de cuve\n   dpwPerRappel = doses vers\u00e9es \u00e0 chaque rappel (tous les intervalDays jours)\n   stockDoses   = nombre de rappels = floor(stock \/ dpwPerRappel) *\/\nconst SCHEDULE_D = [\n  {key:\"small\", icon:\"\", dpwPerRappel:1},\n  {key:\"large\", icon:\"\", dpwPerRappel:5}\n];\nfunction getSchedule(p){\n  if(p.groupe===\"A\") return SCHEDULE_A;\n  if(p.groupe===\"B\") return SCHEDULE_B;\n  if(p.groupe===\"D\") return SCHEDULE_D;\n  return null;\n}\nfunction scheduleLabel(sc, grp){\n  if(grp===\"A\"){\n    if(sc.key===\"1-4\") return T(\"hh_1_4\");\n    if(sc.key===\"5-8\") return T(\"hh_5_8\");\n    return T(\"hh_9p\");\n  } else if(grp===\"B\"){\n    if(sc.key===\"1-4\") return T(\"hh_1_4b\");\n    return T(\"hh_5pb\");\n  } else if(grp===\"D\"){\n    return T(\"cuve_\"+sc.key);\n  }\n  return sc.key;\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   MOTEUR CALCULER RAPPELS\n   nbEvenements = floor(stock \/ dpw) \u2014 s'arr\u00eate au dernier sachet\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction calculerRappels(produit, dpw, dateDebut) {\n  const start = new Date(dateDebut + \"T08:00:00\");\n  const dates = [];\n  if (produit.groupe === \"C\") {\n    \/* Fixe : 1 rappel tous les intervalDays jours, stock = nb rappels *\/\n    for (let i=0; i<produit.stock; i++)\n      dates.push(new Date(start.getTime() + i * produit.intervalDays * 86400000));\n    return dates;\n  }\n  if (produit.groupe === \"D\") {\n    \/* Cuve : dpw = dpwPerRappel, intervalle fixe (intervalDays), n = floor(stock\/dpwPerRappel) *\/\n    const n = Math.floor(produit.stock \/ dpw);\n    for (let i=0; i<n; i++)\n      dates.push(new Date(start.getTime() + i * produit.intervalDays * 86400000));\n    return dates;\n  }\n  const n = Math.floor(produit.stock \/ dpw);\n  const step = (7\/dpw) * 86400000;\n  for (let i=0; i<n; i++) dates.push(new Date(start.getTime() + i*step));\n  return dates;\n}\n\nfunction nbSemaines(produit, dpw) {\n  if (produit.groupe===\"C\")\n    return Math.round(produit.stock * produit.intervalDays \/ 7);\n  if (produit.groupe===\"D\")\n    return Math.round(Math.floor(produit.stock \/ dpw) * produit.intervalDays \/ 7);\n  return Math.floor(produit.stock \/ dpw);\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   STATE\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nconst ST = {prodId:null, hhKey:null, dpw:null, dates:[]};\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   INIT \/ REBUILD GRID PRODUITS\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction rebuildGrid(){\n  const grid = document.getElementById(\"prodGrid\");\n  const selId = ST.prodId; \/\/ garder la s\u00e9lection active\n  grid.innerHTML = \"\";\n\n  const formatStyle = {\n    \"Sachet\":   \"background:#1E5C1B;color:#fff;\",\n    \"Dose\":     \"background:#1E5C1B;color:#fff;\",\n    \"Batonnet\": \"background:#1E5C1B;color:#fff;\",\n  };\n\n  PRODUCTS.forEach(p => {\n    const needsHH  = p.groupe === \"A\" || p.groupe === \"B\";\n    const fmtStyle = formatStyle[p.format] || formatStyle[\"Sachet\"];\n    const fmtLabel = T(\"fmt_\" + p.format) || p.format;\n    const name    = pT(p, \"name\");\n    const variant = pT(p, \"variant\");\n\n    const el = document.createElement(\"div\");\n    el.className  = \"pcard\";\n    el.dataset.id = p.id;\n    if (selId === p.id) el.classList.add(\"sel\");\n    el.innerHTML  = `\n      <div class=\"pimg-wrap\">\n        <img decoding=\"async\" src=\"${p.imageURL}\" alt=\"${name}\" onerror=\"this.style.display='none';this.nextElementSibling.style.display='block'\">\n        <span class=\"fallback\">\ud83e\uddea<\/span>\n      <\/div>\n      <div class=\"pcard-name\">${name}<\/div>\n      <div class=\"pcard-variant\">${variant}<\/div>\n      <div style=\"display:flex;gap:4px;flex-wrap:wrap;justify-content:center;margin-top:4px;\">\n        <span class=\"vbadge\" style=\"${fmtStyle}\">${fmtLabel}<\/span>\n        \n      <\/div>`;\n    el.onclick = () => pickProd(p.id);\n    el.setAttribute(\"tabindex\", \"0\");\n    el.setAttribute(\"role\", \"button\");\n    el.onkeydown = (e) => { if(e.key===\"Enter\"||e.key===\" \"){ e.preventDefault(); pickProd(p.id); } };\n    grid.appendChild(el);\n  });\n}\n\n(function initGrid(){\n  rebuildGrid();\n  const sd = document.getElementById(\"startDate\");\n  sd.min   = todayStr();\n  sd.value = todayStr();   \/* pr\u00e9-rempli avec aujourd'hui *\/\n})();\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   NAVIGATION\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction go(n) {\n  document.querySelectorAll(\".step\").forEach(s => s.classList.remove(\"active\"));\n  document.getElementById(\"s\"+n).classList.add(\"active\");\n  [1,2,3,4,5].forEach(i => document.getElementById(\"ps\"+i).classList.toggle(\"active\", i<=n));\n  \/* Scroll vers le haut de la modal *\/\n  try{ document.querySelector(\".modal\").scrollTop=0; }catch(e){}\n  if (n===3) { onDate(); }  \/* recalcule si la date est d\u00e9j\u00e0 remplie *\/\n  if (n===4) buildExportBanner();\n  if (n===5) buildStep5();\n}\nfunction goBackS3(){ const p=PRODUCTS.find(x=>x.id===ST.prodId); go((p.groupe===\"A\"||p.groupe===\"B\"||p.groupe===\"D\")?2:1); }\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   STEP 1 \u2014 PRODUIT\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction pickProd(id) {\n  ST.prodId=id; ST.hhKey=null; ST.dpw=null; ST.dates=[];\n  \/\/ Reset email step si on change de produit\n  const btnE=document.getElementById('btnEmail');\n  if(btnE && btnE.textContent===T('email_sent')){ btnE.textContent=T('email_btn'); btnE.disabled=true; }\n  const fb=document.getElementById('emailFeedback');\n  if(fb){ fb.style.display='none'; }\n  document.querySelectorAll(\".pcard\").forEach(c=>c.classList.remove(\"sel\"));\n  document.querySelector(`.pcard[data-id=\"${id}\"]`).classList.add(\"sel\");\n  const p = PRODUCTS.find(x=>x.id===id);\n  if (p.groupe===\"A\"||p.groupe===\"B\"||p.groupe===\"D\"){ buildHH(p); go(2); }\n  else { ST.dpw = 1; go(3); } \/* groupe C : dpw=1, 1 rappel\/intervalDays *\/\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   STEP 2 \u2014 FOYER\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction buildHH(p) {\n  document.getElementById(\"heroCard\").innerHTML = `\n    <img decoding=\"async\" src=\"${p.imageURL}\" alt=\"${pT(p,'name')}\" onerror=\"this.style.display='none'\">\n    <div><div class=\"ph-name\">${pT(p,'name')}<\/div><div class=\"ph-meta\">${pT(p,'variant')}<\/div><\/div>`;\n  \/* Titre et question dynamiques selon le groupe *\/\n  const lbl2 = document.querySelector('[data-i18n=\"step2_lbl\"]');\n  const q2   = document.querySelector('[data-i18n=\"step2_q\"]');\n  if(p.groupe===\"D\"){\n    if(lbl2) lbl2.textContent = T(\"step2_lbl_D\");\n    if(q2)   q2.textContent   = T(\"step2_q_D\");\n  } else {\n    if(lbl2) lbl2.textContent = T(\"step2_lbl\");\n    if(q2)   q2.textContent   = T(\"step2_q\");\n  }\n  document.getElementById(\"hhHint\").textContent = T(p.groupe===\"A\"?\"hh_hint_A\":p.groupe===\"D\"?\"hh_hint_D\":\"hh_hint_B\");\n  const wrap = document.getElementById(\"hhOpts\"); wrap.innerHTML=\"\";\n  getSchedule(p).forEach(sc => {\n    const b = document.createElement(\"button\");\n    b.className=\"hh-opt\"; b.dataset.key=sc.key;\n    let lbl, sub;\n    if(p.groupe===\"A\"){\n      lbl = sc.key===\"1-4\"?T(\"hh_1_4\"):sc.key===\"5-8\"?T(\"hh_5_8\"):T(\"hh_9p\");\n      sub = sc.key===\"1-4\"?T(\"hh_1_4_sub\",{unit:p.unit}):sc.key===\"5-8\"?T(\"hh_5_8_sub\",{unit:p.unit}):T(\"hh_9p_sub\",{unit:p.unit});\n    } else if(p.groupe===\"D\"){\n      lbl = T(\"cuve_\"+sc.key);\n      sub = T(\"cuve_\"+sc.key+\"_sub\");\n    } else {\n      lbl = sc.key===\"1-4\"?T(\"hh_1_4b\"):T(\"hh_5pb\");\n      sub = sc.key===\"1-4\"?T(\"hh_1_4b_sub\",{unit:p.unit}):T(\"hh_5pb_sub\",{unit:p.unit});\n    }\n    b.innerHTML=`<span class=\"hh-ico\">${sc.icon}<\/span><div style=\"margin-top:2px\"><span class=\"hh-lbl\">${lbl}<\/span><span class=\"hh-dpw\"> ${sub}<\/span><\/div>`;\n    b.onclick = ()=>pickHH(sc.key, p);\n    wrap.appendChild(b);\n  });\n  document.getElementById(\"previewWrap\").style.display=\"none\";\n  document.getElementById(\"btnHH\").disabled=true;\n}\n\nfunction buildPreview(){\n  if(!ST.hhKey||!ST.prodId) return;\n  const p  = PRODUCTS.find(x=>x.id===ST.prodId);\n  const sc = getSchedule(p).find(s=>s.key===ST.hhKey);\n  if(!sc) return;\n  if(p.groupe===\"D\"){\n    const dpwPR = sc.dpwPerRappel;\n    const n = Math.floor(p.stock\/dpwPR);\n    const w = nbSemaines(p, dpwPR);\n    document.getElementById(\"previewBanner\").innerHTML =\n      T(\"preview_D\",{n, dpw:dpwPR, unit:p.unit, s:dpwPR>1?\"s\":\"\", sem:w, months:Math.round(n*p.intervalDays\/30)});\n    return;\n  }\n  const dpw = p.groupe===\"A\"||p.groupe===\"B\" ? sc.dpw : 1;\n  const w = nbSemaines(p, dpw);\n  const key = p.groupe===\"A\"?\"preview_A\":\"preview_B\";\n  document.getElementById(\"previewBanner\").innerHTML =\n    T(key,{dpw, unit:p.unit, s:dpw>1?\"s\":\"\", sem:w});\n}\n\nfunction pickHH(key, p) {\n  ST.hhKey=key;\n  const sc = getSchedule(p).find(s=>s.key===key);\n  \/* Groupe D : dpwPerRappel = doses\/rappel (intervalle fixe) *\/\n  ST.dpw = p.groupe===\"D\" ? sc.dpwPerRappel : sc.dpw;\n  document.querySelectorAll(\".hh-opt\").forEach(b=>b.classList.remove(\"sel\"));\n  document.querySelector(`.hh-opt[data-key=\"${key}\"]`).classList.add(\"sel\");\n  buildPreview();\n  document.getElementById(\"previewWrap\").style.display=\"block\";\n  document.getElementById(\"btnHH\").disabled=false;\n  \/* Avance automatiquement apr\u00e8s 600ms (feedback visuel puis transition) *\/\n  setTimeout(() => go(3), 600);\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   STEP 3 \u2014 DATE\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction onDate(){\n  const val = document.getElementById(\"startDate\").value;\n  if(!val){ document.getElementById(\"btnGoExport\").disabled=true; return; }\n  \/* Protection : si ST.dpw est null (groupe D sans choix cuve), bloquer *\/\n  const pCheck = PRODUCTS.find(x=>x.id===ST.prodId);\n  if(pCheck && (pCheck.groupe===\"A\"||pCheck.groupe===\"B\"||pCheck.groupe===\"D\") && !ST.dpw){\n    document.getElementById(\"btnGoExport\").disabled=true; return;\n  }\n  const p = PRODUCTS.find(x=>x.id===ST.prodId);\n  ST.dates = calculerRappels(p, ST.dpw, val);\n  renderSummary(p);\n  document.getElementById(\"sumCard\").style.display=\"block\";\n  document.getElementById(\"warnBox\").style.display=\"flex\";\n  document.getElementById(\"btnGoExport\").disabled=false;\n}\n\nfunction renderSummary(p){\n  const total = ST.dates.length;\n  const sem   = nbSemaines(p, ST.dpw);\n  const hhLbl = (p.groupe!==\"C\") ? scheduleLabel(getSchedule(p).find(s=>s.key===ST.hhKey)||{key:ST.hhKey}, p.groupe) : T(\"fixed_consumption\");\n  let freqLbl;\n  if(p.fixedLabel){\n    freqLbl = pT(p, \"fixed\");\n  } else if(p.groupe===\"D\"){\n    freqLbl = T(\"freq_per_interval\",{n:ST.dpw, unit:p.unit, s:ST.dpw>1?\"s\":\"\", days:p.intervalDays});\n  } else if(ST.dpw>=1){\n    freqLbl = T(\"freq_per_week\",{n:ST.dpw, unit:p.unit, s:ST.dpw>1?\"s\":\"\"});\n  } else {\n    freqLbl = T(\"freq_per_days\",{days:Math.round(p.intervalDays)});\n  }\n  \/* Adapter le label \"Foyer\" pour groupe D *\/\n  const foyerLbl = document.querySelector('[data-i18n=\"sum_foyer\"]');\n  if(foyerLbl) foyerLbl.textContent = p.groupe===\"D\" ? T(\"sum_cuve\") : T(\"sum_foyer\");\n  document.getElementById(\"sv1\").textContent=`${pT(p,'name')} \u2014 ${pT(p,'variant')}`;\n  document.getElementById(\"sv2\").textContent=hhLbl;\n  document.getElementById(\"sv3\").textContent=freqLbl;\n  document.getElementById(\"sv4\").textContent=T(\"reminders_count\",{n:total, s:total>1?\"s\":\"\", sem});\n  document.getElementById(\"sv5\").textContent=fmtLong(ST.dates[total-1]);\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   STEP 4 \u2014 EXPORT BANNER\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction buildExportBanner(){\n  const p   = PRODUCTS.find(x=>x.id===ST.prodId);\n  const n   = ST.dates.length;\n  const sem = nbSemaines(p, ST.dpw);\n  document.getElementById(\"exportBanner\").innerHTML =\n    T(\"export_banner\",{n, name:pT(p,\"name\"), start:fmtLong(ST.dates[0]), end:fmtLong(ST.dates[n-1])});\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   FONCTIONS UTILITAIRES ICS \u2014 RFC 5545\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n\n\/** Formate une Date JS en YYYYMMDDTHHMMSSZ (UTC Zulu) *\/\nfunction icsDate(d) {\n  return d.toISOString().replace(\/[-:.]\/g, \"\").slice(0, 15) + \"Z\";\n}\n\n\/**\n * Escape RFC 5545 \u00a73.3.11 pour les valeurs de type TEXT.\n * Escape : backslash \u2192 \\\\ | point-virgule \u2192 \\; | virgule \u2192 \\, | newline \u2192 \\n\n * NE PAS utiliser sur les valeurs URI (URL, lienRachat) \u2014 \u00e7a corrompt les :\/\/\n *\/\nfunction icsEscape(s) {\n  return String(s)\n    .replace(\/\\\\\/g,  \"\\\\\\\\\")\n    .replace(\/;\/g,   \"\\\\;\")\n    .replace(\/,\/g,   \"\\\\,\")\n    .replace(\/\\r\\n|\\r|\\n\/g, \"\\\\n\");\n}\n\n\/**\n * Folding RFC 5545 \u00a73.1 : coupe \u00e0 74 pour que la ligne de continuation\n * (SPACE + contenu) reste \u2264 75 octets.\n * \u00c0 utiliser sur SUMMARY, DESCRIPTION, VALARM DESCRIPTION.\n * NE PAS utiliser sur URL;VALUE=URI et X-ALT-DESC (URI\/HTML ne se plient pas).\n *\/\nfunction icsFold(line) {\n  const LIMIT = 74;\n  let out = \"\";\n  while (line.length > LIMIT) {\n    out  += line.slice(0, LIMIT) + \"\\r\\n \";\n    line  = line.slice(LIMIT);\n  }\n  return out + line;\n}\n\n\/** Construit \"NOM:valeur\\r\\n\" avec folding \u2014 pour les propri\u00e9t\u00e9s TEXT *\/\nfunction icsProp(name, value) {\n  return icsFold(name + \":\" + value) + \"\\r\\n\";\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   G\u00c9N\u00c9RATION ICS \u2014 TRIPLE COUVERTURE LIENS (iOS \/ Android \/ Outlook)\n   \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n   Chaque VEVENT contient trois champs compl\u00e9mentaires pour le lien :\n\n   \u2460 URL;VALUE=URI:<url>\n      \u2192 Champ officiel RFC 5545 \u00a73.8.4.6.\n        iOS Calendar l'affiche comme bouton \"Ouvrir le lien\" sous l'\u00e9v\u00e9nement.\n        macOS Calendar l'affiche dans la fiche d\u00e9tail.\n        R\u00e8gle : UNE SEULE ligne, valeur URI brute, jamais fold\u00e9e.\n\n   \u2461 DESCRIPTION:<texte>\\n<url>\n      \u2192 Android (Google Calendar, Samsung Calendar) lit le texte brut.\n        L'URL brute isol\u00e9e sur sa propre ligne (\\n ICS) est auto-d\u00e9tect\u00e9e\n        et rendue cliquable par le moteur de rendu Android.\n        R\u00e8gle : texte escap\u00e9 via icsEscape(), URL brute sans escape,\n                s\u00e9par\u00e9s par le caract\u00e8re litt\u00e9ral \\n (deux chars dans le fichier).\n\n   \u2462 X-ALT-DESC;FMTTYPE=text\/html:<html>\u2026<a href=\"<url>\">\u2026<\/a>\u2026<\/html>\n      \u2192 Outlook (Windows\/Mac) et certains clients Android lisent ce champ\n        propri\u00e9taire Microsoft. Le <a href> est cliquable directement.\n        R\u00e8gle : valeur HTML sur UNE SEULE ligne, jamais fold\u00e9e\n                (plier du HTML brise les balises et rend le champ inutilisable).\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction dlICS() {\n  \/* Feedback visuel imm\u00e9diat sur le bouton *\/\n  const btn = document.getElementById('btnICS');\n  if(btn){ btn.textContent = '\u23f3 ' + (LANG==='nl'?'Bezig\u2026':'G\u00e9n\u00e9ration\u2026'); btn.disabled=true; }\n\n  const p      = PRODUCTS.find(x => x.id === ST.prodId);\n  const total  = ST.dates.length;\n  const hhLbl  = (p.groupe === \"A\" || p.groupe === \"B\" || p.groupe === \"D\")\n    ? scheduleLabel(getSchedule(p).find(s=>s.key===ST.hhKey)||{key:ST.hhKey}, p.groupe)\n    : \"\";\n  const uLabel = (ST.dpw && ST.dpw > 1)\n    ? `${ST.dpw} ${p.unit}s`\n    : `1 ${p.unit}`;\n  const stamp  = icsDate(new Date());\n  const url = pUrl(p).trim().replace(\/[\\r\\n\\s]+\/g, \"\");\n\n  let cal = \"\";\n  cal += \"BEGIN:VCALENDAR\\r\\n\";\n  cal += \"VERSION:2.0\\r\\n\";\n  cal += `PRODID:-\/\/Epur Bio Solution\/\/Rappels Entretien v7\/\/${LANG.toUpperCase()}\\r\\n`;\n  cal += \"CALSCALE:GREGORIAN\\r\\n\";\n  cal += \"METHOD:PUBLISH\\r\\n\";\n  cal += icsProp(\"X-WR-CALNAME\", icsEscape(`EPUR - ${pT(p,\"name\")}`));\n  cal += `X-WR-CALDESC:${T(\"ics_reorder\")} epurbiosolution.eu\\r\\n`;\n  cal += \"X-WR-TIMEZONE:UTC\\r\\n\";\n\n  ST.dates.forEach((d, i) => {\n    const num    = i + 1;\n    const isLast = (num === total);\n    const dtS    = icsDate(d);\n    const dtE    = icsDate(new Date(d.getTime() + 3600000));\n    const uid    = `epur-${p.id}-${num}-${stamp}@epurbiosolution.eu`;\n\n    const summaryRaw = isLast\n      ? T(\"ics_summary\",{lbl:uLabel,num,total}).replace(\/\\)$\/,`) - ${T(\"ics_lastdose\").split(\" - \")[0]}`)\n      : T(\"ics_summary\",{lbl:uLabel,num,total});\n\n    const descSegments = [\n      icsEscape(T(\"ics_dose\",{name:pT(p,\"name\")})),\n      hhLbl ? icsEscape(p.groupe===\"D\" ? T(\"ics_cuve\",{cuve:hhLbl}) : T(\"ics_foyer\",{foyer:hhLbl})) : null,\n      icsEscape(T(\"ics_dosenum\",{num,total})),\n      isLast ? icsEscape(T(\"ics_lastdose\")) : null,\n      icsEscape(T(\"ics_reorder\")),\n      url\n    ].filter(x => x !== null).join(\"\\\\n\");\n\n    const htmlBody =\n      `<!DOCTYPE html><html><body>` +\n      `<p>${T(\"ics_dose\",{name:pT(p,\"name\")})}<\/p>` +\n      (hhLbl ? `<p>${p.groupe===\"D\" ? T(\"ics_cuve\",{cuve:hhLbl}) : T(\"ics_foyer\",{foyer:hhLbl})}<\/p>` : \"\") +\n      `<p>${T(\"ics_dosenum\",{num,total})}<\/p>` +\n      (isLast ? `<p><strong>${T(\"ics_lastdose\")}<\/strong><\/p>` : \"\") +\n      `<p>${T(\"ics_reorder\")} <a href=\"${url}\">${url}<\/a><\/p>` +\n      `<\/body><\/html>`;\n\n    cal += \"BEGIN:VEVENT\\r\\n\";\n    cal += icsProp(\"UID\",         uid);\n    cal += icsProp(\"DTSTAMP\",     stamp);\n    cal += icsProp(\"DTSTART\",     dtS);\n    cal += icsProp(\"DTEND\",       dtE);\n    cal += icsProp(\"SUMMARY\",     icsEscape(summaryRaw));\n    cal += icsProp(\"DESCRIPTION\", descSegments);\n    cal += \"URL;VALUE=URI:\" + url + \"\\r\\n\";\n    cal += \"X-ALT-DESC;FMTTYPE=text\/html:\" + htmlBody + \"\\r\\n\";\n    cal += icsProp(\"CATEGORIES\", isLast ? \"ENTRETIEN,RENOUVELLEMENT\" : \"ENTRETIEN\");\n\n    cal += \"BEGIN:VALARM\\r\\n\";\n    cal += \"TRIGGER:-PT1H\\r\\n\";\n    cal += \"ACTION:DISPLAY\\r\\n\";\n    cal += icsProp(\"DESCRIPTION\", icsEscape(T(\"ics_alarm\",{lbl:uLabel})));\n    cal += \"END:VALARM\\r\\n\";\n\n    if (isLast) {\n      cal += \"BEGIN:VALARM\\r\\n\";\n      cal += \"TRIGGER:-PT24H\\r\\n\";\n      cal += \"ACTION:DISPLAY\\r\\n\";\n      cal += icsProp(\"DESCRIPTION\", icsEscape(T(\"ics_alarm_last\")));\n      cal += \"END:VALARM\\r\\n\";\n    }\n\n    cal += \"END:VEVENT\\r\\n\";\n  });\n\n  cal += \"END:VCALENDAR\\r\\n\";\n\n  \/* \u2500\u2500 D\u00e9tection plateforme \u2500\u2500\n     iOS : tous les navigateurs (Safari, Chrome, Firefox\u2026) utilisent WebKit.\n     On d\u00e9tecte l'OS, pas le navigateur. \u2500\u2500 *\/\n  \/* Restaurer le bouton ICS *\/\n  setTimeout(() => {\n    if(btn){ btn.textContent = T('step4_ics'); btn.disabled=false; btn.innerHTML='\ud83d\udcc5 '+T('step4_ics'); }\n  }, 1500);\n\n  const isIOS     = \/iP(hone|ad|od)\/.test(navigator.userAgent) ||\n                    (navigator.platform === \"MacIntel\" && navigator.maxTouchPoints > 1);\n  const isAndroid = !isIOS && \/Android\/.test(navigator.userAgent);\n\n  if (isIOS) {\n    \/* iOS \u2014 Safari et Chrome iOS :\n       Le blob URL avec <a download> est ignor\u00e9 par iOS.\n       On encode en base64 et on cr\u00e9e un vrai lien cliquable avec data URI.\n       iOS intercepte text\/calendar et propose Calendrier.app. *\/\n    const b64  = btoa(unescape(encodeURIComponent(cal)));\n    const a    = document.createElement(\"a\");\n    a.href     = \"data:text\/calendar;charset=utf-8;base64,\" + b64;\n    a.download = `epur-${p.id}-${total}rappels.ics`;\n    a.style.display = \"none\";\n    document.body.appendChild(a);\n    a.click();\n    setTimeout(() => document.body.removeChild(a), 500);\n\n  } else if (isAndroid) {\n    \/* Android : blob URL + download *\/\n    const blob  = new Blob([cal], { type: \"text\/calendar;charset=utf-8\" });\n    const bUrl  = URL.createObjectURL(blob);\n    const a     = document.createElement(\"a\");\n    a.href      = bUrl;\n    a.download  = `epur-${p.id}-${total}rappels.ics`;\n    a.type      = \"text\/calendar\";\n    document.body.appendChild(a);\n    a.click();\n    document.body.removeChild(a);\n    setTimeout(() => URL.revokeObjectURL(bUrl), 5000);\n\n  } else {\n    \/* Desktop \u2014 Chrome, Firefox, Edge, Safari Mac *\/\n    const blob = new Blob([cal], { type: \"text\/calendar;charset=utf-8\" });\n    const a    = document.createElement(\"a\");\n    a.href     = URL.createObjectURL(blob);\n    a.download = `epur-${p.id}-${total}rappels.ics`;\n    document.body.appendChild(a);\n    a.click();\n    document.body.removeChild(a);\n    URL.revokeObjectURL(a.href);\n  }\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   GOOGLE CALENDAR \u2014 1\u00e8re dose\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction openGCal(){\n  const p = PRODUCTS.find(x=>x.id===ST.prodId);\n  const d = ST.dates[0], n=ST.dates.length;\n  const s = icsDate(d), e = icsDate(new Date(d.getTime()+3600000));\n  const lbl = ST.dpw&&ST.dpw>1?`${ST.dpw} ${p.unit}s`:`1 ${p.unit}`;\n  const title   = encodeURIComponent(T(\"gcal_title\",{lbl,num:1,n}));\n  const details = encodeURIComponent(T(\"gcal_details\",{name:pT(p,\"name\"),url:pUrl(p),n}));\n  window.open(`https:\/\/calendar.google.com\/calendar\/render?action=TEMPLATE&text=${title}&dates=${s}\/${e}&details=${details}`,\"_blank\");\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   STEP 5 \u2014 EMAIL FIDELISATION\n   Webhook : remplacez WEBHOOK_URL par Make \/ n8n \/ Zapier\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nconst WEBHOOK_URL = \"https:\/\/hooks.zapier.com\/hooks\/catch\/23023244\/unxxcxl\/\"; \/* Zapier \u2014 configur\u00e9 *\/\n\nfunction buildStep5(){\n  const p       = PRODUCTS.find(x=>x.id===ST.prodId);\n  const fin     = ST.dates[ST.dates.length-1];\n  const j7      = new Date(fin.getTime()-7*86400000);\n  const j1      = new Date(fin.getTime()-86400000);\n  const sem     = nbSemaines(p, ST.dpw);\n  document.getElementById(\"stockFinCard\").innerHTML = T(\"stock_card\",{\n    name:pT(p,\"name\"), variant:pT(p,\"variant\"),\n    sem, sems: LANG===\"nl\"?(sem>1?\"en\":\"\"):(sem>1?\"s\":\"\"),\n    n:ST.dates.length,\n    fin:fmtLong(fin), j7:fmtShort(j7), j1:fmtShort(j1)\n  });\n}\n\nfunction onEmailInput(){\n  const val     = document.getElementById(\"emailInput\").value.trim();\n  const dot     = document.getElementById(\"emailDot\");\n  const btn     = document.getElementById(\"btnEmail\");\n  const consent = document.getElementById(\"chkConsent\").checked;\n  const emailOk = \/^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$\/.test(val);\n  document.getElementById(\"emailInput\").classList.toggle(\"valid\",   emailOk);\n  document.getElementById(\"emailInput\").classList.toggle(\"invalid\", val.length>3&&!emailOk);\n  dot.classList.toggle(\"ok\",  emailOk);\n  dot.classList.toggle(\"bad\", val.length>3&&!emailOk);\n  \/* Le bouton n'est actif que si email valide ET consentement coch\u00e9 *\/\n  btn.disabled = !(emailOk && consent);\n  document.getElementById(\"emailFeedback\").style.display=\"none\";\n}\n\nasync function envoyerRappelEmail(){\n  const email   = document.getElementById(\"emailInput\").value.trim();\n  const consent = document.getElementById(\"chkConsent\").checked;\n  const p       = PRODUCTS.find(x=>x.id===ST.prodId);\n  const fin     = ST.dates[ST.dates.length-1];\n  const j7      = new Date(fin.getTime()-7*86400000);\n  const hhLbl   = p.groupe!==\"C\" ? scheduleLabel(getSchedule(p).find(s=>s.key===ST.hhKey)||{key:ST.hhKey}, p.groupe) : T(\"fixed_consumption\");\n  const btn     = document.getElementById(\"btnEmail\");\n  const fb      = document.getElementById(\"emailFeedback\");\n  if (!email || !ST.dates.length || !consent) return;\n\n  const payload = {\n    email_destinataire: email,\n    consentement_rgpd:  true,\n    produit_nom:        pT(p,\"name\"),\n    produit_variant:    pT(p,\"variant\"),\n    produit_foyer:      hhLbl,\n    nb_rappels:         ST.dates.length,\n    semaines:           nbSemaines(p, ST.dpw),\n    lien_rachat:        pUrl(p),\n    date_fin_stock:     fin.toLocaleDateString(I18N[LANG].locale),\n    date_alerte_j7:     j7.toLocaleDateString(I18N[LANG].locale),\n    date_fin_iso:       fin.toISOString().split(\"T\")[0],\n    date_alerte_iso:    j7.toISOString().split(\"T\")[0],\n    langue:             LANG,\n    email_sujet:        T(\"email_subj\",{name:pT(p,\"name\")}),\n    email_corps:        T(\"email_body\",{name:pT(p,\"name\"), variant:pT(p,\"variant\"), fin:fin.toLocaleDateString(I18N[LANG].locale), url:pUrl(p)})\n  };\n\n  btn.disabled=true; btn.textContent=T(\"email_sending\"); fb.style.display=\"none\";\n\n  try {\n    const res = await fetch(WEBHOOK_URL,{method:\"POST\",mode:\"no-cors\",headers:{\"Content-Type\":\"text\/plain\"},body:JSON.stringify(payload)});\n    if (res.ok||res.type===\"opaque\"){\n      btn.textContent=T(\"email_sent\");\n      fb.style.display=\"flex\"; fb.className=\"feedback-ok\";\n      fb.innerHTML=`<div class=\"fk-icon\">\u2713<\/div><div>${T(\"feedback_ok\",{date:j7.toLocaleDateString(I18N[LANG].locale), email})}<\/div>`;\n    } else { throw new Error(\"HTTP \"+res.status); }\n  } catch(err){\n    btn.disabled=false; btn.textContent=T(\"email_btn\");\n    fb.style.display=\"flex\"; fb.className=\"feedback-err\";\n    fb.innerHTML = T(\"email_err\",{msg:err.message});\n  }\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   UTILITAIRES\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction todayStr(){ return new Date().toISOString().split(\"T\")[0]; }\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Your maintenance schedule Plan, export and never miss a dose again. FR NL 01 \/ Select your product Back 02 \/ Household size How many people use this product? Confirm \u2192 Back 03 \/ Start date Date of your first application : Product Home Frequency Recalls generated Stock sold out on \u26a0 The last reminder [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"class_list":["post-8720","page","type-page","status-publish","hentry"],"blocksy_meta":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Your maintenance schedule - Uw onderhoudsagenda - Epur - Products - Biosolution<\/title>\n<meta name=\"description\" content=\"Your maintenance schedule Plan, export and never miss a dose again. FR NL 01 \/ Select your product Back 02 \/ Household size How many people use this\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/epurbiosolution.eu\/en\/reminder-calendar\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Your maintenance schedule - Uw onderhoudsagenda - Epur - Products - Biosolution\" \/>\n<meta property=\"og:description\" content=\"Your maintenance schedule Plan, export and never miss a dose again. FR NL 01 \/ Select your product Back 02 \/ Household size How many people use this\" \/>\n<meta property=\"og:url\" content=\"https:\/\/epurbiosolution.eu\/en\/reminder-calendar\/\" \/>\n<meta property=\"og:site_name\" content=\"Epur - Products - Biosolution\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-16T13:22:48+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/reminder-calendar\\\/\",\"url\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/reminder-calendar\\\/\",\"name\":\"Your maintenance schedule - Uw onderhoudsagenda - Epur - Products - Biosolution\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/#website\"},\"datePublished\":\"2026-04-02T12:49:47+00:00\",\"dateModified\":\"2026-04-16T13:22:48+00:00\",\"description\":\"Your maintenance schedule Plan, export and never miss a dose again. FR NL 01 \\\/ Select your product Back 02 \\\/ Household size How many people use this\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/reminder-calendar\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/reminder-calendar\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/reminder-calendar\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/home\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Your maintenance schedule &#8211; Uw onderhoudsagenda\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/\",\"name\":\"Epur Biosolution\",\"description\":\"Epur is an assortment of maintenance products ideal for homes, septic tanks and pipes. Formulated from enzymes, non-pathogenic bacteria and\\\/or materials of natural origin, these products are efficient and environmentally friendly.\",\"publisher\":{\"@id\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/#organization\",\"name\":\"Epur Biosolution\",\"url\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/epurbiosolution.eu\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/logo_Epur_biosolution.webp\",\"contentUrl\":\"https:\\\/\\\/epurbiosolution.eu\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/logo_Epur_biosolution.webp\",\"width\":500,\"height\":305,\"caption\":\"Epur Biosolution\"},\"image\":{\"@id\":\"https:\\\/\\\/epurbiosolution.eu\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Your maintenance schedule - Uw onderhoudsagenda - Epur - Products - Biosolution","description":"Your maintenance schedule Plan, export and never miss a dose again. FR NL 01 \/ Select your product Back 02 \/ Household size How many people use this","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/epurbiosolution.eu\/en\/reminder-calendar\/","og_locale":"en_US","og_type":"article","og_title":"Your maintenance schedule - Uw onderhoudsagenda - Epur - Products - Biosolution","og_description":"Your maintenance schedule Plan, export and never miss a dose again. FR NL 01 \/ Select your product Back 02 \/ Household size How many people use this","og_url":"https:\/\/epurbiosolution.eu\/en\/reminder-calendar\/","og_site_name":"Epur - Products - Biosolution","article_modified_time":"2026-04-16T13:22:48+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/epurbiosolution.eu\/en\/reminder-calendar\/","url":"https:\/\/epurbiosolution.eu\/en\/reminder-calendar\/","name":"Your maintenance schedule - Uw onderhoudsagenda - Epur - Products - Biosolution","isPartOf":{"@id":"https:\/\/epurbiosolution.eu\/en\/#website"},"datePublished":"2026-04-02T12:49:47+00:00","dateModified":"2026-04-16T13:22:48+00:00","description":"Your maintenance schedule Plan, export and never miss a dose again. FR NL 01 \/ Select your product Back 02 \/ Household size How many people use this","breadcrumb":{"@id":"https:\/\/epurbiosolution.eu\/en\/reminder-calendar\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/epurbiosolution.eu\/en\/reminder-calendar\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/epurbiosolution.eu\/en\/reminder-calendar\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/epurbiosolution.eu\/en\/home\/"},{"@type":"ListItem","position":2,"name":"Your maintenance schedule &#8211; Uw onderhoudsagenda"}]},{"@type":"WebSite","@id":"https:\/\/epurbiosolution.eu\/en\/#website","url":"https:\/\/epurbiosolution.eu\/en\/","name":"Epur Biosolution","description":"Epur is an assortment of maintenance products ideal for homes, septic tanks and pipes. Formulated from enzymes, non-pathogenic bacteria and\/or materials of natural origin, these products are efficient and environmentally friendly.","publisher":{"@id":"https:\/\/epurbiosolution.eu\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/epurbiosolution.eu\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/epurbiosolution.eu\/en\/#organization","name":"Epur Biosolution","url":"https:\/\/epurbiosolution.eu\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/epurbiosolution.eu\/en\/#\/schema\/logo\/image\/","url":"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2026\/04\/logo_Epur_biosolution.webp","contentUrl":"https:\/\/epurbiosolution.eu\/wp-content\/uploads\/2026\/04\/logo_Epur_biosolution.webp","width":500,"height":305,"caption":"Epur Biosolution"},"image":{"@id":"https:\/\/epurbiosolution.eu\/en\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/epurbiosolution.eu\/en\/wp-json\/wp\/v2\/pages\/8720","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/epurbiosolution.eu\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/epurbiosolution.eu\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/epurbiosolution.eu\/en\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/epurbiosolution.eu\/en\/wp-json\/wp\/v2\/comments?post=8720"}],"version-history":[{"count":96,"href":"https:\/\/epurbiosolution.eu\/en\/wp-json\/wp\/v2\/pages\/8720\/revisions"}],"predecessor-version":[{"id":9116,"href":"https:\/\/epurbiosolution.eu\/en\/wp-json\/wp\/v2\/pages\/8720\/revisions\/9116"}],"wp:attachment":[{"href":"https:\/\/epurbiosolution.eu\/en\/wp-json\/wp\/v2\/media?parent=8720"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}