RRosternomics

Yankees NYY

GM: Brian Cashman

Overview

CONTEND → steady
expensive & winning · year 3 in this regime · payroll 1.7× league · TAB +6.4
+6.4TAB — talent above budget10th/30
+1.7WAB — realized wins (TAB + luck)14th/30
6.0depth — 2+ WAR regulars2nd/30
-2.3dead WAR — sub-replacement drag5th/30
16%dead money %2nd/30
0.563win %5th/30
47.0roster fWAR3rd/30
+24surplus $M (value vs cost)23rd/30
$272Mpayroll $M3rd/30luxury tax $294M

Roster

CFTrent Grisham 65GCody Bellinger 11GSpencer Jones 3G
LFCody Bellinger 64GMax Schuemann 5GJosé Caballero 2G
RFAaron Judge 53GSpencer Jones 7GMax Schuemann 6G
SSJosé Caballero 46GAnthony Volpe 20GMax Schuemann 3G
2BJazz Chisholm Jr. 65GAmed Rosario 5GJosé Caballero 2G
3BRyan McMahon 61GAmed Rosario 26GJosé Caballero 3G
1BBen Rice 39GPaul Goldschmidt 33GCody Bellinger 2G
STARTERSCam Schlittler 4.2WARMax Fried 2.2WARRyan Weathers 1.6WARWill Warren 1.2WARCarlos Rodón 1.0WAR
CAustin Wells 47GJ.C. Escarra 25GAli Sánchez 3G
DHGiancarlo Stanton 23GBen Rice 23GJasson Domínguez 7G
BULLPEN David Bednar 13svFernando Cruz 11hTim Hill 8hBrent Headrick 7hJake Bird 7hCamilo Doval 3hPaul Blackburn 0hRyan Yarbrough 0h
CAustin Wells 47 · J.C. Escarra 25 · Ali Sánchez 3
1BBen Rice 39 · Paul Goldschmidt 33 · Cody Bellinger 2
2BJazz Chisholm Jr. 65 · Amed Rosario 5 · José Caballero 2
3BRyan McMahon 61 · Amed Rosario 26 · José Caballero 3
SSJosé Caballero 46 · Anthony Volpe 20 · Max Schuemann 3
LFCody Bellinger 64 · Max Schuemann 5 · José Caballero 2
CFTrent Grisham 65 · Cody Bellinger 11 · Spencer Jones 3
RFAaron Judge 53 · Spencer Jones 7 · Max Schuemann 6
DHGiancarlo Stanton 23 · Ben Rice 23 · Jasson Domínguez 7
SPCam Schlittler 4.2 · Max Fried 2.2 · Ryan Weathers 1.6 · Will Warren 1.2 · Carlos Rodón 1.0
BP David Bednar 13 · Fernando Cruz 11 · Tim Hill 8 · Brent Headrick 7 · Jake Bird 7 · Camilo Doval 3 · Paul Blackburn 0 · Ryan Yarbrough 0

Each position lists its starter then backups by games (so platoons & bench show). Starters (mound) & bullpen (left, foul territory) are ranked by WAR; marks the closer (most save chances). MLB Stats API.

2026 roster — value & surplus

