// VYB LIFE — Overview widgets (compact summaries shown on the Overview page)

// ─── Mobile Header + Drawer ─────────────────────────────────────
// Layout: [☰ hamburger]   [VYB logo centered]   [avatar]
// Avatar → Profile. Hamburger → MobileNavDrawer (nav + Settings).
const MobileHeader = ({ userName, userEmail, userAvatar, onOpenProfile, onOpenMenu, onLogoClick }) => {
  const initial = (userName || userEmail || '?').charAt(0).toUpperCase();
  // Topbar stays dark in both themes — icons/text are forced light here.
  const onDarkIcon = '#FAFAF8';
  const onDarkIconMuted = 'rgba(250,250,248,0.78)';
  return (
    <div className="vyb-mobile-header"
      style={{display:'grid',gridTemplateColumns:'44px 1fr 44px',alignItems:'center',
        padding:'10px 14px',borderBottom:'1px solid rgba(255,255,255,0.08)',
        position:'sticky',top:0,background:'rgba(13,12,11,0.92)',backdropFilter:'blur(14px)',WebkitBackdropFilter:'blur(14px)',zIndex:50}}>
      <button type="button" onClick={onOpenMenu} aria-label="Open menu"
        className="vyb-circle-btn"
        style={{justifySelf:'start',width:40,height:40,display:'flex',alignItems:'center',justifyContent:'center',
          cursor:'pointer',background:'transparent',border:'none',color:onDarkIcon}}>
        <Icon name="menu" size={22} color={onDarkIcon} sw={2}/>
      </button>
      <div style={{justifySelf:'center',display:'flex',alignItems:'center',minWidth:0,padding:'0 8px'}}>
        <img src="assets/logo-white.png" alt="VYB LIFE"
          onClick={onLogoClick}
          className="vyb-logo vyb-logo-on-dark"
          style={{height:42,maxHeight:42,maxWidth:'100%',opacity:0.95,display:'block',objectFit:'contain',cursor:onLogoClick?'pointer':'default'}}/>
      </div>
      <button type="button" onClick={onOpenProfile} aria-label="Open profile"
        className="vyb-circle-btn"
        style={{justifySelf:'end',width:36,height:36,
          background:'rgba(160,138,86,0.18)',border:'1px solid rgba(204,186,147,0.45)',
          display:'flex',alignItems:'center',justifyContent:'center',overflow:'hidden',cursor:'pointer'}}>
        {userAvatar
          ? <img src={userAvatar} alt="" style={{width:'100%',height:'100%',objectFit:'cover',borderRadius:'inherit'}}/>
          : <span style={{fontSize:13,fontWeight:700,color:'#CCBA93',fontStyle:'italic',lineHeight:1}}>{initial}</span>}
      </button>
    </div>
  );
};

