{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "launch-pad",
  "type": "registry:component",
  "title": "Launch Pad",
  "description": "A little rocket venting steam on its pad – click and it lights up, lifts off on a trail of smoke, then drops back in on a retro burn and lands.",
  "categories": [
    "illustration",
    "Objects"
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/devl/launch-pad.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport {\n  type AnimationPlaybackControls,\n  animate,\n  type MotionValue,\n  motion,\n  useInView,\n  useMotionValue,\n  useReducedMotion,\n  useTransform,\n} from \"motion/react\";\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport type LaunchPadHue = \"sky\" | \"emerald\" | \"amber\" | \"violet\" | \"rose\" | \"orange\" | \"indigo\" | \"teal\";\n\nconst hues: Record<LaunchPadHue, string> = {\n  sky: \"[--hue:var(--color-sky-500)] [--hue-soft:var(--color-sky-200)] [--hue-deep:var(--color-sky-700)]\",\n  emerald:\n    \"[--hue:var(--color-emerald-500)] [--hue-soft:var(--color-emerald-200)] [--hue-deep:var(--color-emerald-700)]\",\n  amber: \"[--hue:var(--color-amber-500)] [--hue-soft:var(--color-amber-200)] [--hue-deep:var(--color-amber-700)]\",\n  violet: \"[--hue:var(--color-violet-500)] [--hue-soft:var(--color-violet-200)] [--hue-deep:var(--color-violet-700)]\",\n  rose: \"[--hue:var(--color-rose-500)] [--hue-soft:var(--color-rose-200)] [--hue-deep:var(--color-rose-700)]\",\n  orange: \"[--hue:var(--color-orange-500)] [--hue-soft:var(--color-orange-200)] [--hue-deep:var(--color-orange-700)]\",\n  indigo: \"[--hue:var(--color-indigo-500)] [--hue-soft:var(--color-indigo-200)] [--hue-deep:var(--color-indigo-700)]\",\n  teal: \"[--hue:var(--color-teal-500)] [--hue-soft:var(--color-teal-200)] [--hue-deep:var(--color-teal-700)]\",\n};\n\ntype Phase = \"ready\" | \"ignition\" | \"liftoff\" | \"landing\" | \"landed\";\n\nconst phaseCopy: Record<Phase, string> = {\n  ready: \"Ready\",\n  ignition: \"Ignition\",\n  liftoff: \"Liftoff\",\n  landing: \"Landing\",\n  landed: \"Landed\",\n};\n\nexport interface LaunchPadProps {\n  hue?: LaunchPadHue;\n  name?: string;\n  onLaunch?: () => void;\n  onLanded?: () => void;\n  className?: string;\n}\n\nconst CX = 220;\nconst PAD_Y = 236;\nconst GROUND_Y = 262;\nconst FLIGHT = 360;\n\nconst smoke = [\n  { dx: -1, spread: 74, lift: 14, r: 15, delay: 0 },\n  { dx: 1, spread: 78, lift: 12, r: 16, delay: 0.02 },\n  { dx: -1, spread: 42, lift: 22, r: 19, delay: 0.05 },\n  { dx: 1, spread: 46, lift: 20, r: 18, delay: 0.06 },\n  { dx: -1, spread: 14, lift: 8, r: 20, delay: 0.08 },\n  { dx: 1, spread: 16, lift: 10, r: 19, delay: 0.1 },\n  { dx: -1, spread: 58, lift: 18, r: 17, delay: 0.03 },\n  { dx: 1, spread: 30, lift: 16, r: 17, delay: 0.07 },\n];\n\nconst steam = [\n  { x: CX - 46, delay: 0 },\n  { x: CX - 46, delay: 1.4 },\n  { x: CX + 46, delay: 0.7 },\n  { x: CX + 46, delay: 2.1 },\n];\n\nfunction SmokeCloud({\n  progress,\n  size,\n  dx,\n  spread,\n  lift,\n  r,\n  delay,\n}: (typeof smoke)[number] & { progress: MotionValue<number>; size: MotionValue<number> }) {\n  const local = useTransform(progress, (p) => Math.min(1, Math.max(0, (p - delay) / (1 - delay))));\n  const x = useTransform([local, size] as const, ([p, s]: number[]) => CX + dx * spread * s * (1 - (1 - p) ** 3));\n  const y = useTransform([local, size] as const, ([p, s]: number[]) => GROUND_Y - 6 - lift * s * p);\n  // Fades by shrinking, not opacity, so the goo threshold pinches clouds off cleanly instead of fringing.\n  const scale = useTransform([local, size] as const, ([p, s]: number[]) => {\n    const grow = 0.4 + p * 0.8;\n    const fade = p < 0.06 ? p / 0.06 : p > 0.7 ? Math.max(0, 1 - (p - 0.7) / 0.3) : 1;\n    return grow * fade * (0.55 + s * 0.45);\n  });\n  return <motion.circle className=\"fill-(--smoke)\" cx={0} cy={0} r={r} style={{ scale, x, y }} />;\n}\n\nfunction useCalmMotion() {\n  const prefers = useReducedMotion();\n  const [mounted, setMounted] = useState(false);\n  useEffect(() => setMounted(true), []);\n  return mounted && !!prefers;\n}\n\nexport function LaunchPad({ hue = \"sky\", name = \"Kestrel-1\", onLaunch, onLanded, className }: LaunchPadProps) {\n  const uid = useId().replace(/:/g, \"\");\n  const reduce = useCalmMotion();\n  const root = useRef<HTMLDivElement>(null);\n  const inView = useInView(root);\n  const live = inView && !reduce;\n  const [phase, setPhase] = useState<Phase>(\"ready\");\n\n  const y = useMotionValue(0);\n  const shake = useMotionValue(0);\n  const flame = useMotionValue(0);\n  const arm = useMotionValue(0);\n  const puff = useMotionValue(0);\n  const puffSize = useMotionValue(1);\n  const trailOpacity = useMotionValue(0);\n  const trailSpread = useMotionValue(1);\n  const squash = useMotionValue(1);\n\n  const trailScale = useTransform(y, (v) => Math.min(1, Math.max(0, -v / FLIGHT)));\n  const flameOpacity = useTransform(flame, [0, 0.15, 1], [0, 1, 1]);\n  const smokeOpacity = useTransform(puff, [0, 0.45, 0.95], [1, 1, 0]);\n  const glow = useTransform([flame, y] as const, ([f, v]: number[]) => f * Math.max(0, 1 + v / 110));\n\n  const busy = phase !== \"ready\";\n  const run = useRef(0);\n  const timers = useRef(new Set<ReturnType<typeof setTimeout>>());\n  const controls = useRef<AnimationPlaybackControls[]>([]);\n\n  useEffect(() => {\n    const pending = timers.current;\n    const running = controls.current;\n    return () => {\n      run.current++;\n      for (const t of pending) clearTimeout(t);\n      for (const c of running) c.stop();\n    };\n  }, []);\n\n  const wait = (ms: number) =>\n    new Promise<void>((resolve) => {\n      const t = setTimeout(() => {\n        timers.current.delete(t);\n        resolve();\n      }, ms);\n      timers.current.add(t);\n    });\n\n  const play = (c: AnimationPlaybackControls) => {\n    controls.current.push(c);\n    return c;\n  };\n\n  const launch = async () => {\n    if (busy) return;\n    const id = ++run.current;\n    const alive = () => run.current === id;\n    controls.current = [];\n    onLaunch?.();\n    setPhase(\"ignition\");\n\n    if (reduce) {\n      await wait(500);\n      if (!alive()) return;\n      setPhase(\"liftoff\");\n      await wait(900);\n      if (!alive()) return;\n      setPhase(\"landed\");\n      onLanded?.();\n      await wait(1400);\n      if (alive()) setPhase(\"ready\");\n      return;\n    }\n\n    play(animate(arm, -42, { duration: 0.4, ease: [0.23, 1, 0.32, 1] }));\n    play(animate(flame, 1, { duration: 0.3, ease: \"easeOut\" }));\n    play(animate(shake, [0, -1.2, 1.2, -1, 1, -0.8, 0.8, -0.4, 0], { duration: 0.65, ease: \"linear\" }));\n    puffSize.set(1);\n    puff.set(0);\n    play(animate(puff, 1, { duration: 2.4, ease: [0.2, 0.7, 0.3, 1] }));\n    trailOpacity.set(1);\n    trailSpread.set(1);\n    await wait(600);\n    if (!alive()) return;\n\n    setPhase(\"liftoff\");\n    await play(animate(y, -FLIGHT - 60, { duration: 1.45, ease: [0.6, 0, 0.9, 0.35] }));\n    if (!alive()) return;\n    play(animate(trailOpacity, 0, { duration: 1.1, ease: \"easeOut\" }));\n    play(animate(trailSpread, 2.2, { duration: 1.1, ease: \"easeOut\" }));\n    await wait(700);\n    if (!alive()) return;\n\n    setPhase(\"landing\");\n    const down = play(animate(y, 0, { duration: 1.7, ease: [0.15, 0.6, 0.25, 1] }));\n    const throttle = setTimeout(() => play(animate(flame, 0.55, { duration: 0.5 })), 900);\n    timers.current.add(throttle);\n    await down;\n    if (!alive()) return;\n    play(animate(flame, 0, { duration: 0.2, ease: \"easeOut\" }));\n    play(animate(squash, [1, 0.94, 1], { duration: 0.35, ease: [0.23, 1, 0.32, 1] }));\n    puffSize.set(0.45);\n    puff.set(0);\n    play(animate(puff, 1, { duration: 1.4, ease: [0.2, 0.7, 0.3, 1] }));\n    setPhase(\"landed\");\n    onLanded?.();\n    await wait(450);\n    if (!alive()) return;\n    play(animate(arm, 0, { duration: 0.6, ease: [0.65, 0, 0.35, 1] }));\n    await wait(1100);\n    if (alive()) setPhase(\"ready\");\n  };\n\n  const statusTone =\n    phase === \"ready\" || phase === \"landed\" ? \"bg-success\" : phase === \"ignition\" ? \"bg-warning\" : \"bg-(--hue)\";\n\n  return (\n    <div\n      ref={root}\n      className={cn(\n        \"relative w-full max-w-[480px] overflow-hidden rounded-2xl border bg-card shadow-xs/5\",\n        \"[--smoke:color-mix(in_oklab,var(--color-foreground)_12%,var(--color-card))] dark:[--smoke:color-mix(in_oklab,var(--color-foreground)_24%,var(--color-card))] [--flame-hot:var(--color-amber-300)] [--flame-glow:var(--color-amber-400)]\",\n        hues[hue],\n        className,\n      )}\n    >\n      <div\n        aria-hidden\n        className=\"dot-grid pointer-events-none absolute inset-0 opacity-50 [mask-image:radial-gradient(closest-side,black,transparent)]\"\n      />\n\n      <div className=\"pointer-events-none absolute inset-x-4 top-4 z-10 flex items-center justify-between sm:inset-x-5 sm:top-5\">\n        <div className=\"flex items-center gap-2 rounded-full border bg-popover/90 py-1 pr-2.5 pl-2 text-xs shadow-xs/5 backdrop-blur-sm\">\n          <span className=\"relative flex size-2\">\n            {phase === \"ignition\" && !reduce && (\n              <span className={cn(\"absolute inset-0 animate-ping rounded-full opacity-60\", statusTone)} />\n            )}\n            <span className={cn(\"relative size-2 rounded-full transition-colors duration-300\", statusTone)} />\n          </span>\n          <span className=\"font-medium text-foreground\">{name}</span>\n          <span className=\"inline-block min-w-[3.75rem] text-muted-foreground\" aria-live=\"polite\">\n            {phaseCopy[phase]}\n          </span>\n        </div>\n        <motion.span\n          animate={{ opacity: busy ? 0 : 1 }}\n          className=\"text-muted-foreground text-xs\"\n          initial={false}\n          transition={{ duration: 0.2 }}\n        >\n          Click to launch\n        </motion.span>\n      </div>\n\n      <button\n        aria-label={busy ? `${name}: ${phaseCopy[phase]}` : `Launch ${name}`}\n        aria-disabled={busy}\n        className={cn(\n          \"group/pad relative block aspect-[32/25] w-full cursor-pointer rounded-2xl outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\",\n          busy && \"cursor-default\",\n        )}\n        onClick={launch}\n        type=\"button\"\n      >\n        <svg aria-hidden className=\"absolute inset-0 size-full overflow-visible\" viewBox={`${CX - 160} 42 320 250`}>\n          <defs>\n            <linearGradient id={`${uid}-trail`} x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\">\n              <stop offset=\"0\" stopColor=\"var(--flame-hot)\" stopOpacity=\"0.8\" />\n              <stop offset=\"0.1\" stopColor=\"var(--smoke)\" stopOpacity=\"0.95\" />\n              <stop offset=\"0.75\" stopColor=\"var(--smoke)\" stopOpacity=\"0.85\" />\n              <stop offset=\"1\" stopColor=\"var(--smoke)\" stopOpacity=\"0.4\" />\n            </linearGradient>\n            <filter filterUnits=\"userSpaceOnUse\" height={FLIGHT + 200} id={`${uid}-soft`} width={200} x={CX - 100} y={PAD_Y - FLIGHT - 120}>\n              <feGaussianBlur stdDeviation=\"2.5\" />\n            </filter>\n            <filter filterUnits=\"userSpaceOnUse\" height={140} id={`${uid}-goo`} width={320} x={CX - 160} y={PAD_Y - 90}>\n              <feGaussianBlur in=\"SourceGraphic\" result=\"blur\" stdDeviation=\"4\" />\n              <feColorMatrix in=\"blur\" values=\"1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 20 -8\" />\n            </filter>\n            <clipPath id={`${uid}-sky`}>\n              <rect height={GROUND_Y + 1000} width={800} x={-200} y={-1000} />\n            </clipPath>\n            <linearGradient id={`${uid}-glass`} x1=\"0\" x2=\"1\" y1=\"0\" y2=\"1\">\n              <stop offset=\"0\" stopColor=\"var(--hue-soft)\" />\n              <stop offset=\"1\" stopColor=\"var(--hue)\" />\n            </linearGradient>\n            <radialGradient cx=\"0.5\" cy=\"0\" id={`${uid}-glow`} r=\"0.8\">\n              <stop offset=\"0\" stopColor=\"var(--flame-glow)\" stopOpacity=\"0.35\" />\n              <stop offset=\"1\" stopColor=\"var(--flame-glow)\" stopOpacity=\"0\" />\n            </radialGradient>\n          </defs>\n\n          <line className=\"stroke-foreground/10\" strokeLinecap=\"round\" strokeWidth={1.5} x1={CX - 150} x2={CX + 150} y1={GROUND_Y} y2={GROUND_Y} />\n          <motion.ellipse\n            cx={CX}\n            cy={PAD_Y + 2}\n            fill={`url(#${uid}-glow)`}\n            rx={64}\n            ry={22}\n            style={{ opacity: glow }}\n          />\n\n          <g className=\"stroke-foreground/25\" fill=\"none\" strokeLinecap=\"round\" strokeWidth={1.5}>\n            <line x1={138} x2={138} y1={118} y2={GROUND_Y} />\n            <line x1={152} x2={152} y1={118} y2={GROUND_Y} />\n            {[0, 1, 2, 3, 4, 5].map((i) => (\n              <path d={`M138 ${126 + i * 22} L152 ${146 + i * 22} M152 ${126 + i * 22} L138 ${146 + i * 22}`} key={i} strokeWidth={1} />\n            ))}\n            <line x1={135} x2={155} y1={118} y2={118} />\n          </g>\n          <circle className=\"fill-(--hue)\" cx={145} cy={113} r={2.5}>\n            {live && <animate attributeName=\"opacity\" dur=\"2.4s\" repeatCount=\"indefinite\" values=\"1;0.25;1\" />}\n          </circle>\n          <motion.g style={{ rotate: arm, originX: \"152px\", originY: \"168px\", transformBox: \"view-box\" }}>\n            <rect className=\"fill-card stroke-foreground/25\" height={7} rx={2} strokeWidth={1.5} width={47} x={152} y={164.5} />\n            <line className=\"stroke-foreground/20\" strokeWidth={1} x1={160} x2={192} y1={168} y2={168} />\n          </motion.g>\n\n          <motion.g filter={`url(#${uid}-soft)`} style={{ opacity: trailOpacity }}>\n            <motion.g style={{ originX: `${CX}px`, originY: `${PAD_Y}px`, scaleX: trailSpread, transformBox: \"view-box\" }}>\n              <motion.path\n                d={`M${CX - 4} ${PAD_Y - FLIGHT - 60} Q ${CX} ${PAD_Y - FLIGHT - 66} ${CX + 4} ${PAD_Y - FLIGHT - 60} L ${CX + 13} ${PAD_Y} H ${CX - 13} Z`}\n                fill={`url(#${uid}-trail)`}\n                style={{ originX: \"50%\", originY: \"100%\", scaleY: trailScale }}\n              />\n            </motion.g>\n          </motion.g>\n\n          <path\n            className=\"stroke-foreground/25\"\n            d={`M${CX + 58} ${PAD_Y + 12} H${CX + 84}`}\n            fill=\"none\"\n            strokeLinecap=\"round\"\n            strokeWidth={1.5}\n          />\n          <g className=\"stroke-foreground/25\" strokeLinecap=\"round\" strokeWidth={1.5}>\n            <line x1={CX + 90} x2={CX + 87} y1={PAD_Y + 10} y2={GROUND_Y} />\n            <line x1={CX + 112} x2={CX + 115} y1={PAD_Y + 10} y2={GROUND_Y} />\n          </g>\n          <rect className=\"fill-card stroke-foreground/25\" height={64} rx={13} strokeWidth={1.5} width={26} x={CX + 88} y={PAD_Y - 50} />\n          <path className=\"fill-foreground/[0.05]\" d={`M${CX + 107} ${PAD_Y - 49} a12 12 0 0 1 6 11 v38 a12 12 0 0 1 -6 11 Z`} />\n          <rect className=\"fill-(--hue)\" height={5} width={24.5} x={CX + 88.75} y={PAD_Y - 24} />\n          <line className=\"stroke-foreground/15\" strokeLinecap=\"round\" strokeWidth={1.5} x1={CX + 101} x2={CX + 101} y1={PAD_Y - 58} y2={PAD_Y - 50} />\n          <circle className=\"fill-card stroke-foreground/25\" cx={CX + 101} cy={PAD_Y - 60} r={2.5} strokeWidth={1.5} />\n\n          <g clipPath={`url(#${uid}-sky)`}>\n          <motion.g style={{ x: shake, y }}>\n            <motion.g style={{ originX: `${CX}px`, originY: `${PAD_Y}px`, scaleY: squash, transformBox: \"view-box\" }}>\n              <motion.g style={{ opacity: flameOpacity, originX: `${CX}px`, originY: `${PAD_Y}px`, scale: flame, transformBox: \"view-box\" }}>\n                <motion.g\n                  animate={live ? { scaleY: [1, 0.84, 1.08, 0.9, 1], scaleX: [1, 1.06, 0.96, 1.04, 1] } : undefined}\n                  style={{ originX: `${CX}px`, originY: `${PAD_Y}px`, transformBox: \"view-box\" }}\n                  transition={{ duration: 0.32, ease: \"linear\", repeat: Number.POSITIVE_INFINITY }}\n                >\n                  <path className=\"fill-orange-400\" d={`M${CX - 11} ${PAD_Y + 2} C ${CX - 12} ${PAD_Y + 22}, ${CX - 3} ${PAD_Y + 34}, ${CX} ${PAD_Y + 46} C ${CX + 3} ${PAD_Y + 34}, ${CX + 12} ${PAD_Y + 22}, ${CX + 11} ${PAD_Y + 2} Z`} />\n                  <path className=\"fill-amber-200\" d={`M${CX - 6} ${PAD_Y + 2} C ${CX - 6} ${PAD_Y + 14}, ${CX - 2} ${PAD_Y + 20}, ${CX} ${PAD_Y + 28} C ${CX + 2} ${PAD_Y + 20}, ${CX + 6} ${PAD_Y + 14}, ${CX + 6} ${PAD_Y + 2} Z`} />\n                </motion.g>\n              </motion.g>\n\n              <path className=\"fill-(--hue)\" d={`M${CX - 19} ${PAD_Y - 44} L${CX - 34} ${PAD_Y - 18} L${CX - 34} ${PAD_Y - 2} L${CX - 19} ${PAD_Y - 12} Z`} />\n              <path className=\"fill-(--hue-deep)\" d={`M${CX + 19} ${PAD_Y - 44} L${CX + 34} ${PAD_Y - 18} L${CX + 34} ${PAD_Y - 2} L${CX + 19} ${PAD_Y - 12} Z`} />\n              <path className=\"fill-(--hue-deep)\" d={`M${CX - 10} ${PAD_Y - 6} L${CX + 10} ${PAD_Y - 6} L${CX + 12} ${PAD_Y + 2} L${CX - 12} ${PAD_Y + 2} Z`} />\n              <path\n                className=\"fill-white stroke-black/10\"\n                d={`M${CX - 19} ${PAD_Y - 6} L${CX - 19} ${PAD_Y - 66} C ${CX - 19} ${PAD_Y - 92}, ${CX - 9} ${PAD_Y - 108}, ${CX} ${PAD_Y - 118} C ${CX + 9} ${PAD_Y - 108}, ${CX + 19} ${PAD_Y - 92}, ${CX + 19} ${PAD_Y - 66} L${CX + 19} ${PAD_Y - 6} Z`}\n                strokeWidth={1}\n              />\n              <path\n                className=\"fill-(--hue)\"\n                d={`M${CX - 13.5} ${PAD_Y - 92} C ${CX - 10} ${PAD_Y - 102}, ${CX - 5} ${PAD_Y - 111}, ${CX} ${PAD_Y - 118} C ${CX + 5} ${PAD_Y - 111}, ${CX + 10} ${PAD_Y - 102}, ${CX + 13.5} ${PAD_Y - 92} Z`}\n              />\n              <path\n                className=\"fill-black/[0.07]\"\n                d={`M${CX + 6} ${PAD_Y - 6} L${CX + 6} ${PAD_Y - 66} C ${CX + 6} ${PAD_Y - 90}, ${CX + 3} ${PAD_Y - 106}, ${CX} ${PAD_Y - 118} C ${CX + 9} ${PAD_Y - 108}, ${CX + 19} ${PAD_Y - 92}, ${CX + 19} ${PAD_Y - 66} L${CX + 19} ${PAD_Y - 6} Z`}\n              />\n              <rect className=\"fill-(--hue)\" height={4} rx={1} width={38} x={CX - 19} y={PAD_Y - 24} />\n              <circle className=\"fill-(--hue-deep)\" cx={CX} cy={PAD_Y - 70} r={10} />\n              <circle cx={CX} cy={PAD_Y - 70} fill={`url(#${uid}-glass)`} r={7} />\n              <path className=\"fill-white/70\" d={`M${CX - 4.5} ${PAD_Y - 72} a 5 5 0 0 1 4 -3.6 l 0.6 1.6 a 3.4 3.4 0 0 0 -2.8 2.6 Z`} />\n            </motion.g>\n          </motion.g>\n          </g>\n\n          <path className=\"fill-card stroke-foreground/20\" d={`M${CX - 58} ${PAD_Y + 2} h116 l10 ${GROUND_Y - PAD_Y - 2} h-136 Z`} strokeLinejoin=\"round\" strokeWidth={1.5} />\n          <line className=\"stroke-foreground/10\" strokeWidth={1} x1={CX - 54} x2={CX + 54} y1={PAD_Y + 9} y2={PAD_Y + 9} />\n          {[-1, 1].map((side) => (\n            <circle className=\"fill-(--hue)\" cx={CX + side * 48} cy={PAD_Y + 16} key={side} r={2.25}>\n              {live && (\n                <animate attributeName=\"opacity\" begin={side > 0 ? \"1.2s\" : \"0s\"} dur=\"2.4s\" repeatCount=\"indefinite\" values=\"1;0.3;1\" />\n              )}\n            </circle>\n          ))}\n\n          {!reduce && (\n            <g clipPath={`url(#${uid}-sky)`}>\n              <g filter={`url(#${uid}-goo)`}>\n                {live &&\n                  steam.map((st) => (\n                    <motion.circle\n                      animate={busy ? { r: 0 } : { cy: [PAD_Y + 4, PAD_Y - 30], r: [2, 8, 0] }}\n                      className=\"fill-(--smoke)\"\n                      cx={st.x}\n                      cy={PAD_Y + 4}\n                      initial={{ r: 0 }}\n                      key={`${st.x}-${st.delay}`}\n                      transition={\n                        busy\n                          ? { duration: 0.2 }\n                          : { delay: st.delay, duration: 2.8, ease: \"easeOut\", repeat: Number.POSITIVE_INFINITY, times: [0, 0.45, 1] }\n                      }\n                    />\n                  ))}\n              </g>\n              <motion.g style={{ opacity: smokeOpacity }}>\n                <g filter={`url(#${uid}-goo)`}>\n                  {smoke.map((c) => (\n                    <SmokeCloud key={`${c.dx}-${c.spread}`} progress={puff} size={puffSize} {...c} />\n                  ))}\n                </g>\n              </motion.g>\n            </g>\n          )}\n        </svg>\n      </button>\n    </div>\n  );\n}\n"
    }
  ]
}