{"spec_id":"line-win-probability","library":"makie","language":"julia","code":"# anyplot.ai\n# line-win-probability: Win Probability Chart\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 88/100 | Created: 2026-06-21\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\n\n# Theme tokens (Imprint palette, theme-adaptive chrome)\nconst THEME       = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG     = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst ELEVATED_BG = THEME == \"light\" ? colorant\"#FFFDF6\" : colorant\"#242420\"\nconst INK         = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT    = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst INK_MUTED   = THEME == \"light\" ? colorant\"#6B6A63\" : colorant\"#A8A79F\"\n\n# Imprint palette — home uses position 1 (brand green, first-series rule), away uses position 3\nconst HOME_COLOR = colorant\"#009E73\"  # Imprint position 1 — Lakeside Wolves (home)\nconst AWAY_COLOR = colorant\"#4467A3\"  # Imprint position 3 — Harbor Eagles (away); skip pos 2 (lavender) — blue reads as adversarial contrast to home green\n\n# Data: NBA playoff game, 200 play-by-play win-probability snapshots over 48 minutes\nn_points   = 200\ngame_time  = collect(range(0.0, 48.0, length=n_points))\nwin_prob   = zeros(n_points)\nwin_prob[1] = 0.52  # slight home-court advantage at tip-off\n\nfor i in 2:n_points\n    t = (i - 1) / (n_points - 1)\n    # Q1: home builds early lead; Q2: home extends; Q3: Eagles surge; Q4: Wolves hold\n    if t < 0.25\n        drift = 0.0035\n    elseif t < 0.50\n        drift = 0.0010\n    elseif t < 0.75\n        drift = -0.0028\n    else\n        drift = 0.0018\n    end\n    win_prob[i] = clamp(win_prob[i-1] + drift + randn() * 0.020, 0.04, 0.96)\nend\n\n# Quarter-boundary times (minute 12 = end of Q1, 24 = end of Q2, 36 = end of Q3)\nquarter_times  = [12.0, 24.0, 36.0]\nquarter_labels = [\"Q2\", \"Q3\", \"Q4\"]\n\n# Key scoring events to annotate\nevent_times  = [9.0, 26.5, 42.5]\nevent_descs  = [\"Wolves 10–0 run\", \"Eagles 14–2 surge\", \"Eagles complete comeback\"]\nevent_idx    = [round(Int, t / 48.0 * (n_points - 1)) + 1 for t in event_times]\nevent_probs  = win_prob[event_idx]\n\n# Band fill split at the 50% line\nhome_upper = max.(win_prob, 0.5)  # home region: wp → 0.5 floor when below\naway_lower = min.(win_prob, 0.5)  # away region: wp → 0.5 ceiling when above\n\n# Figure\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title             = \"line-win-probability · julia · makie · anyplot.ai\",\n    titlesize         = 20,\n    titlecolor        = INK,\n    titlefont         = :bold,\n    xlabel            = \"Game Time (minutes)\",\n    ylabel            = \"Win Probability\",\n    xlabelsize        = 14,\n    ylabelsize        = 14,\n    xlabelcolor       = INK,\n    ylabelcolor       = INK,\n    xticklabelsize    = 12,\n    yticklabelsize    = 12,\n    xticklabelcolor   = INK_SOFT,\n    yticklabelcolor   = INK_SOFT,\n    xtickcolor        = INK_SOFT,\n    ytickcolor        = INK_SOFT,\n    backgroundcolor   = PAGE_BG,\n    topspinevisible   = false,\n    rightspinevisible = false,\n    leftspinecolor    = INK_SOFT,\n    bottomspinecolor  = INK_SOFT,\n    xgridvisible      = false,\n    ygridvisible      = true,\n    ygridcolor        = RGBAf(INK.r, INK.g, INK.b, 0.12),\n    xticks            = ([0, 12, 24, 36, 48], [\"0\", \"12\", \"24\", \"36\", \"48\"]),\n    yticks            = ([0.0, 0.25, 0.50, 0.75, 1.0], [\"0%\", \"25%\", \"50%\", \"75%\", \"100%\"]),\n)\n\nxlims!(ax, 0.0, 48.0)\nylims!(ax, 0.0, 1.0)\n\n# Fill: home region above 50% (brand green)\nband!(ax, game_time, fill(0.5, n_points), home_upper;\n    color = (HOME_COLOR, 0.22))\n\n# Fill: away region below 50% (blue)\nband!(ax, game_time, away_lower, fill(0.5, n_points);\n    color = (AWAY_COLOR, 0.22))\n\n# 50% reference line (prominent separator, per spec)\nhlines!(ax, [0.5]; color = INK_SOFT, linewidth = 1.8)\n\n# Win probability line\nlines!(ax, game_time, win_prob; color = INK, linewidth = 2.0)\n\n# Q1 label at far left\ntext!(ax, 0.5, 0.975;\n    text     = \"Q1\",\n    fontsize = 12,\n    color    = INK_MUTED,\n    align    = (:left, :top))\n\n# Quarter dividers and labels (Q2, Q3, Q4 at minutes 12, 24, 36)\nfor (t, lbl) in zip(quarter_times, quarter_labels)\n    vlines!(ax, [t]; color = (INK_SOFT, 0.45), linewidth = 1.0, linestyle = :dash)\n    text!(ax, t + 0.4, 0.975;\n        text     = lbl,\n        fontsize = 12,\n        color    = INK_MUTED,\n        align    = (:left, :top))\nend\n\n# Key event markers: dot on the line + annotation\nfor (t, lbl, p) in zip(event_times, event_descs, event_probs)\n    scatter!(ax, [t], [p];\n        color       = INK,\n        markersize  = 10,\n        strokecolor = PAGE_BG,\n        strokewidth = 1.5)\n    y_pos = p > 0.5 ? p + 0.07 : p - 0.07\n    y_pos = clamp(y_pos, 0.06, 0.90)\n    va    = p > 0.5 ? :bottom : :top\n    text!(ax, t, y_pos;\n        text     = lbl,\n        fontsize = 12,\n        color    = INK_MUTED,\n        align    = (:center, va))\nend\n\n# Team labels in their respective probability regions\ntext!(ax, 1.0, 0.88;\n    text     = \"Lakeside Wolves (home)\",\n    fontsize = 13,\n    color    = HOME_COLOR,\n    align    = (:left, :center))\n\ntext!(ax, 1.0, 0.12;\n    text     = \"Harbor Eagles (away)\",\n    fontsize = 13,\n    color    = AWAY_COLOR,\n    align    = (:left, :center))\n\n# Final score annotation\ntext!(ax, 47.5, 0.975;\n    text     = \"Final: Eagles 104  ·  Wolves 98\",\n    fontsize = 12,\n    color    = INK_MUTED,\n    align    = (:right, :top))\n\n# Save\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}