1/* HomeZero mobile nav. 2 * Vanilla JS, idempotent. Replaces the Webflow hamburger nav on small screens 3 * (â¤991px). Works on the static homepage AND on React-rendered pages. 4 * 5 * - Hides the original .sect-nav on mobile via injected CSS. 6 * - Mounts a fixed top bar with logo + language switcher + hamburger. 7 * - Opens a right-side drawer with accordion sections, B2C/B2B2C tabs, 8 * and Pico + Contact CTAs. 9 * - Language switcher integrates with window.__hzI18n (hz-i18n.js). 10 */ 11(function () { 12 if (window.__hzMobileNav) return; 13 window.__hzMobileNav = true; 14 15 var FLAGS = { 16 nl: '<svg viewBox="0 0 30 21" width="22" height="15" style="display:block;border-radius:3px;box-shadow:0 0 0 1px rgba(0,0,0,.08)"><rect width="30" height="7" fill="#AE1C28"/><rect y="7" width="30" height="7" fill="#fff"/><rect y="14" width="30" height="7" fill="#21468B"/></svg>', 17 en: '<svg viewBox="0 0 60 30" width="22" height="15" style="display:block;border-radius:3px;box-shadow:0 0 0 1px rgba(0,0,0,.08)"><clipPath id="hzt"><path d="M0,0 v30 h60 v-30 z"/></clipPath><path d="M0,0 v30 h60 v-30 z" fill="#012169"/><path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/><path d="M0,0 L60,30 M60,0 L0,30" clip-path="url(#hzt)" stroke="#C8102E" stroke-width="4"/><path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/><path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/></svg>', 18 fr: '<svg viewBox="0 0 30 21" width="22" height="15" style="display:block;border-radius:3px;box-shadow:0 0 0 1px rgba(0,0,0,.08)"><rect width="10" height="21" fill="#0055A4"/><rect x="10" width="10" height="21" fill="#fff"/><rect x="20" width="10" height="21" fill="#EF4135"/></svg>', 19 de: '<svg viewBox="0 0 30 21" width="22" height="15" style="display:block;border-radius:3px;box-shadow:0 0 0 1px rgba(0,0,0,.08)"><rect width="30" height="7" fill="#000"/><rect y="7" width="30" height="7" fill="#DD0000"/><rect y="14" width="30" height="7" fill="#FFCE00"/></svg>', 20 }; 21 var LANGS = ["nl", "en", "de", "fr"]; 22 23 var SECTIONS = [ 24 { 25 id: "oplossingen", label: "Oplossingen", 26 groups: [ 27 { title: "Leads", items: [ 28 ["Lead Flows","/#sales"],["Lead Filter","/#sales"],["Rekenmodellen","/#sales"], 29 ["Website widget","/#sales"],["Huisscan","/#sales"],["Workflow","/#sales"], 30 ]}, 31 { title: "Sales", items: [ 32 ["Sales CRM","/#operations"],["Offerte Module","/#operations"], 33 ["Lead Management","/#operations"],["Sales Automations","/#operations"], 34 ["Adviseurs Planning","/#operations"],["Schouwformulieren","/#operations"], 35 ]}, 36 { title: "Ops", items: [ 37 ["Planning","/#services"],["Field App","/#services"],["Netwerkbeheer","/#services"], 38 ]}, 39 ], 40 }, 41 { 42 id: "voor-wie", label: "Voor wie", 43 groups: [ 44 { title: "Installateurs & bedrijven", items: [ 45 ["Zonnepaneel installateur","/use-cases/zonnepaneel-installateur"], 46 ["Warmtepomp installateur","/use-cases/warmtepomp-installateur"], 47 ["Airco installateur","/use-cases/airco-installateur"], 48 ["Thuisbatterij installateur","/use-cases/thuisbatterij-installateur"], 49 ["Isolatiebedrijf","/use-cases/isolatiebedrijf"], 50 ["Duurzame totaal installateur","/use-cases/duurzame-totaal-installateur"], 51 ["Installatiebedrijven","/use-cases/installatiebedrijven"], 52 ["Adviseurs","/use-cases/adviseurs"], 53 ]}, 54 { title: "B2B2C", items: [ 55 ["Groothandels","/use-cases/groothandels"],["Fabrikanten","/use-cases/fabrikanten"], 56 ["Vergelijkers","/use-cases/vergelijkers"],["Banken","/use-cases/banken"], 57 ["Verzekeraars","/use-cases/verzekeraars"],["Verenigingen","/use-cases/verenigingen"], 58 ]}, 59 ], 60 }, 61 { 62 id: "info", label: "Info", 63 groups: [ 64 { title: "Bedrijf", items: [ 65 ["Over ons","/over-ons"],["Pico","/project-pico"], 66 ["Vacatures","/vacatures"],["Blog","/blog"], 67 ]}, 68 ], 69 }, 70 ]; 71 72 var ICON_BURGER = '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M4 7h16M4 12h16M4 17h16"/></svg>'; 73 var ICON_CLOSE = '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>'; 74 var ICON_CHEV = '<svg class="hz-mnav-chev" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 9l6 6 6-6"/></svg>'; 75 76 function injectStyles() { 77 if (document.getElementById("hz-mnav-styles")) return; 78 var css = '' 79 + '.hz-mnav-root{display:none;}' 80 + '@media(max-width:991px){' 81 + '.sect-nav,.hz-nav{display:none!important;}' 82 + 'body{padding-top:64px;}' 83 + '.sect:first-of-type{padding-top:0!important;}' 84 + '.sect:first-of-type .hero-cont{padding-top:28px!important;}' 85 + '.hz-mnav-root{display:block;}' 86 + '.hz-mnav-bar{position:fixed;top:0;left:0;right:0;z-index:9000;height:64px;padding:0 14px;display:flex;align-items:center;justify-content:space-between;gap:10px;background:#fff;border-bottom:1px solid rgba(0,0,0,.06);box-shadow:0 2px 12px rgba(0,0,0,.04);}' 87 + '.hz-mnav-logo{display:inline-flex;align-items:center;flex:none;}' 88 + '.hz-mnav-logo img{height:60px;width:auto;display:block;}' 89 + '.hz-mnav-btn img{height:20px;width:auto;display:block;flex:none;}' 90 + '.hz-mnav-bar-right{display:flex;align-items:center;gap:8px;flex:none;margin-left:auto;}' 91 + '.hz-mnav-lang{position:relative;display:inline-block;}' 92 + '.hz-mnav-lang-btn{appearance:none;background:transparent;border:0;padding:6px;cursor:pointer;display:inline-flex;align-items:center;line-height:0;border-radius:8px;}' 93 + '.hz-mnav-lang-btn:hover{background:#f5f5f5;}' 94 + '.hz-mnav-lang-menu{position:absolute;
94left:0;top:calc(100% + 6px);background:#fff;color:#111;list-style:none;margin:0;padding:6px;border-radius:12px;box-shadow:0 12px 32px rgba(0,0,0,.18);border:1px solid rgba(0,0,0,.06);min-width:56px;z-index:10000;}' 95 + '.hz-mnav-lang-menu li{margin:0;padding:0;list-style:none;}' 96 + '.hz-mnav-lang-menu button{width:100%;padding:6px;border-radius:8px;border:0;background:transparent;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:0;}' 97 + '.hz-mnav-lang-menu button.active{background:rgba(0,0,0,.06);}' 98 + '.hz-mnav-toggle{appearance:none;background:#fff;color:#0a0a0a;border:1px solid rgba(0,0,0,.1);border-radius:10px;width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;}' 99 + '.hz-mnav-toggle:hover{background:#f5f5f5;}' 100 + '.hz-mnav-backdrop{position:fixed;inset:0;z-index:9001;background:rgba(0,0,0,.45);opacity:0;transition:opacity .2s ease;}' 101 + '.hz-mnav-backdrop.open{opacity:1;}' 102 + '.hz-mnav-drawer{position:fixed;top:0;right:0;bottom:0;width:min(360px,88vw);z-index:9002;background:#fff;color:#0a0a0a;transform:translateX(100%);transition:transform .25s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;overflow-y:auto;padding:76px 20px 24px;box-shadow:-16px 0 40px rgba(0,0,0,.18);}' 103 + '.hz-mnav-drawer.open{transform:translateX(0);}' 104 + '.hz-mnav-tabs{display:grid;grid-template-columns:1fr 1fr;background:#f3f4f6;border-radius:12px;padding:4px;margin-bottom:18px;}' 105 + '.hz-mnav-tab{text-align:center;padding:10px 0;font-size:14px;font-weight:600;color:#4b5563;text-decoration:none;border-radius:9px;}' 106 + '.hz-mnav-tab.active{background:#fff;color:#0a0a0a;box-shadow:0 1px 3px rgba(0,0,0,.06);}' 107 + '.hz-mnav-sections{display:flex;flex-direction:column;}' 108 + '.hz-mnav-section + .hz-mnav-section{border-top:1px solid rgba(0,0,0,.06);}' 109 + '.hz-mnav-section-head{width:100%;appearance:none;background:transparent;border:0;display:flex;align-items:center;justify-content:space-between;padding:14px 4px;font-size:16px;font-weight:600;color:#0a0a0a;
109cursor:pointer;}' 110 + '.hz-mnav-section-head .hz-mnav-chev{transition:transform .2s;}' 111 + '.hz-mnav-section.open .hz-mnav-chev{transform:rotate(180deg);}' 112 + '.hz-mnav-section-body{display:none;padding:4px 4px 14px;flex-direction:column;gap:16px;}' 113 + '.hz-mnav-section.open .hz-mnav-section-body{display:flex;}' 114 + '.hz-mnav-group-title{font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#6b7280;margin-bottom:6px;}' 115 + '.hz-mnav-group-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;}' 116 + '.hz-mnav-group-list li{margin:0;}' 117 + '.hz-mnav-group-list a{display:block;padding:10px 12px;margin-left:-12px;font-size:15px;color:#1f2937;text-decoration:none;border-radius:8px;}' 118 + '.hz-mnav-group-list a:hover{background:#f5f5f5;}' 119 + '.hz-mnav-cta{margin-top:auto;padding-top:20px;display:flex;flex-direction:column;gap:10px;}' 120 + '.hz-mnav-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:48px;padding:0 16px;border-radius:12px;font-size:15px;font-weight:600;text-decoration:none;}' 121 + '.hz-mnav-btn.ghost{background:#fff;color:#0a0a0a;border:1px solid rgba(0,0,0,.12);}' 122 + '.hz-mnav-btn.primary{background:linear-gradient(135deg,#2d8aff 0%,#1a56db 100%);color:#fff;box-shadow:0 6px 18px rgba(26,86,219,.25);}' 123 + '}'; 124 var s = document.createElement("style"); 125 s.id = "hz-mnav-styles"; 126 s.textContent = css; 127 document.head.appendChild(s); 128 } 129 130 function el(tag, attrs, html) { 131 var n = document.createElement(tag); 132 if (attrs) for (var k in attrs) { 133 if (k === "class") n.className = attrs[k]; 134 else if (k === "html") n.innerHTML = attrs[k]; 135 else n.setAttribute(k, attrs[k]); 136 } 137 if (html != null) n.innerHTML = html; 138 return n; 139 } 140 141 function getLang() { 142 try { return (window.__hzI18n && window.__hzI18n.get && window.__hzI18n.get()) || "nl"; } 143 catch (_) { return "nl"; } 144 } 145 function setLang(l) { 146 try { if (window.__hzI18n && window.__hzI18n.set) window.__hzI18n.set(l); } 147 catch (_) {} 148 } 149 150 function buildLangSwitcher() { 151 var wrap = el("div", { class: "hz-mnav-lang", "data-no-i18n": "" }); 152 var btn = el("button", { class: "hz-mnav-lang-btn", type: "button", "aria-label": "Taal kiezen", "aria-haspopup": "listbox", "aria-expanded": "false" }); 153 btn.innerHTML = FLAGS[getLang()] || FLAGS.nl; 154 wrap.appendChild(btn); 155 156 var menu = el("ul", { class: "hz-mnav-lang-menu", role: "listbox" });
157 LANGS.forEach(function (l) { 158 var li = el("li"); 159 var b = el("button", { type: "button", role: "option", "data-lang": l, title: l.toUpperCase() }); 160 b.innerHTML = FLAGS[l]; 161 if (l === getLang()) b.classList.add("active"); 162 b.addEventListener("click", function (e) { 163 e.stopPropagation(); 164 setLang(l); 165 btn.innerHTML = FLAGS[l]; 166 Array.prototype.forEach.call(menu.querySelectorAll("button"), function (x) { x.classList.toggle("active", x.getAttribute("data-lang") === l); }); 167 menu.style.display = "none"; 168 btn.setAttribute("aria-expanded", "false"); 169 }); 170 li.appendChild(b); 171 menu.appendChild(li); 172 }); 173 menu.style.display = "none"; 174 wrap.appendChild(menu); 175 176 btn.addEventListener("click", function (e) { 177 e.stopPropagation(); 178 var isOpen = menu.style.display === "block"; 179 menu.style.display = isOpen ? "none" : "block"; 180 btn.setAttribute("aria-expanded", String(!isOpen)); 181 }); 182 document.addEventListener("click", function (e) { 183 if (!wrap.contains(e.target)) { 184 menu.style.display = "none"; 185 btn.setAttribute("aria-expanded", "false"); 186 } 187 }); 188 189 // Sync flag if language changes elsewhere 190 try { 191 if (window.__hzI18n && window.__hzI18n.onChange) { 192 window.__hzI18n.onChange(function (l) { 193 btn.innerHTML = FLAGS[l] || FLAGS.nl; 194 Array.prototype.forEach.call(menu.querySelectorAll("button"), function (x) { x.classList.toggle("active", x.getAttribute("data-lang") === l); }); 195 }); 196 } 197 } catch (_) {} 198 return wrap; 199 } 200 201 function buildDrawer() { 202 var drawer = el("div", { class: "hz-mnav-drawer", "aria-hidden": "true" }); 203 var path = window.location.pathname; 204 var isB2B2C = path.indexOf("/b2b2c") === 0; 205 206 var tabs = el("div", { class: "hz-mnav-tabs", role: "tablist", "aria-label": "Doelgroep" }); 207 tabs.appendChild(el("a", { href: "/", class: "hz-mnav-tab" + (!isB2B2C ? " active" : "") }, "B2C")); 208 tabs.appendChild(el("a", { href: "/b2b2c", class: "hz-mnav-tab" + (isB2B2C ? " active" : "") }, "B2B2C")); 209 drawer.appendChild(tabs); 210 211 212 213 214 var secWrap = el("nav", { class: "hz-mnav-sections" }); 215 SECTIONS.forEach(function (sec) { 216 var s = el("div", { class: "hz-mnav-section", "data-sec": sec.id }); 217 var head = el("button", { type: "button", class: "hz-mnav-section-head", "aria-expanded": "false" }); 218 head.innerHTML = "<span>" + sec.label + "</span>" + ICON_CHEV; 219 s.appendChild(head); 220 var body = el("div", { class: "hz-mnav-section-body" }); 221 sec.groups.forEach(function (g) { 222 var gw = el("div", { class: "hz-mnav-group" }); 223 gw.appendChild(el("div", { class: "hz-mnav-group-title" }, g.title)); 224 var ul = el("ul", { class: "hz-mnav-group-list" }); 225 g.items.forEach(function (it) { 226 var li = el("li"); 227 li.appendChild(el("a", { href: it[1] }, it[0])); 228 ul.appendChild(li); 229 }); 230 gw.appendChild(ul); 231 body.appendChild(gw); 232 }); 233 s.appendChild(body); 234 head.addEventListener("click", function () { 235 var willOpen = !s.classList.contains("open"); 236 Array.prototype.forEach.call(secWrap.querySelectorAll(".hz-mnav-section"), function (n) { n.classList.remove("open"); n.querySelector(".hz-mnav-section-head").setAttribute("aria-expanded", "false"); }); 237 if (willOpen) { s.classList.add("open"); head.setAttribute("aria-expanded", "true"); } 238 }); 239 secWrap.appendChild(s); 240 }); 241 drawer.appendChild(secWrap); 242 243 var cta = el("div", { class: "hz-mnav-cta" }); 244 cta.innerHTML = 245 '<a href="https://pico.homezero.nl" class="hz-mnav-btn ghost"><img src="/images/pico-icon.png" alt=""/><span>Pico</span></a>' + 246 '<a href="/#contact-form" class="hz-mnav-btn primary">Contact</a>'; 247 drawer.appendChild(cta); 248 249 return drawer; 250 } 251 252 function mount() { 253 if (document.querySelector(".hz-mnav-root")) return; 254 injectStyles(); 255 var root = el("div", { class: "hz-mnav-root" }); 256 var bar = el("div", { class: "hz-mnav-bar" }); 257 bar.innerHTML = '<a href="/" class="hz-mnav-logo" aria-label="HomeZero home"><img src="/images/Frame-1996662659.png" alt="HomeZero"/></a>'; 258 var right = el("div", { class: "hz-mnav-bar-right" }); 259 right.appendChild(buildLangSwitcher()); 260 var toggle = el("button", { type: "button", class: "hz-mnav-toggle", "aria-label": "Menu openen", "aria-expanded": "false" }, ICON_BURGER); 261 right.appendChild(toggle); 262 bar.appendChild(right); 263 root.appendChild(bar); 264 265 var backdrop = el("div", { class: "hz-mnav-backdrop" }); 266 backdrop.style.display = "none"; 267 var drawer = buildDrawer(); 268 269 root.appendChild(backdrop); 270 root.appendChild(drawer); 271 document.body.appendChild(root); 272 273 var open = false;
274 function setOpen(v) { 275 open = v; 276 drawer.classList.toggle("open", v); 277 drawer.setAttribute("aria-hidden", String(!v)); 278 toggle.innerHTML = v ? ICON_CLOSE : ICON_BURGER; 279 toggle.setAttribute("aria-label", v ? "Menu sluiten" : "Menu openen"); 280 toggle.setAttribute("aria-expanded", String(v)); 281 if (v) { 282 backdrop.style.display = "block"; 283 requestAnimationFrame(function () { backdrop.classList.add("open"); }); 284 document.body.style.overflow = "hidden"; 285 } else { 286 backdrop.classList.remove("open"); 287 setTimeout(function () { if (!open) backdrop.style.display = "none"; }, 200); 288 document.body.style.overflow = ""; 289 } 290 } 291 toggle.addEventListener("click", function () { setOpen(!open); }); 292 backdrop.addEventListener("click", function () { setOpen(false); }); 293 document.addEventListener("keydown", function (e) { if (e.key === "Escape" && open) setOpen(false); }); 294 // Close drawer when an internal link is clicked 295 drawer.addEventListener("click", function (e) { 296 var t = e.target; 297 while (t && t !== drawer) { 298 if (t.tagName === "A") { setOpen(false); return; } 299 t = t.parentNode; 300 } 301 }); 302 } 303 304 if (document.readyState === "loading") { 305 document.addEventListener("DOMContentLoaded", mount); 306 } else { 307 mount(); 308 } 309})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.