(() => { const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const MOBILE_RE = /^\d{10,15}$/; function resolveApiBase() { if (window.EVENTBUZZ_API_BASE) { return String(window.EVENTBUZZ_API_BASE).replace(/\/$/, ""); } const host = window.location.hostname; if (host === "localhost" || host === "127.0.0.1") { return "http://localhost:3001/api"; } return "https://api.eventbuzz.live/api"; } const API_BASE = resolveApiBase(); /* ---------- Mobile nav drawer ---------- */ function initNav() { const nav = document.querySelector("nav"); const menuBtn = document.querySelector(".nav-menu-btn"); if (!nav || !menuBtn) return; const mq = window.matchMedia("(max-width: 900px)"); let drawerOpen = false; let drawerEls = null; const links = Array.from( document.querySelectorAll(".navlinks-desktop a") ).map((a) => ({ href: a.getAttribute("href") || "#", label: a.textContent.trim(), target: a.getAttribute("target") || null, })); function syncDrawerMode() { nav.classList.toggle("is-drawer-mode", mq.matches); if (!mq.matches) closeDrawer(true); } function ensureDrawer() { if (drawerEls) return drawerEls; const overlay = document.createElement("div"); overlay.className = "nav-drawer-overlay"; overlay.setAttribute("role", "presentation"); const aside = document.createElement("aside"); aside.className = "nav-drawer"; aside.id = "nav-drawer"; aside.setAttribute("role", "dialog"); aside.setAttribute("aria-modal", "true"); aside.setAttribute("aria-label", "Navigation menu"); aside.innerHTML = `
Request demo `; overlay.appendChild(aside); document.body.appendChild(overlay); overlay.addEventListener("click", (e) => { if (e.target === overlay) closeDrawer(); }); aside .querySelector(".nav-drawer-close") ?.addEventListener("click", () => closeDrawer()); aside.querySelectorAll("a").forEach((a) => { a.addEventListener("click", () => closeDrawer()); }); drawerEls = { overlay, aside }; return drawerEls; } function openDrawer() { const { overlay, aside } = ensureDrawer(); drawerOpen = true; menuBtn.setAttribute("aria-expanded", "true"); menuBtn.setAttribute("aria-label", "Close menu"); requestAnimationFrame(() => { overlay.classList.add("is-open"); aside.classList.add("is-open"); }); document.body.style.overflow = "hidden"; } function closeDrawer(instant) { if (!drawerEls) return; drawerOpen = false; menuBtn.setAttribute("aria-expanded", "false"); menuBtn.setAttribute("aria-label", "Open menu"); drawerEls.overlay.classList.remove("is-open"); drawerEls.aside.classList.remove("is-open"); document.body.style.overflow = ""; if (instant) { drawerEls.overlay.style.transition = "none"; drawerEls.aside.style.transition = "none"; requestAnimationFrame(() => { drawerEls.overlay.style.transition = ""; drawerEls.aside.style.transition = ""; }); } } menuBtn.addEventListener("click", () => { if (drawerOpen) closeDrawer(); else openDrawer(); }); document.addEventListener("keydown", (e) => { if (e.key === "Escape" && drawerOpen) closeDrawer(); }); syncDrawerMode(); mq.addEventListener("change", syncDrawerMode); } /* ---------- Demo modal ---------- */ function validateDemo(data) { const errors = {}; const name = (data.name || "").trim(); const mobile = (data.mobile || "").trim(); const email = (data.email || "").trim(); const message = (data.message || "").trim(); const captchaAnswer = (data.captchaAnswer || "").trim(); if (!name) errors.name = "Full name is required."; if (!mobile) errors.mobile = "Mobile is required."; else { const digits = mobile.replace(/[\s\-().]/g, ""); if (!MOBILE_RE.test(digits)) { errors.mobile = "Enter a valid mobile number (10-15 digits)."; } } if (!email) errors.email = "Email is required."; else if (!EMAIL_RE.test(email)) { errors.email = "Enter a valid email address."; } if (!message) errors.message = "Message is required."; if (!captchaAnswer) errors.captcha = "Please solve the captcha."; return { ok: Object.keys(errors).length === 0, errors, data: { name, mobile: mobile.replace(/[\s\-().]/g, ""), email, message, captchaAnswer, }, }; } function initDemoModal() { let overlay = null; let open = false; let success = false; let captchaToken = ""; let captchaLoading = false; let submitting = false; async function loadCaptcha() { if (!overlay) return; const questionEl = overlay.querySelector("[data-demo-captcha-question]"); const answerEl = overlay.querySelector('[name="captchaAnswer"]'); const reloadBtn = overlay.querySelector("[data-demo-captcha-reload]"); if (!questionEl) return; captchaLoading = true; questionEl.textContent = "Loading…"; if (answerEl) answerEl.value = ""; if (reloadBtn) reloadBtn.disabled = true; try { const res = await fetch(`${API_BASE}/public/captcha/challenge`, { cache: "no-store", }); const body = await res.json(); if (!res.ok || !body?.success || !body?.data?.captchaToken) { throw new Error(body?.error?.message || "Captcha unavailable"); } captchaToken = body.data.captchaToken; questionEl.textContent = body.data.question || "Solve the sum"; } catch { captchaToken = ""; questionEl.textContent = "Unavailable — tap reload"; } finally { captchaLoading = false; if (reloadBtn) reloadBtn.disabled = false; } } function setFieldError(form, key, message) { const inputName = key === "captcha" ? "captchaAnswer" : key; const errId = key === "captchaAnswer" || key === "captcha" ? "demo-captcha-err" : `demo-${key}-err`; const field = form.querySelector(`[name="${inputName}"]`)?.closest(".demo-field"); const errEl = document.getElementById(errId); const input = form.querySelector(`[name="${inputName}"]`); if (message) { field?.classList.add("has-error"); if (errEl) { errEl.hidden = false; errEl.textContent = message; } input?.setAttribute("aria-invalid", "true"); } else { field?.classList.remove("has-error"); if (errEl) { errEl.hidden = true; errEl.textContent = ""; } input?.removeAttribute("aria-invalid"); } } function build() { if (overlay) return overlay; overlay = document.createElement("div"); overlay.className = "demo-modal-overlay"; overlay.setAttribute("role", "presentation"); overlay.innerHTML = `Tell us a bit about your event and we’ll get back to you.
Your demo request was received. Our team will reach out shortly.