RRosternomics

Yankees NYY

GM: Bob Watson

Overview

CONTEND ↘ cooling
expensive & winning · year 1 in this regime · payroll 1.51× league · TAB +2.4
+2.4TAB — talent above budget11th/28
+4.9WAB — realized wins (TAB + luck)7th/28
11.0depth — 2+ WAR regulars1st/28
-5.3dead WAR — sub-replacement drag16th/28
23%dead money %11th/28
0.568win %3rd/28
41.6roster fWAR6th/28
-15surplus $M (value vs cost)25th/28
$50Mpayroll $M4th/28

1996: World Series champions · 92–70. Ranks are vs the 28 teams that season.

Roster

CFBernie Williams 140GRuben Rivera 14G
LFGerald Williams 70GTim Raines Sr. 50GDarryl Strawberry 26G
RFPaul O'Neill 146GRuben Rivera 19GDarryl Strawberry 8G
SSDerek Jeter 157GLuis Sojo 23GAndy Fox 9G
2BMariano Duncan 104GAndy Fox 72GLuis Sojo 41G
3BCharlie Hayes 143GWade Boggs 123GLuis Sojo 34G
1BTino Martinez 151GCecil Fielder 80GMike Aldrete 9G
STARTERSAndy Pettitte 4.6WARJimmy Key 2.7WARDavid Cone 2.2WARKenny Rogers 2.2WARDwight Gooden 2.1WAR
CJoe Girardi 120GJim Leyritz 55GJorge Posada 4G
DHCecil Fielder 78GDarryl Strawberry 24GMike Aldrete 13G
BULLPEN John Wetteland 43svJeff Nelson 0hGraeme Lloyd 0hMariano Rivera 0hDale Polley 0hJim Mecir 0hSteve Howe 0hDave Pavlas 0h
CJoe Girardi 120 · Jim Leyritz 55 · Jorge Posada 4
1BTino Martinez 151 · Cecil Fielder 80 · Mike Aldrete 9
2BMariano Duncan 104 · Andy Fox 72 · Luis Sojo 41
3BCharlie Hayes 143 · Wade Boggs 123 · Luis Sojo 34
SSDerek Jeter 157 · Luis Sojo 23 · Andy Fox 9
LFGerald Williams 70 · Tim Raines Sr. 50 · Darryl Strawberry 26
CFBernie Williams 140 · Ruben Rivera 14
RFPaul O'Neill 146 · Ruben Rivera 19 · Darryl Strawberry 8
DHCecil Fielder 78 · Darryl Strawberry 24 · Mike Aldrete 13
SPAndy Pettitte 4.6 · Jimmy Key 2.7 · David Cone 2.2 · Kenny Rogers 2.2 · Dwight Gooden 2.1
BP John Wetteland 43 · Jeff Nelson 0 · Graeme Lloyd 0 · Mariano Rivera 0 · Dale Polley 0 · Jim Mecir 0 · Steve Howe 0 · Dave Pavlas 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.

1996 roster — value & surplus

