{"spec_id":"area-stacked-confidence","library":"echarts","language":"javascript","code":"// anyplot.ai\n// area-stacked-confidence: Stacked Area Chart with Confidence Bands\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 87/100 | Created: 2026-08-26\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Quarterly energy mix by source, TWh. Measurement uncertainty is narrow for\n// the historical quarters and widens across the forecast horizon.\nconst QUARTER_COUNT = 24;\nconst FORECAST_START = 16;\n\nconst quarters = [];\nfor (let idx = 0; idx < QUARTER_COUNT; idx++) {\n  const year = 2020 + Math.floor(idx / 4);\n  const quarter = (idx % 4) + 1;\n  quarters.push(`Q${quarter} '${String(year).slice(2)}`);\n}\n\nconst solarCenter = [];\nconst windCenter = [];\nconst gasCenter = [];\nconst uncertaintyPct = [];\nfor (let idx = 0; idx < QUARTER_COUNT; idx++) {\n  solarCenter.push(38 + 3.4 * idx + 5 * Math.sin(idx / 3));\n  windCenter.push(58 + 1.9 * idx + 6 * Math.cos(idx / 4));\n  gasCenter.push(Math.max(20, 95 - 1.6 * idx));\n  uncertaintyPct.push(\n    idx < FORECAST_START\n      ? 0.05\n      : 0.05 + ((idx - (FORECAST_START - 1)) / (QUARTER_COUNT - FORECAST_START)) * 0.22\n  );\n}\n\n// Cumulative baseline BELOW each series, matching the stacking order used\n// for the central areas so each band wraps its own layer of the stack.\nconst zeroBaseline = new Array(QUARTER_COUNT).fill(0);\nconst windBaseline = solarCenter;\nconst gasBaseline = solarCenter.map((v, idx) => v + windCenter[idx]);\n\nconst absoluteBand = (center, baseline) => {\n  const lower = center.map((v, idx) => baseline[idx] + v * (1 - uncertaintyPct[idx]));\n  const upper = center.map((v, idx) => baseline[idx] + v * (1 + uncertaintyPct[idx]));\n  const width = upper.map((v, idx) => v - lower[idx]);\n  return { lower, width };\n};\n\nconst seriesDefs = [\n  { name: \"Solar\", color: t.palette[0], center: solarCenter, ...absoluteBand(solarCenter, zeroBaseline) },\n  { name: \"Wind\", color: t.palette[1], center: windCenter, ...absoluteBand(windCenter, windBaseline) },\n  { name: \"Natural Gas\", color: t.palette[2], center: gasCenter, ...absoluteBand(gasCenter, gasBaseline) },\n];\n\n// --- Series -------------------------------------------------------------\n// Each series contributes 3 ECharts series: an invisible line pinning the\n// band's lower edge, a translucent fill spanning up to the band's upper\n// edge (both sharing a private stack so the fill floats at the right\n// height), and the opaque central stacked area below. ECharts paints all\n// z:2 elements above all z:1 elements regardless of series array order, so\n// every band (z:2) must outrank every opaque area (z:1) - otherwise a\n// later-stacked series' opaque fill (e.g. Wind, Gas) paints over an\n// earlier series' band (e.g. Solar's) wherever their height ranges overlap.\nconst series = [];\nseriesDefs.forEach((s, idx) => {\n  series.push({\n    type: \"line\",\n    stack: `band${idx}`,\n    data: s.lower,\n    lineStyle: { opacity: 0 },\n    symbol: \"none\",\n    silent: true,\n    tooltip: { show: false },\n    z: 2,\n  });\n  series.push({\n    type: \"line\",\n    stack: `band${idx}`,\n    data: s.width,\n    lineStyle: { opacity: 0 },\n    symbol: \"none\",\n    areaStyle: { color: s.color, opacity: 0.28 },\n    silent: true,\n    tooltip: { show: false },\n    z: 2,\n  });\n  series.push({\n    name: s.name,\n    type: \"line\",\n    stack: \"total\",\n    data: s.center,\n    symbol: \"none\",\n    itemStyle: { color: s.color },\n    lineStyle: { width: 2.5, color: s.color },\n    areaStyle: { color: s.color, opacity: 1 },\n    z: 1,\n  });\n});\n\n// --- Title sizing (scales down once the string runs past the 67-char baseline) ---\nconst TITLE = \"Energy Mix by Source · area-stacked-confidence · javascript · echarts · anyplot.ai\";\nconst titleFontSize = Math.max(14, Math.round(22 * Math.min(1, 67 / TITLE.length)));\n\n// --- Init ---------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option ---------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: \"transparent\",\n  title: {\n    text: TITLE,\n    subtext: \"Shaded bands show measurement uncertainty; the range widens across the forecast horizon\",\n    left: \"center\",\n    textStyle: { color: t.ink, fontSize: titleFontSize, fontWeight: 500 },\n    subtextStyle: { color: t.inkSoft, fontSize: 15 },\n  },\n  tooltip: { trigger: \"axis\" },\n  legend: {\n    data: seriesDefs.map((s) => s.name),\n    top: 118,\n    textStyle: { color: t.ink, fontSize: 16 },\n  },\n  grid: { left: 110, right: 60, top: 190, bottom: 90 },\n  xAxis: {\n    type: \"category\",\n    data: quarters,\n    boundaryGap: false,\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"Energy Consumption (TWh)\",\n    nameLocation: \"middle\",\n    nameGap: 70,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series,\n});\n"}