RRosternomics

Braves ATL

GM: John Schuerholz

Overview

WINDOW → steady
cheap & winning · year 1 in this regime · payroll 0.94× league · TAB +6.8
+6.8TAB — talent above budget6th/30
+2.9WAB — realized wins (TAB + luck)11th/30
10.0depth — 2+ WAR regulars3rd/30
-6.1dead WAR — sub-replacement drag23rd/30
23%dead money %10th/30
0.519win %13th/30
40.3roster fWAR9th/30
+7surplus $M (value vs cost)9th/30
$77Mpayroll $M16th/30

Roster

CFAndruw Jones 154GWillie Harris 20G
LFMatt Diaz 95GWillie Harris 85GBrandon Jones 5G
RFJeff Francoeur 162GMatt Diaz 5G
SSEdgar Renteria 121GYunel Escobar 53GChris Woodward 13G
2BKelly Johnson 133GYunel Escobar 21GChris Woodward 11G
3BChipper Jones 126GChris Woodward 24GYunel Escobar 22G
1BMark Teixeira 128GScott Thorman 84GCraig Wilson 20G
STARTERSTim Hudson 5.5WARJohn Smoltz 5.1WARBuddy Carlyle 0.4WARChuck James 0.2WARJo-Jo Reyes -0.5WAR
CBrian McCann 132GCorky Miller 11GBrayan Peña 10G
DHChipper Jones 6GJarrod Saltalamacchia 2GMatt Diaz 1G
BULLPEN Octavio Dotel 11svRafael Soriano 19hTyler Yates 13hPeter Moylan 8hRon Mahay 7hOscar Villarreal 2hChad Paronto 2hRoyce Ring 0h
CBrian McCann 132 · Corky Miller 11 · Brayan Peña 10
1BMark Teixeira 128 · Scott Thorman 84 · Craig Wilson 20
2BKelly Johnson 133 · Yunel Escobar 21 · Chris Woodward 11
3BChipper Jones 126 · Chris Woodward 24 · Yunel Escobar 22
SSEdgar Renteria 121 · Yunel Escobar 53 · Chris Woodward 13
LFMatt Diaz 95 · Willie Harris 85 · Brandon Jones 5
CFAndruw Jones 154 · Willie Harris 20
RFJeff Francoeur 162 · Matt Diaz 5
DHChipper Jones 6 · Jarrod Saltalamacchia 2 · Matt Diaz 1
SPTim Hudson 5.5 · John Smoltz 5.1 · Buddy Carlyle 0.4 · Chuck James 0.2 · Jo-Jo Reyes -0.5
BP Octavio Dotel 11 · Rafael Soriano 19 · Tyler Yates 13 · Peter Moylan 8 · Ron Mahay 7 · Oscar Villarreal 2 · Chad Paronto 2 · Royce Ring 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.

2007 roster — value & surplus

PlayerAgeServiceGfWARSalary $MSurplus $M
Chipper Jones Jr. 35 FA-elig 1346.9 12.3 +2.5
Tim Hudson 31 FA-elig 345.5 8.5 +3.4
John Smoltz 40 FA-elig 325.1 8.0 +3.0
Edgar Renteria 30 FA-elig 1244.0 9.0 -0.4
Kelly Johnson 25 pre-arb 1473.4 0.4 +6.8
Andruw Jones 30 FA-elig 1543.3 14.0 -6.9
Jeff Francoeur 23 pre-arb 1623.3 0.4 +6.6
Yunel Escobar 24 942.4 0.3 +4.8
Matt Diaz 29 arb 1352.3 0.4 +4.5
Mark Teixeira↔ TEX 27 arb 542.2 3.7 +1.1
Brian McCann 23 pre-arb 1391.5 0.4 +2.8
Willie Harris 29 1171.4
Kyle Davies↔ KCR 23 180.6 0.2 +1.1
Rafael Soriano 27 arb 710.6 1.2 +0.0
Tyler Yates 29 arb 750.5 0.4 +0.7
Peter Moylan 28 810.5
Oscar Villarreal 25 arb 510.4 0.9 -0.1
Buddy Carlyle 29 220.4 0.4 +0.4
Michael Gonzalez 29 arb 180.3 2.4 -1.7
Ron Mahay↔ TEX 36 FA-elig 300.3 0.6 -0.0
Bob Wickman↔ ARI 38 FA-elig 490.3 5.6 -5.0
Octavio Dotel↔ KCR 33 FA-elig 90.2 1.4 -0.9
Chuck James 25 pre-arb 350.2 0.4 +0.1
Jarrod Saltalamacchia↔ TEX 22 470.2
Corky Miller 31 120.1
Manny Acosta 26 210.1
Clint Sammons 24 20.1
Blaine Boyer 25 50.1 0.4 -0.2
Julio Franco↔ NYM 48 FA-elig 150.1 0.3 -0.2
Chad Paronto 31 FA-elig 410.1 0.4 -0.3
Royce Ring↔ SDP 26 110.0
Kevin Barry 28 10.0
Joey Devine 23 100.0
Jeff Bennett 27 30.0
Wilfredo Ledezma↔ DET 26 arb 120.0 0.1 -0.1
Martín Prado 23 28-0.0
Macay McBride↔ DET 24 pre-arb 18-0.1 0.2 -0.3
Jose Ascanio 22 13-0.1
Mark Redman↔ COL 33 FA-elig 6-0.1 0.4 -0.6
Steve Colyer 28 7-0.1
Brandon Jones 23 5-0.2
Brayan Peña 25 pre-arb 16-0.2 0.4 -0.9
Craig Wilson 30 FA-elig 24-0.2 2.0 -2.5
Anthony Lerew 24 3-0.3
Pete Orr 28 pre-arb 57-0.4 0.4 -1.2
Jo-Jo Reyes 22 14-0.5
Ryan Langerhans↔ OAK 27 arb 20-0.7 0.1 -1.6
Chris Woodward 31 FA-elig 92-1.0 0.8 -2.9
Scott Thorman 25 pre-arb 120-1.0 0.4 -2.5
Lance Cormier 26 arb 10-1.2 0.4 -3.0

