RRosternomics

Mariners SEA

GM: Woody Woodward

Overview

CONTEND ↗ rising
expensive & winning · year 2 in this regime · payroll 1.17× league · TAB +12.3
+12.3TAB — talent above budget3rd/28
+1.6WAB — realized wins (TAB + luck)13th/28
6.0depth — 2+ WAR regulars16th/28
-4.9dead WAR — sub-replacement drag11th/28
7%dead money %2nd/28
0.528win %10th/28
48.1roster fWAR3rd/28
+2surplus $M (value vs cost)10th/28
$38Mpayroll $M9th/28

Roster

CFKen Griffey Jr. 137GRich Amaral 26GAlex Diaz 5G
LFRich Amaral 63GMark Whiten 36GBrian R. Hunter 29G
RFJay Buhner 142GMark Whiten 77GAlex Diaz 5G
SSAlex Rodriguez 146GAndy Sheets 7G
2BJoey Cora 140GAndy Sheets 18GRich Amaral 15G
3BDave Hollins 144GRuss Davis 51GDoug Strange 39G
1BPaul Sorrento 138GBrian R. Hunter 41GRich Amaral 10G
STARTERSSterling Hitchcock 2.7WARRandy Johnson 1.6WARTerry Mulholland 1.2WARJamie Moyer 1.0WARBob Wolcott 0.9WAR
CDan Wilson 135GJohn Marzano 39GChris Widger 7G
DHEdgar Martinez 134GJay Buhner 8GDoug Strange 7G
BULLPEN Norm Charlton 20svMike Jackson 0hRafael Carmona 0hBobby Ayala 0hTim Davis 0hBlas Minor 0hLee Guetterman 0hEdwin Hurtado 0h
CDan Wilson 135 · John Marzano 39 · Chris Widger 7
1BPaul Sorrento 138 · Brian R. Hunter 41 · Rich Amaral 10
2BJoey Cora 140 · Andy Sheets 18 · Rich Amaral 15
3BDave Hollins 144 · Russ Davis 51 · Doug Strange 39
SSAlex Rodriguez 146 · Andy Sheets 7
LFRich Amaral 63 · Mark Whiten 36 · Brian R. Hunter 29
CFKen Griffey Jr. 137 · Rich Amaral 26 · Alex Diaz 5
RFJay Buhner 142 · Mark Whiten 77 · Alex Diaz 5
DHEdgar Martinez 134 · Jay Buhner 8 · Doug Strange 7
SPSterling Hitchcock 2.7 · Randy Johnson 1.6 · Terry Mulholland 1.2 · Jamie Moyer 1.0 · Bob Wolcott 0.9
BP Norm Charlton 20 · Mike Jackson 0 · Rafael Carmona 0 · Bobby Ayala 0 · Tim Davis 0 · Blas Minor 0 · Lee Guetterman 0 · Edwin Hurtado 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
Ken Griffey Jr. 26 FA-elig 1409.7 7.7 +0.5
Alex Rodriguez 20 pre-arb 1469.2 0.4 +7.3
Edgar Martinez 33 FA-elig 1395.9 3.5 +1.5
Jay Buhner 31 FA-elig 1503.7 5.6 -2.5
Dan Wilson 27 arb 1383.5 0.4 +2.5
Sterling Hitchcock 25 arb 352.7 0.2 +2.0
Darren Bragg↔ BOS 26 pre-arb 691.9 0.1 +1.5
Randy Johnson 32 FA-elig 141.6 6.0 -4.6
Mark Whiten↔ ATL 29 FA-elig 401.6 0.2 +1.1
Rich Amaral 34 arb 1181.5 0.5 +0.8
Terry Mulholland↔ PHI 33 FA-elig 121.2 0.3 +0.8
Norm Charlton 33 FA-elig 701.2 2.1 -1.1
Dave Hollins↔ MIN 30 FA-elig 281.1 0.3 +0.7
Jamie Moyer↔ BOS 33 FA-elig 111.0 0.3 +0.5
Paul Sorrento 30 FA-elig 1430.9 1.0 -0.3
Bob Wolcott 22 pre-arb 300.9 0.1 +0.6
Salomon Torres 24 100.7
Mike Jackson 31 FA-elig 730.7 1.2 -0.6
Bob Wells 29 pre-arb 360.6 0.2 +0.3
Bobby Ayala 26 arb 500.6 0.3 +0.1
Chris Bosio 33 FA-elig 180.5 4.2 -3.8
Joey Cora 31 FA-elig 1440.5 0.8 -0.4
Brian R. Hunter 28 arb 750.5 0.3 +0.1
Tim Davis 25 pre-arb 400.5 0.1 +0.3
Manny Martinez↔ PHI 25 90.3
Matt Wagner 24 150.2
Alex Diaz 27 arb 380.1 0.3 -0.3
Greg McCarthy 27 pre-arb 100.1 0.1 -0.0
Lee Guetterman 37 170.1
Bob Milacki 31 70.0
Mac Suzuki 21 1-0.0
Rafael Carmona 24 pre-arb 53-0.0 0.1 -0.1
Tim Harikkala 24 1-0.1
Rusty Meacham 28 15-0.1
Joe Klink 34 3-0.1
John Marzano 33 FA-elig 41-0.1 0.2 -0.2
Ricky Jordan 31 FA-elig 15-0.1 0.2 -0.3
Raul Ibanez 24 4-0.1
Greg Pirkl↔ BOS 25 7-0.1
Chris Widger 25 8-0.2
Blas Minor↔ NYM 30 arb 11-0.3 0.1 -0.3
Jeff Manto↔ BOS 31 FA-elig 21-0.3 0.1 -0.3
Edwin Hurtado 26 pre-arb 16-0.3 0.1 -0.4
Scott Davison 25 5-0.3
Doug Strange 32 FA-elig 88-0.4 0.4 -0.7
Paul Menhart 27 pre-arb 11-0.4 0.1 -0.5
Andy Sheets 24 pre-arb 47-0.4 0.1 -0.5
Russ Davis 26 pre-arb 51-0.5 0.1 -0.5
Luis Sojo↔ NYY 31 FA-elig 77-1.2 0.5 -1.5

