{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "activity-wheel-motion",
  "description": "Scrollable arc-curved vibe picker with violet active fill and a dark indicator pill.",
  "dependencies": [
    "framer-motion",
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/new-york/motion-framer/activity-wheel-motion.tsx",
      "content": "\"use client\";\n\n/**\n * Vibes wheel — a vertical mood picker arranged along a curved arc. Scroll\n * or click to spring a different vibe into focus; the icon swaps to a\n * violet \"active\" fill and a dark indicator slides into the selected row.\n */\n\nimport { motion } from \"framer-motion\";\nimport { useEffect, useRef, useState } from \"react\";\nimport {\n  BookOpen,\n  Brain,\n  Brush,\n  Cloud,\n  Code2,\n  Coffee,\n  Compass,\n  Feather,\n  Flame,\n  Flower2,\n  Footprints,\n  Gamepad2,\n  Headphones,\n  HeartPulse,\n  Lamp,\n  Lightbulb,\n  Moon,\n  Mountain,\n  Rocket,\n  Snowflake,\n  Sparkles,\n  Sprout,\n  Star,\n  Sun,\n  Waves,\n  Wind,\n  Zap,\n  type LucideIcon,\n} from \"lucide-react\";\n\ntype Vibe = {\n  name: string;\n  Icon: LucideIcon;\n  bg: string;\n  color: string;\n};\n\nconst VIBES: Vibe[] = [\n  { name: \"Focus\", Icon: Brain, bg: \"#ECE6F8\", color: \"#6E4DBF\" },\n  { name: \"Spark\", Icon: Sparkles, bg: \"#FFEFD0\", color: \"#D49417\" },\n  { name: \"Calm\", Icon: Cloud, bg: \"#DCEAF5\", color: \"#4A82B5\" },\n  { name: \"Cozy\", Icon: Flame, bg: \"#FFDDD0\", color: \"#DA6034\" },\n  { name: \"Joy\", Icon: Sun, bg: \"#FFE7B0\", color: \"#D9821A\" },\n  { name: \"Dream\", Icon: Star, bg: \"#F4DEE9\", color: \"#BA4F84\" },\n  { name: \"Rest\", Icon: Moon, bg: \"#DEE2F0\", color: \"#4F62A1\" },\n  { name: \"Flow\", Icon: Waves, bg: \"#D5EBF1\", color: \"#2C90A9\" },\n  { name: \"Grow\", Icon: Sprout, bg: \"#DEF0D7\", color: \"#4A8C3A\" },\n  { name: \"Charge\", Icon: Zap, bg: \"#FFF3C2\", color: \"#C99317\" },\n  { name: \"Wander\", Icon: Compass, bg: \"#E2EAD9\", color: \"#6B834E\" },\n  { name: \"Pulse\", Icon: HeartPulse, bg: \"#FFE0E2\", color: \"#C44862\" },\n  { name: \"Read\", Icon: BookOpen, bg: \"#EEE6DA\", color: \"#8A6B45\" },\n  { name: \"Write\", Icon: Feather, bg: \"#E6E9F2\", color: \"#5A6B95\" },\n  { name: \"Code\", Icon: Code2, bg: \"#DDEBE3\", color: \"#3F7C5C\" },\n  { name: \"Brew\", Icon: Coffee, bg: \"#EFE0CC\", color: \"#8A623E\" },\n  { name: \"Listen\", Icon: Headphones, bg: \"#E5DEF2\", color: \"#745EB5\" },\n  { name: \"Move\", Icon: Footprints, bg: \"#F2E5DA\", color: \"#A06A45\" },\n  { name: \"Chill\", Icon: Snowflake, bg: \"#DCEBF4\", color: \"#4685B3\" },\n  { name: \"Bloom\", Icon: Flower2, bg: \"#F4DDE9\", color: \"#B95786\" },\n  { name: \"Breeze\", Icon: Wind, bg: \"#DCEEEC\", color: \"#4A8C8C\" },\n  { name: \"Bright\", Icon: Lightbulb, bg: \"#FFF0BD\", color: \"#C99A1A\" },\n  { name: \"Boost\", Icon: Rocket, bg: \"#FFDDE0\", color: \"#C44A6A\" },\n  { name: \"Game\", Icon: Gamepad2, bg: \"#E0E0F0\", color: \"#6B6BB5\" },\n  { name: \"Climb\", Icon: Mountain, bg: \"#DEE5DC\", color: \"#5C7551\" },\n  { name: \"Sketch\", Icon: Brush, bg: \"#F0E1E8\", color: \"#9A5A78\" },\n  { name: \"Glow\", Icon: Lamp, bg: \"#FFE9C8\", color: \"#C9881A\" },\n];\n\nconst ARC_RADIUS = 140;\nconst ARC_STEP_DEG = 13;\nconst ACTIVE_FILL = \"#8B5CF6\";\nconst ACTIVE_GLOW = \"rgba(139,92,246,0.55)\";\nconst WHEEL_STEP_THRESHOLD = 40;\n\nconst ActivityWheelMotion = () => {\n  const [selected, setSelected] = useState(3);\n  const containerRef = useRef<HTMLDivElement>(null);\n  const wheelAccumRef = useRef(0);\n\n  useEffect(() => {\n    const node = containerRef.current;\n    if (!node) return;\n\n    const handleWheel = (e: WheelEvent) => {\n      e.preventDefault();\n      wheelAccumRef.current += e.deltaY;\n\n      if (Math.abs(wheelAccumRef.current) < WHEEL_STEP_THRESHOLD) return;\n\n      const steps = Math.trunc(wheelAccumRef.current / WHEEL_STEP_THRESHOLD);\n      wheelAccumRef.current -= steps * WHEEL_STEP_THRESHOLD;\n\n      setSelected((prev) =>\n        Math.max(0, Math.min(VIBES.length - 1, prev + steps)),\n      );\n    };\n\n    node.addEventListener(\"wheel\", handleWheel, { passive: false });\n    return () => node.removeEventListener(\"wheel\", handleWheel);\n  }, []);\n\n  return (\n    <div\n      ref={containerRef}\n      className=\"relative w-[300px] h-[300px] overflow-hidden touch-none [--wheel-bg:#FAFAFA] [--wheel-hl:rgba(255,255,255,0.95)] [--wheel-arc1:rgba(0,0,0,0.08)] [--wheel-arc2:rgba(0,0,0,0.04)] dark:[--wheel-bg:#1C1C1F] dark:[--wheel-hl:rgba(255,255,255,0.05)] dark:[--wheel-arc1:rgba(255,255,255,0.10)] dark:[--wheel-arc2:rgba(255,255,255,0.05)]\"\n      style={{\n        background: \"var(--wheel-bg)\",\n        borderRadius: \"28px\",\n        boxShadow:\n          \"0 1px 0 var(--wheel-hl) inset, 0 -1px 0 rgba(0,0,0,0.12) inset, 0 24px 40px -16px rgba(0,0,0,0.30), 0 4px 12px -2px rgba(0,0,0,0.16)\",\n        fontFamily: \"ui-sans-serif, system-ui\",\n        overscrollBehavior: \"contain\",\n      }}\n    >\n      <svg\n        viewBox=\"0 0 300 300\"\n        className=\"pointer-events-none absolute inset-0\"\n        preserveAspectRatio=\"none\"\n      >\n        <circle\n          cx=\"404\"\n          cy=\"150\"\n          r=\"140\"\n          fill=\"none\"\n          strokeWidth=\"1\"\n          strokeDasharray=\"2 4\"\n          style={{ stroke: \"var(--wheel-arc1)\" }}\n        />\n        <circle\n          cx=\"404\"\n          cy=\"150\"\n          r=\"125\"\n          fill=\"none\"\n          strokeWidth=\"1\"\n          strokeDasharray=\"2 4\"\n          style={{ stroke: \"var(--wheel-arc2)\" }}\n        />\n      </svg>\n\n      <div className=\"absolute inset-x-0 top-1/2 -translate-y-1/2\">\n        {VIBES.map((vibe, i) => {\n          const offset = i - selected;\n          const abs = Math.abs(offset);\n          const isSelected = offset === 0;\n\n          const angleRad = (offset * ARC_STEP_DEG * Math.PI) / 180;\n          const arcX = ARC_RADIUS * (1 - Math.cos(angleRad));\n          const arcY = ARC_RADIUS * Math.sin(angleRad);\n\n          return (\n            <motion.button\n              key={vibe.name}\n              type=\"button\"\n              onClick={() => setSelected(i)}\n              aria-pressed={isSelected}\n              aria-label={`Select ${vibe.name}`}\n              className=\"absolute left-0 right-0 flex cursor-pointer items-center justify-between px-5\"\n              style={{ top: \"50%\", marginTop: -16, height: 32 }}\n              animate={{\n                x: arcX,\n                y: arcY,\n                scale: 1 - Math.min(abs, 3) * 0.07,\n                opacity: abs > 3 ? 0 : 1 - abs * 0.22,\n              }}\n              transition={{\n                type: \"spring\",\n                stiffness: 240,\n                damping: 26,\n                mass: 0.7,\n              }}\n            >\n              <motion.span\n                className=\"rounded-full px-2.5 py-1 text-[13px] tracking-tight\"\n                animate={{\n                  color: isSelected ? \"#171717\" : \"#A3A3A3\",\n                  borderColor: isSelected\n                    ? \"rgba(0,0,0,0.08)\"\n                    : \"rgba(0,0,0,0)\",\n                  backgroundColor: isSelected ? \"#FFFFFF\" : \"rgba(0,0,0,0)\",\n                  fontWeight: isSelected ? 500 : 400,\n                }}\n                transition={{ duration: 0.25 }}\n                style={{ borderWidth: 1, borderStyle: \"solid\" }}\n              >\n                {vibe.name}\n              </motion.span>\n\n              <div className=\"flex items-center gap-2\">\n                <motion.div\n                  className=\"grid size-8 place-items-center rounded-full\"\n                  animate={{\n                    backgroundColor: isSelected ? ACTIVE_FILL : vibe.bg,\n                    boxShadow: isSelected\n                      ? `0 6px 14px -4px ${ACTIVE_GLOW}, 0 1px 0 rgba(255,255,255,0.7) inset`\n                      : \"0 1px 0 rgba(255,255,255,0.7) inset\",\n                  }}\n                  transition={{ duration: 0.25 }}\n                >\n                  <vibe.Icon\n                    className=\"size-4\"\n                    style={{ color: isSelected ? \"#FFFFFF\" : vibe.color }}\n                    strokeWidth={2}\n                  />\n                </motion.div>\n\n                <motion.div\n                  className=\"rounded-full bg-neutral-900 dark:bg-neutral-100\"\n                  animate={{\n                    width: isSelected ? 12 : 0,\n                    height: 6,\n                    opacity: isSelected ? 1 : 0,\n                  }}\n                  transition={{\n                    type: \"spring\",\n                    stiffness: 320,\n                    damping: 26,\n                  }}\n                />\n              </div>\n            </motion.button>\n          );\n        })}\n      </div>\n\n    </div>\n  );\n};\n\nexport default ActivityWheelMotion;\n",
      "type": "registry:component",
      "target": "components/pixel-perfect/activity-wheel-motion.tsx"
    }
  ],
  "type": "registry:block"
}