const MobileNavDrawer = ({ open, onClose, active, setActive, onViewProfile, onOpenSettings, onLogoClick, userName, userEmail }) => {
  const tr = useT();
  // Lock background scroll while open + close on Escape.
  React.useEffect(() => {
    if (!open) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => { document.body.style.overflow = prev; window.removeEventListener('keydown', onKey); };
  }, [open, onClose]);
  if (!open) return null;
  const go = (id) => { setActive(id); onClose(); };
  const display = userName || userEmail || '';
  return (
    <div onClick={onClose}
      style={{position:'fixed',inset:0,zIndex:1200,background:'rgba(0,0,0,0.55)',backdropFilter:'blur(6px)',
        display:'flex',justifyContent:'flex-start'}}>
      <div onClick={e=>e.stopPropagation()} role="dialog" aria-label="Navigation menu"
        style={{width:'min(86vw, 320px)',height:'100vh',background:C.bg,borderRight:`1px solid ${C.border}`,
          boxShadow:'0 20px 60px rgba(0,0,0,0.6)',display:'flex',flexDirection:'column',
          animation:'vybSlideIn 0.22s cubic-bezier(0.16,1,0.3,1)'}}>
        <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',padding:'14px 16px',borderBottom:`1px solid ${C.border}`}}>
          <img src="assets/logo-white.png" alt="VYB LIFE" className="vyb-logo"
            onClick={()=>{ if (onLogoClick) { onClose(); onLogoClick(); } }}
            style={{height:38,maxHeight:38,opacity:0.95,objectFit:'contain',cursor:onLogoClick?'pointer':'default'}}/>
          <button type="button" onClick={onClose} aria-label="Close menu"
            style={{width:34,height:34,display:'flex',alignItems:'center',justifyContent:'center',
              borderRadius:8,background:'transparent',border:'none',cursor:'pointer',padding:0,color:C.textMuted}}>
            <Icon name="x" size={18} color={C.textMuted}/>
          </button>
        </div>
        <div style={{flex:1,overflowY:'auto',padding:'12px 10px'}}>
          {NAV.map(n => {
            const on = active === n.id;
            return (
              <div key={n.id} onClick={()=>go(n.id)}
                style={{display:'flex',alignItems:'center',gap:12,padding:'12px 12px',borderRadius:10,marginBottom:2,
                  cursor:'pointer',transition:'background 0.15s',
                  background: on ? C.sandFaint : 'transparent',
                  color: on ? C.sandLight : C.textMuted}}>
                <Icon name={n.icon} size={18} color={on ? C.sandLight : C.textFaint}/>
                <span style={{fontSize:13,fontWeight:on?600:400,letterSpacing:'0.04em'}}>{tr('nav.'+n.id)}</span>
              </div>
            );
          })}
        </div>
        <div style={{borderTop:`1px solid ${C.border}`,padding:'12px 10px',marginTop:'auto'}}>
          {(() => {
            const profileOn = active === 'profile';
            return (
              <div onClick={()=>{ if (onViewProfile) { onClose(); onViewProfile(); } else { go('profile'); } }}
                style={{display:'flex',alignItems:'center',gap:12,padding:'12px 12px',borderRadius:10,marginBottom:2,
                  cursor:'pointer',transition:'background 0.15s',
                  background: profileOn ? C.sandFaint : 'transparent',
                  color: profileOn ? C.sandLight : C.textSecondary}}>
                <Icon name="user" size={18} color={profileOn ? C.sandLight : C.textFaint}/>
                <span style={{fontSize:13,fontWeight:profileOn?600:500,letterSpacing:'0.04em'}}>{tr('nav.viewProfile')}</span>
              </div>
            );
          })()}
          <div onClick={()=>{ onClose(); onOpenSettings && onOpenSettings(); }}
            style={{display:'flex',alignItems:'center',gap:12,padding:'12px 12px',borderRadius:10,
              cursor:'pointer',color:C.textSecondary,transition:'background 0.15s'}}>
            <Icon name="settings" size={18} color={C.textFaint}/>
            <span style={{fontSize:13,fontWeight:500,letterSpacing:'0.04em'}}>{tr('nav.settings')}</span>
          </div>
          {display && (
            <div style={{padding:'10px 12px 4px',fontSize:10,fontWeight:300,color:C.textFaint,
              letterSpacing:'0.12em',textTransform:'uppercase',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>
              {display}
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

const MenuItem = ({ icon, label, onClick, danger }) => (
  <div onClick={onClick}
    style={{display:'flex',alignItems:'center',gap:10,padding:'10px 10px',borderRadius:8,cursor:'pointer',
      color: danger ? '#D67B7B' : C.textSecondary, transition:'background 0.15s'}}
    onMouseEnter={e=>e.currentTarget.style.background=C.sandFaint}
    onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
    <Icon name={icon} size={14} color={danger ? '#D67B7B' : C.textMuted}/>
    <span style={{fontSize:11,fontWeight:500,letterSpacing:'0.04em'}}>{label}</span>
  </div>
);

// ─── Sidebar ───────────────────────────────────────────────────
const NAV = [
  {id:'overview',icon:'layout-dashboard',label:'Overview'},
  {id:'tasks',   icon:'list-todo',       label:'Tasks'},
  {id:'habits',  icon:'check-circle',    label:'Habits'},
  {id:'reading', icon:'book-open',       label:'Reading'},
  {id:'ideas',   icon:'lightbulb',       label:'Ideas'},
  {id:'momentum',icon:'trending-up',     label:'Momentum'},
];
const NAV_IDS = NAV.map(n => n.id);

const Sidebar = ({ active, setActive, userName, userEmail, userAvatar, onViewProfile, onOpenSettings, onLogoClick, onSignOut, expanded=false, pinned=false, onTogglePinned, onMouseEnter, onMouseLeave }) => {
  const tr = useT();
  const wk = weekKeys();
  const fmt = k => { const [y,m,d]=k.split('-'); return new Date(+y,+m-1,+d).toLocaleDateString('en-US',{month:'short',day:'numeric'}); };
  const [menuOpen, setMenuOpen] = React.useState(false);
  React.useEffect(() => {
    if (!menuOpen) return;
    const close = () => setMenuOpen(false);
    window.addEventListener('click', close);
    return () => window.removeEventListener('click', close);
  }, [menuOpen]);
  const display = userName || userEmail || '';
  const initial = (display || '?').charAt(0).toUpperCase();
  const slotWidth = pinned ? 220 : 64;
  const visualWidth = expanded ? 220 : 64;
  const overlay = expanded && !pinned;
  return (
    <div style={{width:slotWidth,flexShrink:0,height:'100vh',position:'sticky',top:0,zIndex:50,alignSelf:'flex-start',
      transition:'width 0.22s cubic-bezier(0.16,1,0.3,1)'}}>
      <div className="vyb-sidebar"
        onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}
        style={{width:visualWidth,height:'100vh',background:C.bg,borderRight:`1px solid ${C.border}`,
          display:'flex',flexDirection:'column',position:'absolute',top:0,left:0,
          transition:'width 0.22s cubic-bezier(0.16,1,0.3,1), box-shadow 0.22s ease-out',overflow:'hidden',
          boxShadow: overlay ? '0 24px 60px rgba(0,0,0,0.45)' : 'none'}}>
      {!expanded ? (
        // ── Collapsed: logo (click to pin) + icon-only nav + avatar ──
        <>
          <div onClick={onLogoClick || onTogglePinned} title={onLogoClick ? 'Home' : 'Pin sidebar'}
            style={{padding:'24px 0 20px',display:'flex',justifyContent:'center',cursor:'pointer'}}>
            <img src="assets/logo-white.png" alt="VYB LIFE" className="vyb-logo" style={{width:36,opacity:0.95,objectFit:'contain'}}/>
          </div>
          <div style={{padding:'0 8px',flex:1,display:'flex',flexDirection:'column',alignItems:'center',gap:2}}>
            {NAV.map(n => (
              <div key={n.id} onClick={()=>setActive(n.id)} title={tr('nav.'+n.id)}
                {...(active===n.id ? {} : {'data-nav-item': true})}
                style={{width:44,height:40,display:'flex',alignItems:'center',justifyContent:'center',borderRadius:10,
                  cursor:'pointer',transition:'all 0.18s ease-out',
                  background: active===n.id ? C.sandFaint : 'transparent'}}>
                <Icon name={n.icon} size={16} color={active===n.id ? C.sandLight : C.textFaint}/>
              </div>
            ))}
          </div>
          <div style={{padding:'10px 0 0',display:'flex',justifyContent:'center'}}>
            <div onClick={onOpenSettings} title="Settings"
              style={{width:36,height:32,display:'flex',alignItems:'center',justifyContent:'center',
                borderRadius:8,cursor:'pointer',transition:'background 0.15s'}}
              onMouseEnter={e=>e.currentTarget.style.background='rgba(255,255,255,0.05)'}
              onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
              <Icon name="settings" size={15} color={C.textFaint}/>
            </div>
          </div>
          {display && (
            <div style={{padding:'10px 0 20px',borderTop:`1px solid ${C.border}`,marginTop:10,display:'flex',justifyContent:'center'}}>
              <div onClick={onViewProfile} title={userName || userEmail} style={{cursor:'pointer'}}>
                {userAvatar ? (
                  <img src={userAvatar} alt="" style={{width:30,height:30,borderRadius:999,objectFit:'cover',border:`1px solid ${C.sandGlow}`}}/>
                ) : (
                  <div style={{width:30,height:30,borderRadius:999,background:C.sandFaint,border:`1px solid ${C.sandGlow}`,display:'flex',alignItems:'center',justifyContent:'center'}}>
                    <span style={{fontSize:11,fontWeight:700,color:C.sandLight,fontStyle:'italic'}}>{initial}</span>
                  </div>
                )}
              </div>
            </div>
          )}
        </>
      ) : (
        // ── Expanded: full sidebar with collapse button ──
        <>
          <div style={{padding:'28px 24px 24px',position:'relative'}}>
            <img src="assets/logo-white.png" alt="VYB LIFE" className="vyb-logo"
              onClick={onLogoClick}
              style={{width:82,opacity:0.95,objectFit:'contain',cursor:onLogoClick?'pointer':'default'}} />
            <div onClick={onTogglePinned} title={pinned ? 'Unpin sidebar' : 'Pin sidebar'}
              style={{position:'absolute',top:24,right:14,width:26,height:26,borderRadius:8,
                display:'flex',alignItems:'center',justifyContent:'center',cursor:'pointer',
                color:C.textFaint,transition:'all 0.15s',
                background: pinned ? 'rgba(160,138,86,0.14)' : 'transparent'}}
              onMouseEnter={e=>e.currentTarget.style.background='rgba(255,255,255,0.06)'}
              onMouseLeave={e=>e.currentTarget.style.background = pinned ? 'rgba(160,138,86,0.14)' : 'transparent'}>
              <Icon name={pinned ? 'pin-off' : 'pin'} size={13} color={pinned ? C.sandLight : C.textFaint}/>
            </div>
          </div>
          <div style={{padding:'0 12px',flex:1}}>
            {NAV.map(n => (
              <div key={n.id} onClick={()=>setActive(n.id)}
                {...(active===n.id ? {} : {'data-nav-item': true})}
                style={{display:'flex',alignItems:'center',gap:12,padding:'11px 12px',borderRadius:10,marginBottom:2,
                  cursor:'pointer',transition:'all 0.18s ease-out',
                  background: active===n.id ? C.sandFaint : 'transparent',
                  color: active===n.id ? C.sandLight : C.textMuted}}>
                <Icon name={n.icon} size={16} color={active===n.id ? C.sandLight : C.textFaint} />
                <span style={{fontSize:12,fontWeight:active===n.id?600:400,letterSpacing:'0.04em'}}>{tr('nav.'+n.id)}</span>
              </div>
            ))}
          </div>
          <div style={{padding:20,borderTop:`1px solid ${C.border}`}}>
            <div style={{fontSize:10,letterSpacing:'0.16em',fontWeight:200,color:C.textFaint,textTransform:'uppercase',marginBottom:4}}>Week of</div>
            <div style={{fontSize:12,fontWeight:500,color:C.textSecondary,marginBottom:16}}>{fmt(wk[0])} — {fmt(wk[6])}</div>
            {display && (
              <div style={{position:'relative'}}>
                <div onClick={e=>{ e.stopPropagation(); setMenuOpen(m=>!m); }}
                  style={{display:'flex',alignItems:'center',gap:10,cursor:'pointer',padding:'8px 8px',borderRadius:10,
                    background: menuOpen ? C.sandFaint : 'transparent', transition:'background 0.2s'}}>
                  {userAvatar ? (
                    <img src={userAvatar} alt="" style={{width:28,height:28,borderRadius:999,objectFit:'cover',flexShrink:0,border:`1px solid ${C.sandGlow}`}}/>
                  ) : (
                    <div style={{width:28,height:28,borderRadius:999,background:C.sandFaint,border:`1px solid ${C.sandGlow}`,display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}}>
                      <span style={{fontSize:11,fontWeight:700,color:C.sandLight,fontStyle:'italic'}}>{initial}</span>
                    </div>
                  )}
                  <div style={{minWidth:0,flex:1}}>
                    <div style={{fontSize:11,fontWeight:600,color:C.textPrimary,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{userName || userEmail}</div>
                    <div style={{fontSize:9,fontWeight:300,color:C.textFaint,letterSpacing:'0.1em',textTransform:'uppercase',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>
                      {userName && userEmail ? userEmail : 'Account'}
                    </div>
                  </div>
                  <Icon name="chevron-up" size={14} color={C.textFaint}/>
                </div>
                {menuOpen && (
                  <div onClick={e=>e.stopPropagation()}
                    style={{position:'absolute',bottom:'calc(100% + 6px)',left:0,right:0,background:C.card,
                      border:`1px solid ${C.borderMid}`,borderRadius:10,padding:6,
                      boxShadow:'0 24px 60px rgba(0,0,0,0.5)',zIndex:100}}>
                    <MenuItem icon="user" label="View Profile" onClick={()=>{ setMenuOpen(false); onViewProfile && onViewProfile(); }}/>
                    <MenuItem icon="settings" label="Settings" onClick={()=>{ setMenuOpen(false); onOpenSettings && onOpenSettings(); }}/>
                    <MenuItem icon="log-out" label="Sign Out" onClick={()=>{ setMenuOpen(false); onSignOut && onSignOut(); }} danger/>
                  </div>
                )}
              </div>
            )}
          </div>
        </>
      )}
      </div>
    </div>
  );
};

// ─── Weekly Overview Bar ───────────────────────────────────────
const WeeklyBar = ({ store }) => {
  const tr = useT();
  const wk = weekKeys();
  const tIdx = todayDow();
  const days = ['M','T','W','T','F','S','S'];
  const { habits, tasks, workouts, mood } = store.state;
  const doneTasks = tasks.filter(t=>t.done).length;
  const habitsDoneToday = habits.filter(h=>h.checkIns[todayKey()]).length;
  const workoutsDone = wk.filter(k => workouts[k]?.done).length;
  const moodToday = mood[todayKey()]?.mood;
  return (
    <Card style={{padding:28}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',flexWrap:'wrap',gap:24}}>
        <div>
          <Label mb={8}>{tr('overview.weeklyOverview')}</Label>
          <div style={{fontSize:26,fontWeight:800,fontStyle:'italic',color:C.textPrimary,letterSpacing:'-0.02em',textTransform:'uppercase',lineHeight:1}}>
            {new Date().toLocaleDateString(getLang()==='es'?'es-ES':'en-US',{weekday:'long'})}
          </div>
          <div style={{fontSize:12,fontWeight:300,color:C.textMuted,marginTop:6,letterSpacing:'0.04em'}}>
            {new Date().toLocaleDateString(getLang()==='es'?'es-ES':'en-US',{month:'long',day:'numeric',year:'numeric'})}
          </div>
        </div>
        <div style={{display:'flex',gap:28}}>
          {[
            {label:tr('overview.habitsToday'),val:habits.length?`${habitsDoneToday}/${habits.length}`:'—',color:C.sand},
            {label:tr('overview.tasksDone'),val:tasks.length?`${doneTasks}/${tasks.length}`:'—',color:C.sage},
            {label:tr('overview.workouts'),val:`${workoutsDone}/7`,color:C.sandLight},
            {label:tr('overview.mood'),val:moodToday?`${moodToday}/5`:'—',color:C.sageLight},
          ].map(s=>(
            <div key={s.label} style={{textAlign:'center'}}>
              <div style={{fontSize:22,fontWeight:800,color:s.color,fontStyle:'italic'}}>{s.val}</div>
              <div style={{fontSize:9,fontWeight:200,letterSpacing:'0.16em',color:C.textFaint,textTransform:'uppercase',marginTop:4}}>{s.label}</div>
            </div>
          ))}
        </div>
        <div style={{display:'flex',gap:6,alignItems:'center'}}>
          {days.map((d,i)=>{
            const dayHabits = habits.filter(h=>h.checkIns[wk[i]]).length;
            const pct = habits.length ? dayHabits/habits.length : 0;
            return (
              <div key={i} style={{display:'flex',flexDirection:'column',alignItems:'center',gap:6}}>
                <div style={{fontSize:9,fontWeight:300,letterSpacing:'0.12em',color:i<=tIdx?C.textSecondary:C.textFaint,textTransform:'uppercase'}}>{d}</div>
                <div style={{width:30,height:30,borderRadius:999,border:`1px solid ${i===tIdx?C.sand:C.border}`,
                  background:pct>0?`rgba(160,138,86,${0.15+pct*0.5})`:'transparent',
                  display:'flex',alignItems:'center',justifyContent:'center',position:'relative'}}>
                  {pct>=1 && <Icon name="check" size={11} color={C.sandLight} sw={2.5} />}
                  {i===tIdx && pct<1 && <div style={{width:5,height:5,borderRadius:999,background:C.sand}}/>}
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </Card>
  );
};

// ─── Priorities (overview) ─────────────────────────────────────
const PrioritiesWidget = ({ store, onOpen }) => {
  const tr = useT();
  const urgent = store.state.tasks.filter(t=>t.pri==='urgent'&&!t.done).slice(0,3);
  const topPri = urgent.length ? urgent : store.state.tasks.filter(t=>!t.done).slice(0,3);
  return (
    <Card hoverable onClick={onOpen}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start'}}>
        <div>
          <Label>{tr('overview.focus')}</Label>
          <Title>{tr('overview.topPriorities')}</Title>
        </div>
        <Icon name="arrow-up-right" size={16} color={C.textFaint} />
      </div>
      {topPri.length === 0 ? (
        <Empty icon="target" title={tr('overview.noPriorities')} sub={tr('overview.addUrgent')} />
      ) : (
        <div style={{display:'flex',flexDirection:'column',gap:10}}>
          {topPri.map((t,i)=>(
            <div key={t.id} style={{display:'flex',alignItems:'center',gap:14,padding:'12px 14px',borderRadius:10,
              border:`1px solid ${C.border}`,background:'rgba(255,255,255,0.02)'}}>
              <div style={{width:26,height:26,borderRadius:999,border:`1px solid ${C.sandGlow}`,
                display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}}>
                <span style={{fontSize:10,fontWeight:800,color:C.sand,fontStyle:'italic'}}>{i+1}</span>
              </div>
              <span style={{fontSize:12,fontWeight:500,color:C.textPrimary,lineHeight:1.4}}>{t.text}</span>
              {t.pri==='urgent' && <div style={{marginLeft:'auto',padding:'3px 8px',borderRadius:999,background:C.clayFaint,fontSize:8,fontWeight:700,letterSpacing:'0.14em',color:'#D0825A',textTransform:'uppercase'}}>{tr('overview.urgent')}</div>}
            </div>
          ))}
        </div>
      )}
    </Card>
  );
};

// ─── Mini: Habits completed today ──────────────────────────────
// Compact "X / Y completados" card. Counts today-scheduled habits and how
// many are checked in for today. Calm by default; warm gold once any are
// done; subtle "complete" state when all planned habits are finished.
const HabitsTodayMini = ({ store, onOpen }) => {
  const tr = useT();
  const today = todayKey();
  const todayDate = React.useMemo(() => new Date(), []);
  const isSched = window.isHabitScheduledOn || (() => true);
  const planned = (store.state.habits || []).filter(h => isSched(h, todayDate));
  const total = planned.length;
  const completed = planned.filter(h => h.checkIns && h.checkIns[today]).length;
  const pct = total ? completed / total : 0;
  const allDone = total > 0 && completed === total;

  // Ring geometry — keep the SVG itself responsive (viewBox scales).
  const SIZE = 100;
  const STROKE = 9;
  const R = (SIZE / 2) - (STROKE / 2);
  const CIRC = 2 * Math.PI * R;
  const offset = CIRC * (1 - pct);
  // Render size: bigger on desktop so the ring fills the card properly.
  const [_isMobile, _setIsMobile] = React.useState(() =>
    typeof window !== 'undefined' && window.matchMedia
      ? window.matchMedia('(max-width: 768px)').matches : false);
  React.useEffect(() => {
    if (typeof window === 'undefined' || !window.matchMedia) return;
    const mq = window.matchMedia('(max-width: 768px)');
    const h = (e) => _setIsMobile(e.matches);
    mq.addEventListener ? mq.addEventListener('change', h) : mq.addListener(h);
    return () => mq.removeEventListener ? mq.removeEventListener('change', h) : mq.removeListener(h);
  }, []);
  const ringPx = _isMobile ? 108 : 156;
  const numPx = _isMobile ? 30 : 44;
  const totalPx = _isMobile ? 18 : 24;

  // Color palette: sand by default, brighter gold when fully completed.
  const ringTrack = 'rgba(255,255,255,0.06)';
  const ringStart = allDone ? '#E8C97A' : C.sand;
  const ringEnd   = allDone ? '#F5DD9B' : C.sandLight;
  const numColor  = allDone ? '#F5DD9B' : C.sandLight;
  const totalColor = C.textFaint;

  // Subtle warm glow on completion. Kept refined — no animation, no confetti.
  const completedGlow = allDone
    ? '0 0 0 1px rgba(232,201,122,0.35), 0 0 32px -8px rgba(232,201,122,0.45)'
    : undefined;
  const completedBorder = allDone ? 'rgba(232,201,122,0.45)' : undefined;

  return (
    <Card hoverable onClick={onOpen}
      style={{display:'flex',flexDirection:'column',height:'100%',
        boxShadow:completedGlow,
        ...(completedBorder ? { borderColor: completedBorder } : {})}}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:8}}>
        <Label mb={0}>{tr('overviewMini.habitsTodayTitle')}</Label>
        <Icon name="arrow-up-right" size={16} color={C.textFaint}/>
      </div>
      <div style={{flex:1,display:'flex',alignItems:'center',justifyContent:'center',
        padding:'8px 0'}}>
        {total === 0 ? (
          <div style={{fontSize:14,fontWeight:300,color:C.textMuted,fontStyle:'italic',
            textAlign:'center',lineHeight:1.4}}>
            {tr('overviewMini.habitsTodayNone')}
          </div>
        ) : (
          <div style={{position:'relative',width:ringPx,height:ringPx,
            display:'flex',alignItems:'center',justifyContent:'center'}}>
            <svg width={ringPx} height={ringPx} viewBox={`0 0 ${SIZE} ${SIZE}`}
              style={{position:'absolute',inset:0,transform:'rotate(-90deg)'}}>
              <defs>
                <linearGradient id={`vyb-habits-ring-${allDone?'done':'go'}`} x1="0" y1="0" x2="1" y2="1">
                  <stop offset="0%" stopColor={ringStart}/>
                  <stop offset="100%" stopColor={ringEnd}/>
                </linearGradient>
              </defs>
              <circle cx={SIZE/2} cy={SIZE/2} r={R}
                fill="none" stroke={ringTrack} strokeWidth={STROKE}/>
              <circle cx={SIZE/2} cy={SIZE/2} r={R}
                fill="none" stroke={`url(#vyb-habits-ring-${allDone?'done':'go'})`}
                strokeWidth={STROKE} strokeLinecap="round"
                strokeDasharray={CIRC} strokeDashoffset={offset}
                style={{transition:'stroke-dashoffset 0.6s cubic-bezier(0.16,1,0.3,1)'}}/>
            </svg>
            <div style={{position:'relative',display:'flex',alignItems:'baseline',
              gap:2,fontStyle:'italic',letterSpacing:'-0.025em',lineHeight:1}}>
              <span style={{fontSize:numPx,fontWeight:800,color:numColor}}>{completed}</span>
              <span style={{fontSize:totalPx,fontWeight:300,color:totalColor}}>/{total}</span>
            </div>
          </div>
        )}
      </div>
    </Card>
  );
};

// ─── Mini: Pending tasks ───────────────────────────────────────
// Counts open (not done) tasks. Tap → /tasks.
const PendingTasksMini = ({ store, onOpen }) => {
  const tr = useT();
  const open = (store.state.tasks || []).filter(t => t && !t.done).length;
  const accent = open > 0 ? C.sandLight : C.textFaint;
  return (
    <Card hoverable onClick={onOpen}
      style={{display:'flex',flexDirection:'column',height:'100%'}}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:14}}>
        <Label mb={0}>{tr('overviewMini.pendingTasksTitle')}</Label>
        <Icon name="arrow-up-right" size={16} color={C.textFaint}/>
      </div>
      <div style={{flex:1,display:'flex',flexDirection:'column',justifyContent:'center',
        alignItems:'center',gap:6,padding:'8px 0'}}>
        <div style={{fontSize:48,fontWeight:800,fontStyle:'italic',color:accent,
          letterSpacing:'-0.025em',lineHeight:1}}>
          {open}
        </div>
        <div style={{fontSize:10,fontWeight:600,letterSpacing:'0.2em',textTransform:'uppercase',
          color:C.textFaint}}>
          {tr('overviewMini.pendingTasksOpenShort','open')}
        </div>
      </div>
      <div style={{fontSize:11,fontWeight:300,color:C.textMuted,
        letterSpacing:'0.02em',fontStyle:'italic',textAlign:'center'}}>
        {tr('overviewMini.pendingTasksHelper')}
      </div>
    </Card>
  );
};

// ─── Mood widget ───────────────────────────────────────────────
const MoodWidget = ({ store, onOpen }) => {
  const tr = useT();
  const today = todayKey();
  const wk = weekKeys();
  const tIdx = todayDow();
  const todayMood = store.state.mood[today];
  const quickSet = (v) => store.setMoodDay(today, { mood: v });
  const moods = [{v:1,icon:'cloud-rain'},{v:2,icon:'meh'},{v:3,icon:'smile'},{v:4,icon:'sun'},{v:5,icon:'zap'}];
  return (
    <Card hoverable onClick={onOpen}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start'}}>
        <div>
          <Label>{tr('overview.checkin')}</Label>
          <Title>{tr('overview.moodEnergy')}</Title>
        </div>
        <Icon name="arrow-up-right" size={16} color={C.textFaint} />
      </div>
      <div style={{marginBottom:16}}>
        <div style={{fontSize:10,fontWeight:200,letterSpacing:'0.16em',color:C.textFaint,textTransform:'uppercase',marginBottom:10}}>{tr('overview.howAreYou')}</div>
        <div style={{display:'flex',gap:6}}>
          {moods.map(m=>(
            <div key={m.v} onClick={e=>{e.stopPropagation();quickSet(m.v);}}
              style={{flex:1,display:'flex',flexDirection:'column',alignItems:'center',gap:5,padding:'10px 4px',borderRadius:10,cursor:'pointer',
                border:`1px solid ${todayMood?.mood===m.v?C.sand:C.border}`,
                background:todayMood?.mood===m.v?C.sandFaint:'transparent',transition:'all 0.2s'}}>
              <Icon name={m.icon} size={16} color={todayMood?.mood===m.v?C.sand:C.textFaint} />
              <span style={{fontSize:8,color:todayMood?.mood===m.v?C.sandLight:C.textFaint}}>{m.v}</span>
            </div>
          ))}
        </div>
      </div>
      <div>
        <div style={{fontSize:10,fontWeight:200,letterSpacing:'0.16em',color:C.textFaint,textTransform:'uppercase',marginBottom:10}}>{tr('overview.thisWeek')}</div>
        <div style={{display:'flex',gap:5,alignItems:'flex-end',height:40}}>
          {wk.map((k,i)=>{
            const v = store.state.mood[k]?.mood;
            return (
              <div key={i} style={{flex:1,display:'flex',flexDirection:'column',justifyContent:'flex-end',alignItems:'center',gap:3}}>
                <div style={{width:'100%',borderRadius:3,
                  background:v?`rgba(160,138,86,${0.3+v/5*0.6})`:'rgba(255,255,255,0.04)',
                  height:v?(v/5)*34:5,transition:'height 0.4s'}}/>
                <div style={{fontSize:8,color:i===tIdx?C.sand:C.textFaint}}>{'MTWTFSS'[i]}</div>
              </div>
            );
          })}
        </div>
      </div>
    </Card>
  );
};

// ─── Habits widget ─────────────────────────────────────────────
// Daily-only checklist for the Overview command center: one row per habit,
// grouped by category, completable in-place. Reuses store.toggleHabit so
// changes round-trip with the Habits view (no duplicate state).
const HabitsWidget = ({ store, onOpen }) => {
  const tr = useT();
  const { habits, habitCategories=[] } = store.state;
  const today = todayKey();
  const doneToday = habits.filter(h=>h.checkIns[today]).length;
  const pct = habits.length ? Math.round(doneToday/habits.length*100) : 0;

  // Group habits by category, preserving the user's category order.
  const grouped = React.useMemo(() => {
    const byId = new Map();
    for (const c of habitCategories) byId.set(c.id, { cat: c, items: [] });
    const orphan = { cat: { id: '__none__', name: window.dl ? window.dl('ideaCat','other') : 'Other', icon: 'target' }, items: [] };
    for (const h of habits) {
      const bucket = byId.get(h.categoryId) || orphan;
      bucket.items.push(h);
    }
    const out = [...byId.values()].filter(g => g.items.length);
    if (orphan.items.length) out.push(orphan);
    return out;
  }, [habits, habitCategories]);

  return (
    <Card>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:18}}>
        <div>
          <Label>{tr('overview.today')}</Label>
          <Title mb={0}>{tr('overview.dailyHabits')}</Title>
          <div style={{fontSize:11,fontWeight:300,color:C.textMuted,marginTop:8,letterSpacing:'0.02em'}}>
            {tr('overview.smallActions')} {habits.length>0 && (<span style={{color:C.textSecondary}}>{tr('overview.doneOfTotal').replace('{done}', String(doneToday)).replace('{total}', String(habits.length))}</span>)}
          </div>
        </div>
        <div style={{display:'flex',alignItems:'center',gap:14}}>
          <Ring pct={pct} size={48} stroke={3} color={C.sand}>
            <span style={{fontSize:10,fontWeight:700,color:C.sand}}>{pct}%</span>
          </Ring>
          <button onClick={onOpen} title={tr('overview.openHabits')}
            style={{background:'transparent',border:`1px solid ${C.border}`,borderRadius:8,
              padding:'6px 10px',color:C.textMuted,cursor:'pointer',
              fontFamily:'Montserrat,sans-serif',fontSize:9,fontWeight:600,letterSpacing:'0.14em',textTransform:'uppercase',
              display:'inline-flex',alignItems:'center',gap:6,transition:'all 0.15s'}}
            onMouseEnter={e=>{e.currentTarget.style.color=C.textPrimary;e.currentTarget.style.borderColor=C.borderMid;}}
            onMouseLeave={e=>{e.currentTarget.style.color=C.textMuted;e.currentTarget.style.borderColor=C.border;}}>
            {tr('common.open')} <Icon name="arrow-up-right" size={11} color="currentColor"/>
          </button>
        </div>
      </div>

      {habits.length === 0 ? (
        <Empty icon="target" title={tr('overview.noHabitsYet')}
          sub={tr('overview.buildDailySys')}
          action={<button onClick={onOpen}
            style={{background:C.sand,color:C.bg,border:'none',borderRadius:8,padding:'8px 14px',
              fontFamily:'Montserrat,sans-serif',fontSize:10,fontWeight:700,letterSpacing:'0.14em',textTransform:'uppercase',cursor:'pointer'}}>
            {tr('overview.openHabits')}
          </button>}/>
      ) : (
        <div style={{display:'flex',flexDirection:'column',gap:14,maxHeight:360,overflowY:'auto',paddingRight:4}}>
          {grouped.map(g => (
            <div key={g.cat.id}>
              <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:6,paddingLeft:2}}>
                <Icon name={g.cat.icon || 'target'} size={11} color={C.textFaint}/>
                <span style={{fontSize:9,fontWeight:700,letterSpacing:'0.18em',color:C.textFaint,textTransform:'uppercase'}}>{g.cat.name}</span>
              </div>
              <div style={{display:'flex',flexDirection:'column',gap:2}}>
                {g.items.map(h => {
                  const done = !!h.checkIns[today];
                  const streak = calcStreak(h.checkIns);
                  return (
                    <div key={h.id} onClick={() => store.toggleHabit(h.id, today)}
                      role="button" tabIndex={0}
                      onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); store.toggleHabit(h.id, today); } }}
                      aria-pressed={done}
                      style={{display:'flex',alignItems:'center',gap:12,padding:'9px 10px',borderRadius:10,
                        cursor:'pointer',transition:'background 0.15s',
                        background:done?'rgba(160,138,86,0.06)':'transparent'}}
                      onMouseEnter={e=>{ if(!done) e.currentTarget.style.background='rgba(255,255,255,0.025)'; }}
                      onMouseLeave={e=>{ if(!done) e.currentTarget.style.background='transparent'; }}>
                      <div style={{width:20,height:20,borderRadius:999,flexShrink:0,
                        border:`1.5px solid ${done?C.sand:C.borderMid}`,
                        background:done?C.sand:'transparent',
                        display:'flex',alignItems:'center',justifyContent:'center',transition:'all 0.18s'}}>
                        {done && <Icon name="check" size={11} color={C.bg} sw={2.8} />}
                      </div>
                      <Icon name={h.icon || g.cat.icon || 'target'} size={13} color={done?C.sandLight:C.textMuted}/>
                      <span style={{flex:1,minWidth:0,fontSize:13,fontWeight:done?500:400,
                        color:done?C.textSecondary:C.textPrimary,
                        textDecoration:done?'line-through':'none',
                        textDecorationColor:'rgba(160,138,86,0.4)',
                        whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>
                        {h.name}
                      </span>
                      {streak > 0 && (
                        <span style={{flexShrink:0,fontSize:10,fontWeight:600,color:C.sandLight,letterSpacing:'0.04em'}}>
                          {streak}<span style={{color:C.textFaint,fontWeight:300,marginLeft:2}}>d</span>
                        </span>
                      )}
                    </div>
                  );
                })}
              </div>
            </div>
          ))}
        </div>
      )}
    </Card>
  );
};

// ─── Workouts widget ───────────────────────────────────────────
const WorkoutsWidget = ({ store, onOpen }) => {
  const tr = useT();
  const wk = weekKeys();
  const tIdx = todayDow();
  const days = [tr('weekday.shortMon'),tr('weekday.shortTue'),tr('weekday.shortWed'),tr('weekday.shortThu'),tr('weekday.shortFri'),tr('weekday.shortSat'),tr('weekday.shortSun')];
  const doneCount = wk.filter(k=>store.state.workouts[k]?.done).length;
  const planCount = wk.filter(k=>store.state.workouts[k]?.type).length;
  return (
    <Card hoverable onClick={onOpen}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:20}}>
        <div>
          <Label>{tr('overview.training')}</Label>
          <Title mb={0}>{tr('overview.workouts')}</Title>
        </div>
        <div style={{textAlign:'right'}}>
          <div style={{fontSize:22,fontWeight:800,color:C.sand,fontStyle:'italic'}}>{doneCount}<span style={{fontSize:14,color:C.textFaint}}>/{planCount||7}</span></div>
          <div style={{fontSize:9,fontWeight:200,letterSpacing:'0.16em',color:C.textFaint,textTransform:'uppercase'}}>{tr('overview.sessions')}</div>
        </div>
      </div>
      <div style={{display:'flex',flexDirection:'column',gap:6}}>
        {wk.map((k,i)=>{
          const w = store.state.workouts[k];
          const isToday = i===tIdx;
          return (
            <div key={k} style={{display:'flex',alignItems:'center',gap:12,padding:'10px 12px',borderRadius:8,
              border:`1px solid ${w?.done?C.sageFaint:isToday?C.borderMid:C.border}`,
              background:w?.done?C.sageFaint:isToday?'rgba(250,250,248,0.02)':'transparent'}}>
              <div style={{width:36,fontSize:9,fontWeight:700,letterSpacing:'0.12em',color:isToday?C.sand:C.textFaint,textTransform:'uppercase'}}>{days[i]}</div>
              <div style={{flex:1,minWidth:0}}>
                {w?.type ? (
                  <div style={{fontSize:12,fontWeight:500,color:w.done?C.textPrimary:C.textSecondary,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>
                    {w.type}{w.duration?` · ${w.duration} min`:''}
                  </div>
                ) : (
                  <div style={{fontSize:11,fontWeight:300,color:C.textFaint,fontStyle:'italic'}}>{tr('overview.restDay')}</div>
                )}
              </div>
              {w?.intensity>0 && <div style={{display:'flex',gap:2}}>
                {[1,2,3,4,5].map(n=><div key={n} style={{width:3,height:10,borderRadius:2,background:n<=w.intensity?C.sage:'rgba(255,255,255,0.08)'}}/>)}
              </div>}
              {w?.done && <Icon name="check" size={13} color={C.sage} sw={2.5} />}
            </div>
          );
        })}
      </div>
    </Card>
  );
};

// ─── Reading widget ────────────────────────────────────────────
// Hero card for the current book. Reuses the Reading view's BookCover
// component (with its premium 3D hover) and deep-links into Reading
// Detail via a window-scoped pending-bookId handoff.
const ReadingWidget = ({ store, onOpen }) => {
  const tr = useT();
  const BookCover = window.BookCover; // Defined in dashboard-views-a.jsx; available at render time.
  const active = store.state.books.find(b => b.status === 'reading') || null;
  const pct = active && active.totalPages
    ? Math.min(100, Math.round(active.currentPage/active.totalPages*100))
    : 0;
  const latestNote = active && active.entries && active.entries[0];

  const openInReading = React.useCallback((id) => {
    if (id != null) window.__vybOpenBook = id;
    onOpen && onOpen();
  }, [onOpen]);

  return (
    <Card>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:active?20:8}}>
        <div>
          <Label>{tr('overview.currentRead')}</Label>
          <Title mb={0}>{tr('overview.reading')}</Title>
          <div style={{fontSize:11,fontWeight:300,color:C.textMuted,marginTop:8,letterSpacing:'0.02em'}}>{tr('overview.keepPageMoving')}</div>
        </div>
        <Icon name="arrow-up-right" size={16} color={C.textFaint} />
      </div>

      {!active ? (
        <Empty icon="book-open" title={tr('overview.noBook')}
          sub={tr('overview.startReading')}
          action={<button onClick={() => onOpen && onOpen()}
            style={{background:C.sand,color:C.bg,border:'none',borderRadius:8,padding:'8px 14px',
              fontFamily:'Montserrat,sans-serif',fontSize:10,fontWeight:700,letterSpacing:'0.14em',textTransform:'uppercase',cursor:'pointer'}}>
            {tr('overview.openReading')}
          </button>}/>
      ) : (
        <>
          <div style={{display:'flex',gap:20,alignItems:'flex-start',marginBottom:16}}>
            <div data-book-cover-anchor={active.id} style={{flexShrink:0,display:'inline-block'}}>
              {BookCover ? (
                <BookCover book={active} size="md" interactive3D
                  onOpen={() => openInReading(active.id)}/>
              ) : (
                <div style={{width:96,height:140,borderRadius:8,background:C.cardEl,
                  border:`1px solid ${C.borderMid}`,display:'flex',alignItems:'center',justifyContent:'center',cursor:'pointer'}}
                  onClick={() => openInReading(active.id)}>
                  <Icon name="book-open" size={26} color={C.sand}/>
                </div>
              )}
            </div>
            <div style={{flex:1,minWidth:0,display:'flex',flexDirection:'column',gap:6,paddingTop:4}}>
              <div style={{fontSize:16,fontWeight:700,color:C.textPrimary,lineHeight:1.25,letterSpacing:'-0.005em',
                wordBreak:'break-word'}}>{active.title || tr('overview.untitled')}</div>
              {active.author && <div style={{fontSize:12,fontWeight:300,color:C.textMuted}}>{active.author}</div>}
              <div style={{marginTop:8}}>
                <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline',marginBottom:6}}>
                  <span style={{fontSize:9,fontWeight:200,letterSpacing:'0.18em',color:C.textFaint,textTransform:'uppercase'}}>{tr('overview.progress')}</span>
                  <span style={{fontSize:14,fontWeight:800,color:C.sand,fontStyle:'italic'}}>{pct}%</span>
                </div>
                <div style={{height:3,borderRadius:999,background:'rgba(255,255,255,0.07)'}}>
                  <div style={{width:`${pct}%`,height:'100%',borderRadius:999,
                    background:`linear-gradient(90deg,${C.sand},${C.sandLight})`,transition:'width 0.4s'}}/>
                </div>
                <div style={{fontSize:10,fontWeight:300,color:C.textFaint,marginTop:6}}>
                  {tr('overview.pageOf').replace('{x}', String(active.currentPage)).replace('{y}', String(active.totalPages || '—'))}
                </div>
              </div>
              <button onClick={() => openInReading(active.id)}
                style={{marginTop:10,alignSelf:'flex-start',
                  background:C.sand,color:C.bg,border:'none',borderRadius:8,
                  padding:'8px 14px',cursor:'pointer',
                  fontFamily:'Montserrat,sans-serif',fontSize:10,fontWeight:700,letterSpacing:'0.14em',textTransform:'uppercase',
                  display:'inline-flex',alignItems:'center',gap:6,
                  boxShadow:'0 6px 18px rgba(160,138,86,0.25)'}}>
                <Icon name="book-open" size={12} color={C.bg} sw={2.5}/>
                {tr('overview.continueReading')}
              </button>
            </div>
          </div>
          {latestNote && (
            <div style={{padding:'12px 14px',borderRadius:8,background:'rgba(255,255,255,0.02)',border:`1px solid ${C.border}`}}>
              <div style={{fontSize:9,fontWeight:200,letterSpacing:'0.16em',color:C.textFaint,textTransform:'uppercase',marginBottom:6}}>{tr('overview.latestEntry')}</div>
              <div style={{fontSize:12,fontStyle:'italic',fontWeight:300,color:C.textSecondary,lineHeight:1.6}}>{latestNote.text}</div>
            </div>
          )}
        </>
      )}
    </Card>
  );
};

// ─── Ideas widget ──────────────────────────────────────────────
const IdeasWidget = ({ store, onOpen }) => {
  const tr = useT();
  const active = store.state.ideas.filter(i=>!i.archived).slice(0,2);
  return (
    <Card hoverable onClick={onOpen}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start'}}>
        <div><Label>{tr('overview.capture')}</Label><Title>{tr('overview.contentIdeas')}</Title></div>
        <Icon name="arrow-up-right" size={16} color={C.textFaint} />
      </div>
      {active.length === 0 ? (
        <Empty icon="lightbulb" title={tr('overview.noIdeasYet')} sub={tr('overview.captureSlip')} />
      ) : (
        <div style={{display:'flex',flexDirection:'column',gap:8}}>
          {active.map(idea=>(
            <div key={idea.id} style={{padding:'11px 13px',borderRadius:10,border:`1px solid ${C.border}`,background:'rgba(255,255,255,0.02)'}}>
              <div style={{fontSize:11,fontWeight:400,color:C.textPrimary,lineHeight:1.4}}>{idea.text}</div>
              <div style={{marginTop:6,display:'inline-block',padding:'2px 8px',borderRadius:999,background:C.sandFaint,
                fontSize:8,fontWeight:700,letterSpacing:'0.14em',color:C.sandLight,textTransform:'uppercase'}}>{window.dl ? window.dl('ideaCat', idea.tag) : idea.tag}</div>
            </div>
          ))}
        </div>
      )}
    </Card>
  );
};

// ─── Tasks widget ──────────────────────────────────────────────
const TasksWidget = ({ store, onOpen }) => {
  const tr = useT();
  const { tasks } = store.state;
  const PRIS = [{key:'urgent',color:C.clay},{key:'important',color:C.sand},{key:'later',color:C.textMuted}];
  const total = tasks.length;
  const done = tasks.filter(t=>t.done).length;
  return (
    <Card hoverable onClick={onOpen}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:20}}>
        <div><Label>{tr('overview.thisWeek')}</Label><Title mb={0}>{tr('overview.tasks')}</Title></div>
        <div style={{textAlign:'right'}}>
          <div style={{fontSize:22,fontWeight:800,color:C.sand,fontStyle:'italic'}}>{done}<span style={{fontSize:14,color:C.textFaint}}>/{total||0}</span></div>
          <div style={{fontSize:9,fontWeight:200,letterSpacing:'0.16em',color:C.textFaint,textTransform:'uppercase'}}>{tr('overview.done')}</div>
        </div>
      </div>
      {total === 0 ? (
        <Empty icon="list-todo" title={tr('overview.noTasksYet')} sub={tr('overview.openTasksHint')} />
      ) : (
        <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',gap:12}}>
          {PRIS.map(({key,color})=>{
            const items = tasks.filter(t=>t.pri===key).slice(0,4);
            return (
              <div key={key}>
                <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:8}}>
                  <div style={{width:6,height:6,borderRadius:999,background:color,flexShrink:0}}/>
                  <div style={{fontSize:9,fontWeight:600,letterSpacing:'0.18em',color:C.textFaint,textTransform:'uppercase'}}>{window.dl ? window.dl('priority', key) : key}</div>
                </div>
                <div style={{display:'flex',flexDirection:'column',gap:5}}>
                  {items.length===0 ? <div style={{fontSize:10,color:C.textFaint,fontStyle:'italic'}}>{tr('overview.noneDash')}</div> :
                  items.map(t=>(
                    <div key={t.id} onClick={e=>{e.stopPropagation();store.toggleTask(t.id);}}
                      style={{display:'flex',alignItems:'center',gap:8,padding:'8px 10px',borderRadius:6,cursor:'pointer',
                        border:`1px solid ${C.border}`,background:'rgba(255,255,255,0.02)',transition:'all 0.2s',opacity:t.done?0.4:1}}>
                      <div style={{width:12,height:12,borderRadius:3,border:`1px solid ${t.done?C.sand:C.borderMid}`,
                        background:t.done?C.sand:'transparent',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}}>
                        {t.done&&<Icon name="check" size={7} color={C.bg} sw={3} />}
                      </div>
                      <span style={{fontSize:10,fontWeight:400,color:C.textPrimary,lineHeight:1.3,textDecoration:t.done?'line-through':'none'}}>{t.text}</span>
                    </div>
                  ))}
                </div>
              </div>
            );
          })}
        </div>
      )}
    </Card>
  );
};