PlayerAgeServiceGfWARSalary $MSurplus $M
Andy Pettitte 24 pre-arb 354.6 0.2 +3.6
Mariano Rivera 26 pre-arb 614.3 0.1 +3.5
Bernie Williams 27 arb 1433.9 3.0 +0.3
Paul O'Neill 33 FA-elig 1503.7 5.3 -2.2
Wade Boggs 38 FA-elig 1323.1 2.0 +0.5
Jimmy Key 35 FA-elig 302.7 1.8 +0.5
Tino Martinez 28 FA-elig 1552.4 2.3 -0.3
Derek Jeter 22 pre-arb 1572.2 0.1 +1.8
Kenny Rogers 31 FA-elig 302.2 5.0 -3.1
David Cone 33 FA-elig 112.2 4.7 -2.8
Dwight Gooden 31 FA-elig 292.1 0.9 +0.8
Mariano Duncan 33 FA-elig 1092.0 0.8 +0.8
Jeff Nelson 29 arb 731.3 0.9 +0.2
John Wetteland 29 FA-elig 621.2 4.0 -3.0
Ramiro Mendoza 24 121.1
Tim Raines Sr. 36 FA-elig 591.1 2.1 -1.1
Ruben Rivera 22 pre-arb 461.0 0.1 +0.8
Joe Girardi 31 FA-elig 1241.0 2.3 -1.5
Jim Leyritz 32 FA-elig 880.8 1.5 -0.8
Bob Wickman↔ MIL 27 arb 580.7 0.7 -0.2
Dave Pavlas 33 FA-elig 160.6 0.1 +0.4
Darryl Strawberry 34 FA-elig 630.6 0.3 +0.2
Charlie Hayes↔ PIT 31 FA-elig 200.5 0.2 +0.2
Brian Boehringer 27 150.5
Cecil Fielder↔ DET 32 FA-elig 530.4 3.1 -2.7
Mark Hutton↔ MIA 26 arb 120.2 0.1 +0.1
Luis Sojo↔ SEA 31 FA-elig 180.2 0.1 +0.1
Steve Howe 38 FA-elig 250.1 0.5 -0.4
David Weathers↔ MIA 26 arb 110.1 0.1 +0.0
Wally Whitehurst 32 FA-elig 20.1 0.1 -0.1
Jim Mecir 26 260.0
Billy Brewer 28 40.0
Paul Gibson 36 40.0
Matt Luke 25 pre-arb 10.0 0.1 -0.1
Graeme Lloyd↔ MIL 29 arb 13-0.1 0.0 -0.1
Mike Aldrete↔ ANA 35 FA-elig 32-0.1 0.1 -0.2
Tim McIntosh 31 3-0.1
Robert Eenhoorn↔ ANA 28 pre-arb 12-0.2 0.1 -0.2
Ricky Bones↔ MIL 27 arb 4-0.2 0.2 -0.3
Dion James 33 6-0.2
Jorge Posada 25 8-0.2
Pat Kelly 28 arb 13-0.3 0.9 -1.1
Dale Polley 30 pre-arb 32-0.4 0.1 -0.4
Scott Kamieniecki 32 arb 7-0.4 1.1 -1.5
Matt Howard 28 35-0.6
Ruben Sierra↔ DET 30 FA-elig 96-0.8 4.2 -4.8
Andy Fox 25 pre-arb 113-0.9 0.1 -0.8
Gerald Williams↔ MIL 29 arb 99-0.9 0.2 -1.0

Key additions (new vs 1995)

PlayerfWAR
Tino Martinez2.4
Kenny Rogers2.2
Dwight Gooden2.1
Mariano Duncan2.0
Jeff Nelson1.3
Ramiro Mendoza1.1

Key subtractions (gone from 1995)

Player1995 fWAR
Jack McDowell3.5
Randy Velarde3.1
Mike Stanley3.1
Sterling Hitchcock2.0
Melido Perez0.5
Tony Fernandez0.5

Current & Historical Payroll

1996 payroll
$49.5M

What the front office was committed to as of 1996, 1996–2002. Pre-2015 has no contract-terms data — forward italic $ are realized actuals (hindsight, not commitments known then). Control labels (ARB/FA) are point-in-time.

current $ guaranteed $ actual $ (hindsight) arbitration (proj) pre-arb (proj) free agent

Guaranteed / signed (24)

PlayerAgeSvc1996199719981999200020012002
Paul O'Neill3311.000$5.3M$5.5M$5.5M$6.2M$6.5M$7.2MFREE AGENT
Kenny Rogers317.000$5.0M$5.0MFREE AGENT
David Cone3310.000$4.7M$6.7M$6.7M$9.5M$12.0MFREE AGENT
Ruben Sierra3010.000$4.2MFREE AGENT
John Wetteland297.000$4.0MFREE AGENT
Cecil Fielder3211.000$3.1M$9.2MFREE AGENT
Joe Girardi317.000$2.3M$2.2M$2.9M$3.4MFREE AGENT
Tino Martinez286.000$2.3M$4.4M$4.3M$4.3M$4.8M$6.3MFREE AGENT
Tim Raines Sr.3617.000$2.1M$1.7M$1.3MFREE AGENT
Wade Boggs3814.000$2.0M$2.0MFREE AGENT
Jimmy Key3512.000$1.8MFREE AGENT
Jim Leyritz326.000$1.5MFREE AGENT$1.2M$631k
Dwight Gooden3112.000$950k$2.0MFREE AGENT$333k
Mariano Duncan3311.000$845k$489kFREE AGENT
Steve Howe3816.000$500kFREE AGENT
Darryl Strawberry3413.000$300k$750k$875kFREE AGENT
Charlie Hayes318.000$236k$1.7MFREE AGENT
Mike Aldrete3510.000$127kFREE AGENT
Wally Whitehurst327.000$125kFREE AGENT
Dave Pavlas336.000$120kFREE AGENT
Luis Sojo316.000$118k$425k$800k$800k$289k$500kFREE AGENT
Paul Gibson368.000FREE AGENT
Dion James3313.000FREE AGENT
Tim McIntosh316.000FREE AGENT

