// Stackless — contact form behaviour: step nav, validation, submit state (function () { var form = document.getElementById('contactForm'); if (!form) return; var steps = Array.prototype.slice.call(form.querySelectorAll('.step')); var dots = Array.prototype.slice.call(document.querySelectorAll('.progress .dot')); var prevBtn = document.getElementById('prevBtn'); var nextBtn = document.getElementById('nextBtn'); var submitBtn = document.getElementById('submitBtn'); var successEl = document.getElementById('formSuccess'); var errBanner = document.getElementById('errBanner'); var current = 0; function paint() { steps.forEach(function (s, i) { s.classList.toggle('is-active', i === current); }); dots.forEach(function (d, i) { d.classList.toggle('is-on', i === current); d.classList.toggle('is-done', i < current); }); prevBtn.disabled = current === 0; if (current === steps.length - 1) { nextBtn.style.display = 'none'; submitBtn.style.display = 'inline-flex'; } else { nextBtn.style.display = 'inline-flex'; submitBtn.style.display = 'none'; } } function validateStep(idx) { var step = steps[idx]; var fields = step.querySelectorAll('input[required], select[required], textarea[required]'); var ok = true; fields.forEach(function (el) { var bad = !el.checkValidity(); el.classList.toggle('is-invalid', bad); el.parentElement.classList.toggle('is-invalid', bad); if (bad) ok = false; }); // Custom: at least one service checkbox picked if on step 1 if (idx === 0) { var any = step.querySelectorAll('input[name="services"]:checked').length; var servicesErr = document.getElementById('servicesErr'); if (any === 0) { ok = false; servicesErr.style.display = 'block'; } else { servicesErr.style.display = 'none'; } } return ok; } function goto(i) { if (i > current && !validateStep(current)) { errBanner.style.display = 'flex'; return; } errBanner.style.display = 'none'; current = Math.max(0, Math.min(steps.length - 1, i)); paint(); // scroll the active step into view (only on mobile-ish where steps stack) var active = steps[current]; if (active && window.innerWidth < 720) { active.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } // live-clear invalid state as the user fixes it form.addEventListener('input', function (e) { var t = e.target; if (t.classList && t.classList.contains('is-invalid') && t.checkValidity()) { t.classList.remove('is-invalid'); if (t.parentElement) t.parentElement.classList.remove('is-invalid'); } if (t.name === 'services') { var any = form.querySelectorAll('input[name="services"]:checked').length; var servicesErr = document.getElementById('servicesErr'); if (any > 0 && servicesErr) servicesErr.style.display = 'none'; } }); prevBtn.addEventListener('click', function () { goto(current - 1); }); nextBtn.addEventListener('click', function () { goto(current + 1); }); // Submit (no real backend — show the success state, log the payload) form.addEventListener('submit', function (e) { e.preventDefault(); if (!validateStep(current)) { errBanner.style.display = 'flex'; return; } var data = {}; new FormData(form).forEach(function (v, k) { data[k] = v; }); console.log('[Stackless] contact submission:', data); submitBtn.disabled = true; submitBtn.textContent = 'Sending…'; setTimeout(function () { form.style.display = 'none'; successEl.style.display = 'block'; successEl.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 700); }); paint(); })();