// ─── Quick Notes widget ────────────────────────────────────────
const QuickNotesWidget = ({ store }) => {
  const tr = useT();
  return (
    <Card>
      <Label>{tr('overview.reflectionsReminders')}</Label>
      <Title mb={16}>{tr('overview.quickNotes')}</Title>
      <textarea value={store.state.notes} onChange={e=>store.setNotes(e.target.value)}
        placeholder={tr('overview.notesPlaceholder')}
        style={{width:'100%',minHeight:90,background:'rgba(255,255,255,0.02)',border:`1px solid ${C.border}`,borderRadius:10,
          padding:'14px 16px',fontSize:13,fontWeight:300,fontFamily:'Montserrat,sans-serif',color:C.textSecondary,
          lineHeight:1.7,resize:'vertical',outline:'none'}}/>
    </Card>
  );
};

// ─── Today Habits summary widget ───────────────────────────────
// Replaces the old Today's Momentum card. Single signal: how many of
// today's scheduled habits are done. Gold glow only when the day is
// fully locked. Reuses calculateMomentum so logic stays centralized.
const TodayMomentumWidget = ({ store, onOpen }) => {
  const tr = useT();
  const calc = window.calculateMomentum || (() => null);
  const m = React.useMemo(() => calc({
    habits: store.state.habits,
  }), [store.state.habits]);

  if (!m) return null;

  const { today } = m;
  const total = today.totalHabits;
  const done  = today.completedHabits;
  const pct   = today.habitsCompletionPercent;
  const isComplete = total > 0 && pct >= 100;
  const isEmpty    = total === 0;

  const accent      = isComplete ? C.sage      : C.sand;
  const accentLight = isComplete ? C.sageLight : C.sandLight;

  // Microcopy tiers — single voice, no jargon.
  const microcopy = isEmpty
    ? tr('overview.noHabitsYet')
    : isComplete
      ? tr('overview.momentumLocked')
      : pct >= 60
        ? tr('overview.almostThere')
        : pct > 0
          ? tr('overview.keepGoing')
          : tr('overview.startTheDay');

  const habitsLine = total
    ? tr('momentum.habitsCompleteOf').replace('{done}', String(done)).replace('{total}', String(total))
    : tr('overview.noHabitsYet');

  return (
    <Card style={isComplete ? {
      borderColor: 'rgba(94,117,88,0.32)',
      boxShadow: '0 0 0 1px rgba(94,117,88,0.10), 0 18px 40px rgba(94,117,88,0.10)',
    } : undefined}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',gap:16,marginBottom:18}}>
        <div>
          <Label>{tr('overview.momentum')}</Label>
          <Title mb={0}>{tr('overview.todayHabits')}</Title>
        </div>
        <div style={{textAlign:'right'}}>
          <div style={{fontSize:22,fontWeight:800,color:isEmpty?C.textMuted:accentLight,fontStyle:'italic',lineHeight:1}}>
            {done}<span style={{fontSize:14,color:C.textFaint}}>/{total||0}</span>
          </div>
          <div style={{fontSize:9,fontWeight:600,letterSpacing:'0.16em',color:C.textFaint,textTransform:'uppercase',marginTop:4}}>
            {tr('overview.dailyHabits')}
          </div>
        </div>
      </div>

      <div aria-hidden="true" style={{
        position:'relative',width:'100%',height:6,borderRadius:999,
        background:'rgba(250,250,248,0.05)',overflow:'hidden',marginBottom:12}}>
        <div style={{position:'absolute',inset:0,width:`${pct}%`,
          background:`linear-gradient(90deg, ${accent}, ${accentLight})`,
          borderRadius:999,
          transition:'width 0.6s cubic-bezier(0.16,1,0.3,1)'}}/>
      </div>

      <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',gap:10,marginBottom:14,flexWrap:'wrap'}}>
        <div style={{fontSize:12,fontWeight:300,fontStyle:'italic',color:isComplete?C.sageLight:C.textMuted,letterSpacing:'0.02em'}}>
          {microcopy}
        </div>
        <div style={{fontSize:11,fontWeight:400,color:C.textSecondary,letterSpacing:'0.01em'}}>
          {habitsLine}
        </div>
      </div>

      {onOpen && (
        <div style={{display:'flex',alignItems:'center',justifyContent:'flex-end'}}>
          <button onClick={onOpen} title={tr('overview.openMomentum')}
            style={{background:'transparent',border:`1px solid ${C.border}`,borderRadius:8,
              padding:'6px 10px',color:C.textMuted,cursor:'pointer',
              fontFamily:'Montserrat,sans-serif',fontSize:9,fontWeight:600,letterSpacing:'0.14em',textTransform:'uppercase',
              display:'inline-flex',alignItems:'center',gap:6,transition:'all 0.15s'}}
            onMouseEnter={e=>{e.currentTarget.style.color=C.textPrimary;e.currentTarget.style.borderColor=C.borderMid;}}
            onMouseLeave={e=>{e.currentTarget.style.color=C.textMuted;e.currentTarget.style.borderColor=C.border;}}>
            {tr('overview.openMomentum')} <Icon name="arrow-up-right" size={11} color="currentColor"/>
          </button>
        </div>
      )}
    </Card>
  );
};

