Add a "What's left" view: five reads of the remaining possibility space - #44
Merged
Merged
Conversation
The forecast table answers "will this player get in", one row at a time. With four points events to go, the questions it can't answer are the shape ones: how wide is everyone's range, which positions are actually still contested, how far the chasers are from the line, which door they walk through, and where their season gets decided. This adds a second top-level view for those. Four of the five panels are pure re-reads of fields the bundle already ships (hist, cutline, p_cut, p_mvp_qual, att) — no pipeline change: - Possibility cloud. The row sparkline drawn for every contender at once, normalised per row exactly as sparkCell scales to each player's own max. Rows whose distribution is mostly the overflow bin are dropped: that bucket is "50th or worse", so such a row draws as one lone mark at the right edge and reads as a rendering fault rather than as data. Ships with a table twin so nothing is encoded in colour alone. - Contested positions. The same matrix read down its columns: effective candidates per finishing position (inverse Herfindahl). Stops one short of max_hist_rank, since the overflow column sums to far more than one player's worth of probability and would render as false chaos. - The cutline. Points only go up, so a total today is a hard floor and the gap to the line is closeable. The axis reaches down to the chasers rather than just covering the cutline — the horizontal distance between the two is the chart. Annotates the nearest chaser and the furthest back with a real shot; the deepest tick on the rug is a sub-1% longshot whose gap is just noise. - The ways in. p_champ decomposed into standings cut, MVP-performance bid, and the event-winner invite (the residual). Two semantic hues plus a structural grey for the invite, which is an entitlement rather than a competitive route. The fifth re-runs the cutline replay held fixed at one event's result: - Leverage. Per contender, per remaining event, the swing in auto-bid odds between a 90th-percentile week there and a 10th-percentile one. This is what makes the 2026 counting caps visible: a new result only counts for what it beats, so winning Worlds is worth the full 300 to a player with a thin major pool and 200 to one whose best two are already banked — and an event a player isn't entered for comes back n/a rather than a leverage of zero dressed up as a real number. Computed one player per tick (~1.2s for a 24-row grid) and cached per division, with a generation token so a run abandoned mid-flight drops its own partial cache instead of leaving a permanently half-empty grid. Verified against the model it re-derives: the counting pools reproduce every player's current season total exactly, and the unconditioned estimator lands within ~1.5 points of the exported p_cut in both divisions. replay()'s per-event pre-sampling is factored out as samplePoints() so the leverage grid can draw an event once and reuse it across nine conditional runs rather than redrawing per run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRdyv62j1tvtkA9NzZhSGx
probClass has always returned pos/pend/dim, and its comment has always promised "green = effectively in, yellow = live bubble". But tokens.css defines .pos/.pend only as `.table-ledger td` background fills, and every caller puts the class on the number inside the cell — a <span> or a <b> — so only .dim, which is a plain global colour rule, ever did anything. Five probability columns in the forecast table have been rendering as flat ink. Colour the number rather than filling the cell. Filling would put five adjacent solid columns across the table, and a filled cell also swallows tr.expandable:hover's fill, which is the table's main "click me" affordance. Both hues clear 5.7:1 against the paper and against the hover fill, in dark, newsprint, and print. The cell-fill form stays available and is still used where a single anchor column earns it — the ways-in and possibility-cloud tables put the class on the <td>. probClass's comment now says which application does which. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRdyv62j1tvtkA9NzZhSGx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
With four points events to go, the forecast table answers "will this player get in", one row at a time. The questions it can't answer are the shape ones: how wide is everyone's range, which positions are still contested, how far the chasers are from the line, which door they walk through, and where their season actually gets decided.
This adds a second top-level view (
Forecast | What's left, beside the division toggle) with five panels for those. The table keeps the page it had; deep links still land on it.Front-end only —
docs/app.js,docs/index.html,docs/style.css. No pipeline change, no new exported fields.The panels
Four are pure re-reads of fields the bundle already ships (
hist,cutline,p_cut,p_mvp_qual,att):1 · The possibility cloud. The row sparkline drawn for every contender at once, normalised per row exactly as
sparkCellscales to each player's own max. Rows whose distribution is mostly the overflow bin are dropped — that bucket is "50th or worse", so such a row draws as one lone mark at the right edge and reads as a rendering fault rather than as data. Ships with a "Numbers" table twin so nothing is encoded in colour alone.2 · How contested each position is. The same matrix read down its columns: effective candidates per finishing position (inverse Herfindahl). Stops one short of
max_hist_rank, since the overflow column sums to far more than one player's worth of probability and would render as false chaos at the right edge. Today MPO position 1 has 1.0 effective candidates and #28 has 19.3 · The cutline, and how far away you are. Points only go up, so a total today is a hard floor and the gap to the line is closeable. The axis reaches down to the chasers rather than just covering the cutline — the horizontal distance between the two is the chart. Annotates the nearest chaser and the furthest back with a real shot (
p_cut ≥ 10%); the deepest tick on the rug is a sub-1% longshot 397 points adrift, whose gap is just noise.4 · The ways in.
p_champdecomposed into standings cut, MVP-performance bid, and the event-winner invite (the residual). Two semantic hues plus a structural grey for the invite, which is an entitlement rather than a competitive route. Surfaces cases the headline number hides — Paul McBeth at rank 75 is 11.6% via the standings and 40.9% via one weekend at the MVP Open.The fifth re-runs the cutline replay held fixed at one event's result:
5 · Where the season actually gets decided. Per contender, per remaining event, the swing in auto-bid odds between a 90th-percentile week there and a 10th-percentile one. This is what makes the 2026 counting caps visible: a new result only counts for what it beats, so winning Worlds is worth the full 300 to a player with a thin major pool and 200 to one whose best two are already banked. An event a player isn't entered for comes back
n/arather than a leverage of zero dressed up as a real number — Lizotte and Evan Smith aren't in the Idlewild field, so one of the four remaining events literally cannot move their season.Notes on the leverage panel
replay()'s per-event pre-sampling is factored out assamplePoints()so the grid draws each event once and reuses it across nine conditional runs rather than redrawing per run.p_cutin both divisions.Second commit:
probClass's colours never renderedprobClasshas always returnedpos/pend/dim, and its comment has always promised "green = effectively in, yellow = live bubble". Buttokens.cssdefines.pos/.pendonly as.table-ledger tdbackground fills, and every caller puts the class on the number inside the cell — aspanor abelement. Only.dim, a plain global colour rule, ever did anything, so five probability columns in the forecast table have been rendering as flat ink.Two lines of CSS colour the number instead of filling the cell. Filling would put five adjacent solid columns across the table, and a filled cell also swallows
tr.expandable:hover's fill, which is the table's only cue that rows are clickable. Both hues clear 5.7:1 against the paper and against the hover fill, in dark, newsprint, and print (@media printredefines the tokens). The cell-fill form stays available and is still used where a single anchor column earns it.Testing
pos→rgb(79,214,136)and 6 resolvepend→rgb(217,171,38); the 99% threshold lands where it should.Caveat carried into the copy
Playoff fields still assume every qualifier attends, so GMC and MVP Open leverage is an upper bound until signups open. Both the ways-in and leverage panels say so.
🤖 Generated with Claude Code