{"version":1,"id":"pricing-card","framework":"next","kind":"block","files":[{"path":"src/blocks/gfa/pricing-card.tsx","content":"\"use client\";\n\nimport { useId, useState, type ReactNode } from \"react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { LiquidMetal } from \"@paper-design/shaders-react\";\nimport { CheckIcon, InfoIcon, XIcon } from \"lucide-react\";\nimport { SPRING_LAYOUT, SPRING_SWAP } from \"@/lib/ease\";\nimport { cn } from \"@/lib/utils\";\nimport type { Transition } from \"motion/react\";\n\nconst FEATURES = [\n  \"Unlimited projects and collaborators\",\n  \"Priority generation and faster exports\",\n  \"Advanced motion presets and shaders\",\n  \"Private components and team libraries\",\n  \"Early access to new GFA blocks\",\n] as const;\n\nexport type Plan = \"monthly\" | \"yearly\";\n\nconst PLAN_COPY: Record<Plan, { price: string; cadence: string }> = {\n  monthly: { price: \"$12.99\", cadence: \"month\" },\n  yearly: { price: \"$9.99\", cadence: \"month, billed annually\" },\n};\n\n/** Near-black primary + orange secondary (Apple-adjacent). */\nconst INK = \"#0a0a0a\";\nconst ORANGE = \"#ff9f0a\";\n\nconst ENTER_BOUNCE: Transition = {\n  type: \"spring\",\n  duration: 0.85,\n  bounce: 0.42,\n};\n\nfunction LiquidShell({\n  children,\n  className,\n  radiusClass = \"rounded-[30px]\",\n  insetClass = \"m-[2.5px] rounded-[27.5px]\",\n}: {\n  children: ReactNode;\n  className?: string;\n  radiusClass?: string;\n  insetClass?: string;\n}) {\n  return (\n    <div className={cn(\"relative\", radiusClass, className)}>\n      <div\n        className={cn(\n          \"pointer-events-none absolute inset-0 overflow-hidden\",\n          radiusClass,\n        )}\n        aria-hidden\n      >\n        <LiquidMetal\n          className=\"absolute inset-0 size-full\"\n          shape=\"none\"\n          speed={0.32}\n          repetition={5}\n          softness={0.55}\n          shiftRed={0.45}\n          shiftBlue={0.05}\n          distortion={0}\n          contour={0}\n          angle={38}\n          scale={7}\n          offsetX={0.08}\n          offsetY={-0.12}\n          colorBack={INK}\n          colorTint={ORANGE}\n        />\n      </div>\n      <div className={cn(\"relative z-[1] bg-white\", insetClass)}>{children}</div>\n    </div>\n  );\n}\n\nexport type PricingCardProps = {\n  productName?: string;\n  className?: string;\n  onUpgrade?: (plan: Plan) => void;\n  onClose?: () => void;\n};\n\nexport function PricingCard({\n  productName = \"Pro+\",\n  className,\n  onUpgrade,\n  onClose,\n}: PricingCardProps) {\n  const [plan, setPlan] = useState<Plan>(\"yearly\");\n  const reduce = useReducedMotion();\n  const pillId = useId();\n  const copy = PLAN_COPY[plan];\n\n  return (\n    <motion.div\n      className=\"w-full max-w-[380px]\"\n      initial={\n        reduce\n          ? false\n          : { opacity: 0, scale: 0.86, y: 28, filter: \"blur(8px)\" }\n      }\n      animate={{ opacity: 1, scale: 1, y: 0, filter: \"blur(0px)\" }}\n      transition={reduce ? { duration: 0 } : ENTER_BOUNCE}\n    >\n      <LiquidShell\n        className={cn(\n          \"w-full\",\n          \"shadow-[0_22px_70px_rgba(0,0,0,0.18)]\",\n          \"font-[system-ui,-apple-system,BlinkMacSystemFont,'SF_Pro_Display','SF_Pro_Text',sans-serif]\",\n          className,\n        )}\n        radiusClass=\"rounded-[30px]\"\n        insetClass=\"m-[2.5px] overflow-hidden rounded-[27.5px]\"\n      >\n      <div className=\"relative bg-white text-[#1d1d1f]\" data-slot=\"pricing-card\">\n        {onClose ? (\n          <button\n            type=\"button\"\n            onClick={onClose}\n            aria-label=\"Close\"\n            className=\"absolute right-4 top-4 z-10 flex size-7 items-center justify-center rounded-full bg-[#f5f5f7] text-[#6e6e73] transition hover:bg-[#e8e8ed] hover:text-[#1d1d1f]\"\n          >\n            <XIcon className=\"size-3.5\" strokeWidth={2.5} />\n          </button>\n        ) : null}\n\n        <div\n          className=\"pointer-events-none absolute inset-x-0 top-0 h-40 bg-[radial-gradient(110%_90%_at_50%_-15%,rgba(255,159,10,0.18)_0%,#f5f5f7_52%,#ffffff_100%)]\"\n          aria-hidden\n        />\n\n        <div className=\"relative px-7 pb-7 pt-10\">\n          <h2 className=\"text-center text-[28px] font-semibold leading-tight tracking-[-0.03em]\">\n            Upgrade to {productName}\n          </h2>\n          <p className=\"mx-auto mt-2 max-w-[16.5rem] text-center text-[15px] leading-snug text-[#6e6e73]\">\n            Unlock the full toolkit — built for teams that ship every day.\n          </p>\n\n          <div\n            className=\"relative mt-6 grid grid-cols-2 gap-0.5 rounded-full bg-[#f5f5f7] p-0.5\"\n            role=\"tablist\"\n            aria-label=\"Billing period\"\n          >\n            {(\n              [\n                { id: \"monthly\" as const, label: \"Monthly\" },\n                { id: \"yearly\" as const, label: \"Yearly\" },\n              ] as const\n            ).map((option) => {\n              const active = plan === option.id;\n              return (\n                <button\n                  key={option.id}\n                  type=\"button\"\n                  role=\"tab\"\n                  aria-selected={active}\n                  onClick={() => setPlan(option.id)}\n                  className={cn(\n                    \"relative z-10 flex items-center justify-center gap-1.5 rounded-full px-3 py-2 text-[13px] font-medium transition-colors duration-200\",\n                    active\n                      ? \"text-[#1d1d1f]\"\n                      : \"text-[#6e6e73] hover:text-[#1d1d1f]\",\n                  )}\n                >\n                  {active ? (\n                    <motion.span\n                      layoutId={pillId}\n                      className=\"absolute inset-0 -z-10 rounded-full bg-white shadow-[0_1px_3px_rgba(0,0,0,0.08),0_0_0_0.5px_rgba(0,0,0,0.04)]\"\n                      transition={reduce ? { duration: 0 } : SPRING_LAYOUT}\n                    />\n                  ) : null}\n                  <span className=\"relative\">{option.label}</span>\n                  {option.id === \"yearly\" ? (\n                    <motion.span\n                      className=\"relative text-[11px] font-semibold\"\n                      style={{ color: ORANGE }}\n                      initial={false}\n                      animate={{\n                        opacity: active ? 1 : 0.55,\n                        scale: active ? 1 : 0.96,\n                      }}\n                      transition={reduce ? { duration: 0 } : SPRING_SWAP}\n                    >\n                      Save 23%\n                    </motion.span>\n                  ) : null}\n                </button>\n              );\n            })}\n          </div>\n\n          <ul className=\"mt-6 space-y-3.5\">\n            {FEATURES.map((feature) => (\n              <li key={feature} className=\"flex items-start gap-3\">\n                <span\n                  className=\"mt-0.5 flex size-[18px] shrink-0 items-center justify-center rounded-full\"\n                  style={{ backgroundColor: INK }}\n                >\n                  <CheckIcon\n                    className=\"size-2.5 text-white\"\n                    strokeWidth={3}\n                  />\n                </span>\n                <span className=\"text-[14px] leading-snug tracking-[-0.01em] text-[#1d1d1f]\">\n                  {feature}\n                </span>\n              </li>\n            ))}\n          </ul>\n\n          <div className=\"mt-7 flex h-[3.25rem] flex-col items-center justify-center text-center\">\n            <AnimatePresence mode=\"wait\" initial={false}>\n              <motion.p\n                key={plan}\n                className=\"text-[13px] text-[#6e6e73]\"\n                initial={\n                  reduce ? false : { opacity: 0, y: 8, filter: \"blur(4px)\" }\n                }\n                animate={{ opacity: 1, y: 0, filter: \"blur(0px)\" }}\n                exit={\n                  reduce\n                    ? undefined\n                    : { opacity: 0, y: -8, filter: \"blur(4px)\" }\n                }\n                transition={reduce ? { duration: 0 } : SPRING_SWAP}\n              >\n                <span className=\"text-[22px] font-semibold tracking-[-0.02em] text-[#1d1d1f]\">\n                  {copy.price}\n                </span>\n                <span className=\"ml-1\">/ {copy.cadence}</span>\n              </motion.p>\n            </AnimatePresence>\n          </div>\n\n          <button\n            type=\"button\"\n            onClick={() => onUpgrade?.(plan)}\n            className=\"mt-1 flex h-12 w-full items-center justify-center rounded-full text-[17px] font-medium tracking-[-0.01em] text-white transition active:scale-[0.985] hover:brightness-110\"\n            style={{ backgroundColor: INK }}\n          >\n            Upgrade to {productName}\n          </button>\n\n          <p className=\"mt-4 flex items-start justify-center gap-1.5 text-center text-[11px] leading-relaxed text-[#86868b]\">\n            <InfoIcon\n              className=\"mt-px size-3.5 shrink-0 opacity-80\"\n              strokeWidth={2}\n              aria-hidden\n            />\n            <span>\n              Plan renews automatically until canceled. Cancel anytime in\n              Settings.\n            </span>\n          </p>\n        </div>\n      </div>\n    </LiquidShell>\n    </motion.div>\n  );\n}\n"}],"meta":{"studioName":"GFA Development & Design Studio","siteUrl":"https://ui.gkhn.dev"}}