{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "illustration-globe-vercel",
  "type": "registry:ui",
  "description": "Globe deployment illustration",
  "files": [
    {
      "path": "registry/default/ui/illustration-globe-vercel.tsx",
      "content": "\"use client\"\n\nimport { useCallback, useMemo, useState } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport type DeployStatus = \"deploying\" | \"deployed\" | \"error\"\nexport type TrajectoryStep = [number, number]\nexport type BuiltInNodeIcon =\n  | \"triangle\"\n  | \"server\"\n  | \"database\"\n  | \"globe\"\n  | \"shield\"\n  | \"zap\"\n  | \"cloud\"\n  | \"hexagon\"\nexport type CustomNodeIcon = { d: string; viewBox?: string }\nexport type NodeIcon = BuiltInNodeIcon | CustomNodeIcon\nexport type DeployNode = {\n  id: string\n  trajectory: TrajectoryStep[]\n  icon?: NodeIcon\n}\n\nexport type DeployNodeData = DeployNode & {\n  pathD: string\n  waypoints: { x: number; y: number }[]\n  endPt: { x: number; y: number }\n}\n\nexport type DeployGlobeProps = {\n  status?: DeployStatus\n  nodes?: DeployNode[]\n  accentColor?: string\n  duration?: number\n  className?: string\n  onReplay?: () => void\n}\n\nconst MERIDIANS = [-4, -3, -2, -1, 0, 1, 2, 3, 4]\nconst ROWS = [1, 2, 3, 4, 5, 6, 7, 8, 9]\n\nconst ICON_PATHS: Record<BuiltInNodeIcon, string> = {\n  triangle: \"M8 1L15 14H1L8 1Z\",\n  server:\n    \"M2 3a1 1 0 011-1h10a1 1 0 011 1v2a1 1 0 01-1 1H3a1 1 0 01-1-1V3zm0 6a1 1 0 011-1h10a1 1 0 011 1v2a1 1 0 01-1 1H3a1 1 0 01-1-1V9zm9-5.5a.75.75 0 100-1.5.75.75 0 000 1.5zM11 10.5a.75.75 0 100-1.5.75.75 0 000 1.5z\",\n  database:\n    \"M8 1C4.13 1 1 2.34 1 4v8c0 1.66 3.13 3 7 3s7-1.34 7-3V4c0-1.66-3.13-3-7-3zm0 2c3.31 0 5 .9 5 1s-1.69 1-5 1-5-.9-5-1 1.69-1 5-1zm5 9c0 .1-1.69 1-5 1s-5-.9-5-1V9.47C4.55 10.06 6.15 10.5 8 10.5s3.45-.44 5-1.03V12zm0-4c0 .1-1.69 1-5 1s-5-.9-5-1V5.47C4.55 6.06 6.15 6.5 8 6.5s3.45-.44 5-1.03V8z\",\n  globe:\n    \"M8 1a7 7 0 100 14A7 7 0 008 1zM6.53 2.66A5.5 5.5 0 002.6 7H5.1c.1-1.62.4-3.07.86-4.13a5.52 5.52 0 01.57-.21zm2.94 0c.2.06.39.13.57.21.46 1.06.76 2.51.87 4.13h2.49a5.5 5.5 0 00-3.93-4.34zM6.6 7c.1-1.46.38-2.73.77-3.6.26-.58.52-.9.63-.9.11 0 .37.32.63.9.39.87.67 2.14.77 3.6H6.6zm-4 2a5.5 5.5 0 003.93 4.34 5.43 5.43 0 01-.57-.21C5.5 12.07 5.2 10.62 5.1 9H2.6zm6.87 4.34A5.5 5.5 0 0013.4 9h-2.49c-.1 1.62-.41 3.07-.87 4.13.18-.06.37-.13.57-.21h-.24zM6.6 9c.1 1.46.38 2.73.77 3.6.26.58.52.9.63.9.11 0 .37-.32.63-.9.39-.87.67-2.14.77-3.6H6.6z\",\n  shield:\n    \"M8 1L2 4v4c0 3.33 2.56 6.44 6 7.15C11.44 14.44 14 11.33 14 8V4L8 1zm0 2.18L12 5.4v2.6c0 2.52-1.77 4.86-4 5.55-2.23-.69-4-3.03-4-5.55V5.4L8 3.18z\",\n  zap: \"M8.94 1.5L3 9h4.5l-.44 5.5L13 7H8.5l.44-5.5z\",\n  cloud:\n    \"M12.5 6.5A4.5 4.5 0 004 7.08 3.5 3.5 0 003.5 14h9a3 3 0 000-6 4.47 4.47 0 000-1.5zM8 4a3 3 0 00-2.83 2.02l-.23.67-.7.1A2 2 0 005.5 12.5h7a1.5 1.5 0 000-3h-.73l-.12-.72A3 3 0 008 4z\",\n  hexagon:\n    \"M8 1.5L14 5v6l-6 3.5L2 11V5l6-3.5zm0 1.73L3.5 6.04v4.92L8 13.77l4.5-2.81V6.04L8 3.23z\",\n}\n\nconst DEFAULT_NODES: DeployNode[] = [\n  {\n    id: \"n1\",\n    trajectory: [\n      [3, 0],\n      [3, 1],\n      [3, 2],\n      [3, 3],\n    ],\n  },\n  {\n    id: \"n2\",\n    trajectory: [\n      [-3, 0],\n      [-3, 1],\n      [-3, 2],\n      [-3, 3],\n      [-3, 4],\n    ],\n  },\n  {\n    id: \"n3\",\n    trajectory: [\n      [1, 0],\n      [1, 1],\n      [1, 2],\n    ],\n  },\n  {\n    id: \"n4\",\n    trajectory: [\n      [-1, 0],\n      [-1, 1],\n      [-2, 1],\n    ],\n  },\n  {\n    id: \"n5\",\n    trajectory: [\n      [0, 0],\n      [0, 1],\n      [0, 2],\n      [0, 3],\n      [-1, 3],\n    ],\n  },\n  {\n    id: \"n6\",\n    trajectory: [\n      [2, 0],\n      [2, 1],\n    ],\n  },\n]\n\nfunction meridianA(index: number, radius: number) {\n  return (\n    radius * Math.sin((Math.abs(index) / 4) * (Math.PI / 2)) * Math.sign(index)\n  )\n}\n\nfunction rowY(row: number, cy: number, radius: number) {\n  return cy - radius + row * ((2 * radius) / 10)\n}\n\nfunction pointOnMeridian(\n  mIdx: number,\n  row: number,\n  cx: number,\n  cy: number,\n  radius: number\n) {\n  const a = meridianA(mIdx, radius)\n  const y = rowY(row, cy, radius)\n  const t = (y - cy) / radius\n  const xOff = Math.abs(a) * Math.sqrt(Math.max(0, 1 - t * t))\n  return { x: cx + (a >= 0 ? xOff : -xOff), y }\n}\n\nfunction horizontalSpan(row: number, cx: number, cy: number, radius: number) {\n  const y = rowY(row, cy, radius)\n  const t = (y - cy) / radius\n  const half = radius * Math.sqrt(Math.max(0, 1 - t * t))\n  return { x1: cx - half, x2: cx + half, y }\n}\n\nfunction buildPathData(\n  trajectory: TrajectoryStep[],\n  cx: number,\n  cy: number,\n  radius: number\n) {\n  const northPole = { x: cx, y: cy - radius }\n  const points = trajectory.map(([m, row]) =>\n    pointOnMeridian(m, row, cx, cy, radius)\n  )\n  const allPoints = [northPole, ...points]\n  const parts = [`M ${northPole.x.toFixed(2)} ${northPole.y.toFixed(2)}`]\n\n  for (let index = 1; index < allPoints.length; index++) {\n    const point = allPoints[index]\n\n    if (index === 1) {\n      const m = trajectory[0][0]\n      const a = Math.abs(meridianA(m, radius))\n      const sweep = m >= 0 ? 1 : 0\n      if (a < 0.5) {\n        parts.push(`L ${point.x.toFixed(2)} ${point.y.toFixed(2)}`)\n      } else {\n        parts.push(\n          `A ${a.toFixed(2)} ${radius} 0 0 ${sweep} ${point.x.toFixed(2)} ${point.y.toFixed(2)}`\n        )\n      }\n    } else {\n      const [prevM, prevRow] = trajectory[index - 2]\n      const [currentM, currentRow] = trajectory[index - 1]\n\n      if (currentM === prevM) {\n        const a = Math.abs(meridianA(currentM, radius))\n        const sweep =\n          currentM >= 0\n            ? currentRow > prevRow\n              ? 1\n              : 0\n            : currentRow > prevRow\n              ? 0\n              : 1\n        if (a < 0.5) {\n          parts.push(`L ${point.x.toFixed(2)} ${point.y.toFixed(2)}`)\n        } else {\n          parts.push(\n            `A ${a.toFixed(2)} ${radius} 0 0 ${sweep} ${point.x.toFixed(2)} ${point.y.toFixed(2)}`\n          )\n        }\n      } else {\n        parts.push(`L ${point.x.toFixed(2)} ${point.y.toFixed(2)}`)\n      }\n    }\n  }\n\n  return { d: parts.join(\" \"), waypoints: allPoints }\n}\n\nfunction GlobeWireframe({\n  cx,\n  cy,\n  radius,\n  wireColor,\n}: {\n  cx: number\n  cy: number\n  radius: number\n  wireColor: string\n}) {\n  return (\n    <g data-slot=\"deploy-globe-wireframe\" opacity=\"0.75\">\n      <circle\n        cx={cx}\n        cy={cy}\n        fill=\"none\"\n        r={radius}\n        stroke={wireColor}\n        strokeWidth=\"1\"\n        vectorEffect=\"non-scaling-stroke\"\n      />\n      {MERIDIANS.map((m) => {\n        const a = Math.abs(meridianA(m, radius))\n        const dir = m >= 0 ? 1 : 0\n        return (\n          <path\n            d={`M ${cx} ${cy + radius} A ${a.toFixed(3)} ${radius} 0 0 ${dir} ${cx} ${cy - radius}`}\n            fill=\"none\"\n            key={`m${m}`}\n            stroke={wireColor}\n            strokeWidth=\"1\"\n            vectorEffect=\"non-scaling-stroke\"\n          />\n        )\n      })}\n      {ROWS.map((row) => {\n        const { x1, x2, y } = horizontalSpan(row, cx, cy, radius)\n        return (\n          <line\n            key={`r${row}`}\n            stroke={wireColor}\n            strokeWidth=\"1\"\n            vectorEffect=\"non-scaling-stroke\"\n            x1={x1.toFixed(2)}\n            x2={x2.toFixed(2)}\n            y1={y.toFixed(2)}\n            y2={y.toFixed(2)}\n          />\n        )\n      })}\n    </g>\n  )\n}\n\nfunction DeployPath({\n  pathD,\n  waypoints,\n  accent,\n  duration,\n  id,\n}: {\n  pathD: string\n  waypoints: { x: number; y: number }[]\n  accent: string\n  duration: number\n  id: string\n}) {\n  const waypointCount = waypoints.length\n  const totalPhases = waypointCount + 3\n  const keyTimes = Array.from({ length: totalPhases + 1 }, (_, index) =>\n    (index / totalPhases).toFixed(4)\n  )\n  const keyTimesStr = keyTimes.join(\";\")\n\n  const cxValues: string[] = []\n  const cyValues: string[] = []\n  const radiusValues: string[] = []\n  const opacityValues: string[] = []\n\n  const first = waypoints[0]\n  const last = waypoints[waypointCount - 1]\n\n  for (let index = 0; index <= totalPhases; index++) {\n    if (index === 0) {\n      cxValues.push(first.x.toFixed(1))\n      cyValues.push(first.y.toFixed(1))\n      radiusValues.push(\"0\")\n      opacityValues.push(\"0\")\n    } else if (index <= waypointCount) {\n      const point = waypoints[index - 1]\n      cxValues.push(point.x.toFixed(1))\n      cyValues.push(point.y.toFixed(1))\n      radiusValues.push(\"140\")\n      opacityValues.push(\"1\")\n    } else if (index === waypointCount + 1) {\n      cxValues.push(last.x.toFixed(1))\n      cyValues.push(last.y.toFixed(1))\n      radiusValues.push(\"140\")\n      opacityValues.push(\"1\")\n    } else if (index === waypointCount + 2) {\n      cxValues.push(last.x.toFixed(1))\n      cyValues.push(last.y.toFixed(1))\n      radiusValues.push(\"0\")\n      opacityValues.push(\"0\")\n    } else {\n      cxValues.push(first.x.toFixed(1))\n      cyValues.push(first.y.toFixed(1))\n      radiusValues.push(\"0\")\n      opacityValues.push(\"0\")\n    }\n  }\n\n  const gradientId = `dg-${id}`\n\n  return (\n    <g data-slot=\"deploy-globe-path\">\n      <path\n        d={pathD}\n        fill=\"none\"\n        stroke={`url(#${gradientId})`}\n        strokeLinecap=\"round\"\n        strokeWidth=\"2\"\n        vectorEffect=\"non-scaling-stroke\"\n      >\n        <animate\n          attributeName=\"opacity\"\n          dur={`${duration}s`}\n          keyTimes={keyTimesStr}\n          repeatCount=\"indefinite\"\n          values={opacityValues.join(\";\")}\n        />\n      </path>\n      <defs>\n        <radialGradient\n          cx={first.x}\n          cy={first.y}\n          gradientUnits=\"userSpaceOnUse\"\n          id={gradientId}\n          r=\"0\"\n        >\n          <stop offset=\"0\" stopColor={accent} />\n          <stop offset=\"0.4\" stopColor={accent} />\n          <stop offset=\"1\" stopColor={accent} stopOpacity=\"0\" />\n          <animate\n            attributeName=\"cx\"\n            dur={`${duration}s`}\n            keyTimes={keyTimesStr}\n            repeatCount=\"indefinite\"\n            values={cxValues.join(\";\")}\n          />\n          <animate\n            attributeName=\"cy\"\n            dur={`${duration}s`}\n            keyTimes={keyTimesStr}\n            repeatCount=\"indefinite\"\n            values={cyValues.join(\";\")}\n          />\n          <animate\n            attributeName=\"r\"\n            dur={`${duration}s`}\n            keyTimes={keyTimesStr}\n            repeatCount=\"indefinite\"\n            values={radiusValues.join(\";\")}\n          />\n        </radialGradient>\n      </defs>\n    </g>\n  )\n}\n\nfunction NodeMarker({\n  x,\n  y,\n  accent,\n  wireColor,\n  duration,\n  waypointCount,\n  icon,\n}: {\n  x: number\n  y: number\n  accent: string\n  wireColor: string\n  duration: number\n  waypointCount: number\n  icon?: NodeIcon\n}) {\n  const totalPhases = waypointCount + 3\n  const arrivalFraction = waypointCount / totalPhases\n  const p1 = arrivalFraction\n  const p2 = Math.min(p1 + 0.06, 0.97)\n  const p3 = Math.min(p1 + 0.12, 0.98)\n  const p4 = Math.min(p1 + 0.2, 0.99)\n  const keyTimes = `0;${p1.toFixed(4)};${p2.toFixed(4)};${p3.toFixed(4)};${p4.toFixed(4)};1`\n  const hasIcon = icon != null\n\n  let iconPath: string | null = null\n  let iconViewBox = \"0 0 16 16\"\n\n  if (hasIcon) {\n    if (typeof icon === \"string\") {\n      iconPath = ICON_PATHS[icon]\n    } else {\n      iconPath = icon.d\n      iconViewBox = icon.viewBox || iconViewBox\n    }\n  }\n\n  const [vbX, vbY, vbW, vbH] = iconViewBox.split(\" \").map(Number)\n  const targetSize = 16\n  const scale = targetSize / Math.max(vbW || 16, vbH || 16)\n  const offsetX = x - ((vbW || 16) * scale) / 2 - vbX * scale\n  const offsetY = y - ((vbH || 16) * scale) / 2 - vbY * scale\n\n  return (\n    <g data-slot=\"deploy-globe-node-marker\">\n      <circle\n        cx={x}\n        cy={y}\n        fill=\"none\"\n        opacity=\"0\"\n        r=\"0\"\n        stroke={accent}\n        strokeWidth=\"2\"\n      >\n        <animate\n          attributeName=\"r\"\n          dur={`${duration}s`}\n          keyTimes={keyTimes}\n          repeatCount=\"indefinite\"\n          values=\"0;0;14;22;28;0\"\n        />\n        <animate\n          attributeName=\"opacity\"\n          dur={`${duration}s`}\n          keyTimes={keyTimes}\n          repeatCount=\"indefinite\"\n          values=\"0;0;0.9;0.5;0;0\"\n        />\n      </circle>\n\n      <circle\n        cx={x}\n        cy={y}\n        fill=\"var(--globe-bg, #fff)\"\n        r=\"16\"\n        stroke={wireColor}\n        strokeWidth=\"1\"\n        vectorEffect=\"non-scaling-stroke\"\n      />\n      {hasIcon && iconPath ? (\n        <path\n          clipRule=\"evenodd\"\n          d={iconPath}\n          fill=\"var(--globe-dot, #4d4d4d)\"\n          fillRule=\"evenodd\"\n          transform={`translate(${offsetX.toFixed(2)}, ${offsetY.toFixed(2)}) scale(${scale.toFixed(4)})`}\n        />\n      ) : (\n        <circle cx={x} cy={y} fill=\"var(--globe-dot, #4d4d4d)\" r=\"8\" />\n      )}\n    </g>\n  )\n}\n\nfunction DeployGlobeSvg({\n  animationKey,\n  width,\n  height,\n  children,\n}: {\n  animationKey: number\n  width: number\n  height: number\n  children: React.ReactNode\n}) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className=\"h-auto w-full\"\n      data-slot=\"deploy-globe-svg\"\n      key={animationKey}\n      style={{ overflow: \"visible\", maxWidth: 800 }}\n      viewBox={`-1 -1 ${width + 2} ${height + 2}`}\n    >\n      {children}\n    </svg>\n  )\n}\n\nfunction DeployGlobeStatusBadge({\n  statusLabel,\n  onReplay,\n}: {\n  statusLabel: string\n  onReplay: () => void\n}) {\n  return (\n    <div\n      className=\"absolute flex select-none items-center overflow-hidden\"\n      data-slot=\"deploy-globe-status\"\n      style={{\n        bottom: \"6%\",\n        left: \"50%\",\n        transform: \"translateX(-50%)\",\n        background: \"var(--globe-bg, #fff)\",\n        border: \"1px solid var(--globe-wire, #d4d4d4)\",\n        borderRadius: 8,\n        boxShadow: \"0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04)\",\n      }}\n    >\n      <div style={{ padding: \"8px 16px\" }}>\n        <span\n          style={{\n            fontFamily: \"ui-monospace, SFMono-Regular, 'Geist Mono', monospace\",\n            fontSize: 12,\n            letterSpacing: \"0.02em\",\n            color: \"var(--globe-dot, #6b7280)\",\n          }}\n        >\n          {statusLabel}\n        </span>\n      </div>\n      <button\n        aria-label=\"Replay globe animation\"\n        data-slot=\"deploy-globe-replay\"\n        onClick={onReplay}\n        onMouseEnter={(event) => {\n          event.currentTarget.style.background =\n            \"var(--color-muted, rgba(0,0,0,0.04))\"\n        }}\n        onMouseLeave={(event) => {\n          event.currentTarget.style.background = \"none\"\n        }}\n        style={{\n          padding: \"8px 12px\",\n          borderLeft: \"1px solid var(--globe-wire, #d4d4d4)\",\n          background: \"none\",\n          cursor: \"pointer\",\n          display: \"flex\",\n          alignItems: \"center\",\n          color: \"var(--globe-dot, #6b7280)\",\n          transition: \"color 0.15s ease, background 0.15s ease\",\n        }}\n        type=\"button\"\n      >\n        <svg\n          aria-hidden=\"true\"\n          fill=\"currentColor\"\n          focusable=\"false\"\n          height=\"14\"\n          viewBox=\"0 0 16 16\"\n          width=\"14\"\n        >\n          <path\n            clipRule=\"evenodd\"\n            d=\"M2.5 8C2.5 4.966 4.974 2.5 8.035 2.5c2.537 0 4.671 1.694 5.328 4H10.75V8h4.5a.75.75 0 0 0 .75-.75v-4.5H14.5v2.483C13.422 2.742 10.932 1 8.035 1 4.154 1 1 4.13 1 8s3.154 7 7.035 7c2.34 0 4.416-1.138 5.694-2.888l.443-.606-1.211-.885-.443.606A5.543 5.543 0 0 1 8.035 13.5C4.974 13.5 2.5 11.034 2.5 8Z\"\n            fillRule=\"evenodd\"\n          />\n        </svg>\n      </button>\n    </div>\n  )\n}\n\nfunction DeployGlobe({\n  status = \"deployed\",\n  nodes = DEFAULT_NODES,\n  accentColor,\n  duration = 4.5,\n  className = \"\",\n  onReplay,\n}: DeployGlobeProps) {\n  const [animationKey, setAnimationKey] = useState(0)\n  const width = 800\n  const height = 400\n  const cx = width / 2\n  const cy = height\n  const radius = height\n\n  const accent =\n    status === \"error\"\n      ? \"var(--globe-error, #EF4444)\"\n      : accentColor || \"#45DEC4\"\n  const wireColor = \"var(--globe-wire, #c9c9c9)\"\n\n  const replay = useCallback(() => {\n    setAnimationKey((key) => key + 1)\n    onReplay?.()\n  }, [onReplay])\n\n  const nodeData = useMemo<DeployNodeData[]>(\n    () =>\n      nodes.map((node) => {\n        const { d, waypoints } = buildPathData(node.trajectory, cx, cy, radius)\n        const endPt = waypoints[waypoints.length - 1]\n        return { ...node, pathD: d, waypoints, endPt }\n      }),\n    [nodes, cx, cy, radius]\n  )\n\n  const statusLabel =\n    status === \"error\"\n      ? \"failed\"\n      : status === \"deploying\"\n        ? \"deploying…\"\n        : \"deployed\"\n\n  return (\n    <div\n      className={cn(\n        \"relative flex w-full flex-col items-center justify-center\",\n        className\n      )}\n      data-slot=\"deploy-globe\"\n      style={\n        {\n          \"--globe-bg\": \"var(--color-background, #fff)\",\n          \"--globe-dot\": \"var(--color-muted-foreground, #4d4d4d)\",\n          \"--globe-icon\": \"var(--color-muted-foreground, #999)\",\n          \"--globe-wire\": \"var(--color-border, #d4d4d4)\",\n          \"--globe-error\": \"var(--color-destructive, #EF4444)\",\n        } as React.CSSProperties\n      }\n    >\n      <DeployGlobeSvg animationKey={animationKey} height={height} width={width}>\n        <GlobeWireframe cx={cx} cy={cy} radius={radius} wireColor={wireColor} />\n\n        {status !== \"error\"\n          ? nodeData.map((node) => (\n              <DeployPath\n                accent={accent}\n                duration={duration}\n                id={`${node.id}-${animationKey}`}\n                key={`p-${node.id}-${animationKey}`}\n                pathD={node.pathD}\n                waypoints={node.waypoints}\n              />\n            ))\n          : null}\n\n        {nodeData.map((node) => (\n          <NodeMarker\n            accent={accent}\n            duration={duration}\n            icon={node.icon}\n            key={`nd-${node.id}`}\n            waypointCount={node.waypoints.length}\n            wireColor={wireColor}\n            x={node.endPt.x}\n            y={node.endPt.y}\n          />\n        ))}\n      </DeployGlobeSvg>\n\n      <DeployGlobeStatusBadge onReplay={replay} statusLabel={statusLabel} />\n    </div>\n  )\n}\n\nfunction DemoGlobeVercelControls({\n  status,\n  accent,\n  preset,\n  onStatusChange,\n  onPresetChange,\n  onAccentChange,\n}: {\n  status: DeployStatus\n  accent: string\n  preset: NodePresetName\n  onStatusChange: (status: DeployStatus) => void\n  onPresetChange: (preset: NodePresetName) => void\n  onAccentChange: (color: string) => void\n}) {\n  const pill = (active: boolean) =>\n    ({\n      padding: \"5px 13px\",\n      borderRadius: 999,\n      border: \"none\",\n      background: active\n        ? \"var(--pill-active-bg, #171717)\"\n        : \"var(--pill-bg, #f4f4f5)\",\n      color: active ? \"var(--pill-active-fg, #fff)\" : \"var(--pill-fg, #52525b)\",\n      fontSize: 12,\n      fontWeight: 500,\n      cursor: \"pointer\",\n      fontFamily: \"ui-monospace, SFMono-Regular, monospace\",\n      transition:\n        \"background 160ms ease-out, color 160ms ease-out, transform 160ms ease-out\",\n      letterSpacing: \"0.02em\",\n    }) as React.CSSProperties\n\n  return (\n    <div\n      data-slot=\"deploy-globe-controls\"\n      style={{\n        display: \"flex\",\n        flexDirection: \"column\",\n        gap: 12,\n        alignItems: \"center\",\n        marginBottom: 28,\n      }}\n    >\n      <div\n        style={{\n          display: \"flex\",\n          gap: 6,\n          alignItems: \"center\",\n          flexWrap: \"wrap\",\n          justifyContent: \"center\",\n        }}\n      >\n        <span\n          style={{\n            fontSize: 11,\n            color: \"var(--color-muted-foreground, #a1a1aa)\",\n            fontWeight: 500,\n            textTransform: \"uppercase\",\n            letterSpacing: \"0.08em\",\n            marginRight: 4,\n          }}\n        >\n          Status\n        </span>\n        {([\"deploying\", \"deployed\", \"error\"] as DeployStatus[]).map(\n          (nextStatus) => (\n            <button\n              key={nextStatus}\n              onClick={() => onStatusChange(nextStatus)}\n              onMouseDown={(event) => {\n                event.currentTarget.style.transform = \"scale(0.95)\"\n              }}\n              onMouseLeave={(event) => {\n                event.currentTarget.style.transform = \"scale(1)\"\n              }}\n              onMouseUp={(event) => {\n                event.currentTarget.style.transform = \"scale(1)\"\n              }}\n              style={pill(status === nextStatus)}\n              type=\"button\"\n            >\n              {nextStatus}\n            </button>\n          )\n        )}\n      </div>\n\n      <div\n        style={{\n          display: \"flex\",\n          gap: 6,\n          alignItems: \"center\",\n          flexWrap: \"wrap\",\n          justifyContent: \"center\",\n        }}\n      >\n        <span\n          style={{\n            fontSize: 11,\n            color: \"var(--color-muted-foreground, #a1a1aa)\",\n            fontWeight: 500,\n            textTransform: \"uppercase\",\n            letterSpacing: \"0.08em\",\n            marginRight: 4,\n          }}\n        >\n          Icons\n        </span>\n        {(Object.keys(NODE_PRESETS) as NodePresetName[]).map((nextPreset) => (\n          <button\n            key={nextPreset}\n            onClick={() => onPresetChange(nextPreset)}\n            onMouseDown={(event) => {\n              event.currentTarget.style.transform = \"scale(0.95)\"\n            }}\n            onMouseLeave={(event) => {\n              event.currentTarget.style.transform = \"scale(1)\"\n            }}\n            onMouseUp={(event) => {\n              event.currentTarget.style.transform = \"scale(1)\"\n            }}\n            style={pill(preset === nextPreset)}\n            type=\"button\"\n          >\n            {nextPreset}\n          </button>\n        ))}\n      </div>\n\n      <div\n        style={{\n          display: \"flex\",\n          gap: 6,\n          alignItems: \"center\",\n          flexWrap: \"wrap\",\n          justifyContent: \"center\",\n        }}\n      >\n        <span\n          style={{\n            fontSize: 11,\n            color: \"var(--color-muted-foreground, #a1a1aa)\",\n            fontWeight: 500,\n            textTransform: \"uppercase\",\n            letterSpacing: \"0.08em\",\n            marginRight: 4,\n          }}\n        >\n          Color\n        </span>\n        {COLOR_PRESETS.map((colorPreset) => (\n          <button\n            key={colorPreset.label}\n            onClick={() => onAccentChange(colorPreset.color)}\n            onMouseDown={(event) => {\n              event.currentTarget.style.transform = \"scale(0.88)\"\n            }}\n            onMouseLeave={(event) => {\n              event.currentTarget.style.transform = \"scale(1)\"\n            }}\n            onMouseUp={(event) => {\n              event.currentTarget.style.transform = \"scale(1)\"\n            }}\n            style={{\n              width: 24,\n              height: 24,\n              borderRadius: 999,\n              border: \"none\",\n              background: colorPreset.color,\n              cursor: \"pointer\",\n              boxShadow:\n                accent === colorPreset.color\n                  ? `0 0 0 2px var(--globe-bg, var(--color-background, #fafafa)), 0 0 0 4px ${colorPreset.color}`\n                  : \"0 0 0 1px var(--color-border, rgba(0,0,0,0.08))\",\n              transition: \"box-shadow 160ms ease-out, transform 160ms ease-out\",\n            }}\n            title={colorPreset.label}\n            type=\"button\"\n          />\n        ))}\n      </div>\n    </div>\n  )\n}\n\nconst TRAJ = {\n  a: [\n    [3, 0],\n    [3, 1],\n    [3, 2],\n    [3, 3],\n  ] as TrajectoryStep[],\n  b: [\n    [-3, 0],\n    [-3, 1],\n    [-3, 2],\n    [-3, 3],\n    [-3, 4],\n  ] as TrajectoryStep[],\n  c: [\n    [1, 0],\n    [1, 1],\n    [1, 2],\n  ] as TrajectoryStep[],\n  d: [\n    [-1, 0],\n    [-1, 1],\n    [-2, 1],\n  ] as TrajectoryStep[],\n  e: [\n    [0, 0],\n    [0, 1],\n    [0, 2],\n    [0, 3],\n    [-1, 3],\n  ] as TrajectoryStep[],\n  f: [\n    [2, 0],\n    [2, 1],\n  ] as TrajectoryStep[],\n}\n\nconst DEMO_DOTS: DeployNode[] = [\n  { id: \"a\", trajectory: TRAJ.a },\n  { id: \"b\", trajectory: TRAJ.b },\n  { id: \"c\", trajectory: TRAJ.c },\n  { id: \"d\", trajectory: TRAJ.d },\n  { id: \"e\", trajectory: TRAJ.e },\n  { id: \"f\", trajectory: TRAJ.f },\n]\n\nconst DEMO_TRIANGLES: DeployNode[] = [\n  { id: \"a\", trajectory: TRAJ.a, icon: \"triangle\" },\n  { id: \"b\", trajectory: TRAJ.b, icon: \"triangle\" },\n  { id: \"c\", trajectory: TRAJ.c, icon: \"triangle\" },\n  { id: \"d\", trajectory: TRAJ.d, icon: \"triangle\" },\n  { id: \"e\", trajectory: TRAJ.e, icon: \"triangle\" },\n  { id: \"f\", trajectory: TRAJ.f, icon: \"triangle\" },\n]\n\nconst DEMO_INFRA: DeployNode[] = [\n  { id: \"a\", trajectory: TRAJ.a, icon: \"server\" },\n  { id: \"b\", trajectory: TRAJ.b, icon: \"database\" },\n  { id: \"c\", trajectory: TRAJ.c, icon: \"cloud\" },\n  { id: \"d\", trajectory: TRAJ.d, icon: \"shield\" },\n  { id: \"e\", trajectory: TRAJ.e, icon: \"globe\" },\n  { id: \"f\", trajectory: TRAJ.f, icon: \"zap\" },\n]\n\nconst DEMO_CUSTOM: DeployNode[] = [\n  {\n    id: \"a\",\n    trajectory: TRAJ.a,\n    icon: { d: \"M12 2L2 22h20L12 2z\", viewBox: \"0 0 24 24\" },\n  },\n  {\n    id: \"b\",\n    trajectory: TRAJ.b,\n    icon: { d: \"M4 4h16v16H4z\", viewBox: \"0 0 24 24\" },\n  },\n  {\n    id: \"c\",\n    trajectory: TRAJ.c,\n    icon: { d: \"M12 3a9 9 0 100 18 9 9 0 000-18z\", viewBox: \"0 0 24 24\" },\n  },\n  {\n    id: \"d\",\n    trajectory: TRAJ.d,\n    icon: {\n      d: \"M12 2l8 4v6c0 5-3.8 9.7-8 10-4.2-.3-8-5-8-10V6l8-4z\",\n      viewBox: \"0 0 24 24\",\n    },\n  },\n  {\n    id: \"e\",\n    trajectory: TRAJ.e,\n    icon: { d: \"M12 2v8h8v4h-8v8h-4v-8H0v-4h8V2z\", viewBox: \"0 0 24 24\" },\n  },\n  {\n    id: \"f\",\n    trajectory: TRAJ.f,\n    icon: { d: \"M12 1l10 6v10l-10 6L2 17V7l10-6z\", viewBox: \"0 0 24 24\" },\n  },\n]\n\nconst NODE_PRESETS = {\n  Dots: DEMO_DOTS,\n  Vercel: DEMO_TRIANGLES,\n  Infra: DEMO_INFRA,\n  Custom: DEMO_CUSTOM,\n} as const\n\ntype NodePresetName = keyof typeof NODE_PRESETS\n\nconst COLOR_PRESETS = [\n  { label: \"Blue\", color: \"#3B82F6\" },\n  { label: \"Teal\", color: \"#45DEC4\" },\n  { label: \"Purple\", color: \"#A855F7\" },\n  { label: \"Rose\", color: \"#F43F5E\" },\n  { label: \"Amber\", color: \"#F59E0B\" },\n]\n\nfunction DemoGlobeVercel() {\n  const [status, setStatus] = useState<DeployStatus>(\"deployed\")\n  const [accent, setAccent] = useState(\"#3B82F6\")\n  const [preset, setPreset] = useState<NodePresetName>(\"Vercel\")\n  const activeNodes = NODE_PRESETS[preset]\n\n  return (\n    <div\n      className=\"relative min-w-xl\"\n      data-slot=\"deploy-globe-demo\"\n      style={{\n        minHeight: \"100vh\",\n        // background: \"#fafafa\",\n        background: \"transparent\",\n        display: \"flex\",\n        flexDirection: \"column\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        padding: \"32px 20px\",\n        fontFamily: \"'Inter', -apple-system, sans-serif\",\n        WebkitFontSmoothing: \"antialiased\",\n        [\"--pill-active-bg\" as string]: \"var(--color-foreground, #171717)\",\n        [\"--pill-active-fg\" as string]: \"var(--color-background, #fff)\",\n        [\"--pill-bg\" as string]: \"var(--color-muted, #f4f4f5)\",\n        [\"--pill-fg\" as string]: \"var(--color-muted-foreground, #52525b)\",\n      }}\n    >\n      <DemoGlobeVercelControls\n        accent={accent}\n        onAccentChange={setAccent}\n        onPresetChange={setPreset}\n        onStatusChange={setStatus}\n        preset={preset}\n        status={status}\n      />\n\n      <div\n        data-slot=\"deploy-globe-demo-stage\"\n        style={{ width: \"100%\", maxWidth: 680 }}\n      >\n        <DeployGlobe\n          accentColor={status === \"error\" ? undefined : accent}\n          duration={4.5}\n          nodes={activeNodes}\n          status={status}\n        />\n      </div>\n\n      <div className=\"mask-b-from-100% mask-t-from-60% pointer-events-none absolute right-0 bottom-0 left-0 h-96 bg-background\" />\n    </div>\n  )\n}\n\nexport {\n  MERIDIANS,\n  ROWS,\n  DEFAULT_NODES,\n  GlobeWireframe,\n  DeployPath,\n  NodeMarker,\n  DeployGlobeSvg,\n  DeployGlobeStatusBadge,\n  DeployGlobe,\n  DemoGlobeVercelControls,\n  DemoGlobeVercel,\n}\n\nexport default DemoGlobeVercel\n",
      "type": "registry:ui"
    }
  ]
}