{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "orbiting-circles",
  "title": "Orbiting Circles",
  "description": "Small things travelling around a centre: one orbit, several at different speeds, a tilted ellipse that passes behind or an arch at the foot of a hero. Focus and pointing stop it.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/miseui/ui/orbiting-circles.tsx",
      "content": "import type * as React from \"react\"\r\nimport { useMemo, useRef, useState } from \"react\"\r\nimport { useBackgroundPlayback } from \"@/registry/miseui/ui/background-loop\"\r\nimport { useOrbitLoop } from \"@/registry/miseui/ui/orbiting-circles-loop\"\r\nimport { labelPlacement, orbitPose, orbitShape, orbitSlots, poseTransform, type LabelSide, type OrbitVariant } from \"@/registry/miseui/ui/orbiting-circles-math\"\r\nimport { cn } from \"@/registry/miseui/lib/utils\"\r\n\r\ntype OrbitingCirclesEffect = \"hover\"\r\n\r\ninterface OrbitItem {\r\n  id: string\r\n  /** Names the item for assistive tech, and is what `hover` shows. */\r\n  label: string\r\n  icon: React.ReactNode\r\n  /** Makes it a link. A link stops the orbit under a pointer and under focus, with or without the effect. */\r\n  href?: string\r\n}\r\n\r\ntype OrbitingCirclesProps = Omit<React.ComponentProps<\"div\">, \"ref\" | \"children\"> & {\r\n  /** `ring` one orbit, `rings` two or three at different speeds, `tilted` an ellipse where items pass behind the centre, `arc` an arch rising from the bottom edge. */\r\n  variant?: OrbitVariant\r\n  /** `hover` stops the orbit on pointing at or focusing any item and shows its label. */\r\n  effect?: OrbitingCirclesEffect\r\n  items: readonly OrbitItem[]\r\n  /** What everything goes around. */\r\n  center?: React.ReactNode\r\n  /** One lap of the innermost orbit. */\r\n  seconds?: number\r\n  /** Holds the motion. Wire it to a control: motion past five seconds needs a way to stop it. */\r\n  paused?: boolean\r\n  /** Names the whole group for assistive tech. */\r\n  label?: string\r\n}\r\n\r\nconst ROOT_SHAPE: Record<OrbitVariant, string> = {\r\n  ring: \"aspect-square\",\r\n  rings: \"aspect-square\",\r\n  tilted: \"aspect-[16/10]\",\r\n  arc: \"aspect-[2/1] overflow-clip\",\r\n}\r\nconst ITEM_CLASS =\r\n  \"flex size-10 items-center justify-center rounded-full border bg-background text-foreground shadow-sm outline-none transition-colors duration-(--duration-fast) focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-4 [&_svg]:shrink-0\"\r\nconst LABEL_SIDE: Record<LabelSide, string> = { start: \"left-0\", center: \"left-1/2 -translate-x-1/2\", end: \"right-0\" }\r\n\r\nfunction OrbitLines({ variant }: { variant: OrbitVariant }) {\r\n  const { radii, squash, centerTop } = orbitShape(variant)\r\n  if (variant === \"tilted\") {\r\n    // Two halves so the near half can pass in front of the centre and the far half behind it.\r\n    const radius = radii[0] as number\r\n    return (\r\n      <>\r\n        {([[\"M -50 0 A 50 50 0 0 1 50 0\", 1], [\"M 50 0 A 50 50 0 0 1 -50 0\", 3]] as const).map(([path, layer]) => (\r\n          <svg\r\n            key={layer}\r\n            aria-hidden=\"true\"\r\n            data-slot=\"orbiting-circles-line\"\r\n            viewBox=\"-50 -50 100 100\"\r\n            className=\"pointer-events-none absolute overflow-visible text-border\"\r\n            style={{ left: \"50%\", top: \"50%\", width: `${radius * 2}cqw`, height: `${radius * 2 * squash}cqw`, transform: \"translate(-50%, -50%)\", zIndex: layer }}\r\n            preserveAspectRatio=\"none\"\r\n          >\r\n            <path d={path} fill=\"none\" stroke=\"currentColor\" vectorEffect=\"non-scaling-stroke\" />\r\n          </svg>\r\n        ))}\r\n      </>\r\n    )\r\n  }\r\n  return radii.map((radius) => (\r\n    <span\r\n      key={radius}\r\n      aria-hidden=\"true\"\r\n      data-slot=\"orbiting-circles-line\"\r\n      className=\"pointer-events-none absolute rounded-full border\"\r\n      style={{ left: \"50%\", top: `${centerTop}%`, width: `${radius * 2}cqw`, height: `${radius * 2}cqw`, transform: \"translate(-50%, -50%)\" }}\r\n    />\r\n  ))\r\n}\r\n\r\ninterface OrbitLabelProps {\r\n  text: string\r\n  side: LabelSide\r\n  below: boolean\r\n}\r\n\r\nfunction OrbitLabel({ text, side, below }: OrbitLabelProps) {\r\n  return (\r\n    <span\r\n      data-slot=\"orbiting-circles-label\"\r\n      aria-hidden=\"true\"\r\n      className={cn(\r\n        \"pointer-events-none absolute z-10 rounded-md bg-popover px-2 py-1 text-xs font-medium whitespace-nowrap text-popover-foreground shadow-md ring-1 ring-border\",\r\n        LABEL_SIDE[side],\r\n        below ? \"top-full mt-1\" : \"bottom-full mb-1\",\r\n      )}\r\n    >\r\n      {text}\r\n    </span>\r\n  )\r\n}\r\n\r\nfunction OrbitingCircles({ variant = \"ring\", effect, items, center, seconds = 28, paused = false, label, className, ...props }: OrbitingCirclesProps) {\r\n  const rootRef = useRef<HTMLDivElement>(null)\r\n  const nodesRef = useRef<(HTMLElement | null)[]>([])\r\n  const [active, setActive] = useState<{ index: number; side: LabelSide; below: boolean } | null>(null)\r\n  const [focused, setFocused] = useState(false)\r\n  const [pointed, setPointed] = useState(false)\r\n  const { still, playing } = useBackgroundPlayback(rootRef, { paused })\r\n  const showsLabels = effect === \"hover\"\r\n  const control = useOrbitLoop(nodesRef, { variant, count: items.length, seconds, playing, still, caught: focused || pointed })\r\n  const { centerTop } = orbitShape(variant)\r\n  // The first frame is placed by the same helper, in the markup, so a server render already looks finished.\r\n  const firstPoses = useMemo(() => orbitSlots(variant, items.length, false).map((slot) => orbitPose(variant, slot, 0)), [variant, items.length])\r\n\r\n  const showLabel = (index: number) => {\r\n    if (!showsLabels) return\r\n    const { x, y } = control.poseOf(index)\r\n    setActive({ index, ...labelPlacement(x, y, centerTop) })\r\n  }\r\n  const catchItem = (index: number) => {\r\n    setPointed(true)\r\n    showLabel(index)\r\n  }\r\n  // A finger lifts straight after a tap. Focus stayed behind, so the label stays until focus moves on.\r\n  const letGo = () => {\r\n    setPointed(false)\r\n    if (!focused) setActive(null)\r\n  }\r\n\r\n  return (\r\n    <div\r\n      {...props}\r\n      ref={rootRef}\r\n      role=\"group\"\r\n      aria-label={label}\r\n      data-slot=\"orbiting-circles\"\r\n      data-variant={variant}\r\n      data-effect={effect}\r\n      data-playing={still ? undefined : String(playing)}\r\n      className={cn(\"relative isolate w-full min-w-0 [container-type:inline-size]\", ROOT_SHAPE[variant], className)}\r\n    >\r\n      <OrbitLines variant={variant} />\r\n      {center ? (\r\n        <div data-slot=\"orbiting-circles-center\" className={cn(\"absolute left-1/2 z-2 -translate-x-1/2\", variant === \"arc\" ? \"bottom-4\" : \"top-1/2 -translate-y-1/2\")}>\r\n          {center}\r\n        </div>\r\n      ) : null}\r\n      {items.map((item, index) => {\r\n        const pose = firstPoses[index]\r\n        const Tag = item.href ? \"a\" : \"span\"\r\n        const isCatchable = showsLabels || Boolean(item.href)\r\n        return (\r\n          <div\r\n            key={item.id}\r\n            ref={(node) => {\r\n              nodesRef.current[index] = node\r\n            }}\r\n            data-slot=\"orbiting-circles-item\"\r\n            className={cn(\"absolute left-1/2\", variant !== \"tilted\" && \"z-3\")}\r\n            style={{ top: `${centerTop}%`, transform: pose ? poseTransform(pose) : undefined, opacity: pose?.opacity, zIndex: variant === \"tilted\" ? (pose?.behind ? 1 : 3) : undefined }}\r\n            onPointerEnter={isCatchable ? () => catchItem(index) : undefined}\r\n            onPointerLeave={isCatchable ? letGo : undefined}\r\n          >\r\n            <Tag\r\n              {...(item.href ? { href: item.href } : { role: \"img\" })}\r\n              // Its only content is its label, so it takes focus only when the label would otherwise need a mouse.\r\n              tabIndex={!item.href && showsLabels ? 0 : undefined}\r\n              aria-label={item.label}\r\n              className={cn(ITEM_CLASS, variant === \"rings\" && \"size-8 pointer-coarse:size-10\", item.href && \"hover:bg-accent\")}\r\n              onFocus={() => {\r\n                setFocused(true)\r\n                if (variant === \"arc\" && !still) control.bringToTop(index)\r\n                showLabel(index)\r\n              }}\r\n              onBlur={() => {\r\n                setFocused(false)\r\n                setActive(null)\r\n              }}\r\n            >\r\n              {item.icon}\r\n            </Tag>\r\n            {active?.index === index ? <OrbitLabel text={item.label} side={active.side} below={active.below} /> : null}\r\n          </div>\r\n        )\r\n      })}\r\n    </div>\r\n  )\r\n}\r\n\r\nexport { OrbitingCircles, type OrbitingCirclesProps, type OrbitVariant as OrbitingCirclesVariant, type OrbitingCirclesEffect, type OrbitItem }\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/orbiting-circles-loop.ts",
      "content": "import type * as React from \"react\"\nimport { useEffect, useRef } from \"react\"\nimport { orbitPose, orbitSlots, poseTransform, ringVelocity, turnToTop, type OrbitVariant } from \"@/registry/miseui/ui/orbiting-circles-math\"\n\nconst CATCH_EASE_SECONDS = 0.14\nconst BRING_EASE_SECONDS = 0.18\n\ninterface OrbitLoopOptions {\n  variant: OrbitVariant\n  count: number\n  seconds: number\n  /** Running at all: on screen, tab visible, not paused, not reduced motion. */\n  playing: boolean\n  /** Reduced motion: a still frame arranged to be read. */\n  still: boolean\n  /** Someone is on an item: slow to a stop, so the thing they reached for stays under them. */\n  caught: boolean\n}\n\nexport function writePoses(nodes: readonly (HTMLElement | null)[], variant: OrbitVariant, count: number, turns: readonly number[], still: boolean) {\n  const slots = orbitSlots(variant, count, still)\n  nodes.forEach((node, index) => {\n    const slot = slots[index]\n    if (!node || !slot) return\n    const pose = orbitPose(variant, slot, turns[slot.ring] ?? 0)\n    node.style.transform = poseTransform(pose)\n    if (variant !== \"tilted\") return\n    node.style.opacity = pose.opacity.toFixed(3)\n    const layer = pose.behind ? \"1\" : \"3\"\n    // A z-index write dirties paint, so only when the item actually crosses from one side to the other.\n    if (node.style.zIndex !== layer) node.style.zIndex = layer\n  })\n}\n\nexport interface OrbitControl {\n  /** Eases the orbit until `index` sits at the top. Reads the live turn, which only this loop knows. */\n  bringToTop: (index: number) => void\n  /** Current horizontal and vertical offset of an item, for placing its label. */\n  poseOf: (index: number) => { x: number; y: number }\n}\n\n/**\n * Turns the orbit by writing styles directly, one frame loop per component. Each orbit keeps its own turn, and speed\n * eases toward zero when caught, so an item stops under a pointer rather than being snatched away from it.\n */\nexport function useOrbitLoop(nodesRef: React.RefObject<(HTMLElement | null)[]>, { variant, count, seconds, playing, still, caught }: OrbitLoopOptions): OrbitControl {\n  const turnsRef = useRef<number[]>([0, 0, 0])\n  const speedRef = useRef(1)\n  const bringRef = useRef(0)\n\n  useEffect(() => {\n    writePoses(nodesRef.current, variant, count, turnsRef.current, still)\n  }, [nodesRef, variant, count, still])\n\n  useEffect(() => {\n    if (!playing) return\n    let frame = 0\n    let last: number | null = null\n    const tick = (now: number) => {\n      const elapsed = last === null ? 0 : Math.min((now - last) / 1000, 0.05)\n      last = now\n      const aim = caught ? 0 : 1\n      speedRef.current += (aim - speedRef.current) * (1 - Math.exp(-elapsed / CATCH_EASE_SECONDS))\n      const bring = bringRef.current * (1 - Math.exp(-elapsed / BRING_EASE_SECONDS))\n      bringRef.current -= bring\n      turnsRef.current = turnsRef.current.map((turn, ring) => turn + ringVelocity(ring, seconds) * speedRef.current * elapsed + bring)\n      writePoses(nodesRef.current, variant, count, turnsRef.current, still)\n      frame = requestAnimationFrame(tick)\n    }\n    frame = requestAnimationFrame(tick)\n    return () => cancelAnimationFrame(frame)\n  }, [nodesRef, variant, count, seconds, playing, still, caught])\n\n  return {\n    bringToTop: (index) => {\n      const slot = orbitSlots(variant, count, still)[index]\n      if (!slot) return\n      const needed = turnToTop(slot.start, turnsRef.current[slot.ring] ?? 0)\n      if (playing) {\n        bringRef.current = needed\n        return\n      }\n      // Not running, so no frame would carry it over. A focused item must still come into view.\n      turnsRef.current = turnsRef.current.map((turn) => turn + needed)\n      writePoses(nodesRef.current, variant, count, turnsRef.current, still)\n    },\n    poseOf: (index) => {\n      const slot = orbitSlots(variant, count, still)[index]\n      if (!slot) return { x: 0, y: 0 }\n      return orbitPose(variant, slot, turnsRef.current[slot.ring] ?? 0)\n    },\n  }\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/orbiting-circles-math.ts",
      "content": "export type OrbitVariant = \"ring\" | \"rings\" | \"tilted\" | \"arc\"\r\n\r\nexport interface OrbitSlot {\r\n  /** Index of the orbit this item rides, 0 is the innermost. */\r\n  ring: number\r\n  /** Degrees at time zero, measured clockwise from the top. */\r\n  start: number\r\n}\r\n\r\nexport interface OrbitShape {\r\n  /** Radii in cqw, a share of the root's width, so the whole thing scales with its container. */\r\n  radii: readonly number[]\r\n  /** Vertical squash of the orbit, 1 for a true circle. */\r\n  squash: number\r\n  /** Where the centre sits within the root, in percent of its height. */\r\n  centerTop: number\r\n}\r\n\r\nconst SHAPES: Record<OrbitVariant, OrbitShape> = {\r\n  ring: { radii: [38], squash: 1, centerTop: 50 },\r\n  rings: { radii: [17, 29.5, 42], squash: 1, centerTop: 50 },\r\n  tilted: { radii: [40], squash: 0.36, centerTop: 50 },\r\n  arc: { radii: [42], squash: 1, centerTop: 100 },\r\n}\r\n\r\nconst RADIANS = Math.PI / 180\r\n\r\nexport function orbitShape(variant: OrbitVariant) {\r\n  return SHAPES[variant]\r\n}\r\n\r\n/** Inner orbits are short, so they hold fewer items: splitting by the radius keeps the gaps along each orbit about equal. */\r\nexport function assignRings(count: number, radii: readonly number[]) {\r\n  if (radii.length === 1) return Array.from({ length: count }, () => 0)\r\n  const total = radii.reduce((sum, radius) => sum + radius, 0)\r\n  const shares = radii.map((radius) => Math.max(1, Math.round((count * radius) / total)))\r\n  const assigned: number[] = []\r\n  shares.forEach((share, ring) => {\r\n    for (let index = 0; index < share; index += 1) assigned.push(ring)\r\n  })\r\n  // Rounding can land one off: fill the outermost, or drop from it.\r\n  while (assigned.length < count) assigned.push(radii.length - 1)\r\n  return assigned.slice(0, count)\r\n}\r\n\r\n/**\r\n * Even spacing per orbit, with orbits offset from each other so no two items line up along a spoke.\r\n * The arc shows half its circle, so a still frame spreads everything across the top half instead of leaving half out of sight.\r\n */\r\nexport function orbitSlots(variant: OrbitVariant, count: number, still = false): readonly OrbitSlot[] {\r\n  const rings = assignRings(count, SHAPES[variant].radii)\r\n  if (variant === \"arc\" && still) return rings.map((ring, index) => ({ ring, start: -90 + ((index + 0.5) * 180) / Math.max(count, 1) }))\r\n  const totals = rings.reduce<Record<number, number>>((sum, ring) => ({ ...sum, [ring]: (sum[ring] ?? 0) + 1 }), {})\r\n  const seen: Record<number, number> = {}\r\n  return rings.map((ring) => {\r\n    const position = seen[ring] ?? 0\r\n    seen[ring] = position + 1\r\n    return { ring, start: (position * 360) / (totals[ring] as number) + ring * 37 }\r\n  })\r\n}\r\n\r\n/** Further orbits go slower, as planets do, and every other one runs the other way, so the rings read as separate things. */\r\nexport function ringVelocity(ring: number, seconds: number) {\r\n  const direction = ring % 2 === 0 ? 1 : -1\r\n  return (direction * 360) / (seconds * (1 + ring * 0.45))\r\n}\r\n\r\nexport interface OrbitPose {\r\n  /** cqw from the centre. */\r\n  x: number\r\n  y: number\r\n  scale: number\r\n  opacity: number\r\n  /** True on the far half of a tilted orbit: the item passes behind the centre. */\r\n  behind: boolean\r\n}\r\n\r\nconst FAR_SCALE = 0.72\r\nconst FAR_OPACITY = 0.5\r\nconst ARC_FADE_CQW = 12\r\n\r\n/** The box edge would otherwise slice an item in half, and with no visible box around the arc that reads as a broken circle. */\r\nfunction arcOpacity(y: number) {\r\n  const share = Math.min(1, Math.max(0, -y / ARC_FADE_CQW))\r\n  return share * share * (3 - 2 * share)\r\n}\r\n\r\nexport function orbitPose(variant: OrbitVariant, slot: OrbitSlot, turn: number): OrbitPose {\r\n  const shape = SHAPES[variant]\r\n  const radius = shape.radii[slot.ring] as number\r\n  const angle = (slot.start + turn) * RADIANS\r\n  const x = radius * Math.sin(angle)\r\n  const circleY = -Math.cos(angle)\r\n  const y = radius * shape.squash * circleY\r\n  if (variant === \"arc\") return { x, y, scale: 1, opacity: arcOpacity(y), behind: false }\r\n  if (variant !== \"tilted\") return { x, y, scale: 1, opacity: 1, behind: false }\r\n  // Below the centre on screen is nearer the viewer in a ring seen from above.\r\n  const nearness = (circleY + 1) / 2\r\n  return { x, y, scale: FAR_SCALE + (1 - FAR_SCALE) * nearness, opacity: FAR_OPACITY + (1 - FAR_OPACITY) * nearness, behind: circleY < 0 }\r\n}\r\n\r\n/** One short string per item per frame, and none of it re-renders React. */\r\nexport function poseTransform({ x, y, scale }: OrbitPose) {\r\n  const base = `translate(-50%, -50%) translate(${x.toFixed(2)}cqw, ${y.toFixed(2)}cqw)`\r\n  return scale === 1 ? base : `${base} scale(${scale.toFixed(3)})`\r\n}\r\n\r\n/** How far a turn must go for this slot to reach the top of its orbit: the arc hides half its circle, so a focused item is brought into view. */\r\nexport function turnToTop(start: number, turn: number) {\r\n  const current = ((start + turn) % 360 + 360) % 360\r\n  return current > 180 ? 360 - current : -current\r\n}\r\n\r\nexport type LabelSide = \"start\" | \"center\" | \"end\"\r\n\r\n/** A label near an edge grows inward and on the side away from the rim, so it stays inside the root in the narrowest cell. */\r\nexport function labelPlacement(x: number, y: number, top: number): { side: LabelSide; below: boolean } {\r\n  const side: LabelSide = x > 12 ? \"end\" : x < -12 ? \"start\" : \"center\"\r\n  return { side, below: top >= 100 ? false : y < 0 }\r\n}\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/background-loop.ts",
      "content": "import type * as React from \"react\"\nimport { useEffect, useState, useSyncExternalStore } from \"react\"\nimport { useReducedMotion } from \"motion/react\"\n\n/**\n * The accent as a light source, held in a lightness band per theme. A near-black accent would read as smoke on a light\n * page and a dark blue would pull muted text under 4.5:1 where a beam crosses it, so the band's floor is 0.6.\n * Older browsers keep plain --mise, which is only a stronger tint.\n */\nexport const LIGHT_TOKEN_CLASS = [\n  \"[--mise-light:var(--mise)]\",\n  \"supports-[color:oklch(from_red_l_c_h)]:[--mise-light:oklch(from_var(--mise)_clamp(0.6,l,0.76)_c_h)]\",\n  \"dark:supports-[color:oklch(from_red_l_c_h)]:[--mise-light:oklch(from_var(--mise)_clamp(0.62,l,0.82)_c_h)]\",\n].join(\" \")\n\n/** Before hydration the hook cannot know the preference yet. A paused animation holds its seeded frame. */\nexport const LOOP_CLASS = \"motion-reduce:[animation-play-state:paused]!\"\n\nconst subscribeToVisibility = (onChange: () => void) => {\n  document.addEventListener(\"visibilitychange\", onChange)\n  return () => document.removeEventListener(\"visibilitychange\", onChange)\n}\n\nfunction useTabVisible() {\n  return useSyncExternalStore(\n    subscribeToVisibility,\n    () => !document.hidden,\n    () => true,\n  )\n}\n\nfunction useOnScreen(rootRef: React.RefObject<HTMLElement | null>, watching: boolean) {\n  const [onScreen, setOnScreen] = useState(true)\n  useEffect(() => {\n    const root = rootRef.current\n    if (!watching || !root || typeof IntersectionObserver === \"undefined\") return\n    const view = new IntersectionObserver(([entry]) => setOnScreen(entry?.isIntersecting ?? true))\n    view.observe(root)\n    return () => view.disconnect()\n  }, [rootRef, watching])\n  return onScreen\n}\n\ninterface PlaybackOptions {\n  /** The owner's stop control. */\n  paused?: boolean\n  /** False when this look never moves, so nothing is watched at all. */\n  loops?: boolean\n}\n\n/** `still` draws one finished frame. `playing` is only true on screen, in a visible tab and not paused. */\nexport function useBackgroundPlayback(rootRef: React.RefObject<HTMLElement | null>, { paused = false, loops = true }: PlaybackOptions) {\n  const reduceMotion = useReducedMotion() ?? false\n  const moving = loops && !reduceMotion\n  const onScreen = useOnScreen(rootRef, moving)\n  const tabVisible = useTabVisible()\n  return { still: !moving, playing: moving && !paused && onScreen && tabVisible }\n}\n\n/** Longhands, because the shorthand would reset the play state each element reads from the layer. */\nexport function loopStyle(name: string, seconds: number, delay: number, timing = \"linear\", direction = \"normal\"): React.CSSProperties {\n  return {\n    animationName: name,\n    animationDuration: `${seconds}s`,\n    animationDelay: `${delay}s`,\n    animationTimingFunction: timing,\n    animationDirection: direction,\n    animationIterationCount: \"infinite\",\n    animationPlayState: \"var(--mise-bg-play)\",\n  }\n}\n\n/** Set once on the layer, so pausing writes one property however many elements are moving. */\nexport function playStyle(playing: boolean) {\n  return { \"--mise-bg-play\": playing ? \"running\" : \"paused\" } as React.CSSProperties\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "interactions"
  ],
  "type": "registry:ui"
}