{
  "$schema": "https://www.cooki-ui.design/schema/registry-item.json",
  "name": "radar-ping-beacon",
  "type": "registry:ui",
  "title": "Radar Ping Beacon",
  "description": "Atmospheric tactical radar sweep with concentric sonar ripples, interactive target blips, azimuth telemetry, and ping impulse physics.",
  "category": "animations",
  "dependencies": [
    "framer-motion",
    "@phosphor-icons/react"
  ],
  "files": [
    {
      "path": "registry/animations/radar-ping-beacon/radar-ping-beacon.tsx",
      "type": "registry:ui",
      "target": "components/ui/radar-ping-beacon.tsx",
      "content": "import React, { useState, useEffect, useRef } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { \n  Crosshair, \n  Broadcast, \n  Compass, \n  ShieldCheck,\n  Target\n} from '@phosphor-icons/react';\n\nexport interface RadarPingBeaconProps {\n  sweepSpeed?: number;\n  colorTheme?: 'emerald' | 'cyan' | 'amber';\n  interactive?: boolean;\n  className?: string;\n}\n\ninterface Blip {\n  id: string;\n  name: string;\n  x: number; // percentage -50 to 50\n  y: number; // percentage -50 to 50\n  distance: string;\n  signal: string;\n  status: 'active' | 'warning' | 'synced';\n}\n\nconst DEFAULT_BLIPS: Blip[] = [\n  { id: '1', name: 'GATEWAY-AP-01', x: 22, y: -26, distance: '142m', signal: '-42 dBm', status: 'synced' },\n  { id: '2', name: 'EDGE-CLUSTER-07', x: -32, y: -18, distance: '280m', signal: '-58 dBm', status: 'active' },\n  { id: '3', name: 'PROXY-NODE-12', x: 14, y: 34, distance: '198m', signal: '-64 dBm', status: 'warning' },\n  { id: '4', name: 'BEACON-SAT-04', x: -20, y: 28, distance: '310m', signal: '-71 dBm', status: 'active' },\n];\n\nexport const RadarPingBeacon: React.FC<RadarPingBeaconProps> = ({\n  sweepSpeed = 2.5,\n  colorTheme = 'emerald',\n  interactive = true,\n  className = '',\n}) => {\n  const [angle, setAngle] = useState(0);\n  const [pings, setPings] = useState<{ id: number; x: number; y: number }[]>([]);\n  const [selectedBlip, setSelectedBlip] = useState<Blip | null>(null);\n  const animRef = useRef<number | null>(null);\n\n  // Sweep rotation loop\n  useEffect(() => {\n    let lastTime = performance.now();\n    const speedDegPerSec = (360 / Math.max(1, sweepSpeed)) * 0.8;\n\n    const tick = (now: number) => {\n      const delta = (now - lastTime) / 1000;\n      lastTime = now;\n      setAngle((prev) => (prev + speedDegPerSec * delta) % 360);\n      animRef.current = requestAnimationFrame(tick);\n    };\n\n    animRef.current = requestAnimationFrame(tick);\n    return () => {\n      if (animRef.current) cancelAnimationFrame(animRef.current);\n    };\n  }, [sweepSpeed]);\n\n  const triggerPing = (e?: React.MouseEvent<HTMLDivElement>) => {\n    if (!interactive) return;\n    let x = 50;\n    let y = 50;\n    if (e && e.currentTarget) {\n      const rect = e.currentTarget.getBoundingClientRect();\n      if (rect.width > 0 && rect.height > 0) {\n        x = Math.round(((e.clientX - rect.left) / rect.width) * 100);\n        y = Math.round(((e.clientY - rect.top) / rect.height) * 100);\n      }\n    }\n    const newId = Date.now();\n    setPings((prev) => [...prev.slice(-3), { id: newId, x, y }]);\n    setTimeout(() => {\n      setPings((prev) => prev.filter((p) => p.id !== newId));\n    }, 1800);\n  };\n\n  const themeConfig = {\n    emerald: {\n      radarLine: 'from-emerald-500/0 via-emerald-500/20 to-emerald-400',\n      blip: 'bg-emerald-400 shadow-[0_0_12px_#34d399]',\n      ring: 'border-emerald-500/20',\n      axis: 'bg-emerald-500/20',\n      badge: 'text-emerald-400 bg-emerald-500/10 border-emerald-500/20',\n      gradientRing: 'border-emerald-500/30',\n    },\n    cyan: {\n      radarLine: 'from-cyan-500/0 via-cyan-500/20 to-cyan-400',\n      blip: 'bg-cyan-400 shadow-[0_0_12px_#38bdf8]',\n      ring: 'border-cyan-500/20',\n      axis: 'bg-cyan-500/20',\n      badge: 'text-cyan-400 bg-cyan-500/10 border-cyan-500/20',\n      gradientRing: 'border-cyan-500/30',\n    },\n    amber: {\n      radarLine: 'from-amber-500/0 via-amber-500/20 to-amber-400',\n      blip: 'bg-amber-400 shadow-[0_0_12px_#fbbf24]',\n      ring: 'border-amber-500/20',\n      axis: 'bg-amber-500/20',\n      badge: 'text-amber-400 bg-amber-500/10 border-amber-500/20',\n      gradientRing: 'border-amber-500/30',\n    },\n  }[colorTheme];\n\n  return (\n    <div className={`relative w-full max-w-lg mx-auto rounded-3xl border border-zinc-200 dark:border-white/10 bg-white dark:bg-[#07090e] p-5 shadow-2xl flex flex-col items-center select-none ${className}`}>\n      {/* Top Telemetry Header */}\n      <div className=\"w-full flex items-center justify-between pb-3 border-b border-zinc-200/80 dark:border-white/5 font-mono text-xs\">\n        <div className=\"flex items-center gap-2\">\n          <Broadcast size={15} className=\"text-emerald-500 animate-pulse\" />\n          <span className=\"font-bold text-zinc-900 dark:text-white uppercase tracking-wider\">Sonar Recon Array</span>\n        </div>\n        <div className=\"flex items-center gap-2\">\n          <span className=\"text-[11px] text-zinc-400\">AZM:</span>\n          <span className=\"font-bold text-zinc-900 dark:text-zinc-100 w-10 text-right\">\n            {Math.floor(angle).toString().padStart(3, '0')}°\n          </span>\n          <span className={`px-2 py-0.5 rounded-full text-[10px] font-semibold border ${themeConfig.badge}`}>\n            4 TARGETS\n          </span>\n        </div>\n      </div>\n\n      {/* Main Radar Screen */}\n      <div \n        onClick={triggerPing}\n        className=\"relative my-4 flex h-64 w-64 sm:h-72 sm:w-72 items-center justify-center rounded-full bg-zinc-950 border border-zinc-800 shadow-inner overflow-hidden cursor-crosshair\"\n      >\n        {/* Subtle grid pattern background */}\n        <div className=\"absolute inset-0 bg-[radial-gradient(#1f2937_1px,transparent_1px)] [background-size:16px_16px] opacity-40\" />\n\n        {/* Concentric Sonar Rings */}\n        {[0.28, 0.52, 0.76, 1.0].map((scale, i) => (\n          <div\n            key={i}\n            style={{ width: `${scale * 100}%`, height: `${scale * 100}%` }}\n            className={`absolute rounded-full border border-dashed ${themeConfig.ring}`}\n          />\n        ))}\n\n        {/* Crosshair Axes */}\n        <div className={`absolute h-full w-[1px] ${themeConfig.axis}`} />\n        <div className={`absolute w-full h-[1px] ${themeConfig.axis}`} />\n\n        {/* Outer Degree Ticks */}\n        <div className=\"absolute inset-2 rounded-full border border-zinc-800 pointer-events-none\" />\n\n        {/* Rotating Radar Sweep Needle with Phosphor Gradient Arc */}\n        <div\n          style={{\n            transform: `rotate(${angle}deg)`,\n            transformOrigin: 'bottom right',\n          }}\n          className=\"absolute top-0 left-0 w-1/2 h-1/2 pointer-events-none\"\n        >\n          {/* Sweep Trail Arc */}\n          <div \n            style={{\n              background: 'conic-gradient(from 180deg at 100% 100%, rgba(52,211,153,0.28) 0deg, rgba(52,211,153,0.05) 45deg, transparent 65deg)',\n            }}\n            className=\"w-full h-full\"\n          />\n          {/* Leading Radius Needle */}\n          <div className=\"absolute right-0 bottom-0 top-0 w-[1.5px] bg-gradient-to-t from-emerald-400 to-emerald-200 shadow-[0_0_8px_#34d399]\" />\n        </div>\n\n        {/* Manual Ping Waves */}\n        <AnimatePresence>\n          {pings.map((p) => (\n            <motion.div\n              key={p.id}\n              initial={{ scale: 0, opacity: 0.8 }}\n              animate={{ scale: 1.4, opacity: 0 }}\n              exit={{ opacity: 0 }}\n              transition={{ duration: 1.6, ease: 'easeOut' }}\n              className={`absolute w-full h-full rounded-full border-2 ${themeConfig.gradientRing} pointer-events-none`}\n            />\n          ))}\n        </AnimatePresence>\n\n        {/* Interactive Target Blips */}\n        {DEFAULT_BLIPS.map((blip) => {\n          // Check proximity of current sweep angle to blip angle\n          const blipAngle = (Math.atan2(blip.y, blip.x) * 180) / Math.PI + 180;\n          const diff = Math.abs(angle - blipAngle);\n          const isSwept = diff < 15 || diff > 345;\n\n          return (\n            <div\n              key={blip.id}\n              style={{\n                left: `${50 + blip.x}%`,\n                top: `${50 + blip.y}%`,\n              }}\n              onClick={(e) => {\n                e.stopPropagation();\n                setSelectedBlip(blip === selectedBlip ? null : blip);\n              }}\n              className=\"absolute -translate-x-1/2 -translate-y-1/2 z-20 group cursor-pointer\"\n            >\n              <div \n                className={`relative flex items-center justify-center h-4 w-4 rounded-full transition-transform hover:scale-150`}\n              >\n                {/* Blip Core Light */}\n                <div \n                  className={`h-2.5 w-2.5 rounded-full ${themeConfig.blip} ${\n                    isSwept ? 'scale-125 brightness-150 animate-ping' : ''\n                  }`} \n                />\n                \n                {/* Ring Beacon on Swept */}\n                {isSwept && (\n                  <div className=\"absolute inset-0 rounded-full border border-emerald-400 animate-ping\" />\n                )}\n              </div>\n\n              {/* Blip Tooltip */}\n              <div className=\"absolute bottom-5 left-1/2 -translate-x-1/2 hidden group-hover:flex flex-col items-center pointer-events-none z-30\">\n                <div className=\"bg-zinc-900 border border-zinc-700 text-white rounded-lg px-2.5 py-1 text-[10px] font-mono whitespace-nowrap shadow-xl\">\n                  <p className=\"font-bold text-emerald-400\">{blip.name}</p>\n                  <p className=\"text-zinc-400\">{blip.distance} &bull; {blip.signal}</p>\n                </div>\n              </div>\n            </div>\n          );\n        })}\n\n        {/* Center Radar Transmitter Core */}\n        <div className=\"relative z-20 flex h-4 w-4 items-center justify-center rounded-full bg-zinc-900 border border-zinc-600 shadow-md\">\n          <div className=\"h-1.5 w-1.5 rounded-full bg-emerald-400 animate-pulse\" />\n        </div>\n      </div>\n\n      {/* Selected Blip Telemetry Banner / Ping Action */}\n      <div className=\"w-full mt-2 pt-3 border-t border-zinc-200/80 dark:border-white/5 flex items-center justify-between text-xs font-mono\">\n        <div className=\"flex items-center gap-2 truncate\">\n          {selectedBlip ? (\n            <div className=\"flex items-center gap-2\">\n              <Target size={14} className=\"text-emerald-500 shrink-0\" />\n              <span className=\"text-zinc-900 dark:text-zinc-100 font-bold\">{selectedBlip.name}</span>\n              <span className=\"text-zinc-400\">({selectedBlip.distance})</span>\n            </div>\n          ) : (\n            <span className=\"text-zinc-500 dark:text-zinc-400 text-[11px]\">\n              Tap radar surface or ping button to emit pulse\n            </span>\n          )}\n        </div>\n\n        <button\n          onClick={() => triggerPing()}\n          className=\"px-3 py-1 rounded-xl bg-zinc-900 text-white dark:bg-white dark:text-zinc-900 text-[11px] font-bold hover:opacity-90 transition-opacity cursor-pointer shadow-xs\"\n        >\n          PING SONAR\n        </button>\n      </div>\n    </div>\n  );\n};\n"
    }
  ]
}