{"spec_id":"line-win-probability","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// line-win-probability: Win Probability Chart\n// Library: highcharts 12.6.0 | JavaScript 22.22.3\n// Quality: 87/100 | Created: 2026-06-21\n\n//# anyplot-orientation: landscape\n\nconst t = window.ANYPLOT_TOKENS;\nconst THEME = window.ANYPLOT_THEME;\n\n// Deterministic LCG for reproducible play-by-play data\nfunction lcg(seed) {\n  let s = seed >>> 0;\n  return () => {\n    s = (Math.imul(s, 1664525) + 1013904223) >>> 0;\n    return s / 0x100000000;\n  };\n}\n\nconst rand = lcg(42);\n\n// Key scoring plays: [playNumber, wpDelta, shortLabel]\nconst keyPlays = [\n  [18,  0.22, \"Home TD\"],\n  [36, -0.20, \"Away TD\"],\n  [55, -0.14, \"Away FG\"],\n  [72,  0.21, \"Home TD\"],\n  [89, -0.27, \"Pick-six\"],\n  [113, 0.38, \"Game-winning TD\"],\n];\n\n// Build play-by-play win probability (120 plays, NFL AFC Wild Card)\nconst TOTAL_PLAYS = 120;\nlet wp = 0.50;\nconst seriesData = Array.from({ length: TOTAL_PLAYS + 1 }, (_, i) => ({ x: i, y: 0 }));\n\nfor (let i = 0; i <= TOTAL_PLAYS; i++) {\n  seriesData[i].y = parseFloat((wp * 100).toFixed(1));\n  if (i < TOTAL_PLAYS) {\n    const play = keyPlays.find(p => p[0] === i);\n    const eventDelta = play ? play[1] : 0;\n    const noise = (rand() - 0.5) * 0.036;\n    const meanRevert = (0.50 - wp) * 0.018;\n    wp = Math.min(0.99, Math.max(0.01, wp + eventDelta + noise + meanRevert));\n  }\n}\n// Home team wins: drive to 99%\nseriesData[TOTAL_PLAYS].y = 99.2;\n\n// Annotate key plays: marker at play+1 so wp has already updated\nfor (const [playNum, , label] of keyPlays) {\n  const markerIdx = Math.min(playNum + 1, TOTAL_PLAYS);\n  const point = seriesData[markerIdx];\n  point.marker = {\n    enabled: true,\n    radius: 5,\n    fillColor: t.ink,\n    lineWidth: 2,\n    lineColor: t.pageBg,\n  };\n  point.dataLabels = {\n    enabled: true,\n    useHTML: false,\n    format: label,\n    style: {\n      color: t.inkSoft,\n      fontSize: \"12px\",\n      fontWeight: \"normal\",\n      textOutline: \"none\",\n    },\n    y: point.y > 50 ? -16 : 18,\n    align: \"center\",\n  };\n}\n\n// Quarter boundary dotted lines (no label — x-axis formatter already shows Q2/Q3/Q4)\nconst quarterLines = [30, 60, 90].map((q) => ({\n  value: q,\n  color: t.grid,\n  dashStyle: \"dot\",\n  width: 1,\n  zIndex: 2,\n}));\n\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"area\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n    marginTop: 90,\n    marginBottom: 72,\n    marginLeft: 88,\n    marginRight: 32,\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"line-win-probability · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  subtitle: {\n    text: \"Home 21 – Away 17  |  NFL AFC Wild Card\",\n    style: { color: t.inkSoft, fontSize: \"14px\" },\n  },\n  xAxis: {\n    min: 0,\n    max: TOTAL_PLAYS,\n    tickPositions: [0, 30, 60, 90, 120],\n    title: {\n      text: \"Play Number\",\n      style: { color: t.inkSoft, fontSize: \"16px\" },\n    },\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    labels: {\n      style: { color: t.inkSoft, fontSize: \"13px\" },\n      formatter() {\n        const map = { 0: \"Start\", 30: \"Q2\", 60: \"Q3\", 90: \"Q4\", 120: \"Final\" };\n        return map[this.value] !== undefined ? map[this.value] : String(this.value);\n      },\n    },\n    plotLines: quarterLines,\n  },\n  yAxis: {\n    min: 0,\n    max: 100,\n    tickInterval: 25,\n    title: {\n      text: \"Home Win Probability\",\n      style: { color: t.inkSoft, fontSize: \"16px\" },\n    },\n    gridLineColor: t.grid,\n    labels: {\n      format: \"{value}%\",\n      style: { color: t.inkSoft, fontSize: \"13px\" },\n    },\n    plotLines: [{\n      value: 50,\n      color: t.inkSoft,\n      width: 1.5,\n      zIndex: 5,\n      label: {\n        text: \"50%\",\n        align: \"right\",\n        x: -8,\n        style: { color: t.inkSoft, fontSize: \"12px\" },\n      },\n    }],\n  },\n  legend: { enabled: false },\n  tooltip: { enabled: false },\n  plotOptions: {\n    series: { animation: false },\n    area: {\n      threshold: 50,\n      color: t.palette[0],          // Brand green — home team leading\n      negativeColor: t.palette[4],  // Matte red — home team trailing\n      fillOpacity: THEME === \"dark\" ? 0.32 : 0.38,\n      lineWidth: 2.5,\n      dataLabels: { enabled: false },\n      marker: {\n        enabled: false,\n        states: { hover: { enabled: false } },\n      },\n    },\n  },\n  series: [{\n    name: \"Home Win Probability\",\n    data: seriesData,\n  }],\n});\n"}