{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "meteors",
  "type": "registry:component",
  "title": "Meteors",
  "description": "Meteor streaks with fading tails that fall diagonally behind your content at random intervals.",
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "https://ui.minidev.pro/r/button.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/premium/meteors.tsx",
      "type": "registry:ui",
      "target": "components/ui/meteors.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { useInView, useReducedMotion } from \"motion/react\"\nimport { ArrowRight } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\n\n/**\n * Meteor streaks with fading tails that fall diagonally behind your content at random intervals.\n * Each meteor takes a fresh path every time it comes round. The streaks sit\n * behind the content, ignore the pointer, pause off screen and hold still for\n * reduced motion.\n */\ntype MeteorsProps = {\n  /** How many meteors are in flight at once. */\n  count?: number\n  /** Direction of travel in degrees. 0 points right, 90 falls straight down. */\n  angle?: number\n  /** Speed multiplier. 2 is twice as fast, 0.5 half as fast. */\n  speed?: number\n  /** Tail length in pixels. */\n  tail?: number\n  className?: string\n  children?: React.ReactNode\n}\n\ntype Meteor = { id: number; x: number; y: number; duration: number; delay: number; glow: number }\n\n/** Share of each cycle a meteor spends in flight; the rest is the pause before it comes round again. */\nconst FLIGHT = 0.7\n\nfunction Meteors({ count = 16, angle = 140, speed = 1, tail = 110, className, children }: MeteorsProps) {\n  const reduce = useReducedMotion()\n  const ref = React.useRef<HTMLDivElement>(null)\n  const inView = useInView(ref, { margin: \"80px\" })\n  const [box, setBox] = React.useState<{ w: number; h: number } | null>(null)\n  const [meteors, setMeteors] = React.useState<Meteor[]>([])\n\n  const rad = (angle * Math.PI) / 180\n  const dx = Math.cos(rad)\n  const dy = Math.sin(rad)\n  const travel = box ? Math.hypot(box.w, box.h) + tail * 2 : 0\n\n  // Measure on the client only, so the server renders no meteors and hydration matches.\n  React.useEffect(() => {\n    const el = ref.current\n    if (!el) return\n    const ro = new ResizeObserver(([e]) => {\n      const w = Math.round(e.contentRect.width)\n      const h = Math.round(e.contentRect.height)\n      setBox((b) => (b && b.w === w && b.h === h ? b : { w, h }))\n    })\n    ro.observe(el)\n    return () => ro.disconnect()\n  }, [])\n\n  // A path through a random point of the panel, entering and leaving off screen.\n  const place = React.useCallback(\n    (w: number, h: number) => {\n      const px = Math.random() * w\n      const py = Math.random() * h\n      const half = (Math.hypot(w, h) + tail * 2) / 2\n      return { x: px - dx * half, y: py - dy * half }\n    },\n    [dx, dy, tail]\n  )\n\n  React.useEffect(() => {\n    if (!box) return\n    const s = Math.max(0.1, speed)\n    setMeteors(\n      Array.from({ length: Math.max(0, Math.round(count)) }, (_, id) => ({\n        id,\n        ...place(box.w, box.h),\n        duration: (1.4 + Math.random() * 1.8) / FLIGHT / s,\n        delay: Math.random() * 3,\n        glow: 0.55 + Math.random() * 0.45,\n      }))\n    )\n  }, [box, count, speed, place])\n\n  // Each lap, the meteor re-enters on a fresh path while it is invisible.\n  const reroll = (id: number) => {\n    if (!box) return\n    setMeteors((all) => all.map((m) => (m.id === id ? { ...m, ...place(box.w, box.h) } : m)))\n  }\n\n  return (\n    <div\n      ref={ref}\n      data-slot=\"meteors\"\n      className={cn(\"relative isolate h-[360px] w-full overflow-hidden rounded-2xl border border-border bg-bg\", className)}\n    >\n      <div aria-hidden className=\"pointer-events-none absolute inset-0 z-0\">\n        <div\n          className=\"absolute inset-0\"\n          style={{ background: \"radial-gradient(120% 70% at 85% -10%, var(--accent-soft), transparent 70%)\" }}\n        />\n        {meteors.map((m) => {\n          const vars = {\n            \"--x\": `${m.x}px`,\n            \"--y\": `${m.y}px`,\n            \"--a\": `${angle}deg`,\n            \"--t\": `${tail}px`,\n            \"--d\": `${travel}px`,\n            \"--o\": m.glow,\n          } as React.CSSProperties\n          return (\n            <span\n              key={m.id}\n              data-slot=\"meteors-meteor\"\n              onAnimationIteration={() => reroll(m.id)}\n              className=\"absolute top-0 left-0 block h-px origin-[0_50%]\"\n              style={{\n                ...vars,\n                width: tail,\n                background: \"linear-gradient(to right, transparent, var(--accent))\",\n                transform: reduce\n                  ? `translate(${m.x + (dx * travel) / 2}px, ${m.y + (dy * travel) / 2}px) rotate(${angle}deg) translateX(-${tail}px)`\n                  : undefined,\n                opacity: reduce ? m.glow * 0.5 : 0,\n                animation: reduce ? undefined : `meteors-fall ${m.duration}s linear ${m.delay}s infinite`,\n                animationPlayState: inView ? \"running\" : \"paused\",\n              }}\n            >\n              <span\n                className=\"absolute top-1/2 right-0 block size-[3px] -translate-y-1/2 translate-x-1/2 rounded-full\"\n                style={{ background: \"var(--accent)\", boxShadow: \"0 0 8px 2px var(--accent-line)\" }}\n              />\n            </span>\n          )\n        })}\n      </div>\n      <div className=\"relative z-10 h-full\">\n        {children ?? (\n          <div className=\"flex h-full flex-col items-center justify-center gap-4 px-6 text-center\">\n            <span className=\"rounded-full border border-accent-line bg-accent-soft px-2.5 py-0.5 text-xs font-medium text-accent-fg\">\n              Lumen 3.0\n            </span>\n            <p className=\"max-w-[16ch] text-3xl font-medium tracking-[-0.035em] text-balance text-fg\">\n              Invoices that close themselves\n            </p>\n            <Button>\n              Read the release notes\n              <ArrowRight />\n            </Button>\n          </div>\n        )}\n      </div>\n      <style>{`@keyframes meteors-fall{0%{transform:translate(var(--x),var(--y)) rotate(var(--a)) translateX(calc(var(--t) * -1));opacity:0}6%{opacity:var(--o)}${Math.round(FLIGHT * 80)}%{opacity:var(--o)}${Math.round(FLIGHT * 100)}%,100%{transform:translate(var(--x),var(--y)) rotate(var(--a)) translateX(calc(var(--d) - var(--t)));opacity:0}}`}</style>\n    </div>\n  )\n}\n\nexport { Meteors }\nexport type { MeteorsProps }\n"
    }
  ]
}
