{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "feature-studio-grid-01",
  "title": "Feature Studio Grid",
  "description": "Heading and one action over two compact cards and a large feature tile, for posts, case studies, or launches",
  "dependencies": [],
  "registryDependencies": [
    "https://ui.cortexcn.dev/r/utils.json",
    "button"
  ],
  "files": [
    {
      "path": "registry/blocks/feature-studio-grid-01/components/feature-studio-grid.tsx",
      "content": "import { buttonVariants } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\n// Cortexcn item \"feature-studio-grid-01\", revision 6f5d56b972cf, MIT license.\n// https://ui.cortexcn.dev/r/v/feature-studio-grid-01/6f5d56b972cf.json\n\ninterface StudioAction {\n  label: string;\n  href: string;\n}\n\ninterface StudioImage {\n  src: string;\n  /** Shown in dark mode instead of `src`, for a logo with a light version. */\n  srcDark?: string;\n  alt: string;\n}\n\nexport interface StudioPost {\n  /** Shown as written, for example \"September 18, 2026\". */\n  date: string;\n  title: string;\n  description: string;\n  href: string;\n  image: StudioImage;\n}\n\nexport interface StudioFeature {\n  /** Small label in the tile's top corner, such as a brand or series name. */\n  label: string;\n  title: string;\n  href: string;\n  image: StudioImage;\n}\n\nexport interface FeatureStudioGridProps {\n  heading?: string;\n  action?: StudioAction;\n  /** Two compact cards sit beside the feature tile on large screens. */\n  posts?: StudioPost[];\n  feature?: StudioFeature;\n  className?: string;\n}\n\nconst CORTEXCN_LOGO: StudioImage = {\n  src: \"https://www.cortexcn.dev/img/cortexcn/icon-light.png\",\n  srcDark: \"https://www.cortexcn.dev/img/cortexcn/icon-dark.png\",\n  alt: \"Cortexcn logo\",\n};\n\n/** Sample photo from Unsplash (free to use under the Unsplash License). */\nfunction unsplash(id: string, width: number) {\n  return `https://images.unsplash.com/photo-${id}?w=${width}&q=80&auto=format&fit=crop`;\n}\n\nconst DEFAULT_POSTS: StudioPost[] = [\n  {\n    date: \"September 18, 2026\",\n    title: \"Motion that knows when to rest\",\n    description:\n      \"How we pace animation so it guides the eye, and keep it still when a visitor asks for reduced motion.\",\n    href: \"#\",\n    image: CORTEXCN_LOGO,\n  },\n  {\n    date: \"August 29, 2026\",\n    title: \"One set of tokens for every theme\",\n    description:\n      \"The shared color tokens that keep charts, cards, and blocks consistent in light and dark mode.\",\n    href: \"#\",\n    image: CORTEXCN_LOGO,\n  },\n];\n\nconst DEFAULT_FEATURE: StudioFeature = {\n  label: \"Studio\",\n  title: \"How a component goes from sketch to registry\",\n  href: \"#\",\n  image: {\n    src: unsplash(\"1581291518857-4e27b48ff24e\", 1400),\n    alt: \"A hand sketching app wireframes on paper\",\n  },\n};\n\nfunction PlusIcon({ className }: { className?: string }) {\n  return (\n    <span\n      aria-hidden=\"true\"\n      className={cn(\n        \"flex size-6 shrink-0 items-center justify-center rounded-full transition-transform duration-300 group-hover:rotate-90\",\n        className,\n      )}\n    >\n      <svg\n        aria-hidden=\"true\"\n        className=\"size-3.5\"\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        <path d=\"M5 12h14M12 5v14\" />\n      </svg>\n    </span>\n  );\n}\n\nfunction ArrowUpRightIcon() {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className=\"size-4\"\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      <path d=\"M7 7h10v10M7 17 17 7\" />\n    </svg>\n  );\n}\n\nconst thumbnailClassName =\n  \"size-24 rounded-xl border bg-muted object-cover lg:size-32\";\n\nfunction Thumbnail({ image }: { image: StudioImage }) {\n  if (!image.srcDark) {\n    return (\n      // biome-ignore lint/performance/noImgElement: framework-agnostic registry block\n      <img\n        alt={image.alt}\n        className={thumbnailClassName}\n        decoding=\"async\"\n        height={128}\n        loading=\"lazy\"\n        src={image.src}\n        width={128}\n      />\n    );\n  }\n\n  return (\n    <>\n      {/* biome-ignore lint/performance/noImgElement: framework-agnostic registry block */}\n      <img\n        alt={image.alt}\n        className={cn(thumbnailClassName, \"dark:hidden\")}\n        decoding=\"async\"\n        height={128}\n        loading=\"lazy\"\n        src={image.src}\n        width={128}\n      />\n      {/* biome-ignore lint/performance/noImgElement: framework-agnostic registry block */}\n      <img\n        alt=\"\"\n        className={cn(thumbnailClassName, \"hidden dark:block\")}\n        decoding=\"async\"\n        height={128}\n        loading=\"lazy\"\n        src={image.srcDark}\n        width={128}\n      />\n    </>\n  );\n}\n\nfunction PostCard({ post }: { post: StudioPost }) {\n  return (\n    <a\n      className=\"group flex h-full flex-col justify-between gap-10 rounded-xl bg-card p-4 text-card-foreground outline-none ring-1 ring-foreground/10 transition-shadow hover:ring-foreground/20 focus-visible:ring-2 focus-visible:ring-ring\"\n      href={post.href}\n    >\n      <div className=\"flex items-start justify-between gap-4\">\n        <Thumbnail image={post.image} />\n        <PlusIcon className=\"bg-secondary text-secondary-foreground\" />\n      </div>\n      <div className=\"flex flex-col gap-3\">\n        <div>\n          <span className=\"text-muted-foreground text-xs\">{post.date}</span>\n          <h3 className=\"mt-1 text-balance font-medium text-xl tracking-tight\">\n            {post.title}\n          </h3>\n        </div>\n        <p className=\"text-muted-foreground text-sm leading-relaxed\">\n          {post.description}\n        </p>\n      </div>\n    </a>\n  );\n}\n\nfunction FeatureTile({ feature }: { feature: StudioFeature }) {\n  return (\n    <a\n      className=\"group relative flex h-full min-h-[30rem] flex-col justify-between overflow-hidden rounded-xl bg-neutral-950 text-white outline-none ring-1 ring-foreground/10 focus-visible:ring-2 focus-visible:ring-ring md:min-h-[32rem]\"\n      href={feature.href}\n    >\n      {/* biome-ignore lint/performance/noImgElement: framework-agnostic registry block */}\n      <img\n        alt={feature.image.alt}\n        className=\"absolute inset-0 size-full object-cover transition-transform duration-500 ease-out group-hover:scale-105\"\n        decoding=\"async\"\n        height={1050}\n        loading=\"lazy\"\n        src={feature.image.src}\n        width={1400}\n      />\n      {/* Keeps the label readable on light photos. */}\n      <div\n        aria-hidden=\"true\"\n        className=\"absolute inset-x-0 top-0 h-32 bg-linear-to-b from-black/50 to-transparent\"\n      />\n\n      <div className=\"relative flex items-center justify-between p-6 md:p-8\">\n        <span className=\"font-bold text-lg tracking-tight\">\n          {feature.label}\n        </span>\n        <PlusIcon className=\"bg-white/15 text-white backdrop-blur-sm\" />\n      </div>\n\n      <div className=\"relative flex justify-end bg-linear-to-t from-black/80 via-black/40 to-transparent p-6 pt-24 text-right md:p-10 md:pt-32\">\n        <h3 className=\"max-w-md text-balance font-semibold text-2xl tracking-tight md:text-3xl lg:text-4xl\">\n          {feature.title}\n        </h3>\n      </div>\n    </a>\n  );\n}\n\nexport function FeatureStudioGrid({\n  heading = \"Stories from the studio.\",\n  action = { label: \"Get in touch\", href: \"#\" },\n  posts = DEFAULT_POSTS,\n  feature = DEFAULT_FEATURE,\n  className,\n}: FeatureStudioGridProps) {\n  return (\n    <section\n      className={cn(\n        \"bg-muted px-6 py-24 sm:py-32 dark:bg-background\",\n        className,\n      )}\n    >\n      <div className=\"mx-auto flex max-w-6xl flex-col gap-12\">\n        <div className=\"flex flex-col justify-between gap-6 md:flex-row md:items-end md:gap-8\">\n          <h2 className=\"max-w-lg text-balance font-bold text-3xl tracking-tight md:text-5xl\">\n            {heading}\n          </h2>\n          <a\n            className={cn(buttonVariants(), \"gap-1.5 self-start md:self-auto\")}\n            href={action.href}\n          >\n            {action.label}\n            <ArrowUpRightIcon />\n          </a>\n        </div>\n\n        <ul className=\"grid gap-2 sm:grid-cols-2 lg:grid-cols-4\">\n          {posts.map((post) => (\n            <li key={post.title}>\n              <PostCard post={post} />\n            </li>\n          ))}\n          <li className=\"sm:col-span-2\">\n            <FeatureTile feature={feature} />\n          </li>\n        </ul>\n      </div>\n    </section>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/feature-studio-grid.tsx"
    }
  ],
  "type": "registry:block",
  "author": "Cortexcn (https://ui.cortexcn.dev)",
  "meta": {
    "license": "MIT",
    "revision": "6f5d56b972cf"
  }
}