{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "icicle",
  "type": "registry:component",
  "title": "Icicle",
  "description": "A breakdown in stacked rows. Sample data: inference spend by team and use case, support $18.4K (chat bot $11.2K, triage $7.2K).",
  "registryDependencies": [
    "@chartory/core",
    "@chartory/plot-icicle"
  ],
  "files": [
    {
      "path": "registry/chartory/components/icicle.tsx",
      "type": "registry:component",
      "target": "components/chartory/icicle.tsx",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// Icicle (Chartory chart 068): 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/icicle\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/068-icicle/data\"\nimport { def } from \"@/lib/chartory/charts/types/068-icicle/layout\"\n\nregisterChart(def, sample)\n\nexport type IcicleData = typeof sample\nexport type IcicleProps = Omit<ChartViewProps, \"def\" | \"data\" | \"states\"> & {\n  /** the data to draw (default: the sample) */\n  data?: IcicleData\n  /** further data states: the chart morphs to them (update) or ticks through them (live, with interval) */\n  states?: IcicleData[]\n}\n\nexport function Icicle(props: IcicleProps) {\n  return <ChartView def={def} {...props} />\n}\n"
    },
    {
      "path": "registry/chartory/charts/types/068-icicle/data.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/068-icicle/data.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 068 Icicle · sample data (Figma chart/068, inference spend this month by team and project, $K).\nimport type { IcicleData } from \"../../plots/icicle\";\n\nexport const data: IcicleData = {\n  root: \"All teams\",\n  fmt: (v) => \"$\" + v + \"K\",\n  teams: [\n    { label: \"Support\", v: 18.4, tok: \"data/cat-2\", kids: [{ label: \"Chat bot\", v: 11.2 }, { label: \"Triage\", v: 7.2 }] },\n    { label: \"Search\", v: 12.1, tok: \"data/cat-3\", kids: [{ label: \"Rerank\", v: 7.5 }, { label: \"Embed\", v: 4.6 }] },\n    { label: \"Agents\", v: 10.6, tok: \"data/primary\", kids: [{ label: \"Ops\", v: 6.4 }, { label: \"Sales\", v: 4.2 }] },\n    { label: \"Labs\", v: 7.1, tok: \"data/cat-5\", kids: [{ label: \"Evals\", v: 7.1 }] },\n  ],\n};\n"
    },
    {
      "path": "registry/chartory/charts/types/068-icicle/layout.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/068-icicle/layout.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 068 Icicle (Figma chart/068, \"Inference spend\"): C.icicle, spend by team and project.\nimport { buildVariant, type ChartDef, type Size, type TooltipSpec } from \"../../core/shell\";\nimport { icicle } from \"../../plots/icicle\";\nimport { data } from \"./data\";\n\nconst total = +data.teams.reduce((a, t) => a + t.v, 0).toFixed(1);\n\nconst r1 = (v: number) => Math.round(v * 10) / 10;\n/** The marks a variant draws, texts named by their own characters counted by kind only (their content may change). */\nconst marks = (d: unknown, s: Size, lg: boolean) =>\n  buildVariant(def, s, lg, d).marks.map((m) => m.kind + \"|\" + (m.kind === \"text\" && m.name === m.text.slice(0, 40) ? \"\" : m.name)).join(\"\\n\");\n/** True when b draws the same marks as a at every size, legend on and off (labels shown by width stay). */\nconst sameMarks = (a: unknown, b: unknown) => ([\"xs\", \"s\", \"m\", \"l\"] as Size[]).every((s) => [true, false].every((lg) => marks(a, s, lg) === marks(b, s, lg)));\n\n// tooltip: one team with its projects (group = team): one row per project, the team's spend as the total\nconst tip = (o: typeof data, g: number): TooltipSpec | null => {\n  const t = o.teams[g];\n  if (!t) return null;\n  const all = o.teams.reduce((a, q) => a + q.v, 0);\n  return {\n    kind: \"series\",\n    caps: t.label.toUpperCase() + \" · \" + Math.round(all ? (t.v / all) * 100 : 0) + \"% OF SPEND\",\n    rows: t.kids.map((q) => ({ label: q.label, value: o.fmt(q.v), swatch: t.tok })),\n    total: { label: t.label, value: o.fmt(t.v) },\n  };\n};\n\nexport const def: ChartDef = {\n  no: 68,\n  slug: \"icicle\",\n  legend: [[\"data/cat-2\", \"Support\"], [\"data/cat-3\", \"Search\"], [\"data/primary\", \"Agents\"], [\"data/cat-5\", \"Labs\"]],\n  plot: (K, p, w, d) => icicle(K, p, w, (d as typeof data | undefined) ?? data),\n  tip: (d, g) => tip(d as typeof data, g),\n  // the next month: every project's spend moves up to ±10 %; team totals are the sums of their projects. A box's\n  // name and value show by its width, so the first move (largest first, then\n  // other phases) that shows the same labels at every size is taken.\n  update: (d, step) => {\n    const o = d as typeof data;\n    const at = (amp: number, ph: number) => {\n      let i = 0;\n      return {\n        ...o,\n        teams: o.teams.map((t) => {\n          const kids = t.kids.map((q) => ({ ...q, v: r1(q.v * (1 + amp * Math.sin(i++ * 1.7 + step * 2.2 + ph))) }));\n          return { ...t, v: r1(kids.reduce((a, q) => a + q.v, 0)), kids };\n        }),\n      };\n    };\n    return [0.1, 0.06, 0.03].flatMap((a) => [0, 1.1, 2.3].map((ph) => at(a, ph))).find((c) => sameMarks(o, c)) ?? o;\n  },\n  describe: {\n    title: \"Spend breakdown\",\n    summary: `Inference spend of ${data.fmt(total)} across ${data.teams.length} teams: Support ${data.fmt(18.4)}, Search ${data.fmt(12.1)}, Agents ${data.fmt(10.6)}, Labs ${data.fmt(7.1)}; the largest project is Chat bot at ${data.fmt(11.2)}.`,\n    table: { head: [\"Team\", \"Project\", \"Spend\"], rows: data.teams.flatMap((t) => t.kids.map((q) => [t.label, q.label, data.fmt(q.v)])) },\n  },\n};\n"
    }
  ],
  "categories": [
    "charts",
    "part-of-a-whole"
  ],
  "meta": {
    "access": "free",
    "no": 68,
    "family": "Part of a whole"
  }
}
