{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "site-bento",
  "type": "registry:ui",
  "description": "Bento grid component for showcasing features and content",
  "files": [
    {
      "path": "packages/site/bento.tsx",
      "content": "\"use client\";\n\nimport React, { useState, useEffect, useRef } from \"react\";\nimport {\n  motion,\n  useMotionValue,\n  useTransform,\n  type Variants,\n} from \"framer-motion\";\nimport {\n  ArrowUpRight,\n  CheckCircle2,\n  Clock,\n  Sparkles,\n  Zap,\n  Plus,\n} from \"lucide-react\";\nimport { cn } from \"@repo/shadcn-ui/lib/utils\";\n\nexport interface BentoItem {\n  id: string;\n  title: string;\n  description: string;\n  icons?: boolean;\n  href?: string;\n  feature?:\n    | \"chart\"\n    | \"counter\"\n    | \"code\"\n    | \"timeline\"\n    | \"spotlight\"\n    | \"icons\"\n    | \"typing\"\n    | \"metrics\";\n  spotlightItems?: string[];\n  timeline?: Array<{ year: string; event: string }>;\n  code?: string;\n  codeLang?: string;\n  typingText?: string;\n  metrics?: Array<{\n    label: string;\n    value: number;\n    suffix?: string;\n    color?: string;\n  }>;\n  statistic?: {\n    value: string;\n    label: string;\n    start?: number;\n    end?: number;\n    suffix?: string;\n  };\n  size?: \"sm\" | \"md\" | \"lg\";\n  className?: string;\n}\n\nexport interface BentoGridProps {\n  items: BentoItem[];\n  className?: string;\n  title?: string;\n  subtitle?: string;\n}\n\nconst fadeInUp: Variants = {\n  hidden: { opacity: 0, y: 20 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: {\n      duration: 0.5,\n      ease: \"easeOut\",\n    },\n  },\n};\n\nconst staggerContainer: Variants = {\n  hidden: { opacity: 0 },\n  visible: {\n    opacity: 1,\n    transition: {\n      staggerChildren: 0.15,\n      delayChildren: 0.3,\n    },\n  },\n};\n\nconst SpotlightFeature = ({ items }: { items: string[] }) => {\n  return (\n    <ul className=\"mt-2 space-y-1.5\">\n      {items.map((item, index) => (\n        <motion.li\n          key={`spotlight-${item.toLowerCase().replace(/\\s+/g, \"-\")}`}\n          initial={{ opacity: 0, x: -10 }}\n          animate={{ opacity: 1, x: 0 }}\n          transition={{ delay: 0.1 * index }}\n          className=\"flex items-center gap-2\"\n        >\n          <CheckCircle2 className=\"h-4 w-4 text-emerald-500 dark:text-emerald-400 flex-shrink-0\" />\n          <span className=\"text-sm text-neutral-700 dark:text-neutral-300\">\n            {item}\n          </span>\n        </motion.li>\n      ))}\n    </ul>\n  );\n};\n\nconst CounterAnimation = ({\n  start,\n  end,\n  suffix = \"\",\n}: {\n  start: number;\n  end: number;\n  suffix?: string;\n}) => {\n  const [count, setCount] = useState(start);\n\n  useEffect(() => {\n    const duration = 2000;\n    const frameRate = 1000 / 60;\n    const totalFrames = Math.round(duration / frameRate);\n\n    let currentFrame = 0;\n    const counter = setInterval(() => {\n      currentFrame++;\n      const progress = currentFrame / totalFrames;\n      const easedProgress = 1 - (1 - progress) ** 3;\n      const current = start + (end - start) * easedProgress;\n\n      setCount(Math.min(current, end));\n\n      if (currentFrame === totalFrames) {\n        clearInterval(counter);\n      }\n    }, frameRate);\n\n    return () => clearInterval(counter);\n  }, [start, end]);\n\n  return (\n    <div className=\"flex items-baseline gap-1\">\n      <span className=\"text-3xl font-bold text-neutral-900 dark:text-neutral-100\">\n        {count.toFixed(1).replace(/\\.0$/, \"\")}\n      </span>\n      <span className=\"text-xl font-medium text-neutral-900 dark:text-neutral-100\">\n        {suffix}\n      </span>\n    </div>\n  );\n};\n\nconst ChartAnimation = ({ value }: { value: number }) => {\n  return (\n    <div className=\"mt-2 w-full h-2 bg-neutral-200 dark:bg-neutral-700 rounded-full overflow-hidden\">\n      <motion.div\n        className=\"h-full bg-emerald-500 dark:bg-emerald-400 rounded-full\"\n        initial={{ width: 0 }}\n        animate={{ width: `${value}%` }}\n        transition={{ duration: 1.5, ease: \"easeOut\" }}\n      />\n    </div>\n  );\n};\n\nconst IconsFeature = ({ icons }: { icons?: React.ReactNode[] }) => {\n  const defaultIcons = [\n    <div key=\"icon-1\" className=\"w-8 h-8 bg-blue-500 rounded-lg\" />,\n    <div key=\"icon-2\" className=\"w-8 h-8 bg-green-500 rounded-lg\" />,\n    <div key=\"icon-3\" className=\"w-8 h-8 bg-purple-500 rounded-lg\" />,\n    <div key=\"icon-4\" className=\"w-8 h-8 bg-orange-500 rounded-lg\" />,\n    <div key=\"icon-5\" className=\"w-8 h-8 bg-red-500 rounded-lg\" />,\n    <Plus key=\"icon-more\" className=\"w-6 h-6 text-neutral-600 dark:text-neutral-400\" />,\n  ];\n\n  const displayIcons = icons || defaultIcons;\n\n  return (\n    <div className=\"grid grid-cols-3 gap-4 mt-4\">\n      {displayIcons.map((icon, index) => (\n        <motion.div\n          key={`icon-${index}`}\n          className=\"flex flex-col items-center gap-2 p-3 rounded-xl bg-gradient-to-b from-neutral-100/80 to-neutral-100 dark:from-neutral-800/80 dark:to-neutral-800 border border-neutral-200/50 dark:border-neutral-700/50 group transition-all duration-300 hover:border-neutral-300 dark:hover:border-neutral-600\"\n          initial={{ opacity: 0, y: 10 }}\n          animate={{ opacity: 1, y: 0 }}\n          transition={{ delay: 0.1 * index }}\n        >\n          <div className=\"relative w-8 h-8 flex items-center justify-center\">\n            {icon}\n          </div>\n        </motion.div>\n      ))}\n    </div>\n  );\n};\n\nconst TimelineFeature = ({\n  timeline,\n}: {\n  timeline: Array<{ year: string; event: string }>;\n}) => {\n  return (\n    <div className=\"mt-3 relative\">\n      <div className=\"absolute top-0 bottom-0 left-[9px] w-[2px] bg-neutral-200 dark:bg-neutral-700\" />\n      {timeline.map((item, index) => (\n        <motion.div\n          key={`timeline-${item.year}-${item.event\n            .toLowerCase()\n            .replace(/\\s+/g, \"-\")}`}\n          className=\"flex gap-3 mb-3 relative\"\n          initial={{ opacity: 0, x: -10 }}\n          animate={{ opacity: 1, x: 0 }}\n          transition={{\n            delay: 0.15 * index,\n          }}\n        >\n          <div className=\"w-5 h-5 rounded-full bg-neutral-100 dark:bg-neutral-800 border-2 border-neutral-300 dark:border-neutral-600 flex-shrink-0 z-10 mt-0.5\" />\n          <div>\n            <div className=\"text-sm font-medium text-neutral-900 dark:text-neutral-100\">\n              {item.year}\n            </div>\n            <div className=\"text-xs text-neutral-600 dark:text-neutral-400\">\n              {item.event}\n            </div>\n          </div>\n        </motion.div>\n      ))}\n    </div>\n  );\n};\n\nconst TypingCodeFeature = ({ text, filename = \"code.ts\" }: { text: string; filename?: string }) => {\n  const [displayedText, setDisplayedText] = useState(\"\");\n  const [currentIndex, setCurrentIndex] = useState(0);\n  const terminalRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    if (currentIndex < text.length) {\n      const timeout = setTimeout(() => {\n        setDisplayedText((prev) => prev + text[currentIndex]);\n        setCurrentIndex((prev) => prev + 1);\n\n        if (terminalRef.current) {\n          terminalRef.current.scrollTop =\n            terminalRef.current.scrollHeight;\n        }\n      }, Math.random() * 30 + 10);\n\n      return () => clearTimeout(timeout);\n    }\n  }, [currentIndex, text]);\n\n  useEffect(() => {\n    setDisplayedText(\"\");\n    setCurrentIndex(0);\n  }, [text]);\n\n  return (\n    <div className=\"mt-3 relative\">\n      <div className=\"flex items-center gap-2 mb-2\">\n        <div className=\"text-xs text-neutral-500 dark:text-neutral-400\">\n          {filename}\n        </div>\n      </div>\n      <div\n        ref={terminalRef}\n        className=\"bg-neutral-900 dark:bg-black text-neutral-100 p-3 rounded-md text-xs font-mono h-[150px] overflow-y-auto\"\n      >\n        <pre className=\"whitespace-pre-wrap\">\n          {displayedText}\n          <span className=\"animate-pulse\">|</span>\n        </pre>\n      </div>\n    </div>\n  );\n};\n\nconst MetricsFeature = ({\n  metrics,\n}: {\n  metrics: Array<{\n    label: string;\n    value: number;\n    suffix?: string;\n    color?: string;\n  }>;\n}) => {\n  const getColorClass = (color = \"emerald\") => {\n    const colors = {\n      emerald: \"bg-emerald-500 dark:bg-emerald-400\",\n      blue: \"bg-blue-500 dark:bg-blue-400\",\n      violet: \"bg-violet-500 dark:bg-violet-400\",\n      amber: \"bg-amber-500 dark:bg-amber-400\",\n      rose: \"bg-rose-500 dark:bg-rose-400\",\n    };\n    return colors[color as keyof typeof colors] || colors.emerald;\n  };\n\n  return (\n    <div className=\"mt-3 space-y-3\">\n      {metrics.map((metric, index) => (\n        <motion.div\n          key={`metric-${metric.label\n            .toLowerCase()\n            .replace(/\\s+/g, \"-\")}`}\n          className=\"space-y-1\"\n          initial={{ opacity: 0, y: 10 }}\n          animate={{ opacity: 1, y: 0 }}\n          transition={{ delay: 0.15 * index }}\n        >\n          <div className=\"flex justify-between items-center text-sm\">\n            <div className=\"text-neutral-700 dark:text-neutral-300 font-medium flex items-center gap-1.5\">\n              {metric.label === \"Uptime\" && (\n                <Clock className=\"w-3.5 h-3.5\" />\n              )}\n              {metric.label === \"Response time\" && (\n                <Zap className=\"w-3.5 h-3.5\" />\n              )}\n              {metric.label === \"Cost reduction\" && (\n                <Sparkles className=\"w-3.5 h-3.5\" />\n              )}\n              {metric.label}\n            </div>\n            <div className=\"text-neutral-700 dark:text-neutral-300 font-semibold\">\n              {metric.value}\n              {metric.suffix}\n            </div>\n          </div>\n          <div className=\"h-1.5 w-full bg-neutral-200 dark:bg-neutral-700 rounded-full overflow-hidden\">\n            <motion.div\n              className={`h-full rounded-full ${getColorClass(\n                metric.color\n              )}`}\n              initial={{ width: 0 }}\n              animate={{\n                width: `${Math.min(100, metric.value)}%`,\n              }}\n              transition={{\n                duration: 1.2,\n                ease: \"easeOut\",\n                delay: 0.15 * index,\n              }}\n            />\n          </div>\n        </motion.div>\n      ))}\n    </div>\n  );\n};\n\nconst BentoCard = ({ item }: { item: BentoItem }) => {\n  const [isHovered, setIsHovered] = useState(false);\n  const x = useMotionValue(0);\n  const y = useMotionValue(0);\n  const rotateX = useTransform(y, [-100, 100], [2, -2]);\n  const rotateY = useTransform(x, [-100, 100], [-2, 2]);\n\n  function handleMouseMove(event: React.MouseEvent<HTMLDivElement>) {\n    const rect = event.currentTarget.getBoundingClientRect();\n    const width = rect.width;\n    const height = rect.height;\n    const mouseX = event.clientX - rect.left;\n    const mouseY = event.clientY - rect.top;\n    const xPct = mouseX / width - 0.5;\n    const yPct = mouseY / height - 0.5;\n    x.set(xPct * 100);\n    y.set(yPct * 100);\n  }\n\n  function handleMouseLeave() {\n    x.set(0);\n    y.set(0);\n    setIsHovered(false);\n  }\n\n  const CardContent = (\n    <div\n      className={`\n        group relative flex flex-col gap-4 h-full rounded-xl p-5\n        bg-gradient-to-b from-neutral-50/60 via-neutral-50/40 to-neutral-50/30 \n        dark:from-neutral-900/60 dark:via-neutral-900/40 dark:to-neutral-900/30\n        border border-neutral-200/60 dark:border-neutral-800/60\n        before:absolute before:inset-0 before:rounded-xl\n        before:bg-gradient-to-b before:from-white/10 before:via-white/20 before:to-transparent \n        dark:before:from-black/10 dark:before:via-black/20 dark:before:to-transparent\n        before:opacity-100 before:transition-opacity before:duration-500\n        after:absolute after:inset-0 after:rounded-xl after:bg-neutral-50/70 dark:after:bg-neutral-900/70 after:z-[-1]\n        backdrop-blur-[4px]\n        shadow-[0_4px_20px_rgb(0,0,0,0.04)] dark:shadow-[0_4px_20px_rgb(0,0,0,0.2)]\n        hover:border-neutral-300/50 dark:hover:border-neutral-700/50\n        hover:shadow-[0_8px_30px_rgb(0,0,0,0.06)] dark:hover:shadow-[0_8px_30px_rgb(0,0,0,0.3)]\n        hover:backdrop-blur-[6px]\n        hover:bg-gradient-to-b hover:from-neutral-50/60 hover:via-neutral-50/30 hover:to-neutral-50/20\n        dark:hover:from-neutral-800/60 dark:hover:via-neutral-800/30 dark:hover:to-neutral-800/20\n        transition-all duration-500 ease-out ${item.className || ''}\n      `}\n    >\n      <div\n        className=\"relative z-10 flex flex-col gap-3 h-full\"\n        style={{ transform: \"translateZ(20px)\" }}\n      >\n        <div className=\"space-y-2 flex-1 flex flex-col\">\n          <div className=\"flex items-center justify-between\">\n            <h3 className=\"text-xl font-semibold tracking-tight text-neutral-900 dark:text-neutral-100 group-hover:text-neutral-700 dark:group-hover:text-neutral-300 transition-colors duration-300\">\n              {item.title}\n            </h3>\n            {item.href && (\n              <div className=\"text-neutral-400 dark:text-neutral-500 opacity-0 transition-opacity duration-200 group-hover:opacity-100\">\n                <ArrowUpRight className=\"h-5 w-5\" />\n              </div>\n            )}\n          </div>\n\n          <p className=\"text-sm text-neutral-600 dark:text-neutral-400 tracking-tight\">\n            {item.description}\n          </p>\n\n          {/* Feature specific content */}\n          {item.feature === \"spotlight\" && item.spotlightItems && (\n            <SpotlightFeature items={item.spotlightItems} />\n          )}\n\n          {item.feature === \"counter\" && item.statistic && (\n            <div className=\"mt-auto pt-3\">\n              <CounterAnimation\n                start={item.statistic.start || 0}\n                end={item.statistic.end || 100}\n                suffix={item.statistic.suffix}\n              />\n            </div>\n          )}\n\n          {item.feature === \"chart\" && item.statistic && (\n            <div className=\"mt-auto pt-3\">\n              <div className=\"flex items-center justify-between mb-1\">\n                <span className=\"text-sm font-medium text-neutral-700 dark:text-neutral-300\">\n                  {item.statistic.label}\n                </span>\n                <span className=\"text-sm font-medium text-neutral-700 dark:text-neutral-300\">\n                  {item.statistic.end}\n                  {item.statistic.suffix}\n                </span>\n              </div>\n              <ChartAnimation value={item.statistic.end || 0} />\n            </div>\n          )}\n\n          {item.feature === \"timeline\" && item.timeline && (\n            <TimelineFeature timeline={item.timeline} />\n          )}\n\n          {item.feature === \"icons\" && <IconsFeature />}\n\n          {item.feature === \"typing\" && item.typingText && (\n            <TypingCodeFeature text={item.typingText} />\n          )}\n\n          {item.feature === \"metrics\" && item.metrics && (\n            <MetricsFeature metrics={item.metrics} />\n          )}\n        </div>\n      </div>\n    </div>\n  );\n\n  return (\n    <motion.div\n      variants={fadeInUp}\n      whileHover={{ y: -5 }}\n      transition={{ type: \"spring\", stiffness: 300, damping: 20 }}\n      className=\"h-full\"\n      onHoverStart={() => setIsHovered(true)}\n      onHoverEnd={handleMouseLeave}\n      onMouseMove={handleMouseMove}\n      style={{\n        rotateX,\n        rotateY,\n        transformStyle: \"preserve-3d\",\n      }}\n    >\n      {item.href ? (\n        <a\n          href={item.href}\n          className=\"block h-full\"\n          tabIndex={0}\n          aria-label={`${item.title} - ${item.description}`}\n        >\n          {CardContent}\n        </a>\n      ) : (\n        CardContent\n      )}\n    </motion.div>\n  );\n};\n\nexport function BentoGrid({ items, className, title, subtitle }: BentoGridProps) {\n  return (\n    <section className={cn(\"relative py-24 sm:py-32 bg-white dark:bg-black overflow-hidden\", className)}>\n      <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n        {(title || subtitle) && (\n          <div className=\"text-center mb-16\">\n            {title && (\n              <h2 className=\"text-3xl font-bold tracking-tight text-neutral-900 dark:text-neutral-100 sm:text-4xl\">\n                {title}\n              </h2>\n            )}\n            {subtitle && (\n              <p className=\"mt-4 text-lg text-neutral-600 dark:text-neutral-400\">\n                {subtitle}\n              </p>\n            )}\n          </div>\n        )}\n        \n        <motion.div\n          initial=\"hidden\"\n          whileInView=\"visible\"\n          viewport={{ once: true }}\n          variants={staggerContainer}\n          className=\"grid gap-6 auto-rows-fr\"\n        >\n          {items.map((item) => (\n            <BentoCard key={item.id} item={item} />\n          ))}\n        </motion.div>\n      </div>\n    </section>\n  );\n}\n\nexport { BentoCard };",
      "type": "registry:component"
    }
  ]
}