Files
nearle_web_nextjs/src/components/home/VoiceCommerceExperience.tsx
2026-07-23 10:26:49 +05:30

183 lines
7.6 KiB
TypeScript

"use client";
import Link from "next/link";
import { motion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { PhoneStage } from "@/components/home/voice/PhoneStage";
import { FloatingRequestCards } from "@/components/home/voice/FloatingRequestCards";
import { VoiceWaves } from "@/components/home/voice/VoiceWaves";
import { viewport } from "@/lib/animations";
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
const EASE_OUT = [0.16, 1, 0.3, 1] as [number, number, number, number];
const FEATURE_CHIPS = [
{ emoji: "🎤", label: "Voice Shopping" },
{ emoji: "🧠", label: "Natural Language AI" },
{ emoji: "🏪", label: "Local Merchant Discovery" },
{ emoji: "⚡", label: "Real-Time Availability" },
];
const badgeVariant = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0, transition: { duration: 0.6, ease: EASE } },
};
const lineStagger = {
hidden: {},
visible: { transition: { staggerChildren: 0.14, delayChildren: 0.12 } },
};
const lineReveal = {
hidden: { y: 48, opacity: 0, filter: "blur(8px)" },
visible: { y: 0, opacity: 1, filter: "blur(0px)", transition: { duration: 0.85, ease: EASE_OUT } },
};
const descVariant = {
hidden: { opacity: 0, y: 22 },
visible: { opacity: 1, y: 0, transition: { duration: 0.6, delay: 0.42, ease: EASE } },
};
const chipStagger = {
hidden: {},
visible: { transition: { staggerChildren: 0.07, delayChildren: 0.56 } },
};
const chipItem = {
hidden: { opacity: 0, y: 12 },
visible: { opacity: 1, y: 0, transition: { duration: 0.45, ease: EASE } },
};
const ctaVariant = {
hidden: { opacity: 0, y: 16 },
visible: { opacity: 1, y: 0, transition: { duration: 0.6, delay: 0.86, ease: EASE } },
};
const phoneVariant = {
hidden: { opacity: 0, y: 40, scale: 0.96 },
visible: { opacity: 1, y: 0, scale: 1, transition: { duration: 1.05, delay: 0.15, ease: EASE_OUT } },
};
// The homepage's product showcase: a live Nearle app running inside the phone,
// so a visitor can watch a spoken request turn into a delivered order without
// installing anything. The Hero above introduces Nearle — this shows the work.
export function VoiceCommerceExperience() {
return (
// Explicit padding rather than the shared .section rhythm: the cards that
// float above and below the phone need more room than 88px to breathe.
<section
className="relative w-full overflow-hidden py-24 lg:py-32"
style={{
background:
"radial-gradient(60% 50% at 78% 32%, rgba(124,58,237,0.13), transparent 70%)," +
"radial-gradient(45% 42% at 12% 22%, rgba(168,85,247,0.10), transparent 70%)," +
"linear-gradient(180deg, #FDFBFF 0%, #F7F3FF 55%, #FAF7FF 100%)",
}}
>
{/* Voice waves sit behind everything, centred on the phone side */}
<VoiceWaves className="left-auto right-0 z-0 hidden w-[62%] lg:block" />
<VoiceWaves className="z-0 lg:hidden" />
<div className="page-container relative z-10">
<div className="grid items-center gap-16 lg:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)] lg:gap-8 xl:gap-12">
{/* ─── LEFT: what it does ─── */}
<motion.div
initial="hidden"
whileInView="visible"
viewport={viewport}
className="order-1 flex flex-col items-center text-center lg:items-start lg:text-left"
>
<motion.span
variants={badgeVariant}
className="inline-flex w-fit items-center gap-1.5 rounded-full border border-[#7C3AED]/15 bg-white px-4 py-1.5 text-xs font-black uppercase tracking-widest text-[#6D28D9] shadow-[0_4px_18px_-6px_rgba(124,58,237,0.35)]"
>
<MaterialIcon icon="graphic_eq" size={14} />
AI Voice Commerce
</motion.span>
<motion.h2
variants={lineStagger}
className="mt-6 font-display text-[32px] font-extrabold tracking-[-0.05em] text-[#16001D] sm:text-[42px] lg:text-[42px] xl:text-[52px]"
>
<div className="overflow-hidden pb-1">
<motion.div variants={lineReveal} className="leading-[0.98]">
Just speak.
</motion.div>
</div>
<div className="overflow-hidden pb-1">
{/* text-balance keeps the mobile wrap as "Nearle handles /
the rest." instead of orphaning "rest." on its own line */}
<motion.div variants={lineReveal} className="text-balance leading-[0.98]">
Nearle handles <span className="text-[#7C3AED]">the rest.</span>
</motion.div>
</div>
</motion.h2>
<motion.p
variants={descVariant}
className="mt-5 max-w-[520px] text-base font-medium leading-[1.6] text-[#4A3F52] md:text-lg"
>
Order products, discover nearby stores, and book trusted local services using
natural conversation.
</motion.p>
<motion.ul
variants={chipStagger}
className="mt-7 flex flex-wrap justify-center gap-2.5 lg:justify-start"
>
{FEATURE_CHIPS.map((chip) => (
<motion.li
key={chip.label}
variants={chipItem}
className="flex items-center gap-2 rounded-full border border-[#7C3AED]/12 bg-white/70 py-2 pl-2.5 pr-4 shadow-[0_6px_20px_-10px_rgba(76,20,140,0.35)] backdrop-blur-md"
>
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-[#7C3AED]/[0.08] text-[13px]">
{chip.emoji}
</span>
<span className="text-[13px] font-bold text-[#2C1A38]">{chip.label}</span>
</motion.li>
))}
</motion.ul>
<motion.div variants={ctaVariant} className="mt-8">
<Link
href="/platform"
className="group inline-flex items-center gap-2.5 rounded-full bg-[#6330D6] px-8 py-4 text-sm font-black uppercase tracking-wider text-white shadow-[0_14px_38px_-12px_rgba(99,48,214,0.75)] transition-all duration-200 hover:-translate-y-0.5 hover:bg-[#5225b8] hover:shadow-[0_18px_44px_-12px_rgba(99,48,214,0.85)] active:translate-y-0 active:scale-[0.98]"
>
<MaterialIcon icon="mic" size={18} />
Try Voice Commerce
<MaterialIcon
icon="arrow_forward"
size={17}
className="transition-transform duration-200 group-hover:translate-x-1"
/>
</Link>
</motion.div>
</motion.div>
{/* ─── RIGHT: the live phone ─── */}
<motion.div
variants={phoneVariant}
initial="hidden"
whileInView="visible"
viewport={viewport}
className="relative order-2 flex justify-center lg:justify-end"
>
{/* Width-driven at every breakpoint — the 476/941 aspect ratio
derives the height, so the phone never collapses. */}
<div className="relative w-[min(72vw,300px)] lg:w-[min(38vh,400px)]">
{/* soft purple glow anchoring the device */}
<div
aria-hidden="true"
className="pointer-events-none absolute left-1/2 top-1/2 -z-10 h-[86%] w-[150%] -translate-x-1/2 -translate-y-1/2 rounded-full bg-[#A855F7]/16 blur-[80px]"
/>
<PhoneStage className="w-full" />
<FloatingRequestCards />
</div>
</motion.div>
</div>
</div>
</section>
);
}