{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "feature-flow",
  "dependencies": [
    "motion"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "../../packages/blocks/src/feature-section/feature-flow.tsx",
      "content": "import React from 'react'\r\nimport {\r\n  Target,\r\n  Mail,\r\n  GitPullRequest,\r\n  Zap,\r\n  ChevronLeft,\r\n  ChevronRight,\r\n  BarChart3,\r\n  Shield,\r\n  Users,\r\n  Clock,\r\n  Waypoints,\r\n  Lightbulb,\r\n} from 'lucide-react'\r\nimport {\r\n  Carousel,\r\n  SliderContainer,\r\n  Slider,\r\n  SliderPrevButton,\r\n  SliderNextButton,\r\n  SliderSnapDisplay,\r\n  SliderProgress,\r\n} from '@/components/ui/carousel'\r\n\r\nconst items = [\r\n  {\r\n    icon: Target,\r\n    title: 'Prioritize high-values',\r\n    desc: \"Skyrocket's AI scores and ranks leads based on conversion potential, so you focus on prospects ready to buy, saving hours.\",\r\n  },\r\n  {\r\n    icon: Mail,\r\n    title: 'Personalized emails',\r\n    desc: \"SkyLead's AI generates tailored cold emails in seconds, boosting response rates and cutting outreach time.\",\r\n  },\r\n  {\r\n    icon: Waypoints,\r\n    title: 'CRM Integration',\r\n    desc: 'SkyLead integrates effortlessly with your CRM, streamlining workflows and keeping your pipeline organized.',\r\n  },\r\n  {\r\n    icon: Lightbulb,\r\n    title: 'Instant engagement',\r\n    desc: 'SkyLead automation ensures timely engagement with every deal, helping you close sales deals faster.',\r\n  },\r\n  {\r\n    icon: BarChart3,\r\n    title: 'Advanced Analytics',\r\n    desc: 'Track performance metrics, conversion rates, and ROI with comprehensive dashboards and real-time reporting.',\r\n  },\r\n  {\r\n    icon: Shield,\r\n    title: 'Data Security',\r\n    desc: 'Enterprise-grade security with encryption, compliance, and data protection to keep your information safe.',\r\n  },\r\n  {\r\n    icon: Users,\r\n    title: 'Team Collaboration',\r\n    desc: 'Work seamlessly with your team through shared pipelines, comments, and collaborative lead management.',\r\n  },\r\n  {\r\n    icon: Clock,\r\n    title: '24/7 Automation',\r\n    desc: 'Round-the-clock automated follow-ups and lead nurturing to ensure no opportunity is missed.',\r\n  },\r\n]\r\n\r\nexport const FeatureFlow: React.FC = () => {\r\n  return (\r\n    <section className=\"py-32 px-6 bg-white overflow-hidden font-manrope relative\">\r\n      <div className=\"max-w-7xl mx-auto\">\r\n        <Carousel options={{ align: 'start', loop: false }}>\r\n          <div className=\"flex flex-col md:flex-row justify-between items-end mb-16 gap-8\">\r\n            <div className=\"max-w-xl\">\r\n              <span className=\"px-3 py-1 bg-slate-50 text-slate-500 text-[10px] font-bold uppercase tracking-widest rounded mb-6 inline-block\">\r\n                Faster Sales\r\n              </span>\r\n              <h2 className=\"text-5xl font-bold text-slate-900 mb-6 tracking-tight text-balance\">\r\n                Prospect to customer, faster than ever!\r\n              </h2>\r\n              <p className=\"text-slate-500 text-lg text-pretty\">\r\n                Stop losing time on slow, outdated lead processes. Our\r\n                AI-powered system finds, nurtures, and converts your best\r\n                prospects efficiently.\r\n              </p>\r\n            </div>\r\n\r\n            <div className=\"flex flex-col items-end gap-4\">\r\n              {/* <SliderSnapDisplay className=\"text-sm\" /> */}\r\n              <div className=\"flex gap-4\">\r\n                <SliderPrevButton\r\n                  className=\"size-12 rounded-full border disabled:opacity-40 cursor-pointer backdrop-blur-lg border-slate-200 flex items-center justify-center hover:bg-slate-50 transition-colors\"\r\n                  aria-label=\"Previous\"\r\n                >\r\n                  <ChevronLeft className=\"size-5 text-orange-600\" />\r\n                </SliderPrevButton>\r\n                <SliderNextButton\r\n                  className=\"size-12 rounded-full disabled:opacity-40 cursor-pointer border backdrop-blur-lg border-slate-200 flex items-center justify-center hover:bg-slate-50 transition-colors\"\r\n                  aria-label=\"Next\"\r\n                >\r\n                  <ChevronRight className=\"size-5 text-orange-600\" />\r\n                </SliderNextButton>\r\n              </div>\r\n            </div>\r\n          </div>\r\n\r\n          <SliderContainer className=\"gap-6\">\r\n            {items.map((item, i) => (\r\n              <Slider key={i} className=\"basis-full md:basis-1/3 lg:basis-1/4\">\r\n                <FlowCard {...item} />\r\n              </Slider>\r\n            ))}\r\n          </SliderContainer>\r\n        </Carousel>\r\n      </div>\r\n    </section>\r\n  )\r\n}\r\n\r\nconst FlowCard = ({ icon: Icon, title, desc }: any) => (\r\n  <div className=\"bg-orange-50/20 border border-orange-100/50 p-8 rounded-2xl flex flex-col gap-10 hover:shadow-lg transition-shadow duration-300 h-full\">\r\n    <div className=\"size-10 flex items-center justify-center text-orange-600\">\r\n      <Icon className=\"size-8\" />\r\n    </div>\r\n    <div className=\"space-y-4\">\r\n      <h3 className=\"text-xl font-bold text-slate-900 tracking-tight\">\r\n        {title}\r\n      </h3>\r\n      <p className=\"text-slate-500 text-sm leading-relaxed text-pretty\">\r\n        {desc}\r\n      </p>\r\n    </div>\r\n  </div>\r\n)\r\n",
      "type": "registry:block"
    },
    {
      "path": "./components/ui/carousel.tsx",
      "content": "'use client';\n\nimport { cn } from '@/lib/utils';\nimport type { EmblaCarouselType, EmblaEventType, EmblaOptionsType } from 'embla-carousel';\nimport useEmblaCarousel from 'embla-carousel-react';\nimport { AnimatePresence, motion } from 'motion/react';\nimport type React from 'react';\nimport {\n  createContext,\n  forwardRef,\n  useCallback,\n  useContext,\n  useEffect,\n  useId,\n  useRef,\n  useState,\n} from 'react';\n\n// ============= TYPES =============\ninterface CarouselProps extends React.HTMLAttributes<HTMLDivElement> {\n  options?: EmblaOptionsType;\n  plugins?: Parameters<typeof useEmblaCarousel>[1];\n  isScale?: boolean;\n}\n\ninterface CarouselContextType {\n  emblaApi: EmblaCarouselType | undefined;\n  emblaThumbsApi: EmblaCarouselType | undefined;\n  emblaRef: ReturnType<typeof useEmblaCarousel>[0];\n  emblaThumbsRef: ReturnType<typeof useEmblaCarousel>[0];\n  prevBtnDisabled: boolean;\n  nextBtnDisabled: boolean;\n  onPrevButtonClick: () => void;\n  onNextButtonClick: () => void;\n  selectedIndex: number;\n  scrollSnaps: number[];\n  onDotButtonClick: (index: number) => void;\n  scrollProgress: number;\n  selectedSnap: number;\n  snapCount: number;\n  isScale: boolean;\n  slidesArr: string[];\n  setSlidesArr: React.Dispatch<React.SetStateAction<string[]>>;\n  onThumbClick: (index: number) => void;\n  carouselId: string;\n  orientation: 'vertical' | 'horizontal';\n  direction: 'ltr' | 'rtl' | undefined;\n  handleKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void;\n}\n\n// ============= CONTEXT =============\nconst CarouselContext = createContext<CarouselContextType | undefined>(undefined);\n\nexport const useCarousel = () => {\n  const context = useContext(CarouselContext);\n  if (!context) {\n    throw new Error('useCarousel must be used within a Carousel component');\n  }\n  return context;\n};\n\n// ============= UTILITIES =============\nconst TWEEN_FACTOR_BASE = 0.52;\nconst numberWithinRange = (number: number, min: number, max: number): number =>\n  Math.min(Math.max(number, min), max);\n\n// ============= MAIN CAROUSEL COMPONENT =============\nexport const Carousel = forwardRef<HTMLDivElement, CarouselProps>(\n  ({ children, options = {}, plugins = [], className, isScale = false, dir, ...props }, ref) => {\n    const carouselId = useId();\n    const [slidesArr, setSlidesArr] = useState<string[]>([]);\n\n    const orientation = options.axis === 'y' ? 'vertical' : 'horizontal';\n    const direction = options.direction ?? (dir as 'ltr' | 'rtl' | undefined);\n\n    // Main carousel\n    const [emblaRef, emblaApi] = useEmblaCarousel(\n      {\n        ...options,\n        axis: orientation === 'vertical' ? 'y' : 'x',\n        direction,\n      },\n      plugins\n    );\n\n    // Thumbnails carousel\n    const [emblaThumbsRef, emblaThumbsApi] = useEmblaCarousel({\n      containScroll: 'keepSnaps',\n      dragFree: true,\n      axis: orientation === 'vertical' ? 'y' : 'x',\n      direction,\n    });\n\n    // State\n    const [prevBtnDisabled, setPrevBtnDisabled] = useState(true);\n    const [nextBtnDisabled, setNextBtnDisabled] = useState(true);\n    const [selectedIndex, setSelectedIndex] = useState(0);\n    const [scrollSnaps, setScrollSnaps] = useState<number[]>([]);\n    const [scrollProgress, setScrollProgress] = useState(0);\n    const [snapCount, setSnapCount] = useState(0);\n\n    // Navigation callbacks\n    const onPrevButtonClick = useCallback(() => {\n      emblaApi?.scrollPrev();\n    }, [emblaApi]);\n\n    const onNextButtonClick = useCallback(() => {\n      emblaApi?.scrollNext();\n    }, [emblaApi]);\n\n    const onDotButtonClick = useCallback(\n      (index: number) => {\n        emblaApi?.scrollTo(index);\n      },\n      [emblaApi]\n    );\n\n    const onThumbClick = useCallback(\n      (index: number) => {\n        if (!emblaApi || !emblaThumbsApi) return;\n        emblaApi.scrollTo(index);\n      },\n      [emblaApi, emblaThumbsApi]\n    );\n\n    // Keyboard navigation\n    const handleKeyDown = useCallback(\n      (event: React.KeyboardEvent<HTMLDivElement>) => {\n        if (!emblaApi) return;\n        switch (event.key) {\n          case 'ArrowLeft':\n            event.preventDefault();\n            if (orientation === 'horizontal') {\n              direction === 'rtl' ? onNextButtonClick() : onPrevButtonClick();\n            }\n            break;\n          case 'ArrowRight':\n            event.preventDefault();\n            if (orientation === 'horizontal') {\n              direction === 'rtl' ? onPrevButtonClick() : onNextButtonClick();\n            }\n            break;\n          case 'ArrowUp':\n            event.preventDefault();\n            if (orientation === 'vertical') onPrevButtonClick();\n            break;\n          case 'ArrowDown':\n            event.preventDefault();\n            if (orientation === 'vertical') onNextButtonClick();\n            break;\n        }\n      },\n      [emblaApi, orientation, direction, onPrevButtonClick, onNextButtonClick]\n    );\n\n    // Selection handler\n    const onSelect = useCallback(() => {\n      if (!emblaApi) return;\n      setSelectedIndex(emblaApi.selectedScrollSnap());\n      setPrevBtnDisabled(!emblaApi.canScrollPrev());\n      setNextBtnDisabled(!emblaApi.canScrollNext());\n      emblaThumbsApi?.scrollTo(emblaApi.selectedScrollSnap());\n    }, [emblaApi, emblaThumbsApi]);\n\n    // Scroll progress handler\n    const onScroll = useCallback((emblaApi: EmblaCarouselType) => {\n      const progress = Math.max(0, Math.min(1, emblaApi.scrollProgress()));\n      setScrollProgress(progress * 100);\n    }, []);\n\n    // Scale animation for isScale mode\n    const tweenFactor = useRef(0);\n    const tweenNodes = useRef<HTMLElement[]>([]);\n\n    const setTweenNodes = useCallback(\n      (emblaApi: EmblaCarouselType): void => {\n        if (!isScale) return;\n        tweenNodes.current = emblaApi\n          .slideNodes()\n          .map((slideNode) => slideNode.querySelector('.slider_content')) as HTMLElement[];\n      },\n      [isScale]\n    );\n\n    const setTweenFactor = useCallback(\n      (emblaApi: EmblaCarouselType) => {\n        if (!isScale) return;\n        tweenFactor.current = TWEEN_FACTOR_BASE * emblaApi.scrollSnapList().length;\n      },\n      [isScale]\n    );\n\n    const tweenScale = useCallback(\n      (emblaApi: EmblaCarouselType, eventName?: EmblaEventType) => {\n        if (!isScale) return;\n        const engine = emblaApi.internalEngine();\n        const scrollProgress = emblaApi.scrollProgress();\n        const slidesInView = emblaApi.slidesInView();\n        const isScrollEvent = eventName === 'scroll';\n\n        emblaApi.scrollSnapList().forEach((scrollSnap, snapIndex) => {\n          let diffToTarget = scrollSnap - scrollProgress;\n          const slidesInSnap = engine.slideRegistry[snapIndex];\n\n          slidesInSnap.forEach((slideIndex) => {\n            if (isScrollEvent && !slidesInView.includes(slideIndex)) return;\n\n            if (engine.options.loop) {\n              engine.slideLooper.loopPoints.forEach((loopItem) => {\n                const target = loopItem.target();\n                if (slideIndex === loopItem.index && target !== 0) {\n                  const sign = Math.sign(target);\n                  if (sign === -1) {\n                    diffToTarget = scrollSnap - (1 + scrollProgress);\n                  }\n                  if (sign === 1) {\n                    diffToTarget = scrollSnap + (1 - scrollProgress);\n                  }\n                }\n              });\n            }\n\n            const tweenValue = 1 - Math.abs(diffToTarget * tweenFactor.current);\n            const scale = numberWithinRange(tweenValue, 0, 1).toString();\n            const tweenNode = tweenNodes.current[slideIndex];\n            if (tweenNode) {\n              tweenNode.style.transform = `scale(${scale})`;\n            }\n          });\n        });\n      },\n      [isScale]\n    );\n\n    // Effects\n    useEffect(() => {\n      if (!emblaApi) return;\n      setScrollSnaps(emblaApi.scrollSnapList());\n      setSnapCount(emblaApi.scrollSnapList().length);\n      onSelect();\n      onScroll(emblaApi);\n\n      emblaApi\n        .on('reInit', onSelect)\n        .on('select', onSelect)\n        .on('reInit', onScroll)\n        .on('scroll', onScroll);\n\n      if (isScale) {\n        setTweenNodes(emblaApi);\n        setTweenFactor(emblaApi);\n        tweenScale(emblaApi);\n        emblaApi\n          .on('reInit', setTweenNodes)\n          .on('reInit', setTweenFactor)\n          .on('reInit', tweenScale)\n          .on('scroll', tweenScale);\n      }\n    }, [emblaApi, onSelect, onScroll, isScale, setTweenNodes, setTweenFactor, tweenScale]);\n\n    return (\n      <CarouselContext.Provider\n        value={{\n          emblaApi,\n          emblaThumbsApi,\n          emblaRef,\n          emblaThumbsRef,\n          prevBtnDisabled,\n          nextBtnDisabled,\n          onPrevButtonClick,\n          onNextButtonClick,\n          selectedIndex,\n          scrollSnaps,\n          onDotButtonClick,\n          scrollProgress,\n          selectedSnap: selectedIndex,\n          snapCount,\n          isScale,\n          slidesArr,\n          setSlidesArr,\n          onThumbClick,\n          carouselId,\n          orientation,\n          direction,\n          handleKeyDown,\n        }}\n      >\n        <div\n          ref={ref}\n          tabIndex={0}\n          onKeyDownCapture={handleKeyDown}\n          className={cn('relative w-full focus:outline-hidden', className)}\n          dir={direction}\n          {...props}\n        >\n          {children}\n        </div>\n      </CarouselContext.Provider>\n    );\n  }\n);\n\nCarousel.displayName = 'Carousel';\n\n// ============= SLIDER CONTAINER =============\nexport const SliderContainer = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, children, ...props }, ref) => {\n    const { emblaRef, orientation } = useCarousel();\n\n    return (\n      <div ref={emblaRef} className='overflow-hidden' {...props}>\n        <div\n          ref={ref}\n          className={cn('flex', orientation === 'vertical' ? 'flex-col' : 'flex-row', className)}\n          style={{ touchAction: 'pan-y pinch-zoom' }}\n        >\n          {children}\n        </div>\n      </div>\n    );\n  }\n);\n\nSliderContainer.displayName = 'SliderContainer';\n\n// ============= SLIDER ITEM =============\ninterface SliderProps extends React.HTMLAttributes<HTMLDivElement> {\n  thumbnailSrc?: string;\n}\n\nexport const Slider = forwardRef<HTMLDivElement, SliderProps>(\n  ({ children, className, thumbnailSrc, ...props }, ref) => {\n    const { isScale, setSlidesArr, orientation } = useCarousel();\n\n    useEffect(() => {\n      if (thumbnailSrc) {\n        setSlidesArr((prev) => {\n          if (!prev.includes(thumbnailSrc)) {\n            return [...prev, thumbnailSrc];\n          }\n          return prev;\n        });\n      }\n    }, [thumbnailSrc, setSlidesArr]);\n\n    return (\n      <div\n        ref={ref}\n        className={cn(\n          'min-w-0 shrink-0 grow-0',\n          // orientation === 'vertical' ? 'pb-1' : 'pr-1',\n          className\n        )}\n        {...props}\n      >\n        {isScale ? <div className='slider_content'>{children}</div> : children}\n      </div>\n    );\n  }\n);\n\nSlider.displayName = 'Slider';\n\n// ============= NAVIGATION BUTTONS =============\nexport const SliderPrevButton = forwardRef<\n  HTMLButtonElement,\n  React.ButtonHTMLAttributes<HTMLButtonElement>\n>(({ children, className, ...props }, ref) => {\n  const { onPrevButtonClick, prevBtnDisabled } = useCarousel();\n\n  return (\n    <button\n      ref={ref}\n      type='button'\n      onClick={onPrevButtonClick}\n      disabled={prevBtnDisabled}\n      className={cn('', className)}\n      {...props}\n    >\n      {children}\n    </button>\n  );\n});\n\nSliderPrevButton.displayName = 'SliderPrevButton';\n\nexport const SliderNextButton = forwardRef<\n  HTMLButtonElement,\n  React.ButtonHTMLAttributes<HTMLButtonElement>\n>(({ children, className, ...props }, ref) => {\n  const { onNextButtonClick, nextBtnDisabled } = useCarousel();\n\n  return (\n    <button\n      ref={ref}\n      type='button'\n      onClick={onNextButtonClick}\n      disabled={nextBtnDisabled}\n      className={cn('', className)}\n      {...props}\n    >\n      {children}\n    </button>\n  );\n});\n\nSliderNextButton.displayName = 'SliderNextButton';\n\n// ============= PROGRESS BAR =============\nexport const SliderProgress = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => {\n    const { scrollProgress } = useCarousel();\n\n    return (\n      <div\n        ref={ref}\n        className={cn(\n          'bg-neutral-500 relative rounded-md h-2 w-96 max-w-full overflow-hidden',\n          className\n        )}\n        {...props}\n      >\n        <div\n          className='dark:bg-white bg-black absolute w-full top-0 -left-full bottom-0 transition-transform'\n          style={{ transform: `translate3d(${scrollProgress}%,0px,0px)` }}\n        />\n      </div>\n    );\n  }\n);\n\nSliderProgress.displayName = 'SliderProgress';\n\n// ============= SNAP DISPLAY =============\nexport const SliderSnapDisplay = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => {\n    const { selectedSnap, snapCount } = useCarousel();\n    const prevSnapRef = useRef(selectedSnap);\n    const direction = selectedSnap > prevSnapRef.current ? 1 : -1;\n\n    useEffect(() => {\n      prevSnapRef.current = selectedSnap;\n    }, [selectedSnap]);\n\n    return (\n      <div\n        ref={ref}\n        className={cn('mix-blend-difference overflow-hidden flex gap-1 items-center', className)}\n        {...props}\n      >\n        <AnimatePresence mode='wait'>\n          <motion.div\n            key={selectedSnap}\n            custom={direction}\n            // @ts-expect-error\n            initial={(d: number) => ({ y: d * 20, opacity: 0 })}\n            animate={{ y: 0, opacity: 1 }}\n            // @ts-expect-error\n            exit={(d: number) => ({ y: d * -20, opacity: 0 })}\n          >\n            {selectedSnap + 1}\n          </motion.div>\n        </AnimatePresence>\n        <span>/ {snapCount}</span>\n      </div>\n    );\n  }\n);\n\nSliderSnapDisplay.displayName = 'SliderSnapDisplay';\n\n// ============= DOT BUTTONS =============\ninterface SliderDotButtonProps extends React.HTMLAttributes<HTMLDivElement> {\n  activeClass?: string;\n}\n\nexport const SliderDotButton = forwardRef<HTMLDivElement, SliderDotButtonProps>(\n  ({ className, activeClass, ...props }, ref) => {\n    const { selectedIndex, scrollSnaps, orientation, onDotButtonClick, carouselId } = useCarousel();\n\n    return (\n      <div ref={ref} className={cn('flex gap-2', className)} {...props}>\n        {scrollSnaps.map((_, index) => (\n          <button\n            key={`${carouselId}-dot-${_}`}\n            type='button'\n            onClick={() => onDotButtonClick(index)}\n            className={cn(\n              'relative inline-flex p-0 m-0',\n              orientation === 'vertical' ? 'h-6 w-1' : 'w-6 h-1'\n            )}\n          >\n            <div\n              className={cn(\n                'bg-neutral-500/40 rounded-full ',\n                orientation === 'vertical' ? 'h-6 w-1' : 'w-6 h-1'\n              )}\n            />\n            {index === selectedIndex && (\n              <AnimatePresence mode='wait'>\n                <motion.div\n                  transition={{\n                    layout: {\n                      duration: 0.4,\n                      ease: 'easeInOut',\n                      delay: 0.04,\n                    },\n                  }}\n                  layoutId={`hover-${carouselId}`}\n                  className={cn(\n                    'absolute z-3 w-full h-full left-0 top-0 dark:bg-white bg-black rounded-full',\n                    orientation === 'vertical' ? 'h-6 w-1' : 'w-6 h-1',\n                    activeClass\n                  )}\n                />\n              </AnimatePresence>\n            )}\n          </button>\n        ))}\n      </div>\n    );\n  }\n);\n\nSliderDotButton.displayName = 'SliderDotButton';\n\n// ============= CAROUSEL INDICATORS =============\ninterface CarouselIndicatorProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  index: number;\n}\n\nexport const CarouselIndicator = forwardRef<HTMLButtonElement, CarouselIndicatorProps>(\n  ({ className, index, ...props }, ref) => {\n    const { selectedIndex, onDotButtonClick } = useCarousel();\n    const isActive = selectedIndex === index;\n\n    return (\n      <button\n        ref={ref}\n        type='button'\n        onClick={() => onDotButtonClick(index)}\n        className={cn(\n          'h-1.5 w-6 rounded-full transition-colors',\n          isActive ? 'bg-primary' : 'bg-primary/50',\n          className\n        )}\n        aria-label={`Go to slide ${index + 1}`}\n        {...props}\n      >\n        <span className='sr-only'>Slide {index + 1}</span>\n      </button>\n    );\n  }\n);\n\nCarouselIndicator.displayName = 'CarouselIndicator';\n\n// Auto-generate thumbnails from slides\nexport const ThumbsSlider = forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & {\n    thumbsClassName?: string;\n    thumbsSliderClassName?: string;\n  }\n>(({ className, thumbsClassName, thumbsSliderClassName, ...props }, ref) => {\n  const { slidesArr, selectedIndex, onThumbClick, orientation, emblaThumbsRef } = useCarousel();\n\n  if (slidesArr.length === 0) return null;\n\n  return (\n    <div ref={emblaThumbsRef} className={cn('overflow-hidden', className)} {...props}>\n      <div\n        ref={ref}\n        className={cn(\n          'flex gap-2 h-[300px]',\n          orientation === 'vertical' ? 'flex-col' : 'flex-row',\n          thumbsClassName\n        )}\n      >\n        {slidesArr.map((src, index) => (\n          <div\n            key={src}\n            onClick={() => onThumbClick(index)}\n            className={cn(\n              'shrink-0 cursor-pointer transition-opacity',\n              'border-2 rounded-md',\n              orientation === 'vertical' ? 'basis-[15%] h-20' : 'basis-[15%] h-24',\n              selectedIndex === index\n                ? 'opacity-100 border-primary'\n                : 'opacity-30 border-transparent',\n              thumbsSliderClassName\n            )}\n          >\n            <img\n              src={src}\n              alt={`Thumbnail ${index + 1}`}\n              className='w-full h-full object-cover rounded-md'\n            />\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n});\n\nThumbsSlider.displayName = 'ThumbsSlider';\n\n// Alias for backward compatibility\n",
      "type": "registry:component"
    },
    {
      "path": "./components/ui/timeline-animation.tsx",
      "content": "import type { Variants } from 'motion/react';\nimport { type HTMLMotionProps, motion, useInView } from 'motion/react';\nimport type React from 'react';\n\ntype TimelineContentProps<T extends keyof HTMLElementTagNameMap> = {\n  children?: React.ReactNode;\n  animationNum: number;\n  className?: string;\n  timelineRef: React.RefObject<HTMLElement | null>;\n  as?: T;\n  customVariants?: Variants;\n  once?: boolean;\n} & HTMLMotionProps<T>;\n\nexport const TimelineAnimation = <T extends keyof HTMLElementTagNameMap = 'div'>({\n  children,\n  animationNum,\n  timelineRef,\n  className,\n  as,\n  customVariants,\n  once = true,\n  ...props\n}: TimelineContentProps<T>) => {\n  const defaultSequenceVariants = {\n    visible: (i: number) => ({\n      filter: 'blur(0px)',\n      y: 0,\n      opacity: 1,\n      transition: {\n        delay: i * 0.5,\n        duration: 0.5,\n      },\n    }),\n    hidden: {\n      filter: 'blur(20px)',\n      y: 0,\n      opacity: 0,\n    },\n  };\n\n  const sequenceVariants = customVariants || defaultSequenceVariants;\n\n  const isInView = useInView(timelineRef, {\n    once,\n  });\n\n  const MotionComponent = motion[as || 'div'] as React.ElementType;\n\n  return (\n    <MotionComponent\n      initial='hidden'\n      animate={isInView ? 'visible' : 'hidden'}\n      custom={animationNum}\n      variants={sequenceVariants}\n      className={className}\n      {...props}\n    >\n      {children}\n    </MotionComponent>\n  );\n};\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}