{"spec_id":"line-growth-percentile","library":"echarts","language":"javascript","code":"// anyplot.ai\n// line-growth-percentile: Pediatric Growth Chart with Percentile Curves\n// Library: echarts 5.5.1 | JavaScript 22.22.3\n// Quality: 91/100 | Created: 2026-06-20\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Reference ages (months) — every 3 months, 0–36\nconst ages = [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36];\nconst ageLabels = ages.map(String);\n\n// Approximate WHO weight-for-age reference curves for boys (kg)\nconst refP3  = [2.5, 4.9, 6.4, 7.5, 8.2,  8.8,  9.2,  9.6, 10.0, 10.3, 10.7, 11.0, 11.3];\nconst refP10 = [2.8, 5.3, 6.9, 8.0, 8.8,  9.4,  9.9, 10.3, 10.8, 11.1, 11.5, 11.9, 12.2];\nconst refP25 = [3.0, 5.7, 7.4, 8.6, 9.5, 10.1, 10.7, 11.1, 11.5, 12.0, 12.4, 12.8, 13.2];\nconst refP50 = [3.3, 6.2, 7.9, 9.2, 10.2, 10.9, 11.5, 12.0, 12.5, 13.0, 13.4, 13.9, 14.4];\nconst refP75 = [3.7, 6.7, 8.5, 9.9, 10.9, 11.7, 12.4, 12.9, 13.4, 14.0, 14.5, 15.0, 15.6];\nconst refP90 = [4.0, 7.2, 9.1, 10.5, 11.6, 12.4, 13.2, 13.8, 14.3, 15.0, 15.5, 16.1, 16.7];\nconst refP97 = [4.3, 7.7, 9.7, 11.2, 12.3, 13.2, 14.0, 14.7, 15.3, 16.0, 16.5, 17.2, 17.9];\n\n// Stacking differences (each band = upper minus lower percentile)\nconst diff = (u, l) => u.map((v, i) => +(v - l[i]).toFixed(2));\nconst band3_10  = diff(refP10, refP3);\nconst band10_25 = diff(refP25, refP10);\nconst band25_75 = diff(refP75, refP25);\nconst band75_90 = diff(refP90, refP75);\nconst band90_97 = diff(refP97, refP90);\n\n// Patient measurements aligned to reference age grid (null = no visit that month)\n// Boy tracking around the 65th percentile at well-child visits\nconst patientData = [3.5, null, 8.2, 9.8, 11.0, 11.9, 12.6, null, 13.8, null, 15.2, null, 16.0];\n\nconst boyBlue = \"#4467A3\";\n\n// --- Init -------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: \"transparent\",\n\n  title: {\n    text: \"line-growth-percentile · javascript · echarts · anyplot.ai\",\n    subtext: \"WHO Weight-for-Age Reference  ·  Boys 0–36 Months\",\n    left: \"center\",\n    top: 16,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: \"bold\" },\n    subtextStyle: { color: t.inkSoft, fontSize: 14 },\n  },\n\n  legend: {\n    data: [\"Patient (Boy, 2024)\", \"50th Percentile (Median)\"],\n    bottom: 20,\n    itemGap: 40,\n    textStyle: { color: t.inkSoft, fontSize: 15 },\n  },\n\n  grid: { left: 90, right: 120, top: 100, bottom: 78 },\n\n  xAxis: {\n    type: \"category\",\n    data: ageLabels,\n    name: \"Age (months)\",\n    nameLocation: \"middle\",\n    nameGap: 48,\n    nameTextStyle: { color: t.inkSoft, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { lineStyle: { color: t.inkSoft } },\n    splitLine: { show: false },\n  },\n\n  yAxis: {\n    type: \"value\",\n    name: \"Weight (kg)\",\n    nameLocation: \"middle\",\n    nameGap: 55,\n    nameTextStyle: { color: t.inkSoft, fontSize: 16 },\n    min: 0,\n    max: 20,\n    interval: 2,\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { lineStyle: { color: t.inkSoft } },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n\n  series: [\n    // ── Stacked band fills (bottom to top) ──────────────────────────────────\n    // Base: invisible fill from 0 to P3\n    {\n      type: \"line\", stack: \"perc\", silent: true, legendHoverLink: false,\n      data: refP3, symbol: \"none\", smooth: true,\n      lineStyle: { opacity: 0 },\n      areaStyle: { color: \"transparent\" },\n    },\n    // P3–P10 (outer lower band — moderate fill)\n    {\n      type: \"line\", stack: \"perc\", silent: true, legendHoverLink: false,\n      data: band3_10, symbol: \"none\", smooth: true,\n      lineStyle: { opacity: 0 },\n      areaStyle: { color: \"rgba(68,103,163,0.45)\" },\n    },\n    // P10–P25\n    {\n      type: \"line\", stack: \"perc\", silent: true, legendHoverLink: false,\n      data: band10_25, symbol: \"none\", smooth: true,\n      lineStyle: { opacity: 0 },\n      areaStyle: { color: \"rgba(68,103,163,0.28)\" },\n    },\n    // P25–P75 (lightest — the normal range)\n    {\n      type: \"line\", stack: \"perc\", silent: true, legendHoverLink: false,\n      data: band25_75, symbol: \"none\", smooth: true,\n      lineStyle: { opacity: 0 },\n      areaStyle: { color: \"rgba(68,103,163,0.13)\" },\n    },\n    // P75–P90\n    {\n      type: \"line\", stack: \"perc\", silent: true, legendHoverLink: false,\n      data: band75_90, symbol: \"none\", smooth: true,\n      lineStyle: { opacity: 0 },\n      areaStyle: { color: \"rgba(68,103,163,0.28)\" },\n    },\n    // P90–P97 (outer upper band — moderate fill)\n    {\n      type: \"line\", stack: \"perc\", silent: true, legendHoverLink: false,\n      data: band90_97, symbol: \"none\", smooth: true,\n      lineStyle: { opacity: 0 },\n      areaStyle: { color: \"rgba(68,103,163,0.45)\" },\n    },\n\n    // ── Percentile reference lines ───────────────────────────────────────────\n    {\n      type: \"line\", name: \"P3\", silent: true, legendHoverLink: false,\n      data: refP3, symbol: \"none\", smooth: true,\n      lineStyle: { color: boyBlue, width: 1, opacity: 0.55, type: \"dashed\" },\n      endLabel: { show: true, formatter: () => \"P3\",\n                  color: boyBlue, fontSize: 13, opacity: 0.75 },\n    },\n    {\n      type: \"line\", name: \"P10\", silent: true, legendHoverLink: false,\n      data: refP10, symbol: \"none\", smooth: true,\n      lineStyle: { color: boyBlue, width: 1, opacity: 0.55 },\n      endLabel: { show: true, formatter: () => \"P10\",\n                  color: boyBlue, fontSize: 13, opacity: 0.75 },\n    },\n    {\n      type: \"line\", name: \"P25\", silent: true, legendHoverLink: false,\n      data: refP25, symbol: \"none\", smooth: true,\n      lineStyle: { color: boyBlue, width: 1.5, opacity: 0.65 },\n      endLabel: { show: true, formatter: () => \"P25\",\n                  color: boyBlue, fontSize: 13, opacity: 0.8 },\n    },\n    // P50 median — thicker, shown in legend\n    {\n      type: \"line\", name: \"50th Percentile (Median)\", silent: true,\n      data: refP50, symbol: \"none\", smooth: true,\n      lineStyle: { color: boyBlue, width: 3 },\n      itemStyle: { color: boyBlue },\n      endLabel: { show: true, formatter: () => \"P50\",\n                  color: boyBlue, fontSize: 14, fontWeight: \"bold\" },\n    },\n    {\n      type: \"line\", name: \"P75\", silent: true, legendHoverLink: false,\n      data: refP75, symbol: \"none\", smooth: true,\n      lineStyle: { color: boyBlue, width: 1.5, opacity: 0.65 },\n      endLabel: { show: true, formatter: () => \"P75\",\n                  color: boyBlue, fontSize: 13, opacity: 0.8 },\n    },\n    {\n      type: \"line\", name: \"P90\", silent: true, legendHoverLink: false,\n      data: refP90, symbol: \"none\", smooth: true,\n      lineStyle: { color: boyBlue, width: 1, opacity: 0.55 },\n      endLabel: { show: true, formatter: () => \"P90\",\n                  color: boyBlue, fontSize: 13, opacity: 0.75 },\n    },\n    {\n      type: \"line\", name: \"P97\", silent: true, legendHoverLink: false,\n      data: refP97, symbol: \"none\", smooth: true,\n      lineStyle: { color: boyBlue, width: 1, opacity: 0.55, type: \"dashed\" },\n      endLabel: { show: true, formatter: () => \"P97\",\n                  color: boyBlue, fontSize: 13, opacity: 0.75 },\n    },\n\n    // ── Patient trajectory — Imprint position 1 (always first categorical) ──\n    {\n      type: \"line\",\n      name: \"Patient (Boy, 2024)\",\n      data: patientData,\n      connectNulls: true,\n      symbol: \"circle\",\n      symbolSize: 12,\n      lineStyle: { color: t.palette[0], width: 3 },\n      itemStyle: {\n        color: t.palette[0],\n        borderColor: t.pageBg,\n        borderWidth: 2,\n      },\n    },\n  ],\n});\n"}