// Shared components — sidebar, badges, charts, body diagram, rest timer, bottom nav
const { useState, useEffect, useRef, useMemo } = React;

/* ============================================================
   ICONS
   ============================================================ */
const Icon = ({ d, size=18, fill='none', stroke='currentColor', sw=1.6, ...rest }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill={fill} stroke={stroke} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round" {...rest}>{d}</svg>
);
const IconDashboard  = p => <Icon {...p} d={<><rect x="3" y="3" width="7" height="9"/><rect x="14" y="3" width="7" height="5"/><rect x="14" y="12" width="7" height="9"/><rect x="3" y="16" width="7" height="5"/></>}/>;
const IconActive     = p => <Icon {...p} d={<><path d="M6 8h2v8H6zm10 0h2v8h-2zM9 11h6v2H9zM3 10h2v4H3zm16 0h2v4h-2z" fill="currentColor" stroke="none"/></>}/>;
const IconHistory    = p => <Icon {...p} d={<><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></>}/>;
const IconAnalytics  = p => <Icon {...p} d={<><path d="M3 3v18h18"/><path d="M7 14l4-5 4 3 5-7"/></>}/>;
const IconLibrary    = p => <Icon {...p} d={<><path d="M4 19V5a2 2 0 0 1 2-2h12v18"/><path d="M6 17h12"/><path d="M6 17a2 2 0 0 0-2 2"/></>}/>;
const IconSettings   = p => <Icon {...p} d={<><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></>}/>;
const IconPlus       = p => <Icon {...p} d={<><path d="M12 5v14"/><path d="M5 12h14"/></>}/>;
const IconChevronRight = p => <Icon {...p} d={<path d="M9 6l6 6-6 6"/>}/>;
const IconChevronLeft  = p => <Icon {...p} d={<path d="M15 6l-6 6 6 6"/>}/>;
const IconChevronDown  = p => <Icon {...p} d={<path d="M6 9l6 6 6-6"/>}/>;
const IconCheck      = p => <Icon {...p} d={<path d="M5 13l4 4 10-10"/>}/>;
const IconX          = p => <Icon {...p} d={<><path d="M6 6l12 12"/><path d="M18 6l-12 12"/></>}/>;
const IconTrophy     = p => <Icon {...p} d={<><path d="M8 4h8v4a4 4 0 0 1-8 0V4z"/><path d="M5 5h3"/><path d="M16 5h3"/><path d="M10 12v3"/><path d="M14 12v3"/><path d="M8 17h8"/><path d="M9 15h6v4H9z"/></>}/>;
const IconClock      = p => <Icon {...p} d={<><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></>}/>;
const IconPlay       = p => <Icon {...p} d={<path d="M7 4l13 8-13 8z" fill="currentColor" stroke="none"/>}/>;
const IconPause      = p => <Icon {...p} d={<><rect x="6" y="4" width="4" height="16" fill="currentColor" stroke="none"/><rect x="14" y="4" width="4" height="16" fill="currentColor" stroke="none"/></>}/>;
const IconSearch     = p => <Icon {...p} d={<><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></>}/>;
const IconFlame      = p => <Icon {...p} d={<path d="M12 3c2 4 5 5 5 9a5 5 0 0 1-10 0c0-2 1-3 2-4 0 2 1 3 2 3-1-3 0-6 1-8z"/>}/>;
const IconArrowUp    = p => <Icon {...p} d={<><path d="M12 19V5"/><path d="M5 12l7-7 7 7"/></>}/>;
const IconDots       = p => <Icon {...p} d={<><circle cx="5" cy="12" r="1" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="1" fill="currentColor" stroke="none"/><circle cx="19" cy="12" r="1" fill="currentColor" stroke="none"/></>}/>;
const IconNote       = p => <Icon {...p} d={<><path d="M5 4h11l4 4v12H5z"/><path d="M16 4v4h4"/><path d="M9 13h6"/><path d="M9 17h4"/></>}/>;
const IconEdit       = p => <Icon {...p} d={<><path d="M14 4l6 6L9 21H3v-6z"/><path d="M14 4l3-3 6 6-3 3"/></>}/>;
const IconTrash      = p => <Icon {...p} d={<><path d="M4 7h16"/><path d="M9 7V4h6v3"/><path d="M6 7l1 13h10l1-13"/></>}/>;
const IconCalendar   = p => <Icon {...p} d={<><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4"/><path d="M8 2v4"/><path d="M3 10h18"/></>}/>;

/* ============================================================
   BADGES
   ============================================================ */
function MuscleBadge({ group, size='sm' }) {
  const c = muscleColor(group);
  return (
    <span style={{
      display:'inline-flex', alignItems:'center', gap:6,
      fontSize: size === 'sm' ? 11 : 12, fontWeight:600,
      padding: size === 'sm' ? '3px 8px' : '4px 10px',
      borderRadius:999, color:c,
      background:`${c}12`, border:`1px solid ${c}26`, letterSpacing:0.2,
    }}>
      <span style={{ width:5, height:5, borderRadius:999, background:c }}></span>
      {group}
    </span>
  );
}

function EquipmentBadge({ equipment, size='sm' }) {
  if (!equipment) return null;
  return (
    <span style={{
      display:'inline-flex', alignItems:'center', flexShrink:0,
      fontSize: size === 'sm' ? 11 : 12, fontWeight:600,
      padding: size === 'sm' ? '3px 8px' : '4px 10px',
      borderRadius:999, color:'var(--text-dim)',
      background:'rgba(255,255,255,0.05)', border:'1px solid rgba(255,255,255,0.1)', letterSpacing:0.2,
    }}>{equipment}</span>
  );
}

function RIRBadge({ rir }) {
  if (rir == null) return <span style={{ color:'var(--text-faint)' }}>—</span>;
  const color = rir === 0 ? '#f87171' : rir <= 1 ? '#fbbf24' : 'var(--text-dim)';
  return <span className="mono" style={{ color, fontSize:12 }}>RIR {rir}</span>;
}

function PRBadge({ small }) {
  return (
    <span style={{
      display:'inline-flex', alignItems:'center', gap:4,
      fontSize: small ? 10 : 11, fontWeight:700, letterSpacing:0.4,
      padding: small ? '2px 6px' : '3px 8px',
      borderRadius:999, color:'#06080d', background:'var(--accent)',
      animation:'fade-up 0.4s ease', textTransform:'uppercase',
    }}>
      <IconTrophy size={small ? 10 : 12} sw={2.2}/> PR
    </span>
  );
}

function DropTag() {
  return (
    <span className="mono" style={{
      fontSize:9, fontWeight:700, letterSpacing:0.6,
      padding:'2px 5px', borderRadius:4,
      color:'var(--drop)', background:'var(--drop-bg)',
      border:'1px solid rgba(251,146,60,0.18)', textTransform:'uppercase',
    }}>DROP</span>
  );
}

function MyoTag() {
  return (
    <span className="mono" style={{
      fontSize:9, fontWeight:700, letterSpacing:0.6,
      padding:'2px 5px', borderRadius:4,
      color:'var(--info)', background:'rgba(96,165,250,0.1)',
      border:'1px solid rgba(96,165,250,0.22)', textTransform:'uppercase',
    }}>MYO</span>
  );
}

/* ============================================================
   BUTTON
   ============================================================ */
function Button({ children, variant='default', size='md', onClick, style, icon:IconC, disabled, type, ...rest }) {
  const variants = {
    primary: { bg:'var(--accent)', color:'#06080d', border:'transparent', hoverBg:'color-mix(in srgb, var(--accent) 85%, white)', shadow:'0 0 22px color-mix(in srgb, var(--accent) 28%, transparent), 0 2px 8px rgba(0,0,0,0.3)' },
    default: { bg:'var(--surface-2)', color:'var(--text)', border:'var(--border-strong)', hoverBg:'var(--surface-3)', shadow:'none' },
    ghost:   { bg:'transparent', color:'var(--text-dim)', border:'transparent', hoverBg:'rgba(255,255,255,0.05)', shadow:'none' },
    danger:  { bg:'transparent', color:'var(--danger)', border:'rgba(248,113,113,0.2)', hoverBg:'rgba(248,113,113,0.08)', shadow:'none' },
    outline: { bg:'transparent', color:'var(--text)', border:'var(--border-strong)', hoverBg:'var(--surface-2)', shadow:'none' },
  };
  const v = variants[variant] || variants.default;
  const s = { md:{h:36,px:14,fs:13}, sm:{h:28,px:10,fs:12}, lg:{h:44,px:18,fs:14} }[size] || {};
  const [hover, setHover] = useState(false);
  return (
    <button type={type || 'button'} onClick={onClick} disabled={disabled} {...rest}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      style={{
        display:'inline-flex', alignItems:'center', justifyContent:'center', gap:6,
        height:s.h, padding:`0 ${s.px}px`, fontSize:s.fs, fontWeight:600,
        background: hover && !disabled ? v.hoverBg : v.bg,
        color: v.color,
        border:`1px solid ${v.border}`,
        borderRadius:'var(--radius-sm)',
        boxShadow: hover && !disabled ? v.shadow : 'none',
        opacity: disabled ? 0.38 : 1,
        cursor: disabled ? 'not-allowed' : 'pointer',
        transition:'background 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease, opacity 0.12s ease',
        letterSpacing: 0.1,
        ...style,
      }}>
      {IconC && <IconC size={s.fs + 2}/>}
      {children}
    </button>
  );
}

/* ============================================================
   CARD / SECTION HEADING / FIELD / TOGGLE
   ============================================================ */
function Card({ children, style, padded=true, hoverable=false, ...rest }) {
  const [hover, setHover] = useState(false);
  return (
    <div {...rest}
      onMouseEnter={hoverable ? () => setHover(true) : undefined}
      onMouseLeave={hoverable ? () => setHover(false) : undefined}
      style={{
        background: 'linear-gradient(160deg, rgba(255,255,255,0.028) 0%, transparent 60%), var(--surface)',
        border: `1px solid ${hover ? 'var(--border-strong)' : 'var(--border)'}`,
        borderRadius: 'var(--radius)',
        padding: padded ? 'var(--pad)' : 0,
        boxShadow: hover ? 'var(--shadow-md)' : 'var(--shadow-sm)',
        transform: hover ? 'translateY(-1px)' : 'none',
        transition: 'border-color 0.15s, box-shadow 0.15s, transform 0.15s',
        ...style,
      }}>{children}</div>
  );
}

function SectionHeading({ children, action, sub }) {
  return (
    <div style={{ display:'flex', alignItems:'flex-end', justifyContent:'space-between', marginBottom:16 }}>
      <div>
        <h2 style={{ margin:0, fontSize:11, fontWeight:700, letterSpacing:0.8, textTransform:'uppercase', color:'var(--text-mute)' }}>{children}</h2>
        {sub && <div style={{ fontSize:12, color:'var(--text-mute)', marginTop:4, letterSpacing:0.1 }}>{sub}</div>}
      </div>
      {action}
    </div>
  );
}

function Field({ label, children }) {
  return (
    <div>
      <div style={{ fontSize:11, color:'var(--text-mute)', letterSpacing:0.3, textTransform:'uppercase', fontWeight:600, marginBottom:6 }}>{label}</div>
      {children}
    </div>
  );
}

const fieldStyle = {
  width:'100%', height:38, padding:'0 12px',
  background:'var(--bg-2)', border:'1px solid var(--border-strong)',
  borderRadius:8, fontSize:13, color:'var(--text)',
};

function Toggle({ value, onChange }) {
  return (
    <button onClick={() => onChange(!value)} style={{
      width:44, height:24, borderRadius:12, flexShrink:0, position:'relative',
      background: value ? 'var(--accent)' : 'var(--surface-3)',
      border:`1px solid ${value ? 'var(--accent)' : 'var(--border-strong)'}`,
      transition:'all 0.2s',
    }}>
      <span style={{
        position:'absolute', top:2, left: value ? 22 : 2,
        width:18, height:18, borderRadius:9,
        background: value ? '#06080d' : 'var(--text-mute)',
        transition:'left 0.2s',
      }}/>
    </button>
  );
}

/* ============================================================
   SIDEBAR (desktop)
   ============================================================ */
function Sidebar({ page, setPage, collapsed, setCollapsed, sessionActive }) {
  const items = [
    { id:'dashboard', label:'Dashboard',        Icon:IconDashboard },
    { id:'active',    label:'Active Session',   Icon:IconActive, indicator:sessionActive },
    { id:'history',   label:'History',          Icon:IconHistory },
    { id:'analytics', label:'Analytics',        Icon:IconAnalytics },
    { id:'library',   label:'Exercise Library', Icon:IconLibrary },
    { id:'settings',  label:'Settings',         Icon:IconSettings },
  ];
  const w = collapsed ? 60 : 220;
  return (
    <aside style={{
      width:w, flexShrink:0, height:'100vh',
      borderRight:'1px solid var(--border)',
      background:'linear-gradient(180deg, rgba(255,255,255,0.018) 0%, transparent 40%), var(--bg-2)',
      display:'flex', flexDirection:'column',
      transition:'width 0.2s cubic-bezier(0.4,0,0.2,1)', overflow:'hidden',
    }}>
      {/* Logo */}
      <div style={{
        height:60, padding: collapsed ? '0' : '0 16px',
        display:'flex', alignItems:'center', justifyContent: collapsed ? 'center' : 'flex-start',
        gap:10, borderBottom:'1px solid var(--border)', flexShrink:0,
      }}>
        <div style={{
          width:30, height:30, borderRadius:9, flexShrink:0,
          background:'linear-gradient(135deg, var(--accent) 0%, #7fb300 100%)',
          display:'grid', placeItems:'center',
          boxShadow:'0 0 20px color-mix(in srgb, var(--accent) 25%, transparent), 0 2px 8px rgba(0,0,0,0.4)',
        }}>
          <svg width="15" height="15" viewBox="0 0 24 24" fill="#050709">
            <path d="M6 8h2v8H6zm10 0h2v8h-2zM9 11h6v2H9zM3 10h2v4H3zm16 0h2v4h-2z"/>
          </svg>
        </div>
        {!collapsed && (
          <div style={{ fontWeight:800, letterSpacing:0.6, fontSize:12, whiteSpace:'nowrap', color:'var(--text)' }}>
            WORKOUT<span style={{ color:'var(--accent)', marginLeft:1 }}>LOG</span>
          </div>
        )}
      </div>

      {/* Nav items */}
      <nav style={{ flex:1, padding:'10px 8px', display:'flex', flexDirection:'column', gap:1, overflowY:'auto' }}>
        {items.map(item => {
          const active = page === item.id;
          return (
            <button key={item.id} onClick={() => setPage(item.id)} title={collapsed ? item.label : ''}
              style={{
                display:'flex', alignItems:'center', gap:11,
                padding: collapsed ? '10px 0' : '9px 12px',
                justifyContent: collapsed ? 'center' : 'flex-start',
                borderRadius: 8,
                fontSize:13, fontWeight: active ? 600 : 500,
                color: active ? 'var(--text)' : 'var(--text-dim)',
                background: active ? 'rgba(255,255,255,0.07)' : 'transparent',
                border: '1px solid ' + (active ? 'rgba(255,255,255,0.08)' : 'transparent'),
                position:'relative',
                transition:'color 0.12s, background 0.12s, border-color 0.12s',
                width:'100%',
              }}
              onMouseEnter={e => { if (!active) { e.currentTarget.style.background='rgba(255,255,255,0.04)'; e.currentTarget.style.color='var(--text)'; } }}
              onMouseLeave={e => { if (!active) { e.currentTarget.style.background='transparent'; e.currentTarget.style.color='var(--text-dim)'; } }}
            >
              {active && (
                <span style={{
                  position:'absolute', left:0, top:'50%', transform:'translateY(-50%)',
                  width:2, height:'60%', borderRadius:'0 2px 2px 0',
                  background:'var(--accent)', boxShadow:'0 0 8px var(--accent)',
                }}/>
              )}
              <span style={{ color: active ? 'var(--accent)' : 'inherit', display:'flex', alignItems:'center', flexShrink:0 }}>
                <item.Icon size={16} sw={active ? 2 : 1.7}/>
              </span>
              {!collapsed && <span style={{ flex:1, textAlign:'left', letterSpacing:0.1 }}>{item.label}</span>}
              {!collapsed && item.indicator && (
                <span style={{ width:6, height:6, borderRadius:999, background:'var(--accent)', boxShadow:'0 0 8px var(--accent)', animation:'pulse-accent 2s ease infinite', flexShrink:0 }}/>
              )}
            </button>
          );
        })}
      </nav>

      {/* Collapse toggle */}
      <div style={{ padding:'8px 8px 12px', borderTop:'1px solid var(--border)', flexShrink:0 }}>
        <button onClick={() => setCollapsed(!collapsed)} style={{
          width:'100%', height:30, display:'flex', alignItems:'center',
          justifyContent:'center', gap:6,
          color:'var(--text-mute)', borderRadius:7, fontSize:11, fontWeight:500,
          transition:'color 0.12s, background 0.12s',
        }}
          onMouseEnter={e => { e.currentTarget.style.color='var(--text-dim)'; e.currentTarget.style.background='rgba(255,255,255,0.04)'; }}
          onMouseLeave={e => { e.currentTarget.style.color='var(--text-mute)'; e.currentTarget.style.background='transparent'; }}
        >
          {collapsed ? <IconChevronRight size={13}/> : <><IconChevronLeft size={13}/><span>Collapse</span></>}
        </button>
      </div>
    </aside>
  );
}

/* ============================================================
   BOTTOM NAV (mobile)
   ============================================================ */
function BottomNav({ page, setPage, sessionActive }) {
  const items = [
    { id:'dashboard', label:'Home',    Icon:IconDashboard },
    { id:'active',    label:'Train',   Icon:IconActive, indicator:sessionActive },
    { id:'history',   label:'History', Icon:IconHistory },
    { id:'analytics', label:'Stats',   Icon:IconAnalytics },
    { id:'library',   label:'Library', Icon:IconLibrary },
    { id:'settings',  label:'More',    Icon:IconSettings },
  ];
  const activeIndex = Math.max(0, items.findIndex(i => i.id === page));
  const seg = 100 / items.length;
  return (
    <nav style={{
      position:'fixed', bottom:0, left:0, right:0, height:58, zIndex:40,
      background:'rgba(8,11,17,0.9)',
      backdropFilter:'blur(16px)', WebkitBackdropFilter:'blur(16px)',
      borderTop:'1px solid rgba(255,255,255,0.07)',
      display:'flex', alignItems:'stretch',
    }}>
      {/* Sliding active indicator — springs between tabs */}
      <span style={{
        position:'absolute', top:0, height:2, width:`${seg}%`,
        left:`${activeIndex * seg}%`,
        transition:'left 0.34s cubic-bezier(0.34,1.45,0.5,1)',
        display:'flex', justifyContent:'center', pointerEvents:'none',
      }}>
        <span style={{ width:20, height:2, borderRadius:'0 0 3px 3px', background:'var(--accent)', boxShadow:'0 0 10px var(--accent)' }}/>
      </span>
      {items.map(item => {
        const active = page === item.id;
        return (
          <button key={item.id} onClick={() => setPage(item.id)} style={{
            flex:1, display:'flex', flexDirection:'column', alignItems:'center',
            justifyContent:'center', gap:3, position:'relative',
            color: active ? 'var(--accent)' : 'var(--text-mute)',
            transition:'color 0.15s',
          }}>
            <span style={{ display:'flex', transform: active ? 'scale(1.18) translateY(-1px)' : 'scale(1)', transition:'transform 0.34s cubic-bezier(0.34,1.6,0.5,1)' }}>
              <item.Icon size={20} sw={active ? 2.2 : 1.6}/>
            </span>
            <span style={{ fontSize:9, fontWeight: active ? 700 : 500, letterSpacing:0.2 }}>{item.label}</span>
            {item.indicator && !active && (
              <span style={{
                position:'absolute', top:9, right:'calc(50% - 12px)',
                width:5, height:5, borderRadius:999,
                background:'var(--accent)', boxShadow:'0 0 6px var(--accent)',
                animation:'pulse-accent 2s ease infinite',
              }}/>
            )}
          </button>
        );
      })}
    </nav>
  );
}

/* ============================================================
   BODY DIAGRAM
   ============================================================ */
function BodyDiagram({ volume, animate=false }) {
  const intensity = g => { const v = volume[g]; if (!v) return 0; return Math.min(1, v.current / v.target); };
  // When animating, fills start at 0 intensity and ramp to actual once `lit` flips true.
  const [lit, setLit] = useState(!animate);
  useEffect(() => {
    if (!animate) return;
    const id = requestAnimationFrame(() => setLit(true));
    return () => cancelAnimationFrame(id);
  }, [animate]);
  const fillStyle = g => ({
    fill: `color-mix(in srgb, var(--accent) ${Math.round((0.15 + (lit ? intensity(g) : 0) * 0.65) * 100)}%, transparent)`,
    transition: animate ? 'fill 0.9s cubic-bezier(0.22,1,0.36,1)' : undefined,
  });
  const stroke = 'rgba(255,255,255,0.12)';
  return (
    <svg viewBox="0 0 200 280" style={{ width:'100%', height:'auto', maxHeight:280 }}>
      <circle cx="100" cy="22" r="14" fill="var(--surface-3)" stroke={stroke}/>
      <rect x="92" y="34" width="16" height="8" rx="3" fill="var(--surface-3)" stroke={stroke}/>
      <ellipse cx="74" cy="56" rx="20" ry="14" style={fillStyle('Shoulders')} stroke={stroke}/>
      <ellipse cx="126" cy="56" rx="20" ry="14" style={fillStyle('Shoulders')} stroke={stroke}/>
      <path d="M70 60 Q100 55 130 60 L130 92 Q100 100 70 92 Z" style={fillStyle('Chest')} stroke={stroke}/>
      <line x1="100" y1="60" x2="100" y2="95" stroke="rgba(255,255,255,0.05)"/>
      <rect x="78" y="95" width="44" height="50" rx="4" style={fillStyle('Core')} stroke={stroke}/>
      <line x1="100" y1="98" x2="100" y2="142" stroke="rgba(255,255,255,0.05)"/>
      <line x1="80" y1="115" x2="120" y2="115" stroke="rgba(255,255,255,0.05)"/>
      <line x1="80" y1="130" x2="120" y2="130" stroke="rgba(255,255,255,0.05)"/>
      <ellipse cx="56" cy="80" rx="10" ry="22" style={fillStyle('Biceps')} stroke={stroke}/>
      <ellipse cx="144" cy="80" rx="10" ry="22" style={fillStyle('Biceps')} stroke={stroke}/>
      <ellipse cx="50" cy="115" rx="8" ry="18" style={fillStyle('Triceps')} stroke={stroke}/>
      <ellipse cx="150" cy="115" rx="8" ry="18" style={fillStyle('Triceps')} stroke={stroke}/>
      <rect x="76" y="145" width="48" height="14" rx="6" style={fillStyle('Glutes')} stroke={stroke}/>
      <path d="M76 159 Q72 200 78 230 L96 230 Q98 200 92 159 Z" style={fillStyle('Quads')} stroke={stroke}/>
      <path d="M124 159 Q128 200 122 230 L104 230 Q102 200 108 159 Z" style={fillStyle('Quads')} stroke={stroke}/>
      <rect x="80" y="170" width="14" height="55" rx="6" style={fillStyle('Hamstrings')} fillOpacity="0.7" stroke={stroke}/>
      <rect x="106" y="170" width="14" height="55" rx="6" style={fillStyle('Hamstrings')} fillOpacity="0.7" stroke={stroke}/>
      <ellipse cx="84" cy="250" rx="8" ry="20" style={fillStyle('Calves')} stroke={stroke}/>
      <ellipse cx="116" cy="250" rx="8" ry="20" style={fillStyle('Calves')} stroke={stroke}/>
    </svg>
  );
}

/* ============================================================
   CHARTS
   ============================================================ */
function LineChart({ data, height=100, color='var(--accent)', formatY }) {
  const ref = useRef();
  const [w, setW] = useState(300);
  const [hover, setHover] = useState(null);
  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(entries => setW(entries[0].contentRect.width));
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);

  if (!data || data.length < 2) {
    return <div ref={ref} style={{ height, color:'var(--text-mute)', fontSize:12, display:'grid', placeItems:'center' }}>Not enough data</div>;
  }

  const pad = { l:6, r:6, t:14, b:20 };
  const ys = data.map(d => d.y);
  const min = Math.min(...ys), max = Math.max(...ys);
  const range = max - min || 1;
  const xStep = (w - pad.l - pad.r) / (data.length - 1);
  const yFor = v => pad.t + (1 - (v - min) / range) * (height - pad.t - pad.b);
  const points = data.map((d, i) => ({ x: pad.l + i * xStep, y: yFor(d.y), d }));
  const pathLine = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ');
  const pathFill = `${pathLine} L ${points[points.length-1].x} ${height-pad.b} L ${points[0].x} ${height-pad.b} Z`;
  const gradId = 'grad-' + color.replace(/[^a-z0-9]/gi, '');
  const gridLines = [0, 0.25, 0.5, 0.75, 1].map(t => pad.t + t * (height - pad.t - pad.b));

  return (
    <div ref={ref} style={{ position:'relative', width:'100%', height }}>
      <svg width={w} height={height}
        onMouseLeave={() => setHover(null)}
        onMouseMove={e => {
          const rect = e.currentTarget.getBoundingClientRect();
          const x = e.clientX - rect.left;
          let near = 0;
          for (let i = 1; i < points.length; i++) {
            if (Math.abs(points[i].x - x) < Math.abs(points[near].x - x)) near = i;
          }
          setHover(near);
        }}>
        <defs>
          <linearGradient id={gradId} x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor={color} stopOpacity="0.18"/>
            <stop offset="75%" stopColor={color} stopOpacity="0.04"/>
            <stop offset="100%" stopColor={color} stopOpacity="0"/>
          </linearGradient>
        </defs>
        {gridLines.map((y, i) => (
          <line key={i} x1={pad.l} x2={w - pad.r} y1={y} y2={y} stroke="rgba(255,255,255,0.04)" strokeWidth="1"/>
        ))}
        <path d={pathFill} fill={`url(#${gradId})`}/>
        <path d={pathLine} fill="none" stroke={color} strokeWidth="2" strokeLinejoin="round" strokeLinecap="round"/>
        {points.map((p, i) => (
          <circle key={i} cx={p.x} cy={p.y} r={hover === i ? 4.5 : 0} fill={color} stroke="var(--bg)" strokeWidth="2"/>
        ))}
        {hover != null && <line x1={points[hover].x} x2={points[hover].x} y1={pad.t} y2={height-pad.b} stroke="rgba(255,255,255,0.1)" strokeDasharray="3 3"/>}
      </svg>
      {hover != null && (
        <div style={{
          position:'absolute', top:0,
          left: Math.min(Math.max(points[hover].x - 54, 0), w - 108),
          background:'var(--surface-3)', border:'1px solid var(--border-strong)',
          borderRadius:8, padding:'6px 10px', fontSize:11, pointerEvents:'none', whiteSpace:'nowrap',
          boxShadow:'var(--shadow-md)', animation:'scale-in 0.1s ease',
        }}>
          <div className="mono" style={{ color:'var(--text)', fontWeight:600 }}>{formatY ? formatY(data[hover].y) : data[hover].y}</div>
          <div style={{ color:'var(--text-mute)', fontSize:10, marginTop:2 }}>{data[hover].label}</div>
        </div>
      )}
    </div>
  );
}

