Plan Your Visit landing page for visit.waterschurch.org

Next.js 16 static export + PHP 8 API for cPanel/Apache hosting.

- Location-aware campus finder (IP estimate, optional precise location, ZIP search)
- Three-step Plan Your Visit form with separate email/SMS consent
- PHP API: validation, anti-spam, rate limiting, SQLite lead store,
  Mailchimp and Sinch Engage adapters, idempotent retries via cron
- Attribution capture (utm_*, gclid/gbraid/wbraid, fbclid, ttclid, twclid) and
  consent-aware analytics hooks
- Vitest and PHP test suites, cPanel deployment script and docs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Waters Church
2026-09-22 13:22:26 -04:00
co-authored by Claude Fable 5.1
commit 78cd0cc550
109 changed files with 16861 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
/**
* Consent disclosures. The version string is stored with every lead so the
* exact language a visitor agreed to can be reproduced later.
*
* ⚠️ Legal review required: TCPA/CTIA SMS language, CAN-SPAM, and the privacy
* notice must be reviewed by Waters Church's compliance adviser before launch.
* Bump `version` whenever any text below changes.
*/
export const consent = {
version: "2026-09-22.v1",
email: {
label: "Email me a first-visit guide and occasional updates from Waters Church.",
detail: "You can unsubscribe with one click at any time.",
},
sms: {
label: "Text me a confirmation and reminders about my visit.",
detail:
"By checking this box you agree to receive text messages from Waters Church about your visit, including a confirmation text. Message frequency varies. Message and data rates may apply. Reply STOP to opt out or HELP for help. Consent is not a condition of visiting.",
},
privacyBlurb:
"We use your information to help you plan your visit. We never sell it.",
locationBlurb:
"We estimate your general area from your network connection to suggest a location. Precise location is only used if you ask for it and is never stored.",
} as const;
+65
View File
@@ -0,0 +1,65 @@
/** Landing-page copy. Keep headlines short, direct, and free of insider language. */
export const copy = {
header: { findCampus: "Find Your Campus", textUs: "Text Us" },
hero: {
eyebrow: "Plan your first visit",
headline: ["Church can feel", "different here."],
sub: "Your first visit doesn't have to feel uncertain. We'll help you know what to expect and make it easy to find the Waters Church location nearest you.",
primaryCta: "Plan My Visit",
secondaryCta: "Find My Campus",
textCta: "Questions? Text us",
trust: "No pressure. Just a friendly welcome.",
},
campus: {
heading: "Find your closest location",
sub: "Six locations across Massachusetts, Rhode Island, and Florida. Pick the one that works for you.",
recommended: "Closest to you (based on your general area)",
recommendedPrecise: "Closest to you",
uncertain: "We couldn't estimate your location. Choose a campus below or search by ZIP.",
useLocation: "Use my precise location",
locating: "Finding nearby locations…",
zipLabel: "Search by ZIP code",
zipPlaceholder: "e.g. 02763",
zipButton: "Search",
choose: "Choose This Campus",
chosen: "Your Campus",
textCampus: "Text This Campus",
directions: "Get Directions",
change: "Choose a different location",
all: "All locations",
launching: "Coming soon",
denied: "No problem. Pick your location below or search by ZIP.",
unsupported: "Your browser doesn't share location. Pick your location below or search by ZIP.",
farAway: "Our closest location is a bit of a drive. You're still welcome, and you can also join us online.",
},
expect: {
heading: "What to expect",
sub: "Real answers to the questions people actually ask before their first visit.",
},
kids: {
heading: "Bringing kids?",
sub: "Waters Kids is built so your kids have a great time and you can actually relax.",
},
proof: { heading: "What first-time visitors say" },
form: {
heading: "Plan your visit",
sub: "Tell us a little about you and we'll help you feel ready. Takes about a minute.",
steps: ["Your campus", "About you", "Your visit"],
next: "Continue",
back: "Back",
submit: "Send My Visit Details",
submitting: "Sending…",
},
confirm: {
heading: "You're all set.",
sub: "We can't wait to meet you.",
emailNote: "Check your inbox. A first-visit guide is on the way.",
smsNote: "We've sent a confirmation text. Reply to it any time with questions.",
smsPending: "Your confirmation text may take a minute to arrive.",
textUs: "Text Us a Question",
again: "Plan another visit",
},
footer: {
online: "Can't make it in person? Join us online.",
},
} as const;
+65
View File
@@ -0,0 +1,65 @@
/**
* "What to Expect" content. Short, scannable, newcomer-first.
* Items flagged `confirm: true` contain general guidance that Waters Church
* should review for campus-specific accuracy before launch.
*/
export interface Faq {
id: string;
question: string;
answer: string;
confirm?: boolean;
}
export const faqs: Faq[] = [
{
id: "wear",
question: "What should I wear?",
answer:
"Whatever you're comfortable in. Most people show up in jeans and a T-shirt or something casual. Nobody is checking.",
},
{
id: "length",
question: "How long is a service?",
answer:
"Plan on a little over an hour. There's music, a message you can actually use during the week, and then you're free to go or stick around and say hi.",
confirm: true,
},
{
id: "music",
question: "What is the music and teaching like?",
answer:
"The music is loud, modern, and led by a live band. The teaching is straight from the Bible, practical, and explained in plain language, even if you've never opened one.",
},
{
id: "parking",
question: "Where should I park?",
answer:
"Every location has free on-site parking. Look for the team in Waters Church gear near the entrance if you're not sure where to go.",
confirm: true,
},
{
id: "arrive",
question: "What happens when I arrive?",
answer:
"Someone will say hello and point you in the right direction. Grab a coffee, find a seat anywhere you like, and settle in. If you have kids, the check-in team will get them set up first.",
},
{
id: "kids",
question: "Is there something for my kids?",
answer:
"Yes. Waters Kids runs during every service at every location, with age-specific rooms from newborns through middle school. Every volunteer is background-checked, and a security tag system covers check-in and pickup.",
},
{
id: "pressure",
question: "Will I be singled out or pressured?",
answer:
"No. You won't be asked to stand, speak, give, or fill anything out. Come as an observer for as long as you like. If you want to take a next step, we'll make it easy to find. If not, that's fine too.",
},
{
id: "accessible",
question: "Is the venue accessible?",
answer:
"Our locations have step-free entrances and accessible seating. If you or someone with you has a specific need, text us ahead of time and we'll be ready.",
confirm: true,
},
];
+73
View File
@@ -0,0 +1,73 @@
/**
* Waters Kids information. Age groups and safety facts are taken from
* waterschurch.org/about/waters-kids (verified 2026-09-22). Items marked
* `placeholder: true` are NOT verified policies and must be confirmed or
* removed by Waters Church before launch.
*/
export const kidsAgeGroups = [
{ id: "0-2", label: "Early Development", ages: "0–2 yrs", rooms: "Blue & Purple Rooms" },
{ id: "3-5", label: "Preschool & Pre-K", ages: "3–5 yrs", rooms: "Green, Yellow & Orange Rooms" },
{ id: "6-10", label: "Elementary", ages: "6–10 yrs", rooms: "Red Room & Ignite" },
{ id: "11-13", label: "Middle School & Pre-Teen", ages: "11–13 yrs", rooms: "Activate Youth Group" },
] as const;
export type KidsAgeRangeId = (typeof kidsAgeGroups)[number]["id"] | "14-18";
/** Age ranges offered in the form (includes high school for planning purposes). */
export const childAgeRangeOptions: { value: KidsAgeRangeId; label: string }[] = [
{ value: "0-2", label: "0–2" },
{ value: "3-5", label: "3–5" },
{ value: "6-10", label: "6–10" },
{ value: "11-13", label: "11–13" },
{ value: "14-18", label: "14–18 (Waters Youth)" },
];
export interface KidsPoint {
id: string;
title: string;
body: string;
placeholder?: boolean;
}
export const kidsPoints: KidsPoint[] = [
{
id: "checkin",
title: "Check-in",
body:
"Head to the Waters Kids check-in area when you arrive. A team member will register your family, print name tags for your kids, and give you a matching security tag for pickup.",
placeholder: true,
},
{
id: "safety",
title: "Safety & security",
body:
"Every Waters Kids volunteer passes a background check. Waters Church also employs its own security team, uses a security tag system for check-in and pickup, and can page you during the service if your child needs you.",
},
{
id: "ages",
title: "Age groups",
body:
"Kids are grouped by age from newborn through middle school, with programs running at every service time and location. High schoolers are invited to Waters Youth.",
},
{
id: "arrive",
title: "When to arrive",
body:
"For a relaxed first visit, plan to arrive about 15 minutes before the service starts so check-in isn't rushed.",
placeholder: true,
},
{
id: "bring",
title: "What to bring",
body:
"Just your kids. If your little one needs diapers, a bottle, or a snack, label them and hand them to the room leader at check-in.",
placeholder: true,
},
{
id: "needs",
title: "Special needs & accessibility",
body:
"If your child has a special need, allergy, or anything the team should know, tell us at check-in or text ahead of your visit. [Waters Church: confirm available accommodations before launch.]",
placeholder: true,
},
];
+19
View File
@@ -0,0 +1,19 @@
/**
* Social proof. No real testimonials were available in the repository or on
* waterschurch.org at build time, so this list is EMPTY and the section is
* hidden automatically. To enable it, add verified quotes with permission:
*
* { quote: "…", name: "First L.", context: "First visited 2025 · Norwood" }
*
* Never invent quotes, attendance numbers, or ratings.
*/
export interface Testimonial {
quote: string;
name: string;
context?: string;
}
export const testimonials: Testimonial[] = [];
/** When true and `testimonials` is empty, a visible placeholder card renders in dev/preview builds only. */
export const showTestimonialPlaceholderInDev = true;