{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "evervault-card",
  "title": "Evervault Card",
  "description": "Card that reveals shuffling brand-tinted characters through a mask following the pointer.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/velora/evervault-card.tsx",
      "content": "\"use client\";\n\nimport { useRef } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%&*+=<>/\";\n\ninterface EvervaultCardProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Centred badge content (text or an icon) drawn above the character field */\n  label?: React.ReactNode;\n  /** Radius of the reveal mask in px */\n  radius?: number;\n  /** Minimum ms between re-randomising the characters while the pointer moves */\n  interval?: number;\n}\n\n/**\n * Card that reveals a field of random, brand-tinted characters through a\n * soft circular mask that follows the pointer; the characters reshuffle as\n * it moves. Keyboard focus inside the card reveals the mask at the centre.\n * `label` renders a centred badge; `children` render below it.\n */\nexport function EvervaultCard({\n  label,\n  radius = 140,\n  interval = 60,\n  className,\n  style,\n  children,\n  ...props\n}: EvervaultCardProps) {\n  const ref = useRef<HTMLDivElement>(null);\n  const field = useRef<HTMLParagraphElement>(null);\n  const last = useRef(0);\n\n  const scramble = (force = false) => {\n    const el = ref.current;\n    const p = field.current;\n    if (!el || !p) return;\n    const now = performance.now();\n    const still = matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n    if (!force && (still || now - last.current < interval)) return;\n    if (still && p.textContent) return;\n    last.current = now;\n    const count = Math.ceil((el.offsetWidth * el.offsetHeight) / 60);\n    let text = \"\";\n    for (let i = 0; i < count; i++) text += CHARS[(Math.random() * CHARS.length) | 0];\n    p.textContent = text;\n  };\n\n  const place = (x: string, y: string) => {\n    ref.current?.style.setProperty(\"--ev-x\", x);\n    ref.current?.style.setProperty(\"--ev-y\", y);\n  };\n\n  return (\n    <div\n      {...props}\n      ref={ref}\n      data-slot=\"evervault-card\"\n      style={{ \"--ev-r\": `${radius}px`, ...style } as React.CSSProperties}\n      className={cn(\n        \"group/ev relative isolate flex flex-col overflow-hidden rounded-3xl border bg-card text-card-foreground [--ev-x:50%] [--ev-y:50%]\",\n        className\n      )}\n      onPointerEnter={(e) => {\n        props.onPointerEnter?.(e);\n        scramble(true);\n      }}\n      onPointerMove={(e) => {\n        props.onPointerMove?.(e);\n        if (matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n        const rect = e.currentTarget.getBoundingClientRect();\n        place(`${e.clientX - rect.left}px`, `${e.clientY - rect.top}px`);\n        scramble();\n      }}\n      onFocus={(e) => {\n        props.onFocus?.(e);\n        if (!e.target.matches(\":focus-visible\")) return;\n        place(\"50%\", \"50%\");\n        scramble(true);\n      }}\n    >\n      <div\n        aria-hidden\n        className=\"pointer-events-none absolute inset-0 -z-10 opacity-0 transition-opacity duration-500 [mask-image:radial-gradient(circle_var(--ev-r)_at_var(--ev-x)_var(--ev-y),#000_15%,transparent)] group-hover/ev:opacity-100 group-has-[:focus-visible]/ev:opacity-100\"\n      >\n        <div className=\"absolute inset-0 bg-linear-to-br from-brand-from/25 via-brand-via/15 to-brand-to/25\" />\n        <p\n          ref={field}\n          className=\"absolute inset-0 overflow-hidden bg-linear-to-br from-brand-from via-brand-via to-brand-to bg-clip-text font-mono text-[10px] leading-3 font-semibold break-all text-transparent\"\n        />\n      </div>\n      {label != null && (\n        <div className=\"flex flex-1 items-center justify-center p-8\">\n          <div className=\"relative\">\n            <span\n              aria-hidden\n              className=\"absolute -inset-3 rounded-full bg-linear-to-br from-brand-from via-brand-via to-brand-to opacity-0 blur-xl transition-opacity duration-500 group-hover/ev:opacity-50 group-has-[:focus-visible]/ev:opacity-50\"\n            />\n            <div className=\"relative flex size-32 items-center justify-center rounded-full border bg-card/85 text-center text-2xl font-semibold tracking-tight shadow-xl backdrop-blur-sm\">\n              {label}\n            </div>\n          </div>\n        </div>\n      )}\n      {children}\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/velora/evervault-card.tsx"
    }
  ],
  "type": "registry:ui"
}