// ─── Current Streak widget ─────────────────────────────────────
// Compact stat card. Numbers and copy come straight from the momentum
// helper (streak.current / .label / .message) — no recalculation here.
const StreakWidget = ({ store }) => {
  const tr = useT();
  const calc = window.calculateMomentum || (() => null);
  const m = React.useMemo(() => calc({
    habits: store.state.habits,
  }), [store.state.habits]);

  if (!m) return null;

  const { current, label, message } = m.streak;
  const totalHabits = m.today.totalHabits;
  const todayPct = m.today.habitsCompletionPercent;

  // Tier thresholds drive the accent intensity (sage once user is locked-in).
  const isAdvanced = current >= 14;
  const accent = isAdvanced ? C.sage : C.sand;
  const accentLight = isAdvanced ? C.sageLight : C.sandLight;
  const numberColor = current === 0 ? C.textMuted : accentLight;

  // Subtle "today still in progress" hint — distinguish 3-and-counting from
  // 3-and-locked when today isn't complete yet.
  const todayLocked = totalHabits > 0 && todayPct >= 100;
  const hint = totalHabits === 0
    ? null
    : todayLocked
      ? tr('overview.todayIncluded')
      : (current > 0 ? tr('overview.todayInProgress') : null);

  return (
    <Card style={current >= 7 ? {
      borderColor: isAdvanced ? 'rgba(94,117,88,0.30)' : 'rgba(160,138,86,0.30)',
      boxShadow: isAdvanced
        ? '0 0 0 1px rgba(94,117,88,0.08), 0 18px 40px rgba(94,117,88,0.10)'
        : '0 0 0 1px rgba(160,138,86,0.08), 0 18px 40px rgba(160,138,86,0.08)',
    } : undefined}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',gap:12,marginBottom:18}}>
        <div>
          <Label>{tr('overview.streak')}</Label>
          <Title mb={0}>{tr('overview.currentStreak')}</Title>
        </div>
        <div aria-hidden="true" style={{
          width:34,height:34,borderRadius:999,
          border:`1px solid ${current>0 ? accent : C.border}`,
          background: current>0 ? `rgba(${isAdvanced?'94,117,88':'160,138,86'},0.10)` : 'transparent',
          display:'inline-flex',alignItems:'center',justifyContent:'center',flexShrink:0,
          boxShadow: current>=7 ? `0 0 0 4px rgba(${isAdvanced?'94,117,88':'160,138,86'},0.06)` : 'none',
          transition:'all 0.3s'}}>
          <Icon name="flame" size={15} color={current>0 ? accentLight : C.textFaint} sw={2}/>
        </div>
      </div>

      <div style={{display:'flex',alignItems:'baseline',gap:10,marginBottom:6}}>
        <div style={{
          fontFamily:'Montserrat,sans-serif',fontSize:54,fontWeight:200,
          fontStyle:'italic',letterSpacing:'-0.02em',lineHeight:1,
          color:numberColor,transition:'color 0.4s'}}>
          {current}
        </div>
        <div style={{fontSize:11,fontWeight:300,color:C.textMuted,letterSpacing:'0.04em'}}>
          {current === 1 ? tr('momentum.day') : tr('momentum.days')}
        </div>
      </div>

      <div style={{fontSize:10,fontWeight:700,letterSpacing:'0.20em',
        color: current>0 ? accentLight : C.textFaint,
        textTransform:'uppercase',marginBottom:14,transition:'color 0.4s'}}>
        {current === 0 ? tr('momentum.startToday') : label}
      </div>

      <div style={{fontSize:11,fontWeight:300,fontStyle:'italic',
        color:C.textMuted,letterSpacing:'0.03em',lineHeight:1.5}}>
        {message}
      </div>
      <div style={{marginTop:6,fontSize:10,fontWeight:300,fontStyle:'italic',
        color:C.textFaint,letterSpacing:'0.03em'}}>
        {tr('momentum.streakHintLine')}
      </div>

      {hint && (
        <div style={{marginTop:10,fontSize:9,fontWeight:600,letterSpacing:'0.16em',
          color:C.textFaint,textTransform:'uppercase'}}>
          {hint}
        </div>
      )}
    </Card>
  );
};