PlayerAgeServiceGfWARSalary $MSurplus $M
Cam Schlittler 25 pre-arb 226.6 0.8 +40.7
Ben Rice 27 pre-arb 985.1 0.8 +31.0
Cody Bellinger 30 FA-elig 1014.4 36.5 -8.8
Jazz Chisholm Jr. 28 arb 973.9 10.2 +14.1
Max Fried 32 FA-elig 113.4 14.5 +7.1
Aaron Judge 34 FA-elig 593.4 40.0 -18.8
Ryan Weathers 26 arb 192.5 1.4 +14.5
Brent Headrick 28 pre-arb 522.5 0.8 +15.1
Paul Goldschmidt 38 FA-elig 742.4 4.0 +11.0
Will Warren 27 pre-arb 202.0 0.8 +11.5
David Bednar 31 arb 401.8 9.0 +2.6
Anthony Volpe 25 arb 491.7 3.5 +7.0
Gerrit Cole 35 FA-elig 111.6 36.0 -25.8
Carlos Rodón 33 FA-elig 91.5 27.8 -18.4
Trent Grisham 29 FA-elig 831.4 22.0 -13.1
Fernando Cruz 36 arb 481.3 1.4 +6.5
José Caballero 29 pre-arb 881.2 2.0 +5.3
Ryan McMahon 31 FA-elig 841.1 16.0 -9.3
Paul Blackburn 32 FA-elig 350.7 2.0 +2.3
Max Schuemann 29 pre-arb 340.4 0.7 +2.1
Ali Sánchez 29 230.4 0.5 +2.1
Ryan Yarbrough 34 FA-elig 250.4 2.5 -0.3
J.C. Escarra 31 pre-arb 320.2 0.8 +0.5
Yovanny Cruz 26 30.2 0.0 +1.2
Angel Chivilli 23 pre-arb 90.1 0.7 +0.2
Kervin Castro 27 pre-arb 20.1 0.0 +0.9
Giancarlo Stanton 36 FA-elig 240.1 19.0 -18.2
Jake Bird 30 arb 360.1 1.0 -0.3
Jasson Domínguez 23 pre-arb 430.1 0.6 -0.1
Spencer Jones 25 310.1 0.1 +0.5
Elmer Rodríguez 22 40.0 0.0 +0.1
Yerry De los Santos 28 50.0
Austin Wells 26 pre-arb 70-0.1 0.9 -1.7
Amed Rosario 30 FA-elig 59-0.2 2.5 -3.6
Camilo Doval 28 arb 41-0.2 6.1 -7.2
Randal Grichuk↔ CHW 34 16-0.3 0.1 -2.2
Oswaldo Cabrera 27 arb 3-0.5 1.2 -4.2
Brendan Beck 27 2-0.7 0.0 -4.1
Luis Gil 28 arb 4-0.8 2.2 -6.9
Tim Hill 36 FA-elig 46-0.9 3.0 -8.9

Key additions (new vs 2025)

PlayerfWAR
Ryan Weathers1.6
Gerrit Cole1.0
Max Schuemann0.3
Ali Sánchez0.3
Angel Chivilli0.1
Kervin Castro0.1

Key subtractions (gone from 2025)

Player2025 fWAR
Devin Williams1.4
Mark Leiter Jr.0.6
Luke Weaver0.5
DJ LeMahieu0.3
Marcus Stroman0.1
JT Brubaker0.1

Current & Historical Payroll

2026 payroll
$271.5M
luxury tax $294M
2027 guaranteed
$202.8M

What the front office was committed to as of 2026, 2026–2032. Guaranteed $ are contract commitments signed by 2026; arbitration & free-agent years are projected from service time. Hover a figure for its luxury-tax (CBT) & actual-cash values.

current $ guaranteed $ arbitration (proj) pre-arb (proj) free agent club option player option mutual option vesting option

Summary — luxury-tax (CBT) basis

2026202720282029203020312032
Tax threshold$244M$247M$250M$253M$256M$259M$262M
Guaranteed contracts$249M$212M$155M$92M$92M$67M
Proj. arbitration$40M
Proj. pre-arbitration$5M$22M
Player benefits$18M$18M$18M$18M$18M$18M
Total CBT payroll$312M$230M$195M$110M$110M$85M
Tax space−$-68M+$17M+$55M+$143M+$146M+$174M
Est. tax bill$50M

Luxury-tax (CBT) basis: contract AAV + ~$18M benefits/bonus-pool — distinct from the base-salary payroll above. Threshold is the CBA line. Tier-3 payor rates on the overage. Forward years show only guaranteed deals (arb/pre-arb not yet set). Figures are estimates.

Guaranteed / signed (14)

PlayerAgeSvc2026202720282029203020312032
Aaron Judge349.051$40.0M$40.0M$40.0M$40.0M$40.0M$40.0MFREE AGENT
Cody Bellinger308.160$36.5M$36.5M$29.8MOPT-OUT$29.8MOPT-OUT$29.9MPLAYERFREE AGENT
Gerrit Cole3512.111$36.0M$36.0M$36.0MFREE AGENT
Carlos Rodón3310.168$27.8M$27.8M$27.8MFREE AGENT
Trent Grisham296.060$22.0MFREE AGENT
Giancarlo Stanton3615.118$19.0M$15.0M$15.0MCLUBFREE AGENT
Ryan McMahon318.006$16.0M$16.0MFREE AGENT
Max Fried327.148$14.5M$31.5M$31.5M$31.5M$31.5M$31.5M$31.5M
Paul Goldschmidt3814.059$4.0MFREE AGENT
Tim Hill367.112$3.0MCLUBFREE AGENT
Amed Rosario308.062$2.5MFREE AGENT
Ryan Yarbrough347.117$2.5MFREE AGENT
Paul Blackburn326.018$2.0MFREE AGENT
Randal Grichuk3411.033$145kFREE AGENT

