{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "promo-pill",
  "title": "Promo Pill",
  "description": "Translucent offer marker with a low-frequency glow pulse and optional dismiss.",
  "dependencies": [
    "framer-motion"
  ],
  "files": [
    {
      "path": "components/conversion/PromoPill.tsx",
      "content": "'use client';\n\nimport { AnimatePresence, motion } from 'framer-motion';\nimport { useState, type ReactNode } from 'react';\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n//\n// label         Pill copy\n// icon          Optional leading mark (node or emoji string)\n// tone          Color mood: neutral | accent | success\n// href          Optional link — renders as <a>\n// pulse         Soft luminous pulse behind the pill\n// dismissible   Show a quiet dismiss control\n// onDismiss     Called after dismiss animation\n// className     Styles for the outer shell\n//\ntype PromoPillProps = {\n  label?: string;\n  icon?: ReactNode;\n  tone?: 'neutral' | 'accent' | 'success';\n  href?: string;\n  pulse?: boolean;\n  dismissible?: boolean;\n  onDismiss?: () => void;\n  className?: string;\n};\n\nconst TONE = {\n  neutral: {\n    shell: 'border-[color:var(--color-border,#e5e7eb)] bg-[var(--color-background,#fff)] text-[color:var(--color-foreground,#1f2937)]',\n    dot: 'bg-[var(--color-muted-foreground,#9ca3af)]',\n    glow: 'rgba(0,0,0,0.06)',\n  },\n  accent: {\n    shell: 'border-rose-200/70 bg-rose-50/70 text-rose-900',\n    dot: 'bg-rose-400',\n    glow: 'rgba(251,113,133,0.28)',\n  },\n  success: {\n    shell: 'border-emerald-200/70 bg-emerald-50/70 text-emerald-900',\n    dot: 'bg-emerald-400',\n    glow: 'rgba(52,211,153,0.28)',\n  },\n};\n\nexport default function PromoPill({\n  label = 'Early access — 20% off',\n  icon,\n  tone = 'accent',\n  href,\n  pulse = true,\n  dismissible = false,\n  onDismiss,\n  className = '',\n}: PromoPillProps) {\n  const [visible, setVisible] = useState(true);\n  const t = TONE[tone];\n\n  const inner = (\n    <>\n      {pulse ? (\n        <motion.span\n          className='pointer-events-none absolute inset-0 -z-10 rounded-full'\n          style={{ background: t.glow }}\n          animate={{ opacity: [0.35, 0.7, 0.35], scale: [1, 1.04, 1] }}\n          transition={{ duration: 2.8, repeat: Infinity, ease: 'easeInOut' }}\n          aria-hidden\n        />\n      ) : null}\n\n      <span\n        className={['h-1.5 w-1.5 shrink-0 rounded-full', t.dot].join(' ')}\n        aria-hidden\n      />\n\n      {icon ? (\n        <span className='shrink-0 text-[13px] leading-none' aria-hidden>\n          {icon}\n        </span>\n      ) : null}\n\n      <span className='text-xs font-medium tracking-wide'>{label}</span>\n\n      {href ? (\n        <motion.span\n          className='text-xs opacity-60'\n          aria-hidden\n          whileHover={{ x: 2 }}\n        >\n          →\n        </motion.span>\n      ) : null}\n    </>\n  );\n\n  const sharedClass = [\n    'group relative inline-flex items-center gap-2 rounded-full border px-3.5 py-1.5 shadow-[0_1px_2px_rgba(0,0,0,0.04)] backdrop-blur-md transition-colors',\n    t.shell,\n    className,\n  ]\n    .filter(Boolean)\n    .join(' ');\n\n  return (\n    <AnimatePresence>\n      {visible ? (\n        <motion.div\n          className='inline-flex items-center gap-1.5'\n          initial={{ opacity: 0, y: 6, filter: 'blur(4px)' }}\n          animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}\n          exit={{ opacity: 0, scale: 0.96, filter: 'blur(4px)' }}\n          transition={{ duration: 0.4, ease: [0.22, 1, 0.36, 1] }}\n        >\n          {href ? (\n            <a href={href} className={sharedClass}>\n              {inner}\n            </a>\n          ) : (\n            <div className={sharedClass}>{inner}</div>\n          )}\n\n          {dismissible ? (\n            <button\n              type='button'\n              aria-label='Dismiss'\n              onClick={() => {\n                setVisible(false);\n                window.setTimeout(() => onDismiss?.(), 280);\n              }}\n              className='inline-flex h-6 w-6 items-center justify-center rounded-full text-[color:var(--color-muted-foreground,#9ca3af)] transition-colors hover:bg-[var(--color-muted,#f3f4f6)] hover:text-[color:var(--color-foreground,#374151)]'\n            >\n              <svg viewBox='0 0 12 12' className='h-3 w-3' aria-hidden>\n                <path\n                  d='M3 3l6 6M9 3L3 9'\n                  stroke='currentColor'\n                  strokeWidth='1.4'\n                  strokeLinecap='round'\n                />\n              </svg>\n            </button>\n          ) : null}\n        </motion.div>\n      ) : null}\n    </AnimatePresence>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/conversion/PromoPill.tsx"
    }
  ],
  "categories": [
    "conversion"
  ],
  "type": "registry:component"
}