{
  "name": "upgrade-modal-default",
  "type": "registry:block",
  "tier": "free",
  "dependencies": [],
  "registryDependencies": [
    "https://www.craftui.space/r/craftui-base.json"
  ],
  "files": [
    {
      "path": "components/craftui/designs/upgrade-modal/default.tsx",
      "content": "// Static by design — no \"use client\", no hooks, no motion. The dialog is drawn\n// in its open state as a still picture of an upsell, not a working modal: the\n// close button and both actions are inert so the piece ships zero JS.\n//\n// Built on the \"Nova\" token set (shadcn Stone base, Blue primary, radius 0).\n// Tailwind only — the tokens are stated once below as class strings so every\n// colour, gap, size and type value on the card reads from the same source; the\n// Figma and Paper originals bind the identical variables. Values are literal\n// (not built from constants) because Tailwind scans source text.\n\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst NOVA = {\n  fg: \"text-[#0C0A09]\",\n  mutedFg: \"text-[#79716B]\",\n  card: \"bg-[#FFFFFF]\",\n  primary: \"bg-[#1447E6]\",\n  primaryFg: \"text-[#EFF6FF]\",\n  primaryFgBg: \"bg-[#EFF6FF]\",\n  border: \"border-[#E7E5E4]\",\n  // Card outline — 1px ring at foreground/10 rather than a border, plus the\n  // dialog's elevation.\n  cardRing: \"shadow-[0_0_0_1px_rgb(12_10_9/0.10),0_24px_48px_-12px_rgb(12_10_9/0.18)]\",\n  // Type pairs: size with its line-height, never set separately.\n  text2xl: \"text-[24px] leading-[32px] tracking-[-0.35px]\",\n  textBase: \"text-[16px] leading-[24px]\",\n  textSm: \"text-[14px] leading-[20px]\",\n  textXs: \"text-[12px] leading-[16px]\",\n} as const;\n\n// The dialog is laid out in fixed pixels — the composition is drawn against a\n// 496px card and does not reflow — so it is scaled to the caller's width by a\n// viewBox rather than by rewriting its measurements (the same device as the\n// Notes Card). The stage is the card plus a 48px margin wide enough to hold\n// the drop shadow, since nothing may paint outside the viewBox.\nconst STAGE = { width: 592, height: 718 } as const;\n\ninterface Feature {\n  title: string;\n  description: string;\n}\n\ninterface UpgradeModalProps {\n  title?: string;\n  description?: ReactNode;\n  /** Pill beside the title. Pass \"\" to drop it. */\n  badge?: string;\n  features?: Feature[];\n  ctaLabel?: string;\n  dismissLabel?: string;\n  className?: string;\n}\n\nfunction CloseMark() {\n  return (\n    <span\n      aria-hidden=\"true\"\n      className={cn(\n        \"absolute top-4 right-4 flex size-7 items-center justify-center rounded-none border\",\n        NOVA.card,\n        NOVA.border,\n      )}\n    >\n      <svg\n        width=\"8\"\n        height=\"8\"\n        viewBox=\"0 0 12 12\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        className={NOVA.fg}\n      >\n        <path d=\"M2 2 10 10M10 2 2 10\" />\n      </svg>\n    </span>\n  );\n}\n\nfunction Check() {\n  return (\n    <span\n      aria-hidden=\"true\"\n      className={cn(\n        \"flex size-6 shrink-0 items-center justify-center rounded-none\",\n        NOVA.primaryFgBg,\n      )}\n    >\n      <svg\n        width=\"12\"\n        height=\"8\"\n        viewBox=\"0 0 14 11\"\n        fill=\"none\"\n        stroke=\"#1447E6\"\n        strokeWidth={2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      >\n        <path d=\"M1 6.5 5 10.5 13 2.5\" />\n      </svg>\n    </span>\n  );\n}\n\nfunction Dialog({\n  title = \"Upgrade to Paper Pro\",\n  description = \"Unlock unlimited files, shared libraries, and hand-off tools built for teams that ship.\",\n  badge = \"2 months free\",\n  features = [\n    {\n      title: \"Unlimited files and pages\",\n      description: \"No caps on projects, artboards, or version history\",\n    },\n    {\n      title: \"Shared design tokens\",\n      description: \"Publish one token set and sync it across every file\",\n    },\n    {\n      title: \"Developer hand-off\",\n      description: \"Export clean JSX and CSS with exact values, no guesswork\",\n    },\n  ],\n  ctaLabel = \"Upgrade now · $12/mo\",\n  dismissLabel = \"Maybe later\",\n}: Omit<UpgradeModalProps, \"className\">) {\n  return (\n    <article\n      role=\"dialog\"\n      aria-label={title}\n      className={cn(\n        \"flex w-[496px] shrink-0 flex-col overflow-hidden rounded-none text-left font-sans\",\n        NOVA.card,\n        NOVA.fg,\n        NOVA.cardRing,\n      )}\n    >\n      {/* Hero — the one surface that is not a token: the mesh shader from the\n          Paper original flattened to a linear ramp, indigo at the bottom-left\n          to violet at the top-right. */}\n      <div className=\"relative h-[200px] shrink-0 bg-[linear-gradient(60deg,#241D9A_0%,#3A2FB0_45%,#9F50D3_100%)]\">\n        <CloseMark />\n      </div>\n\n      <div className=\"flex flex-col gap-6 px-5 pt-6 pb-5\">\n        <header className=\"flex flex-col gap-1\">\n          <div className=\"flex items-center justify-between gap-4\">\n            <h2 className={cn(\"font-medium\", NOVA.text2xl)}>{title}</h2>\n            {badge && (\n              <span\n                className={cn(\n                  \"flex h-5 shrink-0 items-center rounded-none px-2 font-medium whitespace-nowrap\",\n                  NOVA.primary,\n                  NOVA.primaryFg,\n                  NOVA.textXs,\n                )}\n              >\n                {badge}\n              </span>\n            )}\n          </div>\n          <p className={cn(NOVA.textBase, NOVA.mutedFg)}>{description}</p>\n        </header>\n\n        <ul className=\"flex flex-col gap-4\">\n          {features.map((feature) => (\n            <li key={feature.title} className=\"flex items-start gap-3\">\n              <Check />\n              <div className={cn(\"flex min-w-0 flex-1 flex-col gap-0.5\", NOVA.textSm)}>\n                <span className=\"font-medium\">{feature.title}</span>\n                <span className={NOVA.mutedFg}>{feature.description}</span>\n              </div>\n            </li>\n          ))}\n        </ul>\n\n        <div className=\"flex flex-col items-center gap-3 pt-2\">\n          <span\n            className={cn(\n              \"flex h-9 w-full items-center justify-center rounded-none px-2.5 font-medium whitespace-nowrap\",\n              NOVA.primary,\n              NOVA.primaryFg,\n              NOVA.textSm,\n            )}\n          >\n            {ctaLabel}\n          </span>\n          <span\n            className={cn(\n              \"flex h-8 items-center justify-center px-2.5 font-medium whitespace-nowrap\",\n              NOVA.mutedFg,\n              NOVA.textSm,\n            )}\n          >\n            {dismissLabel}\n          </span>\n        </div>\n      </div>\n    </article>\n  );\n}\n\nexport default function UpgradeModal({ className, ...props }: UpgradeModalProps) {\n  return (\n    <svg\n      viewBox={`0 0 ${STAGE.width} ${STAGE.height}`}\n      width={STAGE.width}\n      height={STAGE.height}\n      // 592px is STAGE.width spelled out: Tailwind scans source text, so a\n      // class built from the constant would never be generated.\n      className={cn(\"h-auto w-full max-w-[592px]\", className)}\n    >\n      <foreignObject width={STAGE.width} height={STAGE.height}>\n        <div className=\"flex size-full items-start justify-center p-12\">\n          <Dialog {...props} />\n        </div>\n      </foreignObject>\n    </svg>\n  );\n}\n",
      "type": "registry:component",
      "target": "~/components/craftui/designs/upgrade-modal/default.tsx"
    }
  ],
  "meta": {
    "displayName": "Upgrade Modal - Upgrade Modal",
    "description": "An upsell dialog in its open state on the shadcn Nova preset (Stone, Blue, radius 0). Gradient hero with a square close mark, a title-and-badge row held apart by space-between, three check-tiled feature rows in a fixed lane, and a full-width primary action with a ghost dismiss. Every value on the card reads from one token object. Static — no hooks, no motion, zero JavaScript.",
    "category": "card",
    "tags": [
      "design",
      "static",
      "modal",
      "dialog",
      "upsell",
      "pricing",
      "gradient",
      "shadcn",
      "nova",
      "tokens"
    ],
    "animationType": "none"
  }
}