);
};
// ───────────────────── Process ─────────────────────
const Process = () => (
/ 02 · OUR WORK
How care actually works here.
Four steps, no 15-minute appointments, no billing-code gymnastics. Just time, precision, and a plan that evolves with you.
{PROCESS_STEPS.map((step, i) => (
{step.num}
{step.title}
{step.desc}
))}
);
// ───────────────────── Quiz ─────────────────────
// Native in-page quiz: scores answers, collects contact info, submits to Eden.
// Submission uses FormSubmit.co (no backend needed) — change CLINIC_EMAIL below.
const CLINIC_EMAIL = "hello@edenwellnessclinic.com";
// FormSubmit hashed endpoint (activated). The hash hides the email from page source
// while still routing to CLINIC_EMAIL on the backend.
const FORM_ENDPOINT = `https://formsubmit.co/ajax/68dad4f212a63cac6c1e6f8f409d1b51`;
const Quiz = React.forwardRef((props, ref) => {
// steps: 0..N-1 = questions, N = contact form, N+1 = success
const [step, setStep] = useState(0);
const [answers, setAnswers] = useState({});
const [contact, setContact] = useState({ name: '', email: '', phone: '', notes: '' });
const [submitting, setSubmitting] = useState(false);
const [submitError, setSubmitError] = useState(null);
const total = QUIZ_QUESTIONS.length;
const contactStep = total;
const doneStep = total + 1;
const isContact = step === contactStep;
const isDone = step === doneStep;
// Which questions allow multi-select. Per product spec: every question except the
// financial / budget question accepts up to two answers.
const isMulti = (q) => q && q.field !== 'budget';
const MAX_MULTI = 2;
// Normalize an answers[i] value to an array of options (legacy single-select answers
// were stored as a single object; we coerce on read so the rest of the math stays uniform).
const optsFor = (i) => {
const a = answers[i];
if (!a) return [];
return Array.isArray(a) ? a : [a];
};
// ── Compute the program match (only score-kind questions matter for ranking)
const computeMatch = () => {
const scores = {};
PROGRAMS.forEach(p => { scores[p.id] = 0; });
QUIZ_QUESTIONS.forEach((q, i) => {
optsFor(i).forEach(opt => {
if (!opt || !opt.weight) return;
Object.entries(opt.weight).forEach(([k, v]) => { scores[k] = (scores[k] || 0) + v; });
});
});
const ranked = Object.entries(scores).sort((a, b) => b[1] - a[1]);
const best = PROGRAMS.find(p => p.id === ranked[0][0]) || PROGRAMS[0];
const second = PROGRAMS.find(p => p.id === ranked[1][0]);
return { best, second, scores: ranked };
};
// ── Compute the *outcome* of the quiz (qualified, waitlist, low-budget, consult)
const computeOutcome = () => {
let stateOpts = [], budgetOpt = null, expectOpts = [];
QUIZ_QUESTIONS.forEach((q, i) => {
const opts = optsFor(i);
if (q.field === 'state') stateOpts = opts;
if (q.field === 'budget') budgetOpt = opts[0]; // single-select
if (q.field === 'expectations') expectOpts = opts;
});
if (stateOpts.some(o => o.qualified === 'waitlist')) return 'waitlist_state';
if (budgetOpt && budgetOpt.qualified === 'low_budget') return 'low_budget';
if (budgetOpt && budgetOpt.qualified === 'consult') return 'consult';
if (expectOpts.some(o => o.commitment === 'low') && (!budgetOpt || budgetOpt.qualified !== true)) return 'consult';
return 'qualified';
};
const match = computeMatch();
const outcome = computeOutcome();
const select = (opt) => {
const q = QUIZ_QUESTIONS[step];
if (!isMulti(q)) {
// Single-select (budget): set + auto-advance.
setAnswers({ ...answers, [step]: [opt] });
setTimeout(() => setStep(s => Math.min(s + 1, contactStep)), 220);
return;
}
// Multi-select: toggle, cap at MAX_MULTI (drop oldest when over).
const current = optsFor(step);
const idx = current.findIndex(o => o.label === opt.label);
let next;
if (idx >= 0) {
next = current.filter(o => o.label !== opt.label);
} else if (current.length >= MAX_MULTI) {
next = [...current.slice(1), opt];
} else {
next = [...current, opt];
}
setAnswers({ ...answers, [step]: next });
};
const advance = () => {
if (optsFor(step).length === 0) return;
setStep(s => Math.min(s + 1, contactStep));
};
const reset = () => {
setStep(0);
setAnswers({});
setContact({ name: '', email: '', phone: '', notes: '' });
setSubmitError(null);
};
const submit = async (e) => {
e.preventDefault();
if (!contact.name || !contact.email) return;
setSubmitting(true);
setSubmitError(null);
const answersSummary = QUIZ_QUESTIONS.map((q, i) => {
const opts = optsFor(i);
const labels = opts.length ? opts.map(o => o.label).join(' + ') : '(no answer)';
return `Q${i + 1} [${q.section || ''}] ${q.q}\n → ${labels}`;
}).join('\n\n');
const scoreSummary = match.scores
.map(([id, score]) => {
const prog = PROGRAMS.find(p => p.id === id);
return ` ${prog?.short}: ${score}`;
}).join('\n');
const outcomeLabels = {
qualified: '✓ QUALIFIED — ready to book',
consult: '⚠ NEEDS FIT CONVERSATION — budget or expectations gap',
low_budget: '✗ UNDER-BUDGET — newsletter / library follow-up',
waitlist_state: '◷ OUT-OF-STATE WAITLIST — licensure pending',
};
const payload = {
_subject: `Eden Lead [${outcomeLabels[outcome]}] — ${contact.name} → ${match.best.short}`,
_template: "table",
_captcha: "false",
Outcome: outcomeLabels[outcome],
Name: contact.name,
Email: contact.email,
Phone: contact.phone || '(not provided)',
"Best-Fit Program": match.best.short,
"Secondary Match": match.second?.short || '',
"Program Scores": scoreSummary,
"Quiz Answers": answersSummary,
"Additional Notes": contact.notes || '(none)',
"Submitted": new Date().toLocaleString(),
};
try {
const res = await fetch(FORM_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(`Submission failed (${res.status})`);
setStep(doneStep);
} catch (err) {
setSubmitError(err.message || "Something went wrong. Please try again or call us directly.");
} finally {
setSubmitting(false);
}
};
const pct = isDone ? 100 : isContact ? 92 : Math.round(((step) / (total + 1)) * 92);
// Has the previous question changed section?
const currentQ = !isContact && !isDone ? QUIZ_QUESTIONS[step] : null;
const prevQ = step > 0 ? QUIZ_QUESTIONS[step - 1] : null;
const showSectionHeader = currentQ && (!prevQ || prevQ.section !== currentQ.section);
return (
/ 03 · FIND YOUR PATH
Begin with the assessment.
Every Eden patient starts here. This is how we make sure we're the right fit before either of us spends a dollar — and how we get a real picture of what you actually need.
Your assessment is in front of our team. Based on your answers, the program that looks like the closest fit is {match.best.short} — but we won't make any recommendations until a clinician has reviewed your responses in full.
What happens next
01
Clinical review. A member of the Eden Wellness team reviews your assessment, your goals, and any flags you noted.
02
Personal outreach within one business day. We'll reach out by phone or email — whichever you prefer — to discuss what we saw and confirm whether {match.best.short} is the right starting point.
03
If we're a fit, we'll schedule your Discovery visit together. Booking happens during that conversation, not before.
Your assessment is in front of our team. There are real, meaningful ways we can help you get started — and we'd like to talk through them with you so you land in the right place from day one.
Many of our patients begin with a focused, lower-investment starting point and build from there as their results compound. A short conversation is the best way to map out what makes sense for you.
We'll reach out within one business day to schedule a complimentary 15-minute call.
We'll walk you through the most effective entry points and help you choose the one that fits.
Concierge functional medicine is a meaningful financial commitment. Right now, the work we do isn't a fit for every budget — and we'd rather be honest about that than have you over-extend.
What we can offer: free, in-depth, clinician-written articles on the same science we practice. No fluff, no upsell — the same things we'd tell our own patients.
We'll add you to our list — no spam, no automated funnels.
If your situation changes, the door is always open.