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:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user