Arbitration-eligible (9)

PlayerAgeSvc2026202720282029203020312032
Jazz Chisholm Jr.285.075$10.2MFREE AGENT
David Bednar315.073$9.0MFREE AGENT
Camilo Doval284.071$6.1MARB 3FREE AGENT
Anthony Volpe253.000$3.5MARB 2ARB 3FREE AGENT
Luis Gil283.073$2.2MARB 2ARB 3FREE AGENT
Fernando Cruz363.035$1.4MARB 2ARB 3FREE AGENT
Ryan Weathers263.066$1.4MARB 2ARB 3FREE AGENT
Oswaldo Cabrera273.050$1.2MARB 2ARB 3FREE AGENT
Jake Bird303.051$975kARB 2ARB 3FREE AGENT

Pre-arbitration (14)

PlayerAgeSvc2026202720282029203020312032
José Caballero292.170$2.0MARB 1ARB 2ARB 3FREE AGENT
Austin Wells262.031$866kARB 1ARB 2ARB 3FREE AGENT
Ben Rice271.076$846kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Will Warren271.016$827kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Cam Schlittler250.082$801kPre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
J.C. Escarra310.139$800kPre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Brent Headrick280.171$793kPre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Angel Chivilli231.036$724kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Max Schuemann292.000$667kARB 1ARB 2ARB 3FREE AGENT
Jasson Domínguez231.130$638kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Ali Sánchez292.000$496kARB 1ARB 2ARB 3FREE AGENT
Elmer Rodríguez22$42k
Kervin Castro270.057$8kPre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Yerry De los Santos28

Graphs

Every roster slot ranked best→worst by WAR. Bars = Yankees 2026; the black line = the league-average player at each slot that season. Staying above the line deep into the roster is the depth edge; dropping below it = holes & dead weight.

What each regime is worth — every settled team-season since 1985

