{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cta-band",
  "type": "registry:component",
  "title": "Cta band",
  "description": "A closing CTA section with a large lit headline and an inline email form whose button animates from idle to sending to a success check. Async onSubmit.",
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/blocks/cta-band.tsx",
      "type": "registry:ui",
      "target": "components/ui/cta-band.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { AnimatePresence, motion } from \"motion/react\"\nimport { ArrowRightIcon, CheckIcon } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\n\n/*\n * CtaBand: the last ask. A big lit panel with a headline that catches the\n * light, and an email field whose button morphs into a confirmation.\n */\n\ntype CtaBandProps = {\n  title?: React.ReactNode\n  description?: React.ReactNode\n  placeholder?: string\n  button?: string\n  success?: string\n  onSubmit?: (email: string) => void | Promise<void>\n  note?: React.ReactNode\n  className?: string\n}\n\nfunction CtaBand({\n  title = (\n    <>\n      Know what changed\n      <br />\n      <span className=\"text-lit\">before your coffee.</span>\n    </>\n  ),\n  description = \"Connect a source and get your first digest tomorrow morning. Free for 14 days.\",\n  placeholder = \"you@company.com\",\n  button = \"Start free\",\n  success = \"Check your inbox\",\n  onSubmit,\n  note = \"No card. Cancel in one click.\",\n  className,\n}: CtaBandProps) {\n  const [email, setEmail] = React.useState(\"\")\n  const [state, setState] = React.useState<\"idle\" | \"busy\" | \"done\">(\"idle\")\n  const valid = /.+@.+\\..+/.test(email)\n  return (\n    <section data-slot=\"cta-band\" className={cn(\"mx-auto max-w-7xl px-4 py-24 sm:px-6 lg:px-8\", className)}>\n      <div className=\"relative isolate overflow-hidden rounded-[32px] border border-border bg-surface px-6 py-16 text-center shadow-raised sm:px-16 sm:py-24\">\n        <div aria-hidden className=\"light-spot absolute inset-0 -z-10\" />\n        <div aria-hidden className=\"absolute inset-0 -z-10 bg-grid opacity-70 [--grid-size:40px] [mask-image:radial-gradient(ellipse_60%_70%_at_50%_50%,black,transparent)]\" />\n        <div aria-hidden className=\"absolute -bottom-40 left-1/2 -z-10 size-[520px] -translate-x-1/2 rounded-full bg-accent opacity-20 blur-[120px]\" />\n        <h2 className=\"mx-auto max-w-3xl text-4xl leading-[1.02] font-medium tracking-[-0.045em] text-balance text-fg sm:text-6xl\">{title}</h2>\n        {description ? <p className=\"mx-auto mt-5 max-w-xl text-[1.0625rem] leading-[1.6] text-fg-muted\">{description}</p> : null}\n        <form\n          className=\"mx-auto mt-9 flex max-w-md items-center gap-1.5 rounded-2xl border border-border bg-raised p-1.5 shadow-key focus-within:border-accent focus-within:shadow-[0_0_0_4px_var(--accent-soft)]\"\n          onSubmit={async (e) => {\n            e.preventDefault()\n            if (!valid || state !== \"idle\") return\n            setState(\"busy\")\n            try {\n              await (onSubmit?.(email) ?? new Promise((r) => setTimeout(r, 700)))\n              setState(\"done\")\n            } catch {\n              setState(\"idle\")\n            }\n          }}\n        >\n          <input\n            type=\"email\"\n            required\n            value={email}\n            disabled={state === \"done\"}\n            onChange={(e) => setEmail(e.target.value)}\n            placeholder={placeholder}\n            aria-label=\"Email\"\n            className=\"h-11 min-w-0 flex-1 bg-transparent px-3 text-[15px] text-fg outline-none placeholder:text-fg-subtle\"\n          />\n          <motion.button\n            layout\n            type=\"submit\"\n            disabled={state !== \"idle\"}\n            transition={{ type: \"spring\", bounce: 0.16, duration: 0.5 }}\n            className={cn(\n              \"inline-flex h-11 shrink-0 items-center gap-2 overflow-hidden rounded-xl px-5 text-[14px] font-medium whitespace-nowrap outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-raised\",\n              state === \"done\" ? \"bg-success text-white\" : \"bg-ink text-on-ink shadow-ink hover:bg-ink-hover\"\n            )}\n          >\n            <AnimatePresence mode=\"popLayout\" initial={false}>\n              <motion.span\n                key={state}\n                initial={{ opacity: 0, y: 10, filter: \"blur(4px)\" }}\n                animate={{ opacity: 1, y: 0, filter: \"blur(0px)\" }}\n                exit={{ opacity: 0, y: -10, filter: \"blur(4px)\" }}\n                transition={{ duration: 0.2 }}\n                className=\"inline-flex items-center gap-2\"\n              >\n                {state === \"done\" ? <CheckIcon className=\"size-4\" strokeWidth={2.5} /> : null}\n                {state === \"done\" ? success : state === \"busy\" ? \"Sending…\" : button}\n                {state === \"idle\" ? <ArrowRightIcon className=\"size-4\" /> : null}\n              </motion.span>\n            </AnimatePresence>\n          </motion.button>\n        </form>\n        {note ? <p className=\"mt-4 text-[12.5px] text-fg-subtle\">{note}</p> : null}\n      </div>\n    </section>\n  )\n}\n\nexport { CtaBand }\nexport type { CtaBandProps }\n"
    }
  ]
}
