{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "feature-03",
  "title": "Feature 03",
  "description": "A sticky title/description column paired with a scrolling list of features, each with its own title, description, and media.",
  "dependencies": [
    "react-wrap-balancer",
    "motion"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/blocks/feature/feature-03/feature-03.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { motion, useReducedMotion, type Variants } from 'motion/react'\nimport Balancer from 'react-wrap-balancer'\n\nimport { cn } from '@/lib/utils'\n\nexport interface Feature03Item {\n  id: string\n  title: string\n  description: string\n  media: {\n    src: string\n    alt: string\n  }\n}\n\nexport interface Feature03Props {\n  title: string\n  description?: string\n  items: Feature03Item[]\n  variant?: 'standard' | 'compact'\n  animation?: 'none' | 'subtle'\n}\n\nconst variantStyles = {\n  standard: {\n    section: 'py-16 sm:py-24',\n    grid: 'gap-10 md:gap-16',\n    title: 'text-2xl sm:text-3xl md:text-4xl',\n    description: 'text-sm sm:text-base',\n    itemGap: 'gap-16 sm:gap-24',\n    itemTitle: 'text-xl sm:text-2xl',\n    itemDescription: 'text-sm sm:text-base',\n    media: 'aspect-16/10',\n  },\n  compact: {\n    section: 'py-12 sm:py-16',\n    grid: 'gap-8 md:gap-12',\n    title: 'text-xl sm:text-2xl md:text-3xl',\n    description: 'text-sm',\n    itemGap: 'gap-12 sm:gap-16',\n    itemTitle: 'text-lg sm:text-xl',\n    itemDescription: 'text-sm',\n    media: 'aspect-16/11',\n  },\n} as const\n\nconst container: Variants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.1, delayChildren: 0.05 } },\n}\n\nconst headerItem: Variants = {\n  hidden: { opacity: 0, y: 12, filter: 'blur(6px)' },\n  visible: {\n    opacity: 1,\n    y: 0,\n    filter: 'blur(0px)',\n    transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] },\n  },\n}\n\nconst featureItem: Variants = {\n  hidden: { opacity: 0, y: 16, filter: 'blur(8px)' },\n  visible: {\n    opacity: 1,\n    y: 0,\n    filter: 'blur(0px)',\n    transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] },\n  },\n}\n\nfunction Reveal({\n  active,\n  variants,\n  className,\n  children,\n}: Readonly<{\n  active: boolean\n  variants?: Variants\n  className?: string\n  children: React.ReactNode\n}>) {\n  if (!active) return <div className={className}>{children}</div>\n\n  return (\n    <motion.div variants={variants ?? headerItem} className={className}>\n      {children}\n    </motion.div>\n  )\n}\n\nfunction FeatureRow({\n  item,\n  animate,\n  vs,\n}: Readonly<{\n  item: Feature03Item\n  animate: boolean\n  vs: (typeof variantStyles)[keyof typeof variantStyles]\n}>) {\n  const content = (\n    <>\n      <h3\n        className={cn(\n          'text-foreground font-semibold tracking-tight',\n          vs.itemTitle,\n        )}\n      >\n        <Balancer>{item.title}</Balancer>\n      </h3>\n      <p className={cn('text-muted-foreground max-w-2xl', vs.itemDescription)}>\n        <Balancer>{item.description}</Balancer>\n      </p>\n      <div\n        className={cn(\n          'relative w-full overflow-hidden rounded-2xl shadow-sm outline outline-black/10 dark:outline-white/10',\n          vs.media,\n        )}\n      >\n        <img\n          src={item.media.src}\n          alt={item.media.alt || item.title}\n          loading=\"lazy\"\n          decoding=\"async\"\n          className=\"absolute inset-0 size-full object-cover\"\n        />\n      </div>\n    </>\n  )\n\n  if (!animate) return <div className=\"flex flex-col gap-4\">{content}</div>\n\n  return (\n    <motion.div\n      initial=\"hidden\"\n      whileInView=\"visible\"\n      viewport={{ once: true, margin: '-40px' }}\n      variants={featureItem}\n      className=\"flex flex-col gap-4\"\n    >\n      {content}\n    </motion.div>\n  )\n}\n\nexport function Feature03({\n  title,\n  description,\n  items,\n  variant = 'standard',\n  animation = 'none',\n}: Readonly<Feature03Props>) {\n  const reduce = useReducedMotion()\n  const animate = animation === 'subtle' && !reduce\n  const vs = variantStyles[variant]\n\n  if (!items.length) return null\n\n  const titleElement = title && (\n    <h2\n      className={cn('text-foreground font-semibold tracking-tight', vs.title)}\n    >\n      <Balancer>{title}</Balancer>\n    </h2>\n  )\n\n  const descriptionElement = description && (\n    <p className={cn('text-muted-foreground', vs.description)}>\n      <Balancer>{description}</Balancer>\n    </p>\n  )\n\n  const headerElement = (\n    <div className=\"flex flex-col gap-4 md:sticky md:top-24 md:self-start\">\n      {titleElement}\n      {descriptionElement}\n    </div>\n  )\n\n  const itemsListElement = (\n    <div className={cn('flex flex-col', vs.itemGap)}>\n      {items.map((item) => (\n        <FeatureRow key={item.id} item={item} animate={animate} vs={vs} />\n      ))}\n    </div>\n  )\n\n  return (\n    <section className=\"bg-background w-full\">\n      <motion.div\n        className={cn('mx-auto w-full max-w-6xl px-6', vs.section)}\n        variants={animate ? container : undefined}\n        initial={animate ? 'hidden' : false}\n        whileInView={animate ? 'visible' : undefined}\n        viewport={{ once: true, margin: '-80px' }}\n      >\n        <div\n          className={cn(\n            'grid grid-cols-1 md:grid-cols-[minmax(0,1fr)_minmax(0,1.4fr)]',\n            vs.grid,\n          )}\n        >\n          <Reveal active={animate}>{headerElement}</Reveal>\n          {itemsListElement}\n        </div>\n      </motion.div>\n    </section>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/flx/blocks/feature/feature-03/feature-03.tsx"
    },
    {
      "path": "registry/blocks/feature/feature-03/feature-03-example.tsx",
      "content": "import { Feature03, type Feature03Props } from './feature-03'\n\nexport const values = {\n  title: 'Three surfaces, one connected workflow.',\n  description:\n    'Flexnative brings block browsing, live editing, and code export into a single environment, built around how teams actually ship UI.',\n  variant: 'standard',\n  animation: 'subtle',\n  items: [\n    {\n      id: '1',\n      title: 'Block Library',\n      description:\n        'Browse hundreds of production-ready blocks across every category, filtered by preset, type, and layout.',\n      media: {\n        src: 'https://images.unsplash.com/photo-1750044656775-40fd937a2438?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',\n        alt: 'Soft gradient abstract in warm tones',\n      },\n    },\n    {\n      id: '2',\n      title: 'Live Editor',\n      description:\n        'Tweak copy, media, and variants in real time, with an instant preview across breakpoints and presets.',\n      media: {\n        src: 'https://images.unsplash.com/photo-1441039995991-e5c1178e605a?q=80&w=2053&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',\n        alt: 'Soft abstract shapes in muted color',\n      },\n    },\n    {\n      id: '3',\n      title: 'Code Export',\n      description:\n        \"Copy production-ready code or install it via the CLI, typed and documented so it's ready to ship today.\",\n      media: {\n        src: 'https://images.unsplash.com/photo-1640535092659-7856bff6679d?q=80&w=2071&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',\n        alt: 'Soft warm gradient',\n      },\n    },\n  ],\n} satisfies Feature03Props\n\nexport function Feature03Example() {\n  return <Feature03 {...values} />\n}\n",
      "type": "registry:component",
      "target": "components/flx/blocks/feature/feature-03/feature-03-example.tsx"
    }
  ],
  "meta": {
    "iframeHeight": 1400
  },
  "type": "registry:block"
}