);
}
// ===== PRICE + RISK REVERSAL line =====
function PriceLine({ dark = false, align = 'center', compact = false }) {
const sub = dark ? 'rgba(255,255,255,0.72)' : LV.g600;
return (
$59.99· You're only charged after your session
);
}
// ===== TRUST CUE PILL (single, for above the fold) — no review ratings =====
function TrustPill({ dark = false }) {
return (
America’s Largest & Most Trusted· since 2005
);
}
// ===== THE LIVE-CALL HERO MOCK =====
// A believable live video-inspection call. Photo content is a clearly-marked, swappable slot;
// the call chrome (LIVE badge, timer, inspector lower-third, controls) + cycling inspector
// captions sell the concept in ~3 seconds with sound off.
const CALL_CAPTIONS = [
"Okay — pop the hood and pan slowly across the engine for me.",
"See that seam? Factory paint. No accident on this corner — good sign.",
"Now show me the driver-side front tire… tread's getting low there.",
"Start it up and point at the dash — checking for warning lights.",
"No leaks underneath. This one's looking solid so far."];
function VideoCallMock({ rounded = 24, cycle = true, label = "Live: you film the car, the inspector guides you", inspector = "Marcus T." }) {
const [cap, setCap] = useState(0);
const [secs, setSecs] = useState(252);
useEffect(() => {
if (!cycle) return;
const a = setInterval(() => setCap((c) => (c + 1) % CALL_CAPTIONS.length), 3200);
const b = setInterval(() => setSecs((s) => s + 1), 1000);
return () => {clearInterval(a);clearInterval(b);};
}, [cycle]);
const mm = String(Math.floor(secs / 60)).padStart(2, '0');
const ss = String(secs % 60).padStart(2, '0');
return (
{/* the "video" of the car (swappable) */}
{/* moving scan highlight */}
{/* top status bar */}
LIVE
{mm}:{ss}
{/* inspector PiP */}
{inspector}
{/* caption lower-third (inspector speaking) */}
{inspector} · Certified Inspector
“{CALL_CAPTIONS[cap]}”
{/* call controls */}
{/* honest caption */}
{label}
);
}
// ===== REAL EXPLAINER VIDEO (YouTube) =====
// The actual on-page explainer that's outperforming expectations. Two modes:
// - "ambient": silent autoplay + loop, no controls (Variant A "show it" hero)
// - "facade": branded poster + play button; tapping loads the iframe with sound
const YT_ID = '-nhxJclhPHk';
const YT_THUMB = `https://i.ytimg.com/vi/${YT_ID}/hqdefault.jpg`;
// Lightweight analytics hook: pushes to dataLayer (GA4) AND fires a DOM event.
// Claude Code maps these to GA4 events at integration time (see dev handoff notes).
function lvTrack(event, params) {
try {
const detail = Object.assign({ event: event }, params || {});
(window.dataLayer = window.dataLayer || []).push(detail);
window.dispatchEvent(new CustomEvent('lv-track', { detail: detail }));
} catch (e) {}
}
function ExplainerVideo({ mode = 'ambient', rounded = 18, label = 'See how it works', sub = '60-second explainer', variant = '' }) {
// Native HTML5 player. Behavior: autoplay ONCE muted on view → on end, return to a
// start screen with a Play button → tapping plays from the start WITH sound.
const vref = useRef(null);
const wrapRef = useRef(null);
const [phase, setPhase] = useState('idle'); // 'idle' | 'intro' (muted once) | 'cover' (start screen) | 'playing' (sound)
const poster = window.LS_VIDEO_POSTER || YT_THUMB;
const srcAvail = !!window.LS_VIDEO_SRC;
const seen = useRef({ q25: false, q50: false, q75: false, started: false });
// Autoplay muted once when scrolled into view (better for mobile + battery).
useEffect(() => {
const v = vref.current,w = wrapRef.current;
if (!v || !w || !srcAvail) return;
let started = false;
const io = new IntersectionObserver((ents) => {
ents.forEach((e) => {
if (e.isIntersecting && !started) {
started = true;
v.muted = true;v.currentTime = 0;
const p = v.play();
if (p && p.catch) p.catch(() => setPhase('cover'));
setPhase('intro');
lvTrack('video_autoplay_start', { variant: variant, muted: true });
}
});
}, { threshold: 0.5 });
io.observe(w);
return () => io.disconnect();
}, [srcAvail]);
const onEnded = () => {
lvTrack('video_complete', { variant: variant, with_sound: phase === 'playing' });
setPhase('cover');
};
const onTime = () => {
const v = vref.current;if (!v || !v.duration) return;
const pct = v.currentTime / v.duration;
const s = seen.current;
if (pct >= 0.25 && !s.q25) {s.q25 = true;lvTrack('video_progress', { variant: variant, pct: 25 });}
if (pct >= 0.5 && !s.q50) {s.q50 = true;lvTrack('video_progress', { variant: variant, pct: 50 });}
if (pct >= 0.75 && !s.q75) {s.q75 = true;lvTrack('video_progress', { variant: variant, pct: 75 });}
};
const playWithSound = () => {
const v = vref.current;if (!v) return;
v.muted = false;v.currentTime = 0;
setPhase('playing');
lvTrack('video_sound_on', { variant: variant });
const p = v.play();if (p && p.catch) p.catch(() => {});
};
return (
{srcAvail ?
:
}
{/* fallback (no native file yet): show a live badge over the looping embed */}
{!srcAvail &&
SEE HOW IT WORKS
}
{/* INTRO: muted auto-play once — show a live badge + tap-for-sound */}
{srcAvail && phase === 'intro' &&
SEE HOW IT WORKS
}
{/* COVER: start screen with a Play button (idle before view, or after the silent play-through) */}
{srcAvail && (phase === 'idle' || phase === 'cover') &&
}
);
}
// ===== STICKY CTA BAR =====
function StickyCTA({ label = 'Book your Live Inspection', price = true, onClick, track = 'sticky-cta', secondary }) {
return (
{price &&
$59.99
only after it happens
}
{secondary}
);
}
// ===== HOW IT WORKS (3-step) =====
function HowItWorks({ accent = LV.red, compact = false }) {
const steps = [
{ ic: ICON.wallet, t: 'Book in 2 minutes', d: 'Reserve your session online. Use it now, or save the link for when you go see the car.' },
{ ic: ICON.video, t: 'Connect on video — no app', d: 'Tap one link in your phone’s browser. A certified inspector joins you live.' },
{ ic: ICON.eye, t: 'Walk the car together', d: 'Up to 20 minutes. They guide every check and point out what an untrained eye would miss.' }];
return (
{steps.map((s, i) =>
{i + 1}
{s.t}
{s.d}
)}
);
}
// ===== TRUST / PROOF BLOCK =====
function TrustStats() {
const stats = [
{ n: 'America’s #1', l: 'largest & most trusted' },
{ n: 'Since 2005', l: '20 years on buyers’ side' },
{ n: 'All 50 states', l: 'nationwide coverage' },
{ n: 'Specialists', l: 'pre-purchase is all they do' }];
return (
{stats.map((s, i) =>
{s.n}
{s.l}
)}
);
}
// ===== TESTIMONIALS =====
const TESTIMONIALS = [
{ name: 'Dana R.', loc: 'Austin, TX', stars: 5, text: 'I was buying a car three states away and couldn’t fly out. The inspector walked the whole car with the seller on video and caught a repaint I’d never have spotted. Best $60 I’ve spent.' },
{ name: 'Marie L.', loc: 'Columbus, OH', stars: 5, text: 'I’m not a “car person” at all. Having a real expert on the call asking the seller the right questions made me feel like I finally had someone in my corner. Calm, kind, and clear.' },
{ name: 'Robert P.', loc: 'Phoenix, AZ', stars: 5, text: 'Booked it the night before I drove out to see the truck. Tapped the link in the lot, no app, connected in about a minute. Found a coolant leak — I walked away and saved myself a headache.' },
{ name: 'Sandra K.', loc: 'Tampa, FL', stars: 5, text: 'At 61, I dreaded buying a used car alone. The certified inspector was patient and explained everything in plain language. I bought with total confidence.' }];
function TestimonialCard({ t }) {
return (
);
}
// ===== FAQ =====
const FAQ_ITEMS = [
{ q: 'Is this legit? Who am I actually talking to?', a: 'Yes. You’re joined by a professional, certified inspection specialist — many are former mechanics who now do pre-purchase inspections full-time. It’s all they do, which makes them sharper at spotting problems than a general mechanic. Lemon Squad is America’s largest and most trusted vehicle inspection company, doing pre-purchase inspections since 2005.' },
{ q: 'Do I need to download an app?', a: 'No app required. You join right from your phone’s web browser — just tap the link we send and you’re connected.' },
{ q: 'What if the seller flakes, or I need to cancel?', a: 'You’re only charged after the session happens. If it doesn’t go through, you’re not billed — and you can reschedule for free.' },
{ q: 'Can the inspector tell me whether to buy it?', a: 'They’ll point out exactly what they see — wear, damage, red flags, and what repairs might cost — so you can decide from a position of strength. The call is your expert second opinion; the decision stays yours.' },
{ q: 'Can I upgrade to a full in-person inspection later?', a: 'Absolutely. A Live Video Inspection is the fast, lower-cost option at $59.99. Full on-site inspections start at $220 if you want hands-on diagnostics.' }];
function FAQItem({ item, open, onToggle, idx }) {
return (
:
null;
return { show, node };
}
// ===== UNIFIED LEAD FORM (every CTA opens this) =====
// Small form: name, email, mobile + SMS & email opt-in. Themeable per variant.
function openLead(detail) {window.dispatchEvent(new CustomEvent('lv-open-lead', { detail: detail || {} }));}
function ConsentLink({ href, children }) {
return e.stopPropagation()} style={{ color: LV.ink, textDecoration: 'underline', whiteSpace: 'nowrap' }}>{children};
}
// Collapsible consent text: shows a short lead-in with a "Read more" toggle so the full
// legal disclosure (with links) fits without crowding the form. The toggle and links
// stopPropagation so they don't flip the checkbox.
function ConsentText({ short, children }) {
const [open, setOpen] = useState(false);
return (
{open ? children : short}{' '}
);
}
function LeadCheck({ on, onToggle, label, accent }) {
return (
We’ll send your Live Inspection link to {f.phone || 'your phone'}{f.mail && f.email ? ' and ' + f.email : ''}. Tap it when you’re at the car — today or later this week. You’re only charged after your session happens.
:
{t.eyebrow}
Get your inspection link
Get your link now and use it whenever you’re at the car. No payment until your session actually happens.
setF({ ...f, mail: !f.mail })} accent={t.accent} label={
I agree to receive promotional and informational emails from Lemon Squad and our partners regarding my inspection, promotions, and related services. You may unsubscribe at any time. Privacy Policy
} />
setF({ ...f, sms: !f.sms })} accent={t.accent} label={
I agree to receive phone calls and recurring automated SMS/text messages from Lemon Squad and our partners at the phone number provided, including promotional and informational messages related to my inspection and related services. Message frequency varies. Message and data rates may apply. Reply STOP to opt out of text messages at any time. Consent is not a condition of purchase. TermsPrivacy Policy
} />
{COPY.guaranteeHead}
$59.99 · only charged after your session
}
);
}
// ===== ILLUSTRATED LIVE WALKTHROUGH (made-up car + inspector + cycling focus & quotes) =====
const WALK = [
{ label: 'Full walkaround', pl: '50%', pt: '54%', q: "Let’s start with a slow walkaround — pan down the side for me." },
{ label: 'Hood & engine', pl: '72%', pt: '40%', q: "Pop the hood. Belts and fluid lines look clean — that’s a good sign." },
{ label: 'Front tire', pl: '70%', pt: '72%', q: "Show me the front tire… tread’s getting a little low on this edge." },
{ label: 'Doors & panel gaps', pl: '45%', pt: '44%', q: "Even gaps along the doors — no sign of accident repair here." },
{ label: 'Lights & rear', pl: '22%', pt: '50%', q: "Tap the brakes for me — just confirming all the lights work." }];
function CarArt() {
// a simple, friendly side-profile car (made-up "image" of the car under inspection)
return (
);
}
function InspectorAvatarArt({ size = 84 }) {
// a simple, friendly "person on a video tile" — made-up inspector
return (
);
}
function CarSceneMock({ accent = LV.red, rounded = 24, inspector = 'Marcus T.', aspect = '3 / 4' }) {
const [i, setI] = useState(0);
const [secs, setSecs] = useState(252);
useEffect(() => {
const a = setInterval(() => setI((c) => (c + 1) % WALK.length), 3200);
const b = setInterval(() => setSecs((s) => s + 1), 1000);
return () => {clearInterval(a);clearInterval(b);};
}, []);
const mm = String(Math.floor(secs / 60)).padStart(2, '0');
const ss = String(secs % 60).padStart(2, '0');
const w = WALK[i];
return (
{/* moving inspection highlight */}
{/* part label chip following highlight */}
{w.label}
{/* top status */}
LIVE
{mm}:{ss}
{/* inspector PiP (made-up person) */}
{inspector}
{/* caption (inspector speaking) */}
{inspector} · Certified Inspector
“{w.q}”
{/* call controls */}
);
}
// ===== CANONICAL COPY (shared across A/B/C so all three say the same thing) =====
const COPY = {
heroLead: "See what’s wrong, negotiate a better price, and ",
heroAccent: "buy with confidence.",
heroSub: "A certified inspector joins you on live video while you’re at the car — a quick, expert once-over that shows you exactly what matters, in plain language.",
subhead: "A certified inspector joins you on live video and checks a used car with you — pointing out what an untrained eye would miss, so you buy with confidence.",
bullets: [
["A certified inspection specialist", " — not a general mechanic — right there with you at the car."],
["No app needed.", " Just tap the link we send — it opens right in your phone’s browser."],
["Get your link now,", " then use it when you’re standing at the car."]],
steps: [
["Book in 2 minutes", "Reserve online now. Use it now, or save the link for when you go see the car."],
["Connect on video — no app", "Tap one link in your phone’s browser. A certified inspector joins you live."],
["Walk the car together", "Up to 20 minutes. They guide every check and flag what an untrained eye misses."]],
valueHead: "Quickly spot the details that save you money",
valueLead: "In a few minutes, a certified inspector helps you see exactly what to check — and what’s wrong.",
valueBody: " Use it to negotiate a better price — or walk away with confidence. At $59.99, this service really pays for itself.",
guaranteeHead: "100% satisfaction guarantee",
guaranteeBody: "Not happy with your session? Get your money back — and you’re only charged after your session happens.",
guaranteeShort: "Backed by our 100% satisfaction guarantee.",
ctaSub: "Use it at the car · pay only after your session"
};
// ===== GUARANTEE ROW (themeable; appears on all three) =====
function GuaranteeRow({ theme = {}, style }) {
const t = { bg: LV.greenBg, ring: '#fff', sealBg: LV.redBg, sealIcon: LV.red, ink: LV.ink, sub: LV.g600, border: LV.g200, ...theme };
return (
{COPY.guaranteeHead}
{COPY.guaranteeBody}
);
}
// ===== AI ASSISTANT (themeable; scripted Q&A → booking handoff; used on all three) =====
const ASSIST_QA = [
{ chip: 'What is this?', a: "Live Video Inspection is a video call with a certified inspection specialist. While you’re at the car, you point your phone’s camera at it and they guide you through every check in real time — pointing out anything an untrained eye would miss. Up to 20 minutes, $59.99." },
{ chip: 'How does it work?', a: "Three steps: 1) Reserve online. 2) When you’re at the car, tap the link we send — no app, it opens in your browser. 3) A certified inspector joins you on video and walks the car with you. Get your link now and use it whenever you go see it." },
{ chip: 'When am I charged?', a: "Only when the session actually happens. If the seller flakes or you cancel, you’re not billed — and rescheduling is free. It’s $59.99, backed by our 100% satisfaction guarantee." }];
const ASSIST_FALLBACK = "Great question. The short version: a certified inspector joins you on a live video call to check the car with you — no app, $59.99, and you’re only charged after it happens. Want me to set you up?";
const ASSIST_THEME = {
accent: LV.red, panelBg: LV.g100, headerBg: '#fff',
botBg: '#fff', botText: LV.g700, botBorder: LV.g200,
ink: LV.ink, sub: LV.g500, border: LV.g200, online: LV.green,
chipBg: LV.redBg, chipBorder: LV.redBorder, chipText: LV.redDark,
fieldBg: '#fff', fieldText: LV.ink
};
// Turn bare URLs in the assistant's text into clickable links (the model replies in
// plain text, so without this a "lemonsquad.com/exotic" link wouldn't be tappable).
function lvLinkify(text) {
const s = String(text == null ? '' : text);
return s.split(/(https?:\/\/[^\s]+)/g).map((p, i) => {
if (!/^https?:\/\//.test(p)) return p;
const href = p.replace(/[).,!?:;]+$/, ''); // don't let trailing punctuation into the URL
return {p};
});
}
// Stable, module-level message bubble. Defining this INSIDE LiveAssistant made it a
// new component type on every render, so each keystroke / streamed token remounted the
// whole list and replayed the fade-in animation (the "flashing"). Hoisting it fixes that.
function AssistantBubble({ from, t, children }) {
const me = from === 'me';
return (
{!me &&
}
{me ? children : lvLinkify(children)}
);
}
function LiveAssistant({ theme = {}, track = 'assistant', variant = '' }) {
const t = { ...ASSIST_THEME, ...theme };
const pfx = variant ? `var${variant}-` : '';
const [msgs, setMsgs] = useState([{ from: 'bot', text: "Hi! Ask me anything about Live Video Inspection." }]);
const [typing, setTyping] = useState(false);
const [showBook, setShowBook] = useState(false);
const [asked, setAsked] = useState([]);
const [input, setInput] = useState('');
const endRef = useRef(null);
useEffect(() => {if (endRef.current) endRef.current.scrollTop = endRef.current.scrollHeight;}, [msgs, typing]);
const busyRef = useRef(false);
// AI endpoint config is injected by ls-ai.js (window.LS_AI_*). The anon key is
// public-by-design; the Gemini key stays server-side in the `ai` edge function.
const AI_ENDPOINT = (typeof window !== 'undefined' && window.LS_AI_ENDPOINT) || '';
const AI_KEY = (typeof window !== 'undefined' && window.LS_AI_KEY) || '';
// Stream the assistant reply from the gateway, appending tokens live.
const streamReply = async (history) => {
busyRef.current = true;
setTyping(true);
if (!AI_ENDPOINT) { // not wired (e.g. offline bundle) — graceful canned reply
setTimeout(() => {
setTyping(false); setShowBook(true); busyRef.current = false;
setMsgs((m) => [...m, { from: 'bot', text: ASSIST_FALLBACK }]);
}, 600);
return;
}
try {
const res = await fetch(AI_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + AI_KEY, apikey: AI_KEY },
body: JSON.stringify({ task: 'live-assistant', input: { messages: history } }),
});
if (!res.ok || !res.body) throw new Error('bad response');
const reader = res.body.getReader();
const dec = new TextDecoder();
let acc = '', started = false;
for (;;) {
const { done, value } = await reader.read();
if (done) break;
const chunk = dec.decode(value, { stream: true });
if (!chunk) continue;
acc += chunk;
if (!started) { started = true; setTyping(false); setMsgs((m) => [...m, { from: 'bot', text: acc }]); }
else setMsgs((m) => { const c = m.slice(); c[c.length - 1] = { from: 'bot', text: acc }; return c; });
}
if (!started) setMsgs((m) => [...m, { from: 'bot', text: ASSIST_FALLBACK }]);
} catch (e) {
setMsgs((m) => [...m, { from: 'bot', text: ASSIST_FALLBACK }]);
} finally {
setTyping(false); setShowBook(true); busyRef.current = false;
}
};
// Send a question: record it, build the chat history, and stream a reply.
const ask = (q) => {
const text = (q || '').trim();
if (!text || busyRef.current) return;
lvTrack('assistant_message', { variant: variant, q: text.slice(0, 120) });
const history = msgs
.filter((x, i) => !(i === 0 && x.from === 'bot')) // drop the opening greeting
.map((x) => ({ role: x.from === 'me' ? 'user' : 'assistant', content: x.text }))
.concat([{ role: 'user', content: text }]);
setMsgs((m) => [...m, { from: 'me', text: text }]);
streamReply(history);
};
const tapChip = (item) => { setAsked((a) => [...a, item.chip]); ask(item.chip); };
const send = () => { if (!input.trim()) return; const v = input.trim(); setInput(''); ask(v); };
const remaining = ASSIST_QA.filter((x) => !asked.includes(x.chip));
// (message bubble hoisted to module-level AssistantBubble — see above)
return (
Ask an expert
● Online · replies instantly
{msgs.map((m, i) => {m.text})}
{typing &&
{[0, 1, 2].map((i) => )}
}
{showBook &&
Only charged after it happens · 100% satisfaction guarantee