{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "feature-mockup-grid-01",
  "title": "Feature Mockup Grid",
  "description": "A centered label and heading over five cards, two on top and three below, each with a small product mockup drawn in theme colors",
  "dependencies": [],
  "registryDependencies": [
    "https://ui.cortexcn.dev/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/blocks/feature-mockup-grid-01/components/feature-mockup-grid.tsx",
      "content": "import type { ReactNode } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\n// Cortexcn item \"feature-mockup-grid-01\", revision 36f4edb4b416, MIT license.\n// https://ui.cortexcn.dev/r/v/feature-mockup-grid-01/36f4edb4b416.json\n\nexport interface MockupFeature {\n  title: string;\n  description: string;\n  /** The picture above the title. It is decorative, so screen readers skip it. */\n  visual: ReactNode;\n}\n\nexport interface FeatureMockupGridProps {\n  /** Small label above the heading. */\n  label?: string;\n  heading?: string;\n  /**\n   * Five cards read best: the first two share the top row and the other\n   * three share the row below. On phones each card takes the full width.\n   */\n  features?: MockupFeature[];\n  className?: string;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Shared pieces                                                              */\n/* -------------------------------------------------------------------------- */\n\n/** A white window with a soft shadow, the base of most mockups. */\nconst WINDOW = \"overflow-hidden rounded-lg border bg-card shadow-sm\";\n\n/** Fades a mockup out before it reaches the card's text. */\nconst FADE_OUT_BOTTOM = \"mask-b-from-50%\";\n\n/** Lifts a mockup a little while the card is hovered. */\nconst LIFT_ON_HOVER =\n  \"transition-transform duration-300 ease-out motion-safe:group-hover:-translate-y-1\";\n\n/** A gray stand-in for a line of text or an image. */\nfunction Placeholder({ className }: { className?: string }) {\n  return <span className={cn(\"block rounded-full bg-muted\", className)} />;\n}\n\n/** The top strip of a browser window: three dots and an address bar. */\nfunction BrowserBar() {\n  return (\n    <div className=\"flex items-center gap-1 px-2.5 py-2\">\n      <span className=\"size-1.5 rounded-full bg-primary\" />\n      <span className=\"size-1.5 rounded-full bg-muted\" />\n      <span className=\"size-1.5 rounded-full bg-muted\" />\n      <Placeholder className=\"ml-2 h-1.5 flex-1\" />\n      <Placeholder className=\"ml-6 h-1.5 w-6\" />\n    </div>\n  );\n}\n\n/** A small landing page: a headline, two lines of copy, a button, and an image. */\nfunction LandingSketch({ compact = false }: { compact?: boolean }) {\n  return (\n    <div\n      className={cn(\n        \"flex flex-col items-center\",\n        compact ? \"px-2 pt-2\" : \"px-5 pt-3\",\n      )}\n    >\n      <Placeholder className=\"h-2 w-1/2\" />\n      <Placeholder className=\"mt-2 h-1 w-2/5\" />\n      <Placeholder className=\"mt-1 h-1 w-1/3\" />\n      <span\n        className={cn(\n          \"mt-2.5 h-2 rounded-full bg-primary\",\n          compact ? \"w-6\" : \"w-10\",\n        )}\n      />\n      <Placeholder\n        className={cn(\"mt-3 w-full rounded-sm\", compact ? \"h-16\" : \"h-24\")}\n      />\n    </div>\n  );\n}\n\n/** Puts two faint pages behind its child, so it reads as one of a stack. */\nfunction Stacked({\n  children,\n  className,\n}: {\n  children: ReactNode;\n  className?: string;\n}) {\n  return (\n    <div className={cn(\"relative\", className)}>\n      <div className=\"absolute inset-x-5 -top-3 h-full rounded-lg border bg-card/50\" />\n      <div className=\"absolute inset-x-2.5 -top-1.5 h-full rounded-lg border bg-card/80\" />\n      <div className=\"relative\">{children}</div>\n    </div>\n  );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Icons                                                                      */\n/* -------------------------------------------------------------------------- */\n\n/** A 24px line icon drawn in the current text color. */\nfunction Icon({\n  children,\n  className,\n}: {\n  children: ReactNode;\n  className?: string;\n}) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={cn(\"size-4\", className)}\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      strokeWidth=\"2\"\n      viewBox=\"0 0 24 24\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      {children}\n    </svg>\n  );\n}\n\nfunction SparkleIcon({ className }: { className?: string }) {\n  return (\n    <Icon className={className}>\n      <path\n        d=\"M12 3c.5 4.5 4 8 9 9-5 1-8.5 4.5-9 9-.5-4.5-4-8-9-9 5-1 8.5-4.5 9-9z\"\n        fill=\"currentColor\"\n      />\n    </Icon>\n  );\n}\n\nfunction LockIcon({ className }: { className?: string }) {\n  return (\n    <Icon className={className}>\n      <rect height=\"10\" rx=\"2\" width=\"14\" x=\"5\" y=\"11\" />\n      <path d=\"M8 11V7a4 4 0 0 1 8 0v4\" />\n    </Icon>\n  );\n}\n\nfunction TrendUpIcon({ className }: { className?: string }) {\n  return (\n    <Icon className={className}>\n      <path d=\"M3 17l6-6 4 4 8-8\" />\n      <path d=\"M15 7h6v6\" />\n    </Icon>\n  );\n}\n\nfunction MailIcon() {\n  return (\n    <Icon>\n      <rect height=\"14\" rx=\"2\" width=\"18\" x=\"3\" y=\"5\" />\n      <path d=\"M3 7l9 6 9-6\" />\n    </Icon>\n  );\n}\n\nfunction BranchIcon() {\n  return (\n    <Icon>\n      <circle cx=\"6\" cy=\"5\" r=\"2\" />\n      <circle cx=\"6\" cy=\"19\" r=\"2\" />\n      <circle cx=\"18\" cy=\"7\" r=\"2\" />\n      <path d=\"M6 7v10M18 9c0 4.5-4 6-12 8\" />\n    </Icon>\n  );\n}\n\nfunction ChatIcon() {\n  return (\n    <Icon>\n      <path d=\"M21 12a8 8 0 0 1-11.6 7.1L4 20l1-4.6A8 8 0 1 1 21 12z\" />\n    </Icon>\n  );\n}\n\nfunction BoltIcon() {\n  return (\n    <Icon>\n      <path d=\"M13 2L4 14h7l-1 8 9-12h-7l1-8z\" />\n    </Icon>\n  );\n}\n\nfunction CalendarIcon() {\n  return (\n    <Icon>\n      <rect height=\"16\" rx=\"2\" width=\"18\" x=\"3\" y=\"5\" />\n      <path d=\"M3 10h18M8 3v4M16 3v4\" />\n    </Icon>\n  );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Card 1: a prompt that generates sections                                   */\n/* -------------------------------------------------------------------------- */\n\ntype DraftLayout = \"image-right\" | \"centered\" | \"image-left\";\n\nconst DRAFT_LAYOUTS: DraftLayout[] = [\"image-right\", \"centered\", \"image-left\"];\n\n/** One of the three section drafts the assistant offers. */\nfunction DraftSketch({ layout }: { layout: DraftLayout }) {\n  return (\n    <div className=\"flex h-24 flex-col gap-1.5 rounded-sm border bg-card p-1.5\">\n      <div className=\"flex items-center gap-1\">\n        <span className=\"size-1 rounded-full bg-primary/60\" />\n        <Placeholder className=\"h-1 w-1/3\" />\n      </div>\n      {layout === \"centered\" ? (\n        <div className=\"flex flex-col items-center gap-1 pt-0.5\">\n          <Placeholder className=\"h-1.5 w-3/4\" />\n          <Placeholder className=\"h-1 w-1/2\" />\n        </div>\n      ) : (\n        <div\n          className={cn(\n            \"flex gap-1.5\",\n            layout === \"image-left\" && \"flex-row-reverse\",\n          )}\n        >\n          <div className=\"flex flex-1 flex-col gap-1 pt-0.5\">\n            <Placeholder className=\"h-1.5 w-full\" />\n            <Placeholder className=\"h-1 w-2/3\" />\n          </div>\n          <Placeholder className=\"h-6 w-1/3 rounded-sm\" />\n        </div>\n      )}\n      <Placeholder className=\"w-full flex-1 rounded-sm\" />\n    </div>\n  );\n}\n\nfunction AssistantVisual() {\n  return (\n    <>\n      <div\n        className={cn(\n          WINDOW,\n          FADE_OUT_BOTTOM,\n          \"absolute inset-x-6 top-6 sm:inset-x-14\",\n        )}\n      >\n        <BrowserBar />\n        <div className=\"px-3 pb-3\">\n          <div className=\"flex items-center justify-between\">\n            <span className=\"font-medium text-[10px] text-foreground\">\n              Pick a section\n            </span>\n            <span className=\"flex size-4 items-center justify-center rounded-sm bg-primary/10 text-primary\">\n              <SparkleIcon className=\"size-2.5\" />\n            </span>\n          </div>\n          <div className=\"mt-2.5 grid grid-cols-3 gap-2\">\n            {DRAFT_LAYOUTS.map((layout) => (\n              <DraftSketch key={layout} layout={layout} />\n            ))}\n          </div>\n        </div>\n      </div>\n\n      <div\n        className={cn(\n          LIFT_ON_HOVER,\n          \"absolute inset-x-0 bottom-5 mx-auto flex w-60 max-w-[85%] items-center gap-2 rounded-lg border border-primary/30 bg-card p-1 pl-3 shadow-lg ring-4 ring-primary/5\",\n        )}\n      >\n        <span className=\"flex-1 truncate text-[10px] text-muted-foreground\">\n          Describe the section you need\n        </span>\n        <span className=\"flex items-center gap-1 rounded-md bg-primary px-2 py-1 font-medium text-[10px] text-primary-foreground\">\n          <SparkleIcon className=\"size-2.5\" />\n          Generate\n        </span>\n      </div>\n    </>\n  );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Card 2: a stack of templates                                               */\n/* -------------------------------------------------------------------------- */\n\nfunction TemplatesVisual() {\n  return (\n    <Stacked\n      className={cn(FADE_OUT_BOTTOM, \"absolute inset-x-8 top-9 sm:inset-x-16\")}\n    >\n      <div className={cn(WINDOW, LIFT_ON_HOVER)}>\n        <BrowserBar />\n        <LandingSketch />\n      </div>\n    </Stacked>\n  );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Card 3: a page views chart                                                 */\n/* -------------------------------------------------------------------------- */\n\n/** Bar heights as a percent of the chart's height. */\nconst PAGE_VIEWS = [\n  { month: \"Jan\", value: 48 },\n  { month: \"Feb\", value: 60 },\n  { month: \"Mar\", value: 74 },\n  { month: \"Apr\", value: 66 },\n  { month: \"May\", value: 42 },\n  { month: \"Jun\", value: 62 },\n  { month: \"Jul\", value: 70 },\n  { month: \"Aug\", value: 86 },\n  { month: \"Sep\", value: 58 },\n];\n\nfunction PerformanceVisual() {\n  return (\n    <Stacked\n      className={cn(FADE_OUT_BOTTOM, \"absolute inset-x-6 top-12 sm:inset-x-8\")}\n    >\n      <div className={cn(WINDOW, LIFT_ON_HOVER, \"p-3\")}>\n        <div className=\"flex items-center justify-between\">\n          <span className=\"font-medium text-[11px] text-foreground\">\n            Page views\n          </span>\n          <span className=\"flex items-center gap-0.5 rounded-full bg-emerald-500/10 px-1.5 py-0.5 font-medium text-[9px] text-emerald-600 dark:text-emerald-400\">\n            <TrendUpIcon className=\"size-2.5\" />\n            25%\n          </span>\n        </div>\n\n        <div className=\"mt-3 flex h-20 items-end gap-1.5\">\n          {PAGE_VIEWS.map(({ month, value }) => (\n            <span\n              className=\"flex-1 rounded-t-sm bg-linear-to-b from-primary to-primary/30\"\n              key={month}\n              style={{ height: `${value}%` }}\n            />\n          ))}\n        </div>\n        <div className=\"mt-1.5 flex gap-1.5\">\n          {PAGE_VIEWS.map(({ month }) => (\n            <span\n              className=\"flex-1 text-center text-[8px] text-muted-foreground\"\n              key={month}\n            >\n              {month}\n            </span>\n          ))}\n        </div>\n      </div>\n    </Stacked>\n  );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Card 4: apps orbiting a secure center                                      */\n/* -------------------------------------------------------------------------- */\n\ninterface Integration {\n  name: string;\n  icon: ReactNode;\n  /** Where it sits on the outer ring, in percent of the ring's box. */\n  x: number;\n  y: number;\n}\n\n/** Swap these icons for the logos of the apps you connect to. */\nconst INTEGRATIONS: Integration[] = [\n  { name: \"Email\", icon: <MailIcon />, x: 50, y: 0 },\n  { name: \"Code\", icon: <BranchIcon />, x: 98, y: 37 },\n  { name: \"Chat\", icon: <ChatIcon />, x: 88, y: 82 },\n  { name: \"Automation\", icon: <BoltIcon />, x: 18, y: 88 },\n  { name: \"Calendar\", icon: <CalendarIcon />, x: 3, y: 33 },\n];\n\nfunction IntegrationsVisual() {\n  return (\n    <div className=\"absolute inset-0 flex items-center justify-center pt-4\">\n      <div className=\"relative size-44 rounded-full border\">\n        <div className=\"absolute inset-6 rounded-full border bg-card/40\" />\n        <div className=\"absolute inset-14 rounded-full bg-primary/10\" />\n\n        <span className=\"absolute top-1/2 left-1/2 flex size-10 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg\">\n          <LockIcon className=\"size-4\" />\n        </span>\n\n        {INTEGRATIONS.map((integration) => (\n          <span\n            className=\"absolute flex size-9 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full border bg-card text-foreground shadow-sm\"\n            key={integration.name}\n            style={{ left: `${integration.x}%`, top: `${integration.y}%` }}\n          >\n            {integration.icon}\n          </span>\n        ))}\n      </div>\n    </div>\n  );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Card 5: the same page on a laptop and a phone                              */\n/* -------------------------------------------------------------------------- */\n\nfunction ResponsiveVisual() {\n  return (\n    <div className={cn(FADE_OUT_BOTTOM, \"absolute inset-0\")}>\n      <div className={cn(WINDOW, \"absolute top-6 right-12 left-3 sm:right-16\")}>\n        <BrowserBar />\n        <LandingSketch />\n      </div>\n\n      <div\n        className={cn(\n          WINDOW,\n          LIFT_ON_HOVER,\n          \"absolute top-14 right-3 w-[36%] max-w-28 rounded-xl shadow-md\",\n        )}\n      >\n        <div className=\"flex items-center justify-between px-2 py-1.5\">\n          <span className=\"size-1.5 rounded-full bg-primary\" />\n          <Placeholder className=\"h-1.5 w-1/3\" />\n        </div>\n        <LandingSketch compact />\n      </div>\n    </div>\n  );\n}\n\n/* -------------------------------------------------------------------------- */\n/* The section                                                                */\n/* -------------------------------------------------------------------------- */\n\nconst DEFAULT_FEATURES: MockupFeature[] = [\n  {\n    title: \"Generate sections from a prompt\",\n    description:\n      \"Describe the section you need and choose from three drafts. Each one uses your fonts, colors, and spacing.\",\n    visual: <AssistantVisual />,\n  },\n  {\n    title: \"Start from a template\",\n    description:\n      \"Pick a layout that fits your product, then change the fonts, colors, and copy until it reads like your brand.\",\n    visual: <TemplatesVisual />,\n  },\n  {\n    title: \"Search insights built in\",\n    description:\n      \"Track page views month by month and see which pages need attention.\",\n    visual: <PerformanceVisual />,\n  },\n  {\n    title: \"Connect your tools\",\n    description:\n      \"Send sign-ups, messages, and orders to the apps your team already uses.\",\n    visual: <IntegrationsVisual />,\n  },\n  {\n    title: \"Ready for every screen\",\n    description:\n      \"Pages adjust from phones to wide monitors, with no separate mobile version to keep up.\",\n    visual: <ResponsiveVisual />,\n  },\n];\n\nfunction FeatureCard({\n  feature,\n  className,\n}: {\n  feature: MockupFeature;\n  className?: string;\n}) {\n  return (\n    <div\n      className={cn(\n        \"group flex flex-col overflow-hidden rounded-xl border bg-muted/40\",\n        className,\n      )}\n    >\n      <div aria-hidden=\"true\" className=\"relative h-52 overflow-hidden\">\n        {feature.visual}\n      </div>\n      <div className=\"px-6 pb-6\">\n        <h3 className=\"font-medium text-foreground text-lg tracking-tight\">\n          {feature.title}\n        </h3>\n        <p className=\"mt-2 text-muted-foreground text-sm leading-relaxed\">\n          {feature.description}\n        </p>\n      </div>\n    </div>\n  );\n}\n\nexport function FeatureMockupGrid({\n  label = \"Features\",\n  heading = \"One place to design, launch, and grow your website\",\n  features = DEFAULT_FEATURES,\n  className,\n}: FeatureMockupGridProps) {\n  return (\n    <section className={cn(\"w-full px-6 py-24 sm:py-32\", className)}>\n      <div className=\"mx-auto flex max-w-5xl flex-col items-center\">\n        <p className=\"flex items-center gap-2 font-semibold text-primary text-xs uppercase tracking-widest\">\n          <span aria-hidden=\"true\" className=\"h-4 w-px bg-primary\" />\n          {label}\n        </p>\n        <h2 className=\"mt-4 max-w-xl text-balance text-center font-semibold text-3xl text-foreground tracking-tight sm:text-4xl\">\n          {heading}\n        </h2>\n\n        <div className=\"mt-12 grid w-full gap-4 md:grid-cols-6\">\n          {features.map((feature, index) => (\n            <FeatureCard\n              className={index < 2 ? \"md:col-span-3\" : \"md:col-span-2\"}\n              feature={feature}\n              key={feature.title}\n            />\n          ))}\n        </div>\n      </div>\n    </section>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/feature-mockup-grid.tsx"
    }
  ],
  "type": "registry:block",
  "author": "Cortexcn (https://ui.cortexcn.dev)",
  "meta": {
    "license": "MIT",
    "revision": "36f4edb4b416"
  }
}