{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "repulsive-hover",
  "title": "Repulsive Hover",
  "author": "Visant Labs <visantsupply@gmail.com>",
  "description": "O elemento foge do cursor com mola. Gesto de hero, fora de formulário.",
  "dependencies": [
    "framer-motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/visant/ui/repulsive-hover.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion, useSpring } from \"framer-motion\";\n\n/**\n * Repulsive Hover — o elemento que se esquiva do cursor.\n *\n * O oposto do magnético: em vez de puxar o dedo pro alvo, ele afasta. Serve\n * quando o elemento é **decoração viva** (um selo de hero, uma peça de colagem,\n * um campo de glifos) e o movimento é o que faz a superfície parecer física.\n *\n * Fora de hero ele é armadilha, e vale dizer por quê: alvo que foge é alvo que\n * a pessoa erra. Nunca em botão de formulário, nunca em qualquer coisa que\n * precise ser clicada. O `strength` default é pequeno de propósito — a esquiva\n * boa é a que a pessoa sente antes de perceber.\n *\n * ── As quatro guardas ──────────────────────────────────────────────────────\n *\n * · **Só em ponteiro fino.** Em toque não existe hover: a peça ficaria parada\n *   fora do lugar, deslocada pelo último toque. Sem mouse, ela nem escuta.\n * · **`prefers-reduced-motion` desliga inteiro.** É movimento puro, sem dado\n *   nenhum embutido — o estado de repouso já é o conteúdo completo.\n * · **Distância zero não divide.** Cursor exatamente no centro dava `0/0` e a\n *   peça sumia num `NaN` de transform.\n * · **A mola volta ao repouso.** Fora do raio o alvo é 0, e é a mola que leva de\n *   volta — cortar o valor na hora faria a peça saltar quando o cursor sai\n *   rápido pela borda do raio.\n */\nexport interface RepulsiveHoverProps {\n  children: React.ReactNode;\n  /** Deslocamento máximo em px, no ponto mais próximo do cursor. */\n  strength?: number;\n  /** Raio de influência em px. Abaixo dele a peça começa a fugir. */\n  radius?: number;\n  className?: string;\n}\n\nexport function RepulsiveHover({\n  children,\n  strength = 12,\n  radius = 140,\n  className,\n}: RepulsiveHoverProps) {\n  const ref = React.useRef<HTMLDivElement>(null);\n  // Mola macia e bem amortecida: a esquiva é elástica, não é gelatina.\n  const x = useSpring(0, { stiffness: 140, damping: 18, mass: 0.6 });\n  const y = useSpring(0, { stiffness: 140, damping: 18, mass: 0.6 });\n\n  React.useEffect(() => {\n    let fino = false;\n    try {\n      fino = window.matchMedia(\"(hover: hover) and (pointer: fine)\").matches;\n      if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) fino = false;\n    } catch {\n      fino = false;\n    }\n    if (!fino) return;\n\n    const mover = (e: PointerEvent) => {\n      const el = ref.current;\n      if (!el || (e.pointerType && e.pointerType !== \"mouse\")) return;\n\n      const r = el.getBoundingClientRect();\n      const dx = e.clientX - (r.left + r.width / 2);\n      const dy = e.clientY - (r.top + r.height / 2);\n      const dist = Math.hypot(dx, dy);\n\n      if (dist >= radius) {\n        x.set(0);\n        y.set(0);\n        return;\n      }\n\n      // Quanto mais perto, mais forte. `|| 1` cobre o cursor no centro exato.\n      const forca = (radius - dist) / radius;\n      const d = dist || 1;\n      x.set((-dx / d) * forca * strength);\n      y.set((-dy / d) * forca * strength);\n    };\n    const repousar = () => {\n      x.set(0);\n      y.set(0);\n    };\n\n    window.addEventListener(\"pointermove\", mover, { passive: true });\n    document.addEventListener(\"pointerleave\", repousar, { passive: true });\n    return () => {\n      window.removeEventListener(\"pointermove\", mover);\n      document.removeEventListener(\"pointerleave\", repousar);\n    };\n  }, [radius, strength, x, y]);\n\n  return (\n    <motion.div ref={ref} style={{ x, y }} className={className}>\n      {children}\n    </motion.div>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "meta": {
    "uso": {
      "quando": "Decoração viva de hero que foge do cursor, como selo ou peça de colagem",
      "evite": "Qualquer coisa clicável ou formulário; pra atrair o cursor pro botão, use magnetic-cta"
    }
  },
  "categories": [
    "efeitos",
    "hover",
    "mola"
  ],
  "type": "registry:ui"
}