// ─── Weekly Momentum widget ────────────────────────────────────
// Compact week-at-a-glance card. Reads weekly.days from the momentum
// helper — Monday-first, local dates, isComplete only when habits exist
// and all are checked for that day.
const WeeklyMomentumWidget = ({ store }) => {
  const tr = useT();
  const calc = window.calculateMomentum || (() => null);
  const m = React.useMemo(() => calc({
    habits: store.state.habits,
  }), [store.state.habits]);

  if (!m) return null;

  const { days, completeHabitDays, totalDays } = m.weekly;
  const totalHabits = m.today.totalHabits;
  const hasHabits = totalHabits > 0;
  const isPerfect = hasHabits && completeHabitDays === totalDays;
  const accent = isPerfect ? C.sage : C.sand;
  const accentLight = isPerfect ? C.sageLight : C.sandLight;

  return (
    <Card style={completeHabitDays >= 5 ? {
      borderColor: isPerfect ? 'rgba(94,117,88,0.30)' : 'rgba(160,138,86,0.24)',
      boxShadow: isPerfect
        ? '0 0 0 1px rgba(94,117,88,0.08), 0 18px 40px rgba(94,117,88,0.10)'
        : '0 0 0 1px rgba(160,138,86,0.06), 0 18px 40px rgba(160,138,86,0.06)',
    } : undefined}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',gap:12,marginBottom:18}}>
        <div>
          <Label>{tr('overview.week')}</Label>
          <Title mb={0}>{tr('overview.weeklyMomentum')}</Title>
        </div>
        <div style={{textAlign:'right',flexShrink:0}}>
          <div style={{fontFamily:'Montserrat,sans-serif',fontSize:22,fontWeight:800,
            fontStyle:'italic',color: hasHabits ? accentLight : C.textMuted,letterSpacing:'-0.01em'}}>
            {completeHabitDays}<span style={{fontSize:14,color:C.textFaint,fontWeight:300}}>/{totalDays}</span>
          </div>
          <div style={{fontSize:9,fontWeight:200,letterSpacing:'0.16em',color:C.textFaint,textTransform:'uppercase',marginTop:2}}>
            {tr('overview.completeDays')}
          </div>
        </div>
      </div>

      <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',gap:6,marginBottom:16}}>
        {days.map((d, i) => {
          const filled = d.isComplete;
          const dotBg = filled
            ? `linear-gradient(135deg, ${accent}, ${accentLight})`
            : 'transparent';
          const dotBorder = filled
            ? accent
            : (d.isToday ? C.borderMid : C.border);
          return (
            <div key={d.date} style={{display:'flex',flexDirection:'column',alignItems:'center',gap:8,flex:1,minWidth:0}}>
              <div style={{fontSize:9,fontWeight:600,letterSpacing:'0.14em',
                color: d.isToday ? accentLight : C.textFaint,textTransform:'uppercase'}}>
                {d.label}
              </div>
              <div aria-label={`${d.date} ${filled ? 'complete' : 'incomplete'}`}
                style={{position:'relative',width:14,height:14,borderRadius:999,
                  border:`1.5px solid ${dotBorder}`,background:dotBg,
                  transition:'all 0.3s',
                  boxShadow: filled ? `0 0 0 3px rgba(${isPerfect?'94,117,88':'160,138,86'},0.10)` : 'none'}}>
                {d.isToday && !filled && (
                  <span style={{position:'absolute',top:'50%',left:'50%',transform:'translate(-50%,-50%)',
                    width:4,height:4,borderRadius:999,background:accent}}/>
                )}
              </div>
            </div>
          );
        })}
      </div>

      <div style={{fontSize:11,fontWeight:300,fontStyle:'italic',color:C.textMuted,letterSpacing:'0.03em',lineHeight:1.5}}>
        {!hasHabits
          ? tr('overview.noRhythm')
          : isPerfect
            ? tr('overview.perfectWeek')
            : tr('overview.smallWins')}
      </div>
    </Card>
  );
};

// ─── Daily Completion Seal ─────────────────────────────────────
// Premium "after state" stamp that appears once today's habit system is
// fully complete. Mounts only when isDailyHabitsComplete — disappears
// instantly when a habit is unchecked (React unmount on conditional render).
const DailyCompletionSeal = ({ store }) => {
  const tr = useT();
  const calc = window.calculateMomentum || (() => null);
  const m = React.useMemo(() => calc({
    habits: store.state.habits,
  }), [store.state.habits]);

  if (!m || !m.today.isDailyHabitsComplete) return null;

  const { completedHabits, totalHabits } = m.today;
  const copy = (window.getMomentumCopy && window.getMomentumCopy(m)) || {};

  return (
    <div className="vyb-seal" style={{
      animation: 'vyb-seal-in 0.5s cubic-bezier(0.16,1,0.3,1) both, vyb-seal-glow 6s ease-in-out 0.6s infinite',
      borderRadius: 18,
    }}>
      <Card style={{
        position:'relative',overflow:'hidden',
        background: 'linear-gradient(135deg, rgba(160,138,86,0.08), rgba(94,117,88,0.04) 60%, rgba(160,138,86,0.06))',
        borderColor: 'rgba(160,138,86,0.32)',
      }}>
        {/* Subtle radial wash behind the seal */}
        <div aria-hidden="true" style={{
          position:'absolute',top:-60,left:-40,width:260,height:260,
          background:'radial-gradient(circle, rgba(160,138,86,0.18), transparent 65%)',
          pointerEvents:'none',filter:'blur(2px)'}}/>
        <div style={{position:'relative',display:'flex',alignItems:'center',gap:18,flexWrap:'wrap'}}>
          {/* Wax-seal disc */}
          <div aria-hidden="true" style={{
            position:'relative',width:64,height:64,borderRadius:999,flexShrink:0,
            background:'radial-gradient(circle at 30% 28%, rgba(212,189,138,0.95), rgba(160,138,86,0.85) 55%, rgba(120,98,58,0.95))',
            border:`1px solid rgba(212,189,138,0.55)`,
            boxShadow:'inset 0 1px 0 rgba(255,235,200,0.35), inset 0 -6px 14px rgba(60,46,22,0.45), 0 10px 28px rgba(160,138,86,0.30)',
            display:'flex',alignItems:'center',justifyContent:'center'}}>
            <Icon name="check" size={26} color={C.bg} sw={2.6}/>
            {/* Outer ring */}
            <span style={{position:'absolute',inset:-5,borderRadius:999,
              border:'1px solid rgba(160,138,86,0.35)',pointerEvents:'none'}}/>
          </div>

          <div style={{flex:1,minWidth:0}}>
            <div style={{fontSize:9,fontWeight:700,letterSpacing:'0.24em',
              color:C.sandLight,textTransform:'uppercase',marginBottom:6}}>
              {tr('overview.momentumLocked')}
            </div>
            <div style={{fontSize:18,fontWeight:300,fontStyle:'italic',
              color:C.textPrimary,letterSpacing:'-0.005em',lineHeight:1.2}}>
              {tr('overview.dailySysComplete')}
            </div>
            <div style={{marginTop:8,display:'flex',alignItems:'center',gap:10,flexWrap:'wrap'}}>
              <span style={{fontSize:11,fontWeight:600,color:C.sandLight,letterSpacing:'0.06em'}}>
                {completedHabits}/{totalHabits} {tr('overview.habitsLower')}
              </span>
              <span style={{width:3,height:3,borderRadius:999,background:C.textFaint}}/>
              <span style={{fontSize:11,fontWeight:300,fontStyle:'italic',color:C.textMuted,letterSpacing:'0.03em'}}>
                {copy.sealMessage || tr('overview.comeBackTomorrow')}
              </span>
            </div>
          </div>
        </div>
      </Card>
    </div>
  );
};

