{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "showcase-card",
  "type": "registry:block",
  "title": "Showcase card",
  "description": "Showcase card",
  "files": [
    {
      "path": "components/usages/showcasecardusage.tsx",
      "content": "import { ShowcaseCard } from \"@/registry/open-source/showcase-card\"\n\nexport default function Example() {\n    <ShowcaseCard\n        tagline=\"Work fast. Live slow.\"\n        heading=\"Create your digital reality.\"\n        description=\"From nothing to everything, let's bring your vision to life.\"\n        imageUrl=\"https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&q=80\"\n        ctaText=\"Send a message\"\n        onCtaClick={() => console.log(\"CTA clicked\")}\n        brandName=\"studio.design\"\n        services={[\"web\", \"product\", \"brand\"]}\n    />\n}",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/showcasecardusage.tsx",
      "content": "import { ShowcaseCard } from \"@/registry/open-source/showcase-card\"\n\nexport default function Example() {\n    <ShowcaseCard\n        tagline=\"Work fast. Live slow.\"\n        heading=\"Create your digital reality.\"\n        description=\"From nothing to everything, let's bring your vision to life.\"\n        imageUrl=\"https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&q=80\"\n        ctaText=\"Send a message\"\n        onCtaClick={() => console.log(\"CTA clicked\")}\n        brandName=\"studio.design\"\n        services={[\"web\", \"product\", \"brand\"]}\n    />\n}",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/showcase-card.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { motion, useMotionValue, useSpring, useTransform } from \"motion/react\"\nimport { cn } from \"@/registry/utilities/cn\"\n\n// Credit:\n// https://www.componentry.fun/docs/components/showcase-card\n\ninterface ShowcaseCardProps {\n    /** Top tagline text */\n    tagline?: string\n    /** Main heading text */\n    heading: string\n    /** Description text below heading */\n    description?: string\n    /** Image URL for the hero section */\n    imageUrl: string\n    /** Alt text for the image */\n    imageAlt?: string\n    /** CTA button text */\n    ctaText?: string\n    /** CTA button click handler */\n    onCtaClick?: () => void\n    /** Brand name or logo text */\n    brandName?: string\n    /** Array of service tags */\n    services?: string[]\n    /** Custom class name */\n    className?: string\n    /** Enable 3D tilt effect on hover */\n    enableTilt?: boolean\n    /** Maximum tilt angle in degrees */\n    maxTilt?: number\n    /** Enable parallax effect on image */\n    enableParallax?: boolean\n}\n\nfunction ShowcaseCard({\n    tagline,\n    heading,\n    description,\n    imageUrl,\n    imageAlt = \"Showcase image\",\n    ctaText,\n    onCtaClick,\n    brandName,\n    services = [],\n    className,\n    enableTilt = true,\n    maxTilt = 8,\n    enableParallax = true,\n}: ShowcaseCardProps) {\n    const cardRef = React.useRef<HTMLDivElement>(null)\n    const [isHovered, setIsHovered] = React.useState(false)\n\n    // Mouse position for tilt effect\n    const mouseX = useMotionValue(0)\n    const mouseY = useMotionValue(0)\n\n    // Smooth spring animation for tilt\n    const springConfig = { damping: 25, stiffness: 150 }\n    const rotateX = useSpring(useTransform(mouseY, [-0.5, 0.5], [maxTilt, -maxTilt]), springConfig)\n    const rotateY = useSpring(useTransform(mouseX, [-0.5, 0.5], [-maxTilt, maxTilt]), springConfig)\n\n    // Parallax transform for image\n    const parallaxX = useSpring(useTransform(mouseX, [-0.5, 0.5], [-15, 15]), springConfig)\n    const parallaxY = useSpring(useTransform(mouseY, [-0.5, 0.5], [-15, 15]), springConfig)\n\n    // Glow effect position\n    const glowX = useSpring(useTransform(mouseX, [-0.5, 0.5], [0, 100]), springConfig)\n    const glowY = useSpring(useTransform(mouseY, [-0.5, 0.5], [0, 100]), springConfig)\n\n    const handleMouseMove = React.useCallback(\n        (e: React.MouseEvent<HTMLDivElement>) => {\n            if (!cardRef.current || !enableTilt) return\n\n            const rect = cardRef.current.getBoundingClientRect()\n            const x = (e.clientX - rect.left) / rect.width - 0.5\n            const y = (e.clientY - rect.top) / rect.height - 0.5\n\n            mouseX.set(x)\n            mouseY.set(y)\n        },\n        [mouseX, mouseY, enableTilt]\n    )\n\n    const handleMouseEnter = () => {\n        setIsHovered(true)\n    }\n\n    const handleMouseLeave = () => {\n        setIsHovered(false)\n        mouseX.set(0)\n        mouseY.set(0)\n    }\n\n    return (\n        <motion.div\n            ref={cardRef}\n            className={cn(\n                \"relative w-full max-w-[400px] rounded-3xl overflow-hidden\",\n                \"bg-neutral-950 dark:bg-neutral-950\",\n                \"shadow-2xl shadow-black/20 dark:shadow-black/40\",\n                \"cursor-pointer select-none\",\n                className\n            )}\n            style={{\n                transformStyle: \"preserve-3d\",\n                perspective: 1000,\n                rotateX: enableTilt ? rotateX : 0,\n                rotateY: enableTilt ? rotateY : 0,\n            }}\n            onMouseMove={handleMouseMove}\n            onMouseEnter={handleMouseEnter}\n            onMouseLeave={handleMouseLeave}\n            initial={{ opacity: 0, y: 40 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.6, ease: [0.25, 0.46, 0.45, 0.94] }}\n            whileHover={{\n                scale: 1.02,\n                boxShadow: \"0 40px 80px -20px rgba(0,0,0,0.5)\",\n            }}\n        >\n            {/* Subtle glow overlay on hover */}\n            <motion.div\n                className=\"absolute inset-0 z-10 pointer-events-none opacity-0\"\n                style={{\n                    background: `radial-gradient(circle at ${glowX}% ${glowY}%, rgba(255,255,255,0.08) 0%, transparent 50%)`,\n                }}\n                animate={{ opacity: isHovered ? 1 : 0 }}\n                transition={{ duration: 0.3 }}\n            />\n\n            {/* Image Section */}\n            <div className=\"relative aspect-[4/3] overflow-hidden\">\n                {/* Tagline */}\n                {tagline && (\n                    <motion.div\n                        className=\"absolute top-4 left-4 sm:top-6 sm:left-6 z-20\"\n                        initial={{ opacity: 0, y: -10 }}\n                        animate={{ opacity: 1, y: 0 }}\n                        transition={{ delay: 0.2, duration: 0.5 }}\n                    >\n                        <span className=\"text-white/90 text-sm sm:text-base font-medium tracking-tight\">\n                            {tagline}\n                        </span>\n                    </motion.div>\n                )}\n\n                {/* Hero Image with Parallax */}\n                <motion.div\n                    className=\"absolute inset-0\"\n                    style={{\n                        x: enableParallax ? parallaxX : 0,\n                        y: enableParallax ? parallaxY : 0,\n                        scale: 1.1,\n                    }}\n                >\n                    <motion.img\n                        src={imageUrl}\n                        alt={imageAlt}\n                        className=\"w-full h-full object-cover\"\n                        initial={{ scale: 1.2 }}\n                        animate={{ scale: isHovered ? 1.15 : 1.1 }}\n                        transition={{ duration: 0.6, ease: \"easeOut\" }}\n                    />\n                </motion.div>\n\n                {/* Gradient overlay for text readability */}\n                <div className=\"absolute inset-0 bg-gradient-to-t from-neutral-950 via-transparent to-transparent opacity-60\" />\n            </div>\n\n            {/* Content Section */}\n            <div className=\"relative z-10 px-4 sm:px-6 pb-4 sm:pb-6 -mt-8\">\n                {/* Heading */}\n                <motion.h2\n                    className=\"text-2xl sm:text-3xl lg:text-4xl font-medium text-white tracking-tight leading-tight mb-2 sm:mb-3\"\n                    initial={{ opacity: 0, y: 20 }}\n                    animate={{ opacity: 1, y: 0 }}\n                    transition={{ delay: 0.3, duration: 0.5 }}\n                >\n                    {heading}\n                </motion.h2>\n\n                {/* Description */}\n                {description && (\n                    <motion.p\n                        className=\"text-sm sm:text-base text-neutral-400 mb-4 sm:mb-6 leading-relaxed\"\n                        initial={{ opacity: 0, y: 20 }}\n                        animate={{ opacity: 1, y: 0 }}\n                        transition={{ delay: 0.4, duration: 0.5 }}\n                    >\n                        {description}\n                    </motion.p>\n                )}\n\n                {/* CTA Button */}\n                {ctaText && (\n                    <motion.button\n                        onClick={onCtaClick}\n                        className={cn(\n                            \"relative px-4 sm:px-5 py-2 sm:py-2.5 rounded-full\",\n                            \"text-sm font-medium\",\n                            \"bg-neutral-800/80 text-neutral-200\",\n                            \"border border-neutral-700/50\",\n                            \"overflow-hidden\",\n                            \"transition-colors duration-300\",\n                            \"hover:border-neutral-600/80\"\n                        )}\n                        initial={{ opacity: 0, y: 20 }}\n                        animate={{ opacity: 1, y: 0 }}\n                        transition={{ delay: 0.5, duration: 0.5 }}\n                        whileHover={{ scale: 1.05 }}\n                        whileTap={{ scale: 0.98 }}\n                    >\n                        {/* Button hover shine effect */}\n                        <motion.span\n                            className=\"absolute inset-0 bg-gradient-to-r from-transparent via-white/10 to-transparent -translate-x-full\"\n                            animate={isHovered ? { translateX: \"200%\" } : { translateX: \"-100%\" }}\n                            transition={{ duration: 0.6, ease: \"easeInOut\" }}\n                        />\n                        <span className=\"relative z-10\">{ctaText}</span>\n                    </motion.button>\n                )}\n            </div>\n\n            {/* Footer Section */}\n            {(brandName || services.length > 0) && (\n                <motion.div\n                    className=\"px-4 sm:px-6 py-4 sm:py-5 border-t border-neutral-800/50\"\n                    initial={{ opacity: 0 }}\n                    animate={{ opacity: 1 }}\n                    transition={{ delay: 0.6, duration: 0.5 }}\n                >\n                    <div className=\"flex items-center justify-between flex-wrap gap-2\">\n                        {/* Brand Name */}\n                        {brandName && (\n                            <motion.span\n                                className=\"text-xs sm:text-sm text-neutral-400 font-medium\"\n                                whileHover={{ color: \"#ffffff\" }}\n                                transition={{ duration: 0.2 }}\n                            >\n                                {brandName}\n                            </motion.span>\n                        )}\n\n                        {/* Services */}\n                        {services.length > 0 && (\n                            <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n                                {services.map((service, index) => (\n                                    <React.Fragment key={service}>\n                                        <motion.span\n                                            className=\"text-xs sm:text-sm text-neutral-500\"\n                                            whileHover={{ color: \"#ffffff\", scale: 1.05 }}\n                                            transition={{ duration: 0.2 }}\n                                        >\n                                            {service}\n                                        </motion.span>\n                                        {index < services.length - 1 && (\n                                            <motion.span\n                                                className=\"text-neutral-600\"\n                                                initial={{ rotate: 0 }}\n                                                whileHover={{ rotate: 90 }}\n                                                transition={{ duration: 0.3 }}\n                                            >\n                                                ✦\n                                            </motion.span>\n                                        )}\n                                    </React.Fragment>\n                                ))}\n                            </div>\n                        )}\n                    </div>\n                </motion.div>\n            )}\n\n            {/* Border glow effect */}\n            <motion.div\n                className=\"absolute inset-0 rounded-3xl pointer-events-none\"\n                style={{\n                    boxShadow: \"inset 0 0 0 1px rgba(255,255,255,0.05)\",\n                }}\n                animate={{\n                    boxShadow: isHovered\n                        ? \"inset 0 0 0 1px rgba(255,255,255,0.1)\"\n                        : \"inset 0 0 0 1px rgba(255,255,255,0.05)\",\n                }}\n                transition={{ duration: 0.3 }}\n            />\n        </motion.div>\n    )\n}\n\n// Compact variant for grids\ninterface ShowcaseCardCompactProps {\n    heading: string\n    description?: string\n    imageUrl: string\n    imageAlt?: string\n    className?: string\n    onClick?: () => void\n}\n\nfunction ShowcaseCardCompact({\n    heading,\n    description,\n    imageUrl,\n    imageAlt = \"Showcase image\",\n    className,\n    onClick,\n}: ShowcaseCardCompactProps) {\n    const [isHovered, setIsHovered] = React.useState(false)\n\n    return (\n        <motion.div\n            className={cn(\n                \"relative w-full rounded-2xl overflow-hidden cursor-pointer\",\n                \"bg-neutral-900 dark:bg-neutral-900\",\n                \"shadow-lg shadow-black/10\",\n                className\n            )}\n            onClick={onClick}\n            onMouseEnter={() => setIsHovered(true)}\n            onMouseLeave={() => setIsHovered(false)}\n            whileHover={{ scale: 1.02, y: -4 }}\n            whileTap={{ scale: 0.98 }}\n            transition={{ duration: 0.3 }}\n        >\n            {/* Image */}\n            <div className=\"relative aspect-[16/10] overflow-hidden\">\n                <motion.img\n                    src={imageUrl}\n                    alt={imageAlt}\n                    className=\"w-full h-full object-cover\"\n                    animate={{ scale: isHovered ? 1.08 : 1 }}\n                    transition={{ duration: 0.4, ease: \"easeOut\" }}\n                />\n                <div className=\"absolute inset-0 bg-gradient-to-t from-neutral-900 via-transparent to-transparent opacity-80\" />\n            </div>\n\n            {/* Content */}\n            <div className=\"p-4 -mt-6 relative z-10\">\n                <h3 className=\"text-lg font-medium text-white mb-1 line-clamp-2\">{heading}</h3>\n                {description && (\n                    <p className=\"text-sm text-neutral-400 line-clamp-2\">{description}</p>\n                )}\n            </div>\n\n            {/* Hover indicator */}\n            <motion.div\n                className=\"absolute bottom-4 right-4\"\n                initial={{ opacity: 0, x: -10 }}\n                animate={{ opacity: isHovered ? 1 : 0, x: isHovered ? 0 : -10 }}\n                transition={{ duration: 0.2 }}\n            >\n                <svg\n                    width=\"20\"\n                    height=\"20\"\n                    viewBox=\"0 0 24 24\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                    strokeWidth=\"2\"\n                    strokeLinecap=\"round\"\n                    strokeLinejoin=\"round\"\n                    className=\"text-neutral-400\"\n                >\n                    <path d=\"M7 17L17 7\" />\n                    <path d=\"M7 7h10v10\" />\n                </svg>\n            </motion.div>\n        </motion.div>\n    )\n}\n\n// Horizontal variant for featured sections\ninterface ShowcaseCardHorizontalProps extends Omit<ShowcaseCardProps, \"enableTilt\" | \"maxTilt\"> {\n    imagePosition?: \"left\" | \"right\"\n}\n\nfunction ShowcaseCardHorizontal({\n    tagline,\n    heading,\n    description,\n    imageUrl,\n    imageAlt = \"Showcase image\",\n    ctaText,\n    onCtaClick,\n    brandName,\n    services = [],\n    className,\n    imagePosition = \"left\",\n    enableParallax = true,\n}: ShowcaseCardHorizontalProps) {\n    const [isHovered, setIsHovered] = React.useState(false)\n\n    return (\n        <motion.div\n            className={cn(\n                \"relative w-full rounded-3xl overflow-hidden\",\n                \"bg-neutral-950 dark:bg-neutral-950\",\n                \"shadow-2xl shadow-black/20\",\n                \"flex flex-col md:flex-row\",\n                imagePosition === \"right\" && \"md:flex-row-reverse\",\n                className\n            )}\n            onMouseEnter={() => setIsHovered(true)}\n            onMouseLeave={() => setIsHovered(false)}\n            initial={{ opacity: 0, y: 40 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.6 }}\n            whileHover={{ scale: 1.01 }}\n        >\n            {/* Image Section */}\n            <div className=\"relative w-full md:w-1/2 aspect-[4/3] md:aspect-auto md:min-h-[400px] overflow-hidden\">\n                {tagline && (\n                    <div className=\"absolute top-6 left-6 z-20\">\n                        <span className=\"text-white/90 text-sm font-medium\">{tagline}</span>\n                    </div>\n                )}\n\n                <motion.img\n                    src={imageUrl}\n                    alt={imageAlt}\n                    className=\"w-full h-full object-cover\"\n                    animate={{ scale: isHovered ? 1.05 : 1 }}\n                    transition={{ duration: 0.6, ease: \"easeOut\" }}\n                />\n                <div className=\"absolute inset-0 bg-gradient-to-r from-neutral-950/60 via-transparent to-transparent md:hidden\" />\n            </div>\n\n            {/* Content Section */}\n            <div className=\"relative z-10 w-full md:w-1/2 p-6 md:p-10 flex flex-col justify-center\">\n                <motion.h2\n                    className=\"text-3xl md:text-4xl lg:text-5xl font-medium text-white tracking-tight leading-tight mb-4\"\n                    initial={{ opacity: 0, y: 20 }}\n                    animate={{ opacity: 1, y: 0 }}\n                    transition={{ delay: 0.2, duration: 0.5 }}\n                >\n                    {heading}\n                </motion.h2>\n\n                {description && (\n                    <motion.p\n                        className=\"text-base md:text-lg text-neutral-400 mb-8 leading-relaxed max-w-md\"\n                        initial={{ opacity: 0, y: 20 }}\n                        animate={{ opacity: 1, y: 0 }}\n                        transition={{ delay: 0.3, duration: 0.5 }}\n                    >\n                        {description}\n                    </motion.p>\n                )}\n\n                {ctaText && (\n                    <motion.button\n                        onClick={onCtaClick}\n                        className={cn(\n                            \"w-fit px-6 py-3 rounded-full\",\n                            \"text-sm font-medium\",\n                            \"bg-white text-neutral-900\",\n                            \"transition-all duration-300\",\n                            \"hover:bg-neutral-200\"\n                        )}\n                        initial={{ opacity: 0, y: 20 }}\n                        animate={{ opacity: 1, y: 0 }}\n                        transition={{ delay: 0.4, duration: 0.5 }}\n                        whileHover={{ scale: 1.05 }}\n                        whileTap={{ scale: 0.98 }}\n                    >\n                        {ctaText}\n                    </motion.button>\n                )}\n\n                {(brandName || services.length > 0) && (\n                    <div className=\"mt-auto pt-8 flex items-center justify-between\">\n                        {brandName && (\n                            <span className=\"text-sm text-neutral-500\">{brandName}</span>\n                        )}\n                        {services.length > 0 && (\n                            <div className=\"flex items-center gap-3\">\n                                {services.map((service, index) => (\n                                    <React.Fragment key={service}>\n                                        <span className=\"text-sm text-neutral-500\">{service}</span>\n                                        {index < services.length - 1 && (\n                                            <span className=\"text-neutral-600\">✦</span>\n                                        )}\n                                    </React.Fragment>\n                                ))}\n                            </div>\n                        )}\n                    </div>\n                )}\n            </div>\n        </motion.div>\n    )\n}\n\n// Grid container for showcase cards\ninterface ShowcaseGridProps {\n    children: React.ReactNode\n    columns?: 1 | 2 | 3 | 4\n    gap?: \"sm\" | \"md\" | \"lg\"\n    className?: string\n}\n\nfunction ShowcaseGrid({\n    children,\n    columns = 3,\n    gap = \"md\",\n    className,\n}: ShowcaseGridProps) {\n    const gapClasses = {\n        sm: \"gap-4\",\n        md: \"gap-6\",\n        lg: \"gap-8\",\n    }\n\n    const columnClasses = {\n        1: \"grid-cols-1\",\n        2: \"grid-cols-1 sm:grid-cols-2\",\n        3: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\",\n        4: \"grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\",\n    }\n\n    return (\n        <div className={cn(\"grid\", columnClasses[columns], gapClasses[gap], className)}>\n            {children}\n        </div>\n    )\n}\n\nexport {\n    ShowcaseCard,\n    ShowcaseCardCompact,\n    ShowcaseCardHorizontal,\n    ShowcaseGrid,\n    type ShowcaseCardProps,\n    type ShowcaseCardCompactProps,\n    type ShowcaseCardHorizontalProps,\n    type ShowcaseGridProps,\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/utilities/cn.ts",
      "content": "import { ClassValue, clsx } from \"clsx\";\r\nimport { twMerge } from \"tailwind-merge\";\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n\treturn twMerge(clsx(inputs));\r\n}\r\n",
      "type": "registry:ui"
    }
  ]
}