{id:'r2',sec:'RUNS',n:2,of:7,name:'Middle Run',tag:'RUN',form:'std', routes:{orange:[[10,54],[10,10]],blue:[[32,54],[46,63],[48,14]],green:null,yellow:[[70,54],[90,26]],red:[[50,70],[40,62]]}, handoff:{fx:50,fy:70,tx:46,ty:63},primary:'blue', note:'Blue drops back, takes handoff, hits the gap between C and TE. Straight upfield.'}, {id:'r3',sec:'RUNS',n:3,of:7,name:'WR Sweep',tag:'RUN',form:'std', routes:{orange:[[10,54],[44,38],[90,16]],blue:[[32,54],[10,28]],green:null,yellow:[[70,54],[88,28]],red:[[50,70],[36,60]]}, handoff:{fx:50,fy:70,tx:20,ty:54},primary:'orange', note:'Handoff to Orange who cuts across right. Blue decoys hard left.'}, {id:'r4',sec:'RUNS',n:4,of:7,name:'Draw Play',tag:'RUN',form:'std', routes:{orange:[[10,54],[10,10]],blue:[[32,54],[50,63],[50,14]],green:null,yellow:[[70,54],[90,23]],red:[[50,70],[44,66]]}, handoff:{fx:50,fy:70,tx:50,ty:63},primary:'blue', note:'QB fakes a pass look for 2 counts. Blue delays then takes the handoff up the middle.'}, {id:'r5',sec:'RUNS',n:5,of:7,name:'Pitch Out',tag:'RUN',form:'std', routes:{orange:[[10,54],[8,30]],blue:[[32,54],[68,42],[90,20]],green:null,yellow:[[70,54],[86,24]],red:[[50,70],[46,60]]}, handoff:{fx:50,fy:70,tx:46,ty:60},primary:'blue', note:'QB pitches immediately to Blue going right. No hesitation — pitch and release.'}, {id:'r6',sec:'RUNS',n:6,of:7,name:'Counter',tag:'RUN',form:'std', routes:{orange:[[10,54],[20,40]],blue:[[32,54],[18,44],[58,20]],green:null,yellow:[[70,54],[64,42]],red:[[50,70],[38,60]]}, handoff:{fx:50,fy:70,tx:38,ty:60},primary:'blue', note:'Blue fakes left, cuts hard back right. Sell the fake.'}, {id:'r7',sec:'RUNS',n:7,of:7,name:'Misdirection',tag:'RUN',form:'std', routes:{orange:[[10,54],[50,52],[90,22]],blue:[[32,54],[14,44],[4,26]],green:null,yellow:[[70,54],[86,28]],red:[[50,70],[42,64]]}, handoff:{fx:50,fy:70,tx:34,ty:58},primary:'blue', note:'Orange sweeps hard RIGHT — defenders follow the flow. QB hands to Blue cutting hard LEFT into the vacated gap. Key: wait one beat after the snap for the defense to commit right, then hit Blue going left.'}, // ── TRICKS ── {id:'t1',sec:'TRICKS',n:1,of:5,name:'Jet Sweep',tag:'TRICK',form:'std',motion:'orange', routes:{orange:[[10,54],[46,54],[90,20]],blue:[[32,54],[10,28]],green:null,yellow:[[70,54],[88,28]],red:[[50,70],[44,62]]}, handoff:{fx:50,fy:70,tx:44,ty:54},primary:'orange', note:'Orange in motion at snap — already at full speed. QB gives immediate handoff. Blue decoys.'}, {id:'t2',sec:'TRICKS',n:2,of:5,name:'RB Option Pass',tag:'TRICK',form:'std', routes:{orange:[[10,54],[10,10]],blue:[[32,54],[62,50],[82,32]],green:null,yellow:[[70,54],[88,28]],red:[[50,70],[38,62]]}, handoff:{fx:50,fy:70,tx:38,ty:62},throwL:{fx:38,fy:62,tx:10,ty:10},primary:'blue', note:'QB hands to Blue. Orange open deep? THROW. Nobody open? RUN. Any handoff receiver can pass.'}, {id:'t3',sec:'TRICKS',n:3,of:5,name:'Statue of Liberty',tag:'TRICK',form:'t3',fakeRight:true, routes:{orange:[[10,54],[10,10]],blue:[[32,70],[14,58],[4,30]],green:null,yellow:[[70,54],[88,28]],red:null}, handoff:{fx:50,fy:70,tx:32,ty:70},primary:'blue', note:'QB arm cocked RIGHT (fake). Blue sneaks behind QB and goes LEFT. Defense watches QB.'}, {id:'t4',sec:'TRICKS',n:4,of:5,name:'WR Throwback',tag:'TRICK',form:'std', routes:{orange:[[10,54],[55,44],[80,36]],blue:[[32,54],[32,13]],green:null,yellow:[[70,54],[72,13]],red:[[50,70],[26,58],[10,42]]}, handoff:{fx:50,fy:70,tx:30,ty:54},throwL:{fx:80,fy:36,tx:10,ty:42},primary:'red', note:'QB hands to Orange sweeping right. QB sneaks left. Orange stops and throws back to QB.'}, {id:'t5',sec:'TRICKS',n:5,of:5,name:'Double Reverse',tag:'TRICK',form:'std', routes:{orange:[[10,54],[58,44],[8,20]],blue:[[32,54],[62,44]],green:null,yellow:[[70,54],[74,16]],red:[[50,70],[42,62]]}, handoff:{fx:50,fy:70,tx:42,ty:62},lateral:{fx:62,fy:44,tx:30,ty:44},primary:'orange', note:'QB to Blue going right. Blue laterals to Orange going left. Deep routes clear the way.'}, ]; const SEC_LABELS={MAIN:BLUE,RUNS:DGOLD,TRICKS:'#7C3AED'}; const TAG_COL={TRANSITION:BLUE,RUN:DGOLD,TRICK:'#7C3AED'}; const RULE_SECTIONS=[ {id:'adv',label:'USE TO YOUR ADVANTAGE',color:'#16A34A',bg:'#F0FDF4',border:'#86EFAC',rules:[ {title:'No rush on QB — ever',body:'In 8U regular season, defenders CANNOT rush the passer unless a legal handoff has already been executed in the backfield. Braxton and Christopher have zero pass rush on every dropback. The only clock is 7 seconds. Tell them to relax and find the open receiver.'}, {title:'Coach on field pre-snap',body:'One coach is allowed on the field before every snap to position players. You MUST step off before the ball is snapped. Use this every single play — walk out, point each kid to their spot, step back, snap.'}, {title:'Run anywhere on the field',body:'No run zones are eliminated for 8U. You can run on any down, from any yard line, including inside the 5-yard line. Sweeps and draws are legal everywhere.'}, {title:'Any handoff receiver can throw',body:'After receiving a direct handoff behind the LOS, any player (RB, WR, TE) can choose to pass instead of run. This is what makes the RB Option Pass and WR Throwback legal.'}, {title:'Interceptions are returnable for 6',body:'If you intercept a pass, your player can run it back for a touchdown worth 6 points. Make sure all defenders know this — after a pick, they sprint.'}, {title:'Pre-snap motion is legal',body:'One offensive player can be in motion before the snap. Motion must be parallel to or moving away from the LOS — not moving forward. Use motion (Jet Sweep) to confuse the defense before the snap.'}, {title:'Formation shifts are allowed',body:'You can shift the whole formation before the snap. All players just need to reset and hold for 1 full second before the snap. Stack formations and overload looks are legal.'}, ]}, {id:'know',label:'KEY RULES TO KNOW',color:BLUE,bg:'#EFF6FF',border:'#93C5FD',rules:[ {title:'QB cannot run directly across LOS',body:'The QB — whoever catches the snap — cannot run straight across the line of scrimmage. They must fake a pass look first ("scramble"). A straight keeper with no fake is a penalty. Applies to both Braxton and Christopher.'}, {title:'7-second pass clock',body:'The QB has 7 seconds from the snap to release the ball or cross the LOS. After 7 seconds, play is dead where the QB stands. Defenders count it out loud — coach your QB to be aware.'}, {title:'4 downs to cross midfield, then 3 to score',body:'Offense gets 4 downs to cross the midfield line. Once past midfield, they get 3 downs to score. If they fail, the other team gets the ball at their own 5-yard line.'}, {title:'40-second play clock',body:'40 seconds between plays. The clock starts when the referee spots the ball. Keep plays moving — do not let kids dawdle between downs.'}, {title:'Handoffs must happen behind LOS',body:'All direct handoffs, pitches, and laterals must occur behind the line of scrimmage. A handoff at or beyond the LOS is illegal. All run plays must exchange the ball in the backfield.'}, {title:'Center sneak is not allowed',body:'The center (Green player) cannot receive the ball and run. The center snaps and is done as a ball carrier. Do not run plays that try to use the center as a runner.'}, {title:'Only one player in motion pre-snap',body:'Only one offensive player may be moving at the snap. That player must move parallel to or away from the LOS. Forward motion before the snap is an illegal motion penalty.'}, {title:'Players must reset 1 second after a shift',body:'If players change formation positions, everyone must stop and hold for a full second before snapping. Rush the snap after a shift and it is a false start penalty.'}, ]}, {id:'score',label:'SCORING',color:'#B45309',bg:'#FFFBEB',border:'#FCD34D',rules:[ {title:'Touchdown — 6 points',body:'Ball carrier crosses the goal line with possession. Count it when the player breaks the plane of the end zone.'}, {title:'Extra point (1 pt) — snap from 5 yards',body:'After a TD, offense attempts a 1-point conversion from the 5-yard line. One play, one shot.'}, {title:'Extra point (2 pts) — snap from 10 yards',body:'A harder conversion from the 10-yard line worth 2 points. Consider this when you need two scores to catch up.'}, {title:'Safety — 2 points',body:'If the ball carrier\'s flag is pulled in their own end zone, the defense scores 2 points. Avoid run plays that start near your own goal line.'}, {title:'Interception returned for TD — 6 points',body:'On a change of possession via interception, the new ball carrier can score immediately. Defenders should always know to run after a pick.'}, ]}, {id:'avoid',label:'PENALTIES TO AVOID',color:'#DC2626',bg:'#FEF2F2',border:'#FCA5A5',rules:[ {title:'Flag guarding',body:'The ball carrier cannot guard their flag by stiff-arming, covering it with their hand or arm, or diving to protect it. This is a spot foul — play ends where the guard occurred. Teach runners to keep arms up and pump naturally.'}, {title:'Illegal contact / no blocking',body:'Flag football has no blocking. Offensive players cannot use their body to impede defenders. Players run routes and decoy only — no physical contact. Teach kids to run routes, not set picks.'}, {title:'Pass interference',body:'Defenders cannot grab, hold, or push a receiver going for the ball. Offense cannot push off on routes either. Both sides are penalized. Teach secondary kids to mirror hips, not grab.'}, {title:'False start',body:'Any offensive player who moves before the snap (other than the one legal motion player) causes a false start. 5-yard penalty, replay the down. Emphasize staying frozen until the snap count.'}, {title:'Illegal forward pass',body:'The QB can only throw one forward pass per play, thrown from behind the LOS. A second forward pass on the same play is illegal. The RB Option Pass throw must always come from behind the LOS.'}, {title:'Roughing / unsportsmanlike conduct',body:'Excessive contact, taunting, or rough play results in a penalty and can mean a yellow card warning or ejection. Keep kids calm and focused — especially after a bad call.'}, ]}, ]; // ── HELPERS ── const lerp=(a,b,t)=>a+(b-a)*t; function gPos(rt,p){ if(!rt||rt.length<2)return null; const n=rt.length-1,g=Math.min(p,.9999)*n,s=Math.floor(g),l=g-s; return[lerp(rt[s][0],rt[Math.min(s+1,n)][0],l),lerp(rt[s][1],rt[Math.min(s+1,n)][1],l)]; } function rPts(rt,p){ if(!rt||rt.length<2||p===0)return''; const n=rt.length-1,g=p*n,s=Math.floor(g),l=g-s; const pts=[...rt.slice(0,s+1)]; if(sq.join(',')).join(' '); } function getBallPos(play,fm,prog){ const qb=fm['red'],ctr=fm['green']; if(!qb)return null; if(prog===0){return ctr?[lerp(ctr[0],qb[0],.45),lerp(ctr[1],qb[1],.45)]:qb;} if(play.handoff&&prog<.4){const t=prog/.4;return[lerp(qb[0],play.handoff.tx,t),lerp(qb[1],play.handoff.ty,t)];} const pRt=play.routes?.[play.primary],pBase=fm[play.primary]; if(pRt&&pBase){return gPos(pRt,prog)||pBase;} if(play.throwL&&prog>0.45){const t=(prog-.45)/.55;return[lerp(play.throwL.fx,play.throwL.tx,t),lerp(play.throwL.fy,play.throwL.ty,t)];} return pBase||qb; } // Flip a play + formation horizontally (mirror across x=50) function applyFlip(play, fm){ const fx=x=>100-x; const flipPt=([x,y])=>[fx(x),y]; const flipRt=rt=>rt?rt.map(flipPt):null; const newFm=Object.fromEntries(Object.entries(fm).map(([k,v])=>[k,v?flipPt(v):v])); const newRoutes=play.routes?Object.fromEntries(Object.entries(play.routes).map(([k,v])=>[k,flipRt(v)])):null; return{ fm:newFm, play:{ ...play, routes:newRoutes, handoff:play.handoff?{fx:fx(play.handoff.fx),fy:play.handoff.fy,tx:fx(play.handoff.tx),ty:play.handoff.ty}:null, throwL:play.throwL?{fx:fx(play.throwL.fx),fy:play.throwL.fy,tx:fx(play.throwL.tx),ty:play.throwL.ty}:null, lateral:play.lateral?{fx:fx(play.lateral.fx),fy:play.lateral.fy,tx:fx(play.lateral.tx),ty:play.lateral.ty}:null, fakeRight:play.fakeRight?false:undefined, fakeLeft:play.fakeRight?true:undefined, } }; } function Football({x,y}){ return h('g',{transform:`translate(${x},${y}) rotate(-22)`,style:{pointerEvents:'none'}}, h('ellipse',{rx:5,ry:2.9,fill:'#6B3A1F',stroke:'rgba(255,255,255,0.85)',strokeWidth:.7}), h('line',{x1:-2.5,y1:0,x2:2.5,y2:0,stroke:'rgba(255,255,255,0.8)',strokeWidth:.5}), h('line',{x1:-1.2,y1:-1.4,x2:-1.2,y2:1.4,stroke:'rgba(255,255,255,0.55)',strokeWidth:.38}), h('line',{x1:0,y1:-1.6,x2:0,y2:1.6,stroke:'rgba(255,255,255,0.55)',strokeWidth:.38}), h('line',{x1:1.2,y1:-1.4,x2:1.2,y2:1.4,stroke:'rgba(255,255,255,0.55)',strokeWidth:.38}) ); } function MiniField({play,size=76}){ const fm=FM[play.form],allC=['red','blue','green','orange','yellow']; return h('svg',{viewBox:'0 0 100 90',width:size,height:size*.9,style:{borderRadius:6,flexShrink:0},preserveAspectRatio:'xMidYMid meet'}, h('rect',{width:100,height:90,fill:'#0D2518'}), h('line',{x1:0,y1:54,x2:100,y2:54,stroke:'rgba(255,255,255,0.4)',strokeWidth:1,strokeDasharray:'3 2'}), h('defs',null,...allC.map(c=>h('marker',{key:`mma-${c}`,id:`mma-${c}`,markerWidth:4,markerHeight:4,refX:3.5,refY:2,orient:'auto',markerUnits:'strokeWidth'},h('path',{d:'M0,0 L0,4 L4,2 z',fill:COL[c].fill})))), ...allC.map(c=>{const rt=play.routes?.[c];if(!rt||rt.length<2)return null;const ip=c===play.primary;return h('polyline',{key:'m'+c,points:rt.map(q=>q.join(',')).join(' '),fill:'none',stroke:COL[c].fill,strokeWidth:ip?2.2:1.3,strokeDasharray:ip?'none':'3 2',strokeOpacity:.95,strokeLinecap:'round',strokeLinejoin:'round',markerEnd:`url(#mma-${c})`});}), ...allC.map(c=>{const base=fm[c];if(!base)return null;const[px,py]=base;return h('circle',{key:'d'+c,cx:px,cy:py,r:5,fill:COL[c].fill,stroke:'white',strokeWidth:.8});}) ); } // ── APP ── function App(){ const[screen,setScreen]=useState('splash'); const[present,setPresent]=useState(Object.fromEntries(ROSTER.map(p=>[p.key,true]))); const[names,setNames]=useState(DEF); const[defaultNames,setDefaultNames]=useState(DEF); const[lineNote,setLineNote]=useState(''); const[idx,setIdx]=useState(0); const[team,setTeam]=useState('A'); const[prog,setProg]=useState(0); const[playing,setPlaying]=useState(false); const[selected,setSelected]=useState(null); const[swapMsg,setSwapMsg]=useState(null); const[exitPrompt,setExitPrompt]=useState(false); const[ruleOpen,setRuleOpen]=useState(null); const[flipped,setFlipped]=useState(false); const swapTimer=useRef(null); const raf=useRef(null),t0=useRef(null); const absent=ROSTER.filter(p=>!present[p.key]); const W='#fff',BG='#F0F4F8',BD='#E2EAF2',TX='#0C1B2A',DM='#5A7A96'; useEffect(()=>{ if(screen==='splash'){const t=setTimeout(()=>setScreen('att'),3600);return()=>clearTimeout(t);} },[screen]); const namesModified=['red','blue','green','orange','yellow'].some(c=>names[team][c]!==defaultNames[team][c]); function toggle(key){setPresent(p=>({...p,[key]:!p[key]}));} function clearSel(){setSelected(null);clearTimeout(swapTimer.current);} function stopAnim(){cancelAnimationFrame(raf.current);setPlaying(false);setProg(0);clearSel();t0.current=null;} function handlePlayerTap(color){ if(playing)return; if(selected===null){setSelected(color);setSwapMsg(null);} else if(selected===color){clearSel();} else{ const n1=names[team][selected],n2=names[team][color]; setNames(prev=>({...prev,[team]:{...prev[team],[selected]:n2||null,[color]:n1||null}})); clearSel(); setSwapMsg({n1:n1||'OPEN',n2:n2||'OPEN'}); clearTimeout(swapTimer.current); swapTimer.current=setTimeout(()=>setSwapMsg(null),2800); } } function resetLineup(){setNames(prev=>({...prev,[team]:{...defaultNames[team]}}));clearSel();setSwapMsg(null);} function startStop(){ if(playing){stopAnim();return;} clearSel();setSwapMsg(null);setProg(0);setPlaying(true);t0.current=null; const tick=ts=>{if(!t0.current)t0.current=ts;const p=Math.min((ts-t0.current)/2400,1);setProg(p);if(p<1)raf.current=requestAnimationFrame(tick);else setPlaying(false);}; raf.current=requestAnimationFrame(tick); } function jumpSec(s){stopAnim();const i=PLAYS.findIndex(p=>p.sec===s);if(i>=0)setIdx(i);} function nav(d){stopAnim();setIdx(i=>Math.max(0,Math.min(PLAYS.length-1,i+d)));} function pickPlay(i){stopAnim();setIdx(i);setScreen('play');} async function handleStart(){ if(absent.length===0){setScreen('play');return;} setScreen('load'); const pres=ROSTER.filter(p=>present[p.key]); const mk=(t)=>{ const other=t==='A'?'B':'A'; const res={red:null,blue:null,green:null,orange:null,yellow:null}; pres.filter(p=>p.team===t).forEach(p=>{res[p.c]=p.name;}); for(const c of Object.keys(res)){ if(!res[c]){const match=pres.find(p=>p.team===other&&p.c===c);if(match)res[c]=match.name;} } const subs=pres.filter(p=>p.sub&&!Object.values(res).includes(p.name+(p.sub?' ★':''))); let si=0; for(const c of Object.keys(res)){if(!res[c]&&sip.name).join(', ')} absent — lineup adjusted`); setTimeout(()=>setScreen('play'),500); } const Hdr=(right)=>h('div',{style:{background:BLUE,borderBottom:`2.5px solid ${GOLD}`,padding:'0 14px',height:54,display:'flex',alignItems:'center',gap:12,flexShrink:0}}, h('img',{src:LOGO,style:{width:40,height:30,objectFit:'contain',flexShrink:0}}), h('div',{style:{flex:1,minWidth:0}}, h('div',{style:{fontSize:11,fontWeight:800,color:GOLD,letterSpacing:1.5,lineHeight:1,textTransform:'uppercase'}},'LA RAMS · Flag Football'), h('div',{style:{fontSize:9,color:'rgba(255,255,255,0.55)',fontWeight:600,marginTop:1}},'Coach Dorr · Asst. Coach Welch · Ages 7–8') ), right&&right ); // ── SPLASH ── if(screen==='splash'){ return h('div',{style:{height:'100%',background:BLUE,display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',padding:'0 40px'}}, h('img',{src:LOGO,style:{width:150,objectFit:'contain',marginBottom:32,animation:'splashin .7s ease'}}), h('div',{style:{fontSize:28,fontWeight:900,color:GOLD,letterSpacing:3,textTransform:'uppercase',animation:'splashin .9s ease',lineHeight:1}},'LA RAMS'), h('div',{style:{fontSize:13,fontWeight:700,color:'rgba(255,255,255,0.5)',letterSpacing:5,marginTop:8,textTransform:'uppercase',animation:'splashin 1.1s ease'}},'Flag Football'), h('div',{style:{marginTop:36,textAlign:'center',animation:'splashin 1.3s ease'}}, h('div',{style:{fontSize:14,fontWeight:700,color:'rgba(255,255,255,0.65)',letterSpacing:.5}},'Coach Dorr · Asst. Coach Welch'), h('div',{style:{marginTop:36,fontSize:11,color:GOLD,fontWeight:700,letterSpacing:.5}},'Developed by Jason Welch') ) ); } // ── ATTENDANCE ── if(screen==='att'){ const tA=ROSTER.filter(p=>p.team==='A'),tB=ROSTER.filter(p=>p.team==='B'),allHere=absent.length===0; const Row=p=>{const here=present[p.key];return h('div',{key:p.key,onClick:()=>toggle(p.key),style:{display:'flex',alignItems:'center',gap:14,padding:'13px 16px',background:here?W:'#F7FAFC',borderBottom:`1px solid ${BD}`,cursor:'pointer',minHeight:64}}, h('div',{style:{width:46,height:46,borderRadius:'50%',background:here?COL[p.c].fill:'#C8D4DF',display:'flex',alignItems:'center',justifyContent:'center',color:here?COL[p.c].txt:'#8FA5BC',fontSize:11,fontWeight:800,flexShrink:0}},POS[p.c]), h('div',{style:{flex:1}}, h('div',{style:{fontSize:17,fontWeight:700,color:here?TX:'#9DAFC0',textDecoration:here?'none':'line-through',lineHeight:1.2}},p.name), h('div',{style:{display:'flex',gap:5,marginTop:4}}, h('span',{style:{fontSize:10,fontWeight:700,padding:'3px 7px',borderRadius:4,background:BLUE+'18',color:BLUE}},`TEAM ${p.team}`), h('span',{style:{fontSize:10,fontWeight:700,padding:'3px 7px',borderRadius:4,background:'#EDF2F7',color:DM}},POS[p.c]), p.sub&&h('span',{style:{fontSize:10,fontWeight:700,padding:'3px 7px',borderRadius:4,background:GOLD+'22',color:DGOLD}},'SUB') ) ), h('div',{style:{width:36,height:36,borderRadius:'50%',background:here?'#DCFCE7':'#FEE2E2',display:'flex',alignItems:'center',justifyContent:'center',fontSize:18,color:here?'#16A34A':'#DC2626',fontWeight:700,flexShrink:0}},here?'✓':'✗') );}; return h('div',{style:{height:'100%',display:'flex',flexDirection:'column',background:BG}}, Hdr(null), h('div',{style:{padding:'14px 16px 10px',background:W,borderBottom:`1px solid ${BD}`,display:'flex',alignItems:'baseline',justifyContent:'space-between',flexShrink:0}}, h('div',{style:{fontSize:22,fontWeight:800,color:TX,letterSpacing:-.5}},"Who's here?"), h('div',{style:{fontSize:14,fontWeight:700,color:absent.length===0?'#16A34A':'#C45500'}},[`${ROSTER.filter(p=>present[p.key]).length} / ${ROSTER.length}`]) ), h('div',{style:{flex:1,overflowY:'auto',WebkitOverflowScrolling:'touch'}}, h('div',{style:{padding:'8px 16px 5px',background:BLUE+'0A',borderBottom:`1px solid ${BD}`}},h('span',{style:{fontSize:9,fontWeight:800,letterSpacing:3,color:BLUE}},'GROUP A')), ...tA.map(Row), h('div',{style:{padding:'8px 16px 5px',background:BLUE+'0A',borderBottom:`1px solid ${BD}`,marginTop:6}},h('span',{style:{fontSize:9,fontWeight:800,letterSpacing:3,color:BLUE}},'GROUP B')), ...tB.map(Row) ), h('div',{style:{padding:'14px 16px',background:W,borderTop:`1px solid ${BD}`,flexShrink:0}}, !allHere&&h('div',{style:{marginBottom:12,padding:'10px 14px',background:`${GOLD}18`,border:`1px solid ${GOLD}66`,borderRadius:8,fontSize:13,fontWeight:600,color:'#7A5000',lineHeight:1.5}}, `Absent: ${absent.map(p=>p.name).join(', ')}`, h('div',{style:{fontSize:11,marginTop:4,color:'#8A6000'}},'Their same-color teammate will cover their position in both groups.') ), h('button',{onClick:handleStart,style:{width:'100%',padding:'16px',background:allHere?BLUE:GOLD,border:'none',borderRadius:10,color:allHere?GOLD:BLUE,fontSize:17,fontWeight:800,cursor:'pointer'}}, allHere?'Next':`${absent.length} absent · Adjust lineup & Next` ) ) ); } // ── LOADING ── if(screen==='load'){ return h('div',{style:{height:'100%',display:'flex',flexDirection:'column',background:BG}},Hdr(null), h('div',{style:{flex:1,display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',gap:20}}, h('div',{style:{width:52,height:52,borderRadius:'50%',border:`3px solid ${BD}`,borderTopColor:BLUE,animation:'spin .85s linear infinite'}}), h('div',{style:{fontSize:20,fontWeight:700,color:TX}},'Adjusting lineup…') ) ); } // ── PLAYBOOK ── if(screen==='playbook'){ return h('div',{style:{height:'100%',display:'flex',flexDirection:'column',background:BG}}, Hdr(h('button',{onClick:()=>setScreen('play'),style:{width:44,height:44,borderRadius:'50%',background:'rgba(255,255,255,0.12)',border:'1.5px solid rgba(255,255,255,0.3)',color:'#fff',fontSize:22,cursor:'pointer',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}},'×')), h('div',{style:{padding:'12px 16px 10px',background:W,borderBottom:`1px solid ${BD}`,flexShrink:0}}, h('div',{style:{fontSize:20,fontWeight:800,color:TX,marginBottom:10}},'Playbook'), h('div',{style:{display:'flex',alignItems:'center',gap:10}}, h('span',{style:{fontSize:12,fontWeight:600,color:DM}},'Group:'), h('div',{style:{display:'flex',borderRadius:8,overflow:'hidden',border:`1px solid ${BD}`}}, ['A','B'].map(t=>h('button',{key:t,onClick:()=>setTeam(t),style:{padding:'6px 18px',background:team===t?BLUE:W,color:team===t?GOLD:DM,border:'none',fontSize:14,fontWeight:800,cursor:'pointer',transition:'all .1s'}},`Group ${t}`)) ) ) ), h('div',{style:{flex:1,overflowY:'auto',WebkitOverflowScrolling:'touch',paddingBottom:16}}, ...['MAIN','RUNS','TRICKS'].map(sec=>{ const sp=PLAYS.filter(p=>p.sec===sec),sc=SEC_LABELS[sec]; return h('div',{key:sec}, h('div',{style:{padding:'12px 16px 6px',display:'flex',alignItems:'center',gap:8}}, h('div',{style:{width:4,height:18,borderRadius:2,background:sc,flexShrink:0}}), h('span',{style:{fontSize:11,fontWeight:800,letterSpacing:3,color:sc,textTransform:'uppercase'}},sec), h('span',{style:{fontSize:11,color:DM,fontWeight:600}},`${sp.length} plays`) ), ...sp.map(play=>h('div',{key:play.id,onClick:()=>pickPlay(PLAYS.indexOf(play)),style:{display:'flex',alignItems:'center',gap:12,padding:'12px 16px',background:W,borderBottom:`1px solid ${BD}`,cursor:'pointer',borderLeft:`4px solid ${sc}`}}, h(MiniField,{play,size:72}), h('div',{style:{flex:1,minWidth:0}}, h('div',{style:{display:'flex',gap:6,marginBottom:4}}, play.tag&&h('span',{style:{fontSize:9,fontWeight:700,padding:'2px 6px',borderRadius:3,background:TAG_COL[play.tag]+'18',color:TAG_COL[play.tag],border:`1px solid ${TAG_COL[play.tag]}40`}},play.tag), h('span',{style:{fontSize:10,color:DM,fontWeight:600}},`${play.n} of ${play.of}`) ), h('div',{style:{fontSize:17,fontWeight:800,color:TX,lineHeight:1.1,marginBottom:5}},play.name), h('div',{style:{display:'flex',gap:4,flexWrap:'wrap',marginBottom:4}}, ['red','blue','green','orange','yellow'].map(c=>names[team][c]&&h('span',{key:c,style:{fontSize:10,fontWeight:700,color:COL[c].fill,background:COL[c].fill+'14',padding:'2px 6px',borderRadius:3}},names[team][c])) ), h('div',{style:{fontSize:11,color:DM,lineHeight:1.3,fontStyle:'italic'}},play.note) ), h('div',{style:{color:BD,fontSize:22,flexShrink:0}},'›') )) ); }) ) ); } // ── RULEBOOK ── if(screen==='rules'){ return h('div',{style:{height:'100%',display:'flex',flexDirection:'column',background:BG}}, Hdr(h('button',{onClick:()=>setScreen('play'),style:{width:44,height:44,borderRadius:'50%',background:'rgba(255,255,255,0.12)',border:'1.5px solid rgba(255,255,255,0.3)',color:'#fff',fontSize:22,cursor:'pointer',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}},'×')), h('div',{style:{padding:'12px 16px 8px',background:W,borderBottom:`1px solid ${BD}`,flexShrink:0}}, h('div',{style:{fontSize:20,fontWeight:800,color:TX,letterSpacing:-.3}},'8U Rulebook'), h('div',{style:{fontSize:12,color:DM,fontWeight:600,marginTop:2}},'NFL FLAG · Regular Season · Ages 7–8 · Tap a section to expand') ), h('div',{style:{flex:1,overflowY:'auto',WebkitOverflowScrolling:'touch',paddingBottom:24}}, ...RULE_SECTIONS.map(sec=>h('div',{key:sec.id}, h('div',{onClick:()=>setRuleOpen(ruleOpen===sec.id?null:sec.id),style:{display:'flex',alignItems:'center',gap:12,padding:'14px 16px',background:sec.bg,borderBottom:`1px solid ${sec.border}`,borderTop:`3px solid ${sec.color}`,cursor:'pointer',marginTop:10}}, h('div',{style:{flex:1}}, h('div',{style:{fontSize:12,fontWeight:800,color:sec.color,letterSpacing:1.5,textTransform:'uppercase'}},sec.label), h('div',{style:{fontSize:11,color:DM,marginTop:2}},`${sec.rules.length} rules`) ), h('div',{style:{fontSize:20,color:sec.color,fontWeight:700,transition:'transform .2s',transform:ruleOpen===sec.id?'rotate(90deg)':'rotate(0deg)'}},'›') ), ruleOpen===sec.id&&h('div',{style:{animation:'fadein .2s ease'}}, ...sec.rules.map((rule,ri)=>h('div',{key:ri,style:{padding:'14px 16px',background:W,borderBottom:`1px solid ${BD}`,borderLeft:`4px solid ${sec.color}`}}, h('div',{style:{fontSize:15,fontWeight:800,color:TX,marginBottom:5,lineHeight:1.2}},rule.title), h('div',{style:{fontSize:13,color:DM,lineHeight:1.55,fontWeight:500}},rule.body) )) ) )) ) ); } // ── PLAY SCREEN ── const rawPlay=PLAYS[idx]; const rawFm=FM[rawPlay.form]; const{play,fm}=flipped?applyFlip(rawPlay,rawFm):{play:rawPlay,fm:rawFm}; const allCols=['red','blue','green','orange','yellow']; const players=allCols.map(c=>{ const base=fm[c];if(!base)return null; const rt=play.routes?.[c]; const pos=(playing||prog>0)&&rt?gPos(rt,prog)||base:base; const nm=names[team][c]; return{c,pos,rt,base,name:nm,lbl:POS[c],empty:!nm}; }).filter(Boolean); const showSwapPrompt=selected!==null; const showSwapDone=swapMsg!==null&&selected===null; const ballPos=getBallPos(play,fm,prog); const DOT_R=7,POS_FS=3.6,NAME_FS=3.8; // Motion direction: flipped reverses the arrow const motDx=flipped?-26:26; const motLabel=flipped?'← MOTION':'→ MOTION'; return h('div',{style:{height:'100%',display:'flex',flexDirection:'column',background:'#0D2518',overflow:'hidden',position:'relative'}}, Hdr(h('button',{onClick:()=>setExitPrompt(true),style:{padding:'0 10px',height:34,borderRadius:6,background:'#C42020',border:'none',color:'#fff',fontSize:10,fontWeight:800,cursor:'pointer',letterSpacing:1,flexShrink:0}},'END GAME')), // A/B tabs + section tabs + RULES h('div',{style:{display:'flex',alignItems:'center',background:W,borderBottom:`1px solid ${BD}`,flexShrink:0}}, h('div',{style:{display:'flex',margin:'0 8px'}}, ['A','B'].map(t=>h('button',{key:t,onClick:()=>{setTeam(t);stopAnim();},style:{width:42,height:44,background:team===t?BLUE:BG,color:team===t?GOLD:DM,border:'none',fontSize:14,fontWeight:800,cursor:'pointer',letterSpacing:1,borderRadius:t==='A'?'8px 0 0 8px':'0 8px 8px 0',transition:'all .1s'}},t)) ), h('div',{style:{width:1,height:28,background:BD,flexShrink:0}}), ...['MAIN','RUNS','TRICKS'].map(s=>h('button',{key:s,onClick:()=>jumpSec(s),style:{flex:1,height:44,background:'transparent',border:'none',cursor:'pointer',borderBottom:`3px solid ${play.sec===s?SEC_LABELS[s]:'transparent'}`,color:play.sec===s?SEC_LABELS[s]:DM,fontSize:10,fontWeight:700,letterSpacing:1,transition:'all .1s'}},s)), h('div',{style:{width:1,height:28,background:BD,flexShrink:0}}), h('button',{onClick:()=>setScreen('rules'),style:{padding:'0 10px',height:44,background:BG,border:'none',cursor:'pointer',color:'#7C3AED',fontSize:10,fontWeight:800,letterSpacing:1,flexShrink:0}},'RULES') ), // Play info bar h('div',{style:{padding:'6px 14px 5px',background:W,borderBottom:`1px solid ${BD}`,flexShrink:0}}, h('div',{style:{display:'flex',alignItems:'center',justifyContent:'space-between'}}, h('div',{style:{display:'flex',alignItems:'center',gap:5}}, h('span',{style:{fontSize:9,fontWeight:700,letterSpacing:2,color:SEC_LABELS[play.sec],textTransform:'uppercase'}},play.sec), h('span',{style:{color:BD}},'·'), h('span',{style:{fontSize:9,color:'#9DAFC0'}},`${rawPlay.n}/${rawPlay.of}`), play.tag&&h('span',{style:{fontSize:8,fontWeight:700,padding:'1px 5px',borderRadius:3,background:TAG_COL[play.tag]+'18',color:TAG_COL[play.tag],border:`1px solid ${TAG_COL[play.tag]}40`}},play.tag), flipped&&h('span',{style:{fontSize:8,fontWeight:800,padding:'1px 6px',borderRadius:3,background:GOLD+'22',color:DGOLD,border:`1px solid ${GOLD}66`}},'↔ FLIPPED') ), namesModified&&h('button',{onClick:resetLineup,style:{padding:'4px 10px',borderRadius:6,background:GOLD,border:'none',color:DBLUE,fontSize:10,fontWeight:800,cursor:'pointer'}},'↺ Reset') ), h('div',{style:{fontSize:18,fontWeight:800,color:TX,letterSpacing:.1,lineHeight:1.1,marginTop:2}},play.name) ), lineNote&&!showSwapPrompt&&!showSwapDone&&h('div',{style:{padding:'5px 14px',background:`${GOLD}18`,borderBottom:`1px solid ${GOLD}55`,fontSize:11,fontWeight:600,color:'#7A5000',lineHeight:1.4,flexShrink:0}},`${lineNote}`), showSwapPrompt&&h('div',{style:{display:'flex',alignItems:'center',gap:10,padding:'8px 14px',background:`${GOLD}25`,borderBottom:`2px solid ${GOLD}`,flexShrink:0,animation:'fadein .15s ease'}}, h('div',{style:{width:32,height:32,borderRadius:'50%',background:COL[selected].fill,display:'flex',alignItems:'center',justifyContent:'center',color:COL[selected].txt,fontSize:11,fontWeight:800,flexShrink:0}},POS[selected]), h('div',{style:{flex:1}}, h('div',{style:{fontSize:14,fontWeight:800,color:TX,lineHeight:1}},names[team][selected]||'OPEN'), h('div',{style:{fontSize:10,color:DGOLD,fontWeight:700,marginTop:1}},'Tap another player to swap') ), h('button',{onClick:clearSel,style:{padding:'6px 12px',borderRadius:6,background:'rgba(0,0,0,0.08)',border:'none',color:TX,fontSize:11,fontWeight:700,cursor:'pointer'}},'Cancel') ), showSwapDone&&h('div',{style:{display:'flex',alignItems:'center',gap:10,padding:'8px 14px',background:'#DCFCE7',borderBottom:`2px solid #16A34A`,flexShrink:0,animation:'flashin .25s ease'}}, h('div',{style:{fontSize:13,fontWeight:800}},'SWAP'), h('div',{style:{flex:1,fontSize:13,fontWeight:700,color:'#14532D'}},`${swapMsg.n1} ↔ ${swapMsg.n2} swapped`), h('button',{onClick:()=>setSwapMsg(null),style:{background:'transparent',border:'none',color:'#16A34A',fontSize:20,cursor:'pointer',padding:'4px'}},'×') ), // ── FIELD SVG ── h('div',{style:{flex:1,background:'#0D2518',display:'flex',alignItems:'center',justifyContent:'center',overflow:'hidden',minHeight:0}}, h('svg',{viewBox:'0 0 100 90',preserveAspectRatio:'xMidYMid meet',style:{width:'100%',height:'100%',display:'block'}}, h('defs',null,...allCols.map(c=>h('marker',{key:`a-${c}`,id:`a-${c}`,markerWidth:4,markerHeight:4,refX:3.5,refY:2,orient:'auto',markerUnits:'strokeWidth'},h('path',{d:'M0,0 L0,4 L4,2 z',fill:COL[c].fill})))), h('rect',{width:100,height:90,fill:'#0D2518'}), ...[18,32,54,68,82].map(y=>h('line',{key:y,x1:0,y1:y,x2:100,y2:y,stroke:'#183D26',strokeWidth:.6})), h('rect',{x:0,y:0,width:100,height:7,fill:'rgba(0,53,148,0.22)'}), h('line',{x1:0,y1:54,x2:100,y2:54,stroke:'rgba(255,255,255,0.55)',strokeWidth:1,strokeDasharray:'3 2'}), h('text',{x:2,y:52.5,fill:'rgba(255,255,255,0.3)',fontSize:2.8,fontWeight:700,fontFamily:'system-ui'},'LOS'), ...[10,28,48,68,88].map(x=>h('text',{key:x,x,y:42,fill:'#243F2E',fontSize:5.5,textAnchor:'middle',fontWeight:700,fontFamily:'system-ui'},'×')), !playing&&prog===0&&!selected&&h('text',{x:50,y:87,fill:'rgba(255,255,255,0.18)',fontSize:3,textAnchor:'middle',fontFamily:'system-ui',fontWeight:600},'Tap player · Tap another to swap'), // Motion arrow (flip-aware) rawPlay.motion&&prog===0&&h('g',{opacity:.6}, h('path',{d:`M${fm[rawPlay.motion][0]},${fm[rawPlay.motion][1]} L${fm[rawPlay.motion][0]+motDx},${fm[rawPlay.motion][1]}`,fill:'none',stroke:COL[rawPlay.motion].fill,strokeWidth:.9,strokeDasharray:'2 1.5',markerEnd:`url(#a-${rawPlay.motion})`}), h('text',{x:fm[rawPlay.motion][0]+(motDx/2),y:fm[rawPlay.motion][1]-3.5,fill:COL[rawPlay.motion].fill,fontSize:3,textAnchor:'middle',fontFamily:'system-ui',fontWeight:700},motLabel) ), // Fake indicator (flip-aware) (play.fakeRight||play.fakeLeft)&&prog>0&&h('g',{opacity:Math.min(prog*2.5,.65)}, h('path',{d:play.fakeLeft?'M50,70 L18,48':'M50,70 L82,48',fill:'none',stroke:'#C42020',strokeWidth:.9,strokeDasharray:'2 1.5'}), h('text',{x:play.fakeLeft?16:80,y:46,fill:'#C42020',fontSize:3,fontFamily:'system-ui',fontWeight:700},play.fakeLeft?'← FAKE':'FAKE →') ), // Routes ...players.map(({c,rt})=>{const pts=rPts(rt,prog);if(!pts)return null;const ip=c===play.primary;return h('polyline',{key:'rt'+c,points:pts,fill:'none',stroke:COL[c].fill,strokeWidth:ip?2.5:1.5,strokeDasharray:ip?'none':'3 2',strokeOpacity:ip?1:.8,strokeLinecap:'round',strokeLinejoin:'round',markerEnd:`url(#a-${c})`});}), // Throw line play.throwL&&prog>0.45&&h('line',{x1:play.throwL.fx,y1:play.throwL.fy,x2:play.throwL.tx,y2:play.throwL.ty,stroke:'#fff',strokeWidth:1.8,strokeDasharray:'2.5 1.5',strokeOpacity:Math.min((prog-.45)*2,.9),markerEnd:'url(#a-red)'}), // Lateral play.lateral&&prog>0.45&&h('line',{x1:play.lateral.fx,y1:play.lateral.fy,x2:play.lateral.tx,y2:play.lateral.ty,stroke:'rgba(255,255,255,0.7)',strokeWidth:1.5,strokeDasharray:'2 1.5',strokeOpacity:Math.min((prog-.45)*2,.8)}), // Handoff line play.handoff&&prog>0&&prog<.4&&h('g',null, h('line',{x1:play.handoff.fx,y1:play.handoff.fy,x2:play.handoff.tx,y2:play.handoff.ty,stroke:'rgba(255,255,255,0.55)',strokeWidth:.7,strokeDasharray:'1.5 1',strokeOpacity:1-prog/.4}), prog>.05&&h('text',{x:(play.handoff.fx+play.handoff.tx)/2,y:(play.handoff.fy+play.handoff.ty)/2-2.5,fill:'rgba(255,255,255,0.45)',fontSize:2.8,textAnchor:'middle',fontFamily:'system-ui',fontWeight:700},'HO') ), // Football ballPos&&h(Football,{key:'ball',x:ballPos[0],y:ballPos[1]}), // Players ...players.map(({c,pos,base,name,lbl,empty})=>{ const[px,py]=pos||base; const ip=c===play.primary,isSel=selected===c; const r=DOT_R*(isSel?1.1:(ip&&prog>.86?1.08:1)); return h('g',{key:'pl'+c,onClick:()=>handlePlayerTap(c),style:{cursor:'pointer'}}, isSel&&h('circle',{cx:px,cy:py,r:r+5,fill:'none',stroke:GOLD,strokeWidth:2.5,style:{animation:'pulse .7s ease-in-out infinite'}}), ip&&prog>.88&&h('circle',{cx:px,cy:py,r:11,fill:COL[c].fill,opacity:.18}), h('circle',{cx:px,cy:py,r,fill:empty?'none':COL[c].fill,stroke:isSel?GOLD:empty?COL[c].fill:'rgba(255,255,255,0.9)',strokeWidth:isSel?2.5:empty?1.2:1,strokeDasharray:empty?'2 1.5':'none',opacity:empty?.5:1}), h('text',{x:px,y:py+1.4,fill:empty?COL[c].fill:'rgba(255,255,255,0.97)',fontSize:POS_FS,fontWeight:800,textAnchor:'middle',fontFamily:'system-ui',style:{pointerEvents:'none'},opacity:empty?.5:1},lbl), (!playing||prog>.88)&&( empty ?h('g',{key:'sub'+c}, h('rect',{x:px-8,y:py+r+1,width:16,height:7.5,rx:2,fill:'rgba(200,30,30,0.28)'}), h('text',{x:px,y:py+r+7.2,fill:'#FF2222',fontSize:5,fontWeight:900,textAnchor:'middle',fontFamily:'system-ui',style:{pointerEvents:'none'}},'SUB') ) :h('text',{x:px,y:py+r+6.5,fill:isSel?GOLD:COL[c].fill,fontSize:NAME_FS,fontWeight:isSel?800:700,textAnchor:'middle',fontFamily:'system-ui',opacity:.95,style:{pointerEvents:'none'}},name) ) ); }) ) ), // Coach note h('div',{style:{padding:'5px 14px',background:W,borderTop:`1px solid ${BD}`,fontSize:12,fontWeight:600,color:DM,lineHeight:1.35,fontStyle:'italic',flexShrink:0}},play.note), // ── BOTTOM CONTROLS ── h('div',{style:{display:'flex',alignItems:'center',gap:5,padding:'8px 8px',background:W,borderTop:`1px solid ${BD}`,flexShrink:0}}, // Prev h('button',{onClick:()=>nav(-1),disabled:idx===0,style:{width:40,height:44,borderRadius:'50%',background:'transparent',border:`1.5px solid ${BD}`,color:idx===0?BD:DM,fontSize:22,cursor:idx===0?'default':'pointer',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}},'‹'), // Reset anim h('button',{onClick:stopAnim,style:{padding:'0 8px',height:44,borderRadius:8,background:'#E8F0F8',border:`2px solid #C0D0E4`,color:'#3A5A80',fontSize:14,fontWeight:800,cursor:'pointer',flexShrink:0}},'↺'), // FLIP h('button',{onClick:()=>{stopAnim();setFlipped(f=>!f);},style:{padding:'0 8px',height:44,borderRadius:8,background:flipped?GOLD:'#E8F0F8',border:`2px solid ${flipped?DGOLD:'#C0D0E4'}`,color:flipped?DBLUE:'#3A5A80',fontSize:11,fontWeight:800,cursor:'pointer',flexShrink:0,letterSpacing:.5}},'↔ FLIP'), // RUN / STOP h('button',{onClick:startStop,style:{flex:1,height:48,borderRadius:10,background:playing?DBLUE:BLUE,border:'none',color:playing?'rgba(255,255,255,0.85)':GOLD,fontSize:15,fontWeight:800,cursor:'pointer',display:'flex',alignItems:'center',justifyContent:'center',gap:6,transition:'all .1s'}}, h('span',{style:{fontSize:18}},playing?'■':'▶'),playing?'STOP':'RUN PLAY' ), // PLAYS h('button',{onClick:()=>{stopAnim();setScreen('playbook');},style:{padding:'0 8px',height:44,borderRadius:8,background:BG,border:`1.5px solid ${BD}`,color:TX,fontSize:10,fontWeight:800,cursor:'pointer',flexShrink:0}},'PLAYS'), // Next h('button',{onClick:()=>nav(1),disabled:idx===PLAYS.length-1,style:{width:40,height:44,borderRadius:'50%',background:'transparent',border:`1.5px solid ${BD}`,color:idx===PLAYS.length-1?BD:DM,fontSize:22,cursor:idx===PLAYS.length-1?'default':'pointer',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}},'›') ), // Exit prompt exitPrompt&&h('div',{style:{position:'absolute',top:0,left:0,right:0,bottom:0,background:'rgba(0,0,0,0.75)',display:'flex',alignItems:'center',justifyContent:'center',padding:28,zIndex:200}}, h('div',{style:{background:W,borderRadius:16,padding:'28px 24px',width:'100%',maxWidth:340,animation:'flashin .2s ease'}}, h('div',{style:{fontSize:24,fontWeight:800,color:TX,marginBottom:8}},'End Game?'), h('div',{style:{fontSize:14,color:DM,fontWeight:500,lineHeight:1.5,marginBottom:24}},'This will go back to the attendance screen. Any lineup swaps will be cleared.'), h('div',{style:{display:'flex',gap:12}}, h('button',{onClick:()=>setExitPrompt(false),style:{flex:1,padding:'14px',borderRadius:10,background:BG,border:`1.5px solid ${BD}`,color:DM,fontSize:15,fontWeight:700,cursor:'pointer'}},'Cancel'), h('button',{onClick:()=>{setExitPrompt(false);stopAnim();setFlipped(false);setNames(DEF);setDefaultNames(DEF);setLineNote('');setScreen('att');},style:{flex:1,padding:'14px',borderRadius:10,background:'#C42020',border:'none',color:'#fff',fontSize:15,fontWeight:800,cursor:'pointer'}},'End Game') ) ) ) ); } createRoot(document.getElementById('root')).render(h(App)); {id:'r2',sec:'RUNS',n:2,of:7,name:'Middle Run',tag:'RUN',form:'std', routes:{orange:[[10,54],[10,10]],blue:[[32,54],[46,63],[48,14]],green:null,yellow:[[70,54],[90,26]],red:[[50,70],[40,62]]}, handoff:{fx:50,fy:70,tx:46,ty:63},primary:'blue', note:'Blue drops back, takes handoff, hits the gap between C and TE. Straight upfield.'}, {id:'r3',sec:'RUNS',n:3,of:7,name:'WR Sweep',tag:'RUN',form:'std', routes:{orange:[[10,54],[44,38],[90,16]],blue:[[32,54],[10,28]],green:null,yellow:[[70,54],[88,28]],red:[[50,70],[36,60]]}, handoff:{fx:50,fy:70,tx:20,ty:54},primary:'orange', note:'Handoff to Orange who cuts across right. Blue decoys hard left.'}, {id:'r4',sec:'RUNS',n:4,of:7,name:'Draw Play',tag:'RUN',form:'std', routes:{orange:[[10,54],[10,10]],blue:[[32,54],[50,63],[50,14]],green:null,yellow:[[70,54],[90,23]],red:[[50,70],[44,66]]}, handoff:{fx:50,fy:70,tx:50,ty:63},primary:'blue', note:'QB fakes a pass look for 2 counts. Blue delays then takes the handoff up the middle.'}, {id:'r5',sec:'RUNS',n:5,of:7,name:'Pitch Out',tag:'RUN',form:'std', routes:{orange:[[10,54],[8,30]],blue:[[32,54],[68,42],[90,20]],green:null,yellow:[[70,54],[86,24]],red:[[50,70],[46,60]]}, handoff:{fx:50,fy:70,tx:46,ty:60},primary:'blue', note:'QB pitches immediately to Blue going right. No hesitation — pitch and release.'}, {id:'r6',sec:'RUNS',n:6,of:7,name:'Counter',tag:'RUN',form:'std', routes:{orange:[[10,54],[20,40]],blue:[[32,54],[18,44],[58,20]],green:null,yellow:[[70,54],[64,42]],red:[[50,70],[38,60]]}, handoff:{fx:50,fy:70,tx:38,ty:60},primary:'blue', note:'Blue fakes left, cuts hard back right. Sell the fake.'}, {id:'r7',sec:'RUNS',n:7,of:7,name:'Misdirection',tag:'RUN',form:'std', routes:{orange:[[10,54],[50,52],[90,22]],blue:[[32,54],[14,44],[4,26]],green:null,yellow:[[70,54],[86,28]],red:[[50,70],[42,64]]}, handoff:{fx:50,fy:70,tx:34,ty:58},primary:'blue', note:'Orange sweeps hard RIGHT — defenders follow the flow. QB hands to Blue cutting hard LEFT into the vacated gap. Key: wait one beat after the snap for the defense to commit right, then hit Blue going left.'}, // ── TRICKS ── {id:'t1',sec:'TRICKS',n:1,of:5,name:'Jet Sweep',tag:'TRICK',form:'std',motion:'orange', routes:{orange:[[10,54],[46,54],[90,20]],blue:[[32,54],[10,28]],green:null,yellow:[[70,54],[88,28]],red:[[50,70],[44,62]]}, handoff:{fx:50,fy:70,tx:44,ty:54},primary:'orange', note:'Orange in motion at snap — already at full speed. QB gives immediate handoff. Blue decoys.'}, {id:'t2',sec:'TRICKS',n:2,of:5,name:'RB Option Pass',tag:'TRICK',form:'std', routes:{orange:[[10,54],[10,10]],blue:[[32,54],[62,50],[82,32]],green:null,yellow:[[70,54],[88,28]],red:[[50,70],[38,62]]}, handoff:{fx:50,fy:70,tx:38,ty:62},throwL:{fx:38,fy:62,tx:10,ty:10},primary:'blue', note:'QB hands to Blue. Orange open deep? THROW. Nobody open? RUN. Any handoff receiver can pass.'}, {id:'t3',sec:'TRICKS',n:3,of:5,name:'Statue of Liberty',tag:'TRICK',form:'t3',fakeRight:true, routes:{orange:[[10,54],[10,10]],blue:[[32,70],[14,58],[4,30]],green:null,yellow:[[70,54],[88,28]],red:null}, handoff:{fx:50,fy:70,tx:32,ty:70},primary:'blue', note:'QB arm cocked RIGHT (fake). Blue sneaks behind QB and goes LEFT. Defense watches QB.'}, {id:'t4',sec:'TRICKS',n:4,of:5,name:'WR Throwback',tag:'TRICK',form:'std', routes:{orange:[[10,54],[55,44],[80,36]],blue:[[32,54],[32,13]],green:null,yellow:[[70,54],[72,13]],red:[[50,70],[26,58],[10,42]]}, handoff:{fx:50,fy:70,tx:30,ty:54},throwL:{fx:80,fy:36,tx:10,ty:42},primary:'red', note:'QB hands to Orange sweeping right. QB sneaks left. Orange stops and throws back to QB.'}, {id:'t5',sec:'TRICKS',n:5,of:5,name:'Double Reverse',tag:'TRICK',form:'std', routes:{orange:[[10,54],[58,44],[8,20]],blue:[[32,54],[62,44]],green:null,yellow:[[70,54],[74,16]],red:[[50,70],[42,62]]}, handoff:{fx:50,fy:70,tx:42,ty:62},lateral:{fx:62,fy:44,tx:30,ty:44},primary:'orange', note:'QB to Blue going right. Blue laterals to Orange going left. Deep routes clear the way.'}, ]; const SEC_LABELS={MAIN:BLUE,RUNS:DGOLD,TRICKS:'#7C3AED'}; const TAG_COL={TRANSITION:BLUE,RUN:DGOLD,TRICK:'#7C3AED'}; const RULE_SECTIONS=[ {id:'adv',label:'USE TO YOUR ADVANTAGE',color:'#16A34A',bg:'#F0FDF4',border:'#86EFAC',rules:[ {title:'No rush on QB — ever',body:'In 8U regular season, defenders CANNOT rush the passer unless a legal handoff has already been executed in the backfield. Braxton and Christopher have zero pass rush on every dropback. The only clock is 7 seconds. Tell them to relax and find the open receiver.'}, {title:'Coach on field pre-snap',body:'One coach is allowed on the field before every snap to position players. You MUST step off before the ball is snapped. Use this every single play — walk out, point each kid to their spot, step back, snap.'}, {title:'Run anywhere on the field',body:'No run zones are eliminated for 8U. You can run on any down, from any yard line, including inside the 5-yard line. Sweeps and draws are legal everywhere.'}, {title:'Any handoff receiver can throw',body:'After receiving a direct handoff behind the LOS, any player (RB, WR, TE) can choose to pass instead of run. This is what makes the RB Option Pass and WR Throwback legal.'}, {title:'Interceptions are returnable for 6',body:'If you intercept a pass, your player can run it back for a touchdown worth 6 points. Make sure all defenders know this — after a pick, they sprint.'}, {title:'Pre-snap motion is legal',body:'One offensive player can be in motion before the snap. Motion must be parallel to or moving away from the LOS — not moving forward. Use motion (Jet Sweep) to confuse the defense before the snap.'}, {title:'Formation shifts are allowed',body:'You can shift the whole formation before the snap. All players just need to reset and hold for 1 full second before the snap. Stack formations and overload looks are legal.'}, ]}, {id:'know',label:'KEY RULES TO KNOW',color:BLUE,bg:'#EFF6FF',border:'#93C5FD',rules:[ {title:'QB cannot run directly across LOS',body:'The QB — whoever catches the snap — cannot run straight across the line of scrimmage. They must fake a pass look first ("scramble"). A straight keeper with no fake is a penalty. Applies to both Braxton and Christopher.'}, {title:'7-second pass clock',body:'The QB has 7 seconds from the snap to release the ball or cross the LOS. After 7 seconds, play is dead where the QB stands. Defenders count it out loud — coach your QB to be aware.'}, {title:'4 downs to cross midfield, then 3 to score',body:'Offense gets 4 downs to cross the midfield line. Once past midfield, they get 3 downs to score. If they fail, the other team gets the ball at their own 5-yard line.'}, {title:'40-second play clock',body:'40 seconds between plays. The clock starts when the referee spots the ball. Keep plays moving — do not let kids dawdle between downs.'}, {title:'Handoffs must happen behind LOS',body:'All direct handoffs, pitches, and laterals must occur behind the line of scrimmage. A handoff at or beyond the LOS is illegal. All run plays must exchange the ball in the backfield.'}, {title:'Center sneak is not allowed',body:'The center (Green player) cannot receive the ball and run. The center snaps and is done as a ball carrier. Do not run plays that try to use the center as a runner.'}, {title:'Only one player in motion pre-snap',body:'Only one offensive player may be moving at the snap. That player must move parallel to or away from the LOS. Forward motion before the snap is an illegal motion penalty.'}, {title:'Players must reset 1 second after a shift',body:'If players change formation positions, everyone must stop and hold for a full second before snapping. Rush the snap after a shift and it is a false start penalty.'}, ]}, {id:'score',label:'SCORING',color:'#B45309',bg:'#FFFBEB',border:'#FCD34D',rules:[ {title:'Touchdown — 6 points',body:'Ball carrier crosses the goal line with possession. Count it when the player breaks the plane of the end zone.'}, {title:'Extra point (1 pt) — snap from 5 yards',body:'After a TD, offense attempts a 1-point conversion from the 5-yard line. One play, one shot.'}, {title:'Extra point (2 pts) — snap from 10 yards',body:'A harder conversion from the 10-yard line worth 2 points. Consider this when you need two scores to catch up.'}, {title:'Safety — 2 points',body:'If the ball carrier\'s flag is pulled in their own end zone, the defense scores 2 points. Avoid run plays that start near your own goal line.'}, {title:'Interception returned for TD — 6 points',body:'On a change of possession via interception, the new ball carrier can score immediately. Defenders should always know to run after a pick.'}, ]}, {id:'avoid',label:'PENALTIES TO AVOID',color:'#DC2626',bg:'#FEF2F2',border:'#FCA5A5',rules:[ {title:'Flag guarding',body:'The ball carrier cannot guard their flag by stiff-arming, covering it with their hand or arm, or diving to protect it. This is a spot foul — play ends where the guard occurred. Teach runners to keep arms up and pump naturally.'}, {title:'Illegal contact / no blocking',body:'Flag football has no blocking. Offensive players cannot use their body to impede defenders. Players run routes and decoy only — no physical contact. Teach kids to run routes, not set picks.'}, {title:'Pass interference',body:'Defenders cannot grab, hold, or push a receiver going for the ball. Offense cannot push off on routes either. Both sides are penalized. Teach secondary kids to mirror hips, not grab.'}, {title:'False start',body:'Any offensive player who moves before the snap (other than the one legal motion player) causes a false start. 5-yard penalty, replay the down. Emphasize staying frozen until the snap count.'}, {title:'Illegal forward pass',body:'The QB can only throw one forward pass per play, thrown from behind the LOS. A second forward pass on the same play is illegal. The RB Option Pass throw must always come from behind the LOS.'}, {title:'Roughing / unsportsmanlike conduct',body:'Excessive contact, taunting, or rough play results in a penalty and can mean a yellow card warning or ejection. Keep kids calm and focused — especially after a bad call.'}, ]}, ]; // ── HELPERS ── const lerp=(a,b,t)=>a+(b-a)*t; function gPos(rt,p){ if(!rt||rt.length<2)return null; const n=rt.length-1,g=Math.min(p,.9999)*n,s=Math.floor(g),l=g-s; return[lerp(rt[s][0],rt[Math.min(s+1,n)][0],l),lerp(rt[s][1],rt[Math.min(s+1,n)][1],l)]; } function rPts(rt,p){ if(!rt||rt.length<2||p===0)return''; const n=rt.length-1,g=p*n,s=Math.floor(g),l=g-s; const pts=[...rt.slice(0,s+1)]; if(sq.join(',')).join(' '); } function getBallPos(play,fm,prog){ const qb=fm['red'],ctr=fm['green']; if(!qb)return null; if(prog===0){return ctr?[lerp(ctr[0],qb[0],.45),lerp(ctr[1],qb[1],.45)]:qb;} if(play.handoff&&prog<.4){const t=prog/.4;return[lerp(qb[0],play.handoff.tx,t),lerp(qb[1],play.handoff.ty,t)];} const pRt=play.routes?.[play.primary],pBase=fm[play.primary]; if(pRt&&pBase){return gPos(pRt,prog)||pBase;} if(play.throwL&&prog>0.45){const t=(prog-.45)/.55;return[lerp(play.throwL.fx,play.throwL.tx,t),lerp(play.throwL.fy,play.throwL.ty,t)];} return pBase||qb; } // Flip a play + formation horizontally (mirror across x=50) function applyFlip(play, fm){ const fx=x=>100-x; const flipPt=([x,y])=>[fx(x),y]; const flipRt=rt=>rt?rt.map(flipPt):null; const newFm=Object.fromEntries(Object.entries(fm).map(([k,v])=>[k,v?flipPt(v):v])); const newRoutes=play.routes?Object.fromEntries(Object.entries(play.routes).map(([k,v])=>[k,flipRt(v)])):null; return{ fm:newFm, play:{ ...play, routes:newRoutes, handoff:play.handoff?{fx:fx(play.handoff.fx),fy:play.handoff.fy,tx:fx(play.handoff.tx),ty:play.handoff.ty}:null, throwL:play.throwL?{fx:fx(play.throwL.fx),fy:play.throwL.fy,tx:fx(play.throwL.tx),ty:play.throwL.ty}:null, lateral:play.lateral?{fx:fx(play.lateral.fx),fy:play.lateral.fy,tx:fx(play.lateral.tx),ty:play.lateral.ty}:null, fakeRight:play.fakeRight?false:undefined, fakeLeft:play.fakeRight?true:undefined, } }; } function Football({x,y}){ return h('g',{transform:`translate(${x},${y}) rotate(-22)`,style:{pointerEvents:'none'}}, h('ellipse',{rx:5,ry:2.9,fill:'#6B3A1F',stroke:'rgba(255,255,255,0.85)',strokeWidth:.7}), h('line',{x1:-2.5,y1:0,x2:2.5,y2:0,stroke:'rgba(255,255,255,0.8)',strokeWidth:.5}), h('line',{x1:-1.2,y1:-1.4,x2:-1.2,y2:1.4,stroke:'rgba(255,255,255,0.55)',strokeWidth:.38}), h('line',{x1:0,y1:-1.6,x2:0,y2:1.6,stroke:'rgba(255,255,255,0.55)',strokeWidth:.38}), h('line',{x1:1.2,y1:-1.4,x2:1.2,y2:1.4,stroke:'rgba(255,255,255,0.55)',strokeWidth:.38}) ); } function MiniField({play,size=76}){ const fm=FM[play.form],allC=['red','blue','green','orange','yellow']; return h('svg',{viewBox:'0 0 100 90',width:size,height:size*.9,style:{borderRadius:6,flexShrink:0},preserveAspectRatio:'xMidYMid meet'}, h('rect',{width:100,height:90,fill:'#0D2518'}), h('line',{x1:0,y1:54,x2:100,y2:54,stroke:'rgba(255,255,255,0.4)',strokeWidth:1,strokeDasharray:'3 2'}), h('defs',null,...allC.map(c=>h('marker',{key:`mma-${c}`,id:`mma-${c}`,markerWidth:4,markerHeight:4,refX:3.5,refY:2,orient:'auto',markerUnits:'strokeWidth'},h('path',{d:'M0,0 L0,4 L4,2 z',fill:COL[c].fill})))), ...allC.map(c=>{const rt=play.routes?.[c];if(!rt||rt.length<2)return null;const ip=c===play.primary;return h('polyline',{key:'m'+c,points:rt.map(q=>q.join(',')).join(' '),fill:'none',stroke:COL[c].fill,strokeWidth:ip?2.2:1.3,strokeDasharray:ip?'none':'3 2',strokeOpacity:.95,strokeLinecap:'round',strokeLinejoin:'round',markerEnd:`url(#mma-${c})`});}), ...allC.map(c=>{const base=fm[c];if(!base)return null;const[px,py]=base;return h('circle',{key:'d'+c,cx:px,cy:py,r:5,fill:COL[c].fill,stroke:'white',strokeWidth:.8});}) ); } // ── APP ── function App(){ const[screen,setScreen]=useState('splash'); const[present,setPresent]=useState(Object.fromEntries(ROSTER.map(p=>[p.key,true]))); const[names,setNames]=useState(DEF); const[defaultNames,setDefaultNames]=useState(DEF); const[lineNote,setLineNote]=useState(''); const[idx,setIdx]=useState(0); const[team,setTeam]=useState('A'); const[prog,setProg]=useState(0); const[playing,setPlaying]=useState(false); const[selected,setSelected]=useState(null); const[swapMsg,setSwapMsg]=useState(null); const[exitPrompt,setExitPrompt]=useState(false); const[ruleOpen,setRuleOpen]=useState(null); const[flipped,setFlipped]=useState(false); const swapTimer=useRef(null); const raf=useRef(null),t0=useRef(null); const absent=ROSTER.filter(p=>!present[p.key]); const W='#fff',BG='#F0F4F8',BD='#E2EAF2',TX='#0C1B2A',DM='#5A7A96'; useEffect(()=>{ if(screen==='splash'){const t=setTimeout(()=>setScreen('att'),3600);return()=>clearTimeout(t);} },[screen]); const namesModified=['red','blue','green','orange','yellow'].some(c=>names[team][c]!==defaultNames[team][c]); function toggle(key){setPresent(p=>({...p,[key]:!p[key]}));} function clearSel(){setSelected(null);clearTimeout(swapTimer.current);} function stopAnim(){cancelAnimationFrame(raf.current);setPlaying(false);setProg(0);clearSel();t0.current=null;} function handlePlayerTap(color){ if(playing)return; if(selected===null){setSelected(color);setSwapMsg(null);} else if(selected===color){clearSel();} else{ const n1=names[team][selected],n2=names[team][color]; setNames(prev=>({...prev,[team]:{...prev[team],[selected]:n2||null,[color]:n1||null}})); clearSel(); setSwapMsg({n1:n1||'OPEN',n2:n2||'OPEN'}); clearTimeout(swapTimer.current); swapTimer.current=setTimeout(()=>setSwapMsg(null),2800); } } function resetLineup(){setNames(prev=>({...prev,[team]:{...defaultNames[team]}}));clearSel();setSwapMsg(null);} function startStop(){ if(playing){stopAnim();return;} clearSel();setSwapMsg(null);setProg(0);setPlaying(true);t0.current=null; const tick=ts=>{if(!t0.current)t0.current=ts;const p=Math.min((ts-t0.current)/2400,1);setProg(p);if(p<1)raf.current=requestAnimationFrame(tick);else setPlaying(false);}; raf.current=requestAnimationFrame(tick); } function jumpSec(s){stopAnim();const i=PLAYS.findIndex(p=>p.sec===s);if(i>=0)setIdx(i);} function nav(d){stopAnim();setIdx(i=>Math.max(0,Math.min(PLAYS.length-1,i+d)));} function pickPlay(i){stopAnim();setIdx(i);setScreen('play');} async function handleStart(){ if(absent.length===0){setScreen('play');return;} setScreen('load'); const pres=ROSTER.filter(p=>present[p.key]); const mk=(t)=>{ const other=t==='A'?'B':'A'; const res={red:null,blue:null,green:null,orange:null,yellow:null}; pres.filter(p=>p.team===t).forEach(p=>{res[p.c]=p.name;}); for(const c of Object.keys(res)){ if(!res[c]){const match=pres.find(p=>p.team===other&&p.c===c);if(match)res[c]=match.name;} } const subs=pres.filter(p=>p.sub&&!Object.values(res).includes(p.name)); let si=0; for(const c of Object.keys(res)){if(!res[c]&&sip.name).join(', ')} absent — lineup adjusted`); setTimeout(()=>setScreen('play'),500); } const Hdr=(right)=>h('div',{style:{background:BLUE,borderBottom:`2.5px solid ${GOLD}`,padding:'0 14px',height:54,display:'flex',alignItems:'center',gap:12,flexShrink:0}}, h('img',{src:LOGO,style:{width:40,height:30,objectFit:'contain',flexShrink:0}}), h('div',{style:{flex:1,minWidth:0}}, h('div',{style:{fontSize:11,fontWeight:800,color:GOLD,letterSpacing:1.5,lineHeight:1,textTransform:'uppercase'}},'LA RAMS · Flag Football'), h('div',{style:{fontSize:9,color:'rgba(255,255,255,0.55)',fontWeight:600,marginTop:1}},'Coach Dorr · Asst. Coach Welch · Ages 7–8') ), right&&right ); // ── SPLASH ── if(screen==='splash'){ return h('div',{style:{height:'100%',background:BLUE,display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',padding:'0 40px'}}, h('img',{src:LOGO,style:{width:150,objectFit:'contain',marginBottom:32,animation:'splashin .7s ease'}}), h('div',{style:{fontSize:28,fontWeight:900,color:GOLD,letterSpacing:3,textTransform:'uppercase',animation:'splashin .9s ease',lineHeight:1}},'LA RAMS'), h('div',{style:{fontSize:13,fontWeight:700,color:'rgba(255,255,255,0.5)',letterSpacing:5,marginTop:8,textTransform:'uppercase',animation:'splashin 1.1s ease'}},'Flag Football'), h('div',{style:{marginTop:36,textAlign:'center',animation:'splashin 1.3s ease'}}, h('div',{style:{fontSize:14,fontWeight:700,color:'rgba(255,255,255,0.65)',letterSpacing:.5}},'Coach Dorr · Asst. Coach Welch'), h('div',{style:{marginTop:36,fontSize:11,color:GOLD,fontWeight:700,letterSpacing:.5}},'Developed by Jason Welch') ) ); } // ── ATTENDANCE ── if(screen==='att'){ const tA=ROSTER.filter(p=>p.team==='A'),tB=ROSTER.filter(p=>p.team==='B'),allHere=absent.length===0; const Row=p=>{const here=present[p.key];return h('div',{key:p.key,onClick:()=>toggle(p.key),style:{display:'flex',alignItems:'center',gap:14,padding:'13px 16px',background:here?W:'#F7FAFC',borderBottom:`1px solid ${BD}`,cursor:'pointer',minHeight:64}}, h('div',{style:{width:46,height:46,borderRadius:'50%',background:here?COL[p.c].fill:'#C8D4DF',display:'flex',alignItems:'center',justifyContent:'center',color:here?COL[p.c].txt:'#8FA5BC',fontSize:11,fontWeight:800,flexShrink:0}},POS[p.c]), h('div',{style:{flex:1}}, h('div',{style:{fontSize:17,fontWeight:700,color:here?TX:'#9DAFC0',textDecoration:here?'none':'line-through',lineHeight:1.2}},p.name+(p.sub?' ★':'')), h('div',{style:{display:'flex',gap:5,marginTop:4}}, h('span',{style:{fontSize:10,fontWeight:700,padding:'3px 7px',borderRadius:4,background:BLUE+'18',color:BLUE}},`TEAM ${p.team}`), h('span',{style:{fontSize:10,fontWeight:700,padding:'3px 7px',borderRadius:4,background:'#EDF2F7',color:DM}},POS[p.c]), p.sub&&h('span',{style:{fontSize:10,fontWeight:700,padding:'3px 7px',borderRadius:4,background:GOLD+'22',color:DGOLD}},'SUB') ) ), h('div',{style:{width:36,height:36,borderRadius:'50%',background:here?'#DCFCE7':'#FEE2E2',display:'flex',alignItems:'center',justifyContent:'center',fontSize:18,color:here?'#16A34A':'#DC2626',fontWeight:700,flexShrink:0}},here?'✓':'✗') );}; return h('div',{style:{height:'100%',display:'flex',flexDirection:'column',background:BG}}, Hdr(null), h('div',{style:{padding:'14px 16px 10px',background:W,borderBottom:`1px solid ${BD}`,display:'flex',alignItems:'baseline',justifyContent:'space-between',flexShrink:0}}, h('div',{style:{fontSize:22,fontWeight:800,color:TX,letterSpacing:-.5}},"Who's here?"), h('div',{style:{fontSize:14,fontWeight:700,color:absent.length===0?'#16A34A':'#C45500'}},[`${ROSTER.filter(p=>present[p.key]).length} / ${ROSTER.length}`]) ), h('div',{style:{flex:1,overflowY:'auto',WebkitOverflowScrolling:'touch'}}, h('div',{style:{padding:'8px 16px 5px',background:BLUE+'0A',borderBottom:`1px solid ${BD}`}},h('span',{style:{fontSize:9,fontWeight:800,letterSpacing:3,color:BLUE}},'GROUP A')), ...tA.map(Row), h('div',{style:{padding:'8px 16px 5px',background:BLUE+'0A',borderBottom:`1px solid ${BD}`,marginTop:6}},h('span',{style:{fontSize:9,fontWeight:800,letterSpacing:3,color:BLUE}},'GROUP B')), ...tB.map(Row) ), h('div',{style:{padding:'14px 16px',background:W,borderTop:`1px solid ${BD}`,flexShrink:0}}, !allHere&&h('div',{style:{marginBottom:12,padding:'10px 14px',background:`${GOLD}18`,border:`1px solid ${GOLD}66`,borderRadius:8,fontSize:13,fontWeight:600,color:'#7A5000',lineHeight:1.5}}, `Absent: ${absent.map(p=>p.name).join(', ')}`, h('div',{style:{fontSize:11,marginTop:4,color:'#8A6000'}},'Their same-color teammate will cover their position in both groups.') ), h('button',{onClick:handleStart,style:{width:'100%',padding:'16px',background:allHere?BLUE:GOLD,border:'none',borderRadius:10,color:allHere?GOLD:BLUE,fontSize:17,fontWeight:800,cursor:'pointer'}}, allHere?'Next':`${absent.length} absent · Adjust lineup & Next` ) ) ); } // ── LOADING ── if(screen==='load'){ return h('div',{style:{height:'100%',display:'flex',flexDirection:'column',background:BG}},Hdr(null), h('div',{style:{flex:1,display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',gap:20}}, h('div',{style:{width:52,height:52,borderRadius:'50%',border:`3px solid ${BD}`,borderTopColor:BLUE,animation:'spin .85s linear infinite'}}), h('div',{style:{fontSize:20,fontWeight:700,color:TX}},'Adjusting lineup…') ) ); } // ── PLAYBOOK ── if(screen==='playbook'){ return h('div',{style:{height:'100%',display:'flex',flexDirection:'column',background:BG}}, Hdr(h('button',{onClick:()=>setScreen('play'),style:{width:44,height:44,borderRadius:'50%',background:'rgba(255,255,255,0.12)',border:'1.5px solid rgba(255,255,255,0.3)',color:'#fff',fontSize:22,cursor:'pointer',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}},'×')), h('div',{style:{padding:'12px 16px 10px',background:W,borderBottom:`1px solid ${BD}`,flexShrink:0}}, h('div',{style:{fontSize:20,fontWeight:800,color:TX,marginBottom:10}},'Playbook'), h('div',{style:{display:'flex',alignItems:'center',gap:10}}, h('span',{style:{fontSize:12,fontWeight:600,color:DM}},'Group:'), h('div',{style:{display:'flex',borderRadius:8,overflow:'hidden',border:`1px solid ${BD}`}}, ['A','B'].map(t=>h('button',{key:t,onClick:()=>setTeam(t),style:{padding:'6px 18px',background:team===t?BLUE:W,color:team===t?GOLD:DM,border:'none',fontSize:14,fontWeight:800,cursor:'pointer',transition:'all .1s'}},`Group ${t}`)) ) ) ), h('div',{style:{flex:1,overflowY:'auto',WebkitOverflowScrolling:'touch',paddingBottom:16}}, ...['MAIN','RUNS','TRICKS'].map(sec=>{ const sp=PLAYS.filter(p=>p.sec===sec),sc=SEC_LABELS[sec]; return h('div',{key:sec}, h('div',{style:{padding:'12px 16px 6px',display:'flex',alignItems:'center',gap:8}}, h('div',{style:{width:4,height:18,borderRadius:2,background:sc,flexShrink:0}}), h('span',{style:{fontSize:11,fontWeight:800,letterSpacing:3,color:sc,textTransform:'uppercase'}},sec), h('span',{style:{fontSize:11,color:DM,fontWeight:600}},`${sp.length} plays`) ), ...sp.map(play=>h('div',{key:play.id,onClick:()=>pickPlay(PLAYS.indexOf(play)),style:{display:'flex',alignItems:'center',gap:12,padding:'12px 16px',background:W,borderBottom:`1px solid ${BD}`,cursor:'pointer',borderLeft:`4px solid ${sc}`}}, h(MiniField,{play,size:72}), h('div',{style:{flex:1,minWidth:0}}, h('div',{style:{display:'flex',gap:6,marginBottom:4}}, play.tag&&h('span',{style:{fontSize:9,fontWeight:700,padding:'2px 6px',borderRadius:3,background:TAG_COL[play.tag]+'18',color:TAG_COL[play.tag],border:`1px solid ${TAG_COL[play.tag]}40`}},play.tag), h('span',{style:{fontSize:10,color:DM,fontWeight:600}},`${play.n} of ${play.of}`) ), h('div',{style:{fontSize:17,fontWeight:800,color:TX,lineHeight:1.1,marginBottom:5}},play.name), h('div',{style:{display:'flex',gap:4,flexWrap:'wrap',marginBottom:4}}, ['red','blue','green','orange','yellow'].map(c=>names[team][c]&&h('span',{key:c,style:{fontSize:10,fontWeight:700,color:COL[c].fill,background:COL[c].fill+'14',padding:'2px 6px',borderRadius:3}},names[team][c])) ), h('div',{style:{fontSize:11,color:DM,lineHeight:1.3,fontStyle:'italic'}},play.note) ), h('div',{style:{color:BD,fontSize:22,flexShrink:0}},'›') )) ); }) ) ); } // ── RULEBOOK ── if(screen==='rules'){ return h('div',{style:{height:'100%',display:'flex',flexDirection:'column',background:BG}}, Hdr(h('button',{onClick:()=>setScreen('play'),style:{width:44,height:44,borderRadius:'50%',background:'rgba(255,255,255,0.12)',border:'1.5px solid rgba(255,255,255,0.3)',color:'#fff',fontSize:22,cursor:'pointer',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}},'×')), h('div',{style:{padding:'12px 16px 8px',background:W,borderBottom:`1px solid ${BD}`,flexShrink:0}}, h('div',{style:{fontSize:20,fontWeight:800,color:TX,letterSpacing:-.3}},'8U Rulebook'), h('div',{style:{fontSize:12,color:DM,fontWeight:600,marginTop:2}},'NFL FLAG · Regular Season · Ages 7–8 · Tap a section to expand') ), h('div',{style:{flex:1,overflowY:'auto',WebkitOverflowScrolling:'touch',paddingBottom:24}}, ...RULE_SECTIONS.map(sec=>h('div',{key:sec.id}, h('div',{onClick:()=>setRuleOpen(ruleOpen===sec.id?null:sec.id),style:{display:'flex',alignItems:'center',gap:12,padding:'14px 16px',background:sec.bg,borderBottom:`1px solid ${sec.border}`,borderTop:`3px solid ${sec.color}`,cursor:'pointer',marginTop:10}}, h('div',{style:{flex:1}}, h('div',{style:{fontSize:12,fontWeight:800,color:sec.color,letterSpacing:1.5,textTransform:'uppercase'}},sec.label), h('div',{style:{fontSize:11,color:DM,marginTop:2}},`${sec.rules.length} rules`) ), h('div',{style:{fontSize:20,color:sec.color,fontWeight:700,transition:'transform .2s',transform:ruleOpen===sec.id?'rotate(90deg)':'rotate(0deg)'}},'›') ), ruleOpen===sec.id&&h('div',{style:{animation:'fadein .2s ease'}}, ...sec.rules.map((rule,ri)=>h('div',{key:ri,style:{padding:'14px 16px',background:W,borderBottom:`1px solid ${BD}`,borderLeft:`4px solid ${sec.color}`}}, h('div',{style:{fontSize:15,fontWeight:800,color:TX,marginBottom:5,lineHeight:1.2}},rule.title), h('div',{style:{fontSize:13,color:DM,lineHeight:1.55,fontWeight:500}},rule.body) )) ) )) ) ); } // ── PLAY SCREEN ── const rawPlay=PLAYS[idx]; const rawFm=FM[rawPlay.form]; const{play,fm}=flipped?applyFlip(rawPlay,rawFm):{play:rawPlay,fm:rawFm}; const allCols=['red','blue','green','orange','yellow']; const players=allCols.map(c=>{ const base=fm[c];if(!base)return null; const rt=play.routes?.[c]; const pos=(playing||prog>0)&&rt?gPos(rt,prog)||base:base; const nm=names[team][c]; return{c,pos,rt,base,name:nm,lbl:POS[c],empty:!nm}; }).filter(Boolean); const showSwapPrompt=selected!==null; const showSwapDone=swapMsg!==null&&selected===null; const ballPos=getBallPos(play,fm,prog); const DOT_R=7,POS_FS=3.6,NAME_FS=3.8; // Motion direction: flipped reverses the arrow const motDx=flipped?-26:26; const motLabel=flipped?'← MOTION':'→ MOTION'; return h('div',{style:{height:'100%',display:'flex',flexDirection:'column',background:'#0D2518',overflow:'hidden',position:'relative'}}, Hdr(h('button',{onClick:()=>setExitPrompt(true),style:{padding:'0 10px',height:34,borderRadius:6,background:'#C42020',border:'none',color:'#fff',fontSize:10,fontWeight:800,cursor:'pointer',letterSpacing:1,flexShrink:0}},'END GAME')), // A/B tabs + section tabs + RULES h('div',{style:{display:'flex',alignItems:'center',background:W,borderBottom:`1px solid ${BD}`,flexShrink:0}}, h('div',{style:{display:'flex',margin:'0 8px'}}, ['A','B'].map(t=>h('button',{key:t,onClick:()=>{setTeam(t);stopAnim();},style:{width:42,height:44,background:team===t?BLUE:BG,color:team===t?GOLD:DM,border:'none',fontSize:14,fontWeight:800,cursor:'pointer',letterSpacing:1,borderRadius:t==='A'?'8px 0 0 8px':'0 8px 8px 0',transition:'all .1s'}},t)) ), h('div',{style:{width:1,height:28,background:BD,flexShrink:0}}), ...['MAIN','RUNS','TRICKS'].map(s=>h('button',{key:s,onClick:()=>jumpSec(s),style:{flex:1,height:44,background:'transparent',border:'none',cursor:'pointer',borderBottom:`3px solid ${play.sec===s?SEC_LABELS[s]:'transparent'}`,color:play.sec===s?SEC_LABELS[s]:DM,fontSize:10,fontWeight:700,letterSpacing:1,transition:'all .1s'}},s)), h('div',{style:{width:1,height:28,background:BD,flexShrink:0}}), h('button',{onClick:()=>setScreen('rules'),style:{padding:'0 10px',height:44,background:BG,border:'none',cursor:'pointer',color:'#7C3AED',fontSize:10,fontWeight:800,letterSpacing:1,flexShrink:0}},'RULES') ), // Play info bar h('div',{style:{padding:'6px 14px 5px',background:W,borderBottom:`1px solid ${BD}`,flexShrink:0}}, h('div',{style:{display:'flex',alignItems:'center',justifyContent:'space-between'}}, h('div',{style:{display:'flex',alignItems:'center',gap:5}}, h('span',{style:{fontSize:9,fontWeight:700,letterSpacing:2,color:SEC_LABELS[play.sec],textTransform:'uppercase'}},play.sec), h('span',{style:{color:BD}},'·'), h('span',{style:{fontSize:9,color:'#9DAFC0'}},`${rawPlay.n}/${rawPlay.of}`), play.tag&&h('span',{style:{fontSize:8,fontWeight:700,padding:'1px 5px',borderRadius:3,background:TAG_COL[play.tag]+'18',color:TAG_COL[play.tag],border:`1px solid ${TAG_COL[play.tag]}40`}},play.tag), flipped&&h('span',{style:{fontSize:8,fontWeight:800,padding:'1px 6px',borderRadius:3,background:GOLD+'22',color:DGOLD,border:`1px solid ${GOLD}66`}},'↔ FLIPPED') ), namesModified&&h('button',{onClick:resetLineup,style:{padding:'4px 10px',borderRadius:6,background:GOLD,border:'none',color:DBLUE,fontSize:10,fontWeight:800,cursor:'pointer'}},'↺ Reset') ), h('div',{style:{fontSize:18,fontWeight:800,color:TX,letterSpacing:.1,lineHeight:1.1,marginTop:2}},play.name) ), lineNote&&!showSwapPrompt&&!showSwapDone&&h('div',{style:{padding:'5px 14px',background:`${GOLD}18`,borderBottom:`1px solid ${GOLD}55`,fontSize:11,fontWeight:600,color:'#7A5000',lineHeight:1.4,flexShrink:0}},`ℹ ${lineNote}`), showSwapPrompt&&h('div',{style:{display:'flex',alignItems:'center',gap:10,padding:'8px 14px',background:`${GOLD}25`,borderBottom:`2px solid ${GOLD}`,flexShrink:0,animation:'fadein .15s ease'}}, h('div',{style:{width:32,height:32,borderRadius:'50%',background:COL[selected].fill,display:'flex',alignItems:'center',justifyContent:'center',color:COL[selected].txt,fontSize:11,fontWeight:800,flexShrink:0}},POS[selected]), h('div',{style:{flex:1}}, h('div',{style:{fontSize:14,fontWeight:800,color:TX,lineHeight:1}},names[team][selected]||'OPEN'), h('div',{style:{fontSize:10,color:DGOLD,fontWeight:700,marginTop:1}},'Tap another player to swap') ), h('button',{onClick:clearSel,style:{padding:'6px 12px',borderRadius:6,background:'rgba(0,0,0,0.08)',border:'none',color:TX,fontSize:11,fontWeight:700,cursor:'pointer'}},'Cancel') ), showSwapDone&&h('div',{style:{display:'flex',alignItems:'center',gap:10,padding:'8px 14px',background:'#DCFCE7',borderBottom:`2px solid #16A34A`,flexShrink:0,animation:'flashin .25s ease'}}, h('div',{style:{fontSize:18}},'↕'), h('div',{style:{flex:1,fontSize:13,fontWeight:700,color:'#14532D'}},`${swapMsg.n1} ↔ ${swapMsg.n2} swapped`), h('button',{onClick:()=>setSwapMsg(null),style:{background:'transparent',border:'none',color:'#16A34A',fontSize:20,cursor:'pointer',padding:'4px'}},'×') ), // ── FIELD SVG ── h('div',{style:{flex:1,background:'#0D2518',display:'flex',alignItems:'center',justifyContent:'center',overflow:'hidden',minHeight:0}}, h('svg',{viewBox:'0 0 100 90',preserveAspectRatio:'xMidYMid meet',style:{width:'100%',height:'100%',display:'block'}}, h('defs',null,...allCols.map(c=>h('marker',{key:`a-${c}`,id:`a-${c}`,markerWidth:4,markerHeight:4,refX:3.5,refY:2,orient:'auto',markerUnits:'strokeWidth'},h('path',{d:'M0,0 L0,4 L4,2 z',fill:COL[c].fill})))), h('rect',{width:100,height:90,fill:'#0D2518'}), ...[18,32,54,68,82].map(y=>h('line',{key:y,x1:0,y1:y,x2:100,y2:y,stroke:'#183D26',strokeWidth:.6})), h('rect',{x:0,y:0,width:100,height:7,fill:'rgba(0,53,148,0.22)'}), h('line',{x1:0,y1:54,x2:100,y2:54,stroke:'rgba(255,255,255,0.55)',strokeWidth:1,strokeDasharray:'3 2'}), h('text',{x:2,y:52.5,fill:'rgba(255,255,255,0.3)',fontSize:2.8,fontWeight:700,fontFamily:'system-ui'},'LOS'), ...[10,28,48,68,88].map(x=>h('text',{key:x,x,y:42,fill:'#243F2E',fontSize:5.5,textAnchor:'middle',fontWeight:700,fontFamily:'system-ui'},'×')), !playing&&prog===0&&!selected&&h('text',{x:50,y:87,fill:'rgba(255,255,255,0.18)',fontSize:3,textAnchor:'middle',fontFamily:'system-ui',fontWeight:600},'Tap player · Tap another to swap'), // Motion arrow (flip-aware) rawPlay.motion&&prog===0&&h('g',{opacity:.6}, h('path',{d:`M${fm[rawPlay.motion][0]},${fm[rawPlay.motion][1]} L${fm[rawPlay.motion][0]+motDx},${fm[rawPlay.motion][1]}`,fill:'none',stroke:COL[rawPlay.motion].fill,strokeWidth:.9,strokeDasharray:'2 1.5',markerEnd:`url(#a-${rawPlay.motion})`}), h('text',{x:fm[rawPlay.motion][0]+(motDx/2),y:fm[rawPlay.motion][1]-3.5,fill:COL[rawPlay.motion].fill,fontSize:3,textAnchor:'middle',fontFamily:'system-ui',fontWeight:700},motLabel) ), // Fake indicator (flip-aware) (play.fakeRight||play.fakeLeft)&&prog>0&&h('g',{opacity:Math.min(prog*2.5,.65)}, h('path',{d:play.fakeLeft?'M50,70 L18,48':'M50,70 L82,48',fill:'none',stroke:'#C42020',strokeWidth:.9,strokeDasharray:'2 1.5'}), h('text',{x:play.fakeLeft?16:80,y:46,fill:'#C42020',fontSize:3,fontFamily:'system-ui',fontWeight:700},play.fakeLeft?'← FAKE':'FAKE →') ), // Routes ...players.map(({c,rt})=>{const pts=rPts(rt,prog);if(!pts)return null;const ip=c===play.primary;return h('polyline',{key:'rt'+c,points:pts,fill:'none',stroke:COL[c].fill,strokeWidth:ip?2.5:1.5,strokeDasharray:ip?'none':'3 2',strokeOpacity:ip?1:.8,strokeLinecap:'round',strokeLinejoin:'round',markerEnd:`url(#a-${c})`});}), // Throw line play.throwL&&prog>0.45&&h('line',{x1:play.throwL.fx,y1:play.throwL.fy,x2:play.throwL.tx,y2:play.throwL.ty,stroke:'#fff',strokeWidth:1.8,strokeDasharray:'2.5 1.5',strokeOpacity:Math.min((prog-.45)*2,.9),markerEnd:'url(#a-red)'}), // Lateral play.lateral&&prog>0.45&&h('line',{x1:play.lateral.fx,y1:play.lateral.fy,x2:play.lateral.tx,y2:play.lateral.ty,stroke:'rgba(255,255,255,0.7)',strokeWidth:1.5,strokeDasharray:'2 1.5',strokeOpacity:Math.min((prog-.45)*2,.8)}), // Handoff line play.handoff&&prog>0&&prog<.4&&h('g',null, h('line',{x1:play.handoff.fx,y1:play.handoff.fy,x2:play.handoff.tx,y2:play.handoff.ty,stroke:'rgba(255,255,255,0.55)',strokeWidth:.7,strokeDasharray:'1.5 1',strokeOpacity:1-prog/.4}), prog>.05&&h('text',{x:(play.handoff.fx+play.handoff.tx)/2,y:(play.handoff.fy+play.handoff.ty)/2-2.5,fill:'rgba(255,255,255,0.45)',fontSize:2.8,textAnchor:'middle',fontFamily:'system-ui',fontWeight:700},'HO') ), // Football ballPos&&h(Football,{key:'ball',x:ballPos[0],y:ballPos[1]}), // Players ...players.map(({c,pos,base,name,lbl,empty})=>{ const[px,py]=pos||base; const ip=c===play.primary,isSel=selected===c; const r=DOT_R*(isSel?1.1:(ip&&prog>.86?1.08:1)); return h('g',{key:'pl'+c,onClick:()=>handlePlayerTap(c),style:{cursor:'pointer'}}, isSel&&h('circle',{cx:px,cy:py,r:r+5,fill:'none',stroke:GOLD,strokeWidth:2.5,style:{animation:'pulse .7s ease-in-out infinite'}}), ip&&prog>.88&&h('circle',{cx:px,cy:py,r:11,fill:COL[c].fill,opacity:.18}), h('circle',{cx:px,cy:py,r,fill:empty?'none':COL[c].fill,stroke:isSel?GOLD:empty?COL[c].fill:'rgba(255,255,255,0.9)',strokeWidth:isSel?2.5:empty?1.2:1,strokeDasharray:empty?'2 1.5':'none',opacity:empty?.5:1}), h('text',{x:px,y:py+1.4,fill:empty?COL[c].fill:'rgba(255,255,255,0.97)',fontSize:POS_FS,fontWeight:800,textAnchor:'middle',fontFamily:'system-ui',style:{pointerEvents:'none'},opacity:empty?.5:1},lbl), (!playing||prog>.88)&&( empty ?h('g',{key:'sub'+c}, h('rect',{x:px-8,y:py+r+1,width:16,height:7.5,rx:2,fill:'rgba(200,30,30,0.28)'}), h('text',{x:px,y:py+r+7.2,fill:'#FF2222',fontSize:5,fontWeight:900,textAnchor:'middle',fontFamily:'system-ui',style:{pointerEvents:'none'}},'SUB') ) :h('text',{x:px,y:py+r+6.5,fill:isSel?GOLD:COL[c].fill,fontSize:NAME_FS,fontWeight:isSel?800:700,textAnchor:'middle',fontFamily:'system-ui',opacity:.95,style:{pointerEvents:'none'}},name) ) ); }) ) ), // Coach note h('div',{style:{padding:'5px 14px',background:W,borderTop:`1px solid ${BD}`,fontSize:12,fontWeight:600,color:DM,lineHeight:1.35,fontStyle:'italic',flexShrink:0}},play.note), // ── BOTTOM CONTROLS ── h('div',{style:{display:'flex',alignItems:'center',gap:5,padding:'8px 8px',background:W,borderTop:`1px solid ${BD}`,flexShrink:0}}, // Prev h('button',{onClick:()=>nav(-1),disabled:idx===0,style:{width:40,height:44,borderRadius:'50%',background:'transparent',border:`1.5px solid ${BD}`,color:idx===0?BD:DM,fontSize:22,cursor:idx===0?'default':'pointer',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}},'‹'), // Reset anim h('button',{onClick:stopAnim,style:{padding:'0 8px',height:44,borderRadius:8,background:'#E8F0F8',border:`2px solid #C0D0E4`,color:'#3A5A80',fontSize:14,fontWeight:800,cursor:'pointer',flexShrink:0}},'↺'), // FLIP h('button',{onClick:()=>{stopAnim();setFlipped(f=>!f);},style:{padding:'0 8px',height:44,borderRadius:8,background:flipped?GOLD:'#E8F0F8',border:`2px solid ${flipped?DGOLD:'#C0D0E4'}`,color:flipped?DBLUE:'#3A5A80',fontSize:11,fontWeight:800,cursor:'pointer',flexShrink:0,letterSpacing:.5}},'↔ FLIP'), // RUN / STOP h('button',{onClick:startStop,style:{flex:1,height:48,borderRadius:10,background:playing?DBLUE:BLUE,border:'none',color:playing?'rgba(255,255,255,0.85)':GOLD,fontSize:15,fontWeight:800,cursor:'pointer',display:'flex',alignItems:'center',justifyContent:'center',gap:6,transition:'all .1s'}}, h('span',{style:{fontSize:18}},playing?'■':'▶'),playing?'STOP':'RUN PLAY' ), // PLAYS h('button',{onClick:()=>{stopAnim();setScreen('playbook');},style:{padding:'0 8px',height:44,borderRadius:8,background:BG,border:`1.5px solid ${BD}`,color:TX,fontSize:10,fontWeight:800,cursor:'pointer',flexShrink:0}},'PLAYS'), // Next h('button',{onClick:()=>nav(1),disabled:idx===PLAYS.length-1,style:{width:40,height:44,borderRadius:'50%',background:'transparent',border:`1.5px solid ${BD}`,color:idx===PLAYS.length-1?BD:DM,fontSize:22,cursor:idx===PLAYS.length-1?'default':'pointer',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}},'›') ), // Exit prompt exitPrompt&&h('div',{style:{position:'absolute',top:0,left:0,right:0,bottom:0,background:'rgba(0,0,0,0.75)',display:'flex',alignItems:'center',justifyContent:'center',padding:28,zIndex:200}}, h('div',{style:{background:W,borderRadius:16,padding:'28px 24px',width:'100%',maxWidth:340,animation:'flashin .2s ease'}}, h('div',{style:{fontSize:24,fontWeight:800,color:TX,marginBottom:8}},'End Game?'), h('div',{style:{fontSize:14,color:DM,fontWeight:500,lineHeight:1.5,marginBottom:24}},'This will go back to the attendance screen. Any lineup swaps will be cleared.'), h('div',{style:{display:'flex',gap:12}}, h('button',{onClick:()=>setExitPrompt(false),style:{flex:1,padding:'14px',borderRadius:10,background:BG,border:`1.5px solid ${BD}`,color:DM,fontSize:15,fontWeight:700,cursor:'pointer'}},'Cancel'), h('button',{onClick:()=>{setExitPrompt(false);stopAnim();setFlipped(false);setNames(DEF);setDefaultNames(DEF);setLineNote('');setScreen('att');},style:{flex:1,padding:'14px',borderRadius:10,background:'#C42020',border:'none',color:'#fff',fontSize:15,fontWeight:800,cursor:'pointer'}},'End Game') ) ) ) ); } createRoot(document.getElementById('root')).render(h(App));