// ─── Focus / Deep Work widget ──────────────────────────────────
// Local-only MVP: pick a duration, run a countdown anchored to a real
// timestamp (so background tabs stay accurate), play a soft Web Audio
// chime on completion. AudioContext is lazily created on the first
// Start press to satisfy autoplay policy.
const FOCUS_STORAGE_KEY = 'vyb-focus-timer-state';
const FocusWidget = () => {
  const tr = useT();
  const PRESETS = [15, 25, 45, 60];
  const [duration, setDuration] = React.useState(25); // minutes
  const [phase, setPhase] = React.useState('idle');   // idle|running|paused|complete
  const [endTime, setEndTime] = React.useState(null);
  const [remaining, setRemaining] = React.useState(25*60*1000);
  const pausedRemainingRef = React.useRef(null);
  const [muted, setMuted] = React.useState(false);
  const audioCtxRef = React.useRef(null);
  const completedDurationRef = React.useRef(25);
  const restoredRef = React.useRef(false);

  // Persist timer to localStorage so it survives refresh. Idle clears.
  const persist = (obj) => {
    try {
      if (!obj || obj.mode === 'idle') {
        localStorage.removeItem(FOCUS_STORAGE_KEY);
      } else {
        localStorage.setItem(FOCUS_STORAGE_KEY, JSON.stringify({
          ...obj, updatedAt: new Date().toISOString(),
        }));
      }
    } catch(e) {}
  };

  // Restore on mount. Running state recomputes remaining from elapsed.
  React.useEffect(() => {
    try {
      const raw = localStorage.getItem(FOCUS_STORAGE_KEY);
      if (!raw) { restoredRef.current = true; return; }
      const s = JSON.parse(raw);
      const dMin = s.durationMinutes || 25;
      const dMs  = s.durationMs || dMin*60*1000;
      setDuration(dMin);
      if (s.mode === 'running' && s.startedAt) {
        const elapsed = Date.now() - new Date(s.startedAt).getTime();
        const left = dMs - elapsed;
        if (left <= 0) {
          completedDurationRef.current = dMin;
          setRemaining(0);
          setPhase('complete');
          persist({ mode:'complete', durationMinutes:dMin, durationMs:dMs });
        } else {
          setEndTime(Date.now() + left);
          setRemaining(left);
          setPhase('running');
        }
      } else if (s.mode === 'paused') {
        const left = Math.max(0, s.remainingMsWhenPaused ?? dMs);
        pausedRemainingRef.current = left;
        setRemaining(left);
        setPhase('paused');
      } else if (s.mode === 'complete') {
        completedDurationRef.current = dMin;
        setRemaining(0);
        setPhase('complete');
      }
    } catch(e) {}
    restoredRef.current = true;
  }, []);

  // Reset shown remaining when duration changes outside an active run.
  React.useEffect(() => {
    if (phase === 'idle') setRemaining(duration*60*1000);
  }, [duration, phase]);

  // Tick using a real endTime — survives background tab throttling.
  React.useEffect(() => {
    if (phase !== 'running' || !endTime) return;
    const tick = () => {
      const left = endTime - Date.now();
      if (left <= 0) {
        setRemaining(0);
        completedDurationRef.current = duration;
        setPhase('complete');
        persist({ mode:'complete', durationMinutes:duration, durationMs:duration*60*1000 });
        playChime();
      } else {
        setRemaining(left);
      }
    };
    tick();
    const id = setInterval(tick, 250);
    return () => clearInterval(id);
  }, [phase, endTime, duration]);

  const ensureAudio = () => {
    if (audioCtxRef.current) {
      // Resume in case it was auto-suspended.
      if (audioCtxRef.current.state === 'suspended') {
        try { audioCtxRef.current.resume(); } catch(e) {}
      }
      return audioCtxRef.current;
    }
    try {
      const Ctx = window.AudioContext || window.webkitAudioContext;
      if (Ctx) audioCtxRef.current = new Ctx();
    } catch(e) {}
    return audioCtxRef.current;
  };

  // Soft two-note chime (perfect fifth, sine, gentle decay).
  const playChime = () => {
    if (muted) return;
    const ctx = audioCtxRef.current;
    if (!ctx) return;
    const now = ctx.currentTime;
    [528, 792].forEach((freq, i) => {
      const t0 = now + i*0.09;
      const osc = ctx.createOscillator();
      const gain = ctx.createGain();
      osc.type = 'sine';
      osc.frequency.value = freq;
      gain.gain.setValueAtTime(0.0001, t0);
      gain.gain.exponentialRampToValueAtTime(0.16, t0 + 0.05);
      gain.gain.exponentialRampToValueAtTime(0.0005, t0 + 1.8);
      osc.connect(gain); gain.connect(ctx.destination);
      osc.start(t0); osc.stop(t0 + 1.9);
    });
  };

  const start = () => {
    ensureAudio(); // user gesture — satisfies autoplay policy
    const ms = duration*60*1000;
    const startedAt = new Date().toISOString();
    setEndTime(Date.now() + ms);
    setRemaining(ms);
    setPhase('running');
    persist({ mode:'running', durationMinutes:duration, durationMs:ms, startedAt });
  };
  const pause = () => {
    const left = Math.max(0, endTime - Date.now());
    pausedRemainingRef.current = left;
    setRemaining(left);
    setPhase('paused');
    persist({ mode:'paused', durationMinutes:duration, durationMs:duration*60*1000,
      pausedAt:new Date().toISOString(), remainingMsWhenPaused:left });
  };
  const resume = () => {
    const left = pausedRemainingRef.current ?? remaining;
    // Anchor a fresh startedAt so refresh-while-running computes elapsed correctly.
    const startedAt = new Date(Date.now() - (duration*60*1000 - left)).toISOString();
    setEndTime(Date.now() + left);
    pausedRemainingRef.current = null;
    setPhase('running');
    persist({ mode:'running', durationMinutes:duration, durationMs:duration*60*1000, startedAt });
  };
  const reset = () => {
    setPhase('idle');
    setEndTime(null);
    pausedRemainingRef.current = null;
    setRemaining(duration*60*1000);
    persist({ mode:'idle' });
  };

  const totalMs = duration*60*1000;
  const pct = phase === 'complete' ? 100
    : Math.max(0, Math.min(100, ((totalMs - remaining)/totalMs)*100));

  const mm = Math.floor(remaining/60000);
  const ss = Math.floor((remaining%60000)/1000);
  const timeStr = `${String(mm).padStart(2,'0')}:${String(ss).padStart(2,'0')}`;

  const stateCopy = tr('focus.' + phase);

  const ringColor = phase === 'complete' ? C.sage
    : phase === 'paused' ? C.textMuted : C.sand;
  const timeColor = phase === 'complete' ? C.sage
    : phase === 'paused' ? C.textSecondary : C.sandLight;

  // Reused button styles.
  const primaryBtn = {
    background: C.sand, color: C.bg, border: 'none', borderRadius: 10,
    padding: '12px 22px', cursor: 'pointer',
    fontFamily: 'Montserrat,sans-serif', fontSize: 11, fontWeight: 700,
    letterSpacing: '0.16em', textTransform: 'uppercase',
    display: 'inline-flex', alignItems: 'center', gap: 8,
    boxShadow: '0 8px 22px rgba(160,138,86,0.28)', transition: 'all 0.15s',
  };
  const ghostBtn = {
    background: 'transparent', color: C.textSecondary,
    border: `1px solid ${C.border}`, borderRadius: 10,
    padding: '11px 18px', cursor: 'pointer',
    fontFamily: 'Montserrat,sans-serif', fontSize: 10, fontWeight: 600,
    letterSpacing: '0.16em', textTransform: 'uppercase',
    display: 'inline-flex', alignItems: 'center', gap: 8, transition: 'all 0.15s',
  };

  return (
    <Card>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:8,gap:16}}>
        <div>
          <Label>{tr('overview.focus')}</Label>
          <Title mb={0}>{tr('focus.deepWork')}</Title>
          <div style={{fontSize:11,fontWeight:300,color:C.textMuted,marginTop:8,letterSpacing:'0.02em'}}>
            {tr('focus.setTimer')}
          </div>
        </div>
        <button onClick={()=>setMuted(m=>!m)}
          title={muted ? tr('focus.unmute') : tr('focus.mute')}
          aria-label={muted ? tr('focus.unmute') : tr('focus.mute')}
          aria-pressed={muted}
          style={{width:34,height:34,borderRadius:999,
            border:`1px solid ${C.border}`,background:'transparent',cursor:'pointer',
            display:'inline-flex',alignItems:'center',justifyContent:'center',
            color:muted?C.textFaint:C.textMuted,transition:'all 0.15s',flexShrink:0}}>
          <Icon name={muted ? 'volume-x' : 'volume-2'} size={14}
            color={muted?C.textFaint:C.textMuted} sw={2}/>
        </button>
      </div>

      <div style={{display:'flex',flexDirection:'column',alignItems:'center',gap:18,padding:'22px 0 8px'}}>
        <div style={{position:'relative'}}>
          <Ring pct={pct} size={208} stroke={3} color={ringColor}
            bg='rgba(250,250,248,0.05)'>
            <div style={{display:'flex',flexDirection:'column',alignItems:'center',gap:6}}>
              <div style={{
                fontFamily:'Montserrat,sans-serif',
                fontVariantNumeric:'tabular-nums',
                fontSize:46,fontWeight:200,letterSpacing:'0.04em',
                color:timeColor,fontStyle:'italic',lineHeight:1,
                transition:'color 0.4s'}}>
                {timeStr}
              </div>
              <div style={{fontSize:9,fontWeight:700,letterSpacing:'0.22em',
                color:C.textFaint,textTransform:'uppercase'}}>
                {duration} {tr('focus.minBlock')}
              </div>
            </div>
          </Ring>
          {phase === 'running' && (
            <span aria-hidden="true" style={{position:'absolute',inset:-6,borderRadius:999,
              border:`1px solid ${C.sandFaint}`,opacity:0.5,pointerEvents:'none',
              animation:'vyb-ripple 2.4s ease-out infinite'}}/>
          )}
        </div>

        <div aria-live="polite" style={{
          fontSize:11,fontWeight:300,fontStyle:'italic',
          color: phase==='complete' ? C.sage : C.textMuted,
          letterSpacing:'0.04em',minHeight:16,transition:'color 0.4s'}}>
          {stateCopy}
        </div>

        {(phase === 'idle' || phase === 'complete') && (
          <div style={{display:'flex',gap:8,flexWrap:'wrap',justifyContent:'center'}}>
            {PRESETS.map(p => {
              const on = duration === p && phase === 'idle';
              return (
                <button key={p} onClick={()=>{ setDuration(p); if (phase==='complete') setPhase('idle'); }}
                  style={{padding:'8px 14px',borderRadius:999,cursor:'pointer',
                    border:`1px solid ${on?C.sand:C.border}`,
                    background: on ? 'rgba(160,138,86,0.12)' : 'transparent',
                    color: on ? C.sandLight : C.textMuted,
                    fontFamily:'Montserrat,sans-serif',fontSize:10,fontWeight:600,
                    letterSpacing:'0.16em',textTransform:'uppercase',transition:'all 0.15s'}}>
                  {p} {tr('focus.min')}
                </button>
              );
            })}
          </div>
        )}

        <div style={{display:'flex',gap:10,flexWrap:'wrap',justifyContent:'center',marginTop:4}}>
          {phase === 'idle' && (
            <button onClick={start} style={primaryBtn}
              onMouseEnter={e=>e.currentTarget.style.transform='translateY(-1px)'}
              onMouseLeave={e=>e.currentTarget.style.transform='translateY(0)'}>
              <Icon name="play" size={13} color={C.bg} sw={2.4}/> {tr('focus.start')}
            </button>
          )}
          {phase === 'running' && (
            <>
              <button onClick={pause} style={ghostBtn}>
                <Icon name="pause" size={13} color={C.textSecondary} sw={2}/> {tr('focus.pause')}
              </button>
              <button onClick={reset} style={ghostBtn}
                title={tr('focus.end')}>
                <Icon name="square" size={12} color={C.textSecondary} sw={2}/> {tr('focus.end')}
              </button>
            </>
          )}
          {phase === 'paused' && (
            <>
              <button onClick={resume} style={primaryBtn}>
                <Icon name="play" size={13} color={C.bg} sw={2.4}/> {tr('focus.resume')}
              </button>
              <button onClick={reset} style={ghostBtn}>
                <Icon name="rotate-ccw" size={12} color={C.textSecondary} sw={2}/> {tr('focus.reset')}
              </button>
            </>
          )}
          {phase === 'complete' && (
            <button onClick={reset} style={primaryBtn}>
              <Icon name="rotate-ccw" size={13} color={C.bg} sw={2.4}/> {tr('focus.newSession')}
            </button>
          )}
        </div>
      </div>
    </Card>
  );
};

// ─── Daily Catch-up ────────────────────────────────────────────
// Compact entry card in Overview + a focused multi-step modal flow.
// Reads existing data only. Lightweight answers persisted to localStorage.
//   vyb-daily-catchup-YYYY-MM-DD             (per-day note + answers)
//   vyb-daily-catchup-completed-YYYY-MM-DD   (completion flag)

const CATCHUP_STEPS = ['yesterday','missing','carryover','today','finish'];

const _catchupKey      = (k) => `vyb-daily-catchup-${k}`;
const _catchupDoneKey  = (k) => `vyb-daily-catchup-completed-${k}`;
const _catchupReadAnswers = (k) => {
  try { return JSON.parse(localStorage.getItem(_catchupKey(k)) || '{}') || {}; } catch { return {}; }
};
const _catchupWriteAnswers = (k, patch) => {
  try {
    const cur = _catchupReadAnswers(k);
    localStorage.setItem(_catchupKey(k), JSON.stringify({ ...cur, ...patch }));
  } catch {}
};
const _catchupIsDone = (k) => {
  try { return localStorage.getItem(_catchupDoneKey(k)) === '1'; } catch { return false; }
};
const _catchupMarkDone = (k) => {
  try { localStorage.setItem(_catchupDoneKey(k), '1'); } catch {}
};
const _catchupClearDone = (k) => {
  try { localStorage.removeItem(_catchupDoneKey(k)); } catch {}
};

// Compact entry card. Two visual states: idle and completed.
const DailyCatchupCard = ({ store, goTo }) => {
  const tr = useT();
  const [open, setOpen] = React.useState(false);
  const today = todayKey();
  // Tick to re-read localStorage when modal closes.
  const [tick, setTick] = React.useState(0);
  const isDone = React.useMemo(() => _catchupIsDone(today), [today, tick, open]);

  const onClose = () => { setOpen(false); setTick(t => t+1); };
  const onStart = () => { setOpen(true); };
  const onReview = () => { _catchupClearDone(today); setOpen(true); };

  const title    = isDone ? tr('overview.catchup.doneTitle')    : tr('overview.catchup.title');
  const subtitle = isDone ? tr('overview.catchup.doneSubtitle') : tr('overview.catchup.subtitle');
  const cta      = isDone ? tr('overview.catchup.ctaAgain')     : tr('overview.catchup.cta');

  return (
    <>
      <Card style={{padding:'14px 18px',
        background: isDone ? 'rgba(160,138,86,0.06)' : C.card,
        borderColor: isDone ? 'rgba(160,138,86,0.30)' : C.border}}>
        <div style={{display:'flex',alignItems:'center',gap:14,flexWrap:'wrap'}}>
          <div style={{width:36,height:36,borderRadius:10,
            background: isDone ? C.sandFaint : 'rgba(160,138,86,0.10)',
            border:`1px solid ${isDone ? C.sand : 'rgba(160,138,86,0.30)'}`,
            display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}}>
            <Icon name={isDone ? 'check' : 'sunrise'} size={16} color={C.sandLight} sw={1.8}/>
          </div>
          <div style={{flex:'1 1 200px',minWidth:0}}>
            <div style={{fontSize:9,fontWeight:700,letterSpacing:'0.18em',color:C.textFaint,textTransform:'uppercase',marginBottom:4}}>
              {tr('overview.catchup.title')}
            </div>
            <div style={{fontSize:14,fontWeight:600,color:C.textPrimary,lineHeight:1.3,marginBottom:2}}>
              {title}
            </div>
            <div style={{fontSize:11,fontWeight:300,color:C.textMuted,lineHeight:1.5}}>
              {subtitle}
            </div>
          </div>
          <Button size="sm" variant={isDone?'ghost':'primary'} icon={isDone?'rotate-ccw':'arrow-right'}
            onClick={isDone ? onReview : onStart}>
            {cta}
          </Button>
        </div>
      </Card>

      <DailyCatchupModal open={open} onClose={onClose} store={store} goTo={goTo}/>
    </>
  );
};

