(() => { const STORAGE_KEY = "astoria-code-inquiry"; const toggle = document.querySelector(".nav-toggle"); const nav = document.getElementById("nav"); if (toggle && nav) { toggle.addEventListener("click", () => { const open = toggle.getAttribute("aria-expanded") === "true"; toggle.setAttribute("aria-expanded", String(!open)); nav.classList.toggle("open", !open); }); nav.querySelectorAll("a").forEach((link) => { link.addEventListener("click", () => { toggle.setAttribute("aria-expanded", "false"); nav.classList.remove("open"); }); }); } const fallback = document.getElementById("mail-fallback"); const openMail = document.getElementById("open-mail"); const copyBtn = document.getElementById("copy-mail"); const copyStatus = document.getElementById("copy-status"); if (fallback) { try { const saved = sessionStorage.getItem(STORAGE_KEY); if (saved) { const data = JSON.parse(saved); if (data.body) { fallback.textContent = `To: ${data.to || "andrew@emaration.ai"}\nSubject: ${data.subject || ""}\n\n${data.body}`; } if (openMail && data.mailto) openMail.setAttribute("href", data.mailto); } } catch { /* ignore bad session data */ } } if (copyBtn && fallback) { copyBtn.addEventListener("click", async () => { try { await navigator.clipboard.writeText(fallback.textContent || ""); if (copyStatus) { copyStatus.textContent = "Copied."; copyStatus.classList.add("ok"); } } catch { if (copyStatus) { copyStatus.textContent = "Copy failed. Select the text and copy it yourself."; copyStatus.classList.add("error"); } } }); } const form = document.getElementById("inquiry-form"); if (!form) return; const status = form.querySelector(".form-status"); const submit = form.querySelector('button[type="submit"]'); function setStatus(text, kind) { if (!status) return; status.textContent = text; status.classList.toggle("error", kind === "error"); status.classList.toggle("ok", kind === "ok"); } function fieldError(name, message) { const field = form.querySelector(`[name="${name}"]`); if (!field) return; const label = field.closest("label"); if (!label) return; let err = label.querySelector(".field-error"); if (!err) { err = document.createElement("span"); err.className = "field-error"; label.appendChild(err); } err.textContent = message || ""; } function readForm() { const data = Object.fromEntries(new FormData(form).entries()); return { name: String(data.name || "").trim(), email: String(data.email || "").trim(), projectType: String(data.projectType || "").trim(), projectTypeOther: String(data.projectTypeOther || "").trim(), budget: String(data.budget || "").trim(), budgetOther: String(data.budgetOther || "").trim(), description: String(data.description || "").trim(), website: String(data.website || "").trim(), }; } const types = { app: "App", website: "Website", program: "Program", other: "Other" }; const budgets = { "under-2k": "Under $2,000", "2k-5k": "$2,000–$5,000", "5k-15k": "$5,000–$15,000", "15k-plus": "$15,000+", hourly: "Hourly / T&M only", other: "Other", }; function typeLabel(input) { if (input.projectType === "other") return input.projectTypeOther || "Other"; const base = types[input.projectType] || input.projectType; return input.projectTypeOther ? `${base} — ${input.projectTypeOther}` : base; } function budgetLabel(input) { if (input.budget === "other") return input.budgetOther || "Other"; const base = budgets[input.budget] || input.budget; return input.budgetOther ? `${base} — ${input.budgetOther}` : base; } function compose(input) { const kind = typeLabel(input); const subject = `Astoria Code inquiry: ${input.name} · ${kind}`; const body = [ "Astoria Code project inquiry", "", `Name: ${input.name}`, `Email: ${input.email}`, `Project type: ${kind}`, `Budget: ${budgetLabel(input)}`, "", "Description:", input.description, "", "— sent from astoriacode.app", ].join("\n"); const mailto = `mailto:andrew@emaration.ai?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`; return { to: "andrew@emaration.ai", subject, body, mailto }; } function localValidate(input) { const fields = {}; if (input.website) return { error: "Rejected.", fields }; if (input.name.length < 2) fields.name = "Name is required."; if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input.email)) fields.email = "A real email is required so I can reply."; if (!types[input.projectType]) fields.projectType = "Choose app, website, program, or other."; else if (input.projectType === "other" && input.projectTypeOther.length < 2) { fields.projectTypeOther = "Say what kind of project this is."; } if (!budgets[input.budget]) fields.budget = "Choose a budget range, or pick Other and write one."; else if (input.budget === "other" && input.budgetOther.length < 2) { fields.budgetOther = "Write a budget, a range, or hourly."; } if (input.description.length < 8) fields.description = "A short description is required."; return Object.keys(fields).length ? { error: "Please fix the highlighted fields.", fields } : null; } form.addEventListener("submit", async (event) => { event.preventDefault(); form.querySelectorAll(".field-error").forEach((el) => el.remove()); const input = readForm(); const invalid = localValidate(input); if (invalid) { for (const [name, message] of Object.entries(invalid.fields || {})) fieldError(name, message); setStatus(invalid.error, "error"); return; } if (submit) submit.disabled = true; setStatus("Preparing the email…"); let mail = compose(input); try { const res = await fetch("/api/contact", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(input), }); const body = await res.json().catch(() => ({})); if (res.ok && body.ok && body.mailto) { mail = { to: body.to || mail.to, subject: body.subject || mail.subject, body: body.body || mail.body, mailto: body.mailto, }; } else if (body.fields) { for (const [name, message] of Object.entries(body.fields)) fieldError(name, message); setStatus(body.error || "Please fix the highlighted fields.", "error"); if (submit) submit.disabled = false; return; } } catch { /* mailto fallback is enough */ } try { sessionStorage.setItem(STORAGE_KEY, JSON.stringify(mail)); } catch { /* private mode */ } const opener = document.createElement("a"); opener.href = mail.mailto; opener.style.display = "none"; document.body.appendChild(opener); opener.click(); opener.remove(); window.location.assign("/thanks"); }); })();