// Today — the morning briefing home screen // Synthesizes: weather + sleep + calendar + bills + approvals + recovery function TodayScreen({ go, openCapture, pullProgress = 0 }) { const d = LifeData; const w = d.weather; const h = d.health.today; const baseline = d.health.baseline; const sleepDelta = (h.sleep_hours - baseline.sleep_hours).toFixed(1); const sleepBelow = h.sleep_hours < baseline.sleep_hours; const today = d.today instanceof Date ? d.today : new Date(d.today); const dateStr = today.toLocaleDateString('en-US', { weekday: 'long', day: 'numeric', month: 'long' }); // Next event const nowMin = today.getHours() * 60 + today.getMinutes(); const toMin = (s) => parseInt(s.slice(0,2)) * 60 + parseInt(s.slice(3,5)); const next = d.calendar.find(e => toMin(e.time) >= nowMin) || d.calendar[0]; const minsToNext = next ? toMin(next.time) - nowMin : 0; const haveSleep = h.sleep_hours > 0; const haveHRV = h.hrv_ms > 0; const haveHealth = haveSleep || haveHRV; return (
{/* Greeting */}
{dateStr}
שלום, {d.user === 'shay' ? 'Shay' : 'Gili'}
{w.location} · {w.temp_c}° · {w.summary}
{/* Hero — Morning Read */}
The Morning Read
{haveHealth ? ( <> {haveSleep && <>You slept {Math.floor(h.sleep_hours)}h{Math.round((h.sleep_hours % 1) * 60).toString().padStart(2,'0')}m — {Math.abs(sleepDelta)}h {sleepBelow ? 'below' : 'above'} your baseline. } {haveHRV && <>HRV is at {Math.round(h.hrv_ms)}ms{baseline.hrv_ms ? ` (${h.hrv_ms < baseline.hrv_ms ? '–' : '+'}${Math.round(Math.abs(h.hrv_ms - baseline.hrv_ms))} from avg)` : ''}.} ) : ( <>Good morning. No health data yet — sync Apple Health to see your recovery. )} {next && ( <>

Next up: {next.time}{next.summary}. )}
{/* Next event banner */} {next && (
{minsToNext > 0 ? `IN ${minsToNext} MIN · ` : ''}{next.time}
{next.summary}
{next.location && (
{next.location}
)}
)} {/* Schedule strip */} {d.calendar.length > 0 && (
{d.calendar.map((e, i) => { const done = toMin(e.time) < nowMin; const isNext = next && e.id === next.id; const color = e.type === 'work' ? T.blue : e.type === 'family' ? T.orange : e.type === 'health' ? T.green : T.pink; return (
{e.time}
{e.summary}
{e.location && (
{e.location}
)}
); })}
)} {/* Body glance — only show if we have data */} {haveHealth && (
go('body')} style={{ fontSize: 14, color: T.blue, fontWeight: 500, cursor: 'pointer' }}>Open → }>
} /> {haveHRV && } />} {h.readiness > 0 && = 80 ? 'Good' : h.readiness >= 60 ? 'Moderate' : 'Low'} icon={} />}
)} {/* Bills */} {d.bills.length > 0 && (
₪{d.bills.reduce((s, b) => s + (b.amount_nis || 0), 0).toFixed(0)} total }> {d.bills.slice(0, 3).map((b, i) => (
{(b.provider || '?')[0]}
{b.provider} {b.provider_he ? · {b.provider_he} : null}
Due {b.due_date ? new Date(b.due_date).toLocaleDateString('en-US', { day: 'numeric', month: 'short' }) : '—'}
₪{(b.amount_nis || 0).toFixed(0)}
))}
)} {/* Drift teaser — only if we have people data */} {d.people.length > 0 && (
go('radar')} style={{ fontSize: 14, color: T.blue, fontWeight: 500, cursor: 'pointer' }}>Radar → }> go('radar')} style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
{(() => { const ghosts = d.people.filter(p => p.ghost); const drifting = d.people.filter(p => p.days_since > 7); return ( <>
{drifting.length} drifting{ghosts.length > 0 ? `, ${ghosts.length} waiting` : ''}
{drifting.slice(0,3).map(p => p.name).join(', ')}
); })()}
)} {/* Pull hint */}
↓ pull down to capture
); } function MiniStat({ label, value, unit, detail, icon, bad }) { return (
{icon} {label}
{value} {unit && {unit}}
{detail}
); } window.TodayScreen = TodayScreen;