const { useState, useEffect, useCallback } = React;

// ── Fonts ──────────────────────────────────────────────────────────────────
const FONT_LINK = document.createElement("link");
FONT_LINK.rel = "stylesheet";
FONT_LINK.href = "https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Syne:wght@400;600;700;800&display=swap";
document.head.appendChild(FONT_LINK);

// ── Logo ───────────────────────────────────────────────────────────────────
function WardWatchLogo({ size=40 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 160 160" aria-label="WardWatch logo"
      style={{filter:"drop-shadow(0 0 8px rgba(0,230,167,0.2))", flexShrink:0}}>
      <rect width="160" height="160" rx="24" fill="#0D0B26"/>
      <circle cx="80" cy="80" r="68" fill="none" stroke="#00E6A7" strokeWidth="1.5" opacity="0.15"/>
      <circle cx="80" cy="80" r="58" fill="none" stroke="#7E2EFA" strokeWidth="2" strokeDasharray="3 5" opacity="0.4"/>
      <path d="M 80,32 A 48,48 0 1,1 40,49" fill="none" stroke="#00E6A7" strokeWidth="4" strokeLinecap="round" opacity="0.8"/>
      <path d="M 44,70 L 62,106 L 80,72 L 98,106 L 116,70" fill="none" stroke="#FFFFFF" strokeWidth="7" strokeLinecap="round" strokeLinejoin="round"/>
      <path d="M 84,46 L 74,65 L 81,65 L 76,86 L 87,65 L 80,65 Z" fill="#FFD100" stroke="#0D0B26" strokeWidth="1" strokeLinejoin="round"/>
    </svg>
  );
}

// ── Constants ──────────────────────────────────────────────────────────────
const STORAGE_KEY = "wardwatch_v1";
const SHIFT_TYPES = ["Day", "Evening", "Night", "On-call"];
const DAYS_FOR_ALERT = 14;

const CBI_QUESTIONS = [
  { id: "c1", scale: "personal", text: "How often do you feel tired?" },
  { id: "c2", scale: "personal", text: "How often are you physically exhausted?" },
  { id: "c3", scale: "personal", text: "How often are you emotionally exhausted?" },
  { id: "c4", scale: "personal", text: "How often do you think: I can't take it anymore?" },
  { id: "c5", scale: "personal", text: "How often do you feel worn out?" },
  { id: "c6", scale: "work",     text: "Is your work emotionally exhausting?" },
  { id: "c7", scale: "work",     text: "Do you feel burnt out because of your work?" },
  { id: "c8", scale: "work",     text: "Does your work frustrate you?" },
  { id: "c9", scale: "work",     text: "Do you feel used up at the end of the working day?" },
  { id: "c10", scale: "work",    text: "Is it hard for you to relax after a work shift?" },
];
const CBI_OPTIONS = [
  { label: "Never",     value: 0   },
  { label: "Rarely",    value: 25  },
  { label: "Sometimes", value: 50  },
  { label: "Often",     value: 75  },
  { label: "Always",    value: 100 },
];

// ── Helpers ────────────────────────────────────────────────────────────────
function loadData() {
  try {
    const d = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
    const data = { shifts:[], checkins:[], cbi:[], roster:{}, profile:null, biometrics:[], baselines:{}, ...d };
    // One-time migration: convert legacy 1–10 check-in ratings to the 1–5 scale.
    if (!data.scaleV2) {
      data.checkins = (data.checkins||[]).map(c => {
        const conv = v => (typeof v === "number" && v > 5) ? Math.max(1, Math.round(v/2)) : v;
        return { ...c, fatigue:conv(c.fatigue), stress:conv(c.stress), emotional:conv(c.emotional) };
      });
      data.scaleV2 = true;
      try { localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } catch {}
    }
    return data;
  }
  catch { return { shifts:[], checkins:[], cbi:[], roster:{}, profile:null, biometrics:[], baselines:{}, scaleV2:true }; }
}
function saveData(d) { localStorage.setItem(STORAGE_KEY, JSON.stringify(d)); }

