1/* ============================================================ 2 Site-wide interactivity: 3 1. Sticky navbar â IntersectionObserver on the in-hero `.navbar-wrap`. 4 When it leaves the viewport, body gains `.is-scrolled` â the body-level 5 `.sticky-nav` slides down via CSS transition. 6 2. Mobile menu â any `[data-mobile-toggle]` button toggles `body.menu-open`. 7 Closes on ESC, on link click inside the overlay, and on backdrop click. 8 ============================================================ */ 9(function () { 10 'use strict'; 11 12 // Auto-inject fade-up op alle <section> elementen die nog geen data-aos hebben 13 document.querySelectorAll('section:not([data-aos]) > div').forEach(section => { 14 section.setAttribute('data-aos', 'fade-up'); 15 }); 16 17 AOS.init({ 18 duration: 800, 19 easing: 'ease-out-cubic', 20 once: true, 21 offset: 80, 22 }); 23 24 /* ---------- 1. Sticky navbar ------------------------------------------ */ 25 function initStickyNav() { 26 var sentinel = document.querySelector('.hero .navbar-wrap'); 27 var body = document.body; 28 if (!sentinel || !('IntersectionObserver' in window)) return; 29 30 var observer = new IntersectionObserver(function (entries) { 31 entries.forEach(function (entry) { 32 // intersectionRatio === 0 â fully out of view â show sticky 33 body.classList.toggle('is-scrolled', !entry.isIntersecting); 34 }); 35 }, { rootMargin: '0px', threshold: 0 }); 36 37 observer.observe(sentinel); 38 } 39 40 /* ---------- 2. Mobile menu toggle ------------------------------------- */ 41 function initMobileMenu() { 42 var body = document.body; 43 var toggles = document.querySelectorAll('[data-mobile-toggle]'); 44 var overlay = document.getElementById('mobile-menu'); 45 if (!toggles.length || !overlay) return; 46 47 function setOpen(open) { 48 body.classList.toggle('menu-open', open); 49 overlay.setAttribute('aria-hidden', open ? 'false' : 'true'); 50 // sync any hamburger triggers (the one in navbar) â close button inside the overlay stays at false 51 document.querySelectorAll('.navbar__hamburger').forEach(function (b) { 52 b.setAttribute('aria-expanded', open ? 'true' : 'false'); 53 }); 54 // Focus management: move focus into the menu when opening for keyboard users 55 if (open) { 56 var firstLink = overlay.querySelector('a, button'); 57 if (firstLink) firstLink.focus({ preventScroll: true }); 58 } 59 } 60 61 toggles.forEach(function (btn) { 62 btn.addEventListener('click', function () { 63 setOpen(!body.classList.contains('menu-open')); 64 }); 65 }); 66 67 // Close on ESC 68 document.addEventListener('keydown', function (e) { 69 if (e.key === 'Escape' && body.classList.contains('menu-open')) { 70 setOpen(false); 71 } 72 }); 73 74 // Close on link click inside overlay (so anchor + same-page navigation feels right) 75 overlay.addEventListener('click', function (e) { 76 var a = e.target.closest('a'); 77 if (a) setOpen(false); 78 }); 79 } 80 81 document.addEventListener('DOMContentLoaded', function () { 82 initStickyNav(); 83 initMobileMenu(); 84 }); 85})(); 86 87document.addEventListener('DOMContentLoaded', () => { 88 document.querySelectorAll('.btn--gradient').forEach(btn => { 89 // Skip als al verwerkt 90 if (btn.querySelector('.btn__eq')) return; 91 92 // Wrap bestaande tekst in een label-span 93 const label = document.createElement('span'); 94 label.className = 'btn__label'; 95 while (btn.firstChild) label.appendChild(btn.firstChild); 96 97 // EQ container met 22 bars 98 const eq = document.createElement('span'); 99 eq.className = 'btn__eq'; 100 eq.setAttribute('aria-hidden', 'true'); 101 102 for (let i = 0; i < 12; i++) { 103 const bar = document.createElement('span'); 104 bar.style.setProperty('--dur', (0.5 + Math.random() * 0.7).toFixed(2) + 's'); 105 bar.style.setProperty('--del', (Math.random() * -1.5).toFixed(2) + 's'); 106 eq.appendChild(bar); 107 } 108 109 btn.appendChild(eq); 110 btn.appendChild(label); 111 }); 112}); 113 114/* ========================================================================== 115 Featured-CTA overlay â ruimte eronder precies passend maken. 116 117 De .featured-cta hangt absoluut gepositioneerd over de vorige sectie heen. 118 De hostsectie moet daar met padding-top omheen, maar hoeveel hangt af van de 119 hoogte van de kaart â en die verschilt per pagina, want de tekst verschilt. 120 Eén vaste clamp in CSS is daardoor altijd óf te krap (kop plakt tegen de 121 kaart) óf te ruim (gat onder de kaart). Hier meten we de werkelijke overlap 122 en zetten we de padding daarop. 123 124 De CSS-clamp blijft als fallback staan voor het geval JS niet draait. 125 ========================================================================== */ 126document.addEventListener('DOMContentLoaded', () => { 127 const GAP = 100; // gewenste ruimte tussen onderkant kaart en eerste inhoud 128 129 const hosts = document.querySelectorAll('.section--about--featured, .section--features--has-overlap'); 130 if (!hosts.length) return; 131 132 const fit = () => {
133 hosts.forEach((host) => { 134 const card = host.querySelector('.featured-cta'); 135 if (!card) return; 136 137 /* Onder de breakpoint laat de CSS de kaart los uit de absolute 138 positionering: hij staat dan gewoon in de flow van de vórige 139 sectie en overlapt niets. Zonder deze check zou de meting hem 140 alsnog als overlap zien en er honderden pixels padding onder 141 plakken. */ 142 if (getComputedStyle(card).position !== 'absolute') { 143 host.style.removeProperty('padding-top'); 144 return; 145 } 146 147 // Meet zonder onze eigen padding, anders meten we onszelf mee. 148 host.style.paddingTop = '0px'; 149 const overlap = card.getBoundingClientRect().bottom - host.getBoundingClientRect().top; 150 151 if (overlap > 0) { 152 host.style.paddingTop = Math.round(overlap + GAP) + 'px'; 153 } else { 154 host.style.removeProperty('padding-top'); 155 } 156 }); 157 }; 158 159 fit(); 160 window.addEventListener('resize', fit); 161 // Webfonts veranderen de kaarthoogte nadat DOMContentLoaded al geweest is. 162 if (document.fonts && document.fonts.ready) document.fonts.ready.then(fit); 163});
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.