{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "integrations-hub-diagram",
  "title": "Integrations — Hub Diagram",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/integrations-hub-diagram/IntegrationsHubDiagram.tsx",
      "content": "'use client'\n\nimport { ArrowRight, Boxes } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent } from '@/components/ui/card'\n\nconst nodes = [\n  { mark: 'SF', name: 'Snowflake', direction: 'source' },\n  { mark: 'BQ', name: 'BigQuery', direction: 'source' },\n  { mark: 'PG', name: 'Postgres', direction: 'source' },\n  { mark: 'DT', name: 'dbt', direction: 'source' },\n  { mark: 'SL', name: 'Slack', direction: 'destination' },\n  { mark: 'PD', name: 'PagerDuty', direction: 'destination' },\n  { mark: 'LK', name: 'Looker', direction: 'destination' },\n  { mark: 'HX', name: 'Hex', direction: 'destination' },\n]\n\n// Positions are computed on a circle in a 100x100 user-space viewBox, so any\n// number of nodes distributes evenly without touching the markup.\nconst RADIUS = 38\nconst placed = nodes.map((node, index) => {\n  const angle = (index / nodes.length) * Math.PI * 2 - Math.PI / 2\n  return { ...node, x: 50 + RADIUS * Math.cos(angle), y: 50 + RADIUS * Math.sin(angle) }\n})\n\nexport function IntegrationsHubDiagram() {\n  return (\n    <section data-slot=\"integrations-hub-diagram\" className=\"bg-background\">\n      <div className=\"mx-auto max-w-6xl px-6 py-20 lg:py-28\">\n        <div className=\"grid items-center gap-12 lg:grid-cols-[1fr_1.1fr] lg:gap-16\">\n          <div>\n            <Badge variant=\"secondary\">Architecture</Badge>\n            <h2 className=\"mt-4 text-3xl font-semibold tracking-tight text-balance sm:text-4xl\">\n              One hub between your warehouse and everything downstream\n            </h2>\n            <p className=\"text-muted-foreground mt-3 leading-relaxed\">\n              Sources stay read-only and in place. Destinations receive certified metrics rather than raw extracts, so\n              nothing downstream can quietly redefine a number.\n            </p>\n\n            <dl className=\"mt-8 flex flex-wrap gap-x-8 gap-y-3 text-sm\">\n              <div className=\"flex items-center gap-2\">\n                <span className=\"bg-primary size-2 rounded-full\" aria-hidden=\"true\" />\n                <dt className=\"font-medium\">Sources</dt>\n                <dd className=\"text-muted-foreground\">read-only</dd>\n              </div>\n              <div className=\"flex items-center gap-2\">\n                <span className=\"bg-muted-foreground/50 size-2 rounded-full\" aria-hidden=\"true\" />\n                <dt className=\"font-medium\">Destinations</dt>\n                <dd className=\"text-muted-foreground\">certified metrics only</dd>\n              </div>\n            </dl>\n\n            <Button className=\"mt-8\">\n              See the reference architecture\n              <ArrowRight className=\"ml-2 size-4\" aria-hidden=\"true\" />\n            </Button>\n          </div>\n\n          <Card>\n            <CardContent className=\"p-6\">\n              <div className=\"relative aspect-square\">\n                {/* Spokes are drawn in the same 100-unit space the nodes are\n                    placed in, so line ends always meet the node centres. */}\n                <svg className=\"absolute inset-0 size-full\" viewBox=\"0 0 100 100\" aria-hidden=\"true\">\n                  {placed.map((node) => (\n                    <line\n                      key={node.name}\n                      x1=\"50\"\n                      y1=\"50\"\n                      x2={node.x}\n                      y2={node.y}\n                      className=\"stroke-border\"\n                      strokeWidth=\"0.4\"\n                      strokeDasharray={node.direction === 'destination' ? '2 2' : undefined}\n                    />\n                  ))}\n                  <circle cx=\"50\" cy=\"50\" r={RADIUS} className=\"stroke-border/60\" fill=\"none\" strokeWidth=\"0.3\" />\n                </svg>\n\n                <div className=\"border-border bg-card absolute top-1/2 left-1/2 flex size-20 -translate-x-1/2 -translate-y-1/2 flex-col items-center justify-center gap-1 rounded-xl border shadow-sm\">\n                  <Boxes className=\"text-primary size-5\" aria-hidden=\"true\" />\n                  <span className=\"text-xs font-semibold tracking-tight\">Metric layer</span>\n                </div>\n\n                {placed.map((node) => (\n                  <div\n                    key={node.name}\n                    className=\"absolute flex -translate-x-1/2 -translate-y-1/2 flex-col items-center gap-1\"\n                    style={{ left: `${node.x}%`, top: `${node.y}%` }}\n                  >\n                    <span\n                      className={`border-border bg-background flex size-10 items-center justify-center rounded-lg border font-mono text-xs font-semibold ${\n                        node.direction === 'source' ? 'text-foreground' : 'text-muted-foreground'\n                      }`}\n                      aria-hidden=\"true\"\n                    >\n                      {node.mark}\n                    </span>\n                    <span className=\"text-muted-foreground text-xs whitespace-nowrap\">{node.name}</span>\n                  </div>\n                ))}\n              </div>\n            </CardContent>\n          </Card>\n        </div>\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:block",
      "target": "~/components/blocks/IntegrationsHubDiagram.tsx"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/badge.json",
    "https://uipkge.dev/r/react/button.json",
    "https://uipkge.dev/r/react/card.json"
  ],
  "description": "Radial integration diagram: a central platform node connected by drawn SVG spokes to eight orbiting service nodes, with a legend splitting sources from destinations.",
  "categories": [
    "marketing"
  ]
}