RegimeSeasonsWin %Playoff %Pennant %WS %
WINDOW 28952.230.14.81.0
CONTEND 31156.863.019.310.3
FORCED-SPEND 26047.98.81.21.2
REBUILD 32843.31.20.30.3
const FONT={family:"'Roboto Mono', monospace",size:12}; const ORBIT = [[1985, 1.352, 12.53], [1986, 1.402, 5.33], [1987, 1.533, -3.16], [1988, 1.649, -8.48], [1989, 1.029, -2.54], [1990, 1.173, -15.29], [1991, 1.143, -10.09], [1992, 1.109, 0.62], [1993, 1.381, 6.52], [1994, 1.436, 12.5], [1995, 1.58, -0.14], [1996, 1.512, 2.43], [1997, 1.605, 6.43], [1998, 1.588, 17.45], [1999, 1.803, 5.82], [2000, 1.888, -3.47], [2001, 1.882, 2.14], [2002, 2.002, 13.92], [2003, 2.112, 11.88], [2004, 2.76, -6.07], [2005, 2.884, -14.29], [2006, 2.544, -3.91], [2007, 2.523, 2.09], [2008, 2.484, -6.98], [2009, 2.281, 9.29], [2010, 2.38, 3.28], [2011, 2.079, 11.29], [2012, 2.121, 7.41], [2013, 2.304, -15.17], [2014, 2.004, -7.44], [2015, 1.794, -2.14], [2016, 1.742, -7.29], [2017, 1.43, 13.9], [2018, 1.224, 16.54], [2019, 1.67, 8.1], [2021, 1.736, -0.91], [2022, 1.725, 14.19], [2023, 1.693, -12.33], [2024, 1.823, 7.16], [2025, 1.578, 10.87], [2026, 1.702, 6.42]], TEAMCOL = '#1A2B5E'; function contrastColor(hex,dark){let r=parseInt(hex.slice(1,3),16)/255,g=parseInt(hex.slice(3,5),16)/255,b=parseInt(hex.slice(5,7),16)/255; const mx=Math.max(r,g,b),mn=Math.min(r,g,b);let h=0,s=0,l=(mx+mn)/2; if(mx!==mn){const d=mx-mn;s=l>0.5?d/(2-mx-mn):d/(mx+mn);h=mx===r?(g-b)/d+(g1)t-=1;if(t<1/6)return p+(q-p)*6*t;if(t<0.5)return q;if(t<2/3)return p+(q-p)*(2/3-t)*6;return p;} let R,G,B;if(s===0){R=G=B=L;}else{const q=L<0.5?L*(1+s):L+s-L*s,p=2*L-q;R=h2(p,q,h+1/3);G=h2(p,q,h);B=h2(p,q,h-1/3);} const x=v=>('0'+Math.round(v*255).toString(16)).slice(-2);return '#'+x(R)+x(G)+x(B);} const CURRENT_YEAR = 2026; function drawOrbit(){ const orbEl=document.getElementById('orbit'); if(!ORBIT || ORBIT.length<2){ orbEl.innerHTML='

Not enough history to chart.

'; return; } const f=+document.getElementById('yfrom').value, t=+document.getElementById('yto').value, w=+document.getElementById('win').value; document.getElementById('yflab').textContent=f; document.getElementById('ytlab').textContent=t; document.getElementById('winlab').textContent = w===1?'raw':(w+'-yr'); const dark=document.documentElement.getAttribute('data-theme')==='dark'; const ink=dark?'#cdd4dc':'#1d2630', grid=dark?'#2c313a':'#d6dbe2', refc=dark?'#5a636d':'#9aa3ad', dirc=dark?'#ffffff':'#1d2630'; const col=contrastColor(TEAMCOL,dark); function sm(a,w){const h=(w-1)/2,o=[];for(let i=0;id[0]>=f&&d[0]<=t); if(raw.length<2){ Plotly.purge('orbit'); orbEl.innerHTML='

Pick a wider year range.

'; return; } const s=sm(raw,w),n=s.length,X=s.map(d=>Math.round(d[1]*100)/100),Y=s.map(d=>Math.round(d[2]*10)/10),yr=s.map(d=>d[0]); let xa=Math.min(1,...X),xb=Math.max(1,...X),ya=Math.min(0,...Y),yb=Math.max(0,...Y); const px=(xb-xa)*.12+.04,py=(yb-ya)*.12+1.5;xa-=px;xb+=px;ya-=py;yb+=py; const k=Math.max(0,n-3), ann=[]; [['WINDOW','#15b6a0',xa<1&&yb>0,xa,yb,'left','top'],['CONTEND','#2f7ed8',xb>1&&yb>0,xb,yb,'right','top'], ['FORCED-SPEND','#ec5a3c',xb>1&&ya<0,xb,ya,'right','bottom'],['REBUILD','#f0a91c',xa<1&&ya<0,xa,ya,'left','bottom']].forEach(q=>{ if(q[2]) ann.push({x:q[3],y:q[4],xref:'x',yref:'y',text:q[0],showarrow:false,xanchor:q[5],yanchor:q[6],font:{size:10,color:q[1]},opacity:.85});}); ann.push({x:X[n-1],y:Y[n-1],ax:X[n-1]-0.5*(X[n-1]-X[k]),ay:Y[n-1]-0.5*(Y[n-1]-Y[k]),xref:'x',yref:'y',axref:'x',ayref:'y',showarrow:true,arrowhead:2,arrowsize:1.3,arrowwidth:1.7,arrowcolor:dirc,text:''}); Plotly.newPlot('orbit',[{x:X,y:Y,mode:'lines+markers',line:{color:col,width:2.6,shape:'spline',smoothing:0.7}, marker:{color:col,size:s.map((d,i)=>5+7*(i/Math.max(1,n-1))),line:{color:dark?'#1b1e23':'#fff',width:0.6}}, text:yr,hovertemplate:'%{text}
payroll %{x:.2f}× lg · TAB %{y:+.1f}
'+(w===1?'single season (raw)':w+'-yr smoothed avg')+''}], {margin:{l:50,r:14,t:10,b:40},paper_bgcolor:'rgba(0,0,0,0)',plot_bgcolor:'rgba(0,0,0,0)',font:FONT, xaxis:{title:{text:'relative payroll (× lg avg)',font:{size:11}},range:[xa,xb],gridcolor:grid,zeroline:false}, yaxis:{title:{text:'TAB (talent above budget)',font:{size:11}},range:[ya,yb],gridcolor:grid,zeroline:false}, shapes:[{type:'line',x0:1,x1:1,y0:ya,y1:yb,line:{color:refc,width:1,dash:'dot'}},{type:'line',x0:xa,x1:xb,y0:0,y1:0,line:{color:refc,width:1,dash:'dot'}}], annotations:ann},{displayModeBar:false,responsive:true}); } (function initOrbitSliders(){ const yf=document.getElementById('yfrom'), yt=document.getElementById('yto'); if(ORBIT && ORBIT.length>=2){ const yrs=ORBIT.map(d=>d[0]), y0=Math.min(...yrs), y1=Math.max(...yrs); yf.min=y0; yf.max=y1; yf.value=y0; yt.min=y0; yt.max=y1; yt.value=Math.max(y0, Math.min(CURRENT_YEAR, y1)); yf.addEventListener('input',()=>{ if(+yf.value>+yt.value) yt.value=yf.value; drawOrbit(); }); yt.addEventListener('input',()=>{ if(+yt.value<+yf.value) yf.value=yt.value; drawOrbit(); }); document.getElementById('win').addEventListener('input',drawOrbit); } // only paint on load if the orbit pane is the visible default (else it'd draw into a 0-width hidden box) const _op=document.querySelector('.gpane[data-g="orbit"]'); if(!_op || _op.style.display!=='none') drawOrbit(); })(); new MutationObserver(()=>{ const p=document.querySelector('.gpane[data-g="orbit"]'); if(!p || p.style.display!=='none') drawOrbit(); }).observe(document.documentElement,{attributes:true,attributeFilter:['data-theme']}); // ---- roster depth curve (WAR by roster slot vs league-average-by-slot) ---- const WARDIST = {"cats": ["SP", "IF", "OF", "IF", "SP", "OF", "SP", "RP", "IF", "SP", "RP", "IF", "SP", "SP", "OF", "RP", "IF", "IF", "RP", "OF", "C", "RP", "C", "RP", "RP", "RP", "IF", "RP", "OF", "OF", "SP", "RP", "C", "IF", "RP", "OF", "IF", "RP", "SP", "RP"], "league": [3.73, 2.8, 2.36, 2.09, 1.83, 1.64, 1.41, 1.24, 1.09, 0.96, 0.87, 0.78, 0.68, 0.62, 0.56, 0.49, 0.42, 0.36, 0.3, 0.22, 0.19, 0.16, 0.12, 0.1, 0.08, 0.06, 0.04, 0.03, 0.01, -0.01, -0.03, -0.04, -0.07, -0.09, -0.1, -0.12, -0.15, -0.18, -0.22, -0.25], "names": ["Cam Schlittler", "Ben Rice", "Cody Bellinger", "Jazz Chisholm Jr.", "Max Fried", "Aaron Judge", "Ryan Weathers", "Brent Headrick", "Paul Goldschmidt", "Will Warren", "David Bednar", "Anthony Volpe", "Gerrit Cole", "Carlos Rod\u00f3n", "Trent Grisham", "Fernando Cruz", "Jos\u00e9 Caballero", "Ryan McMahon", "Paul Blackburn", "Max Schuemann", "Ali S\u00e1nchez", "Ryan Yarbrough", "J.C. Escarra", "Yovanny Cruz", "Angel Chivilli", "Kervin Castro", "Giancarlo Stanton", "Jake Bird", "Jasson Dom\u00ednguez", "Spencer Jones", "Elmer Rodr\u00edguez", "Yerry De los Santos", "Austin Wells", "Amed Rosario", "Camilo Doval", "Randal Grichuk", "Oswaldo Cabrera", "Brendan Beck", "Luis Gil", "Tim Hill"], "ranks": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40], "team": [4.19, 3.22, 2.8, 2.46, 2.18, 2.14, 1.6, 1.6, 1.52, 1.25, 1.18, 1.05, 1.03, 0.95, 0.9, 0.8, 0.74, 0.68, 0.44, 0.28, 0.26, 0.22, 0.13, 0.12, 0.09, 0.09, 0.08, 0.07, 0.06, 0.06, 0.01, 0.01, -0.08, -0.11, -0.11, -0.21, -0.31, -0.42, -0.48, -0.59]}; let WD_MODE='raw', WC_MODE='team'; const POSCOL={SP:'#2f7ed8',RP:'#6fb1ec',IF:'#f0a91c',OF:'#15b6a0',C:'#ec5a3c'}; const POSLAB={SP:'Starting P',RP:'Bullpen',IF:'Infield',OF:'Outfield',C:'Catcher'}; function buildPosLegend(){ const el=document.getElementById('poslegend'); if(!el) return; el.style.display=WC_MODE==='pos'?'flex':'none'; el.innerHTML=['SP','RP','C','IF','OF'].map(k=>''+POSLAB[k]+'').join(''); } function drawWarDist(){ if(!WARDIST) return; const dark=document.documentElement.getAttribute('data-theme')==='dark'; const col=contrastColor(TEAMCOL,dark), line=dark?'#e8eef0':'#111', grid=dark?'#2c313a':'#d6dbe2', ink=dark?'#cdd4dc':'#1d2630'; let team=WARDIST.team.slice(), league=WARDIST.league.slice(); if(WD_MODE==='prop'){ const ts=team.reduce((a,b)=>a+Math.max(b,0),0)||1, ls=league.reduce((a,b)=>a+Math.max(b,0),0)||1; team=team.map(v=>v/ts); league=league.map(v=>v/ls); } const fmt=WD_MODE==='prop'?'.1%':'.1f'; const names=WARDIST.names||[], cats=WARDIST.cats||[]; const cd=WARDIST.ranks.map((_,i)=>[names[i]||'(empty slot)', WARDIST.team[i], POSLAB[cats[i]]||'']); const barcol=WC_MODE==='pos'?cats.map(c=>POSCOL[c]||'#9aa3ad'):col; const posbit=WC_MODE==='pos'?' · %{customdata[2]}':''; const htmpl=WD_MODE==='prop' ? '%{customdata[0]}'+posbit+'
slot %{x} · %{customdata[1]:.1f} WAR
%{y:.1%} of roster WAR' : '%{customdata[0]}'+posbit+'
slot %{x} · %{y:.1f} WAR'; buildPosLegend(); const _div=document.getElementById('wardist'); const traces=[ {type:'bar',x:WARDIST.ranks,y:team,name:'NYY',marker:{color:barcol},customdata:cd,hovertemplate:htmpl}, {type:'scatter',x:WARDIST.ranks,y:league,name:'league avg',mode:'lines',line:{color:line,width:2,shape:'spline',smoothing:0.6},hovertemplate:'league slot %{x}: %{y:'+fmt+'}'} ]; const layout={margin:{l:48,r:12,t:6,b:38},paper_bgcolor:'rgba(0,0,0,0)',plot_bgcolor:'rgba(0,0,0,0)',font:FONT, transition:{duration:360,easing:'cubic-in-out'}, xaxis:{title:{text:'roster slot (ranked by WAR)',font:{size:11}},gridcolor:grid,zeroline:false,color:ink}, yaxis:{title:{text:WD_MODE==='prop'?'share of roster WAR':'WAR',font:{size:11}},gridcolor:grid,zeroline:true,zerolinecolor:grid,color:ink,tickformat:WD_MODE==='prop'?'.0%':undefined}, showlegend:true,legend:{orientation:'h',y:1.13,x:0,font:{size:11}},bargap:0.14}; if(_div.data&&_div.data.length){ Plotly.react(_div,traces,layout,{displayModeBar:false,responsive:true}); } else { Plotly.newPlot(_div,traces,layout,{displayModeBar:false,responsive:true}); } } window.wdMode=function(m){WD_MODE=m;document.getElementById('wd-raw').classList.toggle('on',m==='raw');document.getElementById('wd-prop').classList.toggle('on',m==='prop');drawWarDist();}; window.wcToggle=function(){WC_MODE=WC_MODE==='pos'?'team':'pos';const b=document.getElementById('wc-btn');b.classList.toggle('on',WC_MODE==='pos');b.textContent=WC_MODE==='pos'?'Show team color':'Color by position';drawWarDist();}; // ---- Graphs dropdown switcher (orbit ⇄ roster value distribution) ---- let _wdRendered=false; window.showTeamGraph=function(g){ document.querySelectorAll('.gpane').forEach(p=>{ p.style.display=(p.dataset.g===g)?'':'none'; }); if(g==='orbit'){ drawOrbit(); } else if(g==='roster'){ if(!_wdRendered){ drawWarDist(); _wdRendered=true; } else { try{ Plotly.Plots.resize('wardist'); }catch(e){ drawWarDist(); } } } }; { const _ts=document.getElementById('tgsel'); if(_ts) _ts.addEventListener('change',()=>showTeamGraph(_ts.value)); } // initial paint of the default pane (roster when the team has roster data, else orbit already drawn above) if(WARDIST){ drawWarDist(); _wdRendered=true; } // redraw the roster pane on theme change only if it's been rendered and is visible if(WARDIST){ new MutationObserver(()=>{ const p=document.querySelector('.gpane[data-g="roster"]'); if(_wdRendered && p && p.style.display!=='none') drawWarDist(); }).observe(document.documentElement,{attributes:true,attributeFilter:['data-theme']}); } // ---- TOC scroll-spy ---- (function(){ const links=[...document.querySelectorAll('.toc a')]; const secs=links.map(a=>document.querySelector(a.getAttribute('href'))).filter(Boolean); links.forEach(a=>a.addEventListener('click',e=>{e.preventDefault();document.querySelector(a.getAttribute('href')).scrollIntoView({behavior:'smooth'});})); if('IntersectionObserver' in window && secs.length){ const obs=new IntersectionObserver(es=>{es.forEach(e=>{if(e.isIntersecting) links.forEach(l=>l.classList.toggle('on',l.getAttribute('href')==='#'+e.target.id));});},{rootMargin:'-15% 0px -75% 0px'}); secs.forEach(s=>obs.observe(s)); } })(); // ---- sortable roster table ---- const SVC_RANK={'pre-arb':0,'arb':1,'FA-elig':2,'?':3}; function cellNum(td){const n=parseFloat(td.textContent.replace(/[^0-9.\-]/g,''));return isNaN(n)?null:n;} function scale(v,mn,mx,g){ if(g==='diverge'){ if(v>=0){const t=mx>0?v/mx:0;return `rgba(26,125,90,${(0.10+0.55*t).toFixed(2)})`;} const t=mn<0?v/mn:0;return `rgba(192,73,43,${(0.10+0.55*t).toFixed(2)})`; } if(g==='salmag'){const t=mx>mn?(v-mn)/(mx-mn):0;return `rgba(128,135,143,${(0.05+0.45*t).toFixed(2)})`;} const t=mx>mn?(v-mn)/(mx-mn):0;return `rgba(26,125,90,${(0.05+0.55*t).toFixed(2)})`; } function applyGradients(table){ [...table.tHead.rows[0].cells].forEach((th,ci)=>{ if(!th.dataset.grad) return; const rows=[...table.tBodies[0].rows], vals=rows.map(r=>cellNum(r.cells[ci])).filter(v=>v!==null); if(!vals.length) return; const mn=Math.min(...vals),mx=Math.max(...vals); rows.forEach(r=>{const v=cellNum(r.cells[ci]);if(v!==null)r.cells[ci].style.background=scale(v,mn,mx,th.dataset.grad);}); }); } function makeSortable(table){ const ths=[...table.tHead.rows[0].cells]; ths.forEach((th,ci)=>{ th.dataset.label=th.textContent.trim(); th.addEventListener('click',()=>{ th._dir=(th._dir===-1)?1:-1; const dir=th._dir, tb=table.tBodies[0], svc=th.dataset.sortType==='service'; [...tb.rows].sort((a,b)=>{ let va,vb; if(svc){va=SVC_RANK[a.cells[ci].textContent.trim()]??9;vb=SVC_RANK[b.cells[ci].textContent.trim()]??9;} else{va=cellNum(a.cells[ci]);vb=cellNum(b.cells[ci]); if(va===null||vb===null){va=a.cells[ci].textContent.trim().toLowerCase();vb=b.cells[ci].textContent.trim().toLowerCase();}} return vavb?dir:0; }).forEach(r=>tb.appendChild(r)); ths.forEach(h=>h.innerHTML=h.dataset.label); th.innerHTML=th.dataset.label+' '+(dir===1?'▲':'▼')+''; }); }); } document.querySelectorAll('table.sortable').forEach(t=>{applyGradients(t);makeSortable(t);});