{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "features-with-spotlight",
  "type": "registry:block",
  "title": "Features with Spotlight",
  "description": "Interactive feature list with animated spotlight panel and smooth transitions.",
  "author": "Ahdeetai <https://aditya.is-cool.dev>",
  "registryDependencies": [],
  "dependencies": ["clsx", "motion", "tailwind-merge"],
  "files": [
    {
      "type": "registry:component",
      "path": "registry/blocks/features/features-with-spotlight/page.tsx",
      "content": "'use client';\n\nimport { useState, useEffect } from 'react';\nimport { motion, AnimatePresence } from 'motion/react';\nimport { cn } from '@/lib/utils';\n\ninterface SpotlightItem {\n  title: string;\n  description: string;\n}\n\ninterface FeaturesWithSpotlightProps {\n  heading?: string;\n  items?: SpotlightItem[];\n  autoPlayInterval?: number;\n  showDots?: boolean;\n  className?: string;\n}\n\nexport default function FeaturesWithSpotlight({\n  heading = 'Most SaaS Products fail at',\n  items = [\n    {\n      title: 'Slow and hard to scale',\n      description:\n        'Servers buckle under load, latency spikes at the worst times, and engineers are stuck fixing fires instead of shipping.',\n    },\n    {\n      title: 'Siloed across your stack',\n      description:\n        \"Your data lives in five places and talks to none of them. Decisions get made on guesswork because the truth is always somewhere else.\",\n    },\n    {\n      title: 'Painful to onboard onto',\n      description:\n        'New teammates spend weeks just getting oriented. Docs are outdated, flows are buried, and no one agrees on how things work.',\n    },\n    {\n      title: 'Designed to resist change',\n      description:\n        'Every small tweak requires a big effort. The codebase grows heavier, the team moves slower, and momentum quietly dies.',\n    },\n  ],\n  autoPlayInterval = 5000,\n  showDots = true,\n  className,\n}: FeaturesWithSpotlightProps) {\n  const [activeIndex, setActiveIndex] = useState(0);\n  const [descIndex, setDescIndex] = useState(0);\n  const [resetKey, setResetKey] = useState(0);\n\n  useEffect(() => {\n    const timer = setInterval(() => {\n      setActiveIndex((prev) => (prev + 1) % items.length);\n    }, autoPlayInterval);\n    return () => clearInterval(timer);\n  }, [items.length, autoPlayInterval, resetKey]);\n\n  useEffect(() => {\n    const t = setTimeout(() => {\n      setDescIndex(activeIndex);\n    }, 350);\n    return () => clearTimeout(t);\n  }, [activeIndex]);\n\n  const handleSelect = (index: number) => {\n    setActiveIndex(index);\n    setResetKey((k) => k + 1);\n  };\n\n  return (\n    <section className={cn('w-full bg-white dark:bg-zinc-950 py-16 md:py-24', className)}>\n      <div className='max-w-6xl mx-auto px-6'>\n\n        <div className='hidden md:flex flex-row'>\n          <div className='w-1/2 pr-12 flex flex-col gap-10 border-r border-zinc-200 dark:border-zinc-800'>\n            <h2 className='text-2xl md:text-3xl font-bold text-zinc-900 dark:text-zinc-100 leading-tight'>\n              {heading}\n            </h2>\n            <ul className='flex flex-col gap-4'>\n              {items.map((item, index) => (\n                <li key={index}>\n                  <button\n                    onClick={() => handleSelect(index)}\n                    className={cn(\n                      'text-left text-base md:text-lg font-medium transition-all duration-300 cursor-pointer',\n                      activeIndex === index\n                        ? 'text-zinc-900 dark:text-zinc-100'\n                        : 'text-zinc-400 dark:text-zinc-600 hover:text-zinc-600 dark:hover:text-zinc-400'\n                    )}\n                  >\n                    {item.title}\n                  </button>\n                </li>\n              ))}\n            </ul>\n          </div>\n\n          <div className='w-1/2 pl-12 flex items-center min-h-40 overflow-hidden'>\n            <AnimatePresence mode='wait'>\n              <motion.p\n                key={activeIndex}\n                initial={{ opacity: 0, filter: 'blur(6px)', y: 20 }}\n                animate={{ opacity: 1, filter: 'blur(0px)', y: 0 }}\n                exit={{ opacity: 0, filter: 'blur(6px)', y: -20 }}\n                transition={{ duration: 0.4, ease: 'easeInOut' }}\n                className='text-sm md:text-base text-zinc-500 dark:text-zinc-400 leading-relaxed'\n              >\n                {items[activeIndex].description}\n              </motion.p>\n            </AnimatePresence>\n          </div>\n        </div>\n\n        <div className='flex md:hidden flex-col items-center text-center gap-8'>\n          <h2 className='text-xl font-bold text-zinc-900 dark:text-zinc-100'>\n            {heading}\n          </h2>\n\n          <div className='flex flex-col items-center gap-3 px-4 min-h-32 overflow-hidden'>\n            <div className='overflow-hidden'>\n              <AnimatePresence mode='wait'>\n                <motion.h3\n                  key={`title-${activeIndex}`}\n                  initial={{ opacity: 0, filter: 'blur(6px)', y: 20 }}\n                  animate={{ opacity: 1, filter: 'blur(0px)', y: 0 }}\n                  exit={{ opacity: 0, filter: 'blur(6px)', y: -20 }}\n                  transition={{ duration: 0.3, ease: 'easeInOut' }}\n                  className='text-base font-semibold text-zinc-900 dark:text-zinc-100'\n                >\n                  {items[activeIndex].title}\n                </motion.h3>\n              </AnimatePresence>\n            </div>\n\n            <div className='overflow-hidden'>\n              <AnimatePresence mode='wait'>\n                <motion.p\n                  key={`desc-${descIndex}`}\n                  initial={{ opacity: 0, filter: 'blur(6px)', y: 20 }}\n                  animate={{ opacity: 1, filter: 'blur(0px)', y: 0 }}\n                  exit={{ opacity: 0, filter: 'blur(6px)', y: -20 }}\n                  transition={{ duration: 0.3, ease: 'easeInOut' }}\n                  className='text-sm text-zinc-500 dark:text-zinc-400 leading-relaxed'\n                >\n                  {items[descIndex].description}\n                </motion.p>\n              </AnimatePresence>\n            </div>\n          </div>\n\n          {showDots && (\n            <div className='flex gap-2'>\n              {items.map((item, index) => (\n                <button\n                  key={index}\n                  onClick={() => handleSelect(index)}\n                  aria-label={item.title}\n                  className={cn(\n                    'h-1.5 rounded-full transition-all duration-300',\n                    activeIndex === index\n                      ? 'w-6 bg-zinc-900 dark:bg-zinc-100'\n                      : 'w-1.5 bg-zinc-300 dark:bg-zinc-700'\n                  )}\n                />\n              ))}\n            </div>\n          )}\n        </div>\n\n      </div>\n    </section>\n  );\n}"
    },
    {
      "type": "registry:lib",
      "path": "lib/utils.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}"
    }
  ]
}