Key additions (new vs 2006)

PlayerfWAR
Kelly Johnson3.4
Yunel Escobar2.4
Mark Teixeira2.2
Willie Harris1.4
Rafael Soriano0.6
Buddy Carlyle0.4

Key subtractions (gone from 2006)

Player2006 fWAR
Adam LaRoche2.2
Marcus Giles1.0
Horacio Ramirez0.8
John Thomson0.7
Wilson Betemit0.5
Danys Baez0.2

Current & Historical Payroll

2007 payroll
$77.2M

What the front office was committed to as of 2007, 2007–2013. 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 (16)

PlayerAgeSvc2007200820092010201120122013
Andruw Jones3011.000$14.0MFREE AGENT
Chipper Jones Jr.3514.000$12.3M$12.3M$10.0M$14.0M$14.0M$13.0MFREE AGENT
Edgar Renteria3011.000$9.0MFREE AGENT
Tim Hudson318.000$8.5M$15.5M$15.5M$9.0M$9.0M$9.0M$9.0M
John Smoltz4019.000$8.0M$14.0MFREE AGENT
Bob Wickman3815.000$5.6MFREE AGENT
Craig Wilson306.000$2.0MFREE AGENT
Octavio Dotel338.000$1.4MFREE AGENT
Chris Woodward318.000$850kFREE AGENT
Ron Mahay3612.000$647kFREE AGENT
Chad Paronto316.000$420kFREE AGENT
Mark Redman338.000$409kFREE AGENT
Buddy Carlyle298.000$380k$390k$425kFREE AGENT
Julio Franco4825.000$314kFREE AGENT
Corky Miller316.000$400kFREE AGENT
Willie Harris296.000FREE AGENT

Arbitration-eligible (11)

PlayerAgeSvc2007200820092010201120122013
Mark Teixeira274.000$3.7MARB 3FREE AGENT
Michael Gonzalez294.000$2.4MARB 3FREE AGENT
Rafael Soriano275.000$1.2MFREE AGENT
Oscar Villarreal254.000$925kARB 3FREE AGENT
Tyler Yates293.000$412kARB 2ARB 3FREE AGENT
Lance Cormier263.000$405kARB 2ARB 3FREE AGENT
Matt Diaz294.000$395kARB 3FREE AGENT
Wilfredo Ledezma264.000$113kARB 3FREE AGENT
Ryan Langerhans275.000$66kFREE AGENT
Steve Colyer284.000ARB 3FREE AGENT
Jeff Bennett273.000ARB 2ARB 3FREE AGENT

Pre-arbitration (23)

PlayerAgeSvc2007200820092010201120122013
Brian McCann232.000$440kARB 1ARB 2ARB 3FREE AGENT
Jeff Francoeur232.000$428kARB 1ARB 2ARB 3FREE AGENT
Pete Orr282.000$400kARB 1ARB 2ARB 3FREE AGENT
Chuck James252.000$398kARB 1ARB 2ARB 3FREE AGENT
Brayan Peña252.000$380kARB 1ARB 2ARB 3FREE AGENT
Blaine Boyer252.000$380kARB 1ARB 2ARB 3FREE AGENT
Scott Thorman251.000$380kPre-ArbARB 1ARB 2ARB 3FREE AGENT
Kelly Johnson252.000$380kARB 1ARB 2ARB 3FREE AGENT
Yunel Escobar240.000$251kPre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Kyle Davies232.000$236kARB 1ARB 2ARB 3FREE AGENT
Macay McBride242.000$189kARB 1ARB 2ARB 3FREE AGENT
Manny Acosta260.000Pre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Royce Ring262.000ARB 1ARB 2ARB 3FREE AGENT
Anthony Lerew242.000ARB 1ARB 2ARB 3FREE AGENT
Kevin Barry28
Martín Prado231.000Pre-ArbARB 1ARB 2ARB 3FREE AGENT
Brandon Jones23
Jose Ascanio220.000Pre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Jarrod Saltalamacchia220.000Pre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Jo-Jo Reyes220.000Pre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Joey Devine230.000Pre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Clint Sammons240.000Pre-ArbPre-ArbARB 1ARB 2ARB 3FREE AGENT
Peter Moylan281.000Pre-ArbARB 1ARB 2ARB 3FREE AGENT