// ── Export helpers ─────────────────────────────────────────────────────────
function downloadFile(filename, text, mime) {
  const blob = new Blob([text], { type: mime });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url; a.download = filename;
  document.body.appendChild(a); a.click();
  document.body.removeChild(a);
  setTimeout(()=>URL.revokeObjectURL(url), 1000);
}
function exportJSON(data) {
  const stamp = new Date().toISOString().slice(0,10);
  downloadFile(`wardwatch-backup-${stamp}.json`, JSON.stringify(data, null, 2), "application/json");
}
function csvEscape(v) {
  if (v === null || v === undefined) return "";
  const s = String(v);
  return /[",\n]/.test(s) ? `"${s.replace(/"/g,'""')}"` : s;
}
function exportCSV(data) {
  const stamp = new Date().toISOString().slice(0,10);
  const sections = [];
  // Shifts
  let rows = ["type,start,end,hours,overtime,notes"];
  data.shifts.forEach(s => rows.push([s.type,s.start,s.end,s.hours,s.overtime?"yes":"no",s.notes].map(csvEscape).join(",")));
  sections.push("SHIFTS\n" + rows.join("\n"));
  // Check-ins
  rows = ["date,fatigue,stress,emotional,sleep,exercise"];
  data.checkins.forEach(c => rows.push([c.date,c.fatigue,c.stress,c.emotional,c.sleep,c.exercise?"yes":"no"].map(csvEscape).join(",")));
  sections.push("CHECK-INS\n" + rows.join("\n"));
  // CBI
  rows = ["date,total,personal,work"];
  data.cbi.forEach(c => rows.push([c.date,c.score,c.personal,c.work].map(csvEscape).join(",")));
  sections.push("BURNOUT SCREENS\n" + rows.join("\n"));
  downloadFile(`wardwatch-data-${stamp}.csv`, sections.join("\n\n"), "text/csv");
}
function formatDate(iso) { return new Date(iso).toLocaleDateString("en-NZ",{day:"numeric",month:"short"}); }
function formatTime(iso) { return new Date(iso).toLocaleTimeString("en-NZ",{hour:"2-digit",minute:"2-digit"}); }
function shiftHours(s,e) { return Math.round(((new Date(e)-new Date(s))/3600000)*10)/10; }
function burnoutLevel(score) {
  if (score < 35) return { label:"Low",      color:"var(--green)" };
  if (score < 55) return { label:"Moderate", color:"var(--amber)" };
  if (score < 75) return { label:"High",     color:"var(--red)"   };
  return             { label:"Severe",   color:"var(--red)"   };
}
function rollingFatigue(checkins, days=7) {
  const cutoff = Date.now() - days*86400000;
  const r = checkins.filter(c => new Date(c.date) >= cutoff);
  if (!r.length) return null;
  return Math.round(r.reduce((s,c)=>s+c.fatigue,0)/r.length*10)/10;
}

// Is today the recommended CBI day? = a day off that is immediately followed
// by a working day (i.e. last rest day before the next roster block begins).
// roster entries: { [YYYY-MM-DD]: {type, ...} }, where type "Off" or absent = not working.
function isRecoveryDay(roster, dateStr=null) {
  if (!roster) return false;
  const today = dateStr || new Date().toISOString().slice(0,10);
  const tEntry = roster[today];
  const isWorkingToday = tEntry && tEntry.type && tEntry.type !== "Off";
  if (isWorkingToday) return false; // must be a rest day
  // find tomorrow
  const tmrw = new Date(today); tmrw.setDate(tmrw.getDate()+1);
  const tStr = tmrw.toISOString().slice(0,10);
  const nEntry = roster[tStr];
  const isWorkingTomorrow = nEntry && nEntry.type && nEntry.type !== "Off";
  return Boolean(isWorkingTomorrow); // off today, working tomorrow → recovery day
}

// Non-recovery signal: latest CBI is elevated WHILE recent daily fatigue is low.
// Means rest isn't clearing the burnout — a stronger chronic-burnout flag.
function nonRecoveryFlag(data) {
  if (!data.cbi.length) return null;
  const latest = data.cbi[data.cbi.length-1];
  const avg3 = rollingFatigue(data.checkins, 3);
  // elevated chronic score but well-rested recently
  if (latest.score >= 50 && avg3 !== null && avg3 <= 2) {
    return { score: latest.score, fatigue: avg3 };
  }
  return null;
}

// ── Biometrics helpers ─────────────────────────────────────────────────────
// Recompute personal rolling baselines (mean + sd) over a window of recent days.
function computeBaselines(biometrics, windowDays=14) {
  const cutoff = Date.now() - windowDays*86400000;
  const recent = biometrics.filter(b => new Date(b.date) >= cutoff);
  function stat(key) {
    const vals = recent.map(b=>b[key]).filter(v=>typeof v==="number" && !isNaN(v));
    if (vals.length < 3) return null; // need a few points to mean anything
    const mean = vals.reduce((a,b)=>a+b,0)/vals.length;
    const variance = vals.reduce((a,b)=>a+(b-mean)**2,0)/vals.length;
    return { mean: Math.round(mean*10)/10, sd: Math.round(Math.sqrt(variance)*10)/10, n: vals.length, updated: new Date().toISOString().slice(0,10) };
  }
  return {
    hrv:       stat("hrv"),
    restingHr: stat("restingHr"),
    sleep:     stat("sleepHours"),
  };
}

// Latest biometric reading (most recent date)
function latestBiometric(biometrics) {
  if (!biometrics.length) return null;
  return [...biometrics].sort((a,b)=>new Date(b.date)-new Date(a.date))[0];
}

// Silent decline: HRV suppressed vs baseline while subjective fatigue is low.
// Returns a descriptor or null.
function silentDeclineFlag(data) {
  const bl = data.baselines?.hrv;
  if (!bl || bl.sd === 0) return null;
  // average HRV of the last 3 readings
  const recent = [...data.biometrics].sort((a,b)=>new Date(b.date)-new Date(a.date)).slice(0,3)
    .map(b=>b.hrv).filter(v=>typeof v==="number");
  if (recent.length < 2) return null;
  const avgHrv = recent.reduce((a,b)=>a+b,0)/recent.length;
  const z = (avgHrv - bl.mean) / bl.sd; // negative = below baseline
  const avg3fatigue = rollingFatigue(data.checkins, 3);
  // body strained (HRV >1.5 SD low) but person reports feeling okay
  if (z <= -1.5 && avg3fatigue !== null && avg3fatigue <= 2) {
    return { avgHrv: Math.round(avgHrv), baseline: bl.mean, drop: Math.round((1-avgHrv/bl.mean)*100) };
  }
  return null;
}

// ── Unified daily record + recovery matrix ─────────────────────────────────
// Assembles one normalised record for a given date, pairing shift context,
// CBI scores, sleep architecture and autonomic strain into a single shape.
function buildDailyLog(data, dateStr=null) {
  const day = dateStr || new Date().toISOString().slice(0,10);

  // shift context from roster (preferred) or a logged shift on that date
  const rosterEntry = data.roster?.[day];
  let shiftType = "Rest", hoursWorked = 0;
  if (rosterEntry && rosterEntry.type && rosterEntry.type !== "Off") {
    shiftType = rosterEntry.type;
    hoursWorked = calcHours(rosterEntry.customStart||"07:00", rosterEntry.customEnd||"19:00");
  } else {
    const s = data.shifts.find(s => (s.start||"").slice(0,10) === day);
    if (s) { shiftType = s.type; hoursWorked = s.hours || 0; }
  }

  const lastCbi = data.cbi[data.cbi.length-1] || null;
  const bio = [...data.biometrics].filter(b=>b.date===day).sort((a,b)=>new Date(b.syncedAt)-new Date(a.syncedAt))[0]
            || latestBiometric(data.biometrics);
  const blHrv = data.baselines?.hrv?.mean ?? null;

  return {
    date: day,
    shift_context: { type: shiftType, hours_worked: hoursWorked },
    cbi_metrics: lastCbi ? { personal_score: lastCbi.personal, work_score: lastCbi.work } : null,
    sleep_architecture: bio ? {
      total_minutes: bio.sleepHours != null ? Math.round(bio.sleepHours*60) : null,
      deep_minutes:  bio.deepSleep  != null ? Math.round(bio.deepSleep*60)  : null,
      rem_minutes:   bio.remSleep   != null ? Math.round(bio.remSleep*60)   : null,
      awake_minutes: bio.awakeMin   ?? null,
    } : null,
    autonomic_strain: bio ? {
      hrv_rmssd_ms: bio.hrv ?? null,
      resting_heart_rate: bio.restingHr ?? null,
      rolling_14d_hrv_avg: blHrv,
    } : null,
  };
}

// Recovery matrix — evaluates true physiological readiness from a daily log.
// Severity-ordered so no reading is misclassified through a gap.
function evaluatePhysiologicalRecovery(log) {
  if (!log || !log.sleep_architecture || !log.autonomic_strain) return null;
  const { deep_minutes, total_minutes } = log.sleep_architecture;
  const { hrv_rmssd_ms, rolling_14d_hrv_avg } = log.autonomic_strain;
  if (total_minutes == null || hrv_rmssd_ms == null || rolling_14d_hrv_avg == null || total_minutes === 0) return null;

  const deepPct = deep_minutes != null ? (deep_minutes/total_minutes)*100 : null;
  const hrvDrop = ((rolling_14d_hrv_avg - hrv_rmssd_ms)/rolling_14d_hrv_avg)*100;
  const suppressed = hrvDrop >= 15;
  const poorDeep = deepPct != null && deepPct < 15;

  // 1. Critical: poor deep sleep AND suppressed autonomic recovery
  if (poorDeep && suppressed) {
    return { status:"CRITICAL_STRAIN", color:"var(--red)",
      label:"Critical strain",
      message:"High risk of shift fatigue. Both sleep quality and autonomic recovery are compromised." };
  }
  // 2. Systemic exhaustion: enough sleep volume but HRV not recovering
  if (total_minutes > 420 && suppressed) {
    return { status:"SYSTEMIC_EXHAUSTION", color:"#a78bfa",
      label:"Systemic exhaustion",
      message:"You're sleeping enough, but your body isn't recovering — parasympathetic suppression detected." };
  }
  // 2b. Suppressed HRV with unknown/borderline deep sleep — don't let it fall through
  if (suppressed) {
    return { status:"AUTONOMIC_STRAIN", color:"#a78bfa",
      label:"Autonomic strain",
      message:"HRV is well below your baseline. Recovery is lagging even if sleep looks adequate." };
  }
  // 2c. Poor deep sleep without HRV suppression
  if (poorDeep) {
    return { status:"POOR_SLEEP_QUALITY", color:"var(--amber)",
      label:"Poor sleep quality",
      message:"Deep sleep is low. Your nervous system is coping, but restorative sleep is short." };
  }
  // 3. Acute sleep debt: resilient HRV but mechanically short sleep
  if (total_minutes < 360 && hrvDrop < 10) {
    return { status:"ACUTE_SLEEP_DEBT", color:"var(--amber)",
      label:"Acute sleep debt",
      message:"Nervous system is resilient, but sleep debt is accumulating mechanically." };
  }
  // Balanced
  return { status:"OPTIMAL", color:"var(--green)",
    label:"Balanced",
    message:"Physiological systems balanced." };
}

// Dynamic mitigation framework — maps each recovery status to actionable guidance.
// "Biological budgeting": modulate physical output to internal state.
const mitigationRegistry = {
  CRITICAL_STRAIN: {
    title: "Prioritise autonomic recovery",
    exercise: "Swap intense training for 20 min of active mobility or a low-heart-rate walk.",
    mindset: "Run a 10-minute slow-breathing protocol to kickstart parasympathetic recovery.",
  },
  SYSTEMIC_EXHAUSTION: {
    title: "Systemic deload",
    exercise: "Keep it to steady Zone 2 aerobic work. Don't test peak strength today.",
    mindset: "Set a hard boundary on work thoughts — do something completely unrelated and creative.",
  },
  AUTONOMIC_STRAIN: {
    title: "Ease the load",
    exercise: "Light movement only — a walk or easy spin. Skip anything that spikes your heart rate.",
    mindset: "Protect wind-down time tonight: dim lights, no screens for the last hour before sleep.",
  },
  POOR_SLEEP_QUALITY: {
    title: "Rebuild restorative sleep",
    exercise: "Gentle movement is fine; avoid late-evening high intensity that delays deep sleep.",
    mindset: "Anchor a consistent wind-down routine. Avoid caffeine within ~8 hours of sleep.",
  },
  ACUTE_SLEEP_DEBT: {
    title: "Manage mechanical fatigue",
    exercise: "Nervous system is stable but keep volume low — high-weight, low-rep is acceptable.",
    mindset: "Take a 20-minute nap before your next demanding block to drop sleep pressure.",
  },
  OPTIMAL: {
    title: "Clear for high output",
    exercise: "Green light for intervals, heavy compound work, or endurance volume.",
    mindset: "Cognitive reserves are primed — good day for complex problem-solving.",
  },
};

function computeAlert(data) {
  const alerts = [];
  const cutoff = Date.now() - DAYS_FOR_ALERT*86400000;
  const rs = data.shifts.filter(s => new Date(s.start) >= cutoff);
  const sorted = [...rs].sort((a,b)=>new Date(a.start)-new Date(b.start));
  let nr=0, max=0;
  sorted.forEach(s=>{ if(s.type==="Night"){nr++;max=Math.max(max,nr);}else nr=0; });
  if (max>=3) alerts.push(`${max} consecutive night shifts detected.`);
  const avg = rollingFatigue(data.checkins);
  if (avg!==null && avg>=4) alerts.push(`7-day avg fatigue is ${avg}/5 — above safe threshold.`);
  if (data.cbi.length) {
    const sc = data.cbi[data.cbi.length-1].score;
    if (sc>=55) alerts.push(`Latest burnout screen: ${sc}/100 (${burnoutLevel(sc).label}).`);
  }
  const nr2 = nonRecoveryFlag(data);
  if (nr2) alerts.push(`Burnout score ${nr2.score}/100 is staying high despite low recent fatigue (${nr2.fatigue}/5) — rest may not be clearing it.`);
  const sd = silentDeclineFlag(data);
  if (sd) alerts.push(`Your HRV is down ~${sd.drop}% from your baseline (${sd.avgHrv} vs ${sd.baseline}ms) even though you feel okay — your body may be under strain.`);
  const hrs = rs.reduce((s,sh)=>s+(sh.hours||0),0);
  if (hrs>80) alerts.push(`${hrs}h worked in last 14 days — consider rest.`);
  return alerts;
}

// ── Styles ─────────────────────────────────────────────────────────────────
const css = `
  :root {
    --bg:           #0f1129;
    --bg2:          #181b38;
    --surface:      #1e2244;
    --surface2:     #252952;
    --border:       #2e3460;
    --text:         #e8eaf6;
    --muted:        #7b83b8;
    --green:        #2dd4a0;
    --amber:        #f5a623;
    --red:          #f06080;
    --blue:         #60a5fa;
    --accent:       #2dd4a0;
    --accent2:      #7c3aed;
    --accent-light: rgba(45,212,160,0.12);
    --font-display: 'Syne', sans-serif;
    --font-mono:    'DM Mono', monospace;
  }
  * { box-sizing:border-box; margin:0; padding:0; }
  body { background:var(--bg); color:var(--text); font-family:var(--font-display); }
  #ww-root { min-height:100vh; max-width:480px; margin:0 auto; display:flex; flex-direction:column; background:var(--bg); color:var(--text); }

  /* NAV */
  .ww-nav { display:flex; justify-content:space-around; align-items:center;
    background:var(--surface); border-top:1px solid var(--border);
    padding:10px 0 16px; position:fixed; bottom:0; left:50%; transform:translateX(-50%);
    width:100%; max-width:480px; z-index:100; box-shadow:0 -4px 20px rgba(0,0,0,0.4); }
  .ww-nav-btn { display:flex; flex-direction:column; align-items:center; gap:3px;
    background:none; border:none; color:var(--muted); cursor:pointer;
    font-family:var(--font-mono); font-size:10px; letter-spacing:0.04em; transition:color 0.15s; padding:4px 8px; }
  .ww-nav-btn.active { color:var(--accent); }
  .ww-nav-btn svg { width:22px; height:22px; }

  /* PAGE */
  .ww-page { flex:1; padding:28px 20px 110px; animation:fadeUp 0.22s ease; }
  @keyframes fadeUp { from{opacity:0;transform:translateY(6px)} to{opacity:1;transform:translateY(0)} }

  /* HEADER */
  .ww-logo { font-family:var(--font-display); font-weight:800; font-size:24px; letter-spacing:-0.03em; color:var(--text); }
  .ww-logo span { color:var(--accent); }
  .ww-subtitle { font-family:var(--font-mono); font-size:11px; color:var(--muted); letter-spacing:0.07em; text-transform:uppercase; margin-top:2px; }

  /* CARD */
  .ww-card { background:var(--surface); border:1px solid var(--border); border-radius:16px; padding:18px; margin-bottom:12px; }
  .ww-card-title { font-family:var(--font-mono); font-size:10px; color:var(--muted); letter-spacing:0.12em; text-transform:uppercase; margin-bottom:12px; }

  /* STATS */
  .ww-stat-row { display:grid; grid-template-columns:1fr 1fr 1fr; gap:10px; margin-bottom:14px; }
  .ww-stat { background:var(--surface); border:1px solid var(--border); border-radius:14px; padding:16px 8px; text-align:center; }
  .ww-stat-val { font-family:var(--font-mono); font-size:26px; font-weight:500; line-height:1; color:var(--text); }
  .ww-stat-lbl { font-family:var(--font-mono); font-size:9px; color:var(--muted); text-transform:uppercase; letter-spacing:0.07em; margin-top:5px; }

  /* ALERTS */
  .ww-alert { background:rgba(240,96,128,0.1); border:1px solid rgba(240,96,128,0.35); border-radius:14px;
    padding:13px 15px; margin-bottom:10px; display:flex; gap:10px; align-items:flex-start; }
  .ww-alert-dot { width:8px; height:8px; border-radius:50%; background:var(--red);
    margin-top:4px; flex-shrink:0; animation:pulse 2s infinite; }
  @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} }
  .ww-alert-text { font-size:13px; line-height:1.55; color:#fca5a5; }
  .ww-ok { background:rgba(45,212,160,0.08); border:1px solid rgba(45,212,160,0.25); border-radius:14px;
    padding:13px 15px; margin-bottom:10px; display:flex; gap:10px; align-items:center; }
  .ww-ok-dot { width:8px; height:8px; border-radius:50%; background:var(--green); flex-shrink:0; }
  .ww-ok-text { font-size:13px; color:var(--green); }

  /* ACTION CARD */
  .ww-action-card { background:var(--surface); border:1.5px solid var(--accent); border-radius:16px;
    padding:16px; margin-bottom:10px; cursor:pointer; transition:background 0.15s; }
  .ww-action-card:hover { background:var(--accent-light); }
  .ww-action-tag { font-family:var(--font-mono); font-size:10px; color:var(--accent);
    text-transform:uppercase; letter-spacing:0.1em; margin-bottom:6px; }
  .ww-action-text { font-size:14px; color:var(--text); line-height:1.5; }

  /* START BANNER */
  .ww-start-banner { background:linear-gradient(135deg,#1a3a4a 0%,#0f2a3a 50%,#1a2550 100%);
    border:1px solid rgba(45,212,160,0.3); border-radius:20px; padding:24px 20px; margin-bottom:16px;
    position:relative; overflow:hidden; }
  .ww-start-banner::before { content:''; position:absolute; top:-40px; right:-40px;
    width:160px; height:160px; border-radius:50%; border:1px solid rgba(45,212,160,0.15);
    pointer-events:none; }
  .ww-start-banner::after { content:''; position:absolute; top:-70px; right:-70px;
    width:220px; height:220px; border-radius:50%; border:1px solid rgba(45,212,160,0.08);
    pointer-events:none; }
  .ww-start-title { font-size:20px; font-weight:800; margin-bottom:6px; color:var(--text); }
  .ww-start-sub { font-family:var(--font-mono); font-size:12px; color:var(--muted); margin-bottom:18px; line-height:1.6; }
  .ww-start-btn { background:var(--accent); color:#0f1129; border:none; border-radius:12px;
    padding:14px 20px; font-family:var(--font-display); font-weight:800; font-size:14px;
    cursor:pointer; width:100%; transition:opacity 0.15s; letter-spacing:0.02em; }
  .ww-start-btn:hover { opacity:0.88; }

  /* FORM */
  .ww-label { font-family:var(--font-mono); font-size:11px; color:var(--muted);
    letter-spacing:0.08em; text-transform:uppercase; display:block; margin-bottom:6px; }
  .ww-input { width:100%; background:var(--surface2); border:1.5px solid var(--border);
    border-radius:12px; padding:12px 14px; color:var(--text); font-family:var(--font-mono);
    font-size:14px; outline:none; transition:border-color 0.2s; }
  .ww-input:focus { border-color:var(--accent); }
  .ww-form-group { margin-bottom:20px; }

  /* CHIPS */
  .ww-chips { display:flex; gap:8px; flex-wrap:wrap; }
  .ww-chip { background:var(--surface2); border:1.5px solid var(--border); border-radius:20px;
    padding:9px 18px; font-family:var(--font-mono); font-size:12px; cursor:pointer;
    transition:all 0.15s; color:var(--muted); }
  .ww-chip.selected { background:var(--accent); border-color:var(--accent); color:#0f1129; font-weight:600; }

  /* DOT PICKER */
  .ww-dot-row { display:flex; justify-content:space-between; gap:5px; margin:8px 0 4px; }
  .ww-dot { flex:1; aspect-ratio:1; border-radius:50%; border:1.5px solid var(--border);
    background:var(--surface2); cursor:pointer; transition:all 0.12s;
    display:flex; align-items:center; justify-content:center;
    font-family:var(--font-mono); font-size:12px; color:var(--muted);
    min-width:40px; min-height:40px; }
  .ww-dot:hover { border-color:var(--accent); color:var(--accent); }
  .ww-dot.active      { background:var(--accent); border-color:var(--accent); color:#0f1129; font-weight:600; box-shadow:0 0 10px rgba(45,212,160,0.4); }
  .ww-dot.active-warn { background:var(--amber);  border-color:var(--amber);  color:#0f1129; font-weight:600; box-shadow:0 0 10px rgba(245,166,35,0.4); }
  .ww-dot.active-bad  { background:var(--red);    border-color:var(--red);    color:#fff;    font-weight:600; box-shadow:0 0 10px rgba(240,96,128,0.4); }
  .ww-dot-labels { display:flex; justify-content:space-between;
    font-family:var(--font-mono); font-size:10px; color:var(--muted); margin-top:3px; }

  /* BUTTONS */
  .ww-btn { width:100%; padding:15px; background:var(--accent); color:#0f1129; border:none;
    border-radius:12px; font-family:var(--font-display); font-weight:800; font-size:15px;
    cursor:pointer; letter-spacing:0.02em; transition:opacity 0.15s; }
  .ww-btn:hover { opacity:0.88; }
  .ww-btn:disabled { opacity:0.25; cursor:not-allowed; }
  .ww-btn-ghost { background:transparent; border:1.5px solid var(--border); color:var(--muted);
    width:100%; padding:13px; border-radius:12px; font-family:var(--font-mono);
    font-size:13px; cursor:pointer; transition:all 0.15s; }
  .ww-btn-ghost:hover { border-color:var(--accent); color:var(--accent); }

  /* HISTORY */
  .ww-history-item { display:flex; justify-content:space-between; align-items:center;
    padding:12px 0; border-bottom:1px solid var(--border); }
  .ww-history-item:last-child { border-bottom:none; }
  .ww-badge { display:inline-block; padding:4px 10px; border-radius:6px;
    font-family:var(--font-mono); font-size:10px; font-weight:500; }

  /* CHART */
  .ww-chart { position:relative; height:110px; margin:6px 0; }
  .ww-chart svg { width:100%; height:100%; }

  /* CBI */
  .ww-cbi-q { margin-bottom:24px; }
  .ww-cbi-q-text { font-size:14px; color:var(--text); margin-bottom:12px; line-height:1.6; }
  .ww-cbi-opts { display:flex; gap:6px; }
  .ww-cbi-opt { flex:1; text-align:center; padding:11px 4px; background:var(--surface2);
    border:1.5px solid var(--border); border-radius:12px; font-family:var(--font-mono);
    font-size:10px; cursor:pointer; color:var(--muted); transition:all 0.12s; line-height:1.4; }
  .ww-cbi-opt.selected { background:var(--accent); border-color:var(--accent); color:#0f1129; font-weight:600; }

  /* PROGRESS */
  .ww-progress { height:4px; background:var(--border); border-radius:2px; margin-bottom:24px; }
  .ww-progress-bar { height:100%; border-radius:2px; background:var(--accent); transition:width 0.35s ease; }

  /* SCORE RING */
  .ww-score-wrap { display:flex; flex-direction:column; align-items:center; padding:16px 0; }
  .ww-score-ring { position:relative; width:140px; height:140px; }
  .ww-score-ring svg { width:140px; height:140px; transform:rotate(-90deg); }
  .ww-score-center { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); text-align:center; }
  .ww-score-num { font-family:var(--font-mono); font-size:30px; font-weight:500; }
  .ww-score-label { font-family:var(--font-mono); font-size:10px; color:var(--muted); text-transform:uppercase; }

  /* SUCCESS */
  .ww-success { text-align:center; padding:48px 20px; }
  .ww-success-icon { font-size:52px; margin-bottom:16px; }
  .ww-success-title { font-size:22px; font-weight:700; margin-bottom:6px; color:var(--text); }
  .ww-success-sub { font-family:var(--font-mono); font-size:12px; color:var(--muted); }

  /* MISC */
  .ww-section { font-family:var(--font-mono); font-size:10px; color:var(--muted);
    text-transform:uppercase; letter-spacing:0.12em; margin:22px 0 10px;
    display:flex; align-items:center; gap:10px; }
  .ww-section::after { content:''; flex:1; height:1px; background:var(--border); }
  .ww-empty { font-family:var(--font-mono); font-size:12px; color:var(--muted); text-align:center; padding:24px; }
  .mt-3 { margin-top:12px; }
  .mt-4 { margin-top:16px; }
  .mb-1 { margin-bottom:4px; }
  .mb-2 { margin-bottom:8px; }

  /* ROSTER GRID */
  .ww-roster-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:5px; margin-bottom:10px; }
  .ww-roster-day { border-radius:12px; border:1.5px solid var(--border); background:var(--surface2);
    padding:8px 3px 10px; text-align:center; cursor:pointer; transition:all 0.15s; user-select:none; }
  .ww-roster-day:hover { border-color:var(--accent); }
  .ww-roster-day.today { border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }
  .ww-roster-day-name { font-family:var(--font-mono); font-size:9px; color:var(--muted);
    text-transform:uppercase; letter-spacing:0.04em; margin-bottom:5px; }
  .ww-roster-day-num { font-family:var(--font-mono); font-size:15px; font-weight:500; color:var(--text); margin-bottom:6px; }
  .ww-roster-day-badge { font-family:var(--font-mono); font-size:8px; font-weight:700;
    border-radius:4px; padding:2px 5px; letter-spacing:0.03em; display:inline-block; min-height:16px; }
  .ww-modal-backdrop { position:fixed; inset:0; background:rgba(0,0,0,0.65); z-index:200;
    display:flex; align-items:flex-end; justify-content:center; }
  .ww-modal { background:var(--surface); border-radius:22px 22px 0 0; padding:24px 20px 44px;
    width:100%; max-width:480px; border-top:1px solid var(--border); animation:fadeUp 0.2s ease; }
  .ww-modal-title { font-family:var(--font-mono); font-size:11px; color:var(--muted);
    text-transform:uppercase; letter-spacing:0.1em; margin-bottom:18px; text-align:center; }
  .ww-modal-opts { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:12px; }
  .ww-modal-opt { padding:16px 10px; border-radius:14px; border:1.5px solid var(--border);
    background:var(--surface2); text-align:center; cursor:pointer; transition:all 0.15s; }
  .ww-modal-opt:hover { border-color:var(--accent); background:var(--accent-light); }
  .ww-modal-opt-label { font-family:var(--font-display); font-weight:700; font-size:15px; color:var(--text); }
  .ww-modal-opt-time  { font-family:var(--font-mono); font-size:10px; color:var(--muted); margin-top:4px; }
  .ww-modal-off { padding:13px; border-radius:12px; border:1.5px solid var(--border);
    background:transparent; color:var(--muted); font-family:var(--font-mono); font-size:12px;
    cursor:pointer; width:100%; transition:all 0.15s; margin-top:4px; }
  .ww-modal-off:hover { border-color:var(--red); color:var(--red); }
  .ww-notif-bar { background:rgba(45,212,160,0.1); border:1px solid rgba(45,212,160,0.25);
    border-radius:12px; padding:11px 14px; margin-bottom:12px; display:flex; gap:10px; align-items:center;
    font-family:var(--font-mono); font-size:11px; color:var(--green); cursor:pointer; }

  /* ONBOARDING */
  .ww-ob-overlay { position:fixed; inset:0; max-width:480px; margin:0 auto; background:var(--bg);
    z-index:300; display:flex; flex-direction:column; padding:0; }
  .ww-ob-content { flex:1; overflow-y:auto; padding:48px 24px 20px; }
  .ww-ob-actions { display:flex; gap:10px; padding:16px 24px 36px; border-top:1px solid var(--border); background:var(--surface); }
  .ww-ob-dots { display:flex; gap:6px; justify-content:center; margin-bottom:36px; }
  .ww-ob-dot { width:28px; height:4px; border-radius:2px; background:var(--border); transition:background 0.2s; }
  .ww-ob-dot.on   { background:var(--accent); }
  .ww-ob-dot.done { background:rgba(45,212,160,0.5); }
  .ww-ob-emoji { font-size:44px; text-align:center; margin-bottom:18px; }
  .ww-ob-title { font-family:var(--font-display); font-weight:800; font-size:26px; letter-spacing:-0.02em;
    color:var(--text); text-align:center; line-height:1.15; margin-bottom:10px; }
  .ww-ob-sub { font-family:var(--font-mono); font-size:12px; color:var(--muted); text-align:center;
    line-height:1.6; letter-spacing:0.02em; }
  .ww-ob-card { background:var(--surface); border:1.5px solid var(--border); border-radius:16px;
    padding:18px 16px; cursor:pointer; transition:all 0.15s; text-align:center; }
  .ww-ob-card:hover { border-color:var(--accent); }
  .ww-ob-card.sel { border-color:var(--accent); background:var(--accent-light); }
  .ww-ob-card-title { font-family:var(--font-display); font-weight:700; font-size:16px; color:var(--text); }
  .ww-ob-card-sub { font-family:var(--font-mono); font-size:10px; color:var(--muted); margin-top:4px; }
  .ww-ob-fact { font-family:var(--font-display); font-size:14px; line-height:1.65; color:var(--text);
    background:var(--surface); border:1px solid var(--border); border-radius:14px; padding:16px; }
  .ww-ob-fact strong { color:var(--accent); font-weight:700; }

  /* MINI BUTTONS (history) */
  .ww-mini-btn { background:var(--surface2); border:1px solid var(--border); border-radius:8px;
    padding:6px 12px; font-family:var(--font-mono); font-size:11px; color:var(--muted);
    cursor:pointer; transition:all 0.15s; }
  .ww-mini-btn:hover { border-color:var(--accent); color:var(--accent); }
  .ww-mini-btn.del:hover { border-color:var(--red); color:var(--red); }

  /* QUICK ACTION GRID (home) */
  .ww-quick { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;
    background:var(--surface); border:1.5px solid var(--border); border-radius:16px;
    padding:18px 8px; cursor:pointer; transition:all 0.15s; min-height:88px;
    font-family:var(--font-mono); font-size:11px; color:var(--text); }
  .ww-quick:hover, .ww-quick:active { border-color:var(--accent); background:var(--accent-light); }
`;

const styleEl = document.createElement("style");
styleEl.textContent = css;
document.head.appendChild(styleEl);

// ── Dot Picker (replaces slider) ───────────────────────────────────────────
function Stepper({ label, value, min, max, step, onChange, wrap=false }) {
  function dec() {
    let v = value - step;
    if (v < min) v = wrap ? max : min;
    onChange(v);
  }
  function inc() {
    let v = value + step;
    if (v > max) v = wrap ? min : max;
    onChange(v);
  }
  const btn = {flex:"0 0 48px", height:48, borderRadius:12, border:"1.5px solid var(--border)",
    background:"var(--surface2)", color:"var(--text)", fontSize:24, fontWeight:600, cursor:"pointer",
    display:"flex", alignItems:"center", justifyContent:"center", fontFamily:"var(--font-display)", userSelect:"none"};
  return (
    <div style={{flex:1}}>
      <div style={{display:"flex", alignItems:"center", gap:8}}>
        <button style={btn} onClick={dec} aria-label={"decrease "+label}>−</button>
        <div style={{flex:1, height:48, borderRadius:12, border:"1.5px solid var(--border)",
          background:"var(--surface)", display:"flex", alignItems:"center", justifyContent:"center",
          fontFamily:"var(--font-mono)", fontSize:20, fontWeight:500, color:"var(--text)"}}>
          {value}
        </div>
        <button style={btn} onClick={inc} aria-label={"increase "+label}>+</button>
      </div>
      <div style={{textAlign:"center", fontFamily:"var(--font-mono)", fontSize:10, color:"var(--muted)", marginTop:6, textTransform:"uppercase", letterSpacing:"0.06em"}}>{label}</div>
    </div>
  );
}

function DotPicker({ label, value, onChange, min=1, max=5, lowLabel="Low", highLabel="High" }) {
  const dots = Array.from({length: max - min + 1}, (_,i) => i + min);
  function dotClass(d) {
    if (d !== value) return "ww-dot";
    if (d <= 2) return "ww-dot active";
    if (d === 3) return "ww-dot active-warn";
    return "ww-dot active-bad";
  }
  const valColor = value <= 2 ? "var(--green)" : value === 3 ? "var(--amber)" : "var(--red)";
  return (
    <div className="ww-form-group">
      <div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-end", marginBottom:4}}>
        <label className="ww-label" style={{marginBottom:0}}>{label}</label>
      <span style={{fontFamily:"var(--font-mono)", fontSize:22, fontWeight:500, color:valColor, lineHeight:1}}>{value}<span style={{fontSize:13, color:"var(--muted)"}}>/{max}</span></span>
      </div>
      <div className="ww-dot-row">
        {dots.map(d => (
          <div key={d} className={dotClass(d)} onClick={() => onChange(d)}>{d}</div>
        ))}
      </div>
      <div className="ww-dot-labels"><span>{lowLabel}</span><span>{highLabel}</span></div>
    </div>
  );
}

// ── Sparkline ──────────────────────────────────────────────────────────────
function Sparkline({ values, color="var(--accent)" }) {
  if (!values || values.length < 2) return (<div className="ww-empty">Not enough data yet</div>);
  const min=Math.min(...values), max=Math.max(...values), range=max-min||1;
  const w=300, h=80, pad=10;
  const pts = values.map((v,i)=>{
    const x = pad+(i/(values.length-1))*(w-pad*2);
    const y = h-pad-((v-min)/range)*(h-pad*2);
    return `${x},${y}`;
  });
  const area = `${pad},${h-pad} `+pts.join(" ")+` ${w-pad},${h-pad}`;
  return (
    <div className="ww-chart">
      <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none">
        <defs>
          <linearGradient id="sg" x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor={color} stopOpacity="0.25"/>
            <stop offset="100%" stopColor={color} stopOpacity="0"/>
          </linearGradient>
        </defs>
        <polygon points={area} fill="url(#sg)"/>
        <polyline points={pts.join(" ")} fill="none" stroke={color} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/>
        {pts.map((p,i)=>{const[x,y]=p.split(","); return <circle key={i} cx={x} cy={y} r="3.5" fill={color}/>;} )}
      </svg>
    </div>
  );
}

// ── Score Ring ─────────────────────────────────────────────────────────────
function ScoreRing({ score, color }) {
  const r=52, circ=2*Math.PI*r, dash=(score/100)*circ;
  return (
    <div className="ww-score-wrap">
      <div className="ww-score-ring">
        <svg viewBox="0 0 130 130">
          <circle cx="65" cy="65" r={r} fill="none" stroke="var(--border)" strokeWidth="9"/>
          <circle cx="65" cy="65" r={r} fill="none" stroke={color} strokeWidth="9"
            strokeDasharray={`${dash} ${circ}`} strokeLinecap="round"/>
        </svg>
        <div className="ww-score-center">
          <div className="ww-score-num" style={{color}}>{score}</div>
          <div className="ww-score-label">/ 100</div>
        </div>
      </div>
    </div>
  );
}

// ── Shift configs ─────────────────────────────────────────────────────────
// Each type has colour + two preset durations (8h standard, 12h extended)
const SHIFT_CONFIG = {
  Day:      { color:"#f5a623", bg:"rgba(245,166,35,0.15)"  },
  Evening:  { color:"#60a5fa", bg:"rgba(96,165,250,0.15)"  },
  Night:    { color:"#a78bfa", bg:"rgba(167,139,250,0.15)" },
  "On-call":{ color:"#2dd4a0", bg:"rgba(45,212,160,0.15)"  },
};

// Default preset times — used until the user customises them
const DEFAULT_PRESETS = {
  Day:      [{label:"8h",start:"07:00",end:"15:00"},{label:"12h",start:"07:00",end:"19:00"}],
  Evening:  [{label:"8h",start:"14:00",end:"22:00"},{label:"12h",start:"12:00",end:"00:00"}],
  Night:    [{label:"8h",start:"22:00",end:"06:00"},{label:"12h",start:"19:00",end:"07:00"}],
  "On-call":[{label:"8h",start:"08:00",end:"16:00"},{label:"12h",start:"08:00",end:"20:00"}],
};
const PRESETS_KEY = "wardwatch_presets_v1";
function loadPresets() {
  try {
    const saved = JSON.parse(localStorage.getItem(PRESETS_KEY));
    return saved && saved.Day ? saved : JSON.parse(JSON.stringify(DEFAULT_PRESETS));
  } catch { return JSON.parse(JSON.stringify(DEFAULT_PRESETS)); }
}
function savePresets(p) { localStorage.setItem(PRESETS_KEY, JSON.stringify(p)); }


// Compute hours between two HH:MM strings (handles overnight)
function calcHours(start, end) {
  const [sh,sm] = start.split(":").map(Number);
  const [eh,em] = end.split(":").map(Number);
  let mins = (eh*60+em) - (sh*60+sm);
  if (mins <= 0) mins += 24*60;
  return Math.round(mins/60*10)/10;
}

// Returns array of 7 date strings YYYY-MM-DD starting from this week's Monday
function getWeekDays() {
  const now = new Date();
  const mon = new Date(now);
  mon.setDate(now.getDate() - ((now.getDay() + 6) % 7));
  return Array.from({length:7}, (_,i) => {
    const d = new Date(mon);
    d.setDate(mon.getDate() + i);
    return d.toISOString().slice(0,10);
  });
}

// ── Notifications ──────────────────────────────────────────────────────────
const scheduledTimers = new Set();

async function requestNotifPermission() {
  if (!("Notification" in window)) return false;
  if (Notification.permission === "granted") return true;
  const result = await Notification.requestPermission();
  return result === "granted";
}

function scheduleEndOfShiftNotif(dateStr, entry) {
  if (!("Notification" in window) || Notification.permission !== "granted") return;
  if (!entry || entry.type === "Off") return;
  const endHH = (entry.customEnd || "19:00");
  // If end hour < start hour, it crosses midnight
  const [sh] = (entry.customStart || "07:00").split(":").map(Number);
  const [eh] = endHH.split(":").map(Number);
  let endDateStr = dateStr;
  if (eh < sh) {
    const d = new Date(dateStr); d.setDate(d.getDate()+1);
    endDateStr = d.toISOString().slice(0,10);
  }
  const endTime = new Date(`${endDateStr}T${endHH}:00`);
  const delay = endTime - Date.now();
  const key = `${dateStr}-${entry.type}-${endHH}`;
  if (delay > 0 && delay < 24*3600*1000 && !scheduledTimers.has(key)) {
    scheduledTimers.add(key);
    setTimeout(() => {
      new Notification("WardWatch — Shift ended 🏥", {
        body: `Your ${entry.type} shift just finished. Tap to log your check-in.`,
        icon: "/favicon.ico", tag: key,
      });
    }, delay);
  }
}

function scheduleAllRosterNotifs(roster) {
  if (Notification.permission !== "granted") return;
  Object.entries(roster).forEach(([date, entry]) => {
    if (entry && entry.type !== "Off") scheduleEndOfShiftNotif(date, entry);
  });
}

// ── Roster Grid ────────────────────────────────────────────────────────────
const DAY_LABELS = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];

// Modal steps: "type" → "time"
function RosterGrid({ roster, onUpdate }) {
  const [picking, setPicking]     = useState(null);  // dateStr
  const [step, setStep]           = useState("type"); // "type" | "time"
  const [selType, setSelType]     = useState(null);
  const [customStart, setCustomStart] = useState("07:00");
  const [customEnd,   setCustomEnd]   = useState("19:00");
  const [overtime,    setOvertime]    = useState(false);
  const [presets, setPresets]     = useState(()=>loadPresets());
  const [editingPreset, setEditingPreset] = useState(null); // index being edited, or null

  const weekDays = getWeekDays();
  const todayStr = new Date().toISOString().slice(0,10);

  function openPicker(dateStr) {
    const existing = roster[dateStr];
    setEditingPreset(null);
    if (existing && existing.type && existing.type !== "Off") {
      setSelType(existing.type);
      setCustomStart(existing.customStart || presets[existing.type][1].start);
      setCustomEnd(existing.customEnd     || presets[existing.type][1].end);
      setOvertime(existing.overtime || false);
      setStep("time");
    } else {
      setSelType(null);
      setStep("type");
    }
    setPicking(dateStr);
  }

  function handleTypePick(type) {
    const preset = presets[type][1]; // default 12h
    setSelType(type);
    setCustomStart(preset.start);
    setCustomEnd(preset.end);
    setOvertime(false);
    setStep("time");
  }

  function applyPreset(preset) {
    setCustomStart(preset.start);
    setCustomEnd(preset.end);
  }

  // Save an edited preset time back to localStorage defaults
  function updatePresetTime(idx, field, value) {
    const next = {...presets, [selType]: presets[selType].map((p,i)=> i===idx ? {...p,[field]:value} : p)};
    setPresets(next);
    savePresets(next);
  }

  async function handleSave() {
    const entry = { type:selType, customStart, customEnd, overtime };
    const newRoster = {...roster, [picking]: entry};
    onUpdate(newRoster);
    const granted = await requestNotifPermission();
    if (granted) scheduleAllRosterNotifs(newRoster);
    setPicking(null); setStep("type");
  }

  function handleMarkOff() {
    const newRoster = {...roster, [picking]: {type:"Off"}};
    onUpdate(newRoster);
    setPicking(null); setStep("type");
  }

  function handleClear() {
    const newRoster = {...roster};
    delete newRoster[picking];
    onUpdate(newRoster);
    setPicking(null); setStep("type");
  }

  const pickingDate = picking
    ? new Date(picking).toLocaleDateString("en-NZ",{weekday:"long",day:"numeric",month:"short"})
    : "";

  const hrs = selType ? calcHours(customStart, customEnd) : 0;
  const isLong = hrs > 12;
  const cfg = selType ? SHIFT_CONFIG[selType] : null;

  return (
    <>
      <div className="ww-roster-grid">
        {weekDays.map((d, i) => {
          const entry   = roster[d];
          const type    = entry?.type;
          const dcfg    = type && type !== "Off" ? SHIFT_CONFIG[type] : null;
          const isToday = d === todayStr;
          const hrs     = entry && type !== "Off" ? calcHours(entry.customStart||"07:00", entry.customEnd||"19:00") : 0;
          return (
            <div key={d}
              className={`ww-roster-day${isToday?" today":""}${type==="Off"?" off":""}`}
              onClick={() => openPicker(d)}>
              <div className="ww-roster-day-name">{DAY_LABELS[i]}</div>
              <div className="ww-roster-day-num">{parseInt(d.slice(8))}</div>
              {type && type !== "Off" && dcfg ? (
                <>
                  <div className="ww-roster-day-badge" style={{background:dcfg.bg,color:dcfg.color}}>
                    {type==="On-call"?"OC":type.slice(0,3).toUpperCase()}
                  </div>
                  <div style={{fontFamily:"var(--font-mono)",fontSize:8,color:"var(--muted)",marginTop:3}}>{hrs}h{entry.overtime?" +OT":""}</div>
                </>
              ) : type === "Off" ? (
                <div className="ww-roster-day-badge" style={{background:"rgba(123,131,184,0.1)",color:"var(--muted)"}}>OFF</div>
              ) : (
                <div className="ww-roster-day-badge" style={{color:"transparent"}}>·</div>
              )}
            </div>
          );
        })}
      </div>

      {picking && (
        <div className="ww-modal-backdrop" onClick={()=>{setPicking(null);setStep("type");}}>
          <div className="ww-modal" onClick={e=>e.stopPropagation()} style={{maxHeight:"85vh",overflowY:"auto"}}>

            {/* ── STEP 1: Pick type ── */}
            {step === "type" && (
              <>
                <div className="ww-modal-title">{pickingDate} — Select shift type</div>
                <div className="ww-modal-opts">
                  {Object.entries(SHIFT_CONFIG).map(([type, c]) => (
                    <div key={type} className="ww-modal-opt" onClick={()=>handleTypePick(type)}>
                      <div className="ww-modal-opt-label" style={{color:c.color}}>{type}</div>
                      <div className="ww-modal-opt-time">{presets[type][0].start}–{presets[type][0].end} / {presets[type][1].start}–{presets[type][1].end}</div>
                    </div>
                  ))}
                </div>
                <button className="ww-modal-off" onClick={handleMarkOff}>Mark as Day Off</button>
                {roster[picking] && (
                  <button className="ww-modal-off" style={{marginTop:8}} onClick={handleClear}>Clear this day</button>
                )}
              </>
            )}

            {/* ── STEP 2: Edit times ── */}
            {step === "time" && cfg && (
              <>
                <div style={{display:"flex",alignItems:"center",gap:10,marginBottom:18}}>
                  <button onClick={()=>setStep("type")} style={{background:"none",border:"none",color:"var(--muted)",cursor:"pointer",fontSize:18,padding:0}}>←</button>
                  <div className="ww-modal-title" style={{margin:0,flex:1}}>{pickingDate}</div>
                  <div style={{fontFamily:"var(--font-mono)",fontSize:11,color:cfg.color,fontWeight:700}}>{selType}</div>
                </div>

                {/* Duration presets */}
                <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:8}}>
                  <span style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",textTransform:"uppercase",letterSpacing:"0.08em"}}>Duration presets</span>
                  <span onClick={()=>setEditingPreset(editingPreset===null ? "all" : null)}
                    style={{fontFamily:"var(--font-mono)",fontSize:10,color:editingPreset?"var(--accent)":"var(--muted)",cursor:"pointer",letterSpacing:"0.04em"}}>
                    {editingPreset ? "✓ done" : "✎ edit times"}
                  </span>
                </div>

                {!editingPreset ? (
                  /* Normal tappable presets */
                  <div style={{display:"flex",gap:8,marginBottom:18}}>
                    {presets[selType].map(p => {
                      const active = customStart===p.start && customEnd===p.end;
                      return (
                        <div key={p.label} onClick={()=>applyPreset(p)}
                          style={{flex:1,padding:"10px 8px",borderRadius:12,border:`1.5px solid ${active?cfg.color:"var(--border)"}`,
                            background:active?cfg.bg:"var(--surface2)",textAlign:"center",cursor:"pointer",transition:"all 0.15s"}}>
                          <div style={{fontFamily:"var(--font-display)",fontWeight:700,fontSize:16,color:active?cfg.color:"var(--text)"}}>{p.label}</div>
                          <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",marginTop:3}}>{p.start}–{p.end}</div>
                        </div>
                      );
                    })}
                    <div style={{flex:1,padding:"10px 8px",borderRadius:12,border:"1.5px solid var(--border)",
                      background:"var(--surface2)",textAlign:"center"}}>
                      <div style={{fontFamily:"var(--font-display)",fontWeight:700,fontSize:16,color:"var(--muted)"}}>Custom</div>
                      <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",marginTop:3}}>set below</div>
                    </div>
                  </div>
                ) : (
                  /* Edit mode — change the saved preset times */
                  <div style={{marginBottom:18}}>
                    {presets[selType].map((p, idx) => (
                      <div key={p.label} style={{display:"flex",alignItems:"center",gap:8,marginBottom:10}}>
                        <div style={{width:38,fontFamily:"var(--font-display)",fontWeight:700,fontSize:15,color:cfg.color}}>{p.label}</div>
                        <input type="time" className="ww-input" value={p.start}
                          onChange={e=>updatePresetTime(idx,"start",e.target.value)}
                          style={{textAlign:"center",fontSize:15,padding:"9px 6px"}}/>
                        <span style={{color:"var(--muted)"}}>–</span>
                        <input type="time" className="ww-input" value={p.end}
                          onChange={e=>updatePresetTime(idx,"end",e.target.value)}
                          style={{textAlign:"center",fontSize:15,padding:"9px 6px"}}/>
                      </div>
                    ))}
                    <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",textAlign:"center",marginTop:6}}>
                      Saved as your defaults for {selType} shifts
                    </div>
                  </div>
                )}

                {/* Time inputs */}
                <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:10,marginBottom:16}}>
                  <div>
                    <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",textTransform:"uppercase",letterSpacing:"0.08em",marginBottom:6}}>Start</div>
                    <input type="time" className="ww-input" value={customStart}
                      onChange={e=>setCustomStart(e.target.value)}
                      style={{textAlign:"center",fontSize:18,fontWeight:500,color:"var(--text)"}}/>
                  </div>
                  <div>
                    <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",textTransform:"uppercase",letterSpacing:"0.08em",marginBottom:6}}>End</div>
                    <input type="time" className="ww-input" value={customEnd}
                      onChange={e=>setCustomEnd(e.target.value)}
                      style={{textAlign:"center",fontSize:18,fontWeight:500,color:"var(--text)"}}/>
                  </div>
                </div>

                {/* Duration display */}
                <div style={{textAlign:"center",marginBottom:16,padding:"10px",borderRadius:12,background:"var(--surface2)",border:"1px solid var(--border)"}}>
                  <span style={{fontFamily:"var(--font-mono)",fontSize:26,fontWeight:500,color:isLong?"var(--amber)":"var(--accent)"}}>{hrs}h</span>
                  <span style={{fontFamily:"var(--font-mono)",fontSize:11,color:"var(--muted)",marginLeft:6}}>
                    {hrs <= 8 ? "standard" : hrs <= 12 ? "extended" : "⚠ long shift"}
                  </span>
                </div>

                {/* Overtime toggle */}
                <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",
                  padding:"12px 14px",borderRadius:12,border:"1.5px solid var(--border)",
                  background:"var(--surface2)",marginBottom:16,cursor:"pointer"}}
                  onClick={()=>setOvertime(o=>!o)}>
                  <div>
                    <div style={{fontFamily:"var(--font-display)",fontWeight:600,fontSize:14,color:"var(--text)"}}>Overtime shift</div>
                    <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",marginTop:2}}>Flag for hours tracking</div>
                  </div>
                  <div style={{width:44,height:26,borderRadius:13,background:overtime?"var(--amber)":"var(--border)",
                    position:"relative",transition:"background 0.2s",flexShrink:0}}>
                    <div style={{position:"absolute",top:3,left:overtime?21:3,width:20,height:20,
                      borderRadius:"50%",background:"#fff",transition:"left 0.2s",boxShadow:"0 1px 4px rgba(0,0,0,0.3)"}}/>
                  </div>
                </div>

                <button className="ww-btn" onClick={handleSave}>Save Shift</button>
                <button className="ww-modal-off" style={{marginTop:8}} onClick={handleMarkOff}>Mark as Day Off</button>
                {roster[picking] && (
                  <button className="ww-modal-off" style={{marginTop:8}} onClick={handleClear}>Clear this day</button>
                )}
              </>
            )}
          </div>
        </div>
      )}
    </>
  );
}

// ── HOME ───────────────────────────────────────────────────────────────────
function RecoveryWidget({ recovery, onNav, defaultOpen=false }) {
  const [open, setOpen] = useState(defaultOpen);
  if (!recovery) {
    return (
      <div style={{borderRadius:16, padding:"14px 18px", marginBottom:16,
        background:"var(--surface)", border:"1px solid var(--border)", cursor: onNav?"pointer":"default"}}
        onClick={onNav?()=>onNav("biometrics"):undefined}>
        <div style={{fontFamily:"var(--font-mono)",fontSize:11,color:"var(--muted)",lineHeight:1.6}}>
          ⌚ Add HRV &amp; sleep data to see your physiological recovery status and today's recovery plan.
        </div>
      </div>
    );
  }
  const mit = mitigationRegistry[recovery.status] || mitigationRegistry.OPTIMAL;
  return (
    <div style={{borderRadius:16, marginBottom:16, overflow:"hidden",
      background:`linear-gradient(135deg, ${recovery.color}22 0%, var(--surface) 70%)`,
      border:`1.5px solid ${recovery.color}`}}>
      <div style={{padding:"16px 18px"}}>
        <div style={{display:"flex",alignItems:"center",gap:9,marginBottom:7}}>
          <div style={{width:11,height:11,borderRadius:"50%",background:recovery.color,flexShrink:0,boxShadow:`0 0 10px ${recovery.color}`}}/>
          <div style={{fontFamily:"var(--font-display)",fontWeight:800,fontSize:17,color:recovery.color,letterSpacing:"-0.01em"}}>{recovery.label}</div>
          <div style={{marginLeft:"auto",fontFamily:"var(--font-mono)",fontSize:9,color:"var(--muted)",letterSpacing:"0.1em"}}>RECOVERY</div>
        </div>
        <div style={{fontSize:13,lineHeight:1.55,color:"var(--text)"}}>{recovery.message}</div>
        <div onClick={()=>setOpen(o=>!o)} style={{cursor:"pointer",marginTop:12,
          fontFamily:"var(--font-mono)",fontSize:11,color:recovery.color,letterSpacing:"0.03em",
          display:"flex",alignItems:"center",gap:6}}>
          {open ? "Hide today's plan" : "What to do today"} <span>{open?"▲":"▼"}</span>
        </div>
      </div>
      {open && (
        <div style={{borderTop:`1px solid ${recovery.color}44`, padding:"14px 18px",
          background:"rgba(0,0,0,0.15)"}}>
          <div style={{fontFamily:"var(--font-display)",fontWeight:700,fontSize:14,color:"var(--text)",marginBottom:12}}>{mit.title}</div>
          <div style={{display:"flex",gap:10,marginBottom:12,alignItems:"flex-start"}}>
            <span style={{fontSize:16,lineHeight:1.3,flexShrink:0}}>🏃</span>
            <div style={{fontFamily:"var(--font-mono)",fontSize:11.5,lineHeight:1.6,color:"var(--text)"}}>
              <span style={{color:"var(--muted)"}}>Movement — </span>{mit.exercise}
            </div>
          </div>
          <div style={{display:"flex",gap:10,alignItems:"flex-start"}}>
            <span style={{fontSize:16,lineHeight:1.3,flexShrink:0}}>🧠</span>
            <div style={{fontFamily:"var(--font-mono)",fontSize:11.5,lineHeight:1.6,color:"var(--text)"}}>
              <span style={{color:"var(--muted)"}}>Mind — </span>{mit.mindset}
            </div>
          </div>
          <div style={{fontFamily:"var(--font-mono)",fontSize:9,color:"var(--muted)",opacity:0.7,marginTop:12,lineHeight:1.5}}>
            Guidance based on your data — listen to your body and adjust. Not medical advice.
          </div>
        </div>
      )}
    </div>
  );
}

function HomePage({ data, onSave, onNav }) {
  const alerts = computeAlert(data);
  const avg7 = rollingFatigue(data.checkins, 7);
  const lastCbi = data.cbi[data.cbi.length-1];
  const isNew = data.shifts.length === 0;
  const notifGranted = typeof Notification !== "undefined" && Notification.permission === "granted";

  function handleRosterUpdate(newRoster) {
    const nd = {...data, roster: newRoster};
    saveData(nd);
    onSave(nd);
    scheduleAllRosterNotifs(newRoster);
  }

  const now = new Date();
  const startOfWeek = new Date(now); startOfWeek.setDate(now.getDate()-now.getDay()); startOfWeek.setHours(0,0,0,0);
  const shiftsThisWeek = data.shifts.filter(s=>new Date(s.start)>=startOfWeek);

  const lastShift = data.shifts[data.shifts.length-1];
  const lastCheckin = data.checkins[data.checkins.length-1];
  const checkinPending = lastShift && (!lastCheckin || new Date(lastCheckin.date)<new Date(lastShift.start));
  const lastCbiDate = lastCbi ? new Date(lastCbi.date) : null;
  const daysSinceCbi = lastCbiDate ? (Date.now()-lastCbiDate)/86400000 : 999;
  const recoveryDay = isRecoveryDay(data.roster);
  const recovery = evaluatePhysiologicalRecovery(buildDailyLog(data));
  // Prompt on a recovery day if 5+ days since last screen, OR force after 9 days regardless.
  const cbiDue = (recoveryDay && daysSinceCbi >= 5) || daysSinceCbi > 9 || !lastCbiDate;

  // Decide the ONE primary action for a tired user — most time-sensitive first.
  const greeting = (() => {
    const h = now.getHours();
    if (h < 5)  return "You're up late";
    if (h < 12) return "Good morning";
    if (h < 17) return "Good afternoon";
    if (h < 22) return "Good evening";
    return "Winding down";
  })();
  let primary = null;
  if (checkinPending) {
    primary = { tag:"After your shift", text:"How are you feeling? Quick 30-second check-in.", cta:"Start check-in", go:"checkin", color:"var(--accent)" };
  } else if (cbiDue && !isNew) {
    primary = { tag:recoveryDay?"Best moment this week":"Weekly check", text:recoveryDay?"You're rested with a shift coming up — the ideal time for your burnout screen.":"Your weekly burnout screen is due.", cta:"Take the screen", go:"cbi", color:"var(--blue)" };
  } else if (isNew) {
    primary = { tag:"Let's begin", text:"Log your first shift to start tracking.", cta:"Log a shift", go:"log", color:"var(--accent)" };
  }

  return (
    <div className="ww-page">
      <div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-start", marginBottom:20}}>
        <div style={{display:"flex", alignItems:"center", gap:11}}>
          <WardWatchLogo size={40}/>
          <div>
            <div className="ww-logo">Ward<span>Watch</span></div>
            <div className="ww-subtitle">{greeting}{data.profile?.name?`, ${data.profile.name}`:""}</div>
          </div>
        </div>
        <div style={{display:"flex",alignItems:"center",gap:14,paddingTop:4}}>
          <div style={{fontFamily:"var(--font-mono)", fontSize:11, color:"var(--muted)", textAlign:"right"}}>
            {now.toLocaleDateString("en-NZ",{weekday:"short",day:"numeric",month:"short"})}
          </div>
          <button onClick={()=>onNav("settings")} aria-label="Settings"
            style={{background:"none",border:"none",cursor:"pointer",color:"var(--muted)",padding:0,display:"flex"}}>
            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
          </button>
        </div>
      </div>

      {/* ONE primary action — the single thing to do right now */}
      {primary && (
        <div onClick={()=>onNav(primary.go)}
          style={{borderRadius:20, padding:"22px 20px", marginBottom:18, cursor:"pointer",
            background:primary.color,
            boxShadow:"0 8px 24px rgba(0,0,0,0.3)"}}>
          <div style={{fontFamily:"var(--font-mono)",fontSize:10,letterSpacing:"0.1em",textTransform:"uppercase",color:"rgba(15,17,41,0.7)",marginBottom:8,fontWeight:500}}>{primary.tag}</div>
          <div style={{fontFamily:"var(--font-display)",fontWeight:700,fontSize:18,color:"#0f1129",lineHeight:1.3,marginBottom:16}}>{primary.text}</div>
          <div style={{display:"inline-flex",alignItems:"center",gap:8,background:"#0f1129",color:"#fff",
            padding:"12px 20px",borderRadius:12,fontFamily:"var(--font-display)",fontWeight:700,fontSize:15}}>
            {primary.cta} <span style={{fontSize:18}}>→</span>
          </div>
        </div>
      )}

      {/* Recovery status + today's plan — the glanceable "how am I" */}
      {!isNew && <RecoveryWidget recovery={recovery} onNav={onNav}/>}

      {/* Alerts — only the real warnings, no "all good" noise competing for attention */}
      {!isNew && alerts.length > 0 && (
        <>
          <div className="ww-section">Worth noting</div>
          {alerts.map((a,i)=>(
            <div className="ww-alert" key={i}>
              <div className="ww-alert-dot"/>
              <div className="ww-alert-text">{a}</div>
            </div>
          ))}
        </>
      )}

      {/* At-a-glance stats — smaller, lower priority */}
      {!isNew && (
        <div className="ww-stat-row" style={{marginTop:18}}>
          <div className="ww-stat">
            <div className="ww-stat-val">{shiftsThisWeek.length}</div>
            <div className="ww-stat-lbl">Shifts<br/>this wk</div>
          </div>
          <div className="ww-stat">
            <div className="ww-stat-val" style={{color: avg7===null?"var(--muted)":avg7>=4?"var(--red)":avg7>=3?"var(--amber)":"var(--green)"}}>
              {avg7!==null ? avg7 : "—"}
            </div>
            <div className="ww-stat-lbl">Avg<br/>fatigue</div>
          </div>
          <div className="ww-stat">
            <div className="ww-stat-val" style={{color:lastCbi?burnoutLevel(lastCbi.score).color:"var(--muted)"}}>
              {lastCbi ? lastCbi.score : "—"}
            </div>
            <div className="ww-stat-lbl">Burnout<br/>score</div>
          </div>
        </div>
      )}
      {/* Roster */}
      {data.profile?.isShiftWorker && (<>
      <div className="ww-section">This week's roster</div>
      <div className="ww-card" style={{padding:"14px 12px 12px"}}>
        <RosterGrid roster={data.roster||{}} onUpdate={handleRosterUpdate}/>
        {!notifGranted && Object.values(data.roster||{}).some(v=>v&&v.type&&v.type!=="Off") && (
          <div className="ww-notif-bar" onClick={async()=>{ await requestNotifPermission(); }}>
            <span>🔔</span>
            <span>Tap to enable end-of-shift reminders</span>
          </div>
        )}
        {notifGranted && (
          <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",textAlign:"center",paddingTop:4}}>
            🔔 End-of-shift reminders on
          </div>
        )}
      </div>
      </>)}

      {/* Quick actions — equal-weight grid, easy targets */}
      <div className="ww-section">Log something</div>
      <div style={{display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gap:10}}>
        <button className="ww-quick" onClick={()=>onNav("log")}>
          <span style={{fontSize:22}}>📋</span>
          <span>Shift</span>
        </button>
        <button className="ww-quick" onClick={()=>onNav("checkin")}>
          <span style={{fontSize:22}}>💬</span>
          <span>Check-in</span>
        </button>
        <button className="ww-quick" onClick={()=>onNav("biometrics")}>
          <span style={{fontSize:22}}>⌚</span>
          <span>HRV / sleep</span>
        </button>
      </div>
    </div>
  );
}

// ── LOG SHIFT ──────────────────────────────────────────────────────────────
function LogShiftPage({ data, onSave }) {
  const now = new Date();
  const toLocal = d => new Date(d.getTime()-d.getTimezoneOffset()*60000).toISOString().slice(0,16);

  // If today's roster has a planned shift, prefill from it.
  const todayStr = new Date().toISOString().slice(0,16).slice(0,10);
  const rosterToday = data.roster?.[todayStr];
  const hasRoster = rosterToday && rosterToday.type && rosterToday.type !== "Off";

  function rosterStart() {
    if (!hasRoster) return toLocal(new Date(now.getTime()-8*3600000));
    return `${todayStr}T${rosterToday.customStart || "07:00"}`;
  }
  function rosterEnd() {
    if (!hasRoster) return toLocal(now);
    const [sh] = (rosterToday.customStart||"07:00").split(":").map(Number);
    const [eh] = (rosterToday.customEnd||"19:00").split(":").map(Number);
    let endDate = todayStr;
    if (eh < sh) { // crosses midnight (e.g. night shift)
      const d = new Date(todayStr); d.setDate(d.getDate()+1);
      endDate = d.toISOString().slice(0,10);
    }
    return `${endDate}T${rosterToday.customEnd || "19:00"}`;
  }

  const [type, setType]   = useState(hasRoster ? rosterToday.type : "Day");
  const [start, setStart] = useState(rosterStart());
  const [end,   setEnd]   = useState(rosterEnd());
  const [notes, setNotes] = useState("");
  const [overtime, setOvertime] = useState(hasRoster ? !!rosterToday.overtime : false);
  const [saved, setSaved] = useState(false);

  const hours = start && end ? shiftHours(start,end) : 0;
  const valid = start && end && hours>0 && hours<24;

  function handleSave() {
    const shift = {id:Date.now(), type, start, end, hours, notes, overtime, date:start};
    const nd = {...data, shifts:[...data.shifts, shift]};
    saveData(nd); onSave(nd); setSaved(true);
  }

  if (saved) return (
    <div className="ww-page">
      <div className="ww-success">
        <div className="ww-success-icon">✓</div>
        <div className="ww-success-title">Shift logged</div>
        <div className="ww-success-sub">{type} · {hours}h · {formatDate(start)}</div>
        <div style={{height:28}}/>
        <button className="ww-btn" onClick={()=>setSaved(false)}>Log another</button>
      </div>
    </div>
  );

  return (
    <div className="ww-page">
      <div className="ww-logo mb-2">Log <span>Shift</span></div>
      <div className="ww-subtitle" style={{marginBottom:24}}>Record your shift details</div>

      {hasRoster && (
        <div className="ww-notif-bar" style={{cursor:"default"}}>
          <span>📋</span>
          <span>Pre-filled from today's roster ({rosterToday.type}). Adjust if your actual shift differed.</span>
        </div>
      )}

      <div className="ww-form-group">
        <label className="ww-label">Shift type</label>
        <div className="ww-chips">
          {SHIFT_TYPES.map(t=>(
            <div key={t} className={`ww-chip ${type===t?"selected":""}`} onClick={()=>setType(t)}>{t}</div>
          ))}
        </div>
      </div>
      <div className="ww-form-group">
        <label className="ww-label">Start time</label>
        <input type="datetime-local" className="ww-input" value={start} onChange={e=>setStart(e.target.value)}/>
      </div>
      <div className="ww-form-group">
        <label className="ww-label">End time</label>
        <input type="datetime-local" className="ww-input" value={end} onChange={e=>setEnd(e.target.value)}/>
      </div>
      {hours>0 && (
        <div className="ww-card" style={{marginBottom:18}}>
          <span style={{fontFamily:"var(--font-mono)", fontSize:13, color:"var(--muted)"}}>Duration: </span>
          <span style={{fontFamily:"var(--font-mono)", fontSize:13, color:"var(--accent)", fontWeight:500}}>{hours} hours</span>
        </div>
      )}
      <div className="ww-form-group">
        <label className="ww-label">Notes (optional)</label>
        <input className="ww-input" placeholder="e.g. short-staffed, difficult patient..." value={notes} onChange={e=>setNotes(e.target.value)}/>
      </div>

      <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",
        padding:"12px 14px",borderRadius:12,border:"1.5px solid var(--border)",
        background:"var(--surface2)",marginBottom:18,cursor:"pointer"}}
        onClick={()=>setOvertime(o=>!o)}>
        <div style={{fontFamily:"var(--font-display)",fontWeight:600,fontSize:14,color:"var(--text)"}}>Overtime shift</div>
        <div style={{width:44,height:26,borderRadius:13,background:overtime?"var(--amber)":"var(--border)",
          position:"relative",transition:"background 0.2s",flexShrink:0}}>
          <div style={{position:"absolute",top:3,left:overtime?21:3,width:20,height:20,
            borderRadius:"50%",background:"#fff",transition:"left 0.2s"}}/>
        </div>
      </div>

      <button className="ww-btn" onClick={handleSave} disabled={!valid}>Save Shift</button>
    </div>
  );
}

// ── CHECK-IN ───────────────────────────────────────────────────────────────
function CheckinPage({ data, onSave, onNav }) {
  const [fatigue,   setFatigue]   = useState(3);
  const [stress,    setStress]    = useState(3);
  const [emotional, setEmotional] = useState(3);
  const [sleepH,    setSleepH]    = useState(7);
  const [sleepM,    setSleepM]    = useState(0);
  const sleep = Math.round((sleepH + sleepM/60) * 100) / 100; // decimal hours, kept for all downstream logic
  const [exercise,  setExercise]  = useState(false);
  const [saved, setSaved] = useState(false);
  const recovery = evaluatePhysiologicalRecovery(buildDailyLog(data));

  function handleSave() {
    const c = {id:Date.now(), date:new Date().toISOString(), fatigue, stress, emotional, sleep, exercise};
    const nd = {...data, checkins:[...data.checkins, c]};
    saveData(nd); onSave(nd); setSaved(true);
  }

  if (saved) return (
    <div className="ww-page">
      <div className="ww-success">
        <div className="ww-success-icon">✓</div>
        <div className="ww-success-title">Check-in saved</div>
        <div className="ww-success-sub">Fatigue {fatigue}/5 · Stress {stress}/5</div>
        <div style={{height:28}}/>
        <button className="ww-btn" onClick={()=>setSaved(false)}>New check-in</button>
      </div>
    </div>
  );

  return (
    <div className="ww-page">
      <div className="ww-logo mb-2">Post-shift <span>Check-in</span></div>
      <div className="ww-subtitle" style={{marginBottom:20}}>How are you feeling right now?</div>

      {/* Physiological recovery + today's plan */}
      <RecoveryWidget recovery={recovery} onNav={onNav} defaultOpen={recovery && recovery.status!=="OPTIMAL"}/>

      <DotPicker label="Physical fatigue" value={fatigue} onChange={setFatigue} lowLabel="Fresh" highLabel="Exhausted"/>
      <DotPicker label="Stress level"     value={stress}  onChange={setStress}  lowLabel="Calm"  highLabel="Overwhelmed"/>
      <DotPicker label="Emotional load"   value={emotional} onChange={setEmotional} lowLabel="Light" highLabel="Heavy"/>

      <div className="ww-form-group">
        <div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-end", marginBottom:10}}>
          <label className="ww-label" style={{marginBottom:0}}>Sleep last cycle</label>
          <span style={{fontFamily:"var(--font-mono)", fontSize:22, fontWeight:500, color:"var(--blue)", lineHeight:1}}>
            {sleepH}<span style={{fontSize:13, color:"var(--muted)"}}>h</span> {sleepM}<span style={{fontSize:13, color:"var(--muted)"}}>m</span>
          </span>
        </div>
        <div style={{display:"flex", gap:10}}>
          <Stepper label="Hours" value={sleepH} min={0} max={16} step={1} onChange={setSleepH}/>
          <Stepper label="Minutes" value={sleepM} min={0} max={45} step={15} onChange={setSleepM} wrap/>
        </div>
      </div>

      <div className="ww-form-group">
        <label className="ww-label">Did you exercise today?</label>
        <div className="ww-chips">
          <div className={`ww-chip ${exercise?"selected":""}`}  onClick={()=>setExercise(true)}>Yes ✓</div>
          <div className={`ww-chip ${!exercise?"selected":""}`} onClick={()=>setExercise(false)}>No</div>
        </div>
      </div>

      <button className="ww-btn" onClick={handleSave}>Save Check-in</button>
    </div>
  );
}

// ── CBI SCREEN ─────────────────────────────────────────────────────────────
function CbiPage({ data, onSave }) {
  const [answers, setAnswers] = useState({});
  const [submitted, setSubmitted] = useState(false);
  const answered = Object.keys(answers).length;
  const progress = answered/CBI_QUESTIONS.length;

  function handleSubmit() {
    const pScore = Math.round(CBI_QUESTIONS.filter(q=>q.scale==="personal").reduce((s,q)=>s+(answers[q.id]||0),0)/5);
    const wScore = Math.round(CBI_QUESTIONS.filter(q=>q.scale==="work").reduce((s,q)=>s+(answers[q.id]||0),0)/5);
    const total  = Math.round((pScore+wScore)/2);
    const entry  = {id:Date.now(), date:new Date().toISOString(), score:total, personal:pScore, work:wScore};
    const nd     = {...data, cbi:[...data.cbi, entry]};
    saveData(nd); onSave(nd); setSubmitted(true);
  }

  if (submitted) {
    const pScore = Math.round(CBI_QUESTIONS.filter(q=>q.scale==="personal").reduce((s,q)=>s+(answers[q.id]||0),0)/5);
    const wScore = Math.round(CBI_QUESTIONS.filter(q=>q.scale==="work").reduce((s,q)=>s+(answers[q.id]||0),0)/5);
    const total  = Math.round((pScore+wScore)/2);
    const level  = burnoutLevel(total);
    return (
      <div className="ww-page">
        <div className="ww-logo mb-2">Burnout <span>Result</span></div>
        <div className="ww-subtitle" style={{marginBottom:16}}>Copenhagen Burnout Inventory</div>
        <ScoreRing score={total} color={level.color}/>
        <div style={{textAlign:"center", marginBottom:20}}>
          <div style={{fontFamily:"var(--font-mono)", fontSize:18, color:level.color, fontWeight:500}}>{level.label} Burnout</div>
          <div style={{fontFamily:"var(--font-mono)", fontSize:11, color:"var(--muted)", marginTop:4}}>Assessed {new Date().toLocaleDateString("en-NZ")}</div>
        </div>
        <div className="ww-card">
          <div className="ww-card-title">Subscale breakdown</div>
          <div style={{display:"flex",justifyContent:"space-between",fontFamily:"var(--font-mono)",fontSize:13,marginBottom:8}}>
            <span style={{color:"var(--muted)"}}>Personal burnout</span>
            <span style={{color:burnoutLevel(pScore).color,fontWeight:500}}>{pScore}/100</span>
          </div>
          <div style={{display:"flex",justifyContent:"space-between",fontFamily:"var(--font-mono)",fontSize:13}}>
            <span style={{color:"var(--muted)"}}>Work burnout</span>
            <span style={{color:burnoutLevel(wScore).color,fontWeight:500}}>{wScore}/100</span>
          </div>
        </div>
        {total>=55 && (
          <div className="ww-alert">
            <div className="ww-alert-dot"/>
            <div className="ww-alert-text">Your score suggests significant burnout. Consider speaking with your charge nurse, occupational health team, or GP. NZNO EAP services are available to members.</div>
          </div>
        )}
        <button className="ww-btn mt-4" onClick={()=>setSubmitted(false)}>Retake Screen</button>
      </div>
    );
  }

  return (
    <div className="ww-page">
      <div className="ww-logo mb-2">Burnout <span>Screen</span></div>
      <div className="ww-subtitle" style={{marginBottom:8}}>Copenhagen Burnout Inventory · {answered}/{CBI_QUESTIONS.length}</div>
      <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",lineHeight:1.6,marginBottom:14}}>
        Best done on a rest day before your next shift block — it measures how much burnout remains after recovery.
      </div>
      <div className="ww-progress">
        <div className="ww-progress-bar" style={{width:`${progress*100}%`}}/>
      </div>
      {CBI_QUESTIONS.map((q,i)=>(
        <div className="ww-cbi-q" key={q.id}>
          <div className="ww-cbi-q-text">
            <span style={{color:"var(--muted)",fontFamily:"var(--font-mono)",fontSize:11}}>{i+1}. </span>{q.text}
          </div>
          <div className="ww-cbi-opts">
            {CBI_OPTIONS.map(opt=>(
              <div key={opt.value} className={`ww-cbi-opt ${answers[q.id]===opt.value?"selected":""}`}
                onClick={()=>setAnswers(p=>({...p,[q.id]:opt.value}))}>{opt.label}</div>
            ))}
          </div>
        </div>
      ))}
      <button className="ww-btn" onClick={handleSubmit} disabled={answered<CBI_QUESTIONS.length}>
        {answered<CBI_QUESTIONS.length ? `${CBI_QUESTIONS.length-answered} questions left` : "View Results"}
      </button>
    </div>
  );
}

// ── DASHBOARD ──────────────────────────────────────────────────────────────
function DashboardPage({ data }) {
  const [tab, setTab] = useState("fatigue");
  const fatigueVals = data.checkins.slice(-14).map(c=>c.fatigue);
  const stressVals  = data.checkins.slice(-14).map(c=>c.stress);
  const cbiVals     = data.cbi.slice(-8).map(c=>c.score);
  const recentShifts = [...data.shifts].sort((a,b)=>new Date(b.start)-new Date(a.start)).slice(0,7);
  const shiftCounts  = SHIFT_TYPES.reduce((acc,t)=>({...acc,[t]:data.shifts.filter(s=>s.type===t).length}),{});
  const totalHrs14   = data.shifts.filter(s=>new Date(s.start)>=new Date(Date.now()-14*86400000)).reduce((s,sh)=>s+(sh.hours||0),0);

  return (
    <div className="ww-page">
      <div className="ww-logo mb-2">Your <span>Trends</span></div>
      <div className="ww-subtitle" style={{marginBottom:20}}>Personal data overview</div>

      <div className="ww-stat-row" style={{gridTemplateColumns:"1fr 1fr"}}>
        <div className="ww-stat">
          <div className="ww-stat-val">{data.shifts.length}</div>
          <div className="ww-stat-lbl">Total shifts logged</div>
        </div>
        <div className="ww-stat">
          <div className="ww-stat-val" style={{color:totalHrs14>80?"var(--red)":totalHrs14>60?"var(--amber)":"var(--text)"}}>
            {Math.round(totalHrs14)}
          </div>
          <div className="ww-stat-lbl">Hours last 14 days</div>
        </div>
      </div>

      <div className="ww-chips" style={{marginBottom:12}}>
        {[["fatigue","Fatigue"],["stress","Stress"],["burnout","Burnout"]].map(([k,l])=>(
          <div key={k} className={`ww-chip ${tab===k?"selected":""}`} onClick={()=>setTab(k)}>{l}</div>
        ))}
      </div>
      <div className="ww-card">
        <div className="ww-card-title">
          {tab==="fatigue"&&"Fatigue trend (last 14 check-ins)"}
          {tab==="stress"&&"Stress trend (last 14 check-ins)"}
          {tab==="burnout"&&"Burnout score history (CBI)"}
        </div>
        {tab==="fatigue"&&<Sparkline values={fatigueVals} color="var(--amber)"/>}
        {tab==="stress" &&<Sparkline values={stressVals}  color="var(--red)"/>}
        {tab==="burnout"&&<Sparkline values={cbiVals}     color="var(--blue)"/>}
      </div>

      <div className="ww-card">
        <div className="ww-card-title">Shift type breakdown</div>
        {SHIFT_TYPES.map(t=>{
          const cnt = shiftCounts[t];
          const pct = data.shifts.length ? Math.round((cnt/data.shifts.length)*100) : 0;
          return (
            <div key={t} style={{marginBottom:10}}>
              <div style={{display:"flex",justifyContent:"space-between",fontFamily:"var(--font-mono)",fontSize:12,marginBottom:4}}>
                <span style={{color:"var(--muted)"}}>{t}</span>
                <span style={{color:"var(--text)"}}>{cnt} shifts ({pct}%)</span>
              </div>
              <div style={{height:5,background:"var(--border)",borderRadius:3}}>
                <div style={{height:5,width:`${pct}%`,background:"var(--accent)",borderRadius:3,transition:"width 0.4s"}}/>
              </div>
            </div>
          );
        })}
      </div>

      <div className="ww-section">Recent shifts</div>
      <div className="ww-card">
        {recentShifts.length===0 && <div className="ww-empty">No shifts logged yet</div>}
        {recentShifts.map(s=>(
          <div className="ww-history-item" key={s.id}>
            <div>
              <div style={{fontSize:14,fontWeight:600}}>{formatDate(s.start)}</div>
              <div style={{fontFamily:"var(--font-mono)",fontSize:11,color:"var(--muted)"}}>{formatTime(s.start)} → {formatTime(s.end)}</div>
            </div>
            <div style={{textAlign:"right"}}>
              <span className="ww-badge" style={{
                background:s.type==="Night"?"rgba(96,165,250,0.15)":"rgba(45,212,160,0.12)",
                color:s.type==="Night"?"var(--blue)":"var(--green)"
              }}>{s.type}</span>
              <div style={{fontFamily:"var(--font-mono)",fontSize:11,color:"var(--muted)",marginTop:4}}>{s.hours}h</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ── NAV ICONS ──────────────────────────────────────────────────────────────
const icons = {
  home:      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>,
  log:       <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>,
  checkin:   <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z"/></svg>,
  cbi:       <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"/></svg>,
  dashboard: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>,
};

// ── ONBOARDING ─────────────────────────────────────────────────────────────
function Onboarding({ onComplete }) {
  const [stepIdx, setStepIdx] = useState(0);
  const [isShiftWorker, setIsShiftWorker] = useState(null);
  const [selectedTypes, setSelectedTypes] = useState([]); // which shift types they work
  const [times, setTimes] = useState({}); // { Day:{start,end}, ... }
  const [reminders, setReminders] = useState(true);

  function toggleType(t) {
    setSelectedTypes(prev => {
      const next = prev.includes(t) ? prev.filter(x=>x!==t) : [...prev, t];
      // seed default times when newly added
      if (!prev.includes(t) && !times[t]) {
        const def = DEFAULT_PRESETS[t][1];
        setTimes(tm => ({...tm, [t]: {start:def.start, end:def.end}}));
      }
      return next;
    });
  }

  function setTime(type, field, value) {
    setTimes(tm => ({...tm, [type]: {...tm[type], [field]:value}}));
  }

  function finish() {
    // Build presets from chosen times: keep 8h default, set 12h (or actual) to their times
    if (isShiftWorker && selectedTypes.length) {
      const presets = loadPresets();
      selectedTypes.forEach(t => {
        if (times[t]) {
          const hrs = calcHours(times[t].start, times[t].end);
          const label = hrs >= 11 ? "12h" : "8h";
          // Set the matching-length preset to their actual time; keep the other as default
          presets[t] = DEFAULT_PRESETS[t].map(p =>
            p.label === label ? {...p, start:times[t].start, end:times[t].end} : p
          );
        }
      });
      savePresets(presets);
    }
    const profile = {
      isShiftWorker: Boolean(isShiftWorker),
      shiftTypes: isShiftWorker ? selectedTypes : [],
      reminders: Boolean(reminders),
      completedAt: new Date().toISOString(),
    };
    if (reminders && isShiftWorker) requestNotifPermission();
    onComplete(profile);
  }

  const steps = [];

  // Step 0 — shift worker?
  steps.push(
    <div key="s0">
      <div style={{display:"flex",justifyContent:"center",marginBottom:18}}><WardWatchLogo size={72}/></div>
      <div className="ww-ob-title">Welcome to WardWatch</div>
      <div className="ww-ob-sub">Let's set things up. First — are you a shift worker?</div>
      <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",textAlign:"center",marginTop:14,lineHeight:1.6,opacity:0.8}}>
        Preview version — your data is saved only on this device. Use Export in Settings to back it up.
      </div>
      <div style={{display:"grid",gap:10,marginTop:24}}>
        <div className={`ww-ob-card ${isShiftWorker===true?"sel":""}`} onClick={()=>setIsShiftWorker(true)}>
          <div className="ww-ob-card-title">Yes, I work shifts</div>
          <div className="ww-ob-card-sub">Days, evenings, nights, or rotating</div>
        </div>
        <div className={`ww-ob-card ${isShiftWorker===false?"sel":""}`} onClick={()=>setIsShiftWorker(false)}>
          <div className="ww-ob-card-title">No, regular hours</div>
          <div className="ww-ob-card-sub">I'll still track fatigue & burnout</div>
        </div>
      </div>
    </div>
  );

  // Step 1 — which shift types (only if shift worker)
  if (isShiftWorker) {
    steps.push(
      <div key="s1">
        <div className="ww-ob-emoji">🕐</div>
        <div className="ww-ob-title">Which shifts do you work?</div>
        <div className="ww-ob-sub">Select all that apply — we'll hide the rest.</div>
        <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:10,marginTop:24}}>
          {Object.keys(SHIFT_CONFIG).map(t => (
            <div key={t} className={`ww-ob-card ${selectedTypes.includes(t)?"sel":""}`}
              style={selectedTypes.includes(t)?{borderColor:SHIFT_CONFIG[t].color,background:SHIFT_CONFIG[t].bg}:{}}
              onClick={()=>toggleType(t)}>
              <div className="ww-ob-card-title" style={{color:selectedTypes.includes(t)?SHIFT_CONFIG[t].color:"var(--text)"}}>{t}</div>
            </div>
          ))}
        </div>
      </div>
    );

    // Step 2 — set times for each selected type
    steps.push(
      <div key="s2">
        <div className="ww-ob-emoji">⏰</div>
        <div className="ww-ob-title">Your usual shift times</div>
        <div className="ww-ob-sub">Set the times you normally work. You can fine-tune any day later.</div>
        <div style={{marginTop:24}}>
          {selectedTypes.length===0 && <div className="ww-empty">Go back and pick at least one shift type.</div>}
          {selectedTypes.map(t => (
            <div key={t} style={{marginBottom:16}}>
              <div style={{fontFamily:"var(--font-mono)",fontSize:12,color:SHIFT_CONFIG[t].color,fontWeight:700,marginBottom:8,letterSpacing:"0.04em"}}>{t}</div>
              <div style={{display:"flex",alignItems:"center",gap:8}}>
                <input type="time" className="ww-input" value={times[t]?.start||""}
                  onChange={e=>setTime(t,"start",e.target.value)} style={{textAlign:"center",fontSize:16}}/>
                <span style={{color:"var(--muted)"}}>–</span>
                <input type="time" className="ww-input" value={times[t]?.end||""}
                  onChange={e=>setTime(t,"end",e.target.value)} style={{textAlign:"center",fontSize:16}}/>
                <span style={{fontFamily:"var(--font-mono)",fontSize:12,color:"var(--muted)",width:42,textAlign:"right"}}>
                  {times[t]?.start&&times[t]?.end ? calcHours(times[t].start,times[t].end)+"h" : ""}
                </span>
              </div>
            </div>
          ))}
        </div>
      </div>
    );
  }

  // Final step — the burnout fact + (reminders toggle if shift worker)
  steps.push(
    <div key="sf">
      <div className="ww-ob-emoji">💡</div>
      <div className="ww-ob-title">Why this matters</div>
      <div className="ww-ob-fact">
        Around <strong>1 in 3</strong> shift-working nurses experience burnout — and those working more than
        six night shifts a month are over <strong>2.5× more likely</strong> to be affected. The biggest driver
        is circadian disruption from irregular sleep.
      </div>
      <div className="ww-ob-fact" style={{marginTop:12}}>
        What helps most, per the research: <strong>consistent sleep timing</strong>, <strong>strategic naps</strong> before
        night shifts, <strong>controlled light exposure</strong> (bright at work, dark before sleep), and
        <strong> protecting rest days</strong>. WardWatch helps you spot the warning signs early so you can act before burnout sets in.
      </div>

      {isShiftWorker && (
        <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",
          padding:"14px 16px",borderRadius:12,border:"1.5px solid var(--border)",
          background:"var(--surface2)",marginTop:20,cursor:"pointer"}}
          onClick={()=>setReminders(r=>!r)}>
          <div>
            <div style={{fontFamily:"var(--font-display)",fontWeight:600,fontSize:14,color:"var(--text)"}}>End-of-shift reminders</div>
            <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",marginTop:2}}>Nudge me to check in after work</div>
          </div>
          <div style={{width:44,height:26,borderRadius:13,background:reminders?"var(--accent)":"var(--border)",
            position:"relative",transition:"background 0.2s",flexShrink:0}}>
            <div style={{position:"absolute",top:3,left:reminders?21:3,width:20,height:20,
              borderRadius:"50%",background:"#fff",transition:"left 0.2s",boxShadow:"0 1px 4px rgba(0,0,0,0.3)"}}/>
          </div>
        </div>
      )}
    </div>
  );

  const isLast = stepIdx === steps.length - 1;
  // Per-step validation for the Next button
  let canAdvance = true;
  if (stepIdx === 0) canAdvance = isShiftWorker !== null;
  if (isShiftWorker && stepIdx === 1) canAdvance = selectedTypes.length > 0;
  if (isShiftWorker && stepIdx === 2) canAdvance = selectedTypes.every(t => times[t]?.start && times[t]?.end);

  return (
    <div className="ww-ob-overlay">
      <div className="ww-ob-content">
        {/* progress dots */}
        <div className="ww-ob-dots">
          {steps.map((_,i)=>(
            <div key={i} className={`ww-ob-dot ${i===stepIdx?"on":""} ${i<stepIdx?"done":""}`}/>
          ))}
        </div>
        <div className="ww-ob-body">{steps[stepIdx]}</div>
      </div>
      <div className="ww-ob-actions">
        {stepIdx > 0 && (
          <button className="ww-btn-ghost" style={{flex:"0 0 90px"}} onClick={()=>setStepIdx(i=>i-1)}>Back</button>
        )}
        <button className="ww-btn" style={{flex:1, opacity:canAdvance?1:0.35}}
          disabled={!canAdvance}
          onClick={()=> isLast ? finish() : setStepIdx(i=>i+1)}>
          {isLast ? "Get started" : "Continue"}
        </button>
      </div>
    </div>
  );
}

// ── HISTORY / MANAGE ENTRIES ───────────────────────────────────────────────
function HistoryPage({ data, onSave, onNav }) {
  const [tab, setTab] = useState("shifts");
  const [editing, setEditing] = useState(null); // {kind, id, ...fields}

  function deleteEntry(kind, id) {
    const nd = {...data, [kind]: data[kind].filter(x=>x.id!==id)};
    saveData(nd); onSave(nd);
  }
  function saveEdit() {
    const nd = {...data, [editing.kind]: data[editing.kind].map(x=>x.id===editing.id?editing.payload:x)};
    saveData(nd); onSave(nd); setEditing(null);
  }

  const shifts   = [...data.shifts].sort((a,b)=>new Date(b.start)-new Date(a.start));
  const checkins = [...data.checkins].sort((a,b)=>new Date(b.date)-new Date(a.date));
  const screens  = [...data.cbi].sort((a,b)=>new Date(b.date)-new Date(a.date));

  return (
    <div className="ww-page">
      <div style={{display:"flex",alignItems:"center",gap:12,marginBottom:18}}>
        <button onClick={()=>onNav("settings")} style={{background:"none",border:"none",color:"var(--muted)",fontSize:20,cursor:"pointer",padding:0}}>←</button>
        <div className="ww-logo" style={{fontSize:22}}>Manage <span>Entries</span></div>
      </div>

      <div className="ww-chips" style={{marginBottom:16}}>
        {[["shifts","Shifts"],["checkins","Check-ins"],["cbi","Screens"]].map(([k,l])=>(
          <div key={k} className={`ww-chip ${tab===k?"selected":""}`} onClick={()=>setTab(k)}>{l}</div>
        ))}
      </div>

      {/* SHIFTS */}
      {tab==="shifts" && (
        <div className="ww-card">
          {shifts.length===0 && <div className="ww-empty">No shifts logged</div>}
          {shifts.map(s=>(
            <div className="ww-history-item" key={s.id}>
              <div>
                <div style={{fontSize:14,fontWeight:600}}>{formatDate(s.start)} · {s.type}</div>
                <div style={{fontFamily:"var(--font-mono)",fontSize:11,color:"var(--muted)"}}>{formatTime(s.start)}–{formatTime(s.end)} · {s.hours}h{s.overtime?" +OT":""}</div>
              </div>
              <div style={{display:"flex",gap:8}}>
                <button className="ww-mini-btn" onClick={()=>setEditing({kind:"shifts",id:s.id,payload:{...s}})}>Edit</button>
                <button className="ww-mini-btn del" onClick={()=>deleteEntry("shifts",s.id)}>Delete</button>
              </div>
            </div>
          ))}
        </div>
      )}

      {/* CHECK-INS */}
      {tab==="checkins" && (
        <div className="ww-card">
          {checkins.length===0 && <div className="ww-empty">No check-ins yet</div>}
          {checkins.map(c=>(
            <div className="ww-history-item" key={c.id}>
              <div>
                <div style={{fontSize:14,fontWeight:600}}>{formatDate(c.date)}</div>
                <div style={{fontFamily:"var(--font-mono)",fontSize:11,color:"var(--muted)"}}>Fatigue {c.fatigue} · Stress {c.stress} · Sleep {c.sleep}h</div>
              </div>
              <button className="ww-mini-btn del" onClick={()=>deleteEntry("checkins",c.id)}>Delete</button>
            </div>
          ))}
        </div>
      )}

      {/* CBI */}
      {tab==="cbi" && (
        <div className="ww-card">
          {screens.length===0 && <div className="ww-empty">No burnout screens yet</div>}
          {screens.map(c=>(
            <div className="ww-history-item" key={c.id}>
              <div>
                <div style={{fontSize:14,fontWeight:600}}>{formatDate(c.date)} · {c.score}/100</div>
                <div style={{fontFamily:"var(--font-mono)",fontSize:11,color:"var(--muted)"}}>Personal {c.personal} · Work {c.work}</div>
              </div>
              <button className="ww-mini-btn del" onClick={()=>deleteEntry("cbi",c.id)}>Delete</button>
            </div>
          ))}
        </div>
      )}

      {/* EDIT MODAL (shifts only) */}
      {editing && editing.kind==="shifts" && (
        <div className="ww-modal-backdrop" onClick={()=>setEditing(null)}>
          <div className="ww-modal" onClick={e=>e.stopPropagation()}>
            <div className="ww-modal-title">Edit shift — {formatDate(editing.payload.start)}</div>
            <div className="ww-form-group">
              <label className="ww-label">Type</label>
              <div className="ww-chips">
                {Object.keys(SHIFT_CONFIG).map(t=>(
                  <div key={t} className={`ww-chip ${editing.payload.type===t?"selected":""}`}
                    onClick={()=>setEditing(e=>({...e,payload:{...e.payload,type:t}}))}>{t}</div>
                ))}
              </div>
            </div>
            <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:10,marginBottom:16}}>
              <div>
                <label className="ww-label">Start</label>
                <input type="datetime-local" className="ww-input" value={editing.payload.start.slice(0,16)}
                  onChange={e=>{const v=e.target.value; setEditing(ed=>({...ed,payload:{...ed.payload,start:v,hours:shiftHours(v,ed.payload.end)}}));}}/>
              </div>
              <div>
                <label className="ww-label">End</label>
                <input type="datetime-local" className="ww-input" value={editing.payload.end.slice(0,16)}
                  onChange={e=>{const v=e.target.value; setEditing(ed=>({...ed,payload:{...ed.payload,end:v,hours:shiftHours(ed.payload.start,v)}}));}}/>
              </div>
            </div>
            <button className="ww-btn" onClick={saveEdit}>Save Changes</button>
          </div>
        </div>
      )}
    </div>
  );
}

// ── BIOMETRICS (manual entry) ──────────────────────────────────────────────
function BiometricsPage({ data, onSave, onNav }) {
  const today = new Date().toISOString().slice(0,10);
  const [date, setDate]           = useState(today);
  const [hrv, setHrv]             = useState("");
  const [restingHr, setRestingHr] = useState("");
  const [sleepHrs, setSleepHrs]   = useState(0);   // total sleep hours
  const [sleepMin, setSleepMin]   = useState(0);   // total sleep minutes
  const [deepMin, setDeepMin]     = useState(0);   // deep sleep, minutes
  const [remMin, setRemMin]       = useState(0);   // rem sleep, minutes
  const [sleepScore, setSleepScore] = useState("");
  const [saved, setSaved] = useState(false);
  const [showRef, setShowRef] = useState(false);

  // Compose decimal hours / hour-fractions for storage (unchanged downstream shape)
  const sleepHours = (sleepHrs || sleepMin) ? Math.round((sleepHrs + sleepMin/60)*100)/100 : "";
  const deepSleep  = deepMin ? Math.round((deepMin/60)*100)/100 : "";
  const remSleep   = remMin  ? Math.round((remMin/60)*100)/100  : "";

  const bl = data.baselines || {};
  const latest = latestBiometric(data.biometrics);

  // Flag a metric as "off" if the latest reading is outside its target band.
  const flags = {};
  if (latest) {
    if (latest.hrv != null && bl.hrv && bl.hrv.sd) {
      const drop = ((bl.hrv.mean - latest.hrv)/bl.hrv.mean)*100;
      flags.hrv = drop >= 10; // beyond ±10% band (drop side)
    }
    if (latest.sleepHours != null && latest.deepSleep != null && latest.sleepHours > 0) {
      const deepPct = (latest.deepSleep/latest.sleepHours)*100;
      flags.deep = deepPct < 15;
    }
    if (latest.sleepHours != null && latest.remSleep != null && latest.sleepHours > 0) {
      const remPct = (latest.remSleep/latest.sleepHours)*100;
      flags.rem = remPct < 20;
    }
    if (latest.restingHr != null && bl.restingHr && bl.restingHr.sd) {
      flags.rhr = latest.restingHr > bl.restingHr.mean + bl.restingHr.sd;
    }
  }
  const anyOutOfRange = Object.values(flags).some(Boolean);

  // Auto-expand the reference only when something is actually off.
  useEffect(() => { if (anyOutOfRange) setShowRef(true); }, [anyOutOfRange]);

  function handleSave() {
    const entry = {
      id: Date.now(),
      date,
      source: "manual",
      hrv:        hrv        !== "" ? Number(hrv)        : null,
      restingHr:  restingHr  !== "" ? Number(restingHr)  : null,
      sleepHours: sleepHours !== "" ? Number(sleepHours) : null,
      deepSleep:  deepSleep  !== "" ? Number(deepSleep)  : null, // already stored as hours
      remSleep:   remSleep   !== "" ? Number(remSleep)   : null,
      sleepScore: sleepScore !== "" ? Number(sleepScore) : null,
      syncedAt: new Date().toISOString(),
    };
    // Upsert by (date, source): replace any existing manual entry for that date
    const others = data.biometrics.filter(b => !(b.date===date && b.source==="manual"));
    const biometrics = [...others, entry];
    const baselines = computeBaselines(biometrics);
    const nd = {...data, biometrics, baselines};
    saveData(nd); onSave(nd); setSaved(true);
  }

  function zLabel(val, base) {
    if (val==="" || !base || base.sd===0) return null;
    const z = (Number(val)-base.mean)/base.sd;
    if (z <= -1.5) return { txt:"well below your baseline", color:"var(--red)" };
    if (z <= -0.5) return { txt:"a little below baseline", color:"var(--amber)" };
    if (z >=  0.5) return { txt:"above baseline — good", color:"var(--green)" };
    return { txt:"around your baseline", color:"var(--muted)" };
  }

  if (saved) return (
    <div className="ww-page">
      <div className="ww-success">
        <div className="ww-success-icon">✓</div>
        <div className="ww-success-title">Biometrics saved</div>
        <div className="ww-success-sub">{date} · from your watch</div>
        <div style={{height:28}}/>
        <button className="ww-btn" onClick={()=>{setSaved(false);setHrv("");setRestingHr("");setSleepHrs(0);setSleepMin(0);setDeepMin(0);setRemMin(0);setSleepScore("");}}>Add another day</button>
        <div style={{height:10}}/>
        <button className="ww-btn-ghost" onClick={()=>onNav("home")}>Back to home</button>
      </div>
    </div>
  );

  const hrvZ = zLabel(hrv, bl.hrv);
  const sleepZ = zLabel(sleepHours, bl.sleep);

  return (
    <div className="ww-page">
      <div style={{display:"flex",alignItems:"center",gap:12,marginBottom:8}}>
        <button onClick={()=>onNav("home")} style={{background:"none",border:"none",color:"var(--muted)",fontSize:20,cursor:"pointer",padding:0}}>←</button>
        <div className="ww-logo" style={{fontSize:22}}>Log <span>Biometrics</span></div>
      </div>
      <div className="ww-subtitle" style={{marginBottom:6}}>From your watch or fitness app</div>
      <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",lineHeight:1.6,marginBottom:20}}>
        Enter what your wearable shows for the morning of this date. Overnight HRV and resting heart rate are the most useful for spotting strain early.
      </div>

      <div className="ww-form-group">
        <label className="ww-label">Date</label>
        <input type="date" className="ww-input" value={date} max={today} onChange={e=>setDate(e.target.value)}/>
      </div>

      <div className="ww-form-group">
        <label className="ww-label">Overnight HRV (ms)</label>
        <input type="number" inputMode="decimal" className="ww-input" placeholder="e.g. 45" value={hrv} onChange={e=>setHrv(e.target.value)}/>
        {hrvZ && <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:hrvZ.color,marginTop:6}}>{hrvZ.txt}{bl.hrv?` (baseline ${bl.hrv.mean}ms)`:""}</div>}
      </div>

      <div className="ww-form-group">
        <label className="ww-label">Resting heart rate (bpm)</label>
        <input type="number" inputMode="numeric" className="ww-input" placeholder="e.g. 58" value={restingHr} onChange={e=>setRestingHr(e.target.value)}/>
      </div>

      <div className="ww-form-group">
        <div style={{display:"flex", justifyContent:"space-between", alignItems:"flex-end", marginBottom:10}}>
          <label className="ww-label" style={{marginBottom:0}}>Total sleep</label>
          <span style={{fontFamily:"var(--font-mono)", fontSize:18, fontWeight:500, color:"var(--blue)", lineHeight:1}}>
            {sleepHrs}<span style={{fontSize:12, color:"var(--muted)"}}>h</span> {sleepMin}<span style={{fontSize:12, color:"var(--muted)"}}>m</span>
          </span>
        </div>
        <div style={{display:"flex", gap:10}}>
          <Stepper label="Hours" value={sleepHrs} min={0} max={16} step={1} onChange={setSleepHrs}/>
          <Stepper label="Minutes" value={sleepMin} min={0} max={45} step={15} onChange={setSleepMin} wrap/>
        </div>
        {sleepZ && <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:sleepZ.color,marginTop:8}}>{sleepZ.txt}{bl.sleep?` (baseline ${bl.sleep.mean}h)`:""}</div>}
      </div>

      <div style={{display:"flex",gap:10}}>
        <div className="ww-form-group" style={{flex:1}}>
          <div style={{display:"flex",justifyContent:"space-between",alignItems:"flex-end",marginBottom:10}}>
            <label className="ww-label" style={{marginBottom:0}}>Deep sleep</label>
            <span style={{fontFamily:"var(--font-mono)",fontSize:14,color:"var(--muted)"}}>{deepMin}m</span>
          </div>
          <Stepper label="Minutes" value={deepMin} min={0} max={180} step={15} onChange={setDeepMin}/>
        </div>
        <div className="ww-form-group" style={{flex:1}}>
          <div style={{display:"flex",justifyContent:"space-between",alignItems:"flex-end",marginBottom:10}}>
            <label className="ww-label" style={{marginBottom:0}}>REM sleep</label>
            <span style={{fontFamily:"var(--font-mono)",fontSize:14,color:"var(--muted)"}}>{remMin}m</span>
          </div>
          <Stepper label="Minutes" value={remMin} min={0} max={240} step={15} onChange={setRemMin}/>
        </div>
      </div>

      <div className="ww-form-group">
        <label className="ww-label">Sleep score (0–100, optional)</label>
        <input type="number" inputMode="numeric" className="ww-input" placeholder="e.g. 74" value={sleepScore} onChange={e=>setSleepScore(e.target.value)}/>
      </div>

      <button className="ww-btn" onClick={handleSave} disabled={hrv===""&&restingHr===""&&sleepHrs===0&&sleepMin===0&&deepMin===0&&remMin===0&&sleepScore===""}>Save Biometrics</button>

      {/* Baseline summary */}
      {(bl.hrv||bl.restingHr||bl.sleep) && (
        <>
          <div className="ww-section">Your baselines</div>
          <div className="ww-card">
            <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",marginBottom:12}}>Rolling 14-day averages — what "normal" looks like for you</div>
            {bl.hrv && <BaseRow label="HRV" val={`${bl.hrv.mean} ms`} sub={`±${bl.hrv.sd} · ${bl.hrv.n} days`}/>}
            {bl.restingHr && <BaseRow label="Resting HR" val={`${bl.restingHr.mean} bpm`} sub={`±${bl.restingHr.sd} · ${bl.restingHr.n} days`}/>}
            {bl.sleep && <BaseRow label="Sleep" val={`${bl.sleep.mean} h`} sub={`±${bl.sleep.sd} · ${bl.sleep.n} days`}/>}
          </div>
          <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",lineHeight:1.6,textAlign:"center",marginTop:4}}>
            Need ~3+ days of data before baselines and strain alerts kick in.
          </div>
        </>
      )}
      {latest && (
        <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",textAlign:"center",marginTop:16}}>
          Last entry: {latest.date}
        </div>
      )}

      {/* Reference: metric · target · shift-work reality — collapsible, auto-opens when off */}
      <div className="ww-section" onClick={()=>setShowRef(s=>!s)} style={{cursor:"pointer"}}>
        What the numbers mean
        {anyOutOfRange && !showRef && <span style={{color:"var(--amber)",marginLeft:6}}>· check</span>}
        <span style={{marginLeft:6,color:"var(--muted)"}}>{showRef ? "▲" : "▼"}</span>
      </div>
      {showRef && (<>
      {[
        { key:"deep", metric:"Deep sleep %", target:"15–25% of total sleep",
          reality:"Often drops drastically during daytime sleep blocks after a night shift." },
        { key:"hrv", metric:"HRV deviation", target:"Within ±10% of your 14-day average",
          reality:"A drop over 15% suggests your sympathetic nervous system is dominated by work stress." },
        { key:"rem", metric:"REM sleep %", target:"20–25% of total sleep",
          reality:"Fragmented by caffeine and irregular sleep timing across rotating shifts." },
        { key:"rhr", metric:"Resting heart rate", target:"Stable vs your baseline",
          reality:"Creeps up when recovery is incomplete between consecutive shifts." },
      ].map((row,i)=>(
        <div key={i} className="ww-card" style={{padding:"14px 16px", borderColor: flags[row.key] ? "var(--amber)" : "var(--border)"}}>
          <div style={{display:"flex",justifyContent:"space-between",alignItems:"baseline",marginBottom:6,gap:10}}>
            <span style={{fontFamily:"var(--font-display)",fontWeight:700,fontSize:14,color:"var(--text)"}}>
              {row.metric}{flags[row.key] && <span style={{color:"var(--amber)",fontFamily:"var(--font-mono)",fontSize:10,marginLeft:6}}>off target</span>}
            </span>
            <span style={{fontFamily:"var(--font-mono)",fontSize:11,color:"var(--accent)",textAlign:"right",flexShrink:0}}>{row.target}</span>
          </div>
          <div style={{fontFamily:"var(--font-mono)",fontSize:11,color:"var(--muted)",lineHeight:1.55}}>{row.reality}</div>
        </div>
      ))}
      <div style={{fontFamily:"var(--font-mono)",fontSize:9,color:"var(--muted)",lineHeight:1.6,textAlign:"center",marginTop:8,opacity:0.7}}>
        Target ranges are general guidance, not clinical thresholds — useful for spotting trends, not diagnosis.
      </div>
      </>)}
    </div>
  );
}
function BaseRow({ label, val, sub }) {
  return (
    <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"8px 0",borderBottom:"1px solid var(--border)"}}>
      <span style={{fontFamily:"var(--font-mono)",fontSize:13,color:"var(--muted)"}}>{label}</span>
      <div style={{textAlign:"right"}}>
        <div style={{fontFamily:"var(--font-mono)",fontSize:14,color:"var(--text)"}}>{val}</div>
        <div style={{fontFamily:"var(--font-mono)",fontSize:9,color:"var(--muted)"}}>{sub}</div>
      </div>
    </div>
  );
}

// ── SETTINGS ───────────────────────────────────────────────────────────────
function SettingsPage({ data, onSave, onNav }) {
  const [presets, setPresets] = useState(()=>loadPresets());
  const profile = data.profile || {};
  const notifGranted = typeof Notification !== "undefined" && Notification.permission === "granted";

  function toggleShiftType(t) {
    const cur = profile.shiftTypes || [];
    const next = cur.includes(t) ? cur.filter(x=>x!==t) : [...cur, t];
    const nd = {...data, profile:{...profile, shiftTypes:next}};
    saveData(nd); onSave(nd);
  }
  function setReminders(val) {
    const nd = {...data, profile:{...profile, reminders:val}};
    saveData(nd); onSave(nd);
    if (val) requestNotifPermission();
  }
  function updatePresetTime(type, idx, field, value) {
    const next = {...presets, [type]: presets[type].map((p,i)=> i===idx?{...p,[field]:value}:p)};
    setPresets(next); savePresets(next);
  }
  const [confirmReset, setConfirmReset] = useState(false);
  function resetAll() {
    localStorage.removeItem(STORAGE_KEY);
    localStorage.removeItem(PRESETS_KEY);
    const fresh = { shifts:[], checkins:[], cbi:[], roster:{}, profile:null };
    saveData(fresh); onSave(fresh); onNav("home");
  }

  return (
    <div className="ww-page">
      <div style={{display:"flex",alignItems:"center",gap:12,marginBottom:20}}>
        <button onClick={()=>onNav("home")} style={{background:"none",border:"none",color:"var(--muted)",fontSize:20,cursor:"pointer",padding:0}}>←</button>
        <div className="ww-logo" style={{fontSize:22}}>Settings</div>
      </div>

      {/* Profile */}
      <div className="ww-section">Profile</div>
      <div className="ww-card">
        <div style={{display:"flex",justifyContent:"space-between",alignItems:"center"}}>
          <span style={{fontFamily:"var(--font-mono)",fontSize:13,color:"var(--muted)"}}>Shift worker</span>
          <span style={{fontFamily:"var(--font-mono)",fontSize:13,color:profile.isShiftWorker?"var(--accent)":"var(--text)"}}>{profile.isShiftWorker?"Yes":"No"}</span>
        </div>
      </div>

      {profile.isShiftWorker && (<>
        <div className="ww-section">Shift types you work</div>
        <div className="ww-card">
          <div className="ww-chips">
            {Object.keys(SHIFT_CONFIG).map(t=>{
              const on = (profile.shiftTypes||[]).includes(t);
              return <div key={t} className={`ww-chip ${on?"selected":""}`}
                style={on?{borderColor:SHIFT_CONFIG[t].color,background:SHIFT_CONFIG[t].bg,color:SHIFT_CONFIG[t].color}:{}}
                onClick={()=>toggleShiftType(t)}>{t}</div>;
            })}
          </div>
        </div>

        <div className="ww-section">Default shift times</div>
        <div className="ww-card">
          {(profile.shiftTypes||[]).length===0 && <div className="ww-empty">Select a shift type above</div>}
          {(profile.shiftTypes||[]).map(t=>(
            <div key={t} style={{marginBottom:14}}>
              <div style={{fontFamily:"var(--font-mono)",fontSize:11,color:SHIFT_CONFIG[t].color,fontWeight:700,marginBottom:8}}>{t}</div>
              {presets[t].map((p,idx)=>(
                <div key={p.label} style={{display:"flex",alignItems:"center",gap:8,marginBottom:8}}>
                  <span style={{width:34,fontFamily:"var(--font-display)",fontWeight:700,fontSize:14,color:"var(--text)"}}>{p.label}</span>
                  <input type="time" className="ww-input" value={p.start} onChange={e=>updatePresetTime(t,idx,"start",e.target.value)} style={{textAlign:"center",fontSize:14,padding:"9px 6px"}}/>
                  <span style={{color:"var(--muted)"}}>–</span>
                  <input type="time" className="ww-input" value={p.end} onChange={e=>updatePresetTime(t,idx,"end",e.target.value)} style={{textAlign:"center",fontSize:14,padding:"9px 6px"}}/>
                </div>
              ))}
            </div>
          ))}
        </div>

        <div className="ww-section">Reminders</div>
        <div className="ww-card">
          <div style={{display:"flex",justifyContent:"space-between",alignItems:"center"}}>
            <div>
              <div style={{fontFamily:"var(--font-display)",fontWeight:600,fontSize:14,color:"var(--text)"}}>End-of-shift reminders</div>
              <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",marginTop:2}}>
                {notifGranted ? "Notifications enabled" : "Permission not granted yet"}
              </div>
            </div>
            <div onClick={()=>setReminders(!profile.reminders)}
              style={{width:44,height:26,borderRadius:13,background:profile.reminders?"var(--accent)":"var(--border)",position:"relative",transition:"background 0.2s",cursor:"pointer",flexShrink:0}}>
              <div style={{position:"absolute",top:3,left:profile.reminders?21:3,width:20,height:20,borderRadius:"50%",background:"#fff",transition:"left 0.2s"}}/>
            </div>
          </div>
        </div>
      </>)}

      {/* Data */}
      <div className="ww-section">Your data</div>
      <button className="ww-btn-ghost" onClick={()=>onNav("history")} style={{marginBottom:10,textAlign:"left",paddingLeft:16}}>Manage entries (edit / delete) →</button>
      <div style={{display:"flex",gap:10,marginBottom:10}}>
        <button className="ww-btn-ghost" onClick={()=>exportCSV(data)} style={{flex:1}}>Export CSV</button>
        <button className="ww-btn-ghost" onClick={()=>exportJSON(data)} style={{flex:1}}>Export JSON</button>
      </div>
      <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",lineHeight:1.6,marginBottom:16}}>
        CSV opens in Excel/Sheets for analysis. JSON is a full backup you can keep or re-import later.
      </div>

      {/* Danger */}
      <div className="ww-section">Reset</div>
      {!confirmReset ? (
        <button className="ww-btn-ghost" onClick={()=>setConfirmReset(true)} style={{borderColor:"rgba(240,96,128,0.4)",color:"var(--red)"}}>Erase all data & restart setup</button>
      ) : (
        <div className="ww-card" style={{borderColor:"rgba(240,96,128,0.4)"}}>
          <div style={{fontSize:13,color:"var(--text)",lineHeight:1.5,marginBottom:14}}>
            This permanently erases all shifts, check-ins, screens and settings on this device. This cannot be undone.
          </div>
          <div style={{display:"flex",gap:10}}>
            <button className="ww-btn-ghost" style={{flex:1}} onClick={()=>setConfirmReset(false)}>Cancel</button>
            <button className="ww-btn" style={{flex:1,background:"var(--red)",color:"#fff"}} onClick={resetAll}>Yes, erase</button>
          </div>
        </div>
      )}

      <div style={{fontFamily:"var(--font-mono)",fontSize:10,color:"var(--muted)",textAlign:"center",marginTop:24,opacity:0.6}}>WardWatch · all data stored on this device</div>
    </div>
  );
}

// ── ROOT ───────────────────────────────────────────────────────────────────
function WardWatch() {
  const [page, setPage] = useState("home");
  const [data, setData] = useState(()=>loadData());
  const handleSave = useCallback(nd=>setData(nd),[]);

  function completeOnboarding(profile) {
    const nd = {...data, profile};
    saveData(nd);
    setData(nd);
  }

  if (!data.profile) {
    return (
      <div id="ww-root">
        <Onboarding onComplete={completeOnboarding}/>
      </div>
    );
  }

  const navItems = [["home","Home"],["log","Log"],["checkin","Check-in"],["cbi","Screen"],["dashboard","Trends"]];

  return (
    <div id="ww-root">
      {page==="home"     && <HomePage     data={data} onSave={handleSave} onNav={setPage}/>}
      {page==="log"      && <LogShiftPage data={data} onSave={handleSave}/>}
      {page==="checkin"  && <CheckinPage  data={data} onSave={handleSave} onNav={setPage}/>}
      {page==="cbi"      && <CbiPage      data={data} onSave={handleSave}/>}
      {page==="dashboard"&& <DashboardPage data={data}/>}
      {page==="settings" && <SettingsPage  data={data} onSave={handleSave} onNav={setPage}/>}
      {page==="history"  && <HistoryPage   data={data} onSave={handleSave} onNav={setPage}/>}
      {page==="biometrics" && <BiometricsPage data={data} onSave={handleSave} onNav={setPage}/>}
      {page!=="settings" && page!=="history" && page!=="biometrics" && (
      <nav className="ww-nav">
        {navItems.map(([k,l])=>(
          <button key={k} className={`ww-nav-btn ${page===k?"active":""}`} onClick={()=>setPage(k)}>
            {icons[k]}{l}
          </button>
        ))}
      </nav>
      )}
    </div>
  );
}
