{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "grid-card",
  "type": "registry:ui",
  "title": "Grid Card",
  "author": "Rishabh Bose <https://theb0se.vercel.app/>",
  "description": "Interactive feature card and section featuring a top-right flickering canvas grid corner accent with diagonal mask fading, spring hover physics, icon scale/rotation variants, and dynamic hover-accelerated flicker rates.",
  "dependencies": [
    "framer-motion",
    "lucide-react",
    "clsx",
    "tailwind-merge"
  ],
  "files": [
    {
      "path": "components/ui/grid-card.tsx",
      "content": "\"use client\";\n\nimport React, { useState } from \"react\";\nimport Link from \"next/link\";\nimport { motion, useReducedMotion } from \"framer-motion\";\nimport { ArrowRight } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { FlickeringGrid } from \"./FlickeringGrid\";\n\n/* ------------------------------------------------------------------ */\n/* Types & Interfaces                                                 */\n/* ------------------------------------------------------------------ */\n\nexport interface GridCardProps {\n  /** Icon element rendered in the soft-bordered top container */\n  icon: React.ReactNode;\n  /** Main feature title */\n  title: string;\n  /** Explanatory description */\n  description: string;\n  /** Optional target destination URL making the entire card an accessible link */\n  href?: string;\n  /** Grid square color. Default: \"currentColor\" (adapts to light/dark mode) */\n  gridColor?: string;\n  /** Size of each square cell in the flickering grid. Default: 3 */\n  squareSize?: number;\n  /** Gap between adjacent grid cells. Default: 5 */\n  gridGap?: number;\n  /** Flickering rate when card is at rest. Default: 0.1 */\n  restFlickerChance?: number;\n  /** Flickering rate when card is hovered. Default: 0.6 */\n  hoverFlickerChance?: number;\n  /** Max opacity ceiling when card is at rest. Default: 0.15 */\n  restMaxOpacity?: number;\n  /** Max opacity ceiling when card is hovered. Default: 0.4 */\n  hoverMaxOpacity?: number;\n  /** Additional Tailwind CSS classes */\n  className?: string;\n}\n\nexport interface FeatureItem {\n  icon: React.ReactNode;\n  title: string;\n  description: string;\n  href?: string;\n}\n\nexport interface FeatureSectionProps {\n  /** Main section headline */\n  title?: string;\n  /** Subtitle or eyebrow paragraph */\n  subtitle?: string;\n  /** Array of feature items mapped to GridCards */\n  features: FeatureItem[];\n  /** Optional custom grid gap and styling */\n  className?: string;\n}\n\n/* ------------------------------------------------------------------ */\n/* Animation Variants                                                 */\n/* ------------------------------------------------------------------ */\n\nconst cardVariants = {\n  rest: {\n    y: 0,\n    transition: {\n      type: \"spring\",\n      stiffness: 300,\n      damping: 20,\n    },\n  },\n  hover: {\n    y: -4,\n    transition: {\n      type: \"spring\",\n      stiffness: 300,\n      damping: 20,\n    },\n  },\n};\n\nconst iconContainerVariants = {\n  rest: {\n    scale: 1,\n    rotate: 0,\n    transition: {\n      type: \"spring\",\n      stiffness: 300,\n      damping: 20,\n    },\n  },\n  hover: {\n    scale: 1.1,\n    rotate: -4,\n    transition: {\n      type: \"spring\",\n      stiffness: 340,\n      damping: 18,\n    },\n  },\n};\n\nconst arrowVariants = {\n  rest: {\n    x: 0,\n    opacity: 0.5,\n    transition: {\n      type: \"spring\",\n      stiffness: 300,\n      damping: 20,\n    },\n  },\n  hover: {\n    x: 4,\n    opacity: 1,\n    transition: {\n      type: \"spring\",\n      stiffness: 340,\n      damping: 20,\n    },\n  },\n};\n\n/* ------------------------------------------------------------------ */\n/* GridCard Component                                                 */\n/* ------------------------------------------------------------------ */\n\nexport function GridCard({\n  icon,\n  title,\n  description,\n  href,\n  gridColor = \"currentColor\",\n  squareSize = 3,\n  gridGap = 5,\n  restFlickerChance = 0.1,\n  hoverFlickerChance = 0.6,\n  restMaxOpacity = 0.15,\n  hoverMaxOpacity = 0.4,\n  className,\n}: GridCardProps) {\n  const [isHovered, setIsHovered] = useState(false);\n  const shouldReduceMotion = useReducedMotion();\n\n  // Diagonal corner gradient mask: opaque at top-right, transparent toward bottom-left\n  const maskStyle: React.CSSProperties = {\n    WebkitMaskImage:\n      \"linear-gradient(to bottom left, rgba(0,0,0,1) 0%, rgba(0,0,0,0.6) 35%, rgba(0,0,0,0) 75%)\",\n    maskImage:\n      \"linear-gradient(to bottom left, rgba(0,0,0,1) 0%, rgba(0,0,0,0.6) 35%, rgba(0,0,0,0) 75%)\",\n  };\n\n  const cardContent = (\n    <>\n      {/* ── Top-Right Diagonal Flickering Grid Accent ── */}\n      <div\n        style={maskStyle}\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute -top-2 -right-2 h-[60%] w-[60%] z-0 select-none overflow-hidden\"\n      >\n        <FlickeringGrid\n          squareSize={squareSize}\n          gridGap={gridGap}\n          flickerChance={isHovered ? hoverFlickerChance : restFlickerChance}\n          maxOpacity={isHovered ? hoverMaxOpacity : restMaxOpacity}\n          color={gridColor}\n          className=\"h-full w-full opacity-80 transition-opacity duration-300 dark:opacity-70\"\n        />\n      </div>\n\n      {/* ── Card Content Container ── */}\n      <div className=\"relative z-10 flex h-full flex-col justify-between\">\n        <div>\n          {/* Top Row: Icon Container + Optional Arrow Affordance */}\n          <div className=\"flex items-center justify-between\">\n            <motion.div\n              variants={shouldReduceMotion ? undefined : iconContainerVariants}\n              className=\"inline-flex h-11 w-11 items-center justify-center rounded-xl border border-neutral-200/90 bg-neutral-50/90 text-neutral-800 shadow-2xs backdrop-blur-sm transition-colors group-hover:border-neutral-300 group-hover:bg-neutral-100/90 dark:border-neutral-800/90 dark:bg-neutral-900/90 dark:text-neutral-200 dark:group-hover:border-neutral-700\"\n            >\n              {icon}\n            </motion.div>\n\n            {href && (\n              <motion.div\n                variants={shouldReduceMotion ? undefined : arrowVariants}\n                className=\"text-neutral-400 group-hover:text-neutral-900 dark:text-neutral-500 dark:group-hover:text-neutral-100\"\n              >\n                <ArrowRight className=\"h-4 w-4\" />\n              </motion.div>\n            )}\n          </div>\n\n          {/* Heading */}\n          <h3 className=\"mt-4 text-base font-semibold tracking-tight text-neutral-950 dark:text-white sm:text-lg\">\n            {title}\n          </h3>\n\n          {/* Muted Descriptive Text */}\n          <p className=\"mt-2 text-sm leading-relaxed text-neutral-600 dark:text-neutral-400\">\n            {description}\n          </p>\n        </div>\n      </div>\n    </>\n  );\n\n  const sharedClassName = cn(\n    \"group relative flex flex-col justify-between overflow-hidden rounded-xl border p-6 text-left transition-all duration-300\",\n    \"border-neutral-200/90 bg-white text-neutral-900 shadow-2xs hover:border-neutral-300 hover:shadow-md\",\n    \"dark:border-neutral-800/90 dark:bg-neutral-950 dark:text-neutral-100 dark:hover:border-neutral-700 dark:hover:shadow-neutral-900/40\",\n    href &&\n      \"cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-black\",\n    className\n  );\n\n  // If href is set, render as an accessible interactive link\n  if (href) {\n    return (\n      <motion.div\n        variants={shouldReduceMotion ? undefined : cardVariants}\n        initial=\"rest\"\n        whileHover={shouldReduceMotion ? undefined : \"hover\"}\n        whileTap={shouldReduceMotion ? undefined : { scale: 0.98 }}\n        onHoverStart={() => setIsHovered(true)}\n        onHoverEnd={() => setIsHovered(false)}\n        className=\"h-full\"\n      >\n        <Link href={href} className={cn(\"block h-full\", sharedClassName)}>\n          {cardContent}\n        </Link>\n      </motion.div>\n    );\n  }\n\n  return (\n    <motion.div\n      variants={shouldReduceMotion ? undefined : cardVariants}\n      initial=\"rest\"\n      whileHover={shouldReduceMotion ? undefined : \"hover\"}\n      whileTap={shouldReduceMotion ? undefined : { scale: 0.98 }}\n      onHoverStart={() => setIsHovered(true)}\n      onHoverEnd={() => setIsHovered(false)}\n      className={sharedClassName}\n    >\n      {cardContent}\n    </motion.div>\n  );\n}\n\n// Convenient alias\nexport const FeatureCard = GridCard;\n\n/* ------------------------------------------------------------------ */\n/* FeatureSection Component                                           */\n/* ------------------------------------------------------------------ */\n\nconst sectionContainerVariants = {\n  hidden: { opacity: 0 },\n  visible: {\n    opacity: 1,\n    transition: {\n      staggerChildren: 0.08,\n      delayChildren: 0.1,\n    },\n  },\n};\n\nconst itemVariants = {\n  hidden: { opacity: 0, y: 24 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: {\n      duration: 0.6,\n      ease: [0.16, 1, 0.3, 1],\n    },\n  },\n};\n\nexport function FeatureSection({\n  title = \"Engineered for Modern Systems\",\n  subtitle = \"High-performance primitives with tactile spring physics, zero layout shifts, and comprehensive accessibility built in.\",\n  features,\n  className,\n}: FeatureSectionProps) {\n  const shouldReduceMotion = useReducedMotion();\n\n  return (\n    <section className={cn(\"w-full py-16 sm:py-24\", className)}>\n      <div className=\"mx-auto max-w-6xl px-4 sm:px-6 lg:px-8\">\n        {/* Section Header */}\n        {(title || subtitle) && (\n          <motion.div\n            initial={shouldReduceMotion ? false : { opacity: 0, y: 16 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true, margin: \"-40px\" }}\n            transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }}\n            className=\"mb-12 text-center sm:mb-16\"\n          >\n            {title && (\n              <h2 className=\"text-2xl font-bold tracking-tight text-neutral-950 dark:text-white sm:text-3xl md:text-4xl\">\n                {title}\n              </h2>\n            )}\n            {subtitle && (\n              <p className=\"mx-auto mt-3 max-w-2xl text-sm leading-relaxed text-neutral-600 dark:text-neutral-400 sm:text-base\">\n                {subtitle}\n              </p>\n            )}\n          </motion.div>\n        )}\n\n        {/* Responsive Features Grid: 1 col mobile, 2 col md, 3 col lg */}\n        <motion.div\n          variants={shouldReduceMotion ? undefined : sectionContainerVariants}\n          initial=\"hidden\"\n          whileInView=\"visible\"\n          viewport={{ once: true, margin: \"-40px\" }}\n          className=\"grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3\"\n        >\n          {features.map((feature, idx) => (\n            <motion.div\n              key={idx}\n              variants={shouldReduceMotion ? undefined : itemVariants}\n              className=\"h-full\"\n            >\n              <GridCard\n                icon={feature.icon}\n                title={feature.title}\n                description={feature.description}\n                href={feature.href}\n                className=\"h-full\"\n              />\n            </motion.div>\n          ))}\n        </motion.div>\n      </div>\n    </section>\n  );\n}\n\nexport default GridCard;\n",
      "type": "registry:ui",
      "target": "components/ui/grid-card.tsx"
    },
    {
      "path": "lib/utils.ts",
      "content": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n\nexport async function copyToClipboard(text: string): Promise<boolean> {\n  if (typeof window === \"undefined\") return false;\n\n  try {\n    if (navigator.clipboard && window.isSecureContext) {\n      await navigator.clipboard.writeText(text);\n      return true;\n    }\n\n    const textArea = document.createElement(\"textarea\");\n    textArea.value = text;\n    textArea.style.position = \"fixed\";\n    textArea.style.left = \"-999999px\";\n    textArea.style.top = \"-999999px\";\n    document.body.appendChild(textArea);\n    textArea.focus();\n    textArea.select();\n    const succeeded = document.execCommand(\"copy\");\n    textArea.remove();\n    return succeeded;\n  } catch {\n    return false;\n  }\n}\n",
      "type": "registry:lib",
      "target": "lib/utils.ts"
    }
  ]
}