// Multi-step flow modal. State machine: yesterday → missing → carryover → today → finish.
const DailyCatchupModal = ({ open, onClose, store, goTo }) => {
  const tr = useT();
  const [step, setStep] = React.useState(0);
  const today = todayKey();
  const yKey  = dayKey(-1);

  // Reset to step 0 every time the modal opens.
  React.useEffect(() => { if (open) setStep(0); }, [open]);

  // ── Yesterday data (read-only summary) ──
  const habitsCompletedYesterday = React.useMemo(() => {
    return (store.state.habits || []).filter(h => h && h.checkIns && h.checkIns[yKey]).length;
  }, [store.state.habits, yKey]);

  const tasksCompletedYesterday = React.useMemo(() => {
    return (store.state.tasks || []).filter(t => {
      if (!t.done || !t.completedAt) return false;
      const d = new Date(t.completedAt);
      const k = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
      return k === yKey;
    }).length;
  }, [store.state.tasks, yKey]);

  const ideasCapturedYesterday = React.useMemo(() => {
    return (store.state.ideas || []).filter(i => i && i.createdAt === yKey).length;
  }, [store.state.ideas, yKey]);

  const readingEntriesYesterday = React.useMemo(() => {
    let c = 0;
    for (const b of (store.state.books || [])) {
      for (const e of (b.entries || [])) {
        if (e && e.date === yKey) c += 1;
      }
    }
    return c;
  }, [store.state.books, yKey]);

  const moodYesterday = (store.state.mood || {})[yKey] || null;
  const energyYesterdayValue = moodYesterday && moodYesterday.energy != null ? moodYesterday.energy : null;
  const moodYesterdayValue   = moodYesterday && moodYesterday.mood   != null ? moodYesterday.mood   : null;

  // ── Missing-info prompts (only show what's relevant) ──
  const isSched = window.isHabitScheduledOn || (() => true);
  const yDate = React.useMemo(() => {
    const d = new Date(); d.setDate(d.getDate()-1); d.setHours(0,0,0,0); return d;
  }, []);
  const plannedYesterdayCount = React.useMemo(() => {
    return (store.state.habits || []).filter(h => isSched(h, yDate)).length;
  }, [store.state.habits, yDate]);
  const readingHabitCheckedYesterday = React.useMemo(() => {
    return (store.state.habits || []).some(h => {
      if (!h || !h.checkIns || !h.checkIns[yKey]) return false;
      const name = String(h.name || '').toLowerCase();
      return /read|leer|lectura|libro|book/.test(name);
    });
  }, [store.state.habits, yKey]);

  const showMoodPrompt    = energyYesterdayValue == null && moodYesterdayValue == null;
  const showReadingPrompt = readingHabitCheckedYesterday && readingEntriesYesterday === 0;
  const showHabitsNote    = plannedYesterdayCount > 0 && habitsCompletedYesterday === 0;
  const anyMissing = showMoodPrompt || showReadingPrompt || showHabitsNote;

  // Persisted answers
  const [answers, setAnswers] = React.useState(() => _catchupReadAnswers(today));
  const [yNote, setYNote]     = React.useState(() => _catchupReadAnswers(today).yNote || '');
  const updateAnswer = (patch) => {
    setAnswers(a => {
      const next = { ...a, ...patch };
      _catchupWriteAnswers(today, patch);
      return next;
    });
  };

  // ── Carry-over tasks ──
  const carryTasks = React.useMemo(() => {
    return (store.state.tasks || [])
      .filter(t => !t.done && (t.pri === 'urgent' || t.pri === 'important'))
      .sort((a,b) => {
        if (a.pri !== b.pri) return a.pri === 'urgent' ? -1 : 1;
        return (a.sortOrder||0) - (b.sortOrder||0) || (a.id - b.id);
      })
      .slice(0, 5);
  }, [store.state.tasks]);

  // ── Today data ──
  const todayDate = React.useMemo(() => {
    const d = new Date(); d.setHours(0,0,0,0); return d;
  }, []);
  const plannedTodayCount = React.useMemo(() => {
    return (store.state.habits || []).filter(h => isSched(h, todayDate)).length;
  }, [store.state.habits, todayDate]);
  const habitsDoneToday = React.useMemo(() => {
    return (store.state.habits || []).filter(h => h && h.checkIns && h.checkIns[today]).length;
  }, [store.state.habits, today]);
  const openTasksToday = React.useMemo(() => {
    return (store.state.tasks || []).filter(t => !t.done);
  }, [store.state.tasks]);
  const todayLoadLabel = React.useMemo(() => {
    const n = openTasksToday.length;
    if (n === 0) return tr('tasks.load.empty');
    if (n <= 2)  return tr('tasks.load.light');
    if (n <= 5)  return tr('tasks.load.focused');
    if (n <= 8)  return tr('tasks.load.active');
    if (n <= 12) return tr('tasks.load.heavy');
    return tr('tasks.load.overloaded');
  }, [openTasksToday.length, tr]);

  // Suggested focus
  const suggested = React.useMemo(() => {
    const urgent    = openTasksToday.find(t => t.pri === 'urgent');
    const important = openTasksToday.find(t => t.pri === 'important');
    if (urgent)    return { kind:'urgent',    text: tr('overview.catchup.startUrgent'), name: urgent.text };
    if (important) return { kind:'important', text: tr('overview.catchup.startTop'),    name: important.text };
    const habit = (store.state.habits || []).find(h =>
      isSched(h, todayDate) && !(h.checkIns && h.checkIns[today]));
    if (habit) return { kind:'habit', text: tr('overview.catchup.startWith'), name: habit.name };
    const book = (store.state.books || []).find(b => b && b.status === 'reading');
    const readToday = book && (book.entries || []).some(e => e && e.date === today);
    if (book && !readToday) return { kind:'read', text: tr('overview.catchup.readOnePage'), name: book.title };
    return { kind:'small', text: tr('overview.catchup.chooseOneMove'), name: '' };
  }, [openTasksToday, store.state.habits, store.state.books, todayDate, today, tr]);

  // ── Step navigation ──
  const goNext = () => setStep(s => Math.min(s+1, CATCHUP_STEPS.length-1));
  const goBack = () => setStep(s => Math.max(s-1, 0));
  const finish = () => {
    if (yNote.trim()) _catchupWriteAnswers(today, { yNote: yNote.trim() });
    _catchupMarkDone(today);
    onClose();
  };

  // Auto-skip "Missing info" step when nothing to ask.
  const effectiveNext = () => {
    if (step === 0 && !anyMissing) { setStep(2); return; }
    goNext();
  };
  const effectiveBack = () => {
    if (step === 2 && !anyMissing) { setStep(0); return; }
    goBack();
  };

  if (!open) return null;

  const stepTitle = [
    tr('overview.catchup.step1Title'),
    tr('overview.catchup.step2Title'),
    tr('overview.catchup.step3Title'),
    tr('overview.catchup.step4Title'),
    tr('overview.catchup.step5Title'),
  ][step];

  // ── Render helpers ──
  const StatTile = ({ label, value, sub }) => (
    <div style={{padding:'12px 14px',borderRadius:10,background:'rgba(255,255,255,0.02)',
      border:`1px solid ${C.border}`,display:'flex',flexDirection:'column',gap:4,minWidth:0}}>
      <div style={{fontSize:9,fontWeight:700,letterSpacing:'0.18em',color:C.textFaint,textTransform:'uppercase'}}>
        {label}
      </div>
      <div style={{fontSize:20,fontWeight:800,fontStyle:'italic',color:C.textPrimary,letterSpacing:'-0.01em',lineHeight:1.1}}>
        {value}
      </div>
      {sub && <div style={{fontSize:10,color:C.textMuted,letterSpacing:'0.02em'}}>{sub}</div>}
    </div>
  );

  return (
    <Modal open={open} onClose={onClose} title={tr('overview.catchup.title')} width={760}>
      {/* Step indicator */}
      <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:18}}>
        {CATCHUP_STEPS.map((_,i) => {
          const active = i === step;
          const passed = i <  step;
          return (
            <div key={i} style={{flex:1,height:3,borderRadius:2,
              background: active ? C.sand : passed ? 'rgba(160,138,86,0.45)' : 'rgba(250,250,248,0.06)',
              transition:'background 0.25s'}}/>
          );
        })}
      </div>
      <div style={{fontSize:9,fontWeight:700,letterSpacing:'0.18em',color:C.textFaint,textTransform:'uppercase',marginBottom:6}}>
        {tr('overview.catchup.stepOf').replace('{n}', String(step+1)).replace('{total}', String(CATCHUP_STEPS.length))}
      </div>
      <div style={{fontSize:18,fontWeight:700,color:C.textPrimary,marginBottom:18,letterSpacing:'-0.01em'}}>
        {stepTitle}
      </div>

      {/* Step body */}
      {step === 0 && (
        <div className="vyb-catchup-grid" style={{display:'grid',
          gridTemplateColumns:'repeat(auto-fit, minmax(140px, 1fr))',gap:10}}>
          <StatTile label={tr('overview.catchup.habitsCompleted')} value={habitsCompletedYesterday}/>
          <StatTile label={tr('overview.catchup.tasksCompleted')}  value={tasksCompletedYesterday}/>
          <StatTile label={tr('overview.catchup.ideasCaptured')}   value={ideasCapturedYesterday}/>
          <StatTile label={tr('overview.catchup.readingSession')}  value={readingEntriesYesterday}/>
          <StatTile label={tr('overview.catchup.moodEnergy')}
            value={moodYesterdayValue ? `${moodYesterdayValue}/5` : '—'}
            sub={energyYesterdayValue ? `Energy ${energyYesterdayValue}/5` : null}/>
        </div>
      )}

      {step === 1 && (
        <div style={{display:'flex',flexDirection:'column',gap:14}}>
          {!anyMissing && (
            <div style={{fontSize:12,color:C.textMuted,fontStyle:'italic',padding:'10px 0'}}>
              {tr('overview.catchup.noDataYet')}
            </div>
          )}
          {showMoodPrompt && (
            <div style={{padding:14,borderRadius:10,background:'rgba(255,255,255,0.02)',border:`1px solid ${C.border}`}}>
              <div style={{fontSize:12,fontWeight:600,color:C.textPrimary,marginBottom:10}}>
                {tr('overview.catchup.promptMoodMissing')}
              </div>
              <div style={{display:'flex',gap:8,flexWrap:'wrap'}}>
                {[1,2,3,4,5].map(v => {
                  const sel = answers.energyY === v;
                  return (
                    <button key={v} onClick={()=>{
                        updateAnswer({ energyY: v });
                        try { store.setMoodDay(yKey, { energy: v }); } catch {}
                      }}
                      style={{width:38,height:38,borderRadius:10,cursor:'pointer',
                        border:`1px solid ${sel?C.sand:C.border}`,
                        background:sel?C.sandFaint:'transparent',
                        color:sel?C.sandLight:C.textMuted,
                        fontFamily:'Montserrat,sans-serif',fontWeight:700,fontSize:13,
                        transition:'all 0.18s'}}>
                      {v}
                    </button>
                  );
                })}
              </div>
            </div>
          )}
          {showReadingPrompt && (
            <div style={{padding:14,borderRadius:10,background:'rgba(255,255,255,0.02)',border:`1px solid ${C.border}`}}>
              <div style={{fontSize:12,fontWeight:600,color:C.textPrimary,marginBottom:10}}>
                {tr('overview.catchup.promptReadAsk')}
              </div>
              <div style={{display:'flex',gap:8}}>
                {[
                  { k:'yes', label: tr('overview.catchup.yes') },
                  { k:'no',  label: tr('overview.catchup.no') },
                ].map(o => {
                  const sel = answers.readY === o.k;
                  return (
                    <button key={o.k} onClick={()=>updateAnswer({ readY: o.k })}
                      style={{padding:'8px 14px',borderRadius:999,cursor:'pointer',
                        border:`1px solid ${sel?C.sand:C.border}`,
                        background:sel?C.sandFaint:'transparent',
                        color:sel?C.sandLight:C.textMuted,
                        fontFamily:'Montserrat,sans-serif',fontWeight:600,fontSize:11,
                        letterSpacing:'0.08em',textTransform:'uppercase',transition:'all 0.18s'}}>
                      {o.label}
                    </button>
                  );
                })}
              </div>
            </div>
          )}
          {showHabitsNote && (
            <div style={{padding:14,borderRadius:10,background:'rgba(255,255,255,0.02)',border:`1px solid ${C.border}`}}>
              <div style={{fontSize:12,fontWeight:600,color:C.textPrimary,marginBottom:10}}>
                {tr('overview.catchup.promptHabitsNote')}
              </div>
              <textarea value={yNote} onChange={e=>setYNote(e.target.value)}
                placeholder={tr('overview.catchup.notePlaceholder')}
                rows={2}
                style={{width:'100%',padding:'10px 12px',borderRadius:8,fontFamily:'Montserrat,sans-serif',
                  fontSize:12,color:C.textPrimary,resize:'vertical',outline:'none',lineHeight:1.5,
                  background:'rgba(250,250,248,0.03)',border:`1px solid ${C.border}`,boxSizing:'border-box'}}/>
            </div>
          )}
        </div>
      )}

      {step === 2 && (
        <div>
          {carryTasks.length === 0 ? (
            <div style={{fontSize:12,color:C.textMuted,fontStyle:'italic',padding:'10px 0'}}>
              {tr('overview.catchup.noUnfinished')}
            </div>
          ) : (
            <div style={{display:'flex',flexDirection:'column',gap:8}}>
              {carryTasks.map(t => {
                const priColor = t.pri === 'urgent' ? (C.clay || C.sand) : C.sand;
                return (
                  <div key={t.id} style={{padding:'10px 12px',borderRadius:10,
                    background:'rgba(255,255,255,0.02)',border:`1px solid ${C.border}`,
                    display:'flex',alignItems:'center',gap:10,flexWrap:'wrap'}}>
                    <span style={{width:6,height:6,borderRadius:999,background:priColor,flexShrink:0}}/>
                    <span style={{flex:'1 1 200px',minWidth:0,fontSize:12,color:C.textPrimary,
                      overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>
                      {t.text}
                    </span>
                    <div style={{display:'flex',gap:4,flexShrink:0,flexWrap:'wrap'}}>
                      <button onClick={()=>{ try { store.toggleTask(t.id); } catch {} }}
                        title={tr('overview.catchup.markDone')}
                        style={catchupRowBtn(C.sandLight)}>
                        <Icon name="check" size={11} color={C.sandLight} sw={2}/>
                      </button>
                      <button onClick={()=>{ try { store.updateTask(t.id, { pri:'later' }); } catch {} }}
                        title={tr('overview.catchup.moveToLater')}
                        style={catchupRowBtn(C.textMuted)}>
                        <Icon name="clock" size={11} color={C.textMuted} sw={2}/>
                      </button>
                    </div>
                  </div>
                );
              })}
            </div>
          )}
        </div>
      )}

      {step === 3 && (
        <div className="vyb-catchup-grid" style={{display:'grid',
          gridTemplateColumns:'repeat(auto-fit, minmax(180px, 1fr))',gap:10}}>
          <StatTile label={tr('overview.catchup.plannedHabits')}
            value={`${habitsDoneToday}/${plannedTodayCount}`}/>
          <StatTile label={tr('overview.catchup.todayLoadLabel')}
            value={openTasksToday.length}
            sub={todayLoadLabel}/>
          <div style={{padding:'12px 14px',borderRadius:10,background:C.sandFaint,
            border:`1px solid rgba(160,138,86,0.35)`,display:'flex',flexDirection:'column',gap:6,
            gridColumn:'1 / -1'}}>
            <div style={{fontSize:9,fontWeight:700,letterSpacing:'0.18em',color:C.sandLight,textTransform:'uppercase'}}>
              {tr('overview.catchup.todaysFocus')}
            </div>
            <div style={{fontSize:13,color:C.textPrimary,lineHeight:1.5}}>
              {suggested.text}{suggested.name ? <span style={{color:C.sandLight,fontWeight:600}}> {suggested.name}</span> : null}
            </div>
          </div>
        </div>
      )}

      {step === 4 && (
        <div style={{display:'flex',flexDirection:'column',gap:14}}>
          <div style={{padding:'14px 16px',borderRadius:12,background:C.sandFaint,
            border:`1px solid rgba(160,138,86,0.35)`}}>
            <div style={{fontSize:9,fontWeight:700,letterSpacing:'0.18em',color:C.sandLight,textTransform:'uppercase',marginBottom:6}}>
              {tr('overview.catchup.todaysFocus')}
            </div>
            <div style={{fontSize:14,color:C.textPrimary,lineHeight:1.5}}>
              {suggested.text}{suggested.name ? <span style={{color:C.sandLight,fontWeight:600}}> {suggested.name}</span> : null}
            </div>
          </div>
          <div style={{display:'flex',gap:8,flexWrap:'wrap',justifyContent:'flex-end'}}>
            <Button size="sm" variant="ghost" icon="layout-dashboard"
              onClick={()=>{ finish(); try { goTo && goTo('overview'); } catch {} }}>
              {tr('overview.catchup.goToOverview')}
            </Button>
            <Button size="sm" variant="ghost" icon="list-todo"
              onClick={()=>{ finish(); try { goTo && goTo('tasks'); } catch {} }}>
              {tr('overview.catchup.openTasks')}
            </Button>
            <Button size="sm" icon="check"
              onClick={()=>{ finish(); try { goTo && goTo('habits'); } catch {} }}>
              {tr('overview.catchup.goToHabits')}
            </Button>
          </div>
        </div>
      )}

      {/* Footer nav */}
      {step < 4 && (
        <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',gap:8,
          marginTop:22,paddingTop:14,borderTop:`1px solid ${C.border}`,flexWrap:'wrap'}}>
          {step > 0 ? (
            <Button size="sm" variant="ghost" icon="chevron-left" onClick={effectiveBack}>
              {tr('overview.catchup.back')}
            </Button>
          ) : <span/>}
          <div style={{display:'flex',gap:8}}>
            <Button size="sm" variant="ghost" onClick={effectiveNext}>
              {tr('overview.catchup.skip')}
            </Button>
            <Button size="sm" icon="arrow-right" onClick={effectiveNext}>
              {tr('overview.catchup.next')}
            </Button>
          </div>
        </div>
      )}
    </Modal>
  );
};