Arbitration-eligible (11)

PlayerAgeSvc1996199719981999200020012002
Bernie Williams275.000$3.0MFREE AGENT
Scott Kamieniecki325.000$1.1MFREE AGENT
Pat Kelly285.000$900kFREE AGENT
Jeff Nelson294.000$860kARB 3FREE AGENT
Bob Wickman274.000$704kARB 3FREE AGENT
Gerald Williams294.000$158kARB 3FREE AGENT
Ricky Bones275.000$154kFREE AGENT
David Weathers265.000$58kFREE AGENT
Mark Hutton263.000$57kARB 2ARB 3FREE AGENT
Graeme Lloyd293.000$43kARB 2ARB 3FREE AGENT
Billy Brewer283.000ARB 2ARB 3FREE AGENT

Pre-arbitration (13)

PlayerAgeSvc1996199719981999200020012002
Andy Pettitte241.000$195kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Mariano Rivera261.000$131kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Derek Jeter221.000$130kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Ruben Rivera221.000$109kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Dale Polley300.000$109kPre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Andy Fox250.000$109kPre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Matt Luke250.000$109kPre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Robert Eenhoorn282.000$79kARB 1ARB 2ARB 3FREE AGENT
Brian Boehringer271.000Pre-ArbARB 1ARB 2ARB 3FREE AGENT
Matt Howard28
Jim Mecir261.000Pre-ArbARB 1ARB 2ARB 3FREE AGENT
Ramiro Mendoza240.000Pre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Jorge Posada251.000Pre-ArbARB 1ARB 2ARB 3FREE AGENT

Graphs

Every roster slot ranked best→worst by WAR. Bars = Yankees 1996; 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 = 1996; 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", "RP", "OF", "OF", "IF", "SP", "IF", "IF", "SP", "SP", "SP", "IF", "RP", "RP", "SP", "OF", "OF", "C", "C", "RP", "RP", "OF", "IF", "RP", "IF", "RP", "IF", "RP", "RP", "SP", "RP", "RP", "RP", "OF", "RP", "OF", "IF", "IF", "SP", "OF"], "league": [6.48, 4.91, 3.86, 3.3, 2.79, 2.44, 2.17, 1.96, 1.68, 1.47, 1.24, 1.07, 0.92, 0.8, 0.68, 0.58, 0.51, 0.44, 0.37, 0.3, 0.25, 0.2, 0.15, 0.12, 0.07, 0.04, 0.01, -0.01, -0.03, -0.06, -0.09, -0.11, -0.14, -0.17, -0.2, -0.22, -0.29, -0.35, -0.38, -0.48], "names": ["Andy Pettitte", "Mariano Rivera", "Bernie Williams", "Paul O\u0027Neill", "Wade Boggs", "Jimmy Key", "Tino Martinez", "Derek Jeter", "Kenny Rogers", "David Cone", "Dwight Gooden", "Mariano Duncan", "Jeff Nelson", "John Wetteland", "Ramiro Mendoza", "Tim Raines Sr.", "Ruben Rivera", "Joe Girardi", "Jim Leyritz", "Bob Wickman", "Dave Pavlas", "Darryl Strawberry", "Charlie Hayes", "Brian Boehringer", "Cecil Fielder", "Mark Hutton", "Luis Sojo", "Steve Howe", "David Weathers", "Wally Whitehurst", "Jim Mecir", "Billy Brewer", "Paul Gibson", "Matt Luke", "Graeme Lloyd", "Mike Aldrete", "Tim McIntosh", "Robert Eenhoorn", "Ricky Bones", "Dion James"], "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.56, 4.27, 3.87, 3.71, 3.08, 2.73, 2.4, 2.24, 2.22, 2.16, 2.09, 1.97, 1.28, 1.24, 1.15, 1.14, 1.04, 0.98, 0.75, 0.65, 0.65, 0.58, 0.47, 0.46, 0.44, 0.24, 0.22, 0.11, 0.1, 0.06, 0.03, 0.02, 0.01, 0.0, -0.07, -0.07, -0.07, -0.15, -0.17, -0.21]}; 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);});