183 lines
7.6 KiB
TypeScript
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>
|
|
);
|
|
}
|