// Calendar — month grid + selected-day agenda.
//
// Two kinds of events live here:
//   • synced     — pulled from a connected account's real calendar (Google /
//                  Outlook). Already on the calendar, shown as-is.
//   • detected   — found by Zeranda inside an email. These ask for the user's
//                  OK before they're written onto the real calendar ("pending"),
//                  unless the owning inbox has no calendar access at all, in which
//                  case they simply can't be added yet ("blocked" → reconnect).
//
// On top: KPI cards (Upcoming / Today / Conflicts) + a connected-accounts filter
// so the whole view can be scoped to one inbox. Access is per-account, so the
// filter doubles as the place the access story surfaces.

const KINDS = ['Meeting','Call','Webinar','Appointment','Routine','Reminder'];
const TINT_OPTS = [
  { value:'purple', label:'Violet' }, { value:'gold', label:'Gold' },
  { value:'teal', label:'Teal' }, { value:'blue', label:'Blue' },
  { value:'green', label:'Green' }, { value:'red', label:'Coral' },
];
function t12(h) {
  const hh = Math.floor(h), m = Math.round((h - hh) * 60);
  const ap = hh < 12 ? 'AM' : 'PM', h12 = hh % 12 === 0 ? 12 : hh % 12;
  return `${h12}${m ? ':' + String(m).padStart(2, '0') : ''} ${ap}`;
}
const CAL_TIMES = []; for (let h = 7; h <= 22; h += 0.5) CAL_TIMES.push(h);
const DUR_OPTS = [[0.5,'30 min'],[1,'1 hour'],[1.5,'1.5 hours'],[2,'2 hours'],[3,'3 hours']];

const CAL_MON = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
const srcDate = iso => { const d = new Date(iso); return isNaN(d) ? '' : `${CAL_MON[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`; };

// ---- Month-view date helpers ----
const WEEK_SHORT = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
const WEEK_FULL  = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
const MON_FULL   = ['January','February','March','April','May','June','July','August','September','October','November','December'];
const APP_TODAY  = '2026-06-01';   // the app's "today" — keeps the mock's narrative consistent
const isoFromDate = dt => `${dt.getFullYear()}-${String(dt.getMonth()+1).padStart(2,'0')}-${String(dt.getDate()).padStart(2,'0')}`;
function monthCells(year, month) {            // 6×7 grid of Dates covering the month
  const firstDow = new Date(year, month, 1).getDay();
  const out = [];
  for (let i = 0; i < 42; i++) out.push(new Date(year, month, 1 - firstDow + i));
  return out;
}
function fmtLong(iso) {
  const [y, m, d] = iso.split('-').map(Number);
  const dt = new Date(y, m - 1, d);
  return `${WEEK_FULL[dt.getDay()]}, ${MON_FULL[m - 1]} ${d}`;
}
function shortDay(iso) {
  const [y, m, d] = iso.split('-').map(Number);
  const dt = new Date(y, m - 1, d);
  return `${WEEK_SHORT[dt.getDay()]} ${CAL_MON[m - 1]} ${d}`;
}

// ---- origin / approval / conflict model ----
function eventMailbox(e) { return (e.source && e.source.mailbox) || e.mailbox || ''; }
const isDetected = e => !!(e && e.source);            // found in an email (vs. synced from a real calendar)
const overlaps = (a, b) => a.start < b.end && b.start < a.end;
// conflicts among events that are actually on the calendar (confirmed)
function findConflicts(confirmed) {
  const byD = {};
  confirmed.forEach(e => (byD[e.dateISO] = byD[e.dateISO] || []).push(e));
  const map = {}; let pairs = 0;
  Object.values(byD).forEach(list => {
    for (let i = 0; i < list.length; i++) for (let j = i + 1; j < list.length; j++) {
      if (overlaps(list[i], list[j])) {
        pairs++;
        (map[list[i].id] = map[list[i].id] || []).push(list[j].title);
        (map[list[j].id] = map[list[j].id] || []).push(list[i].title);
      }
    }
  });
  return { map, pairs };
}

