// ===================================================================== // PAGE LAYOUTS — CORE (Schedule, Teams, Roster) · 2 options each // Flat / professional C1 (navy + red). Shared nav/footer/PageHead from // HomeVariations.jsx (on window). Contrast rule: red accents only on // LIGHT backgrounds; on navy/dark use white or --bb-red-bright. // ===================================================================== const { hv, BNav, FooterBlock, BrandBar, PageHead } = window; const pcSt = { wrap: { maxWidth:1200, margin:'0 auto', padding:'56px 40px 80px' }, tabs: { display:'flex', gap:8, marginBottom:28 }, tab: (on) => ({ fontFamily:"'Big Shoulders Display', sans-serif", fontWeight:800, fontSize:14, letterSpacing:'0.05em', textTransform:'uppercase', padding:'10px 20px', borderRadius:8, cursor:'pointer', border:0, background: on?'var(--bb-navy)':'#fff', color: on?'#fff':'var(--bb-navy)', boxShadow: on?'none':'inset 0 0 0 1.5px var(--border-1)' }), // schedule list row: { display:'flex', alignItems:'center', gap:28, background:'#fff', border:'1px solid var(--border-1)', borderRadius:12, padding:'20px 28px', marginBottom:12, boxShadow:'var(--shadow-sm)' }, date: { textAlign:'center', width:72, flexShrink:0 }, dDay: { fontFamily:"'JetBrains Mono', monospace", fontSize:12, letterSpacing:'0.12em', color:'var(--bb-red)' }, dNum: { fontFamily:"'Big Shoulders Display', sans-serif", fontWeight:900, fontSize:44, lineHeight:0.9, color:'var(--bb-navy)' }, dMon: { fontFamily:"'JetBrains Mono', monospace", fontSize:11, letterSpacing:'0.12em', textTransform:'uppercase', color:'var(--fg-3)' }, dYr: { fontFamily:"'JetBrains Mono', monospace", fontSize:10, letterSpacing:'0.1em', color:'var(--fg-3)', marginTop:2 }, match: { flex:1, borderLeft:'1px solid var(--border-1)', paddingLeft:28 }, matchName: { fontFamily:"'Big Shoulders Display', sans-serif", fontWeight:800, fontSize:26, textTransform:'uppercase', color:'var(--bb-navy)', lineHeight:1 }, matchMeta: { fontSize:14, color:'var(--fg-2)', marginTop:6 }, badge: (kind) => ({ fontFamily:"'Big Shoulders Display', sans-serif", fontWeight:900, fontSize:14, letterSpacing:'0.06em', textTransform:'uppercase', padding:'8px 16px', borderRadius:8, flexShrink:0, background: kind==='W'?'rgba(47,125,74,0.14)':kind==='L'?'var(--bb-red-soft)':'var(--bb-navy)', color: kind==='W'?'var(--status-win)':kind==='L'?'var(--bb-red)':'#fff' }), score: { fontFamily:"'JetBrains Mono', monospace", fontWeight:600, fontSize:18, color:'var(--bb-navy)', width:64, textAlign:'center', flexShrink:0 }, // section headers within page blockHd: { fontFamily:"'Big Shoulders Display', sans-serif", fontWeight:900, fontSize:30, textTransform:'uppercase', color:'var(--bb-navy)', margin:'0 0 18px' }, // teams grid teamCard: { background:'#fff', border:'1px solid var(--border-1)', borderRadius:12, overflow:'hidden', boxShadow:'var(--shadow-sm)' }, teamBar: { height:8 }, teamBody: { padding:'22px 24px 26px' }, link: { fontFamily:"'Big Shoulders Display', sans-serif", fontWeight:800, fontSize:13, letterSpacing:'0.06em', textTransform:'uppercase', color:'var(--bb-red)', cursor:'pointer', marginTop:14, display:'inline-block' }, // roster card pCard: { background:'#fff', border:'1px solid var(--border-1)', borderRadius:12, overflow:'hidden', boxShadow:'var(--shadow-sm)' }, pPhoto: { position:'relative', height:220, background:'#eceff4' }, pNum: { position:'absolute', bottom:10, right:14, fontFamily:"'Big Shoulders Display', sans-serif", fontWeight:900, fontSize:64, lineHeight:0.8, color:'#fff', textShadow:'0 2px 12px rgba(0,0,0,0.5)' }, pBody: { padding:'16px 18px 20px' }, pName: { fontFamily:"'Big Shoulders Display', sans-serif", fontWeight:800, fontSize:24, textTransform:'uppercase', color:'var(--bb-navy)', lineHeight:1 }, pMeta: { fontFamily:"'JetBrains Mono', monospace", fontSize:12, letterSpacing:'0.08em', color:'var(--fg-3)', marginTop:6 }, pStat: { fontFamily:"'JetBrains Mono', monospace", fontSize:13, color:'var(--bb-red)', marginTop:8 }, // roster team header — photo left, staff right teamHeader: { display:'grid', gridTemplateColumns:'1.25fr 1fr', gap:0, borderRadius:14, overflow:'hidden', boxShadow:'var(--shadow-md)', border:'1px solid var(--border-1)' }, teamPhoto: { position:'relative', minHeight:340, background:'#e7ebf1' }, staffPanel: { background:'var(--bb-navy)', color:'#fff', padding:'14px 38px', display:'flex', flexDirection:'column', justifyContent:'center' }, staffRow: { padding:'20px 0', borderBottom:'1px solid rgba(255,255,255,0.14)' }, staffLbl: { fontFamily:"'JetBrains Mono', monospace", fontSize:11, letterSpacing:'0.14em', textTransform:'uppercase', color:'var(--bb-red-bright)', marginBottom:8 }, staffName: { fontFamily:"'Big Shoulders Display', sans-serif", fontWeight:800, fontSize:28, lineHeight:1, textTransform:'uppercase' }, staffCoaches: { fontSize:16, lineHeight:1.55, color:'rgba(255,255,255,0.88)' }, staffSub: { fontFamily:"'JetBrains Mono', monospace", fontSize:12, letterSpacing:'0.08em', textTransform:'uppercase', color:'rgba(255,255,255,0.55)' }, honorRow: { display:'flex', alignItems:'center', gap:12, background:'#fff', border:'1px solid var(--border-1)', borderRadius:10, padding:'14px 18px', fontSize:15, color:'var(--bb-navy)', fontWeight:600, boxShadow:'var(--shadow-sm)' }, honorStar: { color:'var(--bb-red)', fontSize:18, flexShrink:0 }, dropHint: { fontFamily:"'JetBrains Mono', monospace", fontSize:12.5, letterSpacing:'0.04em', color:'var(--fg-3)', marginTop:28, textAlign:'center' }, }; function SchedTabs({ active='All' }) { return
{['All','Upcoming','Results'].map(t=>)}
; } // Season year shown alongside each game date (per-game `year` overrides it). const SEASON_YEAR = '2026'; function GameRow({ g }) { const kind = g.status==='upcoming' ? 'UP' : (g.us>g.them?'W':'L'); return (
{g.day}
{g.date.split(' ')[1]}
{g.date.split(' ')[0]}
{g.year||SEASON_YEAR}
{g.home?'vs':'@'} {g.opponent}
{g.time} · {g.field}
{g.status!=='upcoming' &&
{g.us}–{g.them}
}
{kind==='UP'?'Upcoming':kind==='W'?'Win':'Loss'}
); } // ---------- SCHEDULE ---------- const SchedList = ({ data }) => (
{data.games.map((g,i)=>)}
); const SchedSplit = ({ data }) => { const up = data.games.filter(g=>g.status==='upcoming'); const past = data.games.filter(g=>g.status!=='upcoming'); return (

Upcoming

{up.map((g,i)=>(
{g.day}
{g.date.split(' ')[1]}
{g.date.split(' ')[0]}
{g.year||SEASON_YEAR}
{g.home?'vs':'@'} {g.opponent}
{g.time} · {g.field}
))}

Recent Results

{past.map((g,i)=>(
g.them?'W':'L'), fontSize:13, padding:'5px 11px' }}>{g.us>g.them?'W':'L'} {g.us}–{g.them} {g.home?'vs':'@'} {g.opponent} {g.date}, {g.year||SEASON_YEAR}
))}
Season Ledger · 12U
{[['18','Wins'],['4','Losses'],['#3','NorCal']].map(([v,l])=>(
{v}
{l}
))}
); }; // ---------- TEAMS ---------- const TeamsGridPage = ({ data }) => (
{data.teamsFull.map((t,i)=>(
{t.age}
{t.record}
{t.color==='red'?'Red Squad':'Blue Squad'}
Coach {t.coach}
{t.note}
{t.roster && t.roster.length ? (
{t.roster.length} players · Mgr. {t.manager}
) : null} { window.__rosterAge = t.age; window.bbGo&&window.bbGo('roster'); }}>{t.roster && t.roster.length ? 'View Roster →' : 'Roster Soon →'}
))}
View Past Teams & Alumni →
); const TeamsRows = ({ data }) => (
{data.teamsFull.map((t,i)=>(
{t.age}
{t.color==='red'?'Red':'Blue'} Squad · Coach {t.coach}
{t.note}
{t.record}
))}
); // ---------- ROSTER ---------- function TeamPills({ active='12U' }) { return
{['12U','11U','10U','9U','8U','7U'].map(t=>)}
; } function initials(name) { // "B. Sakurai" -> "BS" const parts = name.replace(/\./g, '').trim().split(/\s+/); return parts.map(p => p[0]).join('').slice(0, 2).toUpperCase(); } // Stable per-player key so a dropped headshot is anchored to the PLAYER, not // the row index — reorder/add/remove a name and every photo stays put. function slug(name) { return String(name).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''); } const RosterCards = ({ data }) => { const teams = data.teamsFull; const pick = (window.__rosterAge && teams.find(t => t.age === window.__rosterAge)) || teams.find(t => t.roster && t.roster.length) || teams[0]; window.__rosterAge = null; const [sel, setSel] = React.useState(pick); const squad = `${sel.age} ${sel.color === 'red' ? 'Red' : 'Blue'}`; const roster = sel.roster || []; return (
{teams.map((t)=>( ))}
{roster.length ? ( {/* Team header — photo left, staff right */}
Manager
{sel.manager || 'TBD'}
Coaches
{sel.coaches && sel.coaches.length ? sel.coaches.join(' · ') : 'TBD'}
Players
{roster.length}
on the {sel.age} roster
{/* Roster grid */}
{roster.map((name,i)=>{ const pid = `ros-${sel.age}-${slug(name)}`; const ph = (typeof window!=='undefined' && window.ROSTER_PHOTOS) ? window.ROSTER_PHOTOS[pid] : null; return (
{ph ? {name} : }
{name}
{squad}
); })}
{/* Championships */} {sel.champs && sel.champs.length ? (

Championships & Honors

{sel.champs.map((c,i)=>(
{c}
))}
) : null}

Drop a headshot onto any card to fill it in — photos save automatically.

) : (
{squad} Roster Coming Soon

Coach {sel.coach}'s {squad} roster will be posted once the {sel.age} squad is finalized for the season.

)}
); }; const RosterTable = ({ data }) => { const star = data.players[0]; return (
{/* featured player */}
Captain · #{star.num}
{star.first} {star.last}
{star.pos} · Bats {star.bats}
{star.line.map(([v,l])=>(
{v}
{l}
))}
{/* table */}
#
Player
Pos
Bats
2025 Line
{data.players.map((p,i)=>(
{p.num}
{p.first} {p.last}
{p.pos}
{p.bats}
{p.stat}
))}
); }; Object.assign(window, { SchedList, SchedSplit, TeamsGridPage, TeamsRows, RosterCards, RosterTable });