/* ============================================================ Storm Cloud Jumpers — site interactions ============================================================ */ (function () { 'use strict'; /* ---------------------------------------------------------- FORM DELIVERY CONFIG ------------------------------------------------------------ By default the inquiry form opens the visitor's email app addressed to Storm Cloud Jumpers (mailto:). No server needed. To have submissions land straight in the Gmail inbox WITHOUT the visitor needing a mail app, deploy the included `form-handler.gs` as a Google Apps Script web app (it sends from the shop's own Gmail) and paste its /exec URL below. When FORM_ENDPOINT is set, the form POSTs to it and falls back to mailto only if the request fails. ---------------------------------------------------------- */ var CONTACT_EMAIL = 'stormcloudjumpers@gmail.com'; var FORM_ENDPOINT = ''; // e.g. 'https://script.google.com/macros/s/XXXX/exec' var $ = function (s, c) { return (c || document).querySelector(s); }; var $$ = function (s, c) { return Array.prototype.slice.call((c || document).querySelectorAll(s)); }; /* ---------- Sticky nav shadow ---------- */ var nav = $('#nav'); var onScroll = function () { if (nav) nav.classList.toggle('scrolled', window.scrollY > 12); }; onScroll(); window.addEventListener('scroll', onScroll, { passive: true }); /* ---------- Mobile menu ---------- */ var burger = $('#burger'); var links = $('#navLinks'); var closeMenu = function () { if (!links) return; links.classList.remove('open'); nav.classList.remove('menu-open'); burger.setAttribute('aria-expanded', 'false'); }; if (burger && links) { burger.addEventListener('click', function () { var open = links.classList.toggle('open'); nav.classList.toggle('menu-open', open); burger.setAttribute('aria-expanded', String(open)); }); $$('a', links).forEach(function (a) { a.addEventListener('click', closeMenu); }); document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeMenu(); }); } /* ---------- Reveal on scroll ---------- */ var reveals = $$('.reveal'); if ('IntersectionObserver' in window && reveals.length) { var io = new IntersectionObserver(function (entries) { entries.forEach(function (en) { if (en.isIntersecting) { en.target.classList.add('in'); io.unobserve(en.target); } }); }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' }); reveals.forEach(function (el) { io.observe(el); }); } else { reveals.forEach(function (el) { el.classList.add('in'); }); } /* ---------- Hero parallax (subtle, respects reduced motion) ---------- */ var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; var heroBg = $('.hero__bg'); if (heroBg && !reduce) { window.addEventListener('scroll', function () { var y = window.scrollY; if (y < window.innerHeight) heroBg.style.transform = 'translateY(' + (y * 0.25) + 'px)'; }, { passive: true }); } /* ---------- "Reserve" buttons prefill the spider field ---------- */ $$('[data-inquire]').forEach(function (btn) { btn.addEventListener('click', function () { var field = $('#spiderField'); if (field) field.value = btn.getAttribute('data-inquire') || ''; }); }); /* ---------- Inquiry form ---------- */ var form = $('#inquiryForm'); var ok = $('#formOk'); var buildMailto = function (data) { var subjSpider = data.spider ? ' — ' + data.spider : ''; var subject = 'Spider Inquiry' + subjSpider + ' (' + data.name + ')'; var body = 'Name: ' + data.name + '\n' + 'Email: ' + data.email + '\n' + 'Spider of interest: ' + (data.spider || '(not specified)') + '\n\n' + 'Message:\n' + (data.message || '(none)') + '\n'; return 'mailto:' + CONTACT_EMAIL + '?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body); }; if (form) { form.addEventListener('submit', function (e) { e.preventDefault(); if (!form.checkValidity()) { form.reportValidity(); return; } var data = { name: form.name.value.trim(), email: form.email.value.trim(), spider: form.spider.value.trim(), message: form.message.value.trim() }; var finishMailto = function () { window.location.href = buildMailto(data); if (ok) ok.hidden = false; }; if (FORM_ENDPOINT) { // Progressive enhancement: POST to Google Apps Script (sends via Gmail). var body = new URLSearchParams(data).toString(); fetch(FORM_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body }).then(function (r) { if (!r.ok) throw new Error('bad status'); if (ok) { ok.textContent = 'Thanks! Your inquiry has been sent — we’ll reply within a day or two.'; ok.hidden = false; } form.reset(); }).catch(finishMailto); } else { finishMailto(); } }); } /* ---------- Mamas lightbox ---------- */ var lb = $('#lightbox'); var lbImg = $('#lightboxImg'); var lbCap = $('#lightboxCap'); var lbClose = $('#lightboxClose'); var lastFocused = null; var openLightbox = function (trigger, img, capHTML) { if (!img || !lb) return; lastFocused = trigger; lbImg.src = img.getAttribute('src'); lbImg.alt = img.getAttribute('alt') || ''; lbCap.innerHTML = capHTML || ''; lb.hidden = false; requestAnimationFrame(function () { lb.classList.add('open'); }); document.body.style.overflow = 'hidden'; lbClose.focus(); }; var wireZoom = function (trigger, getImg, getCap, label) { trigger.setAttribute('role', 'button'); trigger.setAttribute('tabindex', '0'); trigger.setAttribute('aria-label', label); var open = function () { openLightbox(trigger, getImg(), getCap()); }; trigger.addEventListener('click', open); trigger.addEventListener('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); open(); } }); }; var closeLightbox = function () { if (!lb || lb.hidden) return; lb.classList.remove('open'); document.body.style.overflow = ''; var done = function () { lb.hidden = true; lbImg.src = ''; lb.removeEventListener('transitionend', done); if (lastFocused && lastFocused.focus) lastFocused.focus(); }; if (reduce) { done(); } else { lb.addEventListener('transitionend', done); } }; if (lb) { // Meet the Mamas gallery $$('.mama').forEach(function (fig) { var name = $('.mama__cap b', fig); wireZoom(fig, function () { return $('img', fig); }, function () { var c = $('.mama__cap', fig); return c ? c.innerHTML : ''; }, 'Enlarge photo' + (name ? ' of ' + name.textContent : '')); }); // Available spiders — click the photo to enlarge $$('#available .card__media').forEach(function (media) { var card = media.closest('.card'); wireZoom(media, function () { return $('img', media); }, function () { var h = $('h3', card), sci = $('.card__sci', card); return (h ? '' + h.textContent + '' : '') + (sci ? '' + sci.textContent + '' : ''); }, 'Enlarge photo' + (card && $('h3', card) ? ' of ' + $('h3', card).textContent : '')); }); lbClose.addEventListener('click', closeLightbox); lb.addEventListener('click', function (e) { if (e.target === lb) closeLightbox(); }); document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeLightbox(); }); } /* ---------- Footer year ---------- */ var y = $('#year'); if (y) y.textContent = String(new Date().getFullYear()); })();