Contact

Even_praten

Studio

Watermolenweg 6GeldermalsenKVK 92376193 · BTW NL004950760B86

New business

chris@finalframe.nl+31 6 81 65 05 19

Volg ons

LinkedIn — /f-frameLet's make your story, the Final Frame.

Optimized by Noetis(function(){ var nav=document.getElementById('on-nav'); if(nav) window.addEventListener('scroll',function(){nav.classList.toggle('scrolled',window.scrollY>40)},{passive:true}); if(window.self!==window.top){ document.querySelectorAll('.on-rv').forEach(function(el){el.classList.add('in')}); var builderNav=document.getElementById('on-nav'); if(builderNav){builderNav.style.position='relative';builderNav.style.top='auto';builderNav.style.left='auto';builderNav.style.right='auto'} document.querySelectorAll('.on-page').forEach(function(el){el.style.paddingTop='0'}); }else{ var io=new IntersectionObserver(function(entries){ entries.forEach(function(e){ if(!e.isIntersecting) return; e.target.classList.remove('on-will'); e.target.classList.add('in'); io.unobserve(e.target); }); },{threshold:.08}); // Perf: only HIDE (.on-will) elements below the fold — above-the-fold content // stays painted so it can't delay LCP waiting on JS. var vhR=window.innerHeight||document.documentElement.clientHeight||800; document.querySelectorAll('.on-rv').forEach(function(el){ var r=el.getBoundingClientRect(); if(r.top-40){el.classList.add('in');} else{el.classList.add('on-will');io.observe(el);} }); } // Build the mobile drawer + overlay LAZILY on first open (post-hydration) // instead of at HTML-parse time — appending to before React hydrates // the /s/ render path caused a hydration mismatch (React #418). The guard // makes this idempotent if the block is injected/executed twice. function onEnsureMobDrawer(){ if(document.getElementById('on-mob-drawer')) return; var over=document.createElement('div'); over.className='on-mob-overlay';over.id='on-mob-over'; var drw=document.createElement('nav'); drw.id='on-mob-drawer'; drw.innerHTML=[['Home','/'],['Werk','/work'],['Diensten','/services'],['Prijzen','/pricing'],['Studio','/studio'],['Contact','/contact']].map(function(p){ return ''+p[0]+''; }).join(''); document.body.appendChild(over);document.body.appendChild(drw); } window.onMenu=function(){ onEnsureMobDrawer(); var btn=document.getElementById('on-mob-btn'); var drw=document.getElementById('on-mob-drawer'); var ov=document.getElementById('on-mob-over'); if(!drw||!ov) return; var opening=!drw.classList.contains('open'); drw.classList.toggle('open',opening); ov.classList.toggle('open',opening); if(btn) btn.classList.toggle('open',opening); document.body.style.overflow=opening?'hidden':''; }; // Countup — HTML carries the FINAL value (renderer parity + no-JS safe); // the live site animates from 0 up to it. Skipped in the builder iframe. if(window.self===window.top&&typeof IntersectionObserver!=='undefined'){ var cio=new IntersectionObserver(function(entries){ entries.forEach(function(en){ if(!en.isIntersecting) return; cio.unobserve(en.target); var el=en.target,fin=el.getAttribute('data-on-count')||el.textContent; var num=parseInt(fin,10)||0,t0=null; function step(ts){ if(!t0)t0=ts; var p=Math.min(1,(ts-t0)/1000);p=1-Math.pow(1-p,3); el.textContent=String(Math.round(num*p)); if(p<1)requestAnimationFrame(step);else el.textContent=fin; } requestAnimationFrame(step); }); },{threshold:.4}); document.querySelectorAll('[data-on-count]').forEach(function(el){cio.observe(el)}); } // Work filter tabs — hide via display none on the wrapper (never opacity) function onFilter(cat){ document.querySelectorAll('[data-on-cat]').forEach(function(w){ w.style.display=(cat==='all'||w.getAttribute('data-on-cat')===cat)?'':'none'; }); } // Delegated clicks — inline onclick attrs are stripped on canvas conversion document.addEventListener('click',function(e){ if(!e.target||!e.target.closest) return; var el; el=e.target.closest('#on-mob-btn'); if(el){onMenu();return;} el=e.target.closest('#on-mob-over'); if(el){onMenu();return;} el=e.target.closest('[data-on-tab]');if(el){ document.querySelectorAll('[data-on-tab]').forEach(function(t){t.classList.remove('act')}); el.classList.add('act'); onFilter(el.getAttribute('data-on-tab')); return; } }); // Self-hosted background videos: force muted (SSR can drop the attribute) and // call play() — Safari reliably autoplays muted HTML5 video this way. function ffPlay(v){ try{ v.muted=true; v.setAttribute('muted',''); var p=v.play(); if(p&&p.catch)p.catch(function(){}); }catch(e){} } document.querySelectorAll('video[autoplay]').forEach(function(v){ ffPlay(v); v.addEventListener('canplay',function(){ffPlay(v)},{once:true}); if('IntersectionObserver' in window){ new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting)ffPlay(e.target)})},{threshold:.1}).observe(v); } }); })();(function(){ var nav=document.getElementById('on-nav'); if(nav) window.addEventListener('scroll',function(){nav.classList.toggle('scrolled',window.scrollY>40)},{passive:true}); if(window.self!==window.top){ document.querySelectorAll('.on-rv').forEach(function(el){el.classList.add('in')}); var builderNav=document.getElementById('on-nav'); if(builderNav){builderNav.style.position='relative';builderNav.style.top='auto';builderNav.style.left='auto';builderNav.style.right='auto'} document.querySelectorAll('.on-page').forEach(function(el){el.style.paddingTop='0'}); }else{ document.querySelectorAll('.on-rv').forEach(function(el){el.classList.add('on-will')}); var io=new IntersectionObserver(function(entries){ entries.forEach(function(e){ if(!e.isIntersecting) return; e.target.classList.remove('on-will'); e.target.classList.add('in'); io.unobserve(e.target); }); },{threshold:.08}); document.querySelectorAll('.on-rv').forEach(function(el){io.observe(el)}); } (function(){ var over=document.createElement('div'); over.className='on-mob-overlay';over.id='on-mob-over'; var drw=document.createElement('nav'); drw.id='on-mob-drawer'; drw.innerHTML=[['Home','/'],['Werk','/work'],['Diensten','/services'],['Prijzen','/pricing'],['Studio','/studio'],['Contact','/contact']].map(function(p){ return ''+p[0]+''; }).join(''); document.body.appendChild(over);document.body.appendChild(drw); })(); window.onMenu=function(){ var btn=document.getElementById('on-mob-btn'); var drw=document.getElementById('on-mob-drawer'); var ov=document.getElementById('on-mob-over'); if(!drw||!ov) return; var opening=!drw.classList.contains('open'); drw.classList.toggle('open',opening); ov.classList.toggle('open',opening); if(btn) btn.classList.toggle('open',opening); document.body.style.overflow=opening?'hidden':''; }; // Countup — HTML carries the FINAL value (renderer parity + no-JS safe); // the live site animates from 0 up to it. Skipped in the builder iframe. if(window.self===window.top&&typeof IntersectionObserver!=='undefined'){ var cio=new IntersectionObserver(function(entries){ entries.forEach(function(en){ if(!en.isIntersecting) return; cio.unobserve(en.target); var el=en.target,fin=el.getAttribute('data-on-count')||el.textContent; var num=parseInt(fin,10)||0,t0=null; function step(ts){ if(!t0)t0=ts; var p=Math.min(1,(ts-t0)/1000);p=1-Math.pow(1-p,3); el.textContent=String(Math.round(num*p)); if(p<1)requestAnimationFrame(step);else el.textContent=fin; } requestAnimationFrame(step); }); },{threshold:.4}); document.querySelectorAll('[data-on-count]').forEach(function(el){cio.observe(el)}); } // Work filter tabs — hide via display none on the wrapper (never opacity) function onFilter(cat){ document.querySelectorAll('[data-on-cat]').forEach(function(w){ w.style.display=(cat==='all'||w.getAttribute('data-on-cat')===cat)?'':'none'; }); } // Delegated clicks — inline onclick attrs are stripped on canvas conversion document.addEventListener('click',function(e){ if(!e.target||!e.target.closest) return; var el; el=e.target.closest('#on-mob-btn'); if(el){onMenu();return;} el=e.target.closest('#on-mob-over'); if(el){onMenu();return;} el=e.target.closest('[data-on-tab]');if(el){ document.querySelectorAll('[data-on-tab]').forEach(function(t){t.classList.remove('act')}); el.classList.add('act'); onFilter(el.getAttribute('data-on-tab')); return; } }); // Repaired 2026-08-09. The handler that stood here cancelled the native submit, revealed // the #on-contact-done panel and fired the 'lead' conversion — while sending nothing at // all. The strict lead-v1 contract now owns this submit: form[data-sp-form] is bound by // the _FORM runtime, which POSTs to /api/adaptive/forms/submit and reveals // [data-sp-success] only once the server has answered ok. We record the conversion at // that point and not before. 'sp:submit-success' is dispatched without bubbles, so this // listens in the CAPTURE phase, which reaches document for any event. document.addEventListener('sp:submit-success',function(e){ var form=e.target; if(!form||form.id!=='on-contact-form') return; if(window.noetis&&window.noetis.track) window.noetis.track('lead'); },true); // Self-hosted background videos: force muted (SSR can drop the attribute) and // call play() — Safari reliably autoplays muted HTML5 video this way. function ffPlay(v){ try{ v.muted=true; v.setAttribute('muted',''); var p=v.play(); if(p&&p.catch)p.catch(function(){}); }catch(e){} } document.querySelectorAll('video[autoplay]').forEach(function(v){ ffPlay(v); v.addEventListener('canplay',function(){ffPlay(v)},{once:true}); if('IntersectionObserver' in window){ new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting)ffPlay(e.target)})},{threshold:.1}).observe(v); } }); })();