{"spec_id":"errorbar-asymmetric","library":"muix","language":"javascript","code":"// anyplot.ai\n// errorbar-asymmetric: Asymmetric Error Bars Plot\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-09-05\n//# anyplot-orientation: landscape\n// anyplot.ai\n// errorbar-asymmetric: Asymmetric Error Bars Plot\n// Library: MUI X Charts | React | Node 22\n// License: @mui/x-charts — MIT (community). Pro/Premium are out of scope.\n// Quality: pending | Created: 2026-09-05\n\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { ChartsXAxis } from \"@mui/x-charts/ChartsXAxis\";\nimport { ChartsYAxis } from \"@mui/x-charts/ChartsYAxis\";\nimport { ChartsGrid } from \"@mui/x-charts/ChartsGrid\";\nimport { useXScale, useYScale } from \"@mui/x-charts\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Crop yield increase by nitrogen fertilizer dose — median with 10th-90th\n// percentile range. Response flattens (and the spread widens) at high doses,\n// a realistic diminishing-returns pattern that motivates asymmetric bounds.\nconst doses = [\"0\", \"40\", \"80\", \"120\", \"160\", \"200\", \"240\"];\nconst median = [1, 8, 19, 29, 35, 38, 36];\nconst errorLower = [1, 3, 4, 6, 7, 8, 10];\nconst errorUpper = [2, 5, 8, 11, 13, 11, 9];\n\nconst Y_MIN = 0;\nconst Y_MAX = Math.max(...median.map((m, i) => m + errorUpper[i])) * 1.1;\n\n// Plateau starts once the median gain stops climbing meaningfully — highlight\n// that region to call out the diminishing-returns / widening-uncertainty story.\nconst PLATEAU_START_INDEX = doses.indexOf(\"160\");\n\n// Must be rendered inside ChartContainer to access its scale context\nfunction ErrorBars() {\n  const xScale = useXScale();\n  const yScale = useYScale();\n\n  if (!xScale || !yScale || typeof xScale.bandwidth !== \"function\") return null;\n\n  const capHalfWidth = Math.min(18, xScale.bandwidth() * 0.28);\n  const halfGap = (xScale.step() - xScale.bandwidth()) / 2;\n  const plateauX0 = +xScale(doses[PLATEAU_START_INDEX]) - halfGap;\n  const plateauX1 = +xScale(doses[doses.length - 1]) + xScale.bandwidth() + halfGap;\n  const [yRangeTop, yRangeBottom] = yScale.range();\n  const plateauLabelY = Math.min(yRangeTop, yRangeBottom) + 18;\n\n  return (\n    <g>\n      <rect\n        x={plateauX0}\n        y={Math.min(yRangeTop, yRangeBottom)}\n        width={plateauX1 - plateauX0}\n        height={Math.abs(yRangeBottom - yRangeTop)}\n        fill={t.palette[0]}\n        opacity={0.06}\n      />\n      <text\n        x={(plateauX0 + plateauX1) / 2}\n        y={plateauLabelY}\n        textAnchor=\"middle\"\n        fontSize={13}\n        fontStyle=\"italic\"\n        fill={t.inkSoft}\n      >\n        Diminishing returns, widening spread\n      </text>\n      {doses.map((dose, i) => {\n        const cx = +xScale(dose) + xScale.bandwidth() / 2;\n        const yTop = +yScale(median[i] + errorUpper[i]);\n        const yBottom = +yScale(median[i] - errorLower[i]);\n        const yMid = +yScale(median[i]);\n\n        return (\n          <g key={dose}>\n            <line\n              x1={cx} x2={cx} y1={yTop} y2={yBottom}\n              stroke={t.palette[0]} strokeWidth={3}\n            />\n            <line\n              x1={cx - capHalfWidth} x2={cx + capHalfWidth} y1={yTop} y2={yTop}\n              stroke={t.palette[0]} strokeWidth={3}\n            />\n            <line\n              x1={cx - capHalfWidth} x2={cx + capHalfWidth} y1={yBottom} y2={yBottom}\n              stroke={t.palette[0]} strokeWidth={3}\n            />\n            <circle cx={cx} cy={yMid} r={10} fill={t.palette[0]} stroke={t.pageBg} strokeWidth={2.5} />\n          </g>\n        );\n      })}\n    </g>\n  );\n}\n\nconst TITLE = \"Nitrogen Response Curve · errorbar-asymmetric · javascript · muix · anyplot.ai\";\nconst TITLE_FONT_SIZE = Math.round(22 * Math.min(1, 67 / TITLE.length));\nconst SUBTITLE = \"Median yield increase · whiskers span the 10th–90th percentile range\";\nconst TITLE_HEIGHT = 68;\nconst SUBTITLE_HEIGHT = 44;\nconst MARGIN = { top: 24, right: 60, bottom: 76, left: 96 };\n\nexport default function Chart() {\n  const chartWidth = window.ANYPLOT_SIZE.width;\n  const chartHeight = window.ANYPLOT_SIZE.height - TITLE_HEIGHT - SUBTITLE_HEIGHT;\n\n  return (\n    <Box\n      sx={{\n        width: chartWidth,\n        height: window.ANYPLOT_SIZE.height,\n        display: \"flex\",\n        flexDirection: \"column\",\n      }}\n    >\n      <Box\n        sx={{\n          height: TITLE_HEIGHT,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          flexShrink: 0,\n        }}\n      >\n        <Typography sx={{ color: t.ink, fontSize: TITLE_FONT_SIZE, fontWeight: 500 }}>\n          {TITLE}\n        </Typography>\n      </Box>\n      <Box\n        sx={{\n          height: SUBTITLE_HEIGHT,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          flexShrink: 0,\n        }}\n      >\n        <Typography sx={{ color: t.inkSoft, fontSize: 15 }}>\n          {SUBTITLE}\n        </Typography>\n      </Box>\n\n      <ChartContainer\n        skipAnimation\n        width={chartWidth}\n        height={chartHeight}\n        margin={MARGIN}\n        series={[]}\n        xAxis={[{\n          scaleType: \"band\",\n          data: doses,\n          label: \"Nitrogen Dose (kg/ha)\",\n          labelStyle: { fontSize: 15, fill: t.ink },\n          tickLabelStyle: { fontSize: 13, fill: t.inkSoft },\n          tickSize: 0,\n        }]}\n        yAxis={[{\n          scaleType: \"linear\",\n          min: Y_MIN,\n          max: Y_MAX,\n          label: \"Yield Increase (%)\",\n          labelStyle: { fontSize: 15, fill: t.ink },\n          tickLabelStyle: { fontSize: 13, fill: t.inkSoft },\n          tickSize: 0,\n        }]}\n        sx={{\n          \"& .MuiChartsGrid-line\": { stroke: t.grid, strokeOpacity: 0.5 },\n        }}\n      >\n        <ChartsGrid horizontal />\n        <ErrorBars />\n        <ChartsXAxis />\n        <ChartsYAxis />\n      </ChartContainer>\n    </Box>\n  );\n}\n"}