{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chord",
  "type": "registry:component",
  "title": "Chord",
  "description": "Flows between peers, around a circle. Sample data: bridged volume between five chains, Base +$319M net in, Polygon −$146M net out.",
  "registryDependencies": [
    "@chartory/core",
    "@chartory/plot-chord"
  ],
  "files": [
    {
      "path": "registry/chartory/components/chord.tsx",
      "type": "registry:component",
      "target": "components/chartory/chord.tsx",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// Chord (Chartory chart 048): sizes xs | s | m | l, laid out again from the data at each\n// size; collection \"hearth\" (default) or \"signal\" (with @chartory/signal, Code and Complete). Data in the type's shape (default: the sample). Docs:\n// https://usechartory.com/docs/charts/chord\nimport { ChartView, type ChartViewProps } from \"@/lib/chartory/charts/chart-view\"\nimport { registerChart } from \"@/lib/chartory/charts/defs\"\nimport { data as sample } from \"@/lib/chartory/charts/types/048-chord/data\"\nimport { def } from \"@/lib/chartory/charts/types/048-chord/layout\"\n\nregisterChart(def, sample)\n\nexport type ChordData = typeof sample\nexport type ChordProps = Omit<ChartViewProps, \"def\" | \"data\" | \"states\"> & {\n  /** the data to draw (default: the sample) */\n  data?: ChordData\n  /** further data states: the chart morphs to them (update) or ticks through them (live, with interval) */\n  states?: ChordData[]\n}\n\nexport function Chord(props: ChordProps) {\n  return <ChartView def={def} {...props} />\n}\n"
    },
    {
      "path": "registry/chartory/charts/types/048-chord/data.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/048-chord/data.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 048 Chord · sample data (Figma chart/048, crypto: bridged volume between 5 chains this week, $M; [from, to, v]).\nimport type { ChordData } from \"../../plots/chord\";\n\nexport const data: ChordData = {\n  fmt: (v) => \"$\" + v + \"M\",\n  nodes: [\n    { label: \"Ethereum\", tok: \"data/cat-2\" },\n    { label: \"Arbitrum\", tok: \"data/cat-3\" },\n    { label: \"Base\", tok: \"data/primary\" },\n    { label: \"Solana\", tok: \"data/cat-4\" },\n    { label: \"Polygon\", tok: \"data/cat-5\" },\n  ],\n  flows: [[0, 1, 210], [0, 2, 190], [1, 0, 160], [2, 0, 120], [0, 3, 95], [3, 0, 70], [1, 2, 105], [4, 0, 73], [3, 2, 144], [4, 1, 73]],\n};\n"
    },
    {
      "path": "registry/chartory/charts/types/048-chord/layout.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/048-chord/layout.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 048 Chord (Figma chart/048): C.chord; no legend (the side list names every chain).\nimport type { ChartDef, TooltipSpec } from \"../../core/shell\";\nimport { chord } from \"../../plots/chord\";\nimport { data } from \"./data\";\n\nconst tot = data.flows.reduce((a, f) => a + f[2], 0);\nconst net = data.nodes.map((_, i) => data.flows.reduce((s, f) => s + (f[1] === i ? f[2] : 0) - (f[0] === i ? f[2] : 0), 0));\nconst signed = (v: number) => (v > 0 ? \"+\" : v < 0 ? \"−\" : \"\") + data.fmt(Math.abs(v));\nconst top = net.indexOf(Math.max(...net)), low = net.indexOf(Math.min(...net));\n\n// Ribbon ends are polylines with one point per started 0.08 rad of their flow's arc, so a flow keeps its point count\n// only while its angle stays in the same 0.08 rad step (angles are shares of all flows: k rad per $M).\nconst GAP = 0.06, STEP = 0.08;\nconst kOf = (fl: [number, number, number][], n: number) => (2 * Math.PI - n * GAP) / (2 * fl.reduce((a, f) => a + f[2], 0));\nconst pts = (fl: [number, number, number][], n: number) => { const k = kOf(fl, n); return fl.map((f) => Math.max(2, Math.ceil((f[2] * k) / STEP))); };\nconst netOf = (fl: [number, number, number][], n: number) => Array.from({ length: n }, (_, i) => fl.reduce((s, f) => s + (f[1] === i ? f[2] : 0) - (f[0] === i ? f[2] : 0), 0));\n\n/** Next week's bridging, `step` on: every flow moves up to ±12 % (whole $M), pulled back inside its 0.08 rad step;\n *  each chain keeps its direction (net in / net out). The first amplitude (of 1, ¾, ½, ¼) that keeps both wins. */\nfunction later(o: typeof data, step: number): typeof data {\n  const n = o.nodes.length, p0 = pts(o.flows, n), s0 = netOf(o.flows, n).map(Math.sign);\n  for (const a of [1, 0.75, 0.5, 0.25]) {\n    let fl = o.flows.map(([f, t, v], i) => [f, t, v * (1 + a * 0.12 * Math.sin(step * 1.8 + i * 1.1))] as [number, number, number]);\n    for (let it = 0; it < 6; it++) {\n      const k = kOf(fl, n);\n      fl = fl.map(([f, t, v], i) => [f, t, Math.round(Math.min(Math.max(v * k, (p0[i] - 1) * STEP + 0.006), p0[i] * STEP - 0.006) / k)]);\n    }\n    if (pts(fl, n).every((x, i) => x === p0[i]) && netOf(fl, n).every((x, i) => Math.sign(x) === s0[i])) return { ...o, flows: fl };\n  }\n  return o;\n}\n\n/** Tooltip for one chain (group = its arc and the ribbons it sends): where its volume went, the total sent, and its\n *  net flow (in − out). */\nfunction tipOf(o: typeof data, g: number): TooltipSpec | null {\n  const n = o.nodes[g];\n  if (!n) return null;\n  const out = o.flows.filter((f) => f[0] === g), sent = out.reduce((s, f) => s + f[2], 0);\n  const nt = o.flows.reduce((s, f) => s + (f[1] === g ? f[2] : 0), 0) - sent;\n  return {\n    kind: \"series\", caps: `${n.label.toUpperCase()} · NET ${(nt > 0 ? \"+\" : nt < 0 ? \"−\" : \"\") + o.fmt(Math.abs(nt))}`,\n    rows: out.map(([, t, v]) => ({ label: \"To \" + o.nodes[t].label, value: o.fmt(v), swatch: o.nodes[t].tok })), total: { label: \"Sent\", value: o.fmt(sent) },\n  };\n}\n\nexport const def: ChartDef = {\n  no: 48,\n  slug: \"chord\",\n  legend: null,\n  // update: next week's bridging (see later)\n  update: (d, step) => later(d as typeof data, step),\n  plot: (K, p, w, d) => chord(K, p, w, (d as typeof data | undefined) ?? data),\n  tip: (d, g) => tipOf(d as typeof data, g),\n  describe: {\n    title: \"Bridge flows between chains\",\n    summary: `${data.fmt(tot)} bridged between ${data.nodes.length} chains this week; ${data.nodes[top].label} has the largest net inflow (${signed(net[top])}), ${data.nodes[low].label} the largest net outflow (${signed(net[low])}).`,\n    table: {\n      head: [\"From\", \"To\", \"Volume\"],\n      rows: data.flows.map(([f, t, v]) => [data.nodes[f].label, data.nodes[t].label, data.fmt(v)]),\n    },\n  },\n};\n"
    }
  ],
  "categories": [
    "charts",
    "flow-between-steps"
  ],
  "meta": {
    "access": "free",
    "no": 48,
    "family": "Flow between steps"
  }
}