function BarChart({ data, height=140 }) {
  const max = Math.max(...data.map(d => Math.max(d.value, d.secondary || 0))) || 1;
  return (
    <div style={{ display:'flex', alignItems:'flex-end', gap:8, height, paddingTop:6 }}>
      {data.map((d, i) => (
        <div key={i} style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center', gap:6, minWidth:0 }}>
          <div style={{ flex:1, width:'100%', position:'relative', display:'flex', alignItems:'flex-end', justifyContent:'center', gap:3 }}>
            {d.secondary != null && (
              <div style={{ width:'38%', height:`${(d.secondary/max)*100}%`, background:'var(--surface-3)', borderRadius:'3px 3px 0 0', minHeight:2 }}/>
            )}
            <div style={{
              width: d.secondary != null ? '38%' : '70%',
              height:`${(d.value/max)*100}%`,
              background: d.color || 'var(--accent)',
              borderRadius:'3px 3px 0 0', minHeight:2,
              boxShadow: d.color ? 'none' : '0 0 10px var(--accent-glow)',
            }}/>
          </div>
          <div style={{ fontSize:10, color:'var(--text-mute)', textAlign:'center', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis', width:'100%' }}>{d.label}</div>
        </div>
      ))}
    </div>
  );
}

function ProgressRing({ value, total, size=44, color }) {
  const pct = Math.min(1, value / (total || 1));
  const r = (size - 5) / 2;
  const c = 2 * Math.PI * r;
  const off = c * (1 - pct);
  const clr = color || 'var(--accent)';
  return (
    <div style={{ position:'relative', width:size, height:size }}>
      <svg width={size} height={size} style={{ transform:'rotate(-90deg)' }}>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="var(--surface-3)" strokeWidth="3"/>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={clr} strokeWidth="3"
          strokeDasharray={c} strokeDashoffset={off} strokeLinecap="round"
          style={{ transition:'stroke-dashoffset 0.6s ease' }}/>
      </svg>
      <div className="mono" style={{ position:'absolute', inset:0, display:'grid', placeItems:'center', fontSize:10, fontWeight:600, color:'var(--text)' }}>
        {value}
      </div>
    </div>
  );
}

/* ============================================================
   ERROR BOUNDARY
   ============================================================ */
class ErrorBoundary extends React.Component {
  constructor(props) { super(props); this.state = { error: null }; }
  static getDerivedStateFromError(error) { return { error }; }
  componentDidCatch(error, info) { console.error('WorkoutLog crashed:', error, info); }
  render() {
    if (!this.state.error) return this.props.children;
    return (
      <div style={{ minHeight:'100vh', display:'grid', placeItems:'center', padding:24, textAlign:'center' }}>
        <div style={{ maxWidth:420 }}>
          <div style={{ fontSize:15, fontWeight:800, color:'var(--text)', marginBottom:8 }}>Something broke.</div>
          <div style={{ fontSize:12, color:'var(--text-dim)', marginBottom:16 }}>
            Your data is safe — it's stored locally. Reloading usually fixes this.
          </div>
          <pre style={{ fontSize:10, color:'var(--text-mute)', background:'var(--surface)', border:'1px solid var(--border)', borderRadius:8, padding:'10px 12px', textAlign:'left', overflow:'auto', maxHeight:160, whiteSpace:'pre-wrap' }}>
            {String(this.state.error?.stack || this.state.error)}
          </pre>
          <button onClick={() => window.location.reload()} style={{ marginTop:16, height:42, padding:'0 20px', borderRadius:10, background:'var(--accent)', color:'#06080d', fontWeight:800, fontSize:14, border:'none' }}>
            Reload
          </button>
        </div>
      </div>
    );
  }
}

/* ============================================================
   CONFIRM / ALERT DIALOG  (styled replacements for window.confirm / window.alert)
   Usage:  if (await window.uiConfirm('message', { title, confirmLabel, danger })) {...}
           await window.uiAlert('message', { title, confirmLabel })   // single-button notice
   ============================================================ */
function ConfirmHost() {
  const [req, setReq] = useState(null);
  useEffect(() => {
    window.uiConfirm = (message, opts = {}) => new Promise(resolve => {
      setReq({
        message, resolve,
        title: opts.title || 'Are you sure?',
        confirmLabel: opts.confirmLabel || 'Confirm',
        cancelLabel: opts.cancelLabel || 'Cancel',
        danger: opts.danger !== false,
      });
    });
    window.uiAlert = (message, opts = {}) => new Promise(resolve => {
      setReq({
        message, resolve, alert: true,
        title: opts.title || 'Heads up',
        confirmLabel: opts.confirmLabel || 'Got it',
        danger: false,
      });
    });
  }, []);
  // Hardware back cancels the dialog on mobile (BackNav lives in mobile-app.jsx; absent on desktop).
  useEffect(() => {
    if (!req || !window.wlBackPush) return;
    const id = window.wlBackPush(() => { const r = req.resolve; setReq(null); r(false); });
    return () => window.wlBackRemove(id);
  }, [req]);
  if (!req) return null;
  const close = val => { const r = req.resolve; setReq(null); r(val); };
  return (
    <div onClick={() => close(false)} style={{ position:'fixed', inset:0, zIndex:300, background:'rgba(5,7,9,0.78)', backdropFilter:'blur(6px)', WebkitBackdropFilter:'blur(6px)', display:'grid', placeItems:'center', padding:24, animation:'fade-up 0.15s ease' }}>
      <div onClick={e => e.stopPropagation()} style={{ width:340, maxWidth:'100%', background:'linear-gradient(180deg, rgba(255,255,255,0.035) 0%, transparent 50%), var(--surface)', border:'1px solid var(--border-strong)', borderRadius:16, padding:'22px 22px 18px', boxShadow:'var(--shadow-lg)', animation:'scale-in 0.18s cubic-bezier(0.34,1.2,0.64,1)' }}>
        <div style={{ fontSize:16, fontWeight:800, letterSpacing:-0.3, marginBottom:8 }}>{req.title}</div>
        <div style={{ fontSize:13, color:'var(--text-dim)', lineHeight:1.5, marginBottom:20 }}>{req.message}</div>
        <div style={{ display:'flex', gap:10 }}>
          {!req.alert && <button onClick={() => close(false)} style={{ flex:1, height:44, borderRadius:11, background:'var(--surface-2)', color:'var(--text)', border:'1px solid var(--border-strong)', fontWeight:700, fontSize:14 }}>{req.cancelLabel}</button>}
          <button onClick={() => close(true)} style={{ flex:1, height:44, borderRadius:11, background: req.danger ? 'var(--danger)' : 'var(--accent)', color: req.danger ? '#fff' : '#06080d', border:'none', fontWeight:800, fontSize:14 }}>{req.confirmLabel}</button>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   FLOATING REST TIMER
   ============================================================ */
function RestTimer({ seconds, startedAt, onDismiss, paused, setPaused }) {
  // Wall-clock based: endAt tracks when the timer should reach zero.
  // This keeps working after the app goes to background.
  const endAt = useRef((startedAt || Date.now()) + seconds * 1000);
  const pausedAt = useRef(null);
  const total = useRef(seconds);
  const buzzed = useRef(false);

  const calcRemaining = () => Math.max(0, Math.round((endAt.current - Date.now()) / 1000));
  const [remaining, setRemaining] = useState(calcRemaining);
  // Reset when a new timer is started
  useEffect(() => {
    endAt.current = (startedAt || Date.now()) + seconds * 1000;
    pausedAt.current = null;
    total.current = seconds;
    setRemaining(calcRemaining());
  }, [seconds, startedAt]);

  // Tick loop — uses wall clock so background gaps are recovered instantly on return
  useEffect(() => {
    if (paused) {
      pausedAt.current = Date.now();
      return;
    }
    // Unpausing: shift endAt forward by time spent paused
    if (pausedAt.current !== null) {
      endAt.current += Date.now() - pausedAt.current;
      pausedAt.current = null;
    }

    const tick = () => setRemaining(calcRemaining());
    tick();
    const id = setInterval(tick, 500);
    // Instantly resync when user returns to the app/tab from background
    document.addEventListener('visibilitychange', tick);
    return () => { clearInterval(id); document.removeEventListener('visibilitychange', tick); };
  }, [paused]);

  const done = remaining <= 0;
  // Buzz once when the timer reaches zero (foreground PWA only — works through a pocket).
  useEffect(() => {
    if (done && !buzzed.current) { buzzed.current = true; navigator.vibrate?.([180, 90, 180]); }
    if (!done) buzzed.current = false;
  }, [done]);
  // Nudge the running timer (−15s / +30s). Works whether running or paused; bumps total
  // so the progress ring stays sensible, and re-arms the end-buzz if we add time after zero.
  const adjust = delta => {
    navigator.vibrate?.(6);
    endAt.current += delta * 1000;
    total.current = Math.max(1, total.current + delta);
    const base = (paused && pausedAt.current !== null) ? pausedAt.current : Date.now();
    const r = Math.max(0, Math.round((endAt.current - base) / 1000));
    if (total.current < r) total.current = r;
    if (r > 0) buzzed.current = false;
    setRemaining(r);
  };

  const pct = total.current > 0 ? 1 - remaining / total.current : 1;
  const m = Math.floor(remaining / 60);
  const s = remaining % 60;

  return (
    <div style={{
      position:'fixed', bottom:72, right:24, zIndex:50,
      background: done ? 'var(--accent)' : 'var(--surface-2)',
      color: done ? '#06080d' : 'var(--text)',
      border:`1px solid ${done ? 'var(--accent)' : 'var(--border-strong)'}`,
      borderRadius:999, padding:'12px 17px 12px 14px',
      display:'flex', alignItems:'center', gap:14,
      boxShadow: done ? '0 0 40px var(--accent-glow), 0 8px 30px rgba(0,0,0,0.4)' : '0 8px 30px rgba(0,0,0,0.4)',
      animation:'slide-in-right 0.3s ease', minWidth:240, maxWidth:'calc(100vw - 40px)',
    }}>
      <div style={{ position:'relative', width:38, height:38, flexShrink:0 }}>
        <svg width={38} height={38} style={{ transform:'rotate(-90deg)' }}>
          <circle cx={19} cy={19} r={16} fill="none" stroke={done ? 'rgba(6,8,13,0.2)' : 'var(--surface-3)'} strokeWidth="3"/>
          <circle cx={19} cy={19} r={16} fill="none" stroke={done ? '#06080d' : 'var(--accent)'} strokeWidth="3"
            strokeDasharray={2*Math.PI*16} strokeDashoffset={2*Math.PI*16*(1-pct)} strokeLinecap="round"
            style={{ transition:'stroke-dashoffset 0.5s linear' }}/>
        </svg>
        <div style={{ position:'absolute', inset:0, display:'grid', placeItems:'center' }}>
          {done ? <IconCheck size={17} sw={2.5}/> : <IconClock size={15}/>}
        </div>
      </div>
      <div style={{ flex:1, minWidth:0 }}>
        <div className="mono" style={{ fontSize:19, fontWeight:700, lineHeight:1.1 }}>
          {done ? 'Ready!' : `${m}:${String(s).padStart(2,'0')}`}
        </div>
        <div style={{ fontSize:13, opacity:0.7, marginTop:2 }}>{done ? 'Next set' : 'Rest timer'}</div>
      </div>
      <div style={{ display:'flex', gap:5, alignItems:'center' }}>
        {!done && (
          <div style={{ display:'flex', alignItems:'center', borderRadius:999, overflow:'hidden', border:'1px solid var(--border-strong)', flexShrink:0 }}>
            <button onClick={() => adjust(-15)} className="mono" style={{ padding:'0 9px', height:34, background:'rgba(255,255,255,0.05)', color:'var(--text)', border:'none', fontSize:12, fontWeight:700 }}>−15</button>
            <button onClick={() => adjust(30)} className="mono" style={{ padding:'0 9px', height:34, background:'rgba(255,255,255,0.05)', color:'var(--text)', border:'none', borderLeft:'1px solid var(--border-strong)', fontSize:12, fontWeight:700 }}>+30</button>
          </div>
        )}
        {!done && setPaused && (
          <button onClick={() => setPaused(p => !p)} style={{
            width:34, height:34, borderRadius:999, display:'grid', placeItems:'center',
            background:'rgba(255,255,255,0.06)', color:'var(--text)',
          }}>{paused ? <IconPlay size={14}/> : <IconPause size={14}/>}</button>
        )}
        <button onClick={onDismiss} style={{
          width:34, height:34, borderRadius:999, display:'grid', placeItems:'center',
          background: done ? 'rgba(6,8,13,0.12)' : 'rgba(255,255,255,0.06)',
          color: done ? '#06080d' : 'var(--text)',
        }}><IconX size={14}/></button>
      </div>
    </div>
  );
}

/* ============================================================
   MODAL
   ============================================================ */
function Modal({ open, onClose, title, children, width=480 }) {
  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position:'fixed', inset:0, background:'rgba(5,7,9,0.75)', backdropFilter:'blur(8px)',
      display:'grid', placeItems:'center', zIndex:100, animation:'fade-up 0.15s ease',
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        background:'linear-gradient(180deg, rgba(255,255,255,0.03) 0%, transparent 60%), var(--surface)',
        border:'1px solid var(--border-strong)',
        borderRadius:'var(--radius)', width, maxWidth:'calc(100vw - 24px)',
        maxHeight:'calc(100vh - 48px)', overflow:'auto',
        boxShadow:'var(--shadow-lg)',
        animation:'scale-in 0.18s cubic-bezier(0.34,1.2,0.64,1)',
      }}>
        <div style={{
          display:'flex', alignItems:'center', justifyContent:'space-between',
          padding:'16px 20px', borderBottom:'1px solid var(--border)',
          position:'sticky', top:0,
          background:'linear-gradient(180deg, rgba(255,255,255,0.025) 0%, transparent 100%), var(--surface)',
          zIndex:1,
        }}>
          <div style={{ fontWeight:700, fontSize:15, letterSpacing:-0.2 }}>{title}</div>
          <button onClick={onClose} style={{
            width:28, height:28, borderRadius:7, display:'grid', placeItems:'center',
            color:'var(--text-mute)', transition:'color 0.12s, background 0.12s',
          }}
            onMouseEnter={e => { e.currentTarget.style.color='var(--text)'; e.currentTarget.style.background='rgba(255,255,255,0.06)'; }}
            onMouseLeave={e => { e.currentTarget.style.color='var(--text-mute)'; e.currentTarget.style.background='transparent'; }}
          ><IconX size={15}/></button>
        </div>
        <div style={{ padding:20 }}>{children}</div>
      </div>
    </div>
  );
}

/* ============================================================
   PR FLASH OVERLAY
   ============================================================ */
function PRFlash({ exerciseName, weight, reps, units, onDismiss }) {
  const dismissRef = useRef(onDismiss);
  useEffect(() => { dismissRef.current = onDismiss; }, [onDismiss]);
  useEffect(() => {
    const t = setTimeout(() => dismissRef.current?.(), 3200);
    return () => clearTimeout(t);
  }, []);
  return ReactDOM.createPortal(
    <div onClick={onDismiss} style={{ position:'fixed', inset:0, cursor:'pointer', zIndex:200, display:'grid', placeItems:'center' }}>
      <div style={{ position:'absolute', inset:0, background:'radial-gradient(circle at center, color-mix(in srgb, var(--accent) 12%, transparent), transparent 60%)', animation:'fade-up 0.3s ease' }}/>
      <div style={{
        background:'var(--accent)', color:'#06080d',
        padding:'24px 36px', borderRadius:20,
        boxShadow:'0 0 80px var(--accent-glow), 0 20px 60px rgba(0,0,0,0.5)',
        textAlign:'center', animation:'pr-flash 0.5s cubic-bezier(0.34,1.56,0.64,1)',
        position:'relative',
      }}>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'center', gap:8, fontSize:11, fontWeight:800, letterSpacing:2, textTransform:'uppercase' }}>
          <IconTrophy size={14} sw={2.4}/> New Personal Record
        </div>
        <div style={{ fontSize:22, fontWeight:800, marginTop:8 }}>{exerciseName}</div>
        <div className="mono" style={{ fontSize:26, fontWeight:700, marginTop:6 }}>{weight}{units} × {reps}</div>
        <div style={{ fontSize:10, fontWeight:700, letterSpacing:1, textTransform:'uppercase', marginTop:10, opacity:0.55 }}>Tap to dismiss</div>
      </div>
    </div>,
    document.body
  );
}

