{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "xray-lens",
  "type": "registry:ui",
  "title": "Xray Lens",
  "description": "A loupe over a polished UI. Inside the glass the surface peels away to the layer underneath, such as model tokens, raw JSON or latency telemetry, lined up with the parts it explains. A switch floods it over the whole card.",
  "author": "KeyframeUI <https://keyframeui.com>",
  "categories": [
    "hover",
    "interaction",
    "developer"
  ],
  "docs": "Live preview, examples and props: https://keyframeui.com/components/xray-lens",
  "dependencies": [
    "motion",
    "clsx",
    "tailwind-merge"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "components/ui/xray-lens.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport {\n  animate,\n  cancelFrame,\n  frame,\n  useMotionValue,\n  useSpring,\n  type AnimationPlaybackControls,\n  type MotionValue,\n} from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Keep:\n * - Nothing re-renders as the lens moves: every motion value change schedules one `frame.render`\n *   write that sets the root's `--lens-*` CSS variables, and the layers read only those.\n * - The xray layer is the surface's own box (`aria-hidden`, `inert`), cut to a circle by a\n *   radial-gradient mask and scaled about the lens center; each spot's xray is painted at its\n *   element's measured rect, so the two layers line up.\n * - Moves go to the source values `px`/`py` and the glass trails them on the follow spring; glides\n *   animate that source, and `place()` stops a running glide before any new move.\n * - The flood grows the mask from the lens past the farthest corner while the magnification\n *   settles to 1 and the glass fades; once mostly covered the glass is hidden, so it can't be\n *   grabbed or focused.\n * - Reduced motion reads `px`/`py` directly (no lag), jumps the radius, lift and flood instead of\n *   animating them, and never starts the tour.\n */\nconst TIMING = {\n  /** The glass trailing the pointer, a spring; the `stiffness` and `damping` props. */\n  follow: { stiffness: 420, damping: 38, mass: 0.7 },\n  /** Back to rest, to a tap, between tour stops; the `glideDuration` prop, in seconds. */\n  glide: { type: \"spring\", visualDuration: 0.6, bounce: 0.06 },\n  /** The lens growing open. */\n  open: { type: \"spring\", visualDuration: 0.45, bounce: 0.22 },\n  /** The lens shrinking closed. */\n  close: { type: \"spring\", visualDuration: 0.25, bounce: 0 },\n  /** The lens lifting while it's held, and settling when let go. */\n  lift: { type: \"spring\", visualDuration: 0.3, bounce: 0.3 },\n  /** The reveal across the whole frame; the `revealDuration` prop, in seconds. */\n  flood: { duration: 0.85, ease: \"easeInOut\" },\n  /** A new label's text rising into place, in seconds. */\n  labelText: { duration: 0.18, ease: \"easeOut\" },\n  /** The label fading in and out under the glass, as a Tailwind class. */\n  labelFade: \"duration-200\",\n  /** The switch's thumb sliding between sides, as Tailwind classes. */\n  thumb: \"duration-300 ease-[cubic-bezier(0.2,0.8,0.2,1)]\",\n  /** The switch's labels changing color, as a Tailwind class. */\n  switchColor: \"duration-200\",\n  /** From one tour stop to the next, glide included, in ms. */\n  dwell: 2200,\n  /** After the frame comes into view, before the tour starts, in ms. */\n  tourStart: 900,\n  /** After a finger lifts, a drag ends or focus leaves, before the tour resumes, in ms. */\n  resume: 1800,\n  /** After the pointer leaves, with autoPlay={true}, before the tour resumes, in ms. */\n  resumeMouse: 700,\n} as const;\n\n/* ─────────────────────────────────────────────────────────\n * XRAY LENS STORYBOARD\n *\n *  rest     the surface fills the frame. The xray waits under it,\n *           in the same box, so every part lines up with the part\n *           it explains\n *  intro    the first time the frame scrolls into view, the loupe\n *           opens at `rest`: it grows in on a soft spring and the\n *           magnification swells from 1 to `zoom` as it opens\n *  hover    the loupe flies to the pointer and trails it on a\n *           stiff spring, so it moves like a piece of glass. Inside\n *           it the surface is gone and the xray shows through,\n *           magnified around the lens's center\n *  glint    the highlight on the glass slides against the motion\n *  label    when the lens centers on a spot, its label hangs\n *           under the glass\n *  leave    the loupe glides back to `rest`, or closes with\n *           `rest={false}`\n *  tour     on touch screens (or everywhere, with autoPlay) the\n *           loupe visits each labeled spot in reading order while\n *           the frame is in view, pausing on each. A finger, the\n *           pointer or focus takes over; the tour picks up again\n *           from the nearest spot once they let go\n *  drag     with mode=\"drag\" (and on touch screens) the loupe stays\n *           put until it's picked up. Held, it lifts: it grows a\n *           little, magnifies a little more and its shadow drops.\n *           A tap anywhere else glides it there\n *  reveal   the switch floods the xray across the whole frame from\n *           the switch, or from the lens if it's open: the glass\n *           swells and fades as the circle grows past the corners,\n *           and the magnification settles to 1 so everything lines\n *           up. Switching back pulls the flood in to the lens\n *  keys     the lens is focusable; arrow keys move it, Shift for\n *           bigger steps, Home sends it back to `rest`\n *  reduced  the lens follows the pointer with no lag, opens without\n *           growing and never tours; the switch flips at once\n * ───────────────────────────────────────────────────────── */\n\nconst LENS = {\n  liftGrow: 0.05, // the lens grows by this much while it's held\n  liftZoom: 0.05, // and magnifies this much more\n  liftDrop: 10, // px its shadow drops\n  glint: 0.004, // px of glint travel per px/s of speed\n  glintMax: 8,\n  step: 16, // px per arrow key\n  stepLarge: 64, // with Shift\n  labelGap: 10, // px between the glass and its label\n  minSize: 24,\n} as const;\n\n// Where the tour wanders when no spot has a label, as fractions of the frame.\nconst WANDER: [number, number][] = [\n  [0.3, 0.35],\n  [0.7, 0.3],\n  [0.66, 0.7],\n  [0.32, 0.66],\n];\n\nconst clamp = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v));\n\n/** The lens's diameter in px: px as given, or a percentage of the frame's width, never wider than the frame. */\nfunction lensSize(size: number | string, w: number, h: number) {\n  const text = String(size).trim();\n  const px = typeof size === \"number\" ? size : text.endsWith(\"%\") ? (parseFloat(text) / 100) * w : parseFloat(text);\n  const want = Number.isFinite(px) ? px : 200;\n  const most = Math.min(w, h) * 0.9;\n  return most > 0 ? clamp(want, LENS.minSize, Math.max(LENS.minSize, most)) : want;\n}\n\n/* ── Spots ──────────────────────────────────────────────── */\n\ntype Rect = { x: number; y: number; w: number; h: number };\ntype SpotEntry = { id: string; xray: React.ReactNode; className?: string; label?: string; depth: number };\ntype SpotTag = \"div\" | \"span\" | \"p\" | \"li\" | \"blockquote\";\n\n/**\n * Spots live in the surface but draw in the xray layer. Each one measures its\n * own element and hands the box to this store; the xray layer renders a box\n * per spot, and the lens reads the labels from here.\n */\nfunction createSpots() {\n  let root: HTMLDivElement | null = null;\n  let list: SpotEntry[] = [];\n  const empty: SpotEntry[] = [];\n  const rects = new Map<string, Rect & { radius: string }>();\n  const boxes = new Map<string, HTMLDivElement>();\n  const measurers = new Set<() => void>();\n  const listeners = new Set<() => void>();\n\n  const paint = (id: string) => {\n    const box = boxes.get(id);\n    const rect = rects.get(id);\n    if (!box || !rect) return;\n    box.style.transform = `translate(${rect.x}px, ${rect.y}px)`;\n    box.style.width = `${rect.w}px`;\n    box.style.height = `${rect.h}px`;\n    box.style.borderRadius = rect.radius;\n  };\n\n  return {\n    connect(el: HTMLDivElement | null) {\n      root = el;\n      measurers.forEach((measure) => measure());\n    },\n    measureAll() {\n      measurers.forEach((measure) => measure());\n    },\n    /** A box in root-local px, with any CSS scale on the way divided out. */\n    measure(id: string, el: HTMLElement) {\n      if (!root) return;\n      const frameRect = root.getBoundingClientRect();\n      const kx = root.offsetWidth ? frameRect.width / root.offsetWidth : 1;\n      const ky = root.offsetHeight ? frameRect.height / root.offsetHeight : 1;\n      const r = el.getBoundingClientRect();\n      // The spot's wrapper is usually square; take the corners from what it wraps.\n      let radius = getComputedStyle(el).borderRadius;\n      if ((!radius || radius === \"0px\") && el.firstElementChild) {\n        radius = getComputedStyle(el.firstElementChild).borderRadius;\n      }\n      rects.set(id, {\n        x: (r.left - frameRect.left) / kx - root.clientLeft,\n        y: (r.top - frameRect.top) / ky - root.clientTop,\n        w: r.width / kx,\n        h: r.height / ky,\n        radius,\n      });\n      paint(id);\n    },\n    upsert(entry: SpotEntry) {\n      const i = list.findIndex((s) => s.id === entry.id);\n      const prev = list[i];\n      if (\n        prev &&\n        prev.xray === entry.xray &&\n        prev.className === entry.className &&\n        prev.label === entry.label &&\n        prev.depth === entry.depth\n      ) {\n        return;\n      }\n      list = i === -1 ? [...list, entry] : list.map((s, j) => (j === i ? entry : s));\n      listeners.forEach((l) => l());\n    },\n    remove(id: string) {\n      rects.delete(id);\n      list = list.filter((s) => s.id !== id);\n      listeners.forEach((l) => l());\n    },\n    addMeasurer(fn: () => void) {\n      measurers.add(fn);\n      return () => {\n        measurers.delete(fn);\n      };\n    },\n    attachBox(id: string, el: HTMLDivElement | null) {\n      if (el) {\n        boxes.set(id, el);\n        paint(id);\n      } else {\n        boxes.delete(id);\n      }\n    },\n    /** The label of the smallest spot that contains a point. */\n    labelAt(x: number, y: number) {\n      let best: string | undefined;\n      let area = Infinity;\n      for (const s of list) {\n        const r = rects.get(s.id);\n        if (!s.label || !r) continue;\n        if (x < r.x || y < r.y || x > r.x + r.w || y > r.y + r.h) continue;\n        if (r.w * r.h < area) {\n          area = r.w * r.h;\n          best = s.label;\n        }\n      }\n      return best;\n    },\n    /** The centers of the labeled spots, in reading order. */\n    stops() {\n      const points: { x: number; y: number }[] = [];\n      for (const s of list) {\n        const r = rects.get(s.id);\n        if (s.label && r) points.push({ x: r.x + r.w / 2, y: r.y + r.h / 2 });\n      }\n      return points.sort((a, b) => Math.round(a.y / 24) - Math.round(b.y / 24) || a.x - b.x);\n    },\n    subscribe(listener: () => void) {\n      listeners.add(listener);\n      return () => {\n        listeners.delete(listener);\n      };\n    },\n    snapshot: () => list,\n    serverSnapshot: () => empty,\n  };\n}\n\ntype Spots = ReturnType<typeof createSpots>;\n\nconst XrayLensContext = React.createContext<Spots | null>(null);\n/** How many spots wrap this one, so a nested spot draws over its parent. */\nconst SpotDepth = React.createContext(0);\n\n/** Draws a box in the xray layer for every spot in the surface. */\nfunction SpotLayer({ spots }: { spots: Spots }) {\n  const list = React.useSyncExternalStore(spots.subscribe, spots.snapshot, spots.serverSnapshot);\n  return list.map((spot) => (\n    <div\n      key={spot.id}\n      ref={(el) => {\n        spots.attachBox(spot.id, el);\n        return () => spots.attachBox(spot.id, null);\n      }}\n      className={cn(\"absolute top-0 left-0\", spot.className)}\n      style={{ zIndex: spot.depth }}\n    >\n      {spot.xray}\n    </div>\n  ));\n}\n\nexport interface XrayLensSpotProps extends React.HTMLAttributes<HTMLElement> {\n  /** What this element looks like under the lens. Drawn in the xray layer over the element's exact box. */\n  xray: React.ReactNode;\n  /** Named under the glass while the lens is centered on this element, and a stop on the tour. */\n  label?: string;\n  /** The wrapper element. Use `\"span\"` inside a paragraph. */\n  as?: SpotTag;\n  /** Classes for the spot's box in the xray layer. It gets the element's size and corner radius. */\n  xrayClassName?: string;\n}\n\n/**\n * Marks part of the surface and gives it an xray of its own, laid over the\n * element's box so the two line up wherever the layout puts it.\n */\nexport function XrayLensSpot({\n  xray,\n  label,\n  as = \"div\",\n  xrayClassName,\n  children,\n  ...props\n}: XrayLensSpotProps) {\n  const spots = React.useContext(XrayLensContext);\n  const depth = React.useContext(SpotDepth);\n  const id = React.useId();\n  const ref = React.useRef<HTMLElement>(null);\n\n  React.useLayoutEffect(() => {\n    spots?.upsert({ id, xray, className: xrayClassName, label, depth });\n  });\n\n  React.useLayoutEffect(() => {\n    const el = ref.current;\n    if (!spots || !el) return;\n    const measure = () => spots.measure(id, el);\n    const sized = new ResizeObserver(measure);\n    sized.observe(el);\n    const stop = spots.addMeasurer(measure);\n    measure();\n    return () => {\n      sized.disconnect();\n      stop();\n      spots.remove(id);\n    };\n  }, [spots, id]);\n\n  return React.createElement(\n    as,\n    { ref, ...props },\n    <SpotDepth.Provider value={depth + 1}>{children}</SpotDepth.Provider>,\n  );\n}\n\n/* ── Text as tokens ─────────────────────────────────────── */\n\n/** A token as your model returned it, with its probability (`p`, 0–1) or classes of its own. A `label` makes it a spot. */\nexport type XrayLensToken = string | { text: string; p?: number; label?: string; className?: string };\n/** A phrase to mark in the text, e.g. an entity the model extracted. */\nexport type XrayLensEntity = { text: string; label: string };\n\ntype Token = { text: string; p?: number; label?: string; className?: string };\n\nconst ALTERNATE = [\"bg-violet-400/25 text-violet-100\", \"bg-sky-400/20 text-sky-100\"];\nconst ENTITY_BOX = \"outline-1 -outline-offset-1 outline-dashed outline-amber-300/80 bg-amber-300/10\";\n// The copy sits in a box the size of the text's border box, so it drops the margin and fills the box.\nconst COPY_STYLE: React.CSSProperties = { margin: 0, width: \"100%\", maxWidth: \"none\", background: \"none\" };\n\nconst confidence = (p: number) =>\n  p >= 0.85\n    ? \"bg-emerald-400/15 text-emerald-100\"\n    : p >= 0.5\n      ? \"bg-amber-400/30 text-amber-100\"\n      : \"bg-rose-500/35 text-rose-100\";\n\n/** Splits text roughly the way a tokenizer does: words keep their leading space, long words break in two. */\nfunction tokenize(text: string) {\n  return (text.match(/\\s?[\\w']+|\\s?[^\\w\\s]+|\\s+/g) ?? []).flatMap((part) => {\n    const word = part.trim();\n    if (word.length < 8 || !/^\\w+$/.test(word)) return [part];\n    const cut = part.length - Math.ceil(word.length / 2);\n    return [part.slice(0, cut), part.slice(cut)];\n  });\n}\n\n/** The text as tokens: `tokens` when they spell out `text`, otherwise split here, with each entity kept whole. */\nfunction toTokens(text: string | undefined, tokens: XrayLensToken[] | undefined, entities: XrayLensEntity[] | undefined) {\n  const given = tokens?.map((t): Token => (typeof t === \"string\" ? { text: t } : t));\n  if (given && (text === undefined || given.map((t) => t.text).join(\"\") === text)) return given;\n  const out: Token[] = [];\n  let rest = text ?? \"\";\n  for (const entity of entities ?? []) {\n    const at = rest.indexOf(entity.text);\n    if (at === -1 || !entity.text) continue;\n    out.push(...tokenize(rest.slice(0, at)).map((t) => ({ text: t })), { text: entity.text, label: entity.label });\n    rest = rest.slice(at + entity.text.length);\n  }\n  out.push(...tokenize(rest).map((t) => ({ text: t })));\n  return out;\n}\n\nexport interface XrayLensTextProps extends Omit<React.HTMLAttributes<HTMLElement>, \"children\"> {\n  /** The text, as the surface shows it. */\n  text?: string;\n  /** The model's tokens. If they don't spell out `text`, the text is split roughly instead. */\n  tokens?: XrayLensToken[];\n  /** Phrases to outline and label, e.g. extracted entities. Used when the text is split here. */\n  entities?: XrayLensEntity[];\n  /** Named under the glass. Defaults to the token count. */\n  label?: string;\n  /** The text's element. */\n  as?: SpotTag;\n  /** Classes for the token copy's box in the xray layer. */\n  xrayClassName?: string;\n}\n\n/**\n * Text that turns into its tokens under the lens. The xray copy keeps the\n * text's classes, so it wraps exactly like the surface and each word sits\n * over itself.\n */\nexport function XrayLensText({\n  text,\n  tokens,\n  entities,\n  label,\n  as = \"p\",\n  className,\n  xrayClassName,\n  ...props\n}: XrayLensTextProps) {\n  const parts = toTokens(text, tokens, entities);\n\n  // The surface: plain text, with labeled tokens as spots of their own.\n  const surface: React.ReactNode[] = [];\n  let run = \"\";\n  parts.forEach((t, i) => {\n    if (!t.label) {\n      run += t.text;\n      return;\n    }\n    if (run) surface.push(run);\n    run = \"\";\n    surface.push(\n      <XrayLensSpot key={i} as=\"span\" label={t.label} xray={null} xrayClassName={ENTITY_BOX}>\n        {t.text}\n      </XrayLensSpot>,\n    );\n  });\n  if (run) surface.push(run);\n\n  const copy = React.createElement(\n    as,\n    { className, style: COPY_STYLE },\n    parts.map((t, i) => (\n      <span key={i} className={t.className ?? (t.p !== undefined ? confidence(t.p) : ALTERNATE[i % 2])}>\n        {t.text}\n      </span>\n    )),\n  );\n\n  return (\n    <XrayLensSpot\n      as={as}\n      label={label ?? `${parts.length} tokens`}\n      xray={copy}\n      xrayClassName={xrayClassName}\n      className={className}\n      {...props}\n    >\n      {surface}\n    </XrayLensSpot>\n  );\n}\n\n/* ── The lens ───────────────────────────────────────────── */\n\ntype Options = {\n  mode: \"hover\" | \"drag\";\n  size: number | string;\n  zoom: number;\n  rest: [number, number] | null;\n  autoPlay: boolean | \"touch\";\n  /** Seconds, the spring that glides the lens back to rest, to a tap and between tour stops. */\n  glide: number;\n  /** Seconds, the flood across the whole frame. */\n  flood: number;\n  reduced: boolean;\n};\n\ntype Values = {\n  px: MotionValue<number>;\n  py: MotionValue<number>;\n  sx: MotionValue<number>;\n  sy: MotionValue<number>;\n  radius: MotionValue<number>;\n  lift: MotionValue<number>;\n  flood: MotionValue<number>;\n};\n\nconst INTERACTIVE = \"a,button,input,select,textarea,label,summary,[role=button],[contenteditable=true]\";\n\n/**\n * Everything per frame lives here, outside React: where the lens is headed,\n * the springs that take it there, the tour and the CSS variables they write.\n */\nfunction createLens(\n  els: { root: HTMLDivElement; glass: HTMLDivElement; label: HTMLDivElement; labelText: HTMLSpanElement },\n  values: Values,\n  spots: Spots,\n  initial: Options,\n  initiallyRevealed: boolean,\n) {\n  const { root, glass, label, labelText } = els;\n  const { px, py, sx, sy, radius, lift, flood } = values;\n  let opts = initial;\n\n  let w = root.clientWidth;\n  let h = root.clientHeight;\n  let d = lensSize(opts.size, w, h);\n  let want = false; // whether the lens is open, or opening\n  let atRest = true; // parked at `rest`, so it follows `rest` through resizes\n  let hovering = false;\n  let focused = false;\n  let revealed = initiallyRevealed;\n  let inView = false;\n  let introduced = false;\n  let drag: { id: number; dx: number; dy: number } | null = null;\n  let press: { id: number; x: number; y: number } | null = null;\n  const client = { x: 0, y: 0 };\n  const hoverable = window.matchMedia(\"(hover: hover) and (pointer: fine)\");\n\n  let glide: AnimationPlaybackControls[] = [];\n  let sizing: AnimationPlaybackControls | null = null;\n  let lifting: AnimationPlaybackControls | null = null;\n  let flooding: AnimationPlaybackControls | null = null;\n  let shownLabel: string | undefined;\n  let writtenSize = -1;\n\n  let tourTimer = 0;\n  let touring = false;\n  let stop = 0;\n\n  flood.jump(revealed ? 1 : 0);\n\n  const R = () => d / 2;\n  const restPoint = () => (opts.rest ? { x: opts.rest[0] * w, y: opts.rest[1] * h } : null);\n  const lensX = () => (opts.reduced ? px : sx).get();\n  const lensY = () => (opts.reduced ? py : sy).get();\n\n  /** A client point in the frame's px, with any CSS scale divided out. */\n  function local(cx: number, cy: number) {\n    const rect = root.getBoundingClientRect();\n    const kx = root.offsetWidth ? rect.width / root.offsetWidth : 1;\n    const ky = root.offsetHeight ? rect.height / root.offsetHeight : 1;\n    return { x: (cx - rect.left) / kx - root.clientLeft, y: (cy - rect.top) / ky - root.clientTop };\n  }\n\n  function place(x: number, y: number, how: \"follow\" | \"glide\" | \"jump\") {\n    x = clamp(x, 0, w);\n    y = clamp(y, 0, h);\n    glide.forEach((a) => a.stop());\n    glide = [];\n    if (how === \"jump\" || opts.reduced) {\n      // The source first: its change starts the spring, which the jump then cancels.\n      px.jump(x);\n      py.jump(y);\n      sx.jump(x);\n      sy.jump(y);\n    } else if (how === \"glide\") {\n      const spring = { ...TIMING.glide, visualDuration: opts.glide };\n      glide = [animate(px, x, spring), animate(py, y, spring)];\n    } else {\n      px.set(x);\n      py.set(y);\n    }\n  }\n\n  function open(on: boolean) {\n    const to = on ? R() : 0;\n    want = on;\n    sizing?.stop();\n    if (opts.reduced) radius.jump(to);\n    else sizing = animate(radius, to, on ? TIMING.open : TIMING.close);\n  }\n\n  function setLift(on: boolean) {\n    lifting?.stop();\n    if (opts.reduced) lift.jump(0);\n    else lifting = animate(lift, on ? 1 : 0, TIMING.lift);\n  }\n\n  /** Opens the lens at a point, or glides it there if it's already open. */\n  function openAt(x: number, y: number) {\n    place(x, y, want ? \"glide\" : \"jump\");\n    open(true);\n  }\n\n  function home() {\n    const rest = restPoint();\n    if (rest) {\n      atRest = true;\n      if (want) place(rest.x, rest.y, \"glide\");\n      else openAt(rest.x, rest.y);\n    } else if (!focused) {\n      open(false);\n    }\n  }\n\n  /* The tour: the lens visits each labeled spot while nobody's using it. */\n\n  function tourAllowed() {\n    if (opts.reduced || !inView || hovering || drag || press || focused || revealed) return false;\n    return opts.autoPlay === true || (opts.autoPlay === \"touch\" && !hoverable.matches);\n  }\n\n  function stopTour() {\n    window.clearTimeout(tourTimer);\n    tourTimer = 0;\n    touring = false;\n  }\n\n  function startTour(delay: number) {\n    stopTour();\n    if (tourAllowed()) tourTimer = window.setTimeout(tour, delay);\n  }\n\n  function tourStops() {\n    const points: { x: number; y: number }[] = [];\n    // Nested spots often share a center; visit each place once.\n    for (const p of spots.stops()) {\n      if (!points.some((q) => Math.hypot(q.x - p.x, q.y - p.y) < R() * 0.4)) points.push(p);\n    }\n    return points.length ? points : WANDER.map(([fx, fy]) => ({ x: fx * w, y: fy * h }));\n  }\n\n  function tour() {\n    tourTimer = 0;\n    if (!tourAllowed()) {\n      touring = false;\n      return;\n    }\n    const points = tourStops();\n    if (touring) {\n      stop = (stop + 1) % points.length;\n    } else if (!want) {\n      stop = 0;\n      touring = true;\n    } else {\n      // Pick up from wherever the lens is.\n      const x = lensX();\n      const y = lensY();\n      const away = (i: number) => Math.hypot(points[i].x - x, points[i].y - y);\n      stop = points.reduce((best, _, i) => (away(i) < away(best) ? i : best), 0);\n      touring = true;\n    }\n    atRest = false;\n    openAt(points[stop].x, points[stop].y);\n    tourTimer = window.setTimeout(tour, TIMING.dwell);\n  }\n\n  let scheduled = false;\n  function write() {\n    scheduled = false;\n    const x = lensX();\n    const y = lensY();\n    const held = lift.get();\n    const f = clamp(flood.get(), 0, 1);\n    const k = R() ? clamp(radius.get() / R(), 0, 1.5) : 0;\n    const r = Math.max(0, radius.get()) * (1 + LENS.liftGrow * held);\n    // The flood grows the mask from the lens out past the farthest corner.\n    const far = Math.hypot(Math.max(x, w - x), Math.max(y, h - y)) + 2;\n    // The magnification swells in with the lens, so it seems to come into focus, and settles to 1 under the flood.\n    const zoom = 1 + (opts.zoom - 1 + LENS.liftZoom * held) * Math.min(k, 1) * (1 - f);\n    const gx = opts.reduced ? 0 : clamp(-sx.getVelocity() * LENS.glint, -LENS.glintMax, LENS.glintMax);\n    const gy = opts.reduced ? 0 : clamp(-sy.getVelocity() * LENS.glint, -LENS.glintMax, LENS.glintMax);\n\n    const style = root.style;\n    if (d !== writtenSize) {\n      style.setProperty(\"--lens-d\", `${d}px`);\n      writtenSize = d;\n    }\n    style.setProperty(\"--lens-x\", `${x}px`);\n    style.setProperty(\"--lens-y\", `${y}px`);\n    style.setProperty(\"--lens-r\", `${r + (far - r) * f}px`);\n    style.setProperty(\"--lens-z\", `${zoom}`);\n    style.setProperty(\"--lens-s\", `${(R() ? r / R() : 0) * (1 + 0.6 * f)}`);\n    style.setProperty(\"--lens-o\", `${1 - clamp(f * 2.5, 0, 1)}`);\n    style.setProperty(\"--lens-lift\", `${held}`);\n    style.setProperty(\"--lens-gx\", `${gx}px`);\n    style.setProperty(\"--lens-gy\", `${gy}px`);\n    // Under the flood the glass is gone: it can't be grabbed or focused.\n    glass.style.visibility = f > 0.6 ? \"hidden\" : \"\";\n\n    const next = k > 0.6 && f < 0.02 ? spots.labelAt(x, y) : undefined;\n    if (next !== shownLabel) {\n      if (next) {\n        labelText.textContent = next;\n        if (!opts.reduced) animate(labelText, { opacity: [0, 1], y: [3, 0] }, TIMING.labelText);\n      }\n      label.dataset.show = next ? \"true\" : \"false\";\n      shownLabel = next;\n    }\n    // The label hangs under the glass, or sits above it near the bottom edge.\n    label.style.transform =\n      y + r + LENS.labelGap + 28 > h\n        ? `translate(-50%, calc(-100% - ${r + LENS.labelGap}px))`\n        : `translate(-50%, ${r + LENS.labelGap}px)`;\n  }\n  const schedule = () => {\n    if (scheduled) return;\n    scheduled = true;\n    frame.render(write);\n  };\n  const unsubscribe = [px, py, sx, sy, radius, lift, flood].map((v) => v.on(\"change\", schedule));\n\n  const onMove = (e: PointerEvent) => {\n    client.x = e.clientX;\n    client.y = e.clientY;\n    if (drag) {\n      if (e.pointerId !== drag.id) return;\n      const p = local(e.clientX, e.clientY);\n      place(p.x - drag.dx, p.y - drag.dy, \"follow\");\n      return;\n    }\n    if (opts.mode !== \"hover\" || e.pointerType === \"touch\") return;\n    const p = local(e.clientX, e.clientY);\n    if (!hovering) {\n      hovering = true;\n      atRest = false;\n      stopTour();\n      spots.measureAll();\n      window.addEventListener(\"scroll\", onScroll, { capture: true, passive: true });\n      if (!want) {\n        openAt(p.x, p.y);\n        return;\n      }\n    }\n    place(p.x, p.y, \"follow\");\n  };\n\n  const onLeave = (e: PointerEvent) => {\n    if (drag || !hovering || e.pointerType === \"touch\") return;\n    hovering = false;\n    window.removeEventListener(\"scroll\", onScroll, { capture: true });\n    if (tourAllowed()) startTour(TIMING.resumeMouse);\n    else home();\n  };\n\n  // The page scrolled under a still pointer: keep the lens on it.\n  const onScroll = () => {\n    if (!hovering) return;\n    const p = local(client.x, client.y);\n    place(p.x, p.y, \"follow\");\n  };\n\n  const onDown = (e: PointerEvent) => {\n    if (e.button !== 0) return;\n    // With a mouse in hover mode the lens is already under the pointer, and clicks belong to the surface.\n    if (opts.mode === \"hover\" && e.pointerType !== \"touch\") return;\n    stopTour();\n    if (want && !revealed && glass.contains(e.target as Node)) {\n      const p = local(e.clientX, e.clientY);\n      e.preventDefault();\n      atRest = false;\n      drag = { id: e.pointerId, dx: p.x - lensX(), dy: p.y - lensY() };\n      root.setPointerCapture(e.pointerId);\n      setLift(true);\n      return;\n    }\n    if ((e.target as Element).closest?.(INTERACTIVE)) return;\n    // Wait for the release: a swipe that scrolls the page shouldn't move the lens.\n    press = { id: e.pointerId, x: e.clientX, y: e.clientY };\n  };\n\n  const onUp = (e: PointerEvent) => {\n    if (drag && e.pointerId === drag.id) {\n      drag = null;\n      if (root.hasPointerCapture(e.pointerId)) root.releasePointerCapture(e.pointerId);\n      setLift(false);\n    } else if (press && e.pointerId === press.id) {\n      const tapped = e.type === \"pointerup\" && Math.hypot(e.clientX - press.x, e.clientY - press.y) < 10;\n      press = null;\n      if (tapped && !revealed) {\n        const p = local(e.clientX, e.clientY);\n        atRest = false;\n        openAt(p.x, p.y);\n      }\n    }\n    startTour(TIMING.resume);\n  };\n\n  const onKey = (e: KeyboardEvent) => {\n    const step = e.shiftKey ? LENS.stepLarge : LENS.step;\n    const moves: Record<string, [number, number]> = {\n      ArrowLeft: [-step, 0],\n      ArrowRight: [step, 0],\n      ArrowUp: [0, -step],\n      ArrowDown: [0, step],\n    };\n    if (e.key === \"Home\") {\n      e.preventDefault();\n      const rest = restPoint() ?? { x: w / 2, y: h / 2 };\n      atRest = !!opts.rest;\n      place(rest.x, rest.y, \"glide\");\n      return;\n    }\n    const move = moves[e.key];\n    if (!move) return;\n    e.preventDefault();\n    atRest = false;\n    place(px.get() + move[0], py.get() + move[1], \"follow\");\n  };\n\n  const onFocus = () => {\n    focused = true;\n    stopTour();\n    if (!want) {\n      const rest = restPoint() ?? { x: w / 2, y: h / 2 };\n      openAt(rest.x, rest.y);\n    }\n  };\n\n  const onBlur = () => {\n    focused = false;\n    if (!hovering && !drag && !opts.rest) open(false);\n    startTour(TIMING.resume);\n  };\n\n  const onHoverChange = () => startTour(0);\n\n  root.addEventListener(\"pointermove\", onMove);\n  root.addEventListener(\"pointerleave\", onLeave);\n  root.addEventListener(\"pointerdown\", onDown);\n  root.addEventListener(\"pointerup\", onUp);\n  root.addEventListener(\"pointercancel\", onUp);\n  glass.addEventListener(\"keydown\", onKey);\n  glass.addEventListener(\"focus\", onFocus);\n  glass.addEventListener(\"blur\", onBlur);\n  hoverable.addEventListener(\"change\", onHoverChange);\n\n  const sized = new ResizeObserver(() => {\n    const [ow, oh] = [w, h];\n    w = root.clientWidth;\n    h = root.clientHeight;\n    const size = lensSize(opts.size, w, h);\n    if (size !== d) {\n      d = size;\n      if (want) {\n        sizing?.stop();\n        radius.jump(R());\n      }\n    }\n    spots.measureAll();\n    const rest = restPoint();\n    if (atRest && rest) place(rest.x, rest.y, \"jump\");\n    else if (ow && oh) place((px.get() * w) / ow, (py.get() * h) / oh, \"jump\");\n    schedule();\n  });\n  sized.observe(root);\n\n  // The first time the frame is in view, the lens opens at rest. The tour runs only while it's in view.\n  const seen = new IntersectionObserver(\n    ([entry]) => {\n      if (!entry) return;\n      inView = entry.isIntersecting;\n      if (!inView) {\n        stopTour();\n        return;\n      }\n      if (!introduced) {\n        introduced = true;\n        spots.measureAll();\n        const rest = restPoint();\n        if (rest && !want && !hovering) openAt(rest.x, rest.y);\n      }\n      if (!tourTimer && !touring) startTour(TIMING.tourStart);\n    },\n    { threshold: 0.35 },\n  );\n  seen.observe(root);\n\n  // Web fonts can move every spot without resizing anything.\n  document.fonts?.ready.then(() => spots.measureAll()).catch(() => {});\n\n  schedule();\n\n  return {\n    set(next: Options) {\n      const prev = opts;\n      opts = next;\n      if (prev.reduced !== next.reduced) place(px.get(), py.get(), \"jump\");\n      if (prev.size !== next.size) {\n        d = lensSize(next.size, w, h);\n        if (want) open(true);\n      }\n      const moved = prev.rest?.[0] !== next.rest?.[0] || prev.rest?.[1] !== next.rest?.[1];\n      if (moved && introduced && atRest && !hovering && !drag && !touring) home();\n      if (prev.autoPlay !== next.autoPlay || prev.reduced !== next.reduced) {\n        if (tourAllowed()) {\n          if (!touring) startTour(0);\n        } else {\n          stopTour();\n        }\n      }\n      schedule();\n    },\n    /** Floods the xray over the whole frame, or pulls it back in to the lens. `from` is where it starts when the lens is closed. */\n    reveal(on: boolean, from?: { x: number; y: number }) {\n      if (on === revealed) return;\n      revealed = on;\n      stopTour();\n      if (from && radius.get() < 1) place(from.x, from.y, \"jump\");\n      flooding?.stop();\n      if (opts.reduced) flood.jump(on ? 1 : 0);\n      else flooding = animate(flood, on ? 1 : 0, { ...TIMING.flood, duration: opts.flood });\n      if (!on) startTour(TIMING.resume);\n    },\n    destroy() {\n      cancelFrame(write);\n      stopTour();\n      unsubscribe.forEach((off) => off());\n      glide.forEach((a) => a.stop());\n      sizing?.stop();\n      lifting?.stop();\n      flooding?.stop();\n      root.removeEventListener(\"pointermove\", onMove);\n      root.removeEventListener(\"pointerleave\", onLeave);\n      root.removeEventListener(\"pointerdown\", onDown);\n      root.removeEventListener(\"pointerup\", onUp);\n      root.removeEventListener(\"pointercancel\", onUp);\n      glass.removeEventListener(\"keydown\", onKey);\n      glass.removeEventListener(\"focus\", onFocus);\n      glass.removeEventListener(\"blur\", onBlur);\n      hoverable.removeEventListener(\"change\", onHoverChange);\n      window.removeEventListener(\"scroll\", onScroll, { capture: true });\n      sized.disconnect();\n      seen.disconnect();\n    },\n  };\n}\n\ntype Lens = ReturnType<typeof createLens>;\n\nconst REDUCED = \"(prefers-reduced-motion: reduce)\";\nconst subscribeReduced = (onChange: () => void) => {\n  const query = window.matchMedia(REDUCED);\n  query.addEventListener(\"change\", onChange);\n  return () => query.removeEventListener(\"change\", onChange);\n};\n\n/** Reduced motion, read so the server and the first client render agree. */\nfunction useReduced() {\n  return React.useSyncExternalStore(\n    subscribeReduced,\n    () => window.matchMedia(REDUCED).matches,\n    () => false,\n  );\n}\n\n/** Twelve ticks around the rim and a crosshair, like the scale on a loupe. */\nfunction Reticle() {\n  return (\n    <span aria-hidden className=\"absolute inset-0 rounded-full opacity-70 mix-blend-difference\">\n      {Array.from({ length: 12 }, (_, i) => (\n        <span key={i} className=\"absolute inset-0\" style={{ transform: `rotate(${i * 30}deg)` }}>\n          <span\n            className={cn(\n              \"absolute top-1.5 left-1/2 w-px -translate-x-1/2 bg-white\",\n              i % 3 === 0 ? \"h-2\" : \"h-1\",\n            )}\n          />\n        </span>\n      ))}\n      <span className=\"absolute top-1/2 left-1/2 h-px w-2 -translate-y-1/2 translate-x-1 bg-white\" />\n      <span className=\"absolute top-1/2 right-1/2 h-px w-2 -translate-x-1 -translate-y-1/2 bg-white\" />\n      <span className=\"absolute top-1/2 left-1/2 h-2 w-px -translate-x-1/2 translate-y-1 bg-white\" />\n      <span className=\"absolute bottom-1/2 left-1/2 h-2 w-px -translate-x-1/2 -translate-y-1 bg-white\" />\n    </span>\n  );\n}\n\nexport interface XrayLensProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n  /** The polished UI. It sets the frame's size. */\n  surface: React.ReactNode;\n  /** What's underneath, laid over the same box and shown only inside the lens. */\n  xray?: React.ReactNode;\n  /** `\"hover\"`: the lens follows the pointer. `\"drag\"`: it stays put until it's picked up. Touch screens always drag. */\n  mode?: \"hover\" | \"drag\";\n  /** Diameter of the lens: px, or a percentage of the frame's width like `\"36%\"`. */\n  size?: number | string;\n  /** Magnification inside the lens, around its center. `1` for none. */\n  zoom?: number;\n  /** Where the lens waits, as fractions of the frame. `false` closes it when the pointer leaves. */\n  rest?: { x: number; y: number } | false;\n  /** Tour the labeled spots while the frame is in view: `\"touch\"` on touch screens only, `true` everywhere, `false` never. */\n  autoPlay?: boolean | \"touch\";\n  /** Floods the xray over the whole frame. Leave unset to let the switch manage it. */\n  revealed?: boolean;\n  /** Whether the xray starts out covering the frame. */\n  defaultRevealed?: boolean;\n  /** Called when the switch flips the layers. */\n  onRevealedChange?: (revealed: boolean) => void;\n  /** Show a two-way switch that floods the xray over the whole frame. */\n  toggle?: boolean;\n  /** The switch's labels: the surface first, then the xray. */\n  labels?: [string, string];\n  /** Classes for the switch, e.g. to move it to another corner. */\n  toggleClassName?: string;\n  /** Draw a crosshair and rim ticks on the glass. */\n  reticle?: boolean;\n  /** Stiffness of the spring the glass trails the pointer on. Higher keeps it closer. */\n  stiffness?: number;\n  /** Damping of that spring. Lower lets the glass overshoot and sway before it settles. */\n  damping?: number;\n  /** How long the lens takes to glide back to `rest`, to a tap and between tour stops, in seconds. */\n  glideDuration?: number;\n  /** How long the switch takes to flood the xray over the whole frame, or pull it back in, in seconds. */\n  revealDuration?: number;\n  /** Accessible name of the lens, which is focusable and moves with the arrow keys. */\n  lensLabel?: string;\n  /** Classes for the wrapper around `surface`. */\n  surfaceClassName?: string;\n  /** Classes for the xray layer. It defaults to a dark background, so the surface never shows through. */\n  xrayClassName?: string;\n  /** Classes for the glass. */\n  lensClassName?: string;\n  /** Classes for the label that names the spot under the lens. */\n  labelClassName?: string;\n}\n\n/**\n * A loupe over a polished UI. Inside the glass the surface peels away and\n * the layer underneath shows through, lined up with what it explains.\n */\nexport function XrayLens({\n  surface,\n  xray,\n  mode = \"hover\",\n  size = 200,\n  zoom = 1.06,\n  rest = { x: 0.5, y: 0.5 },\n  autoPlay = \"touch\",\n  revealed,\n  defaultRevealed = false,\n  onRevealedChange,\n  toggle = true,\n  labels = [\"Surface\", \"X-ray\"],\n  toggleClassName,\n  reticle = true,\n  stiffness = TIMING.follow.stiffness,\n  damping = TIMING.follow.damping,\n  glideDuration = TIMING.glide.visualDuration,\n  revealDuration = TIMING.flood.duration,\n  lensLabel = \"Inspection lens\",\n  surfaceClassName,\n  xrayClassName,\n  lensClassName,\n  labelClassName,\n  className,\n  style,\n  ...props\n}: XrayLensProps) {\n  const rootRef = React.useRef<HTMLDivElement>(null);\n  const glassRef = React.useRef<HTMLDivElement>(null);\n  const labelRef = React.useRef<HTMLDivElement>(null);\n  const labelTextRef = React.useRef<HTMLSpanElement>(null);\n  const lensRef = React.useRef<Lens | null>(null);\n  const originRef = React.useRef<{ x: number; y: number } | undefined>(undefined);\n  const [spots] = React.useState(createSpots);\n  const reduced = useReduced();\n\n  const [own, setOwn] = React.useState(defaultRevealed);\n  const shown = revealed ?? own;\n  // The markup starts on this side; the lens animates every flip after it.\n  const [initial] = React.useState(shown);\n  const shownRef = React.useRef(shown);\n\n  const px = useMotionValue(0);\n  const py = useMotionValue(0);\n  const follow = { ...TIMING.follow, stiffness, damping };\n  const sx = useSpring(px, follow);\n  const sy = useSpring(py, follow);\n  const radius = useMotionValue(0);\n  const lift = useMotionValue(0);\n  const flood = useMotionValue(0);\n\n  const options: Options = {\n    mode,\n    size,\n    zoom: Math.max(1, zoom),\n    rest: rest ? [clamp(rest.x, 0, 1), clamp(rest.y, 0, 1)] : null,\n    autoPlay,\n    glide: glideDuration,\n    flood: revealDuration,\n    reduced,\n  };\n  const optionsRef = React.useRef(options);\n  React.useLayoutEffect(() => {\n    optionsRef.current = options;\n    lensRef.current?.set(options);\n  });\n\n  React.useEffect(() => {\n    const root = rootRef.current;\n    const glass = glassRef.current;\n    const label = labelRef.current;\n    const labelText = labelTextRef.current;\n    if (!root || !glass || !label || !labelText) return;\n    spots.connect(root);\n    const lens = createLens(\n      { root, glass, label, labelText },\n      { px, py, sx, sy, radius, lift, flood },\n      spots,\n      optionsRef.current,\n      shownRef.current,\n    );\n    lensRef.current = lens;\n    return () => {\n      lens.destroy();\n      lensRef.current = null;\n      spots.connect(null);\n    };\n  }, [spots, px, py, sx, sy, radius, lift, flood]);\n\n  React.useEffect(() => {\n    shownRef.current = shown;\n    lensRef.current?.reveal(shown, originRef.current);\n    originRef.current = undefined;\n  }, [shown]);\n\n  const choose = (next: boolean, event: React.MouseEvent<HTMLButtonElement>) => {\n    if (next === shown) return;\n    const root = rootRef.current;\n    if (root) {\n      // Flood from the button that was pressed, if the lens is closed.\n      const frameRect = root.getBoundingClientRect();\n      const button = event.currentTarget.getBoundingClientRect();\n      const kx = root.offsetWidth ? frameRect.width / root.offsetWidth : 1;\n      const ky = root.offsetHeight ? frameRect.height / root.offsetHeight : 1;\n      originRef.current = {\n        x: (button.left + button.width / 2 - frameRect.left) / kx - root.clientLeft,\n        y: (button.top + button.height / 2 - frameRect.top) / ky - root.clientTop,\n      };\n    }\n    if (revealed === undefined) setOwn(next);\n    onRevealedChange?.(next);\n  };\n\n  const disc: React.CSSProperties = {\n    width: \"var(--lens-d)\",\n    height: \"var(--lens-d)\",\n    left: \"calc(var(--lens-d) / -2)\",\n    top: \"calc(var(--lens-d) / -2)\",\n  };\n\n  return (\n    <div\n      ref={rootRef}\n      data-xray-lens=\"\"\n      data-revealed={shown ? \"true\" : \"false\"}\n      className={cn(\"relative isolate\", mode === \"hover\" && \"pointer-fine:cursor-crosshair\", className)}\n      style={\n        {\n          \"--lens-d\": typeof size === \"number\" ? `${size}px` : \"0px\",\n          \"--lens-x\": \"0px\",\n          \"--lens-y\": \"0px\",\n          \"--lens-r\": initial ? \"200vmax\" : \"0px\",\n          \"--lens-z\": \"1\",\n          \"--lens-s\": \"0\",\n          \"--lens-o\": \"1\",\n          \"--lens-lift\": \"0\",\n          \"--lens-gx\": \"0px\",\n          \"--lens-gy\": \"0px\",\n          ...style,\n        } as React.CSSProperties\n      }\n      {...props}\n    >\n      <XrayLensContext.Provider value={spots}>\n        {/* Under the flood the surface can't be seen, so it can't be clicked either. */}\n        <div className={cn(\"relative z-0 h-full\", shown && \"pointer-events-none\", surfaceClassName)}>{surface}</div>\n\n        {/* The xray: the same box as the surface, cut to a circle around the lens and magnified about its center. */}\n        <div\n          aria-hidden\n          inert\n          className=\"pointer-events-none absolute inset-0 z-[1] overflow-hidden rounded-[inherit] select-none\"\n          style={{\n            maskImage:\n              \"radial-gradient(circle var(--lens-r) at var(--lens-x) var(--lens-y), #000 calc(100% - 1px), transparent 100%)\",\n            WebkitMaskImage:\n              \"radial-gradient(circle var(--lens-r) at var(--lens-x) var(--lens-y), #000 calc(100% - 1px), transparent 100%)\",\n          }}\n        >\n          <div\n            className={cn(\"absolute inset-0 bg-zinc-950 text-zinc-100\", xrayClassName)}\n            style={{ transformOrigin: \"var(--lens-x) var(--lens-y)\", transform: \"scale(var(--lens-z))\" }}\n          >\n            {xray}\n            <SpotLayer spots={spots} />\n          </div>\n        </div>\n\n        {/* The glass, centered on the lens. */}\n        <div\n          className=\"pointer-events-none absolute top-0 left-0 z-[2]\"\n          style={{ transform: \"translate3d(var(--lens-x), var(--lens-y), 0)\" }}\n        >\n          <div\n            aria-hidden\n            className=\"absolute rounded-full shadow-[0_22px_44px_-16px_rgb(0_0_0/0.5),0_6px_14px_-6px_rgb(0_0_0/0.3)]\"\n            style={{\n              ...disc,\n              opacity: \"var(--lens-o)\",\n              transform: `translateY(calc(var(--lens-lift) * ${LENS.liftDrop}px)) scale(var(--lens-s))`,\n            }}\n          />\n          <div\n            ref={glassRef}\n            tabIndex={0}\n            role=\"group\"\n            aria-roledescription=\"lens\"\n            aria-label={lensLabel}\n            aria-keyshortcuts=\"ArrowUp ArrowDown ArrowLeft ArrowRight Home\"\n            className={cn(\n              \"absolute touch-none rounded-full ring-1 ring-black/25 outline-none focus-visible:ring-2 focus-visible:ring-sky-400 dark:ring-white/20\",\n              mode === \"drag\"\n                ? \"pointer-events-auto cursor-grab active:cursor-grabbing\"\n                : \"pointer-coarse:pointer-events-auto\",\n              lensClassName,\n            )}\n            style={{ ...disc, opacity: \"var(--lens-o)\", transform: \"scale(var(--lens-s))\" }}\n          >\n            {/* The edge of a thick lens: a bright rim, a dark band, then a falloff toward the middle. */}\n            <span className=\"absolute inset-0 rounded-full shadow-[inset_0_0_0_1px_rgb(255_255_255/0.55),inset_0_0_0_3px_rgb(0_0_0/0.3),inset_0_0_26px_rgb(0_0_0/0.45)]\" />\n            {/* A trace of color fringing just inside the rim. */}\n            <span className=\"absolute inset-[3px] rounded-full shadow-[inset_1.5px_1px_0_rgb(56_189_248/0.3),inset_-1.5px_-1px_0_rgb(244_114_182/0.25)]\" />\n            <span className=\"absolute inset-0 rounded-full bg-[linear-gradient(155deg,rgb(255_255_255/0.16),rgb(255_255_255/0)_38%,rgb(255_255_255/0)_72%,rgb(255_255_255/0.06))]\" />\n            <span\n              className=\"absolute top-[9%] left-[16%] h-[12%] w-[36%]\"\n              style={{ transform: \"translate(var(--lens-gx), var(--lens-gy))\" }}\n            >\n              <span className=\"block size-full -rotate-[32deg] rounded-full bg-[radial-gradient(closest-side,rgb(255_255_255/0.55),rgb(255_255_255/0))]\" />\n            </span>\n            {reticle && <Reticle />}\n          </div>\n          <div\n            ref={labelRef}\n            aria-hidden\n            data-show=\"false\"\n            className={cn(\n              `absolute top-0 left-0 flex items-center gap-1.5 rounded-full bg-zinc-950/90 px-2.5 py-1 font-mono text-[11px] leading-4 font-medium whitespace-nowrap text-white opacity-0 shadow-lg ring-1 ring-white/10 backdrop-blur-sm transition-opacity ${TIMING.labelFade} data-[show=true]:opacity-100`,\n              labelClassName,\n            )}\n          >\n            <span className=\"size-1.5 rounded-full bg-emerald-400\" />\n            <span ref={labelTextRef} />\n          </div>\n        </div>\n\n        {toggle && (\n          <div\n            role=\"group\"\n            aria-label=\"Layers\"\n            className={cn(\n              \"absolute right-3 bottom-3 z-[3] grid cursor-default grid-cols-2 rounded-full bg-zinc-950/80 p-1 text-xs font-medium text-white shadow-lg ring-1 ring-white/15 backdrop-blur-md\",\n              toggleClassName,\n            )}\n          >\n            <span\n              aria-hidden\n              className={cn(\n                `absolute inset-y-1 left-1 w-[calc(50%-0.25rem)] rounded-full bg-white shadow-sm transition-transform ${TIMING.thumb} motion-reduce:transition-none`,\n                shown && \"translate-x-full\",\n              )}\n            />\n            {labels.map((text, i) => {\n              const active = shown === (i === 1);\n              return (\n                <button\n                  key={i}\n                  type=\"button\"\n                  aria-pressed={active}\n                  onClick={(event) => choose(i === 1, event)}\n                  className={cn(\n                    `relative cursor-pointer rounded-full px-3 py-1.5 whitespace-nowrap transition-colors ${TIMING.switchColor} outline-none focus-visible:ring-2 focus-visible:ring-white/70`,\n                    active ? \"text-zinc-950\" : \"text-white/75 hover:text-white\",\n                  )}\n                >\n                  {text}\n                </button>\n              );\n            })}\n          </div>\n        )}\n      </XrayLensContext.Provider>\n    </div>\n  );\n}\n"
    }
  ]
}