// Open a tasteful preview of the exact email an event was detected from.
function openEventSource(ev) {
  const s = ev.source || {};
  const from = s.sender || 'invites@calendar.com';
  const subject = s.subject || ev.title;
  const mailbox = s.mailbox || 'you@gmail.com';
  const received = srcDate(s.receivedISO) || ev.date;
  const initial = (s.from || ev.title || '?').trim()[0].toUpperCase();
  const html = `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    *{box-sizing:border-box} body{margin:0;background:#ECEAE5;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1c1b20;padding:28px}
    .mail{max-width:680px;margin:0 auto;background:#fff;border:1px solid #E4E1DB;border-radius:14px;overflow:hidden;box-shadow:0 18px 48px rgba(40,30,12,.12)}
    .mbar{display:flex;align-items:center;gap:8px;padding:10px 18px;background:#FBF7EE;border-bottom:1px solid #EEE7D6;font-size:12px;color:#8A6529;font-weight:600}
    .mbar .dot{width:7px;height:7px;border-radius:50%;background:#C19A4B}
    .hd{display:flex;gap:14px;padding:22px 24px 16px;border-bottom:1px solid #EFEDE8}
    .av{width:46px;height:46px;border-radius:50%;flex:none;display:grid;place-items:center;background:#7C5BD6;color:#fff;font-weight:700;font-size:19px}
    .subj{font-size:18px;font-weight:700;line-height:1.3;margin-bottom:4px}
    .from{font-size:13px;color:#5d5b63}.from b{color:#1c1b20}
    .when{font-size:12px;color:#9A98A0;margin-top:3px}
    .bd{padding:20px 24px;font-size:14px;line-height:1.6;color:#37353c}
    .bd p{margin:0 0 12px}
    .cta{display:inline-block;margin:4px 0 4px;padding:9px 16px;background:#7C5BD6;color:#fff;border-radius:9px;font-weight:600;font-size:13px;text-decoration:none}
    .ics{display:flex;align-items:center;gap:10px;margin-top:14px;padding:12px 14px;border:1px solid #E6E3F4;background:#F6F4FD;border-radius:10px;font-size:13px}
    .ics .ic{width:30px;height:30px;border-radius:7px;background:#fff;border:1px solid #E6E3F4;display:grid;place-items:center;color:#7C5BD6;font-weight:700;font-size:12px}
    .ft{padding:14px 24px;border-top:1px solid #EFEDE8;font-size:11.5px;color:#ACACB1}
  </style></head><body>
    <div class="mail">
      <div class="mbar"><span class="dot"></span> Detected by Zeranda \u00b7 from ${mailbox}</div>
      <div class="hd"><div class="av">${initial}</div>
        <div><div class="subj">${subject}</div>
          <div class="from"><b>${s.from || ev.title}</b> &lt;${from}&gt;</div>
          <div class="from">to ${mailbox}</div>
          <div class="when">${received}</div>
        </div></div>
      <div class="bd">
        <p>Hi there,</p>
        <p>This is to confirm <b>${ev.title}</b>${ev.location ? ` at <b>${ev.location}</b>` : ''} on <b>${ev.date}</b>, ${t12(ev.start)}\u2013${t12(ev.end)}.</p>
        <a class="cta" href="#">Add to calendar</a>
        <div class="ics"><span class="ic">ICS</span> ${ev.title}.ics \u00b7 calendar invite</div>
      </div>
      <div class="ft">Source email preview \u00b7 Zeranda detected this event from your inbox.</div>
    </div>
  </body></html>`;
  const w = window.open('', '_blank');
  if (w) { w.document.write(html); w.document.close(); }
}

