// Inquiry / booking flow — multi-step form
const { useState: useStateI, useEffect: useEffectI } = React;
const FORMSPREE_ENDPOINT = 'https://formspree.io/f/xvkpldwo';
function Inquiry() {
const [step, setStep] = useStateI(0);
const [sending, setSending] = useStateI(false);
const [sendError, setSendError] = useStateI(false);
const [data, setData] = useStateI({
interests: [], when: 'flexible', month: 'september', year: '2026', days: 9,
travelers: 2, kids: 0, style: 'mid', budget: 7000,
name: '', email: '', country: '', phone: '', notes: '',
pace: 'balanced', accommodation: ['tented'], inspirations: []
});
const set = (k, v) => setData(d => ({ ...d, [k]: v }));
const toggle = (k, v) => setData(d => ({ ...d, [k]: d[k].includes(v) ? d[k].filter(x => x !== v) : [...d[k], v] }));
const steps = ['Dream', 'When', 'Who', 'Style', 'You'];
const titles = [
"What's calling you to Tanzania?",
"When can you travel?",
"Who's coming?",
"How do you like to travel?",
"Where should we send the draft?"
];
useEffectI(() => { window.scrollTo({ top: 0, behavior: 'smooth' }); }, [step]);
return (
Plan your trip · Step {step+1} of {steps.length}
{titles[step]}
{step === 0 && "Pick the things that pull you — we'll build the trip around them."}
{step === 1 && "Approximate is fine. We'll suggest the best windows for what you want to see."}
{step === 2 && "Group composition shapes pacing, vehicles, and which camps we recommend."}
{step === 3 && "Pacing, accommodation, and budget — be honest, we work with all three."}
{step === 4 && "We'll respond within 24 hours with a draft itinerary. No charge, no template."}
{steps.map((s, i) => (
{String(i+1).padStart(2,'0')} {s}
))}
Free planning · No obligation · Reply < 24h
{step === 0 && }
{step === 1 && }
{step === 2 && }
{step === 3 && }
{step === 4 && }
{step < 4 ? (
{step > 0 && setStep(step-1)} className="btn btn-ghost">
Back
}
setStep(step+1)} className="btn btn-primary" style={{ marginLeft: 'auto' }}>
Continue
) : (
setStep(step-1)} className="btn btn-ghost">
Back
{
setSending(true);
setSendError(false);
fetch(FORMSPREE_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
body: JSON.stringify(data)
})
.then(res => { if (!res.ok) throw new Error('fail'); setStep(5); })
.catch(() => setSendError(true))
.finally(() => setSending(false));
}} className="btn btn-sun" style={{ marginLeft: 'auto' }}>
{sending ? 'Sending…' : 'Send inquiry'}
)}
{sendError && Something went wrong sending your inquiry — please try again.
}
{step === 5 && }
);
}
function Step1({ data, toggle }) {
const opts = [
{ k: 'migration', t: 'The Great Migration', d: 'Two million wildebeest, river crossings, calving plains.', tone: 'sun', img: 'https://safari.janelsafaris.com/wp-content/uploads/2025/10/23.jpg' },
{ k: 'big5', t: 'The Big Five', d: 'Lion, leopard, elephant, buffalo, rhino — all in one trip.', tone: 'clay', img: 'https://safari.janelsafaris.com/wp-content/uploads/2025/09/DSC_2974-Edit.jpg' },
{ k: 'kilimanjaro', t: 'Kilimanjaro', d: '5,895m. Eight days from rainforest to glacier.', tone: 'ink', img: 'https://safari.janelsafaris.com/wp-content/uploads/2025/10/yoad-shejtman-S8IflvEHBFk-unsplash-scaled.jpg' },
{ k: 'beach', t: 'Beach time', d: 'Zanzibar, Pemba, Mafia. Decompress after the bush.', tone: 'ocean', img: 'https://safari.janelsafaris.com/wp-content/uploads/2025/09/humphrey-m-e6dRLBx6Kg8-unsplash-scaled.jpg' },
{ k: 'culture', t: 'Cultural depth', d: 'Maasai, Hadzabe, Datoga. Real visits, not performances.', tone: 'moss', img: 'https://safari.janelsafaris.com/wp-content/uploads/2025/09/matthew-stephenson-ztJxQYP2bKs-unsplash-scaled.jpg' },
{ k: 'photo', t: 'Photography', d: 'Specialist vehicles, hides, and bean bags. Pro guides.', tone: 'sand', img: 'https://safari.janelsafaris.com/wp-content/uploads/2025/09/janel-scaled.jpg' },
{ k: 'family', t: 'Family-friendly', d: 'Trip designed around kids without dumbing down.', tone: 'sun', img: 'https://safari.janelsafaris.com/wp-content/uploads/2026/03/Rhino-Lodge-15-700px.jpg' },
{ k: 'honeymoon', t: 'Honeymoon', d: 'Privacy, romance, and sunsets you remember.', tone: 'clay', img: 'https://safari.janelsafaris.com/wp-content/uploads/2025/09/ultimate-safaris-namibia-fxx-SJSsiqk-unsplash-scaled.jpg' },
];
return (
Multi-select · Pick anything that resonates
{opts.map(o => {
const sel = data.interests.includes(o.k);
return (
toggle('interests', o.k)} style={{
padding: 0, textAlign: 'left',
border: '1px solid ' + (sel ? 'var(--ink)' : 'var(--line)'),
borderRadius: 'var(--radius-md)',
background: sel ? 'var(--ink)' : 'transparent',
color: sel ? 'var(--bone)' : 'var(--ink)',
transition: 'all 0.2s', overflow: 'hidden'
}}>
);
})}
);
}
function Step2({ data, set }) {
const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
const years = ['2026','2027'];
const monthGuide = {
'Jan': 'Calving season · Ndutu', 'Feb': 'Calving peak · Ndutu', 'Mar': 'Green & quiet',
'Apr': 'Long rains · low season', 'May': 'Green season finishes', 'Jun': 'Dry begins · excellent',
'Jul': 'Migration in central/north', 'Aug': 'River crossings begin', 'Sep': 'Crossings peak ★',
'Oct': 'Crossings continue', 'Nov': 'Short rains · transition', 'Dec': 'Migration south · festive'
};
return (
{[['flexible','I have flexibility'],['set','My dates are set']].map(([k,l]) => (
set('when', k)} style={{
flex: 1, padding: '14px',
border: '1px solid ' + (data.when === k ? 'var(--ink)' : 'var(--line)'),
background: data.when === k ? 'var(--ink)' : 'transparent',
color: data.when === k ? 'var(--bone)' : 'var(--ink)',
borderRadius: 'var(--radius-md)', fontSize: 14, fontWeight: 500
}}>{l}
))}
Target month
{months.map((m, i) => (
set('month', m.toLowerCase())} style={{
padding: '14px 0', borderRadius: 6, fontSize: 13, fontFamily: 'var(--mono)',
textTransform: 'uppercase', letterSpacing: '0.08em',
background: data.month === m.toLowerCase() ? 'var(--moss)' : 'transparent',
color: data.month === m.toLowerCase() ? 'var(--bone)' : 'var(--ink)',
border: '1px solid ' + (data.month === m.toLowerCase() ? 'var(--moss)' : 'var(--line)')
}}>{m}
))}
{months.find(m => m.toLowerCase() === data.month)}
{monthGuide[months.find(m => m.toLowerCase() === data.month)]}
Year
{years.map(y => (
set('year', y)} style={{
padding: '12px 24px', borderRadius: 6, fontFamily: 'var(--serif)', fontSize: 22,
background: data.year === y ? 'var(--ink)' : 'transparent',
color: data.year === y ? 'var(--bone)' : 'var(--ink)',
border: '1px solid ' + (data.year === y ? 'var(--ink)' : 'var(--line)')
}}>{y}
))}
Trip length
{data.days} days
set('days', +e.target.value)} style={{ width: '100%', marginTop: 8, accentColor: 'var(--moss)' }}/>
3 12 21
);
}
function Step3({ data, set }) {
return (
Adults (16+)
set('travelers', Math.max(1, data.travelers-1))} style={{
width: 44, height: 44, borderRadius: '50%', border: '1px solid var(--line)'
}}>
{data.travelers}
set('travelers', Math.min(12, data.travelers+1))} style={{
width: 44, height: 44, borderRadius: '50%', border: '1px solid var(--line)'
}}>
Children (under 16)
set('kids', Math.max(0, data.kids-1))} style={{
width: 44, height: 44, borderRadius: '50%', border: '1px solid var(--line)'
}}>
{data.kids}
set('kids', Math.min(8, data.kids+1))} style={{
width: 44, height: 44, borderRadius: '50%', border: '1px solid var(--line)'
}}>
🛻 Vehicle suggestion
{data.travelers + data.kids <= 4 ? 'One Toyota Land Cruiser fits comfortably. Each guest gets a window — guaranteed.' :
data.travelers + data.kids <= 7 ? 'One Land Cruiser, but we recommend two for guaranteed window seats.' :
'Two Land Cruisers travelling in convoy with two guides.'}
);
}
function Step4({ data, set, toggle }) {
return (
Pace
{[
['relaxed','Relaxed','Long mornings, slow drives'],
['balanced','Balanced','Mix of activity and rest'],
['intense','Intense','Maximum game-driving'],
].map(([k,t,d]) => (
set('pace', k)} style={{
padding: 16, borderRadius: 'var(--radius-md)', textAlign: 'left',
border: '1px solid ' + (data.pace === k ? 'var(--ink)' : 'var(--line)'),
background: data.pace === k ? 'var(--ink)' : 'transparent',
color: data.pace === k ? 'var(--bone)' : 'var(--ink)'
}}>
{t}
{d}
))}
Accommodation style (multi)
{['Tented','Lodge','Mobile camp','Treehouse','Boutique hotel'].map(a => {
const sel = data.accommodation.includes(a.toLowerCase().replace(' ','-'));
const k = a.toLowerCase().replace(' ','-');
return (
toggle('accommodation', k)} className="chip" style={{
cursor: 'pointer', padding: '8px 14px', fontSize: 12,
background: sel ? 'var(--moss)' : 'transparent',
color: sel ? 'var(--bone)' : 'var(--ink)',
borderColor: sel ? 'var(--moss)' : 'var(--line)'
}}>{a}
);
})}
Style of trip
{[
['mid','Mid-luxury','$3–6k pp'],
['lux','Luxury','$6–12k pp'],
['ultra','Ultra-luxury','$12k+ pp'],
].map(([k,t,p]) => (
set('style', k)} style={{
padding: 16, borderRadius: 'var(--radius-md)', textAlign: 'left',
border: '1px solid ' + (data.style === k ? 'var(--ink)' : 'var(--line)'),
background: data.style === k ? 'var(--ink)' : 'transparent',
color: data.style === k ? 'var(--bone)' : 'var(--ink)'
}}>
{t}
{p}
))}
Approximate budget per person
${data.budget.toLocaleString()}
set('budget', +e.target.value)} style={{ width: '100%', marginTop: 8, accentColor: 'var(--moss)' }}/>
$2k $10k $20k+
);
}
function Step5({ data, set }) {
return (
Full name
set('name', e.target.value)} placeholder="Hannah Reiss"/>
Email
set('email', e.target.value)} placeholder="hannah@example.com" type="email"/>
Country
set('country', e.target.value)}>
Select…
{['United Kingdom','United States','Canada','Germany','France','Australia','Japan','India','Other'].map(c => {c} )}
WhatsApp / Phone (optional)
set('phone', e.target.value)} placeholder="+44 …"/>
Anything we should know? (optional)
);
}
function Summary({ label, v }) {
return (
);
}
function Confirmation({ data }) {
return (
Asante sana, {data.name || 'traveler'}.
Your inquiry is in our hands. A Tanzanian planner will read every word and reply within 24 hours — usually with a draft itinerary attached.
Reference
JS-2026-{Math.floor(Math.random()*9000+1000)}
);
}
Object.assign(window, { Inquiry });