{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "halo-toggle-group",
  "type": "registry:ui",
  "description": "Segmented single-select control with frosted track, gradient rim, and spring-driven sliding thumb (aligned with Halo Button)",
  "dependencies": [
    "@base-ui/react",
    "motion"
  ],
  "files": [
    {
      "path": "registry/default/ui/halo-toggle-group.tsx",
      "content": "\"use client\"\n\n/**\n * Segmented control (single-select toggle group) aligned with `HaloButtonPolished`:\n * frosted track, gradient rim, sliding thumb using the same layout spring as the button.\n */\nimport {\n  useCallback,\n  useLayoutEffect,\n  useMemo,\n  useRef,\n  useState,\n  type ReactNode,\n} from \"react\"\nimport { Toggle } from \"@base-ui/react/toggle\"\nimport { ToggleGroup } from \"@base-ui/react/toggle-group\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/** Matches `layoutSpring` in `halo-button-polished.tsx`. */\nconst layoutSpring = {\n  type: \"spring\" as const,\n  bounce: 0.12,\n  damping: 34,\n  stiffness: 420,\n}\n\nexport interface HaloToggleGroupItem {\n  value: string\n  label: ReactNode\n  disabled?: boolean\n}\n\nexport interface HaloToggleGroupProps {\n  items: HaloToggleGroupItem[]\n  /** Selected value; omit for uncontrolled usage with `defaultValue`. */\n  value?: string\n  defaultValue?: string\n  onValueChange?: (next: string) => void\n  className?: string\n  /** Frosted track + backdrop blur (matches card/button translucent mode). */\n  translucent?: boolean\n  disabled?: boolean\n}\n\nexport function HaloToggleGroup({\n  items,\n  value: valueProp,\n  defaultValue,\n  onValueChange,\n  className,\n  translucent = true,\n  disabled = false,\n}: HaloToggleGroupProps) {\n  const reduceMotion = useReducedMotion() ?? false\n  const trackRef = useRef<HTMLDivElement>(null)\n  const itemRefs = useRef<Map<string, HTMLButtonElement>>(new Map())\n  const [thumb, setThumb] = useState({ x: 0, width: 0 })\n\n  const isControlled = valueProp !== undefined\n  const [uncontrolled, setUncontrolled] = useState(\n    defaultValue ?? items[0]?.value ?? \"\"\n  )\n  const selected = isControlled ? (valueProp ?? \"\") : uncontrolled\n\n  const setSelected = useCallback(\n    (next: string) => {\n      if (!isControlled) {\n        setUncontrolled(next)\n      }\n      onValueChange?.(next)\n    },\n    [isControlled, onValueChange]\n  )\n\n  const groupValue = useMemo(() => (selected ? [selected] : []), [selected])\n\n  const measureThumb = useCallback(() => {\n    const track = trackRef.current\n    const active = selected ? itemRefs.current.get(selected) : undefined\n    if (!(track && active)) {\n      setThumb({ x: 0, width: 0 })\n      return\n    }\n    const tr = track.getBoundingClientRect()\n    const br = active.getBoundingClientRect()\n    setThumb({\n      x: br.left - tr.left,\n      width: br.width,\n    })\n  }, [selected])\n\n  useLayoutEffect(() => {\n    measureThumb()\n  }, [measureThumb])\n\n  useLayoutEffect(() => {\n    const track = trackRef.current\n    if (!track || typeof ResizeObserver === \"undefined\") {\n      return\n    }\n    const ro = new ResizeObserver(() => measureThumb())\n    ro.observe(track)\n    return () => ro.disconnect()\n  }, [measureThumb])\n\n  return (\n    <div\n      className={cn(\n        \"relative inline-flex max-w-full rounded-full p-px\",\n        className\n      )}\n    >\n      <div className=\"pointer-events-none absolute inset-0 overflow-hidden rounded-full\">\n        <motion.div\n          animate={{\n            opacity: translucent ? 0.72 : 0.5,\n          }}\n          className=\"absolute inset-x-0 bottom-0 h-1/2\"\n          transition={{ duration: 0.25, ease: \"easeOut\" }}\n        >\n          <motion.div\n            animate={{ left: [\"-5%\", \"75%\", \"-5%\"] }}\n            className=\"-bottom-4 absolute h-14 w-44 blur-xl\"\n            style={{\n              background:\n                \"linear-gradient(90deg, #ff0080, #7928ca, #00d4ff, #0070f3)\",\n            }}\n            transition={{\n              duration: reduceMotion ? 0.01 : 6,\n              ease: \"easeInOut\",\n              repeat: reduceMotion ? 0 : Number.POSITIVE_INFINITY,\n            }}\n          />\n          <motion.div\n            animate={{ left: [\"65%\", \"10%\", \"65%\"] }}\n            className=\"-bottom-3 absolute h-11 w-36 blur-lg\"\n            style={{\n              background: \"linear-gradient(90deg, #ff4d4d, #f9cb28, #ff0080)\",\n            }}\n            transition={{\n              duration: reduceMotion ? 0.01 : 5,\n              ease: \"easeInOut\",\n              repeat: reduceMotion ? 0 : Number.POSITIVE_INFINITY,\n            }}\n          />\n        </motion.div>\n      </div>\n\n      <div\n        className={cn(\n          \"relative rounded-full border border-border/90\",\n          \"shadow-[0_1px_0_rgba(255,255,255,0.1)_inset,0_1px_2px_rgba(0,0,0,0.04),0_4px_14px_rgba(0,0,0,0.05)]\",\n          \"dark:border-border dark:shadow-[0_1px_0_rgba(255,255,255,0.06)_inset,0_1px_2px_rgba(0,0,0,0.35),0_4px_20px_rgba(0,0,0,0.35)]\",\n          translucent && \"backdrop-blur-xl backdrop-saturate-150\"\n        )}\n        style={{\n          background: translucent\n            ? \"linear-gradient(to bottom, color-mix(in oklch, var(--card) 82%, transparent) 0%, color-mix(in oklch, var(--card) 70%, transparent) 100%)\"\n            : \"linear-gradient(to bottom, var(--card) 0%, color-mix(in oklch, var(--card) 94%, var(--muted)) 100%)\",\n        }}\n      >\n        <div className=\"relative p-1\" ref={trackRef}>\n          <motion.div\n            animate={{\n              x: thumb.width > 0 ? thumb.x : 0,\n              width: thumb.width,\n            }}\n            aria-hidden\n            className={cn(\n              \"pointer-events-none absolute top-1 right-auto bottom-1 left-0 rounded-full\",\n              \"border border-border/70\",\n              \"bg-gradient-to-b from-background/95 to-muted/40\",\n              \"shadow-[0_1px_0_rgba(255,255,255,0.12)_inset,0_2px_8px_rgba(0,0,0,0.06)]\",\n              \"dark:border-white/10 dark:from-card/95 dark:to-muted/25\",\n              \"dark:shadow-[0_1px_0_rgba(255,255,255,0.06)_inset,0_4px_12px_rgba(0,0,0,0.35)]\"\n            )}\n            initial={false}\n            transition={reduceMotion ? { duration: 0 } : layoutSpring}\n          />\n\n          <ToggleGroup\n            className=\"relative z-1 flex min-w-0 gap-0\"\n            disabled={disabled}\n            multiple={false}\n            onValueChange={(next) => {\n              const first = next[0]\n              if (typeof first === \"string\") {\n                setSelected(first)\n              }\n            }}\n            value={groupValue}\n          >\n            {items.map((item) => (\n              <Toggle\n                className={cn(\n                  \"relative flex min-h-9 min-w-0 flex-1 items-center justify-center rounded-full px-4 py-2\",\n                  \"font-medium text-muted-foreground text-sm tracking-tight antialiased\",\n                  \"outline-none transition-colors duration-200 ease-out\",\n                  \"focus-visible:ring-2 focus-visible:ring-ring/35 focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n                  \"disabled:pointer-events-none disabled:opacity-50\",\n                  \"aria-pressed:text-foreground\"\n                )}\n                disabled={item.disabled}\n                key={item.value}\n                ref={(el) => {\n                  if (el) {\n                    itemRefs.current.set(item.value, el)\n                  } else {\n                    itemRefs.current.delete(item.value)\n                  }\n                }}\n                value={item.value}\n              >\n                {item.label}\n              </Toggle>\n            ))}\n          </ToggleGroup>\n        </div>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ]
}