{"spec_id":"area-stacked-confidence","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// area-stacked-confidence: Stacked Area Chart with Confidence Bands\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-08-26\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic; tiny LCG for reproducibility) --------\nlet seed = 42;\nfunction rand() {\n  seed = (seed * 1664525 + 1013904223) % 4294967296;\n  return seed / 4294967296;\n}\n\nconst months = Array.from({ length: 24 }, (_, i) => Date.UTC(2025, i, 1));\n\n// Quarterly revenue forecast by product line, with widening 90% prediction\n// intervals the further out the forecast horizon extends.\nconst productLines = [\n  { name: \"Cloud Services\", base: 18, growth: 1.3, noise: 1.1 },\n  { name: \"Hardware\", base: 12, growth: 0.5, noise: 0.9 },\n  { name: \"Professional Services\", base: 8, growth: 0.7, noise: 0.6 },\n];\n\nconst seriesData = productLines.map((p) => {\n  const center = months.map((_, i) => {\n    const trend = p.base + p.growth * i;\n    const wobble = (rand() - 0.5) * 2 * p.noise;\n    return Math.round((trend + wobble) * 10) / 10;\n  });\n  const halfWidth = months.map((_, i) => Math.round((1.5 + 0.35 * i + rand() * 1.5) * 10) / 10);\n  return {\n    name: p.name,\n    center,\n    lower: center.map((v, i) => Math.max(0, v - halfWidth[i])),\n    upper: center.map((v, i) => v + halfWidth[i]),\n  };\n});\n\n// Cumulative center value of every series stacked *below* series i. Highcharts\n// stacks 'area' series in reverse insertion order (the first series added\n// lands on top), so series i's own layer sits on top of every series that\n// comes after it in the array — the band for series i must use that same\n// base to align with its rendered position (per spec: bands follow the same\n// stack order as the central values).\nconst cumulativeBelow = seriesData.map((_, i) =>\n  months.map((_, idx) => seriesData.slice(i + 1).reduce((sum, s) => sum + s.center[idx], 0))\n);\n\n// --- Chart series -----------------------------------------------------------\n\nconst centerSeries = seriesData.map((s, i) => ({\n  type: \"area\",\n  name: s.name,\n  data: s.center.map((v, idx) => [months[idx], v]),\n  stack: \"revenue\",\n  stacking: \"normal\",\n  color: t.palette[i],\n  fillOpacity: 0.85,\n  lineWidth: 2,\n  marker: { enabled: false },\n}));\n\nconst bandSeries = seriesData.flatMap((s, i) => {\n  const base = months.map((_, idx) => cumulativeBelow[i][idx] + s.lower[idx]);\n  const width = months.map((_, idx) => s.upper[idx] - s.lower[idx]);\n  const bandColor = Highcharts.color(t.palette[i]).setOpacity(0.28).get();\n  // xAxis.reversedStacks defaults to true, so within a stack the FIRST series\n  // added ends up on top — the visible band must be added before its\n  // invisible base to land above it, not below.\n  return [\n    {\n      type: \"area\",\n      name: `${s.name} band`,\n      data: width.map((v, idx) => [months[idx], v]),\n      stack: `band${i}`,\n      stacking: \"normal\",\n      color: bandColor,\n      fillOpacity: 1,\n      lineWidth: 0,\n      enableMouseTracking: false,\n      showInLegend: false,\n      marker: { enabled: false },\n    },\n    {\n      type: \"area\",\n      name: `${s.name} band base`,\n      data: base.map((v, idx) => [months[idx], v]),\n      stack: `band${i}`,\n      stacking: \"normal\",\n      color: \"transparent\",\n      fillOpacity: 0,\n      lineWidth: 0,\n      enableMouseTracking: false,\n      showInLegend: false,\n      marker: { enabled: false },\n    },\n  ];\n});\n\nconst legendProxy = {\n  type: \"area\",\n  name: \"90% prediction interval\",\n  data: [],\n  color: Highcharts.color(t.inkSoft).setOpacity(0.35).get(),\n  lineWidth: 0,\n  enableMouseTracking: false,\n  marker: { enabled: false },\n};\n\n// --- Chart -------------------------------------------------------------------\n\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"area\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"area-stacked-confidence · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  subtitle: {\n    text: \"Quarterly revenue forecast by product line, shaded bands show the 90% prediction interval\",\n    style: { color: t.inkSoft, fontSize: \"14px\" },\n  },\n  xAxis: {\n    type: \"datetime\",\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n  },\n  yAxis: {\n    title: { text: \"Revenue ($M)\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    min: 0,\n  },\n  legend: {\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  tooltip: {\n    shared: true,\n    valueSuffix: \"M\",\n    valueDecimals: 1,\n  },\n  plotOptions: {\n    series: { animation: false },\n  },\n  series: [...centerSeries, ...bandSeries, legendProxy],\n});\n"}