{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "contextual-cta",
  "title": "Contextual CTA",
  "description": "Sticky scroll CTA with editable CONTENT (no analytics wiring).",
  "dependencies": [
    "lucide-react",
    "next"
  ],
  "files": [
    {
      "path": "registry/default/blocks/contextual-cta.tsx",
      "content": "\"use client\"\n\nimport { ArrowRight, X } from \"lucide-react\"\nimport Link from \"next/link\"\nimport { usePathname } from \"next/navigation\"\nimport { useEffect, useState } from \"react\"\n\n/**\n * Edit CONTENT to change the sticky scroll CTA copy and thresholds.\n * Simplified vs the docs package demo (no analytics experiment wiring).\n */\nconst CONTENT = {\n  stamp: \"Free intro\",\n  headline: \"Ready to ship?\",\n  body: \"Book a 15-min intro - no pitch deck.\",\n  cta: \"Book a call\",\n  href: \"/contact#book\",\n  hideOn: [\"/contact\"],\n  showAtPct: 28,\n  hideAtPct: 94,\n  dismissKey: \"atroui_cta_dismissed\",\n}\n\nexport type ContextualCtaProps = {\n  /** Docs / demos: skip scroll gating and render inline. */\n  preview?: boolean\n}\n\nexport function ContextualCta({ preview = false }: ContextualCtaProps) {\n  const pathname = usePathname()\n  const [visible, setVisible] = useState(preview)\n  const [dismissed, setDismissed] = useState(false)\n  const [ready, setReady] = useState(preview)\n\n  useEffect(() => {\n    if (preview) return\n    try {\n      if (sessionStorage.getItem(CONTENT.dismissKey) === \"1\") {\n        setDismissed(true)\n      }\n    } catch {\n      /* ignore */\n    }\n    setReady(true)\n  }, [preview])\n\n  useEffect(() => {\n    if (preview || !ready || dismissed) return\n\n    const onScroll = () => {\n      const denom =\n        document.documentElement.scrollHeight - window.innerHeight\n      if (denom <= 0) {\n        setVisible(false)\n        return\n      }\n      const pct = (window.scrollY / denom) * 100\n      setVisible(pct >= CONTENT.showAtPct && pct < CONTENT.hideAtPct)\n    }\n\n    onScroll()\n    window.addEventListener(\"scroll\", onScroll, { passive: true })\n    return () => window.removeEventListener(\"scroll\", onScroll)\n  }, [dismissed, ready, pathname, preview])\n\n  const onHiddenPath =\n    !preview &&\n    CONTENT.hideOn.some(\n      (p) => pathname === p || pathname.startsWith(`${p}/`),\n    )\n\n  if (!ready || dismissed || !visible || onHiddenPath) return null\n\n  const dismiss = () => {\n    setDismissed(true)\n    if (preview) return\n    try {\n      sessionStorage.setItem(CONTENT.dismissKey, \"1\")\n    } catch {\n      /* ignore */\n    }\n  }\n\n  return (\n    <div\n      className={\n        preview\n          ? \"relative w-full pb-3\"\n          : \"fixed inset-x-0 bottom-0 z-40 pb-[max(0.75rem,env(safe-area-inset-bottom))]\"\n      }\n      role=\"complementary\"\n      aria-label=\"Suggested action\"\n    >\n      <div className=\"mx-auto max-w-lg px-[max(0.75rem,env(safe-area-inset-left))] pr-[max(0.75rem,env(safe-area-inset-right))] sm:max-w-xl sm:px-4\">\n        <div className=\"flex items-stretch border border-border-subtle bg-background shadow-[0_16px_48px_-24px_rgba(0,0,0,0.35)]\">\n          <div\n            aria-hidden\n            className=\"w-1 shrink-0 bg-[var(--color-brand,#0b7bff)]\"\n          />\n          <div className=\"flex min-w-0 flex-1 flex-col gap-3 p-3.5 sm:flex-row sm:items-center sm:gap-4 sm:p-4\">\n            <div className=\"min-w-0 flex-1\">\n              <p className=\"mb-1.5 text-[11px] font-semibold tracking-[0.14em] text-muted-foreground uppercase\">\n                {CONTENT.stamp}\n              </p>\n              <p className=\"text-sm font-medium tracking-tight text-foreground\">\n                {CONTENT.headline}\n              </p>\n              <p className=\"mt-0.5 text-xs text-muted-foreground\">\n                {CONTENT.body}\n              </p>\n            </div>\n            <div className=\"flex items-center gap-2\">\n              <Link\n                href={CONTENT.href}\n                className=\"inline-flex h-9 flex-1 items-center justify-center gap-1.5 rounded-lg bg-foreground px-3.5 text-sm font-medium text-background sm:flex-none\"\n              >\n                {CONTENT.cta}\n                <ArrowRight className=\"size-3.5\" aria-hidden />\n              </Link>\n              <button\n                type=\"button\"\n                onClick={dismiss}\n                aria-label=\"Dismiss suggestion\"\n                className=\"inline-flex size-9 shrink-0 items-center justify-center border border-border-subtle text-muted-foreground hover:bg-muted hover:text-foreground\"\n              >\n                <X className=\"size-4\" aria-hidden />\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/blocks/contextual-cta.tsx"
    }
  ],
  "type": "registry:block"
}