const catchupRowBtn = (color) => ({
  width:30,height:30,borderRadius:8,
  background:'rgba(250,250,248,0.04)',border:`1px solid ${C.border}`,
  cursor:'pointer',display:'inline-flex',alignItems:'center',justifyContent:'center',
  padding:0,transition:'all 0.18s',color,
});

// ─── Dashboard Customization (Phase 1: visibility only) ────────
// Future-ready shape stored at vyb-overview-widget-preferences:
//   { version: 1, widgets: { [id]: { visible, order, variant } } }
// Phase 2/3 will introduce reorder + variants. This module only writes
// `visible` from the UI; order/variant are kept in shape for forward compat.
const OVERVIEW_PREFS_KEY     = 'vyb-overview-widget-preferences';
const OVERVIEW_PREFS_VERSION = 1;
const OVERVIEW_WIDGET_CONFIG = [
  { id:'dailyCatchup',   defaultVisible:true, defaultOrder:10,  defaultVariant:'default', implemented:true },
  { id:'todayHabits',    defaultVisible:true, defaultOrder:20,  defaultVariant:'default', implemented:true },
  { id:'topPriorities',  defaultVisible:true, defaultOrder:30,  defaultVariant:'default', implemented:true },
  { id:'deepWork',       defaultVisible:true, defaultOrder:40,  defaultVariant:'default', implemented:true },
  { id:'quickNotes',     defaultVisible:true, defaultOrder:50,  defaultVariant:'default', implemented:true },
  { id:'contentIdeas',   defaultVisible:true, defaultOrder:60,  defaultVariant:'default', implemented:true },
  { id:'todayMomentum',  defaultVisible:true, defaultOrder:70,  defaultVariant:'default', implemented:true },
  { id:'currentReading', defaultVisible:true, defaultOrder:80,  defaultVariant:'default', implemented:true },
  { id:'moodEnergy',     defaultVisible:true, defaultOrder:90,  defaultVariant:'default', implemented:true },
];

const _defaultOverviewPrefs = () => {
  const widgets = {};
  for (const w of OVERVIEW_WIDGET_CONFIG) {
    widgets[w.id] = { visible: w.defaultVisible, order: w.defaultOrder, variant: w.defaultVariant };
  }
  return { version: OVERVIEW_PREFS_VERSION, widgets };
};

const _readOverviewPrefs = () => {
  const def = _defaultOverviewPrefs();
  try {
    const raw = localStorage.getItem(OVERVIEW_PREFS_KEY);
    if (!raw) return def;
    const parsed = JSON.parse(raw);
    if (!parsed || typeof parsed !== 'object') return def;
    // Migrate from older / partial shape: ensure every known widget has an entry.
    const widgets = (parsed.widgets && typeof parsed.widgets === 'object') ? parsed.widgets : {};
    const merged = {};
    for (const w of OVERVIEW_WIDGET_CONFIG) {
      const cur = widgets[w.id] || {};
      merged[w.id] = {
        visible: typeof cur.visible === 'boolean' ? cur.visible : w.defaultVisible,
        order:   typeof cur.order   === 'number'  ? cur.order   : w.defaultOrder,
        variant: typeof cur.variant === 'string'  ? cur.variant : w.defaultVariant,
      };
    }
    return { version: OVERVIEW_PREFS_VERSION, widgets: merged };
  } catch { return def; }
};

const _writeOverviewPrefs = (prefs) => {
  try {
    localStorage.setItem(OVERVIEW_PREFS_KEY, JSON.stringify(prefs));
    window.dispatchEvent(new CustomEvent('vyb-overview-prefs-changed'));
  } catch {}
};

// Hook: live-read current prefs. Returns helpers.
const useOverviewPrefs = () => {
  const [prefs, setPrefs] = React.useState(_readOverviewPrefs);
  React.useEffect(() => {
    const onChange = () => setPrefs(_readOverviewPrefs());
    window.addEventListener('vyb-overview-prefs-changed', onChange);
    window.addEventListener('storage', onChange); // cross-tab
    return () => {
      window.removeEventListener('vyb-overview-prefs-changed', onChange);
      window.removeEventListener('storage', onChange);
    };
  }, []);
  const isVisible = React.useCallback((id) => {
    const w = prefs.widgets && prefs.widgets[id];
    return w ? w.visible !== false : true;
  }, [prefs]);
  return { prefs, isVisible };
};

// Subtle entry button. Renders next to (or above) the Overview top.
const DashboardCustomizeButton = ({ onClick }) => {
  const tr = useT();
  return (
    <div className="vyb-overview-customize-bar"
      style={{display:'flex',justifyContent:'flex-end',alignItems:'center'}}>
      <button onClick={onClick}
        style={{background:'transparent',border:`1px solid ${C.border}`,borderRadius:999,
          padding:'5px 12px',cursor:'pointer',display:'inline-flex',alignItems:'center',gap:6,
          fontFamily:'Montserrat,sans-serif',fontSize:10,fontWeight:600,letterSpacing:'0.12em',
          textTransform:'uppercase',color:C.textMuted,transition:'all 0.18s ease-out'}}
        onMouseEnter={e=>{ e.currentTarget.style.color = C.textPrimary; e.currentTarget.style.borderColor = C.borderMid; }}
        onMouseLeave={e=>{ e.currentTarget.style.color = C.textMuted;   e.currentTarget.style.borderColor = C.border; }}>
        <Icon name="sliders-horizontal" size={11} color="currentColor" sw={1.8}/>
        {tr('overview.customize.button')}
      </button>
    </div>
  );
};

// Customization modal — visibility only in Phase 1.
const DashboardCustomizeModal = ({ open, onClose }) => {
  const tr = useT();
  const [draft, setDraft] = React.useState(_readOverviewPrefs);

  // Reset draft whenever modal opens so cancel discards prior tweaks.
  React.useEffect(() => { if (open) setDraft(_readOverviewPrefs()); }, [open]);

  const setVisible = (id, visible) => {
    setDraft(d => ({
      ...d,
      widgets: {
        ...d.widgets,
        [id]: { ...(d.widgets[id] || {}), visible },
      },
    }));
  };
  const handleSave = () => { _writeOverviewPrefs(draft); onClose(); };
  const handleReset = () => setDraft(_defaultOverviewPrefs());
  // Cancel discards draft changes.
  const handleCancel = () => onClose();

  if (!open) return null;

  const visibleWidgets = OVERVIEW_WIDGET_CONFIG.filter(w =>
    w.implemented && (draft.widgets[w.id]?.visible !== false));
  const hiddenWidgets  = OVERVIEW_WIDGET_CONFIG.filter(w =>
    w.implemented && (draft.widgets[w.id]?.visible === false));

  const Row = ({ w, isShown }) => {
    const title = tr(`overview.customize.widgets.${w.id}.title`);
    const desc  = tr(`overview.customize.widgets.${w.id}.description`);
    const [hover, setHover] = React.useState(false);
    const tooltip = isShown
      ? tr('overview.customize.hideWidget')
      : tr('overview.customize.showWidget');
    const stateLabel = isShown
      ? tr('overview.customize.visible')
      : tr('overview.customize.hidden');
    // Visible: muted by default, sand on hover. Hidden: faint, sand on hover.
    const baseColor  = isShown ? C.textMuted : C.textFaint;
    const hoverColor = C.sandLight;
    return (
      <div style={{display:'flex',alignItems:'center',gap:12,padding:'12px 14px',
        borderRadius:10,background:'rgba(255,255,255,0.02)',border:`1px solid ${C.border}`,
        transition:'background 0.2s, border-color 0.2s'}}>
        <div style={{flex:'1 1 auto',minWidth:0}}>
          <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:3}}>
            <div style={{fontSize:13,fontWeight:600,color:C.textPrimary,lineHeight:1.3}}>
              {title}
            </div>
            <span style={{fontSize:8.5,fontWeight:700,letterSpacing:'0.16em',
              textTransform:'uppercase',
              color: isShown ? C.sandLight : C.textFaint,
              opacity: 0.85}}>
              {stateLabel}
            </span>
          </div>
          <div style={{fontSize:11,fontWeight:300,color:C.textMuted,lineHeight:1.5}}>
            {desc}
          </div>
        </div>
        <button onClick={()=>setVisible(w.id, !isShown)}
          onMouseEnter={()=>setHover(true)} onMouseLeave={()=>setHover(false)}
          title={tooltip} aria-label={tooltip} aria-pressed={isShown}
          style={{flexShrink:0,width:36,height:36,borderRadius:10,
            background: hover ? 'rgba(160,138,86,0.10)' : 'transparent',
            border:'none',cursor:'pointer',display:'inline-grid',placeItems:'center',
            padding:0,transition:'background 0.2s ease-out, transform 0.2s ease-out',
            transform: hover ? 'scale(1.06)' : 'scale(1)',
            WebkitTapHighlightColor:'transparent'}}>
          <span key={isShown ? 'on' : 'off'}
            style={{display:'inline-flex',animation:'vybEyeIn 0.2s ease-out both'}}>
            <Icon name={isShown ? 'eye' : 'eye-off'} size={16}
              color={hover ? hoverColor : baseColor} sw={1.8}/>
          </span>
        </button>
      </div>
    );
  };

  const SectionHeader = ({ children, count }) => (
    <div style={{display:'flex',alignItems:'baseline',gap:8,marginBottom:10,marginTop:4}}>
      <div style={{fontSize:9,fontWeight:700,letterSpacing:'0.18em',color:C.textFaint,textTransform:'uppercase'}}>
        {children}
      </div>
      <div style={{fontSize:10,color:C.textFaint,fontVariantNumeric:'tabular-nums'}}>{count}</div>
    </div>
  );

  return (
    <Modal open={open} onClose={onClose} title={tr('overview.customize.title')} width={620}>
      <div style={{fontSize:12,fontWeight:300,color:C.textMuted,lineHeight:1.5,marginBottom:18}}>
        {tr('overview.customize.subtitle')}
      </div>

      <SectionHeader count={visibleWidgets.length}>
        {tr('overview.customize.sectionActive')}
      </SectionHeader>
      <div style={{display:'flex',flexDirection:'column',gap:8,marginBottom:20}}>
        {visibleWidgets.length === 0 && (
          <div style={{fontSize:12,color:C.textFaint,fontStyle:'italic',padding:'4px 0'}}>—</div>
        )}
        {visibleWidgets.map(w => <Row key={w.id} w={w} isShown={true}/>)}
      </div>

      <SectionHeader count={hiddenWidgets.length}>
        {tr('overview.customize.sectionAvail')}
      </SectionHeader>
      <div style={{display:'flex',flexDirection:'column',gap:8}}>
        {hiddenWidgets.length === 0 && (
          <div style={{fontSize:12,color:C.textFaint,fontStyle:'italic',padding:'4px 0'}}>—</div>
        )}
        {hiddenWidgets.map(w => <Row key={w.id} w={w} isShown={false}/>)}
      </div>

      <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',gap:8,
        marginTop:24,paddingTop:16,borderTop:`1px solid ${C.border}`,flexWrap:'wrap'}}>
        <Button size="sm" variant="ghost" icon="rotate-ccw" onClick={handleReset}>
          {tr('overview.customize.reset')}
        </Button>
        <div style={{display:'flex',gap:8}}>
          <Button size="sm" variant="ghost" onClick={handleCancel}>
            {tr('overview.customize.cancel')}
          </Button>
          <Button size="sm" icon="check" onClick={handleSave}>
            {tr('overview.customize.save')}
          </Button>
        </div>
      </div>
    </Modal>
  );
};

// Convenience: button + modal bundled. OverviewPage drops one of these in.
const DashboardCustomize = () => {
  const [open, setOpen] = React.useState(false);
  return (
    <>
      <DashboardCustomizeButton onClick={()=>setOpen(true)}/>
      <DashboardCustomizeModal open={open} onClose={()=>setOpen(false)}/>
    </>
  );
};

Object.assign(window, { Sidebar, MobileHeader, MobileNavDrawer, WeeklyBar, PrioritiesWidget, MoodWidget, HabitsTodayMini, PendingTasksMini, HabitsWidget, WorkoutsWidget, ReadingWidget, IdeasWidget, TasksWidget, QuickNotesWidget, FocusWidget, TodayMomentumWidget, StreakWidget, WeeklyMomentumWidget, DailyCompletionSeal, DailyCatchupCard, DashboardCustomize, DashboardCustomizeButton, DashboardCustomizeModal, useOverviewPrefs, OVERVIEW_WIDGET_CONFIG });
