{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ds-meteor-shower",
  "title": "Meteor Shower",
  "description": "Chuva de meteoros 100% CSS (sem canvas, sem RAF) para fundo de hero — posições determinísticas, custo de CPU essencialmente nulo.",
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "@lema-ds/tokens"
  ],
  "files": [
    {
      "path": "components/ds/ds-meteor-shower.tsx",
      "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\n// ── Types ──\n\nexport interface MeteorShowerProps\n  extends\n    React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof meteorShowerVariants> {}\n\n// ── Variants ──\n\nexport const meteorShowerVariants = cva(\n  \"pointer-events-none absolute inset-0 overflow-hidden\",\n  {\n    variants: {\n      tone: {\n        primary: \"[--meteor-color:var(--primary)]\",\n        violet: \"[--meteor-color:var(--highlight-violet)]\",\n        sky: \"[--meteor-color:var(--highlight-sky)]\",\n        neutral: \"[--meteor-color:var(--foreground)]\",\n      },\n      density: {\n        sm: \"\",\n        md: \"\",\n        lg: \"\",\n      },\n    },\n    defaultVariants: { tone: \"primary\", density: \"md\" },\n  }\n)\n\nconst DENSITY_COUNT = { sm: 8, md: 14, lg: 24 } as const\nconst MAX_METEORS = 24\n\n// mulberry32 — deterministic pseudo-random generator, ensures server\n// and client markup are identical without needing\n// \"use client\" nem de estado para posicionar os meteoros.\nfunction mulberry32(seed: number) {\n  return () => {\n    seed |= 0\n    seed = (seed + 0x6d2b79f5) | 0\n    let t = Math.imul(seed ^ (seed >>> 15), 1 | seed)\n    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t\n    return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n  }\n}\n\nconst rand = mulberry32(42)\nconst METEORS = Array.from({ length: MAX_METEORS }, (_, i) => ({\n  left: `${Math.round(rand() * 100)}%`,\n  top: `${Math.round(rand() * -55 - 2)}%`,\n  // First 4 meteors without delay for immediate effect when opening the story\n  delay:\n    i < 4 ? `${(rand() * 0.8).toFixed(2)}s` : `${(rand() * 3.5).toFixed(2)}s`,\n  duration: `${(1.8 + rand() * 1.8).toFixed(2)}s`,\n}))\n\n// ── Component ──\n\n/**\n * 100% CSS meteor shower for hero backgrounds — no canvas, no RAF,\n * no \"use client\". Positions are generated deterministically to\n * avoid hydration mismatch. `motion-reduce:hidden` removes the\n * meteors entirely under `prefers-reduced-motion`.\n */\nexport function MeteorShower({\n  className,\n  tone = \"primary\",\n  density = \"md\",\n  ...props\n}: MeteorShowerProps) {\n  const count = DENSITY_COUNT[density ?? \"md\"]\n\n  return (\n    <div\n      aria-hidden=\"true\"\n      data-slot=\"meteor-shower\"\n      className={cn(meteorShowerVariants({ tone, density }), className)}\n      {...props}\n    >\n      {METEORS.slice(0, count).map((meteor, index) => (\n        <span\n          key={index}\n          className=\"absolute top-0 left-0 motion-reduce:hidden\"\n          style={{\n            left: meteor.left,\n            top: meteor.top,\n            animation: `meteor-fall ${meteor.duration} linear ${meteor.delay} infinite`,\n            willChange: \"transform, opacity\",\n          }}\n        >\n          {/* Meteor head + tail via real elements (not pseudo) to ensure Tailwind generation */}\n          <span\n            className=\"absolute top-1/2 left-0 block size-[3px] -translate-y-1/2 rotate-[215deg] rounded-full\"\n            style={{ backgroundColor: \"var(--meteor-color)\" }}\n          />\n          <span\n            className=\"absolute top-1/2 left-0 block h-px w-[72px] origin-right -translate-y-1/2 rotate-[215deg] opacity-90\"\n            style={{\n              background: `linear-gradient(to left, var(--meteor-color), transparent)`,\n            }}\n          />\n        </span>\n      ))}\n      <style>{`\n        @keyframes meteor-fall {\n          0% { transform: translate3d(0, 0, 0); opacity: 1; }\n          70% { opacity: 1; }\n          100% { transform: translate3d(-650px, 650px, 0); opacity: 0; }\n        }\n        @media (prefers-reduced-motion: reduce) {\n          [data-slot=\"meteor-shower\"] { display: none; }\n        }\n      `}</style>\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}
