{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "milestone-track",
  "type": "registry:component",
  "title": "Milestone track",
  "description": "How many reach each milestone, and when. Sample data: onboarding from signed up on day 0 (100%) to paid on day 14 (18%).",
  "registryDependencies": [
    "@chartory/core",
    "@chartory/plot-milestones"
  ],
  "files": [
    {
      "path": "registry/chartory/components/milestone-track.tsx",
      "type": "registry:component",
      "target": "components/chartory/milestone-track.tsx",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// Milestone track (Chartory chart 079): 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/milestone-track\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/079-milestone-track/data\"\nimport { def } from \"@/lib/chartory/charts/types/079-milestone-track/layout\"\n\nregisterChart(def, sample)\n\nexport type MilestoneTrackData = typeof sample\nexport type MilestoneTrackProps = Omit<ChartViewProps, \"def\" | \"data\" | \"states\"> & {\n  /** the data to draw (default: the sample) */\n  data?: MilestoneTrackData\n  /** further data states: the chart morphs to them (update) or ticks through them (live, with interval) */\n  states?: MilestoneTrackData[]\n}\n\nexport function MilestoneTrack(props: MilestoneTrackProps) {\n  return <ChartView def={def} {...props} />\n}\n"
    },
    {
      "path": "registry/chartory/charts/types/079-milestone-track/data.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/079-milestone-track/data.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 079 Milestone track · sample data (Figma chart/079-milestones-onboarding, SaaS: share of sign-ups reaching each step).\nimport type { MilestonesData } from \"../../plots/milestones\";\n\nexport const data: MilestonesData = {\n  steps: [\n    { label: \"Signed up\", pct: 100, day: \"DAY 0\" }, { label: \"Invited\", pct: 72, day: \"DAY 1\" }, { label: \"Connected\", pct: 58, day: \"DAY 2\" },\n    { label: \"Report\", pct: 44, day: \"DAY 4\" }, { label: \"Shared\", pct: 31, day: \"DAY 7\" }, { label: \"Paid\", pct: 18, day: \"DAY 14\" },\n  ],\n};\n"
    },
    {
      "path": "registry/chartory/charts/types/079-milestone-track/layout.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/079-milestone-track/layout.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 079 Milestone track (Figma chart/079-milestones-onboarding): C.milestones on the onboarding steps; no legend.\nimport type { ChartDef, TooltipSpec } from \"../../core/shell\";\nimport { milestones } from \"../../plots/milestones\";\nimport { data } from \"./data\";\n\nconst last = data.steps[data.steps.length - 1];\nconst day = (s: string) => \"Day \" + s.split(\" \")[1];\n\nconst wob = (i: number, step: number) => Math.sin(i * 2.1 + step * 1.7 + 0.4);\n/** A later cohort: every step after sign-up reaches a different share, still falling step by step. */\nfunction update(d0: unknown, step: number): typeof data {\n  const d = d0 as typeof data;\n  const steps = d.steps.map((s) => ({ ...s }));\n  for (let i = 1; i < steps.length; i++) steps[i].pct = Math.min(steps[i - 1].pct - 4, Math.max(5, Math.round(steps[i].pct * (1 + 0.15 * wob(i, step)))));\n  return { steps };\n}\n\n/** Hover: one step (its ring, its % and the link into it): the share reached, against the step before. */\nfunction tip(d0: unknown, g: number): TooltipSpec | null {\n  const s = (d0 as typeof data).steps, q = s[g];\n  if (!q) return null;\n  if (g === 0) return { kind: \"value\", text: `${q.label} · ${day(q.day)} · ${q.pct}%` };\n  const dv = q.pct - s[g - 1].pct;\n  return { kind: \"compare\", caps: `${day(q.day)} · ${q.label}`.toUpperCase(), value: q.pct + \"%\", delta: { text: (dv < 0 ? \"−\" : \"+\") + Math.abs(dv) + \" pt\", tone: \"neutral\" }, comparison: \"VS \" + s[g - 1].label.toUpperCase() };\n}\n\nexport const def: ChartDef = {\n  no: 79,\n  slug: \"milestone-track\",\n  legend: null,\n  plot: (K, p, w, d) => milestones(K, p, w, (d as typeof data | undefined) ?? data),\n  tip,\n  update,\n  describe: {\n    title: \"Onboarding milestones\",\n    summary: `Share of sign-ups reaching each of ${data.steps.length} onboarding steps, from ${data.steps[0].pct}% signed up to ${last.pct}% paid by ${day(last.day).toLowerCase()}.`,\n    table: { head: [\"Step\", \"Day\", \"Reached\"], rows: data.steps.map((s) => [s.label, day(s.day), s.pct + \"%\"]) },\n  },\n};\n"
    }
  ],
  "categories": [
    "charts",
    "schedule-and-status"
  ],
  "meta": {
    "access": "free",
    "no": 79,
    "family": "Schedule and status"
  }
}