// ---- Event detail / edit drawer ----
function EventDrawer({ ev, editing, creating, state, owner, clash, onClose, onEdit, onSave, onDelete, onApprove, onDismiss, onReconnect }) {
  const [form, setForm] = useState(ev);
  useEffect(() => { setForm(ev); }, [ev]);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const dur = (form.end ?? form.start + 1) - form.start;
  const isForm = editing || creating;
  const ownerLabel = owner ? (owner.nickname || owner.email) : '';

  const save = () => {
    const start = Number(form.start);
    onSave({ ...form, start, end: start + Number(form.dur ?? dur ?? 1) });
  };

  // header sub-label + provenance tag vary by state
  const tag = !isForm && isDetected(form) && (
    state === 'pending' ? <span className="via-mail sug"><Icon name="sparkles" size={11} /> Detected — review</span>
    : state === 'blocked' ? <span className="via-mail off"><Icon name="calendar-off" size={11} /> In Zeranda only</span>
    : <span className="via-mail"><Icon name="check" size={11} /> On your calendar</span>
  );

  return (
    <React.Fragment>
      <div className="scrim" onClick={onClose} />
      <div className="drawer zd-scroll">
        <div className="dhead">
          <IconChip icon="calendar" tint={form.tint || 'purple'} size={40} radius={12} />
          <div style={{ flex:1, minWidth:0 }}>
            <div style={{ fontFamily:'var(--font-display)', fontWeight:700, fontSize:18, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{form.title || (creating ? 'New event' : '')}</div>
            <div style={{ fontSize:12.5, color:'var(--fg-3)', display:'flex', alignItems:'center', gap:8, flexWrap:'wrap' }}>
              <span>{creating ? 'Add to your calendar' : editing ? 'Editing event' : form.kind}</span>
              {tag}
            </div>
          </div>
          <button className="iconbtn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>

        {!isForm ? (
          /* ---------- VIEW ---------- */
          <div className="dbody">
            {/* approval / access / conflict banners */}
            {state === 'pending' && (
              <div className="ev-approve">
                <div className="ev-approve-h"><Icon name="sparkles" size={15} color="var(--gold-700)" /> Detected from your email</div>
                <div className="ev-approve-b">Zeranda found this{ownerLabel ? <> in <b>{ownerLabel}</b></> : ''} and hasn’t added it to your calendar yet. Add it to get reminders and conflict checks — or dismiss if it isn’t a real event.</div>
                <div className="ev-approve-acts">
                  <button className="btn btn-primary" onClick={onApprove}><Icon name="calendar-plus" size={15} /> Add to calendar</button>
                  <button className="btn btn-ghost" onClick={onDismiss}><Icon name="x" size={15} /> Dismiss</button>
                </div>
              </div>
            )}
            {state === 'blocked' && (
              <AccessNotice variant="panel" icon="calendar-off"
                title={`Calendar access is off for ${owner ? owner.email : 'this inbox'}`}
                ctaLabel="Reconnect to add" onCta={onReconnect}>
                Zeranda found this event in your email, but can’t place it on your calendar until {owner ? owner.email : 'this inbox'} is reconnected with calendar access. It stays here so you don’t lose it.
              </AccessNotice>
            )}
            {state === 'confirmed' && clash && (
              <div className="ev-clash-note"><Icon name="alert-triangle" size={15} color="var(--red-600)" /> <span>Overlaps <b>{clash}</b> — you may be double-booked.</span></div>
            )}

            <div className="ev-chip"><Icon name="calendar" size={16} color="var(--fg-3)" /> {form.date}</div>
            <div className="ev-chip"><Icon name="clock" size={16} color="var(--fg-3)" /> {t12(form.start)} – {t12(form.end)}</div>
            {form.location && <div className="ev-chip"><Icon name="map-pin" size={16} color="var(--fg-3)" /> {form.location}</div>}
            <div className="field" style={{ marginTop:6 }}><div className="k">Type</div>
              <div className="v"><span className="badge" style={{ background:tintBg(form.tint), color:tintFg(form.tint) }}>{form.kind}</span></div>
            </div>
            {owner && (
              <div className="field"><div className="k">{isDetected(form) ? 'Source inbox' : 'Calendar'}</div>
                <div className="v"><MailboxBadge addr={owner.email} variant="tag" /></div>
              </div>
            )}
            {form.notes && <div className="field"><div className="k">Notes</div><div className="v" style={{ fontSize:14, lineHeight:1.55, color:'var(--fg-2)' }}>{form.notes}</div></div>}
            {form.source && (
              <div className="field"><div className="k">Source email</div>
                <button className="src-card" onClick={() => openEventSource(form)} title="Open the email this event was detected from">
                  <span className="src-ic"><Icon name="mail" size={17} color="var(--gold-600)" /></span>
                  <div className="src-body">
                    <div className="src-subj">{form.source.subject || form.title}</div>
                    <div className="src-from">{form.source.sender}</div>
                    <div className="src-meta"><span className="src-mbx"><Icon name="inbox" size={12} /> {form.source.mailbox}</span><span className="src-sep">·</span>{srcDate(form.source.receivedISO)}</div>
                  </div>
                  <Icon name="external-link" size={15} color="var(--fg-4)" />
                </button>
              </div>
            )}
            <div className="drawer-foot">
              {state === 'confirmed' ? (
                <React.Fragment>
                  <Btn variant="primary" icon="pencil" style={{ flex:1, justifyContent:'center' }} onClick={onEdit}>Edit event</Btn>
                  <Btn variant="secondary" icon="bell">Remind</Btn>
                  <button className="iconbtn danger" onClick={onDelete} title="Delete"><Icon name="trash-2" size={17} /></button>
                </React.Fragment>
              ) : (
                <React.Fragment>
                  <Btn variant="secondary" icon="pencil" style={{ flex:1, justifyContent:'center' }} onClick={onEdit}>Edit details</Btn>
                  <button className="iconbtn danger" onClick={onDelete} title="Remove"><Icon name="trash-2" size={17} /></button>
                </React.Fragment>
              )}
            </div>
          </div>
        ) : (
          /* ---------- EDIT / CREATE ---------- */
          <div className="dbody">
            <label className="fl">Title</label>
            <div className="finput"><Icon name="type" size={16} color="var(--fg-4)" /><input value={form.title || ''} onChange={e => set('title', e.target.value)} placeholder="Event title" /></div>

            <label className="fl">Type</label>
            <Dropdown value={form.kind} options={KINDS.map(k => ({ value:k, label:k }))} onChange={v => set('kind', v)} minWidth={'100%'} icon="tag" />

            <div className="drow">
              <div style={{ flex:1 }}>
                <label className="fl">Start</label>
                <Dropdown value={Number(form.start)} options={CAL_TIMES.map(h => ({ value:h, label:t12(h) }))} onChange={v => set('start', v)} minWidth={'100%'} icon="clock" />
              </div>
              <div style={{ flex:1 }}>
                <label className="fl">Duration</label>
                <Dropdown value={form.dur ?? dur} options={DUR_OPTS.map(([v,l]) => ({ value:v, label:l }))} onChange={v => set('dur', v)} minWidth={'100%'} />
              </div>
            </div>

            <label className="fl">Location</label>
            <div className="finput"><Icon name="map-pin" size={16} color="var(--fg-4)" /><input value={form.location || ''} onChange={e => set('location', e.target.value)} placeholder="Add a place or link" /></div>

            <label className="fl">Color</label>
            <div className="seg-full" style={{ flexWrap:'wrap' }}>
              {TINT_OPTS.map(o => (
                <button key={o.value} className={form.tint === o.value ? 'on' : ''} onClick={() => set('tint', o.value)}>
                  <span className="dd-dot" style={{ background: tintFg(o.value) }} />{o.label}
                </button>
              ))}
            </div>

            <label className="fl">Notes</label>
            <textarea className="fld" value={form.notes || ''} onChange={e => set('notes', e.target.value)} placeholder="Optional details" />

            <div className="drawer-foot">
              <Btn variant="ghost" onClick={onClose}>Cancel</Btn>
              <Btn variant="primary" icon="check" style={{ flex:1, justifyContent:'center' }} onClick={save}>{creating ? 'Add event' : 'Save changes'}</Btn>
            </div>
          </div>
        )}
      </div>
    </React.Fragment>
  );
}

// ---- Review row: one email-detected suggestion awaiting the user's OK ----
function SuggestionRow({ ev, owner, clash, onAdd, onDismiss, onView, onOpen }) {
  const [exit, setExit] = useState(null);   // 'added' | 'dismissed'
  const m = mailboxMeta(eventMailbox(ev));
  const go = (kind, fn) => { setExit(kind); setTimeout(fn, 360); };
  return (
    <div className={`crv-row ${exit ? 'exit ' + exit : ''}`}>
      <span className="crv-bar" style={{ background: tintFg(ev.tint) }} />
      <button className="crv-when" onClick={onOpen} title="Open event">
        <span className="crv-d">{shortDay(ev.dateISO)}</span>
        <span className="crv-t">{t12(ev.start)}</span>
      </button>
      <div className="crv-body" onClick={onOpen} style={{ cursor:'pointer' }}>
        <div className="crv-ev-title">{ev.title}
          {clash && <span className="ev-tag clash"><Icon name="alert-triangle" size={11} /> Overlaps {clash}</span>}
        </div>
        <div className="crv-src-row">
          <span className="crv-src"><Icon name="mail" size={12} color="var(--gold-700)" /> {ev.source.sender}</span>
          <span className="crv-mbx"><span className="mbx-dot" style={{ background: tintFg(m.tint) }} />{owner ? (owner.nickname || m.label) : m.label}</span>
          <button className="link-btn crv-view" onClick={(e) => { e.stopPropagation(); onView(); }}>View email</button>
        </div>
      </div>
      {exit ? (
        <div className={`crv-ack ${exit}`}>
          <Icon name={exit === 'added' ? 'check' : 'x'} size={15} color={exit === 'added' ? 'var(--green-600)' : 'var(--fg-3)'} />
          {exit === 'added' ? 'Added' : 'Dismissed'}
        </div>
      ) : (
        <div className="crv-acts">
          <button className="btn btn-primary crv-add" onClick={() => go('added', onAdd)} title="Add to your calendar"><Icon name="calendar-plus" size={15} /> Add</button>
          <button className="crv-dismiss" onClick={() => go('dismissed', onDismiss)} title="Dismiss this suggestion"><Icon name="x" size={15} /></button>
        </div>
      )}
    </div>
  );
}

function Calendar() {
  const D = window.ZD_DATA;
  const perm = usePermissions();
  const accounts = perm.accounts().filter(a => a.status === 'connected');
  const acctByEmail = {}; accounts.forEach(a => { acctByEmail[a.email] = a; });
  const ownerOf = e => acctByEmail[eventMailbox(e)] || null;
  const acctHasCal = a => !!a && perm.granted(a).includes('calendar');

  const [events, setEvents] = useState(D.events);
  const [approved, setApproved] = useState(() => new Set());     // detected events the user added this session
  const [dismissed, setDismissed] = useState(() => new Set());   // suggestions the user dismissed
  const [scope, setScope] = useState('all');                     // 'all' | account.id
  const [selectedISO, setSelectedISO] = useState(APP_TODAY);
  const [cursor, setCursor] = useState(() => { const [y, m] = APP_TODAY.split('-').map(Number); return { y, m: m - 1 }; });
  const [sel, setSel] = useState(null);          // event id (drawer)
  const [editing, setEditing] = useState(false);
  const [creating, setCreating] = useState(false);
  const [reconnectAcct, setReconnectAcct] = useState(null);

  // derive each event's lifecycle state
  const stateOf = e => {
    if (!e || e.id === 'new') return 'confirmed';
    if (dismissed.has(e.id)) return 'dismissed';
    if (!isDetected(e)) return 'confirmed';              // synced from a real calendar
    if (!acctHasCal(ownerOf(e))) return 'blocked';       // inbox has no calendar access
    if (e.needsApproval && !approved.has(e.id)) return 'pending';
    return 'confirmed';
  };
  const inScope = e => scope === 'all' || (ownerOf(e) && ownerOf(e).id === scope);

  const realEvents = events.filter(e => e.id !== 'new' && e.dateISO);
  const live = realEvents.filter(e => stateOf(e) !== 'dismissed' && inScope(e));

  // per-account counts for the filter bar (all live states)
  const counts = { all: realEvents.filter(e => stateOf(e) !== 'dismissed').length };
  accounts.forEach(a => {
    counts[a.id] = realEvents.filter(e => stateOf(e) !== 'dismissed' && ownerOf(e) && ownerOf(e).id === a.id).length;
  });

  const confirmed = live.filter(e => stateOf(e) === 'confirmed');
  const { map: clashMap, pairs: conflictPairs } = findConflicts(confirmed);

  // KPIs
  const todayCount = confirmed.filter(e => e.dateISO === APP_TODAY).length;
  const upcoming = confirmed.filter(e => e.dateISO > APP_TODAY)
    .sort((a, b) => a.dateISO < b.dateISO ? -1 : a.dateISO > b.dateISO ? 1 : a.start - b.start);
  const nextEv = upcoming[0];

  // suggestions awaiting approval (respecting the account filter)
  const pending = live.filter(e => stateOf(e) === 'pending')
    .sort((a, b) => a.dateISO < b.dateISO ? -1 : a.dateISO > b.dateISO ? 1 : a.start - b.start);
  const clashAgainstConfirmed = e => {
    const hit = confirmed.find(c => c.dateISO === e.dateISO && overlaps(c, e));
    return hit ? hit.title : null;
  };

  // group live events by ISO date → month dots + agenda
  const byDate = {};
  live.forEach(e => { (byDate[e.dateISO] = byDate[e.dateISO] || []).push(e); });
  const dayEvents = (byDate[selectedISO] || []).slice().sort((a, b) => a.start - b.start);
  const dayBlocked = dayEvents.filter(e => stateOf(e) === 'blocked');
  const cells = monthCells(cursor.y, cursor.m);
  const monthPrefix = `${cursor.y}-${String(cursor.m + 1).padStart(2, '0')}`;
  const monthCount = live.filter(e => e.dateISO.startsWith(monthPrefix)).length;
  const isToday = selectedISO === APP_TODAY;

  // access banner context
  const filterAcct = scope === 'all' ? null : accounts.find(a => a.id === scope);
  const offAccounts = accounts.filter(a => !acctHasCal(a));

  const selEvent = sel && events.find(e => e.id === sel);
  const selState = selEvent ? stateOf(selEvent) : 'confirmed';
  const openEvent = id => { setSel(id); setEditing(false); setCreating(false); };
  const closeDrawer = () => { setEvents(es => es.filter(e => e.id !== 'new')); setSel(null); setEditing(false); setCreating(false); };

  const approve = id => { setApproved(s => { const n = new Set(s); n.add(id); return n; }); };
  const dismiss = id => { setDismissed(s => { const n = new Set(s); n.add(id); return n; }); if (sel === id) closeDrawer(); };

  const create = () => {
    const [y, m, d] = selectedISO.split('-').map(Number);
    const dateStr = `${WEEK_SHORT[new Date(y, m - 1, d).getDay()]}, ${CAL_MON[m - 1]} ${d}, ${y}`;
    const scopeAcct = filterAcct || accounts.find(a => acctHasCal(a)) || accounts[0];
    setSel('new'); setCreating(true); setEditing(false);
    setEvents(es => es.some(e => e.id === 'new') ? es : [...es, { id:'new', title:'', kind:'Meeting', start:18, end:19, dur:1, tint:'purple', location:'', date:dateStr, dateISO:selectedISO, notes:'', mailbox: scopeAcct ? scopeAcct.email : undefined }]);
  };
  useCreateShortcut(create);

  const saveEvent = upd => {
    if (creating) {
      const id = 'e' + Date.now();
      setEvents(es => es.filter(e => e.id !== 'new').concat({ ...upd, id }));
      setSel(id);
    } else {
      setEvents(es => es.map(e => e.id === upd.id ? upd : e));
    }
    setEditing(false); setCreating(false);
  };
  const delEvent = id => { setEvents(es => es.filter(e => e.id !== id)); closeDrawer(); };

  const shiftMonth = delta => setCursor(c => { const nm = new Date(c.y, c.m + delta, 1); return { y: nm.getFullYear(), m: nm.getMonth() }; });
  const goToday = () => { const [y, m] = APP_TODAY.split('-').map(Number); setCursor({ y, m: m - 1 }); setSelectedISO(APP_TODAY); };
  const selectDay = dt => { setSelectedISO(isoFromDate(dt)); if (dt.getMonth() !== cursor.m || dt.getFullYear() !== cursor.y) setCursor({ y: dt.getFullYear(), m: dt.getMonth() }); };

  return (
    <div className="screen-inner">
      {/* ---- access banner (account-aware) ---- */}
      {filterAcct && !acctHasCal(filterAcct) && (
        <AccessNotice variant="banner" icon="calendar-off"
          title={`Calendar sync is off for ${filterAcct.nickname || filterAcct.email}`}
          ctaLabel="Reconnect calendar" onCta={() => setReconnectAcct(filterAcct)}>
          Events Zeranda finds in {filterAcct.email} are kept here, but aren’t written to your calendar and aren’t checked for conflicts. Reconnect with calendar access to turn that on — your existing access stays exactly as it is.
        </AccessNotice>
      )}
      {!filterAcct && offAccounts.length > 0 && (
        <AccessNotice variant="banner" icon="calendar-cog"
          title={offAccounts.length === 1 ? `1 inbox isn’t adding events to your calendar` : `${offAccounts.length} inboxes aren’t adding events to your calendar`}
          ctaLabel="Reconnect" onCta={() => setReconnectAcct(offAccounts[0])}
          secondaryLabel={offAccounts.length === 1 ? 'View that inbox' : null}
          onSecondary={offAccounts.length === 1 ? () => setScope(offAccounts[0].id) : null}>
          {offAccounts.map(a => a.nickname || a.email).join(', ')} {offAccounts.length === 1 ? 'is' : 'are'} connected and syncing, but {offAccounts.length === 1 ? "doesn’t" : "don’t"} have calendar access yet — so detected events stay in Zeranda instead of landing on your calendar. Everything else keeps working.
        </AccessNotice>
      )}

      {/* ---- KPIs ---- */}
      <div className="statrow">
        <StatCard label="Upcoming" value={upcoming.length} sub={nextEv ? `Next · ${shortDay(nextEv.dateISO)}` : 'Nothing ahead'} icon="calendar-clock" tint="purple" />
        <StatCard label="Today" value={todayCount} sub={todayCount ? 'On your calendar' : 'Clear day'} icon="calendar-days" tint="gold" />
        <StatCard label="Conflicts" value={conflictPairs} sub={conflictPairs ? `${conflictPairs} overlap${conflictPairs > 1 ? 's' : ''} to check` : 'No clashes'} icon={conflictPairs ? 'alert-triangle' : 'check-circle-2'} tint={conflictPairs ? 'red' : 'green'} />
      </div>

      {/* ---- account filter ---- */}
      <div className="cal-scope">
        <span className="cal-scope-lbl"><Icon name="filter" size={14} color="var(--fg-3)" /> Showing</span>
        <AccountFilterBar accounts={accounts} value={scope} onChange={setScope} counts={counts}
          warnScope="calendar" warnTitle="Calendar sync is off for this inbox — reconnect to add its events" />
      </div>

      {/* ---- suggestions awaiting approval ---- */}
      {pending.length > 0 && (
        <div className="card crv rise">
          <div className="crv-head">
            <span className="crv-ic"><Icon name="sparkles" size={18} color="var(--gold-700)" /></span>
            <div className="crv-h-meta">
              <div className="crv-title">Detected from email · review</div>
              <div className="crv-sub">Zeranda found {pending.length} event{pending.length === 1 ? '' : 's'} in your inbox. Add the ones you want on your calendar.</div>
            </div>
            <span className="crv-count">{pending.length}</span>
          </div>
          <div className="crv-list">
            {pending.map(e => (
              <SuggestionRow key={e.id} ev={e} owner={ownerOf(e)} clash={clashAgainstConfirmed(e)}
                onAdd={() => approve(e.id)} onDismiss={() => dismiss(e.id)}
                onView={() => openEventSource(e)} onOpen={() => { const [y, m, d] = e.dateISO.split('-').map(Number); selectDay(new Date(y, m - 1, d)); openEvent(e.id); }} />
            ))}
          </div>
        </div>
      )}

      <div className="card card-pad rise">
        <div className="cm-top">
          <div className="cm-nav">
            <button className="iconbtn" onClick={() => shiftMonth(-1)} title="Previous month"><Icon name="chevron-left" size={18} /></button>
            <div className="cm-month">{MON_FULL[cursor.m]} {cursor.y}</div>
            <button className="iconbtn" onClick={() => shiftMonth(1)} title="Next month"><Icon name="chevron-right" size={18} /></button>
          </div>
          <Btn variant="secondary" onClick={goToday}>Today</Btn>
          <div style={{ flex:1 }} />
          <Btn variant="primary" icon="plus" kbd="C" title="Create event — press C" onClick={create}>New event</Btn>
        </div>

        <div className="cm-wrap">
          {/* ---- left: month grid ---- */}
          <div className="cm-cal">
            <div className="cm-dow-row">{WEEK_SHORT.map(w => <div className="cm-dow" key={w}>{w}</div>)}</div>
            <div className="cm-grid">
              {cells.map(dt => {
                const iso = isoFromDate(dt);
                const inMonth = dt.getMonth() === cursor.m;
                const dts = byDate[iso] || [];
                const cls = ['cm-cell'];
                if (!inMonth) cls.push('out');
                if (iso === selectedISO) cls.push('sel');
                if (iso === APP_TODAY) cls.push('today');
                if (dts.length) cls.push('has');
                return (
                  <button key={iso} className={cls.join(' ')} onClick={() => selectDay(dt)}
                    title={dts.length ? `${dts.length} event${dts.length === 1 ? '' : 's'}` : undefined}>
                    <span className="cm-num">{dt.getDate()}</span>
                    {dts.length > 0 && (
                      <span className="cm-dots">
                        {dts.slice(0, 3).map((e, i) => {
                          const st = stateOf(e);
                          const c = tintFg(e.tint);
                          return <span key={i} className={`cm-dot ${st === 'pending' ? 'sug' : ''} ${clashMap[e.id] ? 'clash' : ''}`}
                            style={st === 'pending' ? { background:'transparent', boxShadow:`inset 0 0 0 1.5px ${c}` } : { background: c }} />;
                        })}
                        {dts.length > 3 && <span className="cm-more">+{dts.length - 3}</span>}
                      </span>
                    )}
                  </button>
                );
              })}
            </div>
            <div className="cm-foot">
              <span className="cm-foot-k"><span className="cm-foot-dot" /> On calendar</span>
              <span className="cm-foot-k"><span className="cm-foot-ring" /> Suggested</span>
              <span>{monthCount} this month</span>
            </div>
          </div>

          {/* ---- right: selected-day agenda ---- */}
          <div className="cm-agenda">
            <div className="cm-a-head">
              <div>
                <div className="cm-a-date">{fmtLong(selectedISO)}{isToday && <span className="cm-a-today">Today</span>}</div>
                <div className="cm-a-sub">{dayEvents.length ? `${dayEvents.length} event${dayEvents.length === 1 ? '' : 's'}${filterAcct ? ` · ${filterAcct.nickname || filterAcct.email}` : ''}` : 'No events scheduled'}</div>
              </div>
              <Btn variant="secondary" icon="plus" onClick={create}>Add</Btn>
            </div>

            {dayBlocked.length > 0 && (
              <div className="cal-detected-note">
                <Icon name="calendar-off" size={14} color="var(--gold-700)" />
                {dayBlocked.length} event{dayBlocked.length === 1 ? '' : 's'} here {dayBlocked.length === 1 ? "isn’t" : "aren’t"} on your calendar — {(ownerOf(dayBlocked[0]) || {}).email || 'that inbox'} needs calendar access.
                <button className="link-btn" onClick={() => setReconnectAcct(ownerOf(dayBlocked[0]))}>Reconnect</button>
              </div>
            )}

            <div className="cm-a-list zd-scroll">
              {dayEvents.length === 0 ? (
                <div className="cm-a-empty">
                  <span className="cm-a-empty-ic"><Icon name="calendar-check" size={26} color="var(--green-500)" /></span>
                  <div className="cm-a-empty-t">Nothing scheduled</div>
                  <div className="cm-a-empty-s">{isToday ? 'Your day is clear.' : 'No events on this day.'}</div>
                  <button className="link-btn" onClick={create}><Icon name="plus" size={14} /> Add an event</button>
                </div>
              ) : dayEvents.map(e => {
                const st = stateOf(e);
                const cl = clashMap[e.id];
                return (
                  <button className={`cm-ev ${st !== 'confirmed' ? st : ''} ${cl ? 'clash' : ''}`} key={e.id} onClick={() => openEvent(e.id)}>
                    <span className="cm-ev-bar" style={{ background: tintFg(e.tint) }} />
                    <div className="cm-ev-time"><span className="t1">{t12(e.start)}</span><span className="t2">{t12(e.end)}</span></div>
                    <div className="cm-ev-body">
                      <div className="cm-ev-title">{e.title}</div>
                      <div className="cm-ev-meta">
                        <span className="badge" style={{ background: tintBg(e.tint), color: tintFg(e.tint) }}>{e.kind}</span>
                        {e.location && <span className="cm-ev-loc"><Icon name="map-pin" size={12} /> {e.location}</span>}
                        {isDetected(e) && <MailboxBadge addr={eventMailbox(e)} variant="mini" />}
                        {st === 'pending' && <span className="ev-tag sug"><Icon name="sparkles" size={11} /> Suggested</span>}
                        {st === 'blocked' && <span className="ev-tag off"><Icon name="calendar-off" size={11} /> Not synced</span>}
                        {cl && <span className="ev-tag clash"><Icon name="alert-triangle" size={11} /> Conflict</span>}
                      </div>
                    </div>
                    <Icon name="chevron-right" size={16} color="var(--fg-4)" />
                  </button>
                );
              })}
            </div>
          </div>
        </div>
      </div>

      {(selEvent || creating) && <EventDrawer ev={creating ? (events.find(e => e.id === 'new') || {}) : selEvent}
        editing={editing} creating={creating} state={selState} owner={selEvent ? ownerOf(selEvent) : null}
        clash={selEvent && clashMap[selEvent.id] ? clashMap[selEvent.id].join(', ') : null}
        onClose={closeDrawer} onEdit={() => setEditing(true)} onSave={saveEvent} onDelete={() => delEvent(sel)}
        onApprove={() => { approve(sel); }} onDismiss={() => dismiss(sel)}
        onReconnect={() => selEvent && setReconnectAcct(ownerOf(selEvent))} />}

      {reconnectAcct && <ReconnectModal account={reconnectAcct} highlight={['calendar']} reason="so Zeranda can add events to your calendar"
        onClose={() => setReconnectAcct(null)} onDone={() => setReconnectAcct(null)} />}
    </div>
  );
}
window.Calendar = Calendar;
