{"spec_id":"line-confidence","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// line-confidence: Line Plot with Confidence Interval\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-09-05\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Regression fit of crop yield against fertilizer application rate. The 95%\n// confidence band follows the classic prediction-interval shape: narrowest\n// near the mean of x (most data support) and widening toward both extremes,\n// unlike a forecast band that only widens in one direction over time.\nlet seed = 7;\nfunction nextRandom() {\n  seed = (seed * 1103515245 + 12345) % 2147483648;\n  return seed / 2147483648;\n}\n\nconst rateStep = 5; // kg/ha\nconst rateMean = 100; // kg/ha, center of the sampled range\nconst lowerBase = []; // invisible stack base = y_lower\nconst bandHeight = []; // stacked on top of lowerBase to reach y_upper\nconst centerLine = []; // fitted trend\n\nfor (let rate = 0; rate <= 200; rate += rateStep) {\n  const fit = 2.8 + 6.2 * (1 - Math.exp(-rate / 70)); // diminishing-returns fit\n  const wobble = (nextRandom() - 0.5) * 0.15; // small residual scatter\n  const center = fit + wobble;\n  const se = 0.15 + 0.00003 * (rate - rateMean) ** 2; // bowtie-shaped std. error\n  const halfWidth = 1.96 * se; // 95% prediction interval\n\n  lowerBase.push(Math.round((center - halfWidth) * 100) / 100);\n  bandHeight.push(Math.round(halfWidth * 2 * 100) / 100);\n  centerLine.push(Math.round(center * 100) / 100);\n}\n\nconst rates = [];\nfor (let rate = 0; rate <= 200; rate += rateStep) rates.push(rate);\n\nconst bandFill = Highcharts.color(t.palette[0]).setOpacity(0.22).get();\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"line-confidence · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"26px\", fontWeight: \"700\" },\n  },\n  subtitle: {\n    text: \"Crop yield vs. fertilizer application rate · regression fit with 95% confidence interval\",\n    style: { color: t.inkSoft, fontSize: \"14px\" },\n  },\n  xAxis: {\n    title: {\n      text: \"Fertilizer Application Rate (kg/ha)\",\n      style: { color: t.inkSoft, fontSize: \"16px\" },\n    },\n    lineWidth: 0,\n    tickWidth: 0,\n    gridLineColor: t.grid,\n    tickInterval: 25,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    // Diminishing-returns marker: the fit's slope flattens sharply past this\n    // rate, so a light guide line + label reinforces the story the band and\n    // curve already tell.\n    plotLines: [\n      {\n        value: 150,\n        color: t.inkSoft,\n        dashStyle: \"Dash\",\n        width: 1,\n        zIndex: 4,\n        label: {\n          text: \"Diminishing returns\",\n          align: \"left\",\n          x: 6,\n          y: 14,\n          style: { color: t.inkSoft, fontSize: \"12px\" },\n        },\n      },\n    ],\n  },\n  yAxis: {\n    title: {\n      text: \"Predicted Crop Yield (t/ha)\",\n      style: { color: t.inkSoft, fontSize: \"16px\" },\n    },\n    // The stacking trick anchors an invisible spacer series at 0, which would\n    // otherwise force the axis to start at 0 and waste most of the canvas\n    // below the ~2-9 t/ha data range. Cropping the visible min is safe: the\n    // spacer stays fully transparent, so the stacking math is unaffected.\n    min: 1.5,\n    lineWidth: 0,\n    tickWidth: 0,\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n  },\n  legend: {\n    symbolRadius: 2,\n    itemDistance: 24,\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  tooltip: { shared: true, valueSuffix: \" t/ha\" },\n  plotOptions: {\n    series: { animation: false, marker: { enabled: false } },\n  },\n  series: [\n    // Stacking order matters: Highcharts stacks the FIRST series in this array\n    // as the cumulative top of the stack, and the LAST as the base at 0. So the\n    // visible band goes first (rendering from y_lower to y_upper) and the\n    // invisible spacer goes second (rendering from 0 to y_lower, hidden).\n    {\n      name: \"95% confidence interval\",\n      type: \"area\",\n      data: rates.map((rate, i) => [rate, bandHeight[i]]),\n      stacking: \"normal\",\n      color: bandFill,\n      lineWidth: 0,\n      enableMouseTracking: false,\n      showInLegend: true,\n    },\n    {\n      name: \"Lower bound\",\n      type: \"area\",\n      data: rates.map((rate, i) => [rate, lowerBase[i]]),\n      stacking: \"normal\",\n      color: \"transparent\",\n      fillOpacity: 0,\n      lineWidth: 0,\n      enableMouseTracking: false,\n      showInLegend: false,\n    },\n    {\n      name: \"Predicted yield\",\n      type: \"spline\",\n      data: rates.map((rate, i) => [rate, centerLine[i]]),\n      color: t.palette[0],\n      lineWidth: 3.5,\n      marker: {\n        enabled: true,\n        radius: 3.5,\n        fillColor: t.palette[0],\n        lineWidth: 1,\n        lineColor: t.pageBg,\n      },\n      showInLegend: true,\n    },\n  ],\n});\n"}