{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "user-graph",
  "title": "User Graph",
  "author": "AlgorizeTech",
  "description": "An orbital diagram placing a user at the centre with tools and sub-agents circling around. Use it to illustrate an AI agent or integration ecosystem.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://avaraui.com/r/icons.json",
    "utils"
  ],
  "files": [
    {
      "path": "src/components/custom/blocks/UserGraphBlock.tsx",
      "content": "\"use client\";\n\nimport React, { useState, useRef, useEffect } from \"react\";\nimport {\n  Mic,\n  Phone,\n  MessageSquare,\n  Send,\n  Mail,\n  Code2,\n  Plug,\n  User,\n  Globe,\n  BarChart3,\n  BookOpen,\n  Database,\n  CreditCard,\n  MessageCircle,\n  Network,\n  Share2,\n} from \"lucide-react\";\nimport { GitHubIcon } from \"./Icons\";\nimport { cn } from \"@/lib/utils\";\n\n// --- Types ---\nexport interface OrbitTool {\n  id: string;\n  name: string;\n  icon: React.ElementType;\n}\n\nexport interface SubApp {\n  id: string;\n  name: string;\n  icon: React.ElementType;\n  bgColor: string;\n  textColor: string;\n  borderColor?: string;\n  customText?: string;\n}\n\nexport interface SubAgent {\n  id: string;\n  title: string;\n  role: string;\n  avatarUrl: string;\n  status: \"online\" | \"idle\" | \"offline\" | string;\n  percentX: number;\n  svgX: number;\n  apps: SubApp[];\n}\n\n// --- Color Mapping Helper ---\nfunction getColorDetails(colorKey: string) {\n  switch (colorKey) {\n    case \"rose\":\n      return {\n        hex: \"#f43f5e\",\n        bgClass: \"bg-rose-600 dark:bg-rose-500 text-white\",\n        textClass: \"text-rose-600 dark:text-rose-400\",\n        ringClass: \"ring-rose-500 border-rose-500\",\n        borderClass: \"border-rose-500\",\n        strokeHex: \"#f43f5e\",\n      };\n    case \"emerald\":\n      return {\n        hex: \"#10b981\",\n        bgClass: \"bg-emerald-600 dark:bg-emerald-500 text-white\",\n        textClass: \"text-emerald-600 dark:text-emerald-400\",\n        ringClass: \"ring-emerald-500 border-emerald-500\",\n        borderClass: \"border-emerald-500\",\n        strokeHex: \"#10b981\",\n      };\n    case \"zinc\":\n      return {\n        hex: \"#71717a\",\n        bgClass: \"bg-primary text-primary-foreground\",\n        textClass: \"text-foreground\",\n        ringClass: \"ring-zinc-500 border-zinc-500\",\n        borderClass: \"border-zinc-500\",\n        strokeHex: \"#71717a\",\n      };\n    case \"indigo\":\n      return {\n        hex: \"#6366f1\",\n        bgClass: \"bg-indigo-600 dark:bg-indigo-500 text-white\",\n        textClass: \"text-indigo-600 dark:text-indigo-400\",\n        ringClass: \"ring-indigo-500 border-indigo-500\",\n        borderClass: \"border-indigo-500\",\n        strokeHex: \"#6366f1\",\n      };\n    case \"primary\":\n    case \"default\":\n    default:\n      if (colorKey.startsWith(\"#\")) {\n        return {\n          hex: colorKey,\n          bgClass: \"bg-primary text-primary-foreground\",\n          textClass: \"text-primary\",\n          ringClass: \"ring-primary/30 border-primary/30\",\n          borderClass: \"border-primary/30\",\n          strokeHex: colorKey,\n        };\n      }\n      return {\n        hex: \"currentColor\",\n        bgClass: \"bg-primary text-primary-foreground\",\n        textClass: \"text-primary\",\n        ringClass: \"ring-primary/30 border-primary/30\",\n        borderClass: \"border-primary/30\",\n        strokeHex: \"var(--primary, #3b82f6)\",\n      };\n  }\n}\n\n// --- Data Configuration ---\nconst DEFAULT_ORBIT_TOOLS: OrbitTool[] = [\n  { id: \"slack\", name: \"Slack\", icon: Share2 },\n  { id: \"whatsapp\", name: \"WhatsApp\", icon: MessageCircle },\n  { id: \"telegram\", name: \"Telegram\", icon: Send },\n  { id: \"email\", name: \"Email\", icon: Mail },\n  { id: \"code\", name: \"Code\", icon: Code2 },\n  { id: \"workflow\", name: \"Workflow\", icon: Network },\n  { id: \"mcp\", name: \"MCP\", icon: Plug },\n  { id: \"tree\", name: \"Tree\", icon: Share2 },\n  { id: \"chat\", name: \"Chat\", icon: MessageSquare },\n  { id: \"voice\", name: \"Voice\", icon: Mic },\n  { id: \"phone\", name: \"Phone\", icon: Phone },\n];\n\nconst DEFAULT_SUB_AGENTS: SubAgent[] = [\n  {\n    id: \"sales\",\n    title: \"Sales Lead\",\n    role: \"Enterprise Conversions\",\n    avatarUrl:\n      \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_300,c_limit/v1790158814/photo-1500648767791-00dcc994a43e-1790158812212.jpg\",\n    status: \"online\",\n    percentX: 10,\n    svgX: 100,\n    apps: [\n      { id: \"hubspot\", name: \"HubSpot\", icon: Globe, bgColor: \"bg-teal-50 dark:bg-teal-950/80\", textColor: \"text-teal-600 dark:text-teal-400\", borderColor: \"border-teal-300 dark:border-teal-700\" },\n      { id: \"salesforce\", name: \"Salesforce\", icon: Globe, bgColor: \"bg-sky-50 dark:bg-sky-950/80\", textColor: \"text-sky-600 dark:text-sky-400\", borderColor: \"border-sky-300 dark:border-sky-700\" },\n      { id: \"stripe\", name: \"Stripe\", icon: CreditCard, bgColor: \"bg-amber-50 dark:bg-amber-950/80\", textColor: \"text-amber-600 dark:text-amber-400\", borderColor: \"border-amber-300 dark:border-amber-700\", customText: \"S\" },\n    ],\n  },\n  {\n    id: \"marketing\",\n    title: \"Marketing Director\",\n    role: \"Global Campaigns\",\n    avatarUrl:\n      \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_300,c_limit/v1790150483/photo-1534528741775-53994a69daeb-1790150482280.jpg\",\n    status: \"online\",\n    percentX: 30,\n    svgX: 300,\n    apps: [\n      { id: \"web\", name: \"Website\", icon: Globe, bgColor: \"bg-muted\", textColor: \"text-foreground\", borderColor: \"border-border\" },\n      { id: \"analytics\", name: \"Analytics\", icon: BarChart3, bgColor: \"bg-blue-50 dark:bg-blue-950/80\", textColor: \"text-blue-600 dark:text-blue-400\", borderColor: \"border-blue-300 dark:border-blue-700\" },\n      { id: \"mailchimp\", name: \"Mailchimp\", icon: Send, bgColor: \"bg-purple-50 dark:bg-purple-950/80\", textColor: \"text-purple-600 dark:text-purple-400\", borderColor: \"border-purple-300 dark:border-purple-700\" },\n    ],\n  },\n  {\n    id: \"support\",\n    title: \"Support Lead\",\n    role: \"Priority Escalations\",\n    avatarUrl:\n      \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_300,c_limit/v1790159965/photo-1519085360753-af0119f7cbe7-1790159970113.jpg\",\n    status: \"idle\",\n    percentX: 50,\n    svgX: 500,\n    apps: [\n      { id: \"notion\", name: \"Notion\", icon: BookOpen, bgColor: \"bg-muted\", textColor: \"text-foreground\", borderColor: \"border-border\" },\n      { id: \"zendesk\", name: \"Zendesk\", icon: Database, bgColor: \"bg-rose-50 dark:bg-rose-950/80\", textColor: \"text-rose-600 dark:text-rose-400\", borderColor: \"border-rose-300 dark:border-rose-700\", customText: \"Z\" },\n      { id: \"intercom\", name: \"Intercom\", icon: MessageSquare, bgColor: \"bg-pink-50 dark:bg-pink-950/80\", textColor: \"text-pink-600 dark:text-pink-400\", borderColor: \"border-pink-300 dark:border-pink-700\" },\n    ],\n  },\n  {\n    id: \"accountant\",\n    title: \"Financial Officer\",\n    role: \"Billing & Audit\",\n    avatarUrl:\n      \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_300,c_limit/v1790158814/photo-1573496359142-b8d87734a5a2-1790158812214.jpg\",\n    status: \"online\",\n    percentX: 70,\n    svgX: 700,\n    apps: [\n      { id: \"quickbooks\", name: \"Books\", icon: CreditCard, bgColor: \"bg-violet-50 dark:bg-violet-950/80\", textColor: \"text-violet-600 dark:text-violet-400\", borderColor: \"border-violet-300 dark:border-violet-700\", customText: \"qb\" },\n      { id: \"xero\", name: \"Xero\", icon: Globe, bgColor: \"bg-orange-50 dark:bg-orange-950/80\", textColor: \"text-orange-600 dark:text-orange-400\", borderColor: \"border-orange-300 dark:border-orange-700\", customText: \"xero\" },\n      { id: \"paypal\", name: \"PayPal\", icon: CreditCard, bgColor: \"bg-blue-50 dark:bg-blue-950/80\", textColor: \"text-blue-600 dark:text-blue-400\", borderColor: \"border-blue-300 dark:border-blue-700\" },\n    ],\n  },\n  {\n    id: \"analyst\",\n    title: \"Data Engineer\",\n    role: \"BI & Pipeline Systems\",\n    avatarUrl:\n      \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_300,c_limit/v1790158814/photo-1522075469751-3a6694fb2f61-1790158812213.jpg\",\n    status: \"online\",\n    percentX: 90,\n    svgX: 900,\n    apps: [\n      { id: \"drive\", name: \"Drive\", icon: Database, bgColor: \"bg-amber-50 dark:bg-amber-950/80\", textColor: \"text-amber-600 dark:text-amber-400\", borderColor: \"border-amber-300 dark:border-amber-700\" },\n      { id: \"sheets\", name: \"Sheets\", icon: BarChart3, bgColor: \"bg-emerald-50 dark:bg-emerald-950/80\", textColor: \"text-emerald-600 dark:text-emerald-400\", borderColor: \"border-emerald-300 dark:border-emerald-700\" },\n      { id: \"github\", name: \"GitHub\", icon: GitHubIcon, bgColor: \"bg-muted\", textColor: \"text-foreground\", borderColor: \"border-border\" },\n    ],\n  },\n];\n\nexport interface UserGraphProps extends React.HTMLAttributes<HTMLDivElement> {\n  color?: string;\n  orbitTools?: OrbitTool[];\n  subAgents?: SubAgent[];\n  orchestrator?: { name: string; role: string; avatarUrl: string };\n  defaultSelectedTool?: string;\n  defaultSelectedAgentId?: string;\n  autoPlay?: boolean;\n  autoPlayInterval?: number;\n}\n\nconst DEFAULT_ORCHESTRATOR = {\n  name: \"John Doe\",\n  role: \"Senior Product Designer\",\n  avatarUrl:\n    \"https://res.cloudinary.com/dodndg7ai/image/upload/f_webp,q_auto,w_300,c_limit/v1790150480/photo-1506794778202-cad84cf45f1d-1790150482275.jpg\",\n};\n\nexport function UserGraph({\n  color = \"primary\",\n  orbitTools = DEFAULT_ORBIT_TOOLS,\n  subAgents = DEFAULT_SUB_AGENTS,\n  orchestrator = DEFAULT_ORCHESTRATOR,\n  defaultSelectedTool = \"phone\",\n  defaultSelectedAgentId,\n  autoPlay = true,\n  autoPlayInterval = 2500,\n  className,\n  ...props\n}: UserGraphProps) {\n  const [selectedTool, setSelectedTool] = useState<string>(defaultSelectedTool);\n  const [selectedAgentId, setSelectedAgentId] = useState<string>(\n    defaultSelectedAgentId || subAgents[0]?.id || \"sales\"\n  );\n  const [hoveredAgentId, setHoveredAgentId] = useState<string | null>(null);\n\n  const containerRef = useRef<HTMLDivElement>(null);\n  const [isSmallScreen, setIsSmallScreen] = useState<boolean>(false);\n\n  useEffect(() => {\n    const handleResize = () => {\n      const width = containerRef.current?.clientWidth || window.innerWidth;\n      setIsSmallScreen(width < 1024 || window.innerWidth < 1024);\n    };\n\n    handleResize();\n    window.addEventListener(\"resize\", handleResize);\n\n    let observer: ResizeObserver | null = null;\n    if (containerRef.current) {\n      observer = new ResizeObserver(() => handleResize());\n      observer.observe(containerRef.current);\n    }\n\n    return () => {\n      window.removeEventListener(\"resize\", handleResize);\n      if (observer) observer.disconnect();\n    };\n  }, []);\n\n  // Auto-switch randomly between orbit tools\n  useEffect(() => {\n    if (!autoPlay || orbitTools.length <= 1) return;\n\n    const timer = setInterval(() => {\n      setSelectedTool((prev) => {\n        const currIdx = orbitTools.findIndex((t) => t.id === prev);\n        let nextIdx: number;\n        do {\n          nextIdx = Math.floor(Math.random() * orbitTools.length);\n        } while (nextIdx === currIdx && orbitTools.length > 1);\n        return orbitTools[nextIdx]?.id || prev;\n      });\n    }, autoPlayInterval);\n\n    return () => clearInterval(timer);\n  }, [autoPlay, autoPlayInterval, orbitTools]);\n\n  const accentColor = getColorDetails(color);\n\n  const selectedToolIndex = orbitTools.findIndex((t) => t.id === selectedTool);\n  const selectedRadius = isSmallScreen ? 115 : 140;\n  let selectedOrbitPos: { x: number; y: number } | null = null;\n  if (selectedToolIndex !== -1) {\n    const angleDegree = (selectedToolIndex / orbitTools.length) * 360 - 90;\n    const rad = (angleDegree * Math.PI) / 180;\n    selectedOrbitPos = {\n      x: selectedRadius * Math.cos(rad),\n      y: selectedRadius * Math.sin(rad),\n    };\n  }\n\n  return (\n    <div\n      ref={containerRef}\n      className={cn(\n        \"w-full min-h-[640px] sm:min-h-[720px] p-2 sm:p-3 bg-transparent flex flex-col items-center overflow-hidden [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]\",\n        className,\n      )}\n      {...props}\n    >\n      <style>{`\n        @keyframes iconBgPulse {\n          0%, 80%, 100% {\n            background-color: transparent;\n            box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);\n          }\n          90% {\n            background-color: rgba(59, 130, 246, 0.45);\n            box-shadow: 0 0 12px 3px rgba(59, 130, 246, 0.6);\n          }\n        }\n        @keyframes line-pulse {\n          0% { stroke-dashoffset: 24; }\n          100% { stroke-dashoffset: 0; }\n        }\n        .active-connector-path {\n          stroke-dasharray: 6 6;\n          animation: line-pulse 1.2s linear infinite;\n        }\n        @keyframes avatar-ripple {\n          0% {\n            transform: scale(0.9);\n            opacity: 0.6;\n          }\n          100% {\n            transform: scale(2.5);\n            opacity: 0;\n          }\n        }\n        .animate-avatar-ripple {\n          animation: avatar-ripple 3s cubic-bezier(0, 0.2, 0.8, 1) infinite;\n        }\n        .animate-avatar-ripple-delayed {\n          animation: avatar-ripple 3s cubic-bezier(0, 0.2, 0.8, 1) 1.5s infinite;\n        }\n      `}</style>\n\n      {/* Theme Dynamic Canvas */}\n      <div className=\"relative w-full rounded-none bg-muted p-3 sm:p-6 overflow-hidden shadow-xs border border-border flex flex-col items-center justify-center transition-colors [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]\">\n        {/* Ambient Dynamic Theme Glow */}\n        <div\n          className={cn(\n            \"pointer-events-none absolute left-1/2 top-1/4 -z-0 h-64 sm:h-96 w-64 sm:w-96 -translate-x-1/2 rounded-full opacity-15 blur-[100px] sm:blur-[120px] transition-colors\",\n            color === \"default\" || color === \"primary\" ? \"bg-primary\" : \"\"\n          )}\n          style={color !== \"default\" && color !== \"primary\" ? { backgroundColor: accentColor.hex } : undefined}\n        />\n\n        <div className=\"relative w-full max-w-5xl mx-auto flex flex-col items-center z-10\">\n          {/* TOP TIER: Concentric Circles & Dashed Orbit Ring */}\n          <div className=\"relative size-[270px] sm:size-[320px] flex items-center justify-center my-2 z-10 scale-95 sm:scale-100\">\n            {/* SVG Active Orbit Ray Line connecting Center to Active Tool */}\n            <svg\n              className=\"pointer-events-none absolute inset-0 size-full overflow-visible z-15\"\n              viewBox=\"-160 -160 320 320\"\n            >\n              {selectedOrbitPos && (\n                <line\n                  x1=\"0\"\n                  y1=\"0\"\n                  x2={selectedOrbitPos.x}\n                  y2={selectedOrbitPos.y}\n                  stroke={accentColor.strokeHex}\n                  strokeWidth=\"2\"\n                  className=\"opacity-75 transition-all duration-300\"\n                />\n              )}\n            </svg>\n\n            {/* Outer Dashed Orbit Circle */}\n            <div className=\"absolute size-[230px] sm:size-[280px] rounded-full border border-dashed border-foreground/20\" />\n\n            {/* Concentric Outer Solid Circle */}\n            <div className=\"absolute size-[140px] sm:size-[170px] rounded-full border border-foreground/20 flex items-center justify-center\" />\n\n            {/* Concentric Inner Ring */}\n            <div\n              className={cn(\n                \"absolute size-[90px] sm:size-[110px] rounded-full border opacity-20\",\n                color === \"default\" || color === \"primary\" ? \"border-primary/30 bg-primary/10\" : \"\"\n              )}\n              style={color !== \"default\" && color !== \"primary\" ? { borderColor: accentColor.hex, backgroundColor: `${accentColor.hex}10` } : undefined}\n            />\n\n            {/* Pulsing Color Waves emanating outward in 4 directions */}\n            <div\n              className={cn(\n                \"absolute size-12 sm:size-16 rounded-full animate-avatar-ripple pointer-events-none z-10\",\n                color === \"default\" || color === \"primary\" ? \"bg-primary\" : \"\"\n              )}\n              style={color !== \"default\" && color !== \"primary\" ? { backgroundColor: accentColor.hex } : undefined}\n            />\n            <div\n              className={cn(\n                \"absolute size-12 sm:size-16 rounded-full animate-avatar-ripple-delayed pointer-events-none z-10\",\n                color === \"default\" || color === \"primary\" ? \"bg-primary\" : \"\"\n              )}\n              style={color !== \"default\" && color !== \"primary\" ? { backgroundColor: accentColor.hex } : undefined}\n            />\n\n            {/* Center Theme-Dynamic User Avatar Circle */}\n            <div\n              className={cn(\n                \"relative z-20 size-12 sm:size-16 rounded-full flex items-center justify-center shadow-md ring-2 sm:ring-4 transition-colors\",\n                accentColor.bgClass,\n                accentColor.ringClass\n              )}\n            >\n              <User className=\"size-5 sm:size-7 stroke-[2.5]\" />\n            </div>\n\n            {/* 11 Orbiting Tool Nodes */}\n            {orbitTools.map((tool, index) => {\n              const angleDegree = (index / orbitTools.length) * 360 - 90;\n              const rad = (angleDegree * Math.PI) / 180;\n              const radius = isSmallScreen ? 115 : 140;\n              const x = radius * Math.cos(rad);\n              const y = radius * Math.sin(rad);\n              const isSelected = selectedTool === tool.id;\n              const isTopHalf = y < -15;\n\n              return (\n                <div\n                  key={tool.id}\n                  style={{ transform: `translate(${x}px, ${y}px)` }}\n                  className={cn(\"absolute flex flex-col items-center\", isSelected ? \"z-50\" : \"z-30\")}\n                >\n                  <button\n                    type=\"button\"\n                    onClick={() => setSelectedTool(tool.id)}\n                    className={cn(\n                      \"size-7 sm:size-9 rounded-full border flex items-center justify-center transition-all duration-200 cursor-pointer shadow-sm group\",\n                      isSelected\n                        ? cn(\"shadow-md ring-2 sm:ring-3 ring-offset-1 ring-offset-muted scale-110\", accentColor.bgClass, accentColor.borderClass, accentColor.ringClass)\n                        : \"bg-card border-border text-muted-foreground hover:border-foreground/30\"\n                    )}\n                  >\n                    <tool.icon className=\"size-3.5 sm:size-4\" />\n                  </button>\n\n                  {isSelected && (\n                    isTopHalf ? (\n                      <div className=\"absolute bottom-full mb-1.5 sm:mb-2 flex flex-col items-center pointer-events-none drop-shadow-md\">\n                        <div className=\"px-2 py-0.5 rounded-md bg-primary text-primary-foreground text-[9px] font-semibold whitespace-nowrap shadow-md\">\n                          {tool.name}\n                        </div>\n                        <div className=\"w-0 h-0 border-x-[5px] border-x-transparent border-t-[5px] border-t-primary -mt-[1px]\" />\n                      </div>\n                    ) : (\n                      <div className=\"absolute top-full mt-1.5 sm:mt-2 flex flex-col items-center pointer-events-none drop-shadow-md\">\n                        <div className=\"w-0 h-0 border-x-[5px] border-x-transparent border-b-[5px] border-b-primary -mb-[1px]\" />\n                        <div className=\"px-2 py-0.5 rounded-md bg-primary text-primary-foreground text-[9px] font-semibold whitespace-nowrap shadow-md\">\n                          {tool.name}\n                        </div>\n                      </div>\n                    )\n                  )}\n                </div>\n              );\n            })}\n          </div>\n\n          {/* DYNAMIC VIEW SWITCH BASED ON CONTAINER OR SCREEN SIZE */}\n          {!isSmallScreen ? (\n            /* DESKTOP & LARGE SCREEN INTEGRATED SVG GRAPH DIAGRAM */\n            <div className=\"relative w-full h-[400px]\">\n              {/* Embedded explicit keyframes for line flow animation */}\n              <style>{`\n                @keyframes linePulseFlow {\n                  0% {\n                    stroke-dashoffset: 1000;\n                  }\n                  100% {\n                    stroke-dashoffset: -1000;\n                  }\n                }\n              `}</style>\n\n              {/* SVG Precision Connector Lines Layer */}\n              <svg\n                className=\"pointer-events-none absolute inset-0 size-full overflow-visible\"\n                viewBox=\"0 0 1000 400\"\n                preserveAspectRatio=\"none\"\n                style={{ zIndex: 1 }}\n              >\n                <defs>\n                  <linearGradient id=\"lineGradientPulse\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"0%\">\n                    <stop offset=\"0%\" stopColor={accentColor.strokeHex} stopOpacity=\"0.1\" />\n                    <stop offset=\"25%\" stopColor={accentColor.strokeHex} stopOpacity=\"0.9\" />\n                    <stop offset=\"50%\" stopColor={accentColor.strokeHex} stopOpacity=\"1\" />\n                    <stop offset=\"75%\" stopColor={accentColor.strokeHex} stopOpacity=\"0.9\" />\n                    <stop offset=\"100%\" stopColor={accentColor.strokeHex} stopOpacity=\"0.1\" />\n                  </linearGradient>\n                </defs>\n\n                {/* 1. Line from Top User Avatar to Orchestrator Avatar Top */}\n                <line\n                  x1=\"500\"\n                  y1=\"-135\"\n                  x2=\"500\"\n                  y2=\"47\"\n                  stroke={accentColor.strokeHex}\n                  strokeWidth=\"2\"\n                  className=\"opacity-60\"\n                />\n\n                {/* 2. Outer & Inner Arched Curves + Vertical Line with Traveling Dot Animation */}\n                {/* Outer Arc (1st -> 5th sub-agent) */}\n                <g>\n                  <path\n                    d=\"M 100 272 C 220 5, 780 5, 900 272\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                    strokeWidth=\"1.0\"\n                    strokeLinecap=\"round\"\n                    className=\"text-muted-foreground opacity-60 dark:opacity-75\"\n                  />\n                  <path\n                    d=\"M 100 272 C 220 5, 780 5, 900 272\"\n                    fill=\"none\"\n                    stroke=\"url(#lineGradientPulse)\"\n                    strokeWidth=\"2.0\"\n                    strokeLinecap=\"round\"\n                    strokeDasharray=\"70 800\"\n                    style={{\n                      animation: \"linePulseFlow 9s linear infinite\",\n                    }}\n                  />\n                </g>\n\n                {/* Inner Arc (2nd -> 4th sub-agent) */}\n                <g>\n                  <path\n                    d=\"M 300 272 C 380 5, 620 5, 700 272\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                    strokeWidth=\"1.0\"\n                    strokeLinecap=\"round\"\n                    className=\"text-muted-foreground opacity-60 dark:opacity-75\"\n                  />\n                  <path\n                    d=\"M 300 272 C 380 5, 620 5, 700 272\"\n                    fill=\"none\"\n                    stroke=\"url(#lineGradientPulse)\"\n                    strokeWidth=\"2.0\"\n                    strokeLinecap=\"round\"\n                    strokeDasharray=\"50 600\"\n                    style={{\n                      animation: \"linePulseFlow 9s linear infinite\",\n                      animationDelay: \"2.1s\",\n                    }}\n                  />\n                </g>\n\n                {/* Middle Vertical Line */}\n                <g>\n                  <path\n                    d=\"M 500 5 L 500 272\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                    strokeWidth=\"1.0\"\n                    strokeLinecap=\"round\"\n                    className=\"text-muted-foreground opacity-60 dark:opacity-75\"\n                  />\n                  <path\n                    d=\"M 500 5 L 500 272\"\n                    fill=\"none\"\n                    stroke=\"url(#lineGradientPulse)\"\n                    strokeWidth=\"2.0\"\n                    strokeLinecap=\"round\"\n                    strokeDasharray=\"30 250\"\n                    style={{\n                      animation: \"linePulseFlow 9s linear infinite\",\n                      animationDelay: \"4.2s\",\n                    }}\n                  />\n                </g>\n\n                {/* 3 Radiating Sub-App Lines per agent with Pulse Animation */}\n                {subAgents.map((agent, idx) => (\n                  <g key={agent.id}>\n                    {/* Passive Base Lines */}\n                    <line\n                      x1={agent.svgX}\n                      y1={272}\n                      x2={agent.svgX - 60}\n                      y2={340}\n                      stroke=\"currentColor\"\n                      strokeWidth=\"1.0\"\n                      className=\"text-muted-foreground opacity-60 dark:opacity-75\"\n                    />\n                    <line\n                      x1={agent.svgX}\n                      y1={272}\n                      x2={agent.svgX}\n                      y2={340}\n                      stroke=\"currentColor\"\n                      strokeWidth=\"1.0\"\n                      className=\"text-muted-foreground opacity-60 dark:opacity-75\"\n                    />\n                    <line\n                      x1={agent.svgX}\n                      y1={272}\n                      x2={agent.svgX + 60}\n                      y2={340}\n                      stroke=\"currentColor\"\n                      strokeWidth=\"1.0\"\n                      className=\"text-muted-foreground opacity-60 dark:opacity-75\"\n                    />\n\n                    {/* Animated Flowing Pulse Lines */}\n                    <line\n                      x1={agent.svgX}\n                      y1={272}\n                      x2={agent.svgX - 60}\n                      y2={340}\n                      stroke=\"url(#lineGradientPulse)\"\n                      strokeWidth=\"1.6\"\n                      strokeLinecap=\"round\"\n                      strokeDasharray=\"20 150\"\n                      style={{\n                        animation: \"linePulseFlow 9s linear infinite\",\n                        animationDelay: `${idx * 1.35}s`,\n                      }}\n                    />\n                    <line\n                      x1={agent.svgX}\n                      y1={272}\n                      x2={agent.svgX}\n                      y2={340}\n                      stroke=\"url(#lineGradientPulse)\"\n                      strokeWidth=\"1.6\"\n                      strokeLinecap=\"round\"\n                      strokeDasharray=\"20 150\"\n                      style={{\n                        animation: \"linePulseFlow 9s linear infinite\",\n                        animationDelay: `${idx * 1.35 + 0.6}s`,\n                      }}\n                    />\n                    <line\n                      x1={agent.svgX}\n                      y1={272}\n                      x2={agent.svgX + 60}\n                      y2={340}\n                      stroke=\"url(#lineGradientPulse)\"\n                      strokeWidth=\"1.6\"\n                      strokeLinecap=\"round\"\n                      strokeDasharray=\"20 150\"\n                      style={{\n                        animation: \"linePulseFlow 9s linear infinite\",\n                        animationDelay: `${idx * 1.35 + 1.2}s`,\n                      }}\n                    />\n                  </g>\n                ))}\n              </svg>\n\n              {/* MIDDLE TIER: Main Orchestrator Avatar Node */}\n              <div className=\"absolute top-[35px] left-1/2 -translate-x-1/2 flex flex-col items-center z-20\">\n                <div\n                  className={cn(\n                    \"relative size-16 rounded-full ring-2 p-0.5 bg-card shadow-md transition-colors\",\n                    accentColor.ringClass\n                  )}\n                >\n                  <img\n                    src={orchestrator.avatarUrl}\n                    alt={orchestrator.name}\n                    className=\"size-full rounded-full object-cover\"\n                  />\n                  <span className=\"absolute bottom-0 right-0.5 size-3 rounded-full bg-emerald-500 ring-2 ring-muted\" />\n                </div>\n                <div className=\"mt-1 text-center\">\n                  <h4 className=\"text-xs font-semibold text-foreground tracking-wide whitespace-nowrap\">{orchestrator.name}</h4>\n                  <p className=\"text-[10px] font-medium text-muted-foreground whitespace-nowrap\">{orchestrator.role}</p>\n                </div>\n              </div>\n\n              {/* BOTTOM TIER: 5 Sub-Agents Grid */}\n              <div className=\"absolute inset-0 z-10\">\n                {subAgents.map((agent) => {\n                  const isHovered = hoveredAgentId === agent.id;\n                  const isAnyHovered = hoveredAgentId !== null;\n                  const isDimmed = isAnyHovered && !isHovered;\n\n                  return (\n                    <div\n                      key={agent.id}\n                      style={{ left: `${agent.percentX}%` }}\n                      onMouseEnter={() => setHoveredAgentId(agent.id)}\n                      onMouseLeave={() => setHoveredAgentId(null)}\n                      className={cn(\n                        \"absolute -translate-x-1/2 flex flex-col items-center w-[19%] top-[205px] transition-all duration-300 ease-out cursor-pointer\",\n                        isHovered ? \"z-30 opacity-100\" : isDimmed ? \"opacity-35 z-10\" : \"opacity-100 z-10\"\n                      )}\n                    >\n                      {/* Sub-Agent Title Header ABOVE Avatar */}\n                      <span className={cn(\"text-[11px] sm:text-xs font-semibold text-center leading-tight mb-2.5 whitespace-nowrap transition-colors\", isHovered ? accentColor.textClass : \"text-foreground\")}>\n                        {agent.title}\n                      </span>\n\n                      {/* Sub-Agent Avatar Circle (Only Avatar Zooms Slightly on Hover with Theme Border) */}\n                      <div\n                        className={cn(\n                          \"relative size-13 rounded-full p-0.5 bg-card shadow-sm mb-10 transition-all duration-300\",\n                          isHovered\n                            ? cn(\"scale-105 shadow-md ring-2\", accentColor.ringClass)\n                            : \"ring-1 ring-foreground/20\"\n                        )}\n                      >\n                        <img\n                          src={agent.avatarUrl}\n                          alt={agent.title}\n                          className=\"size-full rounded-full object-cover\"\n                        />\n                        <span\n                          className={cn(\n                            \"absolute bottom-0 right-0 size-3 rounded-full ring-2 ring-muted\",\n                            agent.status === \"online\"\n                              ? \"bg-emerald-500\"\n                              : agent.status === \"idle\"\n                              ? \"bg-amber-500\"\n                              : \"bg-slate-400\"\n                          )}\n                        />\n                      </div>\n\n                      {/* 3 Sub-App Badges BELOW Avatar with Theme-Colored Hover Tooltips */}\n                      <div className=\"flex items-center gap-7\">\n                        {agent.apps.map((app) => (\n                          <div key={app.id} className=\"relative flex flex-col items-center group/app\">\n                            <div\n                              className={cn(\n                                \"size-7 sm:size-8 rounded-full flex items-center justify-center border shadow-xs transition-transform duration-200 group-hover/app:scale-105\",\n                                app.bgColor,\n                                app.textColor,\n                                app.borderColor || \"border-border\"\n                              )}\n                            >\n                              {app.customText ? (\n                                <span className=\"text-[10px] font-semibold font-mono uppercase tracking-tighter\">\n                                  {app.customText}\n                                </span>\n                              ) : (\n                                <app.icon className=\"size-3 sm:size-4\" />\n                              )}\n                            </div>\n\n                            {/* Sub-App Tooltip shown when agent is hovered with Theme Color Background */}\n                            {isHovered && (\n                              <div className=\"absolute top-full mt-1.5 flex flex-col items-center pointer-events-none drop-shadow-md z-40 animate-in fade-in zoom-in-95 duration-150\">\n                                <div\n                                  className={cn(\n                                    \"w-0 h-0 border-x-[4px] border-x-transparent border-b-[4px] -mb-[1px]\",\n                                    color === \"rose\" ? \"border-b-rose-600 dark:border-b-rose-500\" :\n                                    color === \"emerald\" ? \"border-b-emerald-600 dark:border-b-emerald-500\" :\n                                    color === \"indigo\" ? \"border-b-indigo-600 dark:border-b-indigo-500\" :\n                                    \"border-b-primary\"\n                                  )}\n                                />\n                                <div className={cn(\"px-2 py-0.5 rounded text-[9px] font-semibold whitespace-nowrap shadow-sm\", accentColor.bgClass)}>\n                                  {app.name}\n                                </div>\n                              </div>\n                            )}\n                          </div>\n                        ))}\n                      </div>\n                    </div>\n                  );\n                })}\n              </div>\n            </div>\n          ) : (\n            /* UNIFIED 100% IDENTICAL TIMELINE VIEW FOR MOBILE & MD (MEDIUM) SCREENS */\n            <div className=\"w-full mt-2 flex flex-col items-center gap-4 max-w-sm mx-auto\">\n              {/* Orchestrator Item: Horizontally Centered Avatar & Card */}\n              <div className=\"w-full flex flex-col items-center gap-2 z-10\">\n                {/* Horizontally Centered Avatar Image */}\n                <div className={cn(\"relative size-14 rounded-md p-0.5 bg-card shadow-md ring-2 mx-auto\", accentColor.ringClass)}>\n                  <img\n                    src={orchestrator.avatarUrl}\n                    alt={orchestrator.name}\n                    className=\"size-full rounded-sm object-cover\"\n                  />\n                  <span className=\"absolute -bottom-1 -right-1 size-3.5 rounded-xs bg-emerald-500 ring-2 ring-card\" />\n                </div>\n\n                {/* Horizontally Centered Info Card */}\n                <div className=\"w-full flex items-center justify-between p-3.5 rounded-xl bg-card border-2 border-transparent shadow-xs\">\n                  <div className=\"min-w-0 pr-2\">\n                    <h4 className=\"text-xs sm:text-sm font-semibold text-foreground truncate\">{orchestrator.name}</h4>\n                    <p className=\"text-[10px] sm:text-xs font-medium text-muted-foreground truncate\">{orchestrator.role}</p>\n                  </div>\n                  <span className=\"px-2 py-0.5 sm:px-2.5 sm:py-1 rounded-md text-[10px] sm:text-[11px] font-semibold bg-emerald-50 text-emerald-600 border border-emerald-200 dark:bg-emerald-950/60 dark:text-emerald-400 dark:border-emerald-800 flex-shrink-0 whitespace-nowrap\">\n                    Active Lead\n                  </span>\n                </div>\n              </div>\n\n              {/* Sub-Agents Vertical Timeline Container */}\n              <div className=\"relative w-full flex flex-col gap-4 pl-1\">\n                {/* Continuous Vertical Line passing straight through sub-agent avatars */}\n                <div className=\"absolute left-[24px] top-4 bottom-6 w-0.5 bg-foreground/20 z-0\" />\n\n                {/* Sub-Agent Timeline Items */}\n                {subAgents.map((agent, idx) => {\n                  const isActive = selectedAgentId === agent.id;\n\n                  return (\n                    <div\n                      key={agent.id}\n                      onClick={() => setSelectedAgentId(agent.id)}\n                      className=\"relative z-10 flex items-start gap-4 cursor-pointer group\"\n                    >\n                      {/* Left Avatar Column with Numbered Badge */}\n                      <div className=\"relative flex-shrink-0\">\n                        {/* Numbered Badge */}\n                        <div className=\"absolute -top-1 -left-1 size-4 bg-primary text-primary-foreground font-semibold text-[9px] rounded-xs flex items-center justify-center z-20 shadow-xs\">\n                          {idx + 1}\n                        </div>\n\n                        {/* Avatar Image Frame */}\n                        <div\n                          className={cn(\n                            \"relative size-12 rounded-md p-0.5 bg-card shadow-xs transition-all\",\n                            isActive\n                              ? cn(\"ring-2 scale-105\", accentColor.ringClass)\n                              : \"ring-1 ring-foreground/20 group-hover:scale-105\"\n                          )}\n                        >\n                          <img\n                            src={agent.avatarUrl}\n                            alt={agent.title}\n                            className=\"size-full rounded-sm object-cover\"\n                          />\n                          <span\n                            className={cn(\n                              \"absolute -bottom-1 -right-1 size-2.5 rounded-xs ring-2 ring-card\",\n                              agent.status === \"online\"\n                                ? \"bg-emerald-500\"\n                                : agent.status === \"idle\"\n                                ? \"bg-amber-500\"\n                                : \"bg-slate-400\"\n                            )}\n                          />\n                        </div>\n                      </div>\n\n                      {/* Right Sub-Agent Content Card (Constant border-2 to prevent jumping) */}\n                      <div\n                        className={cn(\n                          \"flex-1 min-w-0 p-3.5 rounded-xl border-2 shadow-xs transition-colors duration-200 bg-card\",\n                          isActive\n                            ? accentColor.borderClass\n                            : \"border-transparent group-hover:border-foreground/20\"\n                        )}\n                      >\n                        {/* Card Header Row */}\n                        <div className=\"flex items-center justify-between gap-1.5\">\n                          <h5 className={cn(\"text-xs sm:text-sm font-semibold truncate\", isActive ? accentColor.textClass : \"text-foreground\")}>\n                            {agent.title}\n                          </h5>\n                          <span className=\"text-[9px] sm:text-[10px] font-mono font-semibold uppercase text-muted-foreground tracking-wider flex-shrink-0 whitespace-nowrap\">\n                            AGENT 0{idx + 1}\n                          </span>\n                        </div>\n\n                        {/* Divider Line */}\n                        <div className=\"h-[1px] bg-border/60 my-2.5\" />\n\n                        {/* Apps Footer Row */}\n                        <div className=\"flex items-center gap-2\">\n                          <span className=\"text-[11px] sm:text-xs font-medium text-muted-foreground flex-shrink-0\">Apps:</span>\n                          <div className=\"flex items-center gap-1.5 flex-wrap\">\n                            {agent.apps.map((app) => (\n                              <div\n                                key={app.id}\n                                className={cn(\n                                  \"size-6 sm:size-7 rounded-md flex items-center justify-center border text-[9px] sm:text-[10px] font-semibold shadow-2xs transition-transform group-hover:scale-105 flex-shrink-0\",\n                                  app.bgColor,\n                                  app.textColor,\n                                  app.borderColor || \"border-border\"\n                                )}\n                                title={app.name}\n                              >\n                                {app.customText ? app.customText : <app.icon className=\"size-3 sm:size-3.5\" />}\n                              </div>\n                            ))}\n                          </div>\n                        </div>\n                      </div>\n                    </div>\n                  );\n                })}\n              </div>\n            </div>\n          )}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default UserGraph;\n",
      "type": "registry:component",
      "target": "components/custom/blocks/UserGraphBlock.tsx"
    }
  ],
  "docs": "Named exports: UserGraph. Takes orbiting tools, sub-apps and sub-agents, each with its own icon and colour tokens. Presentational — the layout is decorative, not a real graph engine, so it will not lay out arbitrary node counts gracefully.",
  "categories": [
    "data-display",
    "marketing"
  ],
  "type": "registry:component"
}
