{"spec_id":"horizon-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// horizon-basic: Horizon Chart\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 85/100 | Created: 2026-08-20\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { AreaPlot } from \"@mui/x-charts/LineChart\";\nimport { Box, Typography } from \"@mui/material\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst SIZE = window.ANYPLOT_SIZE;\n\n// --- Layout constants (CSS px in the mount's coordinate space) -------------\nconst OUTER_PAD = 32;\nconst LABEL_COL_WIDTH = 180;\nconst RIGHT_PAD = 16;\nconst CHART_WIDTH = SIZE.width - OUTER_PAD * 2 - LABEL_COL_WIDTH - RIGHT_PAD;\n\nconst ROW_HEIGHT = 70;\nconst HALF_HEIGHT = ROW_HEIGHT / 2;\nconst ROW_GAP = 10;\nconst BAND_COUNT = 3;\nconst MAX_DEVIATION = 45; // percentage points from the 24h rolling baseline\nconst POINT_COUNT = 168; // hourly readings over 7 days\nconst ZERO_MARGIN = { top: 0, right: 0, bottom: 0, left: 0 };\n// Upper bound of each band's percentage-point range (e.g. 0-15 / 15-30 / 30-45).\nconst BAND_THRESHOLDS = Array.from({ length: BAND_COUNT }, (_, k) => Math.round((MAX_DEVIATION * (k + 1)) / BAND_COUNT));\n\nconst TITLE = \"horizon-basic · javascript · muix · anyplot.ai\";\nconst TITLE_FONT_SIZE = Math.round(22 * (TITLE.length > 67 ? 67 / TITLE.length : 1));\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Fixed-seed LCG — the browser has no seeded RNG (see prompts/library/muix.md\n// \"Reproducibility\").\nfunction mulberry32(seed) {\n  let s = seed;\n  return () => {\n    s = (s + 0x6d2b79f5) | 0;\n    let v = Math.imul(s ^ (s >>> 15), 1 | s);\n    v = (v + Math.imul(v ^ (v >>> 7), 61 | v)) ^ v;\n    return ((v ^ (v >>> 14)) >>> 0) / 4294967296;\n  };\n}\n\n// Deviation of a server metric (e.g. CPU load) from its 24h rolling baseline:\n// a daily sine cycle plus a mean-reverting random walk, clamped to the domain.\nfunction seriesValues(amplitude, phase, walkScale, seed) {\n  const rand = mulberry32(seed);\n  let walk = 0;\n  const values = [];\n  for (let i = 0; i < POINT_COUNT; i += 1) {\n    walk += (rand() - 0.5) * walkScale * 0.6 - walk * 0.15;\n    const daily = amplitude * Math.sin((2 * Math.PI * i) / 24 + phase);\n    values.push(Math.max(-MAX_DEVIATION, Math.min(MAX_DEVIATION, daily + walk)));\n  }\n  return values;\n}\n\nconst SERVERS = [\n  { name: \"api-gateway\", amplitude: 22, phase: 0.0, walkScale: 6, seed: 1013 },\n  { name: \"auth-service\", amplitude: 18, phase: 1.1, walkScale: 5, seed: 1069 },\n  { name: \"orders-db\", amplitude: 30, phase: 2.4, walkScale: 9, seed: 1151 },\n  { name: \"inventory-db\", amplitude: 26, phase: 0.6, walkScale: 8, seed: 1223 },\n  { name: \"redis-cache\", amplitude: 15, phase: 3.0, walkScale: 4, seed: 1291 },\n  { name: \"search-index\", amplitude: 20, phase: 1.8, walkScale: 6, seed: 1373 },\n  { name: \"payment-worker\", amplitude: 35, phase: 0.3, walkScale: 10, seed: 1447 },\n  { name: \"email-worker\", amplitude: 12, phase: 2.1, walkScale: 3, seed: 1523 },\n];\n\nconst X_INDEX = Array.from({ length: POINT_COUNT }, (_, i) => i);\n\nconst SERIES_DATA = SERVERS.map((server) => {\n  const values = seriesValues(server.amplitude, server.phase, server.walkScale, server.seed);\n  return {\n    name: server.name,\n    positive: values.map((v) => Math.max(v, 0)),\n    negative: values.map((v) => Math.max(-v, 0)),\n  };\n});\n\n// --- Imprint diverging colours — mirrored positive/negative bands ----------\n// t.div = [red pole, page-bg midpoint, blue pole] (see default-style-guide.md\n// \"Continuous Data\"). Bands fade from the midpoint toward the pole so the\n// folded magnitude reads as increasing colour intensity.\nfunction mixHex(hexA, hexB, ratio) {\n  const a = parseInt(hexA.slice(1), 16);\n  const b = parseInt(hexB.slice(1), 16);\n  const channel = (shift) => {\n    const va = (a >> shift) & 255;\n    const vb = (b >> shift) & 255;\n    return Math.round(va + (vb - va) * ratio);\n  };\n  const [r, g, bl] = [16, 8, 0].map(channel);\n  return `#${[r, g, bl].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nfunction bandColors(pole) {\n  return Array.from({ length: BAND_COUNT }, (_, k) => {\n    const strength = 0.45 + 0.55 * ((k + 1) / BAND_COUNT);\n    return mixHex(t.div[1], pole, strength);\n  });\n}\n\nconst POS_BAND_COLORS = bandColors(t.div[2]); // above baseline — blue\nconst NEG_BAND_COLORS = bandColors(t.div[0]); // below baseline — red\n\n// --- Chart geometry ----------------------------------------------------------\n// Classic CSS \"horizon chart\" fold: every band layer renders the SAME area\n// (baseline 0 → value) at full virtual height (HALF_HEIGHT * BAND_COUNT), then\n// is shifted down by `bandIndex * HALF_HEIGHT` and clipped to one HALF_HEIGHT\n// window. Lower bands are always fully covered wherever the value reaches\n// them, so stacking band 0 → band N-1 in DOM order (later = on top) reveals\n// exactly the highest reached band's colour at each point — no opacity\n// blending needed.\nfunction BandStack({ values, colors }) {\n  return (\n    <>\n      {colors.map((color, bandIndex) => (\n        <Box\n          key={bandIndex}\n          sx={{\n            position: \"absolute\",\n            left: 0,\n            bottom: 0,\n            width: CHART_WIDTH,\n            height: HALF_HEIGHT * BAND_COUNT,\n            transform: `translateY(${bandIndex * HALF_HEIGHT}px)`,\n          }}\n        >\n          <ChartContainer\n            width={CHART_WIDTH}\n            height={HALF_HEIGHT * BAND_COUNT}\n            margin={ZERO_MARGIN}\n            disableAxisListener\n            series={[\n              {\n                type: \"line\",\n                data: values,\n                area: true,\n                baseline: 0,\n                curve: \"monotoneX\",\n                color,\n              },\n            ]}\n            xAxis={[{ scaleType: \"linear\", data: X_INDEX, min: 0, max: POINT_COUNT - 1, domainLimit: \"strict\" }]}\n            yAxis={[{ scaleType: \"linear\", min: 0, max: MAX_DEVIATION, domainLimit: \"strict\" }]}\n          >\n            {/* slotProps forces the exact Imprint hex — AreaPlot's default style brightens `color` */}\n            <AreaPlot skipAnimation slotProps={{ area: { style: { fill: color, fillOpacity: 1 } } }} />\n          </ChartContainer>\n        </Box>\n      ))}\n    </>\n  );\n}\n\nfunction HorizonRow({ name, positive, negative }) {\n  return (\n    <Box sx={{ display: \"flex\", alignItems: \"center\", height: ROW_HEIGHT }}>\n      <Box sx={{ width: LABEL_COL_WIDTH, pr: \"14px\", boxSizing: \"border-box\", textAlign: \"right\" }}>\n        <Typography\n          sx={{\n            m: 0,\n            fontSize: 17,\n            fontWeight: 500,\n            color: t.ink,\n            whiteSpace: \"nowrap\",\n            overflow: \"hidden\",\n            textOverflow: \"ellipsis\",\n          }}\n        >\n          {name}\n        </Typography>\n      </Box>\n      <Box sx={{ position: \"relative\", width: CHART_WIDTH, height: ROW_HEIGHT }}>\n        <Box sx={{ position: \"absolute\", top: 0, left: 0, width: CHART_WIDTH, height: HALF_HEIGHT, overflow: \"hidden\" }}>\n          <BandStack values={positive} colors={POS_BAND_COLORS} />\n        </Box>\n        <Box\n          sx={{\n            position: \"absolute\",\n            top: HALF_HEIGHT,\n            left: 0,\n            width: CHART_WIDTH,\n            height: HALF_HEIGHT,\n            overflow: \"hidden\",\n            transform: \"scaleY(-1)\",\n          }}\n        >\n          <BandStack values={negative} colors={NEG_BAND_COLORS} />\n        </Box>\n        {/* baseline (0 deviation) reference rule */}\n        <Box sx={{ position: \"absolute\", top: HALF_HEIGHT, left: 0, width: CHART_WIDTH, height: \"1px\", bgcolor: t.grid }} />\n      </Box>\n    </Box>\n  );\n}\n\nfunction LegendSwatches({ colors, thresholds, label, align }) {\n  return (\n    <Box sx={{ display: \"flex\", alignItems: \"center\", gap: \"6px\", flexDirection: align === \"left\" ? \"row-reverse\" : \"row\" }}>\n      <Typography sx={{ m: 0, fontSize: 13, color: t.inkSoft, whiteSpace: \"nowrap\" }}>{label}</Typography>\n      <Box sx={{ display: \"flex\", flexDirection: \"column\", alignItems: \"center\", gap: \"2px\" }}>\n        <Box sx={{ display: \"flex\", gap: \"3px\" }}>\n          {colors.map((color, i) => (\n            <Box key={i} sx={{ width: 14, height: 14, borderRadius: \"3px\", bgcolor: color }} />\n          ))}\n        </Box>\n        <Box sx={{ display: \"flex\", gap: \"3px\" }}>\n          {thresholds.map((th, i) => (\n            <Typography key={i} sx={{ m: 0, fontSize: 11, color: t.inkSoft, width: 14, textAlign: \"center\", whiteSpace: \"nowrap\" }}>\n              {th}\n            </Typography>\n          ))}\n        </Box>\n      </Box>\n    </Box>\n  );\n}\n\nfunction formatTick(index) {\n  const day = Math.floor(index / 24) + 1;\n  const hour = index % 24;\n  return `Day ${day} · ${String(hour).padStart(2, \"0\")}:00`;\n}\n\nconst TICKS = [0, Math.round((POINT_COUNT - 1) / 4), Math.round((POINT_COUNT - 1) / 2), Math.round((3 * (POINT_COUNT - 1)) / 4), POINT_COUNT - 1];\n\nexport default function Chart() {\n  return (\n    <Box\n      sx={{\n        width: SIZE.width,\n        height: SIZE.height,\n        boxSizing: \"border-box\",\n        p: `${OUTER_PAD}px`,\n        display: \"flex\",\n        flexDirection: \"column\",\n        justifyContent: \"center\",\n      }}\n    >\n      <Typography sx={{ m: 0, fontSize: TITLE_FONT_SIZE, fontWeight: 600, color: t.ink }}>{TITLE}</Typography>\n      <Typography sx={{ m: 0, mb: \"14px\", fontSize: 14, color: t.inkSoft }}>\n        Deviation from 24h rolling baseline, in percentage points (pp)\n      </Typography>\n\n      <Box sx={{ display: \"flex\", justifyContent: \"flex-end\", alignItems: \"center\", gap: \"18px\", mb: \"20px\" }}>\n        <LegendSwatches\n          label=\"Below baseline\"\n          colors={[...NEG_BAND_COLORS].reverse()}\n          thresholds={[...BAND_THRESHOLDS].reverse()}\n          align=\"left\"\n        />\n        <Box sx={{ width: \"1px\", height: \"16px\", bgcolor: t.grid }} />\n        <LegendSwatches label=\"Above baseline\" colors={POS_BAND_COLORS} thresholds={BAND_THRESHOLDS} align=\"right\" />\n      </Box>\n\n      <Box>\n        {SERIES_DATA.map((series, i) => (\n          <Box key={series.name} sx={{ mb: i === SERIES_DATA.length - 1 ? 0 : `${ROW_GAP}px` }}>\n            <HorizonRow name={series.name} positive={series.positive} negative={series.negative} />\n          </Box>\n        ))}\n      </Box>\n\n      <Box sx={{ display: \"flex\", mt: \"14px\" }}>\n        <Box sx={{ width: LABEL_COL_WIDTH, flexShrink: 0 }} />\n        <Box sx={{ position: \"relative\", width: CHART_WIDTH, height: \"18px\" }}>\n          {TICKS.map((index) => (\n            <Typography\n              key={index}\n              component=\"span\"\n              sx={{\n                m: 0,\n                position: \"absolute\",\n                left: `${(index / (POINT_COUNT - 1)) * 100}%`,\n                transform: index === 0 ? \"none\" : index === POINT_COUNT - 1 ? \"translateX(-100%)\" : \"translateX(-50%)\",\n                fontSize: 14,\n                color: t.inkSoft,\n                whiteSpace: \"nowrap\",\n              }}\n            >\n              {formatTick(index)}\n            </Typography>\n          ))}\n        </Box>\n      </Box>\n    </Box>\n  );\n}\n"}