// All 6 pages: Dashboard, Active Session, History, Analytics, Library, Settings
const { useState: uS, useEffect: uE, useRef: uR, useMemo: uM, useCallback: uC } = React;

/* ============================================================
   DASHBOARD
   ============================================================ */
function DashboardPage({ units, sessions, library, onStartSession, sessionActive, onDayPress, onGoToHistory, onViewSession }) {
  const today = new Date();
  // Date-ordered, not insertion-ordered — logging an old past session prepends it to the array.
  const byDate = uM(() => [...sessions].sort((a, b) => new Date(b.startedAt||b.date||0) - new Date(a.startedAt||a.date||0)), [sessions]);
  const recent = byDate.slice(0, 5);
  const [statPeriod, setStatPeriod] = uS('7d');
  const cyclePeriod = () => setStatPeriod(p => p === '7d' ? '30d' : p === '30d' ? 'all' : '7d');
  const periodStats = uM(() => {
    const cut = statPeriod === 'all' ? new Date(0) : (() => {
      const d = new Date(); d.setDate(d.getDate() - (statPeriod === '7d' ? 7 : 30)); return d;
    })();
    const periodSess = sessions.filter(s => new Date(s.startedAt||s.date) >= cut);
    const count = periodSess.length;
    const volume = periodSess.reduce((n, s) => n + totalSets(s), 0);
    const avgVol = count ? Math.round(volume / count * 10) / 10 : 0;
    const sorted = [...sessions].sort((a,b) => new Date(a.startedAt||a.date) - new Date(b.startedAt||b.date));
    const best = {};
    let prCount = 0;
    sorted.forEach(s => {
      const inPeriod = new Date(s.startedAt||s.date) >= cut;
      (s.exercises||[]).forEach(blk => (blk.sets||[]).forEach(set => {
        if (set.drop || set.myo || !set.weight || !set.reps) return;
        const e = epley(set.weight, set.reps);
        if (!best[blk.exId] || e > best[blk.exId]) { if (inPeriod) prCount++; best[blk.exId] = e; }
      }));
    });
    return { count, volume, avgVol, prCount };
  }, [sessions, statPeriod]);
  const weeklyVolume = uM(() => computeWeeklyVolume(sessions), [sessions]);
  const recentPRs = uM(() => computeRecentPRs(sessions, 3), [sessions]);
  const prSessionIds = uM(() => {
    const sorted = [...sessions].sort((a,b) => new Date(a.startedAt||a.date) - new Date(b.startedAt||b.date));
    const best = {}, ids = new Set();
    sorted.forEach(s => {
      (s.exercises||[]).forEach(blk => (blk.sets||[]).forEach(set => {
        if (set.drop || set.myo || !set.weight || !set.reps) return;
        const e = epley(set.weight, set.reps);
        if (!best[blk.exId] || e > best[blk.exId]) { best[blk.exId] = e; ids.add(s.id); }
      }));
    });
    return ids;
  }, [sessions]);

  return (
    <div style={{ display:'flex', flexDirection:'column', gap:24, padding:28 }}>
      <div style={{ display:'flex', alignItems:'flex-end', justifyContent:'space-between', flexWrap:'wrap', gap:16 }}>
        <div>
          <div style={{ fontSize:13, color:'var(--text-mute)', letterSpacing:0.4, textTransform:'uppercase', fontWeight:600 }}>
            {today.toLocaleDateString('en-US', { weekday:'long', month:'long', day:'numeric' })}
          </div>
          <h1 style={{ margin:'8px 0 0', fontSize:28, fontWeight:800, letterSpacing:-0.5, lineHeight:1.1 }}>{HERO_TITLE.lead} <span style={{ color:'var(--accent)' }}>{HERO_TITLE.words.join(' ')}</span></h1>
          {sessions.length > 0 && (
            <div style={{ marginTop:6, color:'var(--text-dim)', fontSize:14 }}>
              Last session <span style={{ color:'var(--text)' }}>{formatDate(byDate[0].startedAt || byDate[0].date, { relative:true, time:true, todayOnly:true })}</span>
            </div>
          )}
        </div>
        <Button variant="primary" size="lg" onClick={onStartSession} icon={IconPlus}>
          {sessionActive ? 'Resume session' : 'Start new session'}
        </Button>
      </div>

      <div>
        <button onClick={cyclePeriod} style={{ display:'flex', alignItems:'center', justifyContent:'space-between', width:'100%', background:'none', border:'none', padding:'0 0 10px', cursor:'pointer' }}>
          <div style={{ display:'flex', alignItems:'center', gap:6 }}>
            {statPeriod === 'all'
              ? <span style={{ fontSize:14, fontWeight:800, color:'var(--text-mute)', lineHeight:1 }}>∞</span>
              : <IconCalendar size={12} style={{ color:'var(--text-mute)' }}/>
            }
            <span style={{ fontSize:10, fontWeight:700, letterSpacing:0.8, textTransform:'uppercase', color:'var(--text-mute)' }}>
              {statPeriod === '7d' ? 'Last 7 Days' : statPeriod === '30d' ? 'Last 30 Days' : 'All Time'}
            </span>
          </div>
          <span style={{ fontSize:11, color:'var(--text-faint)', fontWeight:600 }}>click to cycle ›</span>
        </button>
        <div style={{ display:'grid', gridTemplateColumns:'repeat(4, 1fr)', gap:12 }}>
          {[
            { label:'Volume',        value:periodStats.volume,  unit:'sets', iconColor:'var(--accent)', iconBg:'color-mix(in srgb, var(--accent) 10%, transparent)', icon:<IconArrowUp size={14}/> },
            { label:'Sessions',      value:periodStats.count,   unit:'',     iconColor:'#60a5fa',       iconBg:'rgba(96,165,250,0.1)', icon:<IconActive size={14}/>,  onClick:onGoToHistory },
            { label:'Avg / Session', value:periodStats.avgVol,  unit:'sets', iconColor:'#a78bfa',       iconBg:'rgba(167,139,250,0.1)',icon:<IconFlame size={14}/> },
            { label:'PRs Hit',       value:periodStats.prCount, unit:'',     iconColor:'#fbbf24',       iconBg:'rgba(251,191,36,0.1)', icon:<IconTrophy size={14}/> },
          ].map(card => {
            const Tag = card.onClick ? 'button' : 'div';
            return (
              <Tag key={card.label} onClick={card.onClick} style={{
                background:'linear-gradient(160deg, rgba(255,255,255,0.03) 0%, transparent 60%), var(--surface)',
                border:'1px solid var(--border)',
                borderRadius:'var(--radius)', padding:'18px 20px',
                boxShadow:'var(--shadow-sm)',
                display:'flex', flexDirection:'column', gap:14,
                position:'relative', overflow:'hidden',
                textAlign:'left', width:'100%',
                cursor: card.onClick ? 'pointer' : 'default',
              }}>
                <div style={{ position:'absolute', top:0, left:0, right:0, height:2, background:`linear-gradient(90deg, ${card.iconColor}, transparent 70%)`, opacity:0.6 }}/>
                <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between' }}>
                  <div style={{ fontSize:10, fontWeight:700, letterSpacing:0.7, textTransform:'uppercase', color:'var(--text-mute)' }}>{card.label}</div>
                  <div style={{ width:26, height:26, borderRadius:7, background:card.iconBg, display:'grid', placeItems:'center', color:card.iconColor }}>
                    {card.icon}
                  </div>
                </div>
                <div className="mono" style={{ fontSize:32, fontWeight:700, letterSpacing:-1.5, lineHeight:1, color:'var(--text)' }}>
                  {card.value}{card.unit && <span style={{ fontSize:14, color:'var(--text-mute)', marginLeft:4, fontWeight:500, letterSpacing:0 }}>{card.unit}</span>}
                </div>
              </Tag>
            );
          })}
        </div>
      </div>

      {sessions.length === 0 ? (
        <Card style={{ textAlign:'center', padding:48 }}>
          <div style={{ fontSize:32, marginBottom:12 }}>💪</div>
          <div style={{ fontSize:16, fontWeight:700, marginBottom:8 }}>No sessions yet</div>
          <div style={{ color:'var(--text-dim)', fontSize:14 }}>Start your first session to see stats here.</div>
        </Card>
      ) : (
        <div style={{ display:'grid', gridTemplateColumns:'1.4fr 1fr 1fr', gap:14 }}>
          <Card padded={false}>
            <div style={{ padding:'18px 20px 12px', display:'flex', alignItems:'center', justifyContent:'space-between' }}>
              <div style={{ fontWeight:700, fontSize:14, letterSpacing:0.3 }}>Recent sessions</div>
            </div>
            <div>
              {recent.map((s, i) => {
                const breakdown = Object.entries(
                  (s.exercises||[]).reduce((acc, blk) => {
                    const ex = exById(blk.exId);
                    const n = (blk.sets||[]).filter(x => !x.drop).length;
                    if (!n) return acc;
                    exGroups(ex).forEach(g => { acc[g] = (acc[g]||0) + n; });
                    exSecondary(ex).forEach(g => { acc[g] = (acc[g]||0) + n * 0.5; });
                    return acc;
                  }, {})
                ).sort((a, b) => b[1] - a[1]);
                return (
                <div key={s.id} onClick={() => onDayPress && onDayPress([s])} style={{
                  display:'grid', gridTemplateColumns:'100px 1fr auto auto',
                  gap:16, alignItems:'center', padding:'13px 20px',
                  borderTop:'1px solid var(--border)', fontSize:13, cursor:'pointer',
                  transition:'background 0.12s',
                }}
                  onMouseEnter={e => e.currentTarget.style.background = 'rgba(255,255,255,0.025)'}
                  onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                >
                  <div className="mono" style={{ fontSize:11, color:'var(--text-mute)', letterSpacing:0.2 }}>{formatDate(s.startedAt||s.date, { relative:true, time:true, todayOnly:true })}</div>
                  <div style={{ minWidth:0 }}>
                    <div style={{ fontWeight:600, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis', letterSpacing:-0.2 }}>{s.name}</div>
                    <div style={{ fontSize:11, color:'var(--text-mute)', marginTop:2, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>
                      {breakdown.slice(0,4).map(([g,n]) => `${g}×${fmtVol(n)}`).join(' · ')}
                    </div>
                  </div>
                  <div>{prSessionIds.has(s.id) && <PRBadge small/>}</div>
                  <div className="mono" style={{ fontSize:12, color:'var(--text)', fontWeight:600, textAlign:'right', minWidth:52 }}>
                    {totalSets(s)}<span style={{ color:'var(--text-faint)', fontSize:10, marginLeft:2 }}>sets</span>
                  </div>
                </div>
                );
              })}
            </div>
          </Card>

          <Card padded={false}>
            <div style={{ padding:'18px 20px 8px' }}>
              <div style={{ fontWeight:700, fontSize:11, letterSpacing:0.7, textTransform:'uppercase', color:'var(--text-mute)' }}>Last 7 Days Volume (Sets)</div>
            </div>
            <div style={{ display:'grid', gridTemplateColumns:'110px 1fr', gap:10, padding:'0 20px 20px', alignItems:'center' }}>
              <BodyDiagram volume={weeklyVolume}/>
              <div style={{ display:'flex', flexDirection:'column', gap:6 }}>
                {Object.entries(weeklyVolume).map(([g, v]) => {
                  const pct = Math.min(1, v.current / v.target);
                  return (
                    <div key={g}>
                      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom:3 }}>
                        <span style={{ fontSize:11, color:'var(--text-dim)' }}>{g}</span>
                        <span className="mono" style={{ fontSize:10, color: pct >= 1 ? 'var(--accent)' : 'var(--text-mute)' }}>
                          {v.current}
                        </span>
                      </div>
                      <div style={{ height:3, background:'var(--surface-3)', borderRadius:2, overflow:'hidden' }}>
                        <div style={{
                          width:`${pct*100}%`, height:'100%',
                          background: pct >= 1 ? 'var(--accent)' : muscleColor(g),
                          transition:'width 0.4s',
                          boxShadow: pct >= 1 ? '0 0 6px var(--accent-glow)' : 'none',
                        }}/>
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          </Card>

          <Card style={{ padding:'18px 20px' }}>
            <WorkoutCalendar sessions={sessions} onDayPress={onDayPress}/>
          </Card>
        </div>
      )}

      {recentPRs.length > 0 && (
        <div>
          <SectionHeading sub="Most recent personal records">Recent PRs</SectionHeading>
          <div style={{ display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:14 }}>
            {recentPRs.map((pr, i) => {
              const ex = exById(pr.exId);
              const sess = sessions.find(s => (s.startedAt||s.date) === pr.date && s.name === pr.sessionName);
              return (
                <button key={i} onClick={() => sess && onViewSession && onViewSession(sess)} style={{ display:'block', width:'100%', textAlign:'left', background:'none', border:'none', padding:0, cursor: sess ? 'pointer' : 'default' }}>
                  <Card hoverable={!!sess} style={{ position:'relative', overflow:'hidden' }}>
                    <div style={{ position:'absolute', top:-20, right:-20, width:100, height:100, borderRadius:999, background:'radial-gradient(circle, var(--accent-glow), transparent 70%)' }}/>
                    <div style={{ display:'flex', alignItems:'center', gap:10, marginBottom:10 }}>
                      <PRBadge small/>
                      {exGroups(ex).map(g => <MuscleBadge key={g} group={g}/>)}
                    </div>
                    <div style={{ fontSize:15, fontWeight:700, position:'relative' }}>{ex.name}</div>
                    <div className="mono" style={{ fontSize:22, fontWeight:700, marginTop:10, position:'relative' }}>
                      {convertWeight(pr.weight, units)}<span style={{ color:'var(--text-mute)', fontSize:13, marginLeft:4 }}>{weightLabel(units)}</span>
                      <span style={{ color:'var(--text-faint)', fontSize:14, margin:'0 8px' }}>×</span>
                      {pr.reps}
                    </div>
                    <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginTop:10, position:'relative' }}>
                      <div style={{ fontSize:11, color:'var(--text-mute)' }}>
                        e1RM <span className="mono" style={{ color:'var(--text)' }}>{Math.round(convertWeight(pr.est1RM,units))}{weightLabel(units)}</span>
                      </div>
                      <div style={{ fontSize:11, color:'var(--text-mute)' }}>{formatDate(pr.date, { relative:true })}</div>
                    </div>
                  </Card>
                </button>
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
}

/* ============================================================
   ACTIVE SESSION
   ============================================================ */
function ActiveSessionPage({ units, sessions, library, session, updateSession, finishSession, cancelSession, startTimer, gymFilter, onAddToLibrary }) {
  const [elapsed, setElapsed] = uS(0);
  const [lastTimePanel, setLastTimePanel] = uS(null);
  const [addExOpen, setAddExOpen] = uS(false);
  const [prMoment, setPrMoment] = uS(null);

  // Per-set PR status, computed dynamically (never from stored set.isPR). Baseline = best
  // e1RM from history; within the session the bar rises set-by-set across duplicate blocks.
  // Keyed by block uid. MUST stay above the early return (hooks can't be conditional).
  const activePRMap = uM(() => {
    const bestByEx = computeBestE1RMs(sessions);
    const map = {};
    (session?.exercises||[]).forEach(blk => {
      const prs = new Set();
      (blk.sets||[]).forEach((set, i) => {
        if (set.drop || set.myo || !set.weight || !set.reps) return;
        const e = epley(set.weight, set.reps);
        if (bestByEx[blk.exId] == null || e > bestByEx[blk.exId]) { prs.add(i); bestByEx[blk.exId] = e; }
      });
      map[blk.uid] = prs;
    });
    return map;
  }, [session?.exercises, sessions]);

  uE(() => {
    if (!session) return;
    const start = new Date(session.startedAt).getTime();
    const tick = () => setElapsed(Math.floor((Date.now() - start) / 1000));
    tick();
    const id = setInterval(tick, 1000);
    return () => clearInterval(id);
  }, [session?.id]);

  if (!session) return <StartSessionPrompt onStart={updateSession.start} sessions={sessions}/>;

  const elapsedDisplay = () => {
    const h = Math.floor(elapsed / 3600);
    const m = Math.floor((elapsed % 3600) / 60);
    const s = elapsed % 60;
    if (h) return `${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`;
    return `${m}:${String(s).padStart(2,'0')}`;
  };

  // Live baseline for the PR flash: best from history AND the sets currently in this session
  // (so deleting/editing a set, or a sync bringing in new history, is reflected).
  const checkPR = (exId, weight, reps) => {
    if (!weight || !reps) return false;
    let prev = computeBestE1RMs(sessions)[exId] || 0;
    (session.exercises||[]).filter(b => b.exId === exId).forEach(b => (b.sets||[]).forEach(s => {
      if (s.drop || s.myo || !s.weight || !s.reps) return;
      prev = Math.max(prev, epley(s.weight, s.reps));
    }));
    return !prev || epley(weight, reps) > prev;
  };

  const onLogSet = (blk, setData) => {
    const isPR = !setData.drop && checkPR(blk.exId, setData.weight, setData.reps);
    updateSession.addSet(blk.uid, { ...setData, isPR });
    if (isPR) setPrMoment({ exerciseName: exById(blk.exId).name, weight: convertWeight(setData.weight, units), reps: setData.reps });
    if (setData.rest) startTimer(setData.rest);
  };

  const sessionVolume = totalVolume(session);
  const sessionSetCount = (session.exercises||[]).reduce((n, e) => n + (e.sets||[]).length, 0);
  const sessionPRs = (session.exercises||[]).flatMap(blk =>
    (blk.sets||[]).filter((_, i) => activePRMap[blk.uid]?.has(i)).map(s => ({ exId:blk.exId, ...s })));

  return (
    <div style={{ display:'flex', flexDirection:'column', height:'100vh', overflow:'hidden' }}>
      {/* Header */}
      <div style={{ padding:'16px 28px', borderBottom:'1px solid var(--border)', display:'flex', alignItems:'center', justifyContent:'space-between', gap:16, background:'var(--bg)', flexShrink:0 }}>
        <div style={{ display:'flex', alignItems:'center', gap:10 }}>
          <span style={{ width:8, height:8, borderRadius:999, background:'var(--accent)', boxShadow:'0 0 12px var(--accent)', animation:'pulse-accent 2s ease infinite', flexShrink:0 }}/>
          <h1 style={{ margin:0, fontSize:22, fontWeight:700 }}>{session.name}</h1>
          <span style={{ fontSize:11, padding:'2px 8px', borderRadius:999, background:'color-mix(in srgb, var(--accent) 8%, transparent)', color:'var(--accent)', fontWeight:600, letterSpacing:0.3, textTransform:'uppercase' }}>Live</span>
        </div>
        <div style={{ display:'flex', alignItems:'center', gap:12 }}>
          <span style={{ fontSize:13, color:'var(--text-dim)' }}>
            {new Date(session.startedAt).toLocaleDateString('en-US', { weekday:'long', month:'short', day:'numeric' })}
          </span>
          <Button variant="ghost" onClick={cancelSession} style={{ color:'var(--danger)' }}>Cancel session</Button>
          <Button variant="primary" onClick={finishSession}>Finish session</Button>
        </div>
      </div>

      {/* Body: exercise list + right panel */}
      <div style={{ flex:1, display:'flex', overflow:'hidden' }}>
        {/* Exercise list */}
        <div style={{ flex:1, overflowY:'auto', padding:28 }}>
          <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
            {(session.exercises||[]).map(blk => (
              <ExerciseBlock
                key={blk.uid}
                block={blk}
                units={units}
                onLogSet={setData => onLogSet(blk, setData)}
                onDeleteSet={idx => updateSession.deleteSet(blk.uid, idx)}
                onUpdateSet={(idx, data) => updateSession.updateSet(blk.uid, idx, data)}
                onStartTimer={startTimer}
                onShowLastTime={() => setLastTimePanel(blk.exId)}
                onRemove={() => updateSession.removeExercise(blk.uid)}
                onUpdateNotes={notes => updateSession.updateExercise(blk.uid, { notes })}
                prSets={activePRMap[blk.uid]}
              />
            ))}
            <button onClick={() => setAddExOpen(true)} style={{
              display:'flex', alignItems:'center', justifyContent:'center', gap:8,
              padding:16, borderRadius:'var(--radius)',
              border:'1px dashed var(--border-strong)', color:'var(--text-dim)', fontSize:13, fontWeight:600,
            }}
              onMouseEnter={e => { e.currentTarget.style.background = 'var(--surface)'; e.currentTarget.style.color = 'var(--text)'; }}
              onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = 'var(--text-dim)'; }}
            ><IconPlus size={15}/> Add exercise</button>
          </div>
        </div>

        {/* Right stats panel */}
        <div style={{ width:272, borderLeft:'1px solid var(--border)', padding:24, background:'var(--bg-2)', display:'flex', flexDirection:'column', gap:24, flexShrink:0, overflowY:'auto' }}>
          <div>
            <div style={{ fontSize:10, color:'var(--text-mute)', letterSpacing:0.5, textTransform:'uppercase', fontWeight:600, marginBottom:8 }}>Elapsed</div>
            <div className="mono" style={{ fontSize:44, fontWeight:700, letterSpacing:-2, lineHeight:1 }}>{elapsedDisplay()}</div>
            <div style={{ fontSize:11, color:'var(--text-mute)', marginTop:6 }}>
              Started {new Date(session.startedAt).toLocaleTimeString('en-US', { hour:'numeric', minute:'2-digit' })}
            </div>
          </div>
          <div style={{ display:'flex', flexDirection:'column', gap:6 }}>
            {[
              { label:'Exercises', val:(session.exercises||[]).length },
              { label:'Sets', val:sessionSetCount },
              { label:'Volume', val:Math.round(convertWeight(sessionVolume,units))+' '+weightLabel(units) },
            ].map(({label, val}) => (
              <div key={label} style={{ display:'flex', justifyContent:'space-between', alignItems:'center', padding:'10px 14px', background:'var(--surface)', borderRadius:'var(--radius-sm)', border:'1px solid var(--border)' }}>
                <span style={{ fontSize:12, color:'var(--text-dim)', fontWeight:600 }}>{label}</span>
                <span className="mono" style={{ fontSize:14, fontWeight:700 }}>{val}</span>
              </div>
            ))}
          </div>
          {sessionPRs.length > 0 && (
            <div>
              <div style={{ fontSize:10, color:'var(--text-mute)', letterSpacing:0.5, textTransform:'uppercase', fontWeight:600, marginBottom:10 }}>PRs this session</div>
              <div style={{ display:'flex', flexDirection:'column', gap:6 }}>
                {sessionPRs.map((pr, i) => (
                  <div key={i} style={{ padding:'10px 12px', background:'var(--surface)', borderRadius:'var(--radius-sm)', border:'1px solid color-mix(in srgb, var(--accent) 15%, transparent)' }}>
                    <div style={{ fontWeight:600, fontSize:12 }}>{exById(pr.exId).name}</div>
                    <div className="mono" style={{ fontSize:11, color:'var(--accent)', marginTop:3 }}>
                      {convertWeight(pr.weight,units)} {weightLabel(units)} × {pr.reps}
                    </div>
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>
      </div>

      {lastTimePanel && <LastTimePanel exId={lastTimePanel} sessions={sessions} units={units} onClose={() => setLastTimePanel(null)}/>}
      {addExOpen && (
        <AddExerciseModal
          library={library}
          onClose={() => setAddExOpen(false)}
          onPick={exId => { updateSession.addExercise(exId); setAddExOpen(false); }}
          addedIds={(session.exercises||[]).map(e => e.exId)}
          sessionName={session.name}
          gymFilter={gymFilter}
          onAddToLibrary={onAddToLibrary}
        />
      )}
      {prMoment && <PRFlash {...prMoment} units={weightLabel(units)} onDismiss={() => setPrMoment(null)}/>}
    </div>
  );
}

function StartSessionPrompt({ onStart, sessions }) {
  const [step, setStep] = uS('type');
  const [chosen, setChosen] = uS(null);
  const [customName, setCustomName] = uS('');
  const types = SESSION_TYPES;

  return (
    <div style={{ padding:40, display:'grid', placeItems:'center', minHeight:'100vh' }}>
      <div style={{ width:480, maxWidth:'100%' }}>
        {step === 'type' && (
          <div style={{ animation:'fade-up 0.3s ease' }}>
            <div style={{ textAlign:'center', marginBottom:32 }}>
              <h1 style={{ margin:0, fontSize:26, fontWeight:700 }}>Start a session</h1>
              <div style={{ color:'var(--text-dim)', fontSize:14, marginTop:8 }}>Pick a template or start custom.</div>
            </div>
            <div style={{ display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:12 }}>
              {types.map(t => (
                <button key={t} onClick={() => { setChosen(t); setStep('preload'); }} style={{
                  padding:24, borderRadius:'var(--radius)',
                  border:'1px solid var(--border-strong)', background:'var(--surface)',
                  color:'var(--text)', display:'flex', flexDirection:'column', alignItems:'center', gap:8,
                  transition:'all 0.15s',
                }}
                  onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--accent)'; e.currentTarget.style.background = 'var(--surface-2)'; }}
                  onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--border-strong)'; e.currentTarget.style.background = 'var(--surface)'; }}
                >
                  <div style={{ fontSize:18, fontWeight:700 }}>{t}</div>
                  <div style={{ fontSize:11, color:'var(--text-mute)' }}>
                    {(() => { const last = latestSession(sessions, t); return last
                      ? 'Last: ' + formatDate(last.startedAt||last.date, { relative:true })
                      : 'No history yet'; })()}
                  </div>
                </button>
              ))}
            </div>
            <div style={{ marginTop:16, display:'flex', justifyContent:'center', gap:10, alignItems:'center', flexWrap:'wrap' }}>
              <input
                value={customName}
                onChange={e => setCustomName(e.target.value)}
                placeholder="Custom session name…"
                style={{ height:36, padding:'0 12px', background:'var(--surface)', border:'1px solid var(--border-strong)', borderRadius:'var(--radius-sm)', fontSize:13, color:'var(--text)', flex:'1 1 160px', minWidth:0 }}
              />
              <Button size="sm" onClick={() => { if (customName.trim()) { setChosen(customName.trim()); setStep('preload'); } }} disabled={!customName.trim()}>
                Start custom
              </Button>
            </div>
          </div>
        )}
        {step === 'preload' && (
          <div style={{ animation:'fade-up 0.3s ease' }}>
            <div style={{ textAlign:'center', marginBottom:28 }}>
              <h1 style={{ margin:0, fontSize:24, fontWeight:700 }}>{chosen}</h1>
              <div style={{ color:'var(--text-dim)', fontSize:14, marginTop:6 }}>How would you like to start?</div>
            </div>
            <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
              {latestSession(sessions, chosen) && (
                <button onClick={() => onStart({ name:chosen, preloadLast:true })} style={{
                  padding:20, borderRadius:'var(--radius)', textAlign:'left',
                  background:'var(--surface)', border:'1px solid var(--accent)',
                  display:'flex', alignItems:'center', gap:14,
                }}>
                  <div style={{ width:40, height:40, borderRadius:10, background:'var(--accent)', display:'grid', placeItems:'center', color:'#06080d', flexShrink:0 }}>
                    <IconHistory size={20} sw={2}/>
                  </div>
                  <div style={{ flex:1 }}>
                    <div style={{ fontWeight:700, color:'var(--text)' }}>Load last {chosen} session</div>
                    <div style={{ fontSize:12, color:'var(--text-dim)', marginTop:3 }}>
                      Pre-fill exercises from {formatDate(latestSession(sessions, chosen)?.startedAt||latestSession(sessions, chosen)?.date, { relative:true })}
                    </div>
                  </div>
                  <IconChevronRight size={16}/>
                </button>
              )}
              <button onClick={() => onStart({ name:chosen, preloadLast:false })} style={{
                padding:20, borderRadius:'var(--radius)', textAlign:'left',
                background:'var(--surface)', border:'1px solid var(--border-strong)',
                display:'flex', alignItems:'center', gap:14,
              }}
                onMouseEnter={e => e.currentTarget.style.borderColor = 'rgba(255,255,255,0.18)'}
                onMouseLeave={e => e.currentTarget.style.borderColor = 'var(--border-strong)'}
              >
                <div style={{ width:40, height:40, borderRadius:10, background:'var(--surface-3)', display:'grid', placeItems:'center', color:'var(--text)', flexShrink:0 }}>
                  <IconPlus size={20} sw={2}/>
                </div>
                <div style={{ flex:1 }}>
                  <div style={{ fontWeight:700, color:'var(--text)' }}>Start fresh</div>
                  <div style={{ fontSize:12, color:'var(--text-dim)', marginTop:3 }}>Build the workout as you go.</div>
                </div>
                <IconChevronRight size={16}/>
              </button>
            </div>
            <div style={{ marginTop:16, textAlign:'center' }}>
              <button onClick={() => setStep('type')} style={{ fontSize:12, color:'var(--text-mute)' }}>← Back</button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

function ExerciseBlock({ block, units, onLogSet, onDeleteSet, onUpdateSet, onStartTimer, onShowLastTime, onRemove, onUpdateNotes, prSets }) {
  const ex = exById(block.exId);
  const [notesOpen, setNotesOpen] = uS(!!block.notes);
  const notes = block.notes || '';

  return (
    <div style={{
      background:'linear-gradient(160deg, rgba(255,255,255,0.025) 0%, transparent 50%), var(--surface)',
      border:'1px solid var(--border)', borderRadius:'var(--radius)', overflow:'hidden',
      boxShadow:'var(--shadow-sm)',
    }}>
      <div style={{ padding:'13px 18px', display:'flex', alignItems:'center', justifyContent:'space-between', gap:12, borderBottom:'1px solid var(--border)', background:'rgba(255,255,255,0.015)' }}>
        <div style={{ minWidth:0 }}>
          <div style={{ display:'flex', alignItems:'center', gap:10 }}>
            <h3 style={{ margin:0, fontSize:14, fontWeight:700, letterSpacing:-0.2 }}>{ex.name}</h3>
            {exGroups(ex).map(g => <MuscleBadge key={g} group={g}/>)}
          </div>
          <div style={{ fontSize:11, color:'var(--text-mute)', marginTop:3 }}>{ex.equipment}</div>
        </div>
        <div style={{ display:'flex', alignItems:'center', gap:4 }}>
          <Button variant="ghost" size="sm" onClick={onShowLastTime}><IconHistory size={12}/> Last time</Button>
          <button onClick={() => setNotesOpen(!notesOpen)} style={{
            width:28, height:28, borderRadius:6, display:'grid', placeItems:'center',
            color: notesOpen || notes ? 'var(--accent)' : 'var(--text-mute)',
            background: notesOpen ? 'color-mix(in srgb, var(--accent) 7%, transparent)' : 'transparent',
            transition:'color 0.12s, background 0.12s',
          }}><IconNote size={14}/></button>
          <button onClick={onRemove} title="Remove exercise" style={{
            width:28, height:28, borderRadius:6, display:'grid', placeItems:'center', color:'var(--text-mute)',
            transition:'color 0.12s, background 0.12s',
          }}
            onMouseEnter={e => { e.currentTarget.style.color = 'var(--danger)'; e.currentTarget.style.background = 'rgba(248,113,113,0.08)'; }}
            onMouseLeave={e => { e.currentTarget.style.color = 'var(--text-mute)'; e.currentTarget.style.background = 'transparent'; }}
          ><IconTrash size={13}/></button>
        </div>
      </div>

      {notesOpen && (
        <div style={{ padding:'10px 18px', borderBottom:'1px solid var(--border)', background:'var(--bg-2)' }}>
          <input value={notes} onChange={e => onUpdateNotes && onUpdateNotes(e.target.value)}
            placeholder="Add a note…"
            style={{ width:'100%', fontSize:12, color:'var(--text)', padding:'6px 0', background:'transparent' }}/>
        </div>
      )}

      <div style={{ display:'grid', gridTemplateColumns:'40px 1fr 1fr 1fr 1fr 36px', gap:8, padding:'10px 18px', fontSize:10, color:'var(--text-mute)', letterSpacing:0.5, textTransform:'uppercase', fontWeight:600, borderBottom:'1px solid var(--border)' }}>
        <div>Set</div><div>Weight</div><div>Reps</div><div>RIR</div><div style={{ textAlign:'right' }}>e1RM</div><div/>
      </div>

      <div>
        {(block.sets||[]).map((set, i) => (
          <SetRow key={i} setIdx={i} set={set} units={units} isPR={!!prSets?.has(i)}
            onDelete={() => onDeleteSet(i)}
            onUpdate={d => onUpdateSet(i, d)}/>
        ))}
      </div>

      <div style={{ padding:'12px 18px', borderTop:'1px solid var(--border)' }}>
        <InlineAddSet
          onLog={onLogSet}
          onUpdateSet={onUpdateSet}
          onStartTimer={onStartTimer}
          units={units}
          lastSet={(block.sets||[])[block.sets.length - 1]}
          sets={block.sets||[]}
        />
      </div>
    </div>
  );
}

function SetRow({ setIdx, set, units, isPR, onDelete }) {
  const [hover, setHover] = uS(false);
  const e1rm = set.weight && set.reps && !set.drop && !set.myo ? Math.round(epley(set.weight, set.reps)) : null;
  return (
    <div
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{
        display:'grid', gridTemplateColumns:'40px 1fr 1fr 1fr 1fr 36px',
        gap:8, padding:'10px 18px', alignItems:'center', fontSize:13,
        borderBottom:'1px solid var(--border)',
        background: set.drop ? 'var(--drop-bg)' : 'transparent',
        animation:'fade-up 0.2s ease',
      }}>
      <div style={{ color:'var(--text-mute)' }}>{set.drop ? <DropTag/> : <span className="mono" style={{ fontSize:12, fontWeight:600 }}>{setIdx+1}</span>}</div>
      <div className="mono" style={{ fontWeight:600 }}>{convertWeight(set.weight,units)}<span style={{ color:'var(--text-faint)', fontSize:11, marginLeft:3 }}>{weightLabel(units)}</span></div>
      <div className="mono" style={{ fontWeight:600 }}>{set.reps}</div>
      <div style={{ display:'flex', alignItems:'center', gap:6 }}>
        <RIRBadge rir={set.rir}/>
        {isPR && <PRBadge small/>}
      </div>
      <div className="mono" style={{ fontSize:12, color:'var(--text-dim)', textAlign:'right', display:'flex', alignItems:'center', justifyContent:'flex-end' }}>
        {e1rm && !set.drop ? convertWeight(e1rm,units) : '—'}
      </div>
      <div style={{ display:'flex', justifyContent:'flex-end' }}>
        {hover && (
          <button onClick={onDelete} style={{ width:24, height:24, borderRadius:5, color:'var(--text-mute)', display:'grid', placeItems:'center' }}
            onMouseEnter={e => { e.currentTarget.style.color = 'var(--danger)'; e.currentTarget.style.background = 'rgba(248,113,113,0.08)'; }}
            onMouseLeave={e => { e.currentTarget.style.color = 'var(--text-mute)'; e.currentTarget.style.background = 'transparent'; }}
          ><IconTrash size={12}/></button>
        )}
      </div>
    </div>
  );
}

function InlineAddSet({ onLog, onUpdateSet, onStartTimer, units, lastSet, sets }) {
  const [mode, setMode] = uS('closed'); // closed | editing | rest
  const [weight, setWeight] = uS(lastSet ? String(convertWeight(lastSet.weight, units)) : '');
  const [reps, setReps] = uS(lastSet ? String(lastSet.reps) : '');
  const [rir, setRir] = uS(lastSet?.rir != null ? String(lastSet.rir) : '');
  const [drop, setDrop] = uS(false);
  const [customRest, setCustomRest] = uS(false);
  const [customMin, setCustomMin] = uS('');
  const [customSec, setCustomSec] = uS('');
  const weightRef = uR();
  const customRef = uR();
  const lastIdxRef = uR(null);

  uE(() => {
    if (mode === 'editing') setTimeout(() => weightRef.current?.focus(), 50);
    if (customRest) setTimeout(() => customRef.current?.focus(), 50);
  }, [mode, customRest]);

  const open = (asDrop) => {
    setDrop(!!asDrop);
    setMode('editing');
    setCustomRest(false);
    if (lastSet) {
      setWeight(String(convertWeight(lastSet.weight, units)));
      setReps(String(lastSet.reps));
      setRir(lastSet.rir != null ? String(lastSet.rir) : '');
    }
  };

  const submitSet = e => {
    e?.preventDefault?.();
    const raw = weight ? (units === 'lb' ? parseFloat(weight) / 2.20462 : parseFloat(weight)) : 0;
    const setData = {
      weight: Math.round(clampWeight(raw) * 10) / 10,
      reps: clampReps(reps || 0),
      rir: clampRir(rir),
      drop, notes:'', rest:null,
    };
    onLog(setData);
    lastIdxRef.current = sets.length;
    setMode('rest');
  };

  const chooseRest = seconds => {
    if (lastIdxRef.current != null) onUpdateSet(lastIdxRef.current, { rest: seconds });
    if (seconds && seconds > 0) onStartTimer(seconds);
    setMode('closed');
    setCustomRest(false);
    setCustomMin(''); setCustomSec('');
  };

  const cancel = () => { setMode('closed'); setCustomRest(false); };

  if (mode === 'closed') {
    return (
      <div style={{ display:'flex', gap:8 }}>
        <Button size="sm" onClick={() => open(false)} icon={IconPlus}>Add set</Button>
        <Button size="sm" variant="ghost" onClick={() => open(true)}>
          <span style={{ color:'var(--drop)' }}>+ Drop set</span>
        </Button>
      </div>
    );
  }

  if (mode === 'rest') {
    if (customRest) {
      const total = (parseInt(customMin||'0')||0)*60 + (parseInt(customSec||'0')||0);
      return (
        <div style={{ display:'flex', alignItems:'center', gap:10, width:'100%', padding:'4px 0', animation:'fade-up 0.15s ease', flexWrap:'wrap' }}>
          <span style={{ fontSize:12, color:'var(--text-dim)', display:'flex', alignItems:'center', gap:6, fontWeight:600 }}><IconClock size={13}/> Custom:</span>
          <input ref={customRef} value={customMin} onChange={e => setCustomMin(e.target.value)} placeholder="0" inputMode="numeric" className="mono"
            style={{ width:48, height:32, padding:'0 6px', background:'var(--bg-2)', border:'1px solid var(--accent)', borderRadius:6, fontSize:14, fontWeight:600, textAlign:'center' }}/>
          <span style={{ fontSize:11, color:'var(--text-mute)' }}>min</span>
          <input value={customSec} onChange={e => setCustomSec(e.target.value)} placeholder="0" inputMode="numeric" className="mono"
            style={{ width:48, height:32, padding:'0 6px', background:'var(--bg-2)', border:'1px solid var(--border-strong)', borderRadius:6, fontSize:14, fontWeight:600, textAlign:'center' }}/>
          <span style={{ fontSize:11, color:'var(--text-mute)' }}>sec</span>
          <Button size="sm" variant="primary" onClick={() => chooseRest(total||0)} disabled={!total}>Start</Button>
          <Button size="sm" variant="ghost" onClick={() => setCustomRest(false)}>Back</Button>
        </div>
      );
    }
    return (
      <div style={{ display:'flex', alignItems:'center', gap:8, width:'100%', padding:'4px 0', animation:'fade-up 0.15s ease', flexWrap:'wrap' }}>
        <span style={{ fontSize:12, color:'var(--text-dim)', display:'flex', alignItems:'center', gap:6, fontWeight:600 }}><IconClock size={13}/> Rest timer?</span>
        {[{l:'1 min',s:60},{l:'2 min',s:120},{l:'3 min',s:180}].map(p => (
          <button key={p.s} onClick={() => chooseRest(p.s)} className="mono" style={{
            height:32, padding:'0 14px', borderRadius:999,
            background:'var(--surface-2)', color:'var(--text)', border:'1px solid var(--border-strong)', fontSize:12, fontWeight:600, transition:'all 0.12s',
          }}
            onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--accent)'; e.currentTarget.style.color = 'var(--accent)'; }}
            onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--border-strong)'; e.currentTarget.style.color = 'var(--text)'; }}
          >{p.l}</button>
        ))}
        <button onClick={() => setCustomRest(true)} style={{ height:32, padding:'0 14px', borderRadius:999, background:'transparent', color:'var(--text-dim)', border:'1px dashed var(--border-strong)', fontSize:12, fontWeight:600 }}>Custom…</button>
        <button onClick={() => chooseRest(0)} style={{ height:32, padding:'0 14px', borderRadius:999, background:'transparent', color:'var(--text-mute)', border:'1px solid transparent', fontSize:12, fontWeight:600 }}
          onMouseEnter={e => e.currentTarget.style.color = 'var(--text)'}
          onMouseLeave={e => e.currentTarget.style.color = 'var(--text-mute)'}
        >Skip</button>
      </div>
    );
  }

  const cellStyle = {
    width:'100%', height:36, padding:'0 10px',
    background:'var(--bg-2)', border:'1px solid var(--accent)',
    borderRadius:'var(--radius-sm)',
    fontFamily:'JetBrains Mono, monospace',
    fontSize:14, fontWeight:600,
  };
  return (
    <form onSubmit={submitSet} style={{ display:'grid', gridTemplateColumns:'40px 1fr 1fr 1fr auto', gap:8, alignItems:'center', width:'100%', padding:'4px 0', animation:'fade-up 0.15s ease' }}>
      <div style={{ fontSize:11, color:'var(--accent)', fontWeight:700 }}>{drop ? <DropTag/> : 'NEW'}</div>
      <input ref={weightRef} value={weight} onChange={e => setWeight(e.target.value)} placeholder={weightLabel(units)} style={cellStyle} inputMode="decimal"/>
      <input value={reps} onChange={e => setReps(e.target.value)} placeholder="reps" style={cellStyle} inputMode="numeric"/>
      <input value={rir} onChange={e => setRir(e.target.value)} placeholder="rir" style={cellStyle} inputMode="numeric"/>
      <div style={{ display:'flex', alignItems:'center', gap:4 }}>
        <Button size="sm" variant="primary" onClick={submitSet}><IconCheck size={13} sw={2.4}/></Button>
        <Button size="sm" variant="ghost" onClick={cancel}><IconX size={13}/></Button>
      </div>
    </form>
  );
}

function LastTimePanel({ exId, sessions, units, onClose }) {
  const ex = exById(exId);
  // A session may contain the exercise more than once (duplicate blocks) — merge them.
  const setsFor = s => (s.exercises||[]).filter(e => e.exId === exId).flatMap(e => e.sets||[]);
  // Most recent first by DATE — the array is insertion-ordered (logging an old session prepends it).
  const pastSessions = sessions
    .filter(s => (s.exercises||[]).some(e => e.exId === exId))
    .sort((a,b) => new Date(b.startedAt||b.date||0) - new Date(a.startedAt||a.date||0))
    .slice(0, 5);

  // Dynamic PR map — same logic as mobile lastPanelPRMap
  const prMap = (() => {
    const allForEx = sessions
      .filter(s => (s.exercises||[]).some(e => e.exId === exId))
      .sort((a,b) => new Date(a.startedAt||a.date) - new Date(b.startedAt||b.date));
    const map = {};
    let best = 0;
    allForEx.forEach(s => {
      const prSet = new Set();
      setsFor(s).forEach((set, i) => {
        if (set.drop || set.myo || !set.weight || !set.reps) return;
        const e = epley(set.weight, set.reps);
        if (e > best) { best = e; prSet.add(i); }
      });
      map[s.id] = prSet;
    });
    return map;
  })();

  return (
    <div onClick={onClose} style={{ position:'fixed', inset:0, background:'rgba(6,8,13,0.5)', zIndex:80, animation:'fade-up 0.15s ease' }}>
      <div onClick={e => e.stopPropagation()} style={{
        position:'absolute', right:0, top:0, bottom:0, width:400, maxWidth:'92vw',
        background:'var(--surface)', borderLeft:'1px solid var(--border-strong)',
        padding:24, display:'flex', flexDirection:'column', gap:0,
        animation:'slide-in-right 0.25s ease', overflowY:'auto',
      }}>
        <div style={{ display:'flex', alignItems:'flex-start', justifyContent:'space-between', marginBottom:22 }}>
          <div>
            <div style={{ fontSize:11, color:'var(--text-mute)', letterSpacing:0.4, textTransform:'uppercase', fontWeight:600 }}>Last time</div>
            <h3 style={{ margin:'6px 0 0', fontSize:18, fontWeight:700 }}>{ex.name}</h3>
          </div>
          <button onClick={onClose} style={{ color:'var(--text-mute)' }}><IconX/></button>
        </div>
        {pastSessions.length === 0
          ? <div style={{ color:'var(--text-mute)', fontSize:13, textAlign:'center', paddingTop:32 }}>No previous data.</div>
          : pastSessions.map(sess => {
            return (
              <div key={sess.id} style={{ marginBottom:20 }}>
                <div style={{ fontSize:11, fontWeight:700, color:'var(--text-dim)', marginBottom:8, display:'flex', alignItems:'center', gap:6 }}>
                  <span>{formatDate(sess.startedAt||sess.date, { relative:true, time:true, todayOnly:true })}</span>
                  <span style={{ color:'var(--border-strong)' }}>·</span>
                  <span style={{ color:'var(--text-mute)' }}>{sess.name}</span>
                </div>
                <div style={{ display:'flex', flexDirection:'column', gap:5 }}>
                  {setsFor(sess).map((s, i) => (
                    <div key={i} style={{
                      display:'grid', gridTemplateColumns:'28px 1fr 1fr 80px', gap:10, padding:'9px 12px',
                      background: s.drop ? 'var(--drop-bg)' : 'var(--bg-2)',
                      border:'1px solid var(--border)', borderRadius:8, fontSize:13, alignItems:'center',
                    }}>
                      <div className="mono" style={{ color:'var(--text-mute)', fontSize:11 }}>{s.drop ? <DropTag/> : i+1}</div>
                      <div className="mono" style={{ fontWeight:600 }}>{convertWeight(s.weight,units)}<span style={{ color:'var(--text-faint)', fontSize:11, marginLeft:3 }}>{weightLabel(units)}</span></div>
                      <div className="mono" style={{ fontWeight:600 }}>{s.reps}<span style={{ color:'var(--text-faint)', fontSize:11, marginLeft:3 }}>reps</span></div>
                      <div style={{ display:'flex', alignItems:'center', gap:6 }}>
                        <RIRBadge rir={s.rir}/>
                        {prMap[sess.id]?.has(i) && <PRBadge small/>}
                      </div>
                    </div>
                  ))}
                </div>
              </div>
            );
          })
        }
      </div>
    </div>
  );
}

function AddExerciseModal({ library, onClose, onPick, addedIds, sessionName, gymFilter, onAddToLibrary }) {
  const [q, setQ] = uS('');
  const [group, setGroup] = uS('All');
  const [creating, setCreating] = uS(false);
  // Already-added exercises stay listed (tagged "Added") — picking one adds a second
  // block, e.g. to come back for more sets later in the session.
  const filtered = (library||[]).filter(e =>
    (!gymFilter || !e.gym || e.gym === gymFilter) &&
    (group === 'All' || exGroups(e).includes(group)) &&
    e.name.toLowerCase().includes(q.toLowerCase())
  );

  const handleCreate = data => {
    const id = generateId();
    onAddToLibrary({ id, ...data });
    onPick(id);
  };

  return (
    <Modal open onClose={onClose} title={creating ? 'New exercise' : 'Add exercise'} width={520}>
      {creating ? (
        <>
          <button onClick={() => setCreating(false)} style={{ fontSize:12, color:'var(--text-mute)', marginBottom:14, display:'flex', alignItems:'center', gap:4 }}>
            ← Back to library
          </button>
          <ExerciseFormModal embedded onSave={handleCreate} onClose={() => setCreating(false)}/>
        </>
      ) : (
        <>
          <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:12, padding:'8px 12px', background:'var(--bg-2)', borderRadius:8, border:'1px solid var(--border)' }}>
            <IconSearch size={14} stroke="var(--text-mute)"/>
            <input value={q} onChange={e => setQ(e.target.value)} placeholder="Search library…" autoFocus style={{ flex:1, fontSize:13 }}/>
          </div>
          <div style={{ display:'flex', flexWrap:'wrap', gap:6, marginBottom:14 }}>
            {['All', ...muscleGroupOrder(sessionName)].map(g => (
              <button key={g} onClick={() => setGroup(g)} style={{
                fontSize:11, padding:'4px 10px', borderRadius:999, fontWeight:600,
                color: group===g ? 'var(--accent)' : 'var(--text-dim)',
                background: group===g ? 'color-mix(in srgb, var(--accent) 8%, transparent)' : 'var(--surface-2)',
                border:'1px solid ' + (group===g ? 'color-mix(in srgb, var(--accent) 20%, transparent)' : 'var(--border)'),
              }}>{g}</button>
            ))}
          </div>
          <div style={{ maxHeight:280, overflowY:'auto', display:'flex', flexDirection:'column', gap:4 }}>
            {filtered.map(ex => (
              <button key={ex.id} onClick={() => onPick(ex.id)} style={{
                display:'flex', alignItems:'center', justifyContent:'space-between',
                padding:12, borderRadius:8, background:'var(--bg-2)', border:'1px solid var(--border)', textAlign:'left',
              }}
                onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--accent)'; e.currentTarget.style.background = 'var(--surface-2)'; }}
                onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--border)'; e.currentTarget.style.background = 'var(--bg-2)'; }}
              >
                <div>
                  <div style={{ display:'flex', alignItems:'center', gap:8 }}>
                    <span style={{ fontWeight:600, fontSize:13 }}>{ex.name}</span>
                    {addedIds?.includes(ex.id) && (
                      <span style={{ fontSize:9, fontWeight:700, letterSpacing:0.4, textTransform:'uppercase', padding:'2px 6px', borderRadius:4, color:'var(--accent)', background:'color-mix(in srgb, var(--accent) 8%, transparent)', border:'1px solid color-mix(in srgb, var(--accent) 20%, transparent)', flexShrink:0 }}>Added</span>
                    )}
                  </div>
                  <div style={{ fontSize:11, color:'var(--text-mute)', marginTop:2 }}>{ex.equipment}</div>
                </div>
                {exGroups(ex).map(g => <MuscleBadge key={g} group={g}/>)}
              </button>
            ))}
            {filtered.length === 0 && <div style={{ padding:24, textAlign:'center', fontSize:13, color:'var(--text-mute)' }}>No exercises match.</div>}
          </div>
          {onAddToLibrary && (
            <button onClick={() => setCreating(true)} style={{
              width:'100%', marginTop:12, padding:'10px 0', borderRadius:8, fontSize:13, fontWeight:600,
              color:'var(--accent)', border:'1px dashed color-mix(in srgb, var(--accent) 35%, transparent)', background:'color-mix(in srgb, var(--accent) 4%, transparent)',
            }}>+ Create new exercise</button>
          )}
        </>
      )}
    </Modal>
  );
}

/* ============================================================
   HISTORY
   ============================================================ */
function HistoryPage({ units, sessions, library, onDeleteSession, onUpsertSession, gymFilter, openSessionId, openInViewMode, onAddToLibrary }) {
  const [filter, setFilter] = uS('All');
  const [sortAsc, setSortAsc] = uS(false);
  const [modalOpen, setModalOpen] = uS(false);
  const [editingSession, setEditingSession] = uS(null);
  const [modalViewMode, setModalViewMode] = uS(false);
  const [compareMode, setCompareMode] = uS(false);
  const [selectedIds, setSelectedIds] = uS([]);
  const [compareOpen, setCompareOpen] = uS(false);

  const toggleCompareMode = () => { setCompareMode(m => !m); setSelectedIds([]); setCompareOpen(false); };
  const toggleSelect = id => setSelectedIds(prev => {
    if (prev.includes(id)) return prev.filter(x => x !== id);
    if (prev.length >= 2) return [prev[1], id];
    return [...prev, id];
  });
  const selectedSessions = selectedIds.map(id => sessions.find(s => s.id === id)).filter(Boolean);
  const canCompare = selectedSessions.length === 2 && selectedSessions[0].name === selectedSessions[1].name;
  const typeMismatch = selectedSessions.length === 2 && selectedSessions[0].name !== selectedSessions[1].name;

  uE(() => {
    if (openSessionId) {
      const s = sessions.find(x => x.id === openSessionId);
      if (s) { setEditingSession(s); setModalViewMode(openInViewMode || false); setModalOpen(true); }
    }
  }, [openSessionId]);

  const prSessionIds = uM(() => {
    const sorted = [...sessions].sort((a,b) => new Date(a.startedAt||a.date) - new Date(b.startedAt||b.date));
    const best = {}, ids = new Set();
    sorted.forEach(s => {
      (s.exercises||[]).forEach(blk => (blk.sets||[]).forEach(set => {
        if (set.drop || set.myo || !set.weight || !set.reps) return;
        const e = epley(set.weight, set.reps);
        if (!best[blk.exId] || e > best[blk.exId]) { best[blk.exId] = e; ids.add(s.id); }
      }));
    });
    return ids;
  }, [sessions]);

  const filtered = [...sessions]
    .filter(s => filter === 'All' || s.name === filter)
    .sort((a, b) => {
      const da = new Date(a.startedAt||a.date||0), db = new Date(b.startedAt||b.date||0);
      return sortAsc ? da - db : db - da;
    });
  const grouped = {};
  filtered.forEach(s => {
    const d = new Date(s.startedAt || s.date || 0);
    const key = d.toLocaleDateString('en-US', { month:'long', year:'numeric' });
    grouped[key] = grouped[key] || [];
    grouped[key].push(s);
  });


  const openView = s => { setEditingSession(s); setModalViewMode(true); setModalOpen(true); };
  const openNew  = ()  => { setEditingSession(null); setModalViewMode(false); setModalOpen(true); };

  return (
    <div style={{ padding:28, display:'flex', flexDirection:'column', gap:20 }}>
      <div style={{ display:'flex', alignItems:'flex-end', justifyContent:'space-between', flexWrap:'wrap', gap:12 }}>
        <div>
          <h1 style={{ margin:0, fontSize:26, fontWeight:800, letterSpacing:-0.5 }}>History<span style={{ color:'var(--accent)' }}>.</span></h1>
          <div style={{ color:'var(--text-dim)', fontSize:14, marginTop:6 }}>{sessions.length} sessions logged</div>
        </div>
        <div style={{ display:'flex', gap:8 }}>
          <button onClick={toggleCompareMode} style={{
            padding:'9px 16px', fontSize:12, fontWeight:600, borderRadius:8,
            color: compareMode ? '#06080d' : 'var(--text-dim)',
            background: compareMode ? 'var(--accent)' : 'var(--surface)',
            border:'1px solid ' + (compareMode ? 'var(--accent)' : 'var(--border)'),
            display:'flex', alignItems:'center', gap:6,
          }}>
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/>
            </svg>
            {compareMode ? 'Cancel compare' : 'Compare'}
          </button>
          <Button variant="default" onClick={openNew} icon={IconCalendar}>Log past session</Button>
        </div>
      </div>

      <div style={{ display:'flex', alignItems:'center', gap:8, flexWrap:'wrap' }}>
        {['All', ...SESSION_TYPES].map(f => (
          <button key={f} onClick={() => setFilter(f)} style={{
            padding:'8px 14px', fontSize:12, fontWeight:600, borderRadius:999,
            color: filter===f ? 'var(--accent)' : 'var(--text-dim)',
            background: filter===f ? 'color-mix(in srgb, var(--accent) 8%, transparent)' : 'var(--surface)',
            border:'1px solid ' + (filter===f ? 'color-mix(in srgb, var(--accent) 20%, transparent)' : 'var(--border)'),
          }}>{f}</button>
        ))}
        <button onClick={() => setSortAsc(v => !v)} style={{
          marginLeft:'auto', padding:'8px 14px', fontSize:12, fontWeight:600, borderRadius:999,
          color:'var(--text-dim)', background:'var(--surface)', border:'1px solid var(--border)',
          display:'flex', alignItems:'center', gap:5,
        }}>{sortAsc ? '↑ Oldest first' : '↓ Newest first'}</button>
      </div>

      {compareMode && (
        <div style={{
          display:'flex', alignItems:'center', gap:12, padding:'12px 16px',
          background: typeMismatch ? 'rgba(239,68,68,0.06)' : canCompare ? 'color-mix(in srgb, var(--accent) 6%, transparent)' : 'var(--surface)',
          border:'1px solid ' + (typeMismatch ? 'rgba(239,68,68,0.25)' : canCompare ? 'color-mix(in srgb, var(--accent) 25%, transparent)' : 'var(--border)'),
          borderRadius:'var(--radius)',
        }}>
          <div style={{ flex:1, fontSize:13, color: typeMismatch ? 'var(--danger)' : 'var(--text-dim)' }}>
            {selectedIds.length === 0 && 'Select 2 sessions of the same type to compare'}
            {selectedIds.length === 1 && 'Select one more session of the same type'}
            {typeMismatch && `Can't compare: ${selectedSessions[0].name} vs ${selectedSessions[1].name} — pick sessions of the same type`}
            {canCompare && `Ready to compare 2 ${selectedSessions[0].name} sessions`}
          </div>
          {selectedIds.length > 0 && (
            <button onClick={() => setSelectedIds([])} style={{ fontSize:12, color:'var(--text-mute)', background:'none', border:'none', cursor:'pointer' }}>
              Clear
            </button>
          )}
          {canCompare && (
            <button onClick={() => setCompareOpen(true)} style={{
              padding:'8px 18px', fontSize:12, fontWeight:700, borderRadius:7,
              background:'var(--accent)', color:'#06080d', border:'none', cursor:'pointer',
            }}>
              Compare sessions ›
            </button>
          )}
        </div>
      )}

      {sessions.length === 0 ? (
        <Card style={{ textAlign:'center', padding:48 }}>
          <div style={{ fontSize:16, fontWeight:700, marginBottom:8 }}>No sessions yet</div>
          <div style={{ color:'var(--text-dim)', fontSize:14 }}>Complete a workout or log a past session to see it here.</div>
        </Card>
      ) : (
        <div style={{ display:'flex', flexDirection:'column', gap:24 }}>
          {Object.entries(grouped).map(([month, list]) => (
            <div key={month}>
              <div style={{ fontSize:11, color:'var(--text-mute)', letterSpacing:0.6, textTransform:'uppercase', fontWeight:700, marginBottom:10, display:'flex', alignItems:'center', gap:10 }}>
                {month}
                <div style={{ flex:1, height:1, background:'var(--border)' }}/>
                <span className="mono">{list.length} sessions</span>
              </div>
              <div style={{ display:'flex', flexDirection:'column', gap:6 }}>
                {list.map(s => {
                  const hasPRs = prSessionIds.has(s.id);
                  const isSelected = selectedIds.includes(s.id);
                  const breakdown = Object.entries(
                    (s.exercises||[]).reduce((acc, blk) => {
                      const ex = exById(blk.exId);
                      const n = (blk.sets||[]).filter(x => !x.drop).length;
                      if (!n) return acc;
                      exGroups(ex).forEach(g => { acc[g] = (acc[g]||0) + n; });
                      exSecondary(ex).forEach(g => { acc[g] = (acc[g]||0) + n * 0.5; });
                      return acc;
                    }, {})
                  ).sort((a, b) => b[1] - a[1]);
                  const borderCol = isSelected ? 'color-mix(in srgb, var(--accent) 55%, transparent)' : 'var(--border)';
                  const bgCol = isSelected ? 'color-mix(in srgb, var(--accent) 5%, transparent)' : 'linear-gradient(160deg, rgba(255,255,255,0.02) 0%, transparent 50%), var(--surface)';
                  return (
                    <button key={s.id}
                      onClick={() => compareMode ? toggleSelect(s.id) : openView(s)}
                      style={{
                        width:'100%', padding:'13px 18px',
                        display:'grid',
                        gridTemplateColumns: compareMode ? '28px 70px 110px 1fr auto auto' : '70px 110px 1fr auto auto',
                        alignItems:'center', gap:16, textAlign:'left',
                        background: bgCol,
                        border:'1px solid ' + borderCol,
                        borderRadius:'var(--radius)',
                        boxShadow: isSelected ? '0 0 0 1px color-mix(in srgb, var(--accent) 20%, transparent)' : 'var(--shadow-sm)',
                        transition:'background 0.12s, border-color 0.12s',
                      }}
                      onMouseEnter={e => { if (!isSelected) { e.currentTarget.style.background='rgba(255,255,255,0.045)'; e.currentTarget.style.borderColor='var(--border-strong)'; }}}
                      onMouseLeave={e => { if (!isSelected) { e.currentTarget.style.background=bgCol; e.currentTarget.style.borderColor=borderCol; }}}
                    >
                      {compareMode && (
                        <div style={{
                          width:20, height:20, borderRadius:999, flexShrink:0,
                          border: isSelected ? 'none' : '2px solid var(--border-strong)',
                          background: isSelected ? 'var(--accent)' : 'transparent',
                          display:'grid', placeItems:'center',
                        }}>
                          {isSelected && <IconCheck size={11} style={{ color:'#06080d' }}/>}
                        </div>
                      )}
                      <div style={{ width:50, height:50, borderRadius:10, display:'grid', placeItems:'center', background:'var(--bg-2)', border:'1px solid var(--border)' }}>
                        <div className="mono" style={{ fontSize:16, fontWeight:700, lineHeight:1 }}>{new Date(s.startedAt||s.date||0).getDate()}</div>
                        <div style={{ fontSize:9, color:'var(--text-mute)', marginTop:2 }}>{new Date(s.startedAt||s.date||0).toLocaleDateString('en-US',{month:'short'}).toUpperCase()}</div>
                      </div>
                      <div>
                        <div style={{ fontWeight:700, fontSize:14 }}>{s.name}</div>
                        <div style={{ fontSize:11, color:'var(--text-mute)', marginTop:2 }}>{(() => { const d=new Date(s.startedAt||s.date||0); return d.toLocaleDateString('en-US',{weekday:'short'}) + ' · ' + d.getHours().toString().padStart(2,'0') + ':' + d.getMinutes().toString().padStart(2,'0'); })()}</div>
                      </div>
                      <div style={{ fontSize:12, color:'var(--text-dim)', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>
                        {breakdown.slice(0,5).map(([g,n]) => `${g}×${fmtVol(n)}`).join(' · ')}
                      </div>
                      <div>{hasPRs && <PRBadge small/>}</div>
                      <div className="mono" style={{ fontSize:12, color:'var(--text)', fontWeight:600, textAlign:'right', minWidth:56 }}>
                        {totalSets(s)}<span style={{ color:'var(--text-faint)', fontSize:11 }}>sets</span>
                      </div>
                    </button>
                  );
                })}
              </div>
            </div>
          ))}
        </div>
      )}

      {modalOpen && (
        <PastSessionModal
          library={library}
          units={units}
          sessions={sessions}
          existing={editingSession}
          initialViewMode={modalViewMode}
          onClose={() => { setModalOpen(false); setEditingSession(null); }}
          onSave={s => { onUpsertSession(s); setModalOpen(false); setEditingSession(null); }}
          onDelete={editingSession ? () => { onDeleteSession(editingSession.id); setModalOpen(false); setEditingSession(null); } : undefined}
          gymFilter={gymFilter}
          onAddToLibrary={onAddToLibrary}
        />
      )}
      {compareOpen && canCompare && (
        <CompareSessionModal
          sessA={selectedSessions[0]}
          sessB={selectedSessions[1]}
          units={units}
          onClose={() => setCompareOpen(false)}
        />
      )}
    </div>
  );
}

function buildCompareData(sess1, sess2, units) {
  // Merge duplicate blocks of the same exercise within a session
  const getSets = (sess, exId) => (sess.exercises||[]).filter(b => b.exId === exId).flatMap(b => b.sets||[]);
  const sessionVol = sess => (sess.exercises||[]).reduce((sum, blk) =>
    sum + (blk.sets||[]).filter(x => !x.drop && x.weight && x.reps)
            .reduce((s, x) => s + convertWeight(x.weight, units) * x.reps, 0), 0);
  const exCount = sess => (sess.exercises||[]).filter(b => (b.sets||[]).some(s => !s.drop)).length;

  const allExIds = [...new Set([
    ...(sess1.exercises||[]).map(b => b.exId),
    ...(sess2.exercises||[]).map(b => b.exId),
  ])];

  // Total Δ e1RM: sum of best e1RM per exercise, for exercises present in BOTH sessions
  let sum1e1rm = 0, sum2e1rm = 0;
  allExIds.forEach(exId => {
    const s1 = getSets(sess1, exId);
    const s2 = getSets(sess2, exId);
    if (!s1.length || !s2.length) return;
    const bestE = sets => sets.filter(s => !s.drop && !s.myo && s.weight && s.reps)
      .reduce((b, s) => Math.max(b, epley(s.weight, s.reps)), 0);
    sum1e1rm += bestE(s1);
    sum2e1rm += bestE(s2);
  });

  const byMuscle = {};
  allExIds.forEach(id => {
    const ex = exById(id);
    const gs = exGroups(ex).length ? exGroups(ex) : ['Other'];
    gs.forEach(g => {
      if (!byMuscle[g]) byMuscle[g] = [];
      if (!byMuscle[g].includes(id)) byMuscle[g].push(id);
    });
  });
  const order = [...MUSCLE_GROUPS, 'Other'];
  const groupedExercises = order.filter(g => byMuscle[g]).map(g => [g, byMuscle[g]]);

  return {
    vol1: Math.round(sessionVol(sess1)), vol2: Math.round(sessionVol(sess2)),
    sum1e1rm: Math.round(convertWeight(sum1e1rm, units)),
    sum2e1rm: Math.round(convertWeight(sum2e1rm, units)),
    exCount1: exCount(sess1), exCount2: exCount(sess2),
    getSets, groupedExercises,
  };
}

function CompareBody({ sess1, sess2, units, COLS, compact }) {
  const d = uM(() => buildCompareData(sess1, sess2, units), [sess1, sess2, units]);
  const sc = { Push:'#f87171', Pull:'#60a5fa', Legs:'#a78bfa' }[sess1.name] || 'var(--accent)';
  const d1Label = formatDate(sess1.startedAt||sess1.date, {});
  const d2Label = formatDate(sess2.startedAt||sess2.date, {});
  const wl = weightLabel(units);
  const fs = compact ? 12 : 14;

  const overviewCols = compact ? '1fr 1fr 1fr 52px' : '1fr 1fr 1fr 64px';
  const highlightBg = 'color-mix(in srgb, var(--accent) 4%, transparent)';

  return (
    <div>
      {/* Session header cards */}
      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:8, marginBottom:compact?14:20 }}>
        {[[sess1,'Session 1'],[sess2,'Session 2']].map(([s, label], i) => (
          <div key={i} style={{ background:'var(--bg-2)', border:'1px solid var(--border)', borderRadius:10, padding: compact?'10px 12px':'14px 16px', position:'relative', overflow:'hidden' }}>
            <div style={{ position:'absolute', top:0, left:0, right:0, height:3, background:sc }}/>
            <div style={{ fontSize:9, fontWeight:700, color:'var(--text-mute)', textTransform:'uppercase', letterSpacing:0.7, marginBottom:3 }}>{label} · {s.name}</div>
            <div style={{ fontWeight:800, fontSize:compact?13:16, letterSpacing:-0.3, marginBottom:compact?3:5 }}>{formatDate(s.startedAt||s.date, { time:true })}</div>
            <div style={{ display:'flex', gap:compact?8:12, fontSize:compact?10:12, color:'var(--text-dim)', flexWrap:'wrap' }}>
              <span className="mono">{totalSets(s)} sets</span>
              <span>{s.durationMin||'?'} min</span>
            </div>
          </div>
        ))}
      </div>

      {/* Overview */}
      <div style={{ marginBottom:compact?14:24 }}>
        <div style={{ fontSize:9, fontWeight:700, textTransform:'uppercase', letterSpacing:0.7, color:'var(--text-mute)', marginBottom:8 }}>Overview</div>
        <div style={{ border:'1px solid var(--border)', borderRadius:10, overflow:'hidden' }}>
          <div style={{ display:'grid', gridTemplateColumns:overviewCols, gap:0, padding:'7px 14px', borderBottom:'2px solid var(--border)', background:'rgba(255,255,255,0.018)' }}>
            <div style={{ fontSize:9, color:'var(--text-faint)', fontWeight:700, textTransform:'uppercase', letterSpacing:0.5 }}>Metric</div>
            <div style={{ fontSize:compact?10:11, fontWeight:700, color:'var(--text-dim)' }}>{d1Label}</div>
            <div style={{ fontSize:compact?10:11, fontWeight:700, color:'var(--text-dim)' }}>{d2Label}</div>
            <div style={{ fontSize:9, color:'var(--text-faint)', fontWeight:700, textTransform:'uppercase', textAlign:'right' }}>Δ</div>
          </div>
          {[
            { label:'Volume',       vA:d.vol1,       vB:d.vol2,       unit:wl,    up:true,  highlight:true  },
            { label:'Total Δ e1RM', vA:d.sum1e1rm,   vB:d.sum2e1rm,   unit:wl,    up:true,  highlight:true  },
            { label:'Exercises',    vA:d.exCount1,   vB:d.exCount2,   unit:'',    up:true,  highlight:false },
            { label:'Duration',     vA:sess1.durationMin||0, vB:sess2.durationMin||0, unit:'min', up:false, highlight:false },
          ].map(({ label, vA, vB, unit, up, highlight }) => {
            const diff = vB - vA;
            const pct = vA > 0 ? Math.round(Math.abs(diff)/vA*100) : null;
            const better = up ? diff>0 : diff<0;
            const worse  = up ? diff<0 : diff>0;
            const dc = better ? 'var(--accent)' : worse ? 'var(--danger)' : 'var(--text-mute)';
            const sign = diff>0?'+':diff<0?'-':'';
            const dt = diff===0 ? '=' : `${sign}${pct!==null ? pct+'%' : Math.abs(diff)}`;
            const fs = compact ? 12 : 14;
            const numFs = highlight ? (compact?16:18) : fs;
            return (
              <div key={label} style={{ display:'grid', gridTemplateColumns:overviewCols, gap:0, padding:highlight?'12px 14px':'9px 14px', borderBottom:'1px solid var(--border)', alignItems:'center', background:highlight?highlightBg:'transparent', position:'relative' }}>
                {highlight && <div style={{ position:'absolute', left:0, top:0, bottom:0, width:3, background:'var(--accent)', opacity:0.7 }}/>}
                <div style={{ fontSize:highlight?12:11, fontWeight:highlight?700:600, color:highlight?'var(--text)':'var(--text-dim)', paddingLeft:highlight?10:0 }}>{label}</div>
                <div className="mono" style={{ fontSize:numFs, fontWeight:800 }}>{vA}{unit&&<span style={{ fontSize:compact?9:10, color:'var(--text-mute)', marginLeft:2 }}>{unit}</span>}</div>
                <div className="mono" style={{ fontSize:numFs, fontWeight:800 }}>{vB}{unit&&<span style={{ fontSize:compact?9:10, color:'var(--text-mute)', marginLeft:2 }}>{unit}</span>}</div>
                <div className="mono" style={{ fontSize:highlight?13:11, fontWeight:800, color:dc, textAlign:'right' }}>{dt}</div>
              </div>
            );
          })}
        </div>
      </div>

      {/* Sets by exercise, grouped by muscle */}
      <div style={{ fontSize:9, fontWeight:700, textTransform:'uppercase', letterSpacing:0.7, color:'var(--text-mute)', marginBottom:8 }}>Sets by Exercise</div>
      <div style={{ display:'flex', flexDirection:'column', gap:compact?10:16 }}>
        {d.groupedExercises.map(([group, exIds]) => (
          <div key={group}>
            <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:6 }}>
              <div style={{ width:7, height:7, borderRadius:2, background:muscleColor(group), flexShrink:0 }}/>
              <span style={{ fontSize:9, fontWeight:800, textTransform:'uppercase', letterSpacing:0.8, color:'var(--text-dim)' }}>{group}</span>
              <div style={{ flex:1, height:1, background:'var(--border)' }}/>
            </div>
            <div style={{ display:'flex', flexDirection:'column', gap:5 }}>
              {exIds.map(exId => {
                const ex = exById(exId);
                const sets1 = d.getSets(sess1, exId);
                const sets2 = d.getSets(sess2, exId);
                const maxRows = Math.max(sets1.length, sets2.length);
                const onlyIn = sets1.length === 0 ? '2' : sets2.length === 0 ? '1' : null;
                return (
                  <div key={exId} style={{ border:'1px solid var(--border)', borderRadius:7, overflow:'hidden' }}>
                    <div style={{ padding: compact?'6px 12px':'8px 14px', borderBottom:'1px solid var(--border)', background:'rgba(255,255,255,0.02)', display:'flex', alignItems:'center', gap:8 }}>
                      <span style={{ fontWeight:700, fontSize:compact?12:13, flex:1 }}>{ex.name}</span>
                      {onlyIn && <span style={{ fontSize:9, color:'var(--text-faint)', fontWeight:600 }}>session {onlyIn} only</span>}
                    </div>
                    <div style={{ display:'grid', gridTemplateColumns:COLS, gap:compact?4:8, padding: compact?'4px 12px':'5px 14px', fontSize:9, fontWeight:700, color:'var(--text-faint)', textTransform:'uppercase', letterSpacing:0.4, borderBottom:'1px solid var(--border)' }}>
                      <div>#</div><div>{d1Label}</div><div>{d2Label}</div><div style={{ textAlign:'right' }}>Δ e1RM</div>
                    </div>
                    {Array.from({ length: maxRows }).map((_, i) => {
                      const s1 = sets1[i], s2 = sets2[i];
                      const e1 = s1 && !s1.drop && !s1.myo && s1.weight && s1.reps ? Math.round(convertWeight(epley(s1.weight,s1.reps),units)) : null;
                      const e2 = s2 && !s2.drop && !s2.myo && s2.weight && s2.reps ? Math.round(convertWeight(epley(s2.weight,s2.reps),units)) : null;
                      const dlt = e1!==null && e2!==null ? e2-e1 : null;
                      const dc = dlt===null ? 'var(--text-faint)' : dlt>0 ? 'var(--accent)' : dlt<0 ? 'var(--danger)' : 'var(--text-mute)';
                      const isDrop = s1?.drop||s2?.drop;
                      const setW = compact?'10px 12px':'7px 14px';
                      return (
                        <div key={i} style={{ display:'grid', gridTemplateColumns:COLS, gap:compact?4:8, padding:setW, alignItems:'center', borderBottom: i<maxRows-1 ? '1px solid rgba(255,255,255,0.04)' : 'none', background: isDrop ? 'rgba(251,146,60,0.04)' : i%2===1 ? 'rgba(255,255,255,0.012)' : 'transparent' }}>
                          <span className="mono" style={{ color:'var(--text-mute)', fontSize:10 }}>{isDrop?'D':i+1}</span>
                          <div>
                            {s1 ? <span className="mono" style={{ fontSize:compact?11:13, fontWeight:600 }}>
                              {Math.round(convertWeight(s1.weight,units)*10)/10}<span style={{ fontSize:9, color:'var(--text-faint)', marginLeft:1 }}>{wl}</span>
                              <span style={{ color:'var(--text-mute)', margin:'0 3px' }}>×</span>{s1.reps}
                            </span> : <span style={{ color:'var(--text-faint)', fontSize:11 }}>—</span>}
                          </div>
                          <div>
                            {s2 ? <span className="mono" style={{ fontSize:compact?11:13, fontWeight:600 }}>
                              {Math.round(convertWeight(s2.weight,units)*10)/10}<span style={{ fontSize:9, color:'var(--text-faint)', marginLeft:1 }}>{wl}</span>
                              <span style={{ color:'var(--text-mute)', margin:'0 3px' }}>×</span>{s2.reps}
                            </span> : <span style={{ color:'var(--text-faint)', fontSize:11 }}>—</span>}
                          </div>
                          <div style={{ textAlign:'right' }}>
                            <span className="mono" style={{ fontSize:10, fontWeight:700, color:dc }}>
                              {dlt===null ? '—' : `${dlt>0?'+':dlt<0?'-':''}${Math.abs(dlt)}`}
                              {dlt!==null && <span style={{ fontSize:8, marginLeft:1 }}>{wl}</span>}
                            </span>
                          </div>
                        </div>
                      );
                    })}
                  </div>
                );
              })}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function CompareSessionModal({ sessA, sessB, units, onClose }) {
  const [sess1, sess2] = new Date(sessA.startedAt||sessA.date) <= new Date(sessB.startedAt||sessB.date)
    ? [sessA, sessB] : [sessB, sessA];
  return (
    <Modal open onClose={onClose} title={`${sess1.name} · Session Comparison`} width={920}>
      <CompareBody sess1={sess1} sess2={sess2} units={units} COLS="26px 1fr 1fr 62px" compact={false}/>
    </Modal>
  );
}

function PastSessionModal({ library, units, sessions, existing, onClose, onSave, onDelete, gymFilter, onAddToLibrary, initialViewMode = false }) {
  const today = localDateStr(new Date());
  const [viewMode, setViewMode] = uS(initialViewMode);
  const [name, setName] = uS(existing?.name || SESSION_TYPES[0]);
  const [customName, setCustomName] = uS(!SESSION_TYPES.includes(existing?.name) ? existing?.name||'' : '');
  const [date, setDate] = uS(existing ? localDateStr(existing.startedAt||existing.date||0) : today);
  const [time, setTime] = uS(() => { const d = new Date(existing ? (existing.startedAt||existing.date||0) : Date.now()); return d.getHours().toString().padStart(2,'0') + ':' + d.getMinutes().toString().padStart(2,'0'); });
  const [duration, setDuration] = uS(String(existing?.durationMin||60));
  const [exercises, setExercises] = uS(existing?.exercises || []);
  const [addExOpen, setAddExOpen] = uS(false);

  const isCustom = !SESSION_TYPES.includes(name);
  const finalName = isCustom ? (customName.trim() || name) : name;

  const updateExSets = (idx, sets) => setExercises(prev => prev.map((e,i) => i===idx ? {...e,sets} : e));
  const removeEx = idx => setExercises(prev => prev.filter((_,i) => i!==idx));

  // Dynamic set-level PR badges (same logic as mobile LogPastSession) — the stored
  // set.isPR flag may be missing on older sessions or stale after edits.
  const sessionPRs = uM(() => {
    if (!existing || !sessions) return new Set();
    const sessionDate = new Date(existing.startedAt || existing.date || 0);
    const best = {};
    sessions.filter(s => s.id !== existing.id && new Date(s.startedAt||s.date) < sessionDate)
      .forEach(s => (s.exercises||[]).forEach(blk => (blk.sets||[]).forEach(set => {
        if (set.drop || set.myo || !set.weight || !set.reps) return;
        const e = epley(set.weight, set.reps);
        if (!best[blk.exId] || e > best[blk.exId]) best[blk.exId] = e;
      })));
    const curBest = {...best}, keys = new Set();
    exercises.forEach((blk, bi) => (blk.sets||[]).forEach((set, si) => {
      if (set.drop || set.myo || !set.weight || !set.reps) return;
      const e = epley(set.weight, set.reps);
      if (!curBest[blk.exId] || e > curBest[blk.exId]) { keys.add(bi + '-' + si); curBest[blk.exId] = e; }
    }));
    return keys;
  }, [exercises, existing, sessions]);

  const save = async () => {
    if (!totalSets({ exercises })) {
      await window.uiAlert('Add at least one exercise with a set.', { title:'Nothing logged' });
      return;
    }
    const start = new Date(date + 'T' + (time || '12:00') + ':00');
    const durationMin = parseInt(duration)||60;
    const startedAt = start.toISOString();
    onSave({
      ...(existing || {}), // keep feedback, session notes, and any mobile-only fields
      id: existing?.id || generateId(),
      name: finalName,
      startedAt, date: startedAt,
      endedAt: new Date(start.getTime() + durationMin * 60000).toISOString(),
      durationMin,
      exercises,
    });
  };

  const modalTitle = viewMode ? (existing?.name || 'Session') : (existing ? 'Edit session' : 'Log past session');

  return (
    <Modal open onClose={onClose} title={modalTitle} width={560}>
      {viewMode ? (
        <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
          <div style={{ display:'flex', gap:10, flexWrap:'wrap', alignItems:'center' }}>
            <span style={{ fontSize:14, fontWeight:700, color:'var(--text)' }}>{formatDate(existing?.startedAt || existing?.date, { time:true })}</span>
            <span style={{ fontSize:12, color:'var(--text-mute)' }}>{existing?.durationMin || duration} min</span>
          </div>
          <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
            {exercises.map((blk, bi) => {
              const ex = exById(blk.exId);
              return (
                <div key={bi} style={{ background:'var(--bg-2)', border:'1px solid var(--border)', borderRadius:10, overflow:'hidden' }}>
                  <div style={{ padding:'10px 14px', display:'flex', alignItems:'center', gap:8, borderBottom:'1px solid var(--border)' }}>
                    <span style={{ fontWeight:700, fontSize:13, flex:1 }}>{ex.name}</span>
                    {exGroups(ex).map(g => <MuscleBadge key={g} group={g}/>)}
                  </div>
                  <table style={{ width:'100%', borderCollapse:'collapse', fontSize:13, padding:'0 14px' }}>
                    <thead>
                      <tr>
                        <th style={{ width:24, textAlign:'left', fontSize:10, color:'var(--text-mute)', textTransform:'uppercase', letterSpacing:0.4, fontWeight:600, padding:'6px 0 4px 14px' }}>#</th>
                        <th style={{ textAlign:'left', fontSize:10, color:'var(--text-mute)', textTransform:'uppercase', letterSpacing:0.4, fontWeight:600, padding:'6px 8px 4px' }}>Weight</th>
                        <th style={{ textAlign:'left', fontSize:10, color:'var(--text-mute)', textTransform:'uppercase', letterSpacing:0.4, fontWeight:600, padding:'6px 8px 4px' }}>Reps</th>
                        <th style={{ width:50, textAlign:'left', fontSize:10, color:'var(--text-mute)', textTransform:'uppercase', letterSpacing:0.4, fontWeight:600, padding:'6px 8px 4px' }}>RIR</th>
                        <th style={{ width:36, padding:'6px 14px 4px 8px' }}/>
                      </tr>
                    </thead>
                    <tbody>
                      {(blk.sets||[]).map((set, si) => (
                        <tr key={si} style={{ borderTop:'1px solid var(--border)' }}>
                          <td className="mono" style={{ color:'var(--text-mute)', fontSize:11, padding:'6px 0 6px 14px', verticalAlign:'middle' }}>{set.drop ? 'D' : si+1}</td>
                          <td className="mono" style={{ fontWeight:600, padding:'6px 8px', verticalAlign:'middle' }}>
                            {set.weight ? Math.round(convertWeight(set.weight,units)*10)/10 : 0}
                            <span style={{ fontSize:10, color:'var(--text-faint)', marginLeft:2 }}>{weightLabel(units)}</span>
                          </td>
                          <td className="mono" style={{ fontWeight:600, padding:'6px 8px', verticalAlign:'middle' }}>
                            {set.reps||0}<span style={{ fontSize:10, color:'var(--text-faint)', marginLeft:2 }}>reps</span>
                          </td>
                          <td style={{ fontSize:12, color:'var(--text-mute)', padding:'6px 8px', verticalAlign:'middle' }}>{set.rir != null ? set.rir : '—'}</td>
                          <td style={{ padding:'6px 14px 6px 8px', verticalAlign:'middle' }}>{sessionPRs.has(bi + '-' + si) && <PRBadge small/>}</td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              );
            })}
          </div>
          <div style={{ display:'flex', gap:8 }}>
            <Button variant="ghost" onClick={() => setViewMode(false)} icon={IconEdit} style={{ flex:1 }}>Edit session</Button>
            {onDelete && <Button variant="danger" onClick={async () => { if (await window.uiConfirm('This session will be permanently removed.', { title:'Delete session?', confirmLabel:'Delete' })) onDelete(); }} icon={IconTrash}>Delete</Button>}
          </div>
        </div>
      ) : (
        <div style={{ display:'flex', flexDirection:'column', gap:16 }}>
          <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr 1fr 1fr', gap:10 }}>
            <Field label="Date">
              <input type="date" value={date} onChange={e => setDate(e.target.value)} style={{...fieldStyle, paddingRight:8}}/>
            </Field>
            <Field label="Time">
              <input type="time" value={time} onChange={e => setTime(e.target.value)} style={{...fieldStyle, paddingRight:8}}/>
            </Field>
            <Field label="Session">
              <select value={isCustom ? 'Custom' : name} onChange={e => setName(e.target.value)} style={fieldStyle}>
                <option>Push</option><option>Pull</option><option>Legs</option><option>Custom</option>
              </select>
            </Field>
            <Field label="Duration (min)">
              <input type="number" value={duration} onChange={e => setDuration(e.target.value)} min="1" style={fieldStyle} inputMode="numeric"/>
            </Field>
          </div>
          {isCustom && (
            <Field label="Custom name">
              <input value={customName} onChange={e => setCustomName(e.target.value)} placeholder="e.g. Upper body" style={fieldStyle}/>
            </Field>
          )}

          <div>
            <div style={{ fontSize:11, color:'var(--text-mute)', letterSpacing:0.3, textTransform:'uppercase', fontWeight:600, marginBottom:10 }}>Exercises</div>
            {exercises.length === 0 && (
              <div style={{ color:'var(--text-mute)', fontSize:13, marginBottom:10 }}>No exercises yet. Add one below.</div>
            )}
            <div style={{ display:'flex', flexDirection:'column', gap:12 }}>
              {exercises.map((blk, bi) => {
                const ex = exById(blk.exId);
                return (
                  <div key={bi} style={{ background:'var(--bg-2)', border:'1px solid var(--border)', borderRadius:10, overflow:'hidden' }}>
                    <div style={{ padding:'10px 14px', display:'flex', alignItems:'center', justifyContent:'space-between', borderBottom:'1px solid var(--border)' }}>
                      <div style={{ display:'flex', alignItems:'center', gap:8 }}>
                        <span style={{ fontWeight:700, fontSize:13 }}>{ex.name}</span>
                        {exGroups(ex).map(g => <MuscleBadge key={g} group={g}/>)}
                      </div>
                      <button onClick={() => removeEx(bi)} style={{ color:'var(--text-mute)' }}><IconX size={14}/></button>
                    </div>
                    <div style={{ padding:'10px 14px' }}>
                      <PastSetTable sets={blk.sets||[]} onUpdate={sets => updateExSets(bi, sets)} units={units}/>
                    </div>
                  </div>
                );
              })}
            </div>
            <Button size="sm" variant="ghost" onClick={() => setAddExOpen(true)} icon={IconPlus} style={{ marginTop:10 }}>Add exercise</Button>
          </div>

          <Button variant="primary" size="lg" onClick={save}>{existing ? 'Save changes' : 'Log session'}</Button>
        </div>
      )}

      {addExOpen && (
        <AddExerciseModal
          library={library}
          onClose={() => setAddExOpen(false)}
          onPick={exId => { setExercises(prev => [...prev, { exId, sets:[], notes:'' }]); setAddExOpen(false); }}
          addedIds={exercises.map(e => e.exId)}
          sessionName={finalName}
          gymFilter={gymFilter}
          onAddToLibrary={onAddToLibrary}
        />
      )}
    </Modal>
  );
}

// Weight cell that keeps the raw typed text while focused, so intermediate values like "62."
// survive (a controlled numeric value re-parsed per keystroke turned "62.5" into 625).
function WeightInput({ kg, units, onCommit, style }) {
  const shown = kg ? String(convertWeight(kg, units)) : '';
  const [text, setText] = uS(shown);
  const [focused, setFocused] = uS(false);
  uE(() => { if (!focused) setText(shown); }, [shown, focused]);
  return (
    <input className="mono" value={focused ? text : shown} inputMode="decimal" placeholder={weightLabel(units)} style={style}
      onFocus={() => { setText(shown); setFocused(true); }}
      onBlur={() => setFocused(false)}
      onChange={e => {
        const t = e.target.value.replace(',', '.');
        if (!/^\d*\.?\d*$/.test(t)) return;
        setText(t);
        const v = parseFloat(t);
        const w = isNaN(v) ? 0 : (units === 'lb' ? v / 2.20462 : v);
        onCommit(Math.round(clampWeight(w) * 10) / 10);
      }}/>
  );
}

function PastSetTable({ sets, onUpdate, units }) {
  // Starter row is blank (no invented weight/reps) — an untouched row saves as an empty set.
  const [rows, setRows] = uS(sets.length > 0 ? sets : [{ weight:0, reps:0, rir:null, drop:false, notes:'', rest:null, isPR:false }]);
  // The default starter row is display state until committed — sync it to the parent
  // on mount so saving without touching the table doesn't store zero sets.
  uE(() => { if (sets.length === 0) onUpdate(rows); }, []);
  const sync = newRows => { setRows(newRows); onUpdate(newRows); };
  const update = (i, patch) => sync(rows.map((r, j) => j===i ? {...r,...patch} : r));
  const addRow = () => {
    const last = rows[rows.length-1];
    sync([...rows, { weight:last?.weight||0, reps:last?.reps||0, rir:last?.rir??null, drop:false, notes:'', rest:null, isPR:false }]);
  };
  const remove = i => sync(rows.filter((_,j) => j!==i));

  const inp = { height:30, padding:'0 6px', background:'var(--surface)', border:'1px solid var(--border-strong)', borderRadius:5, fontSize:13, color:'var(--text)' };
  return (
    <div>
      <div style={{ display:'grid', gridTemplateColumns:'20px 1fr 1fr 1fr 28px 20px', gap:6, padding:'4px 0', fontSize:10, color:'var(--text-mute)', textTransform:'uppercase', letterSpacing:0.4, fontWeight:600 }}>
        <div>#</div><div>Weight</div><div>Reps</div><div>RIR</div><div style={{ textAlign:'center' }}>Drop</div><div/>
      </div>
      {rows.map((set, i) => (
        <div key={i} style={{ display:'grid', gridTemplateColumns:'20px 1fr 1fr 1fr 28px 20px', gap:6, padding:'4px 0', alignItems:'center', borderTop:'1px solid var(--border)' }}>
          <span className="mono" style={{ fontSize:11, color:'var(--text-mute)' }}>{set.drop ? '↓' : i+1}</span>
          <WeightInput kg={set.weight} units={units} onCommit={w => update(i,{weight:w})} style={{...inp, width:'100%'}}/>
          <input className="mono" value={set.reps || ''} inputMode="numeric"
            onChange={e => update(i,{reps:clampReps(parseInt(e.target.value)||0)})}
            style={{...inp, width:'100%'}} placeholder="reps"/>
          <input className="mono" value={set.rir??''} inputMode="numeric"
            onChange={e => update(i,{rir:clampRir(e.target.value)})}
            style={{...inp, width:'100%'}} placeholder="—"/>
          <div style={{ display:'grid', placeItems:'center' }}>
            <input type="checkbox" checked={set.drop} onChange={e => update(i,{drop:e.target.checked})}
              style={{ width:16, height:16, accentColor:'var(--drop)' }}/>
          </div>
          <button onClick={() => remove(i)} style={{ color:'var(--text-mute)', display:'grid', placeItems:'center' }}
            onMouseEnter={e => e.currentTarget.style.color = 'var(--danger)'}
            onMouseLeave={e => e.currentTarget.style.color = 'var(--text-mute)'}
          ><IconX size={12}/></button>
        </div>
      ))}
      <button onClick={addRow} style={{ fontSize:12, color:'var(--accent)', marginTop:8, fontWeight:600 }}>+ Add set</button>
    </div>
  );
}

/* ============================================================
   ANALYTICS
   ============================================================ */
function AnalyticsPage({ units, sessions, library, onOpenSession }) {
  const [tab, setTab] = uS('curve');

  const exHistory = uM(() => {
    const map = {};
    [...sessions].sort((a,b) => new Date(a.startedAt||a.date)-new Date(b.startedAt||b.date))
      .forEach((s, idx) => {
        // Best set per exercise across ALL blocks in the session (an exercise may appear
        // more than once) — one chart point per session, not per block.
        const bestPerEx = {};
        (s.exercises||[]).forEach(blk => {
          (blk.sets||[]).filter(x=>!x.drop&&!x.myo&&x.weight&&x.reps).forEach(x => {
            const e = epley(x.weight, x.reps);
            const cur = bestPerEx[blk.exId];
            if (!cur || e > cur.e) bestPerEx[blk.exId] = { e, weight:x.weight, reps:x.reps };
          });
        });
        Object.entries(bestPerEx).forEach(([exId, b]) => {
          if (!map[exId]) map[exId] = [];
          map[exId].push({
            x:idx, y:Math.round(b.e),
            label:formatDate(s.startedAt||s.date,{relative:true}),
            date:s.startedAt||s.date, sessionId:s.id,
            weight:b.weight, reps:b.reps,
          });
        });
      });
    return map;
  }, [sessions]);

  const muscleWeekData = uM(() => {
    const wv = computeWeeklyVolume(sessions);
    return MUSCLE_GROUPS.map(g => ({ label:g.slice(0,4), value:wv[g]?.current||0, secondary:wv[g]?.prev||0, color:muscleColor(g) }));
  }, [sessions]);

  const calendar = uM(() => {
    const cells = [];
    for (let i = 69; i >= 0; i--) {
      const d = new Date(); d.setDate(d.getDate()-i);
      const ds = localDateStr(d); // local calendar day — UTC slicing shifts evening/early-morning sessions
      const s = sessions.find(x => localDateStr(x.startedAt||x.date||0) === ds);
      cells.push({ date:d, session:s });
    }
    return cells;
  }, [sessions]);

  const allPRExercises = uM(() =>
    (library||[]).filter(e => (exHistory[e.id]||[]).length >= 1)
      .sort((a,b) => (exHistory[b.id]?.length||0) - (exHistory[a.id]?.length||0))
  , [exHistory, library]);

  const prList = uM(() =>
    allPRExercises.map(ex => {
      const data = exHistory[ex.id] || [];
      const best = data.reduce((b, d) => d.y > b.y ? d : b, data[0]);
      return best ? { ex, best } : null;
    }).filter(Boolean)
  , [allPRExercises, exHistory]);

  const tabs = [
    { id:'curve',  label:'Strength' },
    { id:'prs',    label:'PRs' },
    { id:'muscle', label:'Volume' },
    { id:'freq',   label:'Frequency' },
  ];

  return (
    <div style={{ padding:28, display:'flex', flexDirection:'column', gap:20 }}>
      <div>
        <h1 style={{ margin:0, fontSize:26, fontWeight:800, letterSpacing:-0.5 }}>Analytics<span style={{ color:'var(--accent)' }}>.</span></h1>
        <div style={{ color:'var(--text-dim)', fontSize:14, marginTop:6 }}>Trends across all logged sessions.</div>
      </div>

      <div style={{ display:'flex', gap:4, padding:4, background:'var(--surface)', border:'1px solid var(--border)', borderRadius:'var(--radius-sm)', alignSelf:'flex-start' }}>
        {tabs.map(t => (
          <button key={t.id} onClick={() => setTab(t.id)} style={{
            padding:'8px 18px', fontSize:12, fontWeight:600, borderRadius:6,
            color: tab===t.id ? '#06080d' : 'var(--text-dim)',
            background: tab===t.id ? 'var(--accent)' : 'transparent',
          }}>{t.label}</button>
        ))}
      </div>

      {sessions.length === 0 && (
        <Card style={{ textAlign:'center', padding:48 }}>
          <div style={{ fontSize:16, fontWeight:700, marginBottom:8 }}>No data yet</div>
          <div style={{ color:'var(--text-dim)', fontSize:14 }}>Log some sessions to see analytics.</div>
        </Card>
      )}

      {/* Strength — e1RM progression per exercise */}
      {tab === 'curve' && sessions.length > 0 && (
        <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(320px, 1fr))', gap:14 }}>
          {allPRExercises.length === 0 && <div style={{ color:'var(--text-mute)', fontSize:13 }}>No exercise data yet.</div>}
          {allPRExercises.map(ex => {
            const data = (exHistory[ex.id]||[]).map(d => ({...d, y:Math.round(convertWeight(d.y,units))}));
            const latest = data[data.length-1]?.y || 0;
            const earliest = data[0]?.y || 0;
            const delta = latest - earliest;
            return (
              <Card key={ex.id}>
                <div style={{ display:'flex', alignItems:'flex-start', justifyContent:'space-between', marginBottom:14 }}>
                  <div>
                    <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:4 }}>{exGroups(ex).map(g => <MuscleBadge key={g} group={g}/>)}</div>
                    <div style={{ fontSize:15, fontWeight:700 }}>{ex.name}</div>
                    <div style={{ fontSize:11, color:'var(--text-mute)', marginTop:4 }}>est. 1RM · Epley</div>
                  </div>
                  <div style={{ textAlign:'right' }}>
                    <div className="mono" style={{ fontSize:22, fontWeight:700 }}>{latest}<span style={{ fontSize:12, color:'var(--text-mute)', marginLeft:3 }}>{weightLabel(units)}</span></div>
                    {delta !== 0 && (
                      <div className="mono" style={{ fontSize:11, color: delta>0?'var(--accent)':'var(--danger)', display:'flex', alignItems:'center', justifyContent:'flex-end', gap:3, marginTop:3 }}>
                        {delta>0 && <IconArrowUp size={10}/>}{delta>0?'+':''}{delta}{weightLabel(units)}
                      </div>
                    )}
                  </div>
                </div>
                <LineChart data={data} height={160} color="var(--accent)" formatY={v=>`${v}${weightLabel(units)}`}/>
              </Card>
            );
          })}
        </div>
      )}

      {/* PRs — current best per exercise */}
      {tab === 'prs' && sessions.length > 0 && (
        <Card>
          <SectionHeading sub="All-time best estimated 1RM per exercise">Personal Records</SectionHeading>
          {prList.length === 0
            ? <div style={{ color:'var(--text-mute)', fontSize:13 }}>No exercise data yet.</div>
            : prList.map(({ ex, best }) => {
                const e1rm = Math.round(convertWeight(best.y, units));
                const w = Math.round(convertWeight(best.weight, units) * 10) / 10;
                const sess = sessions.find(s => s.id === best.sessionId);
                return (
                  <button key={ex.id}
                    onClick={() => sess && onOpenSession && onOpenSession(sess)}
                    style={{ display:'flex', alignItems:'center', width:'100%', padding:'12px 0', background:'none', borderTop:'none', borderLeft:'none', borderRight:'none', borderBottom:'1px solid var(--border)', textAlign:'left', cursor: sess ? 'pointer' : 'default', gap:14 }}>
                    <div style={{ flex:1, minWidth:0 }}>
                      <div style={{ fontWeight:600, fontSize:14 }}>{ex.name}</div>
                      <div style={{ display:'flex', alignItems:'center', gap:8, marginTop:3 }}>
                        {exGroups(ex).map(g => <MuscleBadge key={g} group={g}/>)}
                        <span style={{ fontSize:11, color:'var(--text-mute)' }}>{formatDate(best.date, {})}</span>
                      </div>
                    </div>
                    <div style={{ textAlign:'right', flexShrink:0 }}>
                      <div className="mono" style={{ fontSize:16, fontWeight:700 }}>{e1rm}<span style={{ fontSize:11, color:'var(--text-mute)', marginLeft:2 }}>{weightLabel(units)}</span></div>
                      <div className="mono" style={{ fontSize:11, color:'var(--text-dim)', marginTop:2 }}>{w}{weightLabel(units)} × {best.reps}</div>
                    </div>
                    {sess && <IconChevronRight size={14} style={{ color:'var(--text-faint)', flexShrink:0 }}/>}
                  </button>
                );
              })
          }
        </Card>
      )}

      {/* Volume — sets per muscle */}
      {tab === 'muscle' && sessions.length > 0 && (
        <Card>
          <SectionHeading sub="This week (colored) vs previous week (gray)">Sets per muscle group</SectionHeading>
          <BarChart data={muscleWeekData} height={280}/>
          <div style={{ display:'flex', gap:16, marginTop:16, fontSize:11, color:'var(--text-mute)' }}>
            <div style={{ display:'flex', alignItems:'center', gap:6 }}><div style={{ width:10, height:10, background:'var(--accent)', borderRadius:2 }}/> This week</div>
            <div style={{ display:'flex', alignItems:'center', gap:6 }}><div style={{ width:10, height:10, background:'var(--surface-3)', borderRadius:2 }}/> Previous week</div>
          </div>
        </Card>
      )}

      {/* Frequency */}
      {tab === 'freq' && sessions.length > 0 && (
        <Card>
          <SectionHeading sub="Last 10 weeks — each cell is one day">Session frequency</SectionHeading>
          <div style={{ display:'grid', gridTemplateColumns:'repeat(10,1fr)', gap:4, marginBottom:18 }}>
            {calendar.map((c, i) => {
              const color = c.session ? SESSION_COLORS[c.session.name] || '#94a3b8' : null;
              return (
                <div key={i} title={c.date.toDateString() + (c.session ? ' · ' + c.session.name : '')} style={{
                  aspectRatio:'1', borderRadius:4,
                  background: color ? color+'38' : 'var(--surface-2)',
                  border:'1px solid ' + (color ? color+'60' : 'var(--border)'),
                  display:'grid', placeItems:'center',
                  fontSize:10, color: color||'var(--text-faint)', fontWeight:700, fontFamily:'JetBrains Mono, monospace',
                }}>
                  {c.session ? sessionAbbr(c.session.name) : ''}
                </div>
              );
            })}
          </div>
          <div style={{ display:'grid', gridTemplateColumns:'repeat(3,1fr)', gap:12, paddingTop:16, borderTop:'1px solid var(--border)' }}>
            {SESSION_TYPES.map(t => {
              const count = sessions.filter(s=>s.name===t).length;
              const color = SESSION_COLORS[t];
              return (
                <div key={t} style={{ display:'flex', alignItems:'center', gap:12 }}>
                  <div style={{ width:36, height:36, borderRadius:8, background:color+'20', border:'1px solid '+color+'40', display:'grid', placeItems:'center', color, fontWeight:700 }}>{sessionAbbr(t)}</div>
                  <div>
                    <div style={{ fontSize:13, fontWeight:700 }}>{t}</div>
                    <div className="mono" style={{ fontSize:11, color:'var(--text-mute)' }}>{count} sessions</div>
                  </div>
                </div>
              );
            })}
          </div>
        </Card>
      )}
    </div>
  );
}

/* ============================================================
   EXERCISE LIBRARY
   ============================================================ */
function LibraryPage({ units, library, sessions, onAddExercise, onUpdateExercise, onDeleteExercise, gymFilter }) {
  const [q, setQ] = uS('');
  const [group, setGroup] = uS('All');
  const [newOpen, setNewOpen] = uS(false);
  const [editEx, setEditEx] = uS(null);

  const filtered = (library||[]).filter(e =>
    (!gymFilter || !e.gym || e.gym === gymFilter) &&
    (group==='All' || exGroups(e).includes(group)) &&
    e.name.toLowerCase().includes(q.toLowerCase())
  );

  const bestMap = uM(() => computeBestE1RMs(sessions), [sessions]);

  // Group filtered exercises by muscle group; exercises with multiple primary groups appear in each
  const grouped = uM(() => {
    const map = {};
    filtered.forEach(e => {
      exGroups(e).forEach(g => { (map[g] = map[g] || []).push(e); });
    });
    return MUSCLE_GROUPS.filter(g => map[g]?.length).map(g => ({ group: g, exercises: map[g] }));
  }, [filtered]);

  const ExCard = ({ ex }) => {
    const count = sessions.reduce((n,s) => n + ((s.exercises||[]).some(e=>e.exId===ex.id)?1:0), 0);
    const best = bestMap[ex.id];
    return (
      <div style={{
        background:'var(--surface)', border:'1px solid var(--border)',
        borderRadius:'var(--radius)', padding:16,
        display:'flex', flexDirection:'column', gap:10,
        transition:'all 0.15s',
      }}
        onMouseEnter={e => { e.currentTarget.style.borderColor='var(--border-strong)'; e.currentTarget.style.transform='translateY(-1px)'; }}
        onMouseLeave={e => { e.currentTarget.style.borderColor='var(--border)'; e.currentTarget.style.transform='translateY(0)'; }}
      >
        <div style={{ display:'flex', justifyContent:'flex-end', gap:2 }}>
          <button onClick={() => setEditEx(ex)} style={{ width:26, height:26, borderRadius:5, color:'var(--text-mute)', display:'grid', placeItems:'center' }}
            onMouseEnter={e => e.currentTarget.style.color='var(--text)'}
            onMouseLeave={e => e.currentTarget.style.color='var(--text-mute)'}
          ><IconEdit size={13}/></button>
          <button onClick={async () => {
            if (count > 0) { await window.uiAlert(`"${ex.name}" is used in ${count} session${count !== 1 ? 's' : ''}, so it can't be deleted.`, { title:"Can't delete" }); return; }
            if (await window.uiConfirm(ex.name + ' will be removed from your library.', { title:'Delete exercise?', confirmLabel:'Delete' })) onDeleteExercise(ex.id);
          }} style={{ width:26, height:26, borderRadius:5, color:'var(--text-mute)', display:'grid', placeItems:'center' }}
            onMouseEnter={e => e.currentTarget.style.color='var(--danger)'}
            onMouseLeave={e => e.currentTarget.style.color='var(--text-mute)'}
          ><IconTrash size={13}/></button>
        </div>
        <div style={{ fontSize:15, fontWeight:700, lineHeight:1.3 }}>{ex.name}</div>
        <div style={{ display:'flex', alignItems:'center', gap:6, flexWrap:'wrap' }}>
          {ex.equipment && <span style={{ fontSize:11, color:'var(--text-mute)' }}>{ex.equipment}</span>}
          <GymBadge gym={ex.gym}/>
        </div>
        {ex.notes && <div style={{ fontSize:11, color:'var(--text-mute)', fontStyle:'italic' }}>{ex.notes}</div>}
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-end', marginTop:'auto', paddingTop:10, borderTop:'1px solid var(--border)' }}>
          <div>
            <div style={{ fontSize:10, color:'var(--text-mute)', letterSpacing:0.4, textTransform:'uppercase', fontWeight:600 }}>Best e1RM</div>
            <div className="mono" style={{ fontSize:16, fontWeight:700, marginTop:3 }}>
              {best ? Math.round(convertWeight(best,units)) : '—'}
              {best && <span style={{ fontSize:11, color:'var(--text-mute)', marginLeft:3 }}>{weightLabel(units)}</span>}
            </div>
          </div>
          <div className="mono" style={{ fontSize:12, color:'var(--text-dim)' }}>{count}<span style={{ color:'var(--text-mute)', marginLeft:3 }}>sessions</span></div>
        </div>
      </div>
    );
  };

  return (
    <div style={{ padding:28, display:'flex', flexDirection:'column', gap:20 }}>
      <div style={{ display:'flex', alignItems:'flex-end', justifyContent:'space-between', flexWrap:'wrap', gap:16 }}>
        <div>
          <h1 style={{ margin:0, fontSize:26, fontWeight:800, letterSpacing:-0.5 }}>Exercise <span style={{ color:'var(--accent)' }}>Library</span></h1>
          <div style={{ color:'var(--text-dim)', fontSize:14, marginTop:6 }}>{(library||[]).length} exercises</div>
        </div>
        <Button variant="primary" onClick={() => setNewOpen(true)} icon={IconPlus}>New exercise</Button>
      </div>

      <div style={{ display:'flex', alignItems:'center', gap:12, flexWrap:'wrap' }}>
        <div style={{ display:'flex', alignItems:'center', gap:8, padding:'8px 12px', background:'var(--surface)', borderRadius:8, border:'1px solid var(--border)', minWidth:240 }}>
          <IconSearch size={14} stroke="var(--text-mute)"/>
          <input value={q} onChange={e => setQ(e.target.value)} placeholder="Search exercises…" style={{ flex:1, fontSize:13 }}/>
        </div>
        <div style={{ display:'flex', flexWrap:'wrap', gap:4 }}>
          {['All', ...MUSCLE_GROUPS].map(g => (
            <button key={g} onClick={() => setGroup(g)} style={{
              fontSize:11, padding:'6px 10px', borderRadius:999, fontWeight:600,
              color: group===g ? 'var(--accent)' : 'var(--text-dim)',
              background: group===g ? 'color-mix(in srgb, var(--accent) 8%, transparent)' : 'var(--surface)',
              border:'1px solid ' + (group===g ? 'color-mix(in srgb, var(--accent) 20%, transparent)' : 'var(--border)'),
            }}>{g}</button>
          ))}
        </div>
      </div>

      {grouped.length === 0 && (
        <div style={{ color:'var(--text-mute)', fontSize:13, padding:'32px 0' }}>No exercises match.</div>
      )}

      <div style={{ display:'flex', flexDirection:'column', gap:28 }}>
        {grouped.map(({ group: g, exercises }) => {
          const c = muscleColor(g);
          return (
            <div key={g}>
              <div style={{ display:'flex', alignItems:'center', gap:10, marginBottom:14 }}>
                <span style={{ width:8, height:8, borderRadius:999, background:c, flexShrink:0 }}/>
                <span style={{ fontSize:12, fontWeight:700, letterSpacing:0.4, textTransform:'uppercase', color:'var(--text-dim)' }}>{g}</span>
                <span className="mono" style={{ fontSize:11, color:'var(--text-mute)' }}>{exercises.length}</span>
                <div style={{ flex:1, height:1, background:'var(--border)' }}/>
              </div>
              <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(240px, 1fr))', gap:12 }}>
                {exercises.map(ex => <ExCard key={ex.id} ex={ex}/>)}
              </div>
            </div>
          );
        })}
      </div>

      {(newOpen || editEx) && (
        <ExerciseFormModal
          existing={editEx}
          onClose={() => { setNewOpen(false); setEditEx(null); }}
          onSave={data => {
            if (editEx) onUpdateExercise(editEx.id, data);
            else onAddExercise(data);
            setNewOpen(false); setEditEx(null);
          }}
        />
      )}
    </div>
  );
}


function GymBadge({ gym }) {
  if (!gym) return null;
  const c = GYM_COLORS[gym] || { bg:'rgba(255,255,255,0.08)', text:'var(--text-mute)' };
  return <span style={{ fontSize:10, fontWeight:700, padding:'2px 6px', borderRadius:4, background:c.bg, color:c.text, letterSpacing:0.3, flexShrink:0 }}>{gym}</span>;
}

function MuscleChipSelect({ label, selected, onChange, exclude }) {
  const available = MUSCLE_GROUPS.filter(g => !(exclude||[]).includes(g));
  return (
    <Field label={label}>
      <div style={{ display:'flex', flexWrap:'wrap', gap:6 }}>
        {available.map(g => {
          const on = selected.includes(g);
          return (
            <button key={g} type="button" onClick={() => onChange(on ? selected.filter(x=>x!==g) : [...selected, g])} style={{
              fontSize:11, padding:'4px 10px', borderRadius:999, fontWeight:600,
              color: on ? muscleColor(g) : 'var(--text-dim)',
              background: on ? 'rgba(255,255,255,0.06)' : 'var(--surface-2)',
              border:'1px solid ' + (on ? muscleColor(g) : 'var(--border)'),
              transition:'all 0.12s',
            }}>{g}</button>
          );
        })}
      </div>
    </Field>
  );
}

function ExerciseFormModal({ existing, onClose, onSave, embedded }) {
  const [name, setName] = uS(existing?.name || '');
  const [groups, setGroups] = uS(existing ? exGroups(existing) : ['Chest']);
  const [secondary, setSecondary] = uS(existing ? exSecondary(existing) : []);
  const [equipment, setEquipment] = uS(existing?.equipment || 'Barbell');
  const [gym, setGym] = uS(existing?.gym || '');
  const [notes, setNotes] = uS(existing?.notes || '');
  const save = () => {
    if (!name.trim() || !groups.length) return;
    onSave({ name:name.trim(), groups, secondary, equipment, gym, notes });
  };
  const fields = (
    <div style={{ display:'flex', flexDirection:'column', gap:12 }}>
      <Field label="Exercise name">
        <input value={name} onChange={e => setName(e.target.value)} placeholder="e.g. Incline DB Press" autoFocus style={fieldStyle}/>
      </Field>
      <MuscleChipSelect label="Primary muscle groups" selected={groups} onChange={setGroups} exclude={secondary}/>
      <MuscleChipSelect label="Secondary muscles (×0.5 volume)" selected={secondary} onChange={setSecondary} exclude={groups}/>
      <Field label="Equipment">
        <select value={equipment} onChange={e => setEquipment(e.target.value)} style={fieldStyle}>
          {['Barbell','Dumbbell','Machine','Cable','Bodyweight'].map(g => <option key={g}>{g}</option>)}
        </select>
      </Field>
      {GYMS.length > 0 && <Field label="Gym">
        <select value={gym} onChange={e => setGym(e.target.value)} style={fieldStyle}>
          <option value="">Any / unspecified</option>
          {GYMS.map(g => <option key={g}>{g}</option>)}
        </select>
      </Field>}
      <Field label="Notes (optional)">
        <textarea value={notes} onChange={e => setNotes(e.target.value)} rows={3} placeholder="Form cues, setup, etc."
          style={{...fieldStyle, height:'auto', padding:10, resize:'vertical'}}/>
      </Field>
      <Button variant="primary" size="lg" onClick={save}>{existing ? 'Save changes' : 'Create exercise'}</Button>
    </div>
  );
  if (embedded) return fields;
  return (
    <Modal open onClose={onClose} title={existing ? 'Edit exercise' : 'New exercise'}>
      {fields}
    </Modal>
  );
}

/* ============================================================
   SETTINGS
   ============================================================ */
function SettingsPage({ prefs, onPrefsChange, sessions, library, syncStatus, onSyncNow }) {
  const set = (key, val) => onPrefsChange({ ...prefs, [key]: val });
  const [token, setToken] = uS(workerToken());
  const accentOptions = [
    { value:'#c6f432', label:'Lime' },
    { value:'#22d3ee', label:'Cyan' },
    { value:'#5eead4', label:'Teal' },
    { value:'#60a5fa', label:'Blue' },
    { value:'#f472b6', label:'Pink' },
    { value:'#fb923c', label:'Orange' },
    { value:'#e4e7ee', label:'Mono' },
  ];
  const statusLabel = { synced:'Synced', offline:'Offline / sync failed', notoken:'No sync token on this device', pending:'Syncing…' }[syncStatus] || syncStatus;

  return (
    <div style={{ padding:28 }}>
      <div style={{ marginBottom:24 }}>
        <h1 style={{ margin:0, fontSize:26, fontWeight:800, letterSpacing:-0.5 }}>Settings<span style={{ color:'var(--accent)' }}>.</span></h1>
        <div style={{ color:'var(--text-dim)', fontSize:14, marginTop:6 }}>Preferences and configuration.</div>
      </div>

      <div style={{ display:'grid', gridTemplateColumns:'1fr 360px', gap:20, alignItems:'start' }}>
        {/* Left column */}
        <div style={{ display:'flex', flexDirection:'column', gap:16 }}>
          <Card>
            <SectionHeading>Appearance</SectionHeading>
            <div style={{ display:'flex', flexDirection:'column', gap:20 }}>
              <div>
                <div style={{ fontSize:13, fontWeight:600, marginBottom:10 }}>Accent color</div>
                <div style={{ display:'flex', gap:10 }}>
                  {accentOptions.map(o => (
                    <button key={o.value} onClick={() => set('accent', o.value)} title={o.label} style={{
                      width:36, height:36, borderRadius:8, background:o.value, flexShrink:0,
                      outline: prefs.accent===o.value ? '2px solid white' : '2px solid transparent',
                      outlineOffset:2,
                      boxShadow: prefs.accent===o.value ? `0 0 0 4px ${o.value}44` : 'none',
                      transition:'all 0.15s',
                    }}/>
                  ))}
                </div>
              </div>
              <div>
                <div style={{ fontSize:13, fontWeight:600, marginBottom:10 }}>Density</div>
                <div style={{ display:'flex', gap:6 }}>
                  {['compact','comfortable','spacious'].map(d => (
                    <button key={d} onClick={() => set('density', d)} style={{
                      padding:'7px 14px', borderRadius:8, fontSize:13, fontWeight:600, textTransform:'capitalize',
                      background: prefs.density===d ? 'var(--accent)' : 'var(--surface-2)',
                      color: prefs.density===d ? '#06080d' : 'var(--text)',
                      border:'1px solid ' + (prefs.density===d ? 'var(--accent)' : 'var(--border)'),
                    }}>{d}</button>
                  ))}
                </div>
              </div>
            </div>
          </Card>

          <Card>
            <SectionHeading>Units</SectionHeading>
            <div style={{ display:'flex', gap:6 }}>
              {['kg','lb'].map(u => (
                <button key={u} onClick={() => set('units', u)} style={{
                  padding:'7px 20px', borderRadius:8, fontSize:13, fontWeight:700,
                  background: prefs.units===u ? 'var(--accent)' : 'var(--surface-2)',
                  color: prefs.units===u ? '#06080d' : 'var(--text)',
                  border:'1px solid ' + (prefs.units===u ? 'var(--accent)' : 'var(--border)'),
                }}>{u}</button>
              ))}
            </div>
            <div style={{ fontSize:12, color:'var(--text-mute)', marginTop:10 }}>
              Weights are stored internally in kg. This affects display only.
            </div>
          </Card>

          {GYMS.length > 0 && <Card>
            <SectionHeading>Default Gym</SectionHeading>
            <div style={{ fontSize:13, color:'var(--text-dim)', marginBottom:12 }}>
              Only exercises tagged for this gym (or untagged) will appear in the library and exercise picker.
            </div>
            <div style={{ display:'flex', gap:6, flexWrap:'wrap' }}>
              {['', ...GYMS].map(g => (
                <button key={g} onClick={() => set('gym', g)} style={{
                  padding:'7px 16px', borderRadius:8, fontSize:13, fontWeight:600,
                  background: prefs.gym===g ? 'var(--accent)' : 'var(--surface-2)',
                  color: prefs.gym===g ? '#06080d' : 'var(--text)',
                  border:'1px solid ' + (prefs.gym===g ? 'var(--accent)' : 'var(--border)'),
                }}>{g || 'All gyms'}</button>
              ))}
            </div>
          </Card>}

          <Card>
            <SectionHeading>Layout</SectionHeading>
            <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between' }}>
              <div style={{ fontSize:13, fontWeight:600 }}>Collapse sidebar by default</div>
              <Toggle value={prefs.sidebarCollapsed} onChange={v => set('sidebarCollapsed', v)}/>
            </div>
          </Card>
        </div>

        {/* Right column — Cloud sync + Data */}
        <div style={{ display:'flex', flexDirection:'column', gap:16 }}>
        <Card>
          <SectionHeading>Cloud sync</SectionHeading>
          <div style={{ fontSize:12, color: syncStatus === 'synced' ? 'var(--accent)' : 'var(--text-dim)', marginBottom:12, fontWeight:600 }}>{statusLabel}</div>
          <Field label="Sync token (this device only)">
            <input type="password" value={token} onChange={e => setToken(e.target.value)} placeholder="Enter token" style={fieldStyle} autoComplete="off"/>
          </Field>
          <Button variant="primary" style={{ marginTop:12, width:'100%' }} onClick={() => { saveWorkerToken(token); onSyncNow && onSyncNow(); }}>Save & sync</Button>
        </Card>
        <Card>
          <SectionHeading>Data</SectionHeading>
          <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:12, marginBottom:20 }}>
            {[
              { label:'Sessions', val:sessions.length },
              { label:'Exercises', val:(library||[]).length },
              { label:'Total sets', val:sessions.reduce((n,s) => n + (s.exercises||[]).reduce((m,e) => m + (e.sets||[]).length, 0), 0) },
              { label:'Total volume', val: Math.round(sessions.reduce((n,s)=>n+totalVolume(s),0)/1000) + 't' },
            ].map(({label,val}) => (
              <div key={label} style={{ padding:'14px 16px', background:'var(--bg-2)', borderRadius:'var(--radius-sm)', border:'1px solid var(--border)' }}>
                <div style={{ fontSize:10, color:'var(--text-mute)', textTransform:'uppercase', letterSpacing:0.4, fontWeight:600 }}>{label}</div>
                <div className="mono" style={{ fontSize:22, fontWeight:700, marginTop:6 }}>{val}</div>
              </div>
            ))}
          </div>
        </Card>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { DashboardPage, ActiveSessionPage, HistoryPage, AnalyticsPage, LibraryPage, SettingsPage });