Graphs

Every roster slot ranked best→worst by WAR. Bars = Braves 2007; 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.431, -23.61], [1986, 1.401, -14.45], [1987, 1.273, -13.43], [1988, 1.03, -21.95], [1989, 0.616, -0.3], [1990, 0.757, -6.68], [1991, 0.705, 2.11], [1992, 1.131, 8.31], [1993, 1.367, 18.33], [1994, 1.367, 16.3], [1995, 1.423, 8.57], [1996, 1.549, 15.1], [1997, 1.317, 22.84], [1998, 1.466, 25.7], [1999, 1.4, 13.27], [2000, 1.486, 7.8], [2001, 1.531, 2.68], [2002, 1.381, 3.37], [2003, 1.507, 11.61], [2004, 1.359, 7.23], [2005, 1.232, 7.29], [2006, 1.02, 0.18], [2007, 0.939, 6.79], [2008, 1.142, -3.51], [2009, 1.101, 6.18], [2010, 1.027, 4.02], [2011, 0.985, -1.77], [2012, 0.885, 5.76], [2013, 0.897, 10.67], [2014, 0.911, -1.0], [2015, 0.791, -14.54], [2016, 0.584, -6.39], [2017, 0.954, -10.45], [2018, 0.699, 9.92], [2019, 1.138, 2.66], [2021, 1.137, 3.69], [2022, 1.274, 14.31], [2023, 1.283, 20.39], [2024, 1.525, 3.14], [2025, 1.218, -5.85], [2026, 1.416, -2.74]], TEAMCOL = '#13274F'; 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 = 2007; 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": ["IF", "SP", "SP", "IF", "IF", "OF", "OF", "IF", "OF", "IF", "C", "OF", "SP", "RP", "RP", "RP", "RP", "SP", "RP", "RP", "RP", "RP", "SP", "C", "C", "RP", "C", "RP", "IF", "RP", "RP", "RP", "RP", "SP", "RP", "IF", "RP", "RP", "SP", "RP"], "league": [5.72, 4.58, 3.74, 3.11, 2.66, 2.43, 2.17, 1.81, 1.59, 1.45, 1.23, 1.1, 0.94, 0.85, 0.75, 0.67, 0.57, 0.49, 0.44, 0.37, 0.3, 0.27, 0.22, 0.18, 0.13, 0.1, 0.08, 0.05, 0.02, -0.0, -0.03, -0.05, -0.07, -0.09, -0.12, -0.15, -0.18, -0.23, -0.29, -0.33], "names": ["Chipper Jones Jr.", "Tim Hudson", "John Smoltz", "Edgar Renteria", "Kelly Johnson", "Andruw Jones", "Jeff Francoeur", "Yunel Escobar", "Matt Diaz", "Mark Teixeira", "Brian McCann", "Willie Harris", "Kyle Davies", "Rafael Soriano", "Tyler Yates", "Peter Moylan", "Oscar Villarreal", "Buddy Carlyle", "Michael Gonzalez", "Ron Mahay", "Bob Wickman", "Octavio Dotel", "Chuck James", "Jarrod Saltalamacchia", "Corky Miller", "Manny Acosta", "Clint Sammons", "Blaine Boyer", "Julio Franco", "Chad Paronto", "Royce Ring", "Kevin Barry", "Joey Devine", "Jeff Bennett", "Wilfredo Ledezma", "Mart\u00edn Prado", "Macay McBride", "Jose Ascanio", "Mark Redman", "Steve Colyer"], "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": [6.88, 5.53, 5.08, 4.0, 3.35, 3.31, 3.28, 2.35, 2.29, 2.23, 1.52, 1.44, 0.6, 0.57, 0.53, 0.49, 0.4, 0.36, 0.32, 0.29, 0.25, 0.23, 0.21, 0.2, 0.14, 0.12, 0.1, 0.07, 0.07, 0.06, 0.05, 0.03, 0.02, 0.02, 0.0, -0.04, -0.06, -0.08, -0.11, -0.11]}; 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:'ATL',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);});