Key additions (new vs 1995)

PlayerfWAR
Sterling Hitchcock2.7
Mark Whiten1.6
Terry Mulholland1.2
Dave Hollins1.1
Jamie Moyer1.0
Paul Sorrento0.9

Key subtractions (gone from 1995)

Player1995 fWAR
Tino Martinez4.5
Jeff Nelson2.2
Mike Blowers1.8
Tim Belcher1.7
Bill Risley1.3
Andy Benes0.6

Current & Historical Payroll

1996 payroll
$38.4M

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 (21)

PlayerAgeSvc1996199719981999200020012002
Ken Griffey Jr.267.000$7.7M$7.9M$8.2M$8.8MFREE AGENT
Randy Johnson328.000$6.0M$6.3M$4.1MFREE AGENT
Jay Buhner319.000$5.6M$4.8M$5.4M$4.8M$1.4M$1.9MFREE AGENT
Chris Bosio3310.000$4.2MFREE AGENT
Edgar Martinez339.000$3.5M$3.0M$3.5M$3.5M$5.4M$5.5M$7.1M
Norm Charlton338.000$2.1M$3.0MFREE AGENT
Mike Jackson3110.000$1.2MFREE AGENT
Paul Sorrento307.000$1.0M$1.5MFREE AGENT
Joey Cora319.000$850k$1.0M$1.4MFREE AGENT
Luis Sojo316.000$507kFREE AGENT
Doug Strange327.000$380kFREE AGENT
Jamie Moyer3310.000$267k$1.7M$2.0M$2.3M$6.0M$6.5M$6.5M
Terry Mulholland3310.000$255kFREE AGENT
Dave Hollins306.000$254kFREE AGENT
Ricky Jordan318.000$250kFREE AGENT
Mark Whiten296.000$235kFREE AGENT
John Marzano339.000$175k$250k$300kFREE AGENT
Jeff Manto316.000$98kFREE AGENT
Lee Guetterman3712.000FREE AGENT
Joe Klink349.000FREE AGENT
Bob Milacki318.000FREE AGENT

Arbitration-eligible (10)

PlayerAgeSvc1996199719981999200020012002
Rich Amaral345.000$450kFREE AGENT
Dan Wilson274.000$400kARB 3FREE AGENT
Alex Diaz274.000$350kARB 3FREE AGENT
Bobby Ayala264.000$325kARB 3FREE AGENT
Brian R. Hunter285.000$300kFREE AGENT
Sterling Hitchcock254.000$235kARB 3FREE AGENT
Blas Minor304.000$78kARB 3FREE AGENT
Rusty Meacham285.000FREE AGENT
Greg Pirkl253.000ARB 2ARB 3FREE AGENT
Salomon Torres243.000ARB 2ARB 3FREE AGENT

Pre-arbitration (18)

