// siteSections.jsx — non-video sections of the Terra website
// ───── Reusable ─────
function SectionHeader({ eyebrow, title, titleAccent, kicker }) {
return (
{eyebrow}
{title}{titleAccent && {titleAccent} }
{kicker &&
{kicker}
}
);
}
// ───── Top nav ─────
function Nav() {
const [scrolled, setScrolled] = React.useState(false);
const [open, setOpen] = React.useState(false);
React.useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 40);
window.addEventListener('scroll', onScroll);
return () => window.removeEventListener('scroll', onScroll);
}, []);
const close = () => setOpen(false);
return (
<>
>
);
}
// ───── Stats strip (between videos) ─────
function StatsStrip() {
const stats = [
{ v: '150+', l: 'Clients served' },
{ v: '3.2x', l: 'Avg. ROI increase' },
{ v: '24/7', l: 'AI support' },
{ v: '98%', l: 'Client retention' },
];
return (
{stats.map((s, i) => (
))}
);
}
// ───── Video preview card (under services grid) ─────
// Architecture notes:
// - Preview video: muted/autoplay/loop with preload="metadata" (keeps page light — no 5x MP4 preloads).
// - Modal is always in the DOM (display toggled) so the element survives across opens.
// - On click we SYNCHRONOUSLY unmute, build the AudioContext + 8x GainNode, and call v.play() —
// iOS Safari requires play() within the user-gesture stack frame; any await/useEffect hop breaks it.
// - We NEVER call v.load() (it resets currentTime and kills startTime).
// - startTime is applied on loadedmetadata and enforced via timeupdate so users can't scrub before it.
function VideoPreviewCard({ src = 'IMG_1914.mp4', title = 'Behind the receipts.', subtitle = 'Terra · Studio cut · 00:30', tag = 'CLIP / 01', startTime = 0, modalStartTime }) {
const [open, setOpen] = React.useState(false);
const previewRef = React.useRef(null);
const modalVideoRef = React.useRef(null);
const audioSetup = React.useRef(false);
const effectiveStart = (modalStartTime != null ? modalStartTime : startTime) || 0;
// Click handler — MUST run fully synchronously inside the user gesture for iOS Safari.
const handleOpen = React.useCallback(() => {
const v = modalVideoRef.current;
if (v) {
// 1) Unmute + full volume (the GainNode will multiply this by 8).
v.muted = false;
v.volume = 1;
// 2) Build AudioContext + 8x GainNode once, inside the gesture so mobile unlocks it.
if (!audioSetup.current) {
try {
const Ctx = window.AudioContext || window.webkitAudioContext;
if (Ctx) {
const ctx = new Ctx();
const source = ctx.createMediaElementSource(v);
const gain = ctx.createGain();
gain.gain.value = 8;
source.connect(gain);
gain.connect(ctx.destination);
if (ctx.state === 'suspended') ctx.resume();
audioSetup.current = true;
}
} catch (e) { /* ignore — some browsers reject a second createMediaElementSource */ }
}
// 3) Apply startTime. Safe now if metadata is ready, otherwise set on loadedmetadata.
// NOTE: never call v.load() here — it would reset currentTime to 0.
const applyStart = () => {
if (effectiveStart > 0) {
try { v.currentTime = effectiveStart; } catch (e) {}
}
};
if (v.readyState >= 1) applyStart();
else v.addEventListener('loadedmetadata', applyStart, { once: true });
// 4) play() — synchronous call inside gesture. iOS Safari unlocks audio here.
const p = v.play();
if (p && typeof p.catch === 'function') p.catch(() => {});
}
setOpen(true);
}, [effectiveStart]);
// Close / cleanup effect. Does NOT call play() — that belongs in the click handler only.
React.useEffect(() => {
if (!open) {
const v = modalVideoRef.current;
if (v) {
v.pause();
v.muted = true;
// Reset to startTime (not 0 — so the preview loop stays in-clip next time).
try { v.currentTime = effectiveStart; } catch (e) {}
}
document.body.style.overflow = '';
return;
}
document.body.style.overflow = 'hidden';
const v = modalVideoRef.current;
const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
const onTimeUpdate = () => {
if (effectiveStart > 0 && v && v.currentTime < effectiveStart - 0.1) {
try { v.currentTime = effectiveStart; } catch (e) {}
}
};
const onSeeking = onTimeUpdate;
window.addEventListener('keydown', onKey);
if (v) {
v.addEventListener('timeupdate', onTimeUpdate);
v.addEventListener('seeking', onSeeking);
}
return () => {
window.removeEventListener('keydown', onKey);
if (v) {
v.removeEventListener('timeupdate', onTimeUpdate);
v.removeEventListener('seeking', onSeeking);
}
};
}, [open, effectiveStart]);
const onOverlayClick = (e) => {
if (e.target === e.currentTarget) setOpen(false);
};
return (
<>
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleOpen(); } }}
aria-label="Play clip with sound"
>
LIVE PREVIEW
{tag}
{ if (startTime) e.currentTarget.currentTime = startTime; }}
onPlay={(e) => { if (startTime && e.currentTarget.currentTime < startTime) e.currentTarget.currentTime = startTime; }}
onTimeUpdate={(e) => { if (startTime && e.currentTarget.currentTime < startTime - 0.1) e.currentTarget.currentTime = startTime; }}
/>
{/* Modal stays mounted (display toggled) so the ref is stable across opens. */}
NOW PLAYING
setOpen(false)}
aria-label="Close video"
>
CLOSE ✕
>
);
}
// ───── Services grid ─────
function Services() {
const services = [
{ tag: 'MOST POPULAR', name: 'AI Voice Receptionist', desc: 'Never miss a call. Answers 24/7, qualifies leads, transfers calls & books appointments.', feats: ['24/7 availability', 'Call transfer', 'Calendar booking', 'SMS follow-up'], featured: true },
{ name: 'AI Chatbot', desc: 'Engages visitors instantly — captures leads, answers FAQs, guides prospects through your sales funnel.', feats: ['Lead capture', 'FAQ automation', 'Live chat handoff', 'CRM integration'] },
{ name: 'Paid Advertising', desc: 'Data-driven Google & Meta campaigns. Every dollar optimized to maximize return on ad spend.', feats: ['Google Ads', 'Meta Ads', 'Retargeting', 'A/B testing'] },
{ name: 'SEO & Content', desc: 'Rank higher and attract organic traffic with strategic content and technical SEO that compounds.', feats: ['Keyword research', 'On-page SEO', 'Link building', 'Content strategy'] },
{ name: 'Email & SMS', desc: 'Nurture leads and retain customers with automated sequences and targeted SMS campaigns that convert.', feats: ['Automation flows', 'Segmentation', 'SMS campaigns', 'Analytics'] },
{ name: 'Brand & Web Design', desc: 'From logo to landing page — visuals that communicate value and convert visitors into clients.', feats: ['Brand identity', 'Landing pages', 'UI/UX design', 'Conversion optimization'] },
{ name: 'Social Media', desc: 'Consistent, engaging presence across every platform. Content that builds audience and drives results.', feats: ['Content creation', 'Scheduling', 'Community mgmt', 'Analytics'] },
{ name: 'Analytics & Reporting', desc: 'Clear, actionable reports so you always know what\'s working. Full-funnel tracking from click to closed deal.', feats: ['Dashboard setup', 'Monthly reports', 'Attribution', 'Funnel analysis'] },
];
return (
{services.map((s, i) => (
{s.tag &&
{s.tag}
}
{String(i + 1).padStart(2, '0')}
{s.name}
{s.desc}
{s.feats.map((f, j) => (
{f}
))}
))}
);
}
// ───── Results — case cards ─────
function Results() {
const cases = [
{ vert: 'Home Services', metric: '$127K', label: 'tracked revenue / 90 days', client: 'Summit Roofing Co.', detail: 'Launched, tested & scaled to 3.8x ROAS in 90 days from a cold account.' },
{ vert: 'HVAC & Plumbing', metric: '0', label: 'missed calls — ever', client: 'Prestige Home Services', detail: 'From 15 lost calls a day to a fully automated front desk — 24/7.' },
{ vert: 'Legal Services', metric: '4.1x', label: 'return on ad spend', client: 'ClearPath Law Group', detail: 'Rebuilt Google Ads, added AI chatbot, cut cost-per-consultation by 61%.' },
{ vert: 'Aesthetics & Wellness', metric: '+340%', label: 'organic enquiries', client: 'Luxe Skin Clinic', detail: 'Ranked #1 locally in 90 days — booked 6 weeks out via SEO, Meta & social.' },
{ vert: 'Real Estate', metric: '#1', label: 'for 14 local keywords', client: 'NestFinder Realty', detail: 'Rebuilt content around buyer intent; organic now outperforms every paid channel.' },
{ vert: 'Financial Services', metric: '−61%', label: 'cost per lead', client: 'LoanBridge Financial', detail: 'Restructured paid media, tightened targeting, added email nurturing.' },
];
return (
{cases.map((c, i) => ( ))}
);
}
function CaseCard({ c, i }) {
const ref = React.useRef(null);
const seen = useInViewOnce(ref, 0.18);
const delay = i * 110;
return (
{c.vert}
CASE / {String(i + 1).padStart(2, '0')}
{c.metric}
{c.label}
{c.client}
{c.detail}
);
}
// ───── Process ─────
function Process() {
const steps = [
{ n: '01', t: 'Discovery Call', d: 'Deep-dive into your goals, audience, and biggest gaps. You leave with a written 1-page growth audit — whether you hire us or not.' },
{ n: '02', t: 'Custom Strategy', d: 'Tailored growth plan with specific channels, timelines, and KPIs. You get a written proposal with exact deliverables before signing anything.' },
{ n: '03', t: 'Build & Launch', d: 'Campaigns live, AI configured, assets designed fast. Most clients are live within 2 weeks and receive a launch report on day one.' },
{ n: '04', t: 'Optimize & Scale', d: 'Monthly performance reports, live dashboard access, and a strategy call every 30 days. Real data drives every decision.' },
];
return (
{steps.map((s, i) => ( ))}
);
}
function StepCard({ s, i, isLast }) {
const ref = React.useRef(null);
const seen = useInViewOnce(ref, 0.25);
const delay = i * 160;
return (
);
}
// ───── Testimonials ─────
// Hook: fires true once the element is visible, then stays true.
function useInViewOnce(ref, threshold = 0.15) {
const [seen, setSeen] = React.useState(false);
React.useEffect(() => {
if (!ref.current || seen) return;
const io = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) { setSeen(true); io.disconnect(); }
},
{ threshold }
);
io.observe(ref.current);
return () => io.disconnect();
}, [seen, threshold]);
return seen;
}
// Prints text character-by-character once visible (receipt printer vibe)
function TypePrint({ text, delay = 0, speed = 28, className, as = 'span', onDone }) {
const ref = React.useRef(null);
const seen = useInViewOnce(ref);
const [n, setN] = React.useState(0);
React.useEffect(() => {
if (!seen) return;
let raf, start = null;
const total = text.length;
const step = (ts) => {
if (start == null) start = ts;
const elapsed = ts - start - delay;
if (elapsed < 0) { raf = requestAnimationFrame(step); return; }
const chars = Math.min(total, Math.floor(elapsed / speed));
setN(chars);
if (chars < total) raf = requestAnimationFrame(step);
else if (onDone) onDone();
};
raf = requestAnimationFrame(step);
return () => cancelAnimationFrame(raf);
}, [seen, text, speed, delay]);
const Tag = as;
return (
{text.slice(0, n)}
{seen && n < text.length && ▍ }
);
}
function QuoteCard({ qt, i }) {
const ref = React.useRef(null);
const seen = useInViewOnce(ref, 0.2);
const delay = i * 140;
return (
RECEIPT №
{String(i + 1).padStart(4, '0')}
“
{qt.q}
);
}
function Testimonials() {
const quotes = [
{ q: "We went from drowning in missed calls to a fully booked schedule in three weeks. The AI receptionist paid for itself the first week.", name: 'Denise M.', role: 'Owner, Prestige Home Services', stat: '0 missed calls' },
{ q: "I've worked with five agencies. Terra is the first one that actually gives a damn about the number next to my name.", name: 'Marcus T.', role: 'Managing Partner, ClearPath Law', stat: '4.1x ROAS' },
{ q: "Fully booked six weeks out. From basically invisible to the #1 clinic in my zip code in 90 days. Not hype — that's my calendar.", name: 'Amelia K.', role: 'Founder, Luxe Skin Clinic', stat: '+340% enquiries' },
{ q: "They don't sell you a package. They build the thing you actually need, then they keep tuning it. That's why I stayed.", name: 'Ray P.', role: 'CEO, Summit Roofing Co.', stat: '$127K / 90 days' },
];
const headRef = React.useRef(null);
const seen = useInViewOnce(headRef, 0.2);
return (
04 — WHAT CLIENTS SAY
Not reviews.
{seen ? (
) : (
Receipts with names on them.
)}
{quotes.map((qt, i) => (
))}
);
}
// ───── Why Terra ─────
function WhyCard({ p, i }) {
const ref = React.useRef(null);
const seen = useInViewOnce(ref, 0.2);
const delay = i * 140;
return (
0{i + 1}
0{i + 1}
{p.t}
{p.d}
);
}
function WhyTerra() {
const points = [
{ t: 'Barbados-Based', d: 'We know your market, your customers, and the local competitive landscape. No guessing from overseas.' },
{ t: 'AI-Native', d: 'You get the AI Voice Receptionist as part of your stack — not as an upsell. It\'s built into how we operate.' },
{ t: 'Founder-Led', d: 'You work directly with Dmitri — not a junior account manager. Your strategy never gets lost in handoffs.' },
];
return (
{points.map((p, i) => ( ))}
);
}
// ───── CTA + footer ─────
function Contact() {
const [sent, setSent] = React.useState(false);
const handleSubmit = (e) => {
e.preventDefault();
const fd = new FormData(e.target);
const name = fd.get('name');
const biz = fd.get('business');
const msg = fd.get('issue');
window.location.href = `mailto:d.defreitas@wustl.edu?subject=Terra Enquiry from ${encodeURIComponent(name)} — ${encodeURIComponent(biz)}&body=${encodeURIComponent(`Name: ${name}\nBusiness: ${biz}\n\nWhat's not working:\n${msg}`)}`;
setSent(true);
};
return (
);
}
function Footer() {
return (
);
}
Object.assign(window, {
Nav, StatsStrip, Services, Results, Process, Testimonials, WhyTerra, Contact, Footer,
useInViewOnce, TypePrint, VideoPreviewCard,
});