function WorkoutCalendar({ sessions, onDayPress }) {
  const COLORS = { Push:'#f87171', Pull:'#60a5fa', Legs:'#a78bfa' };
  const colorFor = name => COLORS[name] || 'var(--accent)';
  const [monthOffset, setMonthOffset] = useState(0);

  const today = new Date();
  const baseDate = new Date(today.getFullYear(), today.getMonth() + monthOffset, 1);
  const year = baseDate.getFullYear();
  const month = baseDate.getMonth();

  const dayMap = {};
  sessions.forEach(s => {
    const d = new Date(s.startedAt || s.date);
    if (d.getFullYear() === year && d.getMonth() === month) {
      const day = d.getDate();
      if (!dayMap[day]) dayMap[day] = [];
      dayMap[day].push(s);
    }
  });

  const firstDow = new Date(year, month, 1).getDay();
  const offset = firstDow === 0 ? 6 : firstDow - 1;
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const cells = Array(offset).fill(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(d);
  while (cells.length % 7 !== 0) cells.push(null);

  const todayDate = today.getDate();
  const monthLabel = baseDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
  const DAY_LABELS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];

  const usedInMonth = new Set(Object.values(dayMap).flat().map(s => s.name));
  const legendEntries = [
    ...Object.entries(COLORS).filter(([n]) => usedInMonth.has(n)),
    ...(([...usedInMonth].some(t => !COLORS[t]) && usedInMonth.size) ? [['Other', 'var(--accent)']] : []),
  ];

  const navBtn = (disabled) => ({
    width:24, height:24, borderRadius:6, display:'grid', placeItems:'center',
    background:'transparent', border:'none', fontSize:16, lineHeight:1,
    color: disabled ? 'var(--text-faint)' : 'var(--text-dim)',
    cursor: disabled ? 'default' : 'pointer',
  });

  return (
    <div>
      <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:10 }}>
        <button style={navBtn(false)} onClick={() => setMonthOffset(o => o - 1)}>‹</button>
        <div style={{ fontSize:11, fontWeight:700, color:'var(--text-dim)', textTransform:'uppercase', letterSpacing:0.6 }}>
          {monthLabel}
        </div>
        <button style={navBtn(monthOffset >= 0)} onClick={() => setMonthOffset(o => Math.min(0, o + 1))} disabled={monthOffset >= 0}>›</button>
      </div>
      <div style={{ display:'grid', gridTemplateColumns:'repeat(7,1fr)', marginBottom:4 }}>
        {DAY_LABELS.map(d => (
          <div key={d} style={{ textAlign:'center', fontSize:9, fontWeight:700, color:'var(--text-faint)', letterSpacing:0.5, paddingBottom:4 }}>{d}</div>
        ))}
      </div>
      <div style={{ display:'grid', gridTemplateColumns:'repeat(7,1fr)', gap:3 }}>
        {cells.map((day, i) => {
          if (!day) return <div key={`e${i}`}/>;
          const daySessions = dayMap[day] || [];
          const isToday = monthOffset === 0 && day === todayDate;
          const primary = daySessions[0];
          const color = primary ? colorFor(primary.name) : null;
          const clickable = onDayPress && daySessions.length > 0;
          const Cell = clickable ? 'button' : 'div';
          return (
            <Cell key={day}
              onClick={clickable ? () => onDayPress(daySessions) : undefined}
              style={{
                position:'relative', overflow:'hidden', borderRadius:7,
                padding:'6px 0 5px', display:'flex', flexDirection:'column', alignItems:'center', gap:3,
                background: color ? `${color}15` : 'transparent',
                border: isToday ? '1.5px solid var(--accent)' : '1px solid transparent',
                minHeight:36, width:'100%',
                cursor: clickable ? 'pointer' : 'default',
              }}>
              {color && <div style={{ position:'absolute', top:0, left:0, right:0, height:2, background:color, opacity:0.85 }}/>}
              <span style={{
                fontSize:11, lineHeight:1,
                fontWeight: isToday ? 700 : daySessions.length ? 600 : 400,
                color: isToday ? 'var(--accent)' : daySessions.length ? 'var(--text)' : 'var(--text-mute)',
              }}>{day}</span>
              {daySessions.length > 0 && (
                <div style={{ display:'flex', gap:2, justifyContent:'center' }}>
                  {[...new Set(daySessions.map(s => s.name))].slice(0,3).map((name, ti) => (
                    <div key={ti} style={{ width:4, height:4, borderRadius:'50%', background:colorFor(name), flexShrink:0 }}/>
                  ))}
                </div>
              )}
            </Cell>
          );
        })}
      </div>
      {legendEntries.length > 0 && (
        <div style={{ display:'flex', gap:10, marginTop:10, flexWrap:'wrap' }}>
          {legendEntries.map(([name, color]) => (
            <div key={name} style={{ display:'flex', alignItems:'center', gap:4 }}>
              <div style={{ width:7, height:7, borderRadius:2, background:color, flexShrink:0 }}/>
              <span style={{ fontSize:10, color:'var(--text-mute)', fontWeight:600 }}>{name}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

Object.assign(window, {
  IconDashboard, IconActive, IconHistory, IconAnalytics, IconLibrary, IconSettings,
  IconPlus, IconChevronRight, IconChevronLeft, IconChevronDown, IconCheck, IconX,
  IconTrophy, IconClock, IconPlay, IconPause, IconSearch, IconFlame, IconArrowUp,
  IconDots, IconNote, IconEdit, IconTrash, IconCalendar,
  MuscleBadge, EquipmentBadge, RIRBadge, PRBadge, DropTag, MyoTag,
  Button, Card, SectionHeading, Field, fieldStyle, Toggle,
  Sidebar, BottomNav, BodyDiagram, WorkoutCalendar,
  LineChart, BarChart, ProgressRing,
  RestTimer, Modal, PRFlash, ErrorBoundary, ConfirmHost,
  // default fallbacks so callers can always `await` these; ConfirmHost overrides on mount
  uiConfirm: (message) => Promise.resolve(window.confirm(message)),
  uiAlert: (message) => Promise.resolve(window.alert(message)),
});