PlayerAgeSvc1996199719981999200020012002
Alex Rodriguez202.000$442kARB 1ARB 2ARB 3FREE AGENT
Bob Wells292.000$155kARB 1ARB 2ARB 3FREE AGENT
Tim Davis252.000$140kARB 1ARB 2ARB 3FREE AGENT
Paul Menhart271.000$140kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Rafael Carmona241.000$125kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Russ Davis262.000$120kARB 1ARB 2ARB 3FREE AGENT
Edwin Hurtado261.000$118kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Bob Wolcott221.000$115kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Andy Sheets240.000$109kPre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Greg McCarthy270.000$109kPre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Darren Bragg262.000$71kARB 1ARB 2ARB 3FREE AGENT
Scott Davison25
Tim Harikkala24
Raul Ibanez240.000Pre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Manny Martinez250.000Pre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Mac Suzuki210.000Pre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Matt Wagner24
Chris Widger251.000Pre-ArbARB 1ARB 2ARB 3FREE AGENT

Graphs

Every roster slot ranked best→worst by WAR. Bars = Mariners 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, 0.443, 6.1], [1986, 0.536, -7.33], [1987, 0.438, 9.95], [1988, 0.648, 2.45], [1989, 0.611, 4.26], [1990, 0.645, 11.14], [1991, 0.686, -3.33], [1992, 0.814, -7.23], [1993, 1.052, 4.91], [1994, 0.904, 3.43], [1995, 1.037, 10.41], [1996, 1.172, 12.26], [1997, 1.092, 13.89], [1998, 1.239, 5.25], [1999, 1.052, -7.44], [2000, 1.121, 8.63], [2001, 1.171, 27.16], [2002, 1.262, 13.33], [2003, 1.274, 9.99], [2004, 1.049, -8.58], [2005, 1.155, -12.96], [2006, 1.132, -2.62], [2007, 1.294, -7.82], [2008, 1.266, -19.91], [2009, 1.106, -0.66], [2010, 0.894, -13.99], [2011, 0.903, -11.17], [2012, 0.737, -3.27], [2013, 0.758, -5.07], [2014, 0.869, -2.55], [2015, 1.023, -8.04], [2016, 1.126, 2.14], [2017, 1.175, -4.38], [2018, 1.237, -0.83], [2019, 0.906, -11.33], [2021, 0.634, -4.04], [2022, 0.77, 6.17], [2023, 0.869, 13.26], [2024, 0.873, 5.41], [2025, 0.909, 7.6], [2026, 1.004, 1.73]], TEAMCOL = '#005C5C'; 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": ["OF", "IF", "IF", "OF", "C", "SP", "OF", "SP", "OF", "OF", "SP", "RP", "IF", "SP", "IF", "SP", "SP", "RP", "RP", "RP", "SP", "IF", "IF", "RP", "OF", "SP", "OF", "RP", "RP", "SP", "RP", "RP", "SP", "RP", "RP", "C", "IF", "OF", "IF", "C"], "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": ["Ken Griffey Jr.", "Alex Rodriguez", "Edgar Martinez", "Jay Buhner", "Dan Wilson", "Sterling Hitchcock", "Darren Bragg", "Randy Johnson", "Mark Whiten", "Rich Amaral", "Terry Mulholland", "Norm Charlton", "Dave Hollins", "Jamie Moyer", "Paul Sorrento", "Bob Wolcott", "Salomon Torres", "Mike Jackson", "Bob Wells", "Bobby Ayala", "Chris Bosio", "Joey Cora", "Brian R. Hunter", "Tim Davis", "Manny Martinez", "Matt Wagner", "Alex Diaz", "Greg McCarthy", "Lee Guetterman", "Bob Milacki", "Mac Suzuki", "Rafael Carmona", "Tim Harikkala", "Rusty Meacham", "Joe Klink", "John Marzano", "Ricky Jordan", "Raul Ibanez", "Greg Pirkl", "Chris Widger"], "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": [9.72, 9.25, 5.94, 3.68, 3.47, 2.71, 1.87, 1.65, 1.57, 1.45, 1.24, 1.21, 1.13, 0.96, 0.88, 0.86, 0.74, 0.68, 0.6, 0.56, 0.55, 0.51, 0.5, 0.47, 0.31, 0.17, 0.12, 0.1, 0.05, 0.03, -0.01, -0.03, -0.06, -0.06, -0.08, -0.09, -0.09, -0.11, -0.15, -0.17]}; 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:'SEA',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);});