/* The Founder's Break - nav state, scroll reveal, RSVP submit. No scroll listeners: nav state and reveal both ride IntersectionObserver. */ (function () { "use strict"; var reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; /* ── Nav background once the page has moved off the top ── */ var nav = document.getElementById("nav"); var sentinel = document.createElement("div"); sentinel.style.cssText = "position:absolute;top:0;left:0;width:1px;height:1px;pointer-events:none"; document.body.prepend(sentinel); new IntersectionObserver(function (entries) { nav.classList.toggle("scrolled", !entries[0].isIntersecting); }).observe(sentinel); /* ── Scroll reveal: sequences sections in as they arrive ── */ var reveals = document.querySelectorAll(".reveal"); if (reduceMotion) { reveals.forEach(function (el) { el.classList.add("in"); }); } else { var io = new IntersectionObserver(function (entries, obs) { entries.forEach(function (entry, i) { if (!entry.isIntersecting) return; var delay = Math.min(i, 4) * 70; setTimeout(function () { entry.target.classList.add("in"); }, delay); obs.unobserve(entry.target); }); }, { threshold: 0.15, rootMargin: "0px 0px -8% 0px" }); reveals.forEach(function (el) { io.observe(el); }); } /* ── RSVP ── */ var form = document.getElementById("rsvp-form"); if (!form) return; var submit = document.getElementById("rsvp-submit"); var status = document.getElementById("form-status"); var done = document.getElementById("rsvp-done"); var nameField = document.getElementById("f-name"); var emailField = document.getElementById("f-email"); function setError(input, message) { var field = input.closest(".field"); field.classList.toggle("has-error", Boolean(message)); var slot = field.querySelector(".field-error"); if (slot) slot.textContent = message || ""; input.setAttribute("aria-invalid", message ? "true" : "false"); } function validate() { var ok = true; if (!nameField.value.trim()) { setError(nameField, "We need a name to put on the list."); ok = false; } else setError(nameField, ""); var email = emailField.value.trim(); if (!email) { setError(emailField, "We need an email to send the reminder to."); ok = false; } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(email)) { setError(emailField, "That email does not look right."); ok = false; } else setError(emailField, ""); return ok; } [nameField, emailField].forEach(function (input) { input.addEventListener("input", function () { if (input.closest(".field").classList.contains("has-error")) validate(); }); }); form.addEventListener("submit", function (event) { event.preventDefault(); status.textContent = ""; if (!validate()) { form.querySelector(".has-error input").focus(); return; } submit.setAttribute("aria-busy", "true"); var payload = new URLSearchParams(); new FormData(form).forEach(function (value, key) { if (key !== "_redirect") payload.append(key, value); }); fetch(form.action, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", Accept: "application/json", }, body: payload.toString(), }) .then(function (res) { if (!res.ok) throw new Error("HTTP " + res.status); return res.json().catch(function () { return { ok: true }; }); }) .then(function (body) { if (body && body.ok === false) throw new Error(body.error || "rejected"); showDone(); }) .catch(function () { submit.removeAttribute("aria-busy"); status.textContent = "That did not go through. Try again, or email contact@onetouch.studio and we will add you by hand."; }); }); function showDone() { form.hidden = true; done.hidden = false; done.setAttribute("tabindex", "-1"); done.focus({ preventScroll: true }); } /* No-JS fallback lands back here with ?rsvp=ok after the plain form POST. */ if (new URLSearchParams(window.location.search).get("rsvp") === "ok") showDone(); })();