"use client"; import { useRef, useState } from "react"; import { AnimatePresence, motion, useMotionValueEvent, useScroll } from "framer-motion"; import { Smartphone, Send, Loader, Sparkles, Search, Link2, CalendarCheck, Check, } from "lucide-react"; import type { LucideIcon } from "lucide-react"; import { Container } from "@/components/ui/Container"; import { CACHY } from "@/lib/constants"; import { EASE_OUT } from "@/lib/motion"; import { usePrefersReducedMotion } from "@/lib/useReducedMotion"; type Beat = { icon: LucideIcon; title: string; note: string }; const BEATS: Beat[] = [ { icon: Smartphone, title: "You spot something good", note: "A reel, a video, a page worth keeping." }, { icon: Send, title: "Send it to Cachy", note: `A share, or a DM to ${CACHY.handle}.` }, { icon: Loader, title: "Cachy reads it", note: "Watches, transcribes, structures." }, { icon: Sparkles, title: "A knowledge card appears", note: "Summary, key ideas, what to do next." }, { icon: Search, title: "Search by meaning", note: "Ask for the idea, not the filename." }, { icon: Link2, title: "Related ideas surface", note: "One card leads to the next." }, { icon: CalendarCheck, title: "Act on it", note: "Reminders and to-dos, one tap out." }, { icon: Check, title: "Done", note: "Kept for good. You never opened the app." }, ]; export function InteractiveDemo() { const reduced = usePrefersReducedMotion(); const ref = useRef(null); const [active, setActive] = useState(0); const { scrollYProgress } = useScroll({ target: ref, offset: ["start start", "end end"], }); useMotionValueEvent(scrollYProgress, "change", (v) => { const idx = Math.min(BEATS.length - 1, Math.floor(v * BEATS.length)); setActive(idx); }); return (
{/* Mobile + reduced-motion: static list — no 5-screen pinned-scroll tax. */}
{/* Desktop only: pinned scrollytelling, where the long scroll reads well. */} {!reduced && (

Watch it happen.

Scroll through the whole thing — from a link you'd lose to knowledge you'll keep.

{/* tall scroll track with a pinned stage */}
{/* step list */}
    {BEATS.map((beat, idx) => { const on = idx === active; const done = idx < active; return (
  1. {beat.title}

  2. ); })}
{/* stage */}
{/* mobile progress dots */}
{BEATS.map((b, idx) => ( ))}
)}
); } function DemoStage({ active }: { active: number }) { const beat = BEATS[active]; const Icon = beat.icon; return (
{String(active + 1).padStart(2, "0")} / {String(BEATS.length).padStart(2, "0")}

{beat.title}

{beat.note}

); } /** Static list of all eight beats — used on mobile and under reduced-motion. */ function StaticDemo() { return (

Watch it happen.

From a link you'd lose to knowledge you'll keep.

    {BEATS.map((beat, idx) => (
  1. {String(idx + 1).padStart(2, "0")}

    {beat.title}

    {beat.note}

  2. ))}
); }