// ChecklistScreen.jsx — iPad view of today's Daily Checklist.
//
// Reads checklist_groups + checklist_items from the window globals
// (hydrated by dataBootstrap), filters groups to today's weekday, and
// shows each group's items as tappable rows. Tapping a row toggles a
// local "pending" check. Once one or more are pending, a sticky bottom
// CTA lets the crew tap "Complete (N)" → opens the single-staff picker
// → on confirm, inserts checklist_completions for each item attributed
// to that staff. Already-completed items show a green check + the
// staff's avatar; tapping uncompletes (deletes the row).

const { useState: useChk, useEffect: useChkEffect, useMemo: useChkMemo, useRef: useChkRef } = React;

const CHECK_PALETTE = {
  pending:  { bg: '#FEF3C7', border: '#F59E0B', tick: '#92400E' },
  done:     { bg: '#2D6A2A', border: '#2D6A2A', tick: '#FFFFFF' },
};

const ChecklistScreen = ({ currentStaff, showToast, openReminderForGroup }) => {
  // Today's local date, recomputed every minute so the page rolls over
  // at midnight without a manual reload. Matches the PrepScreen pattern.
  const [today, setToday] = useChk(() => new Date());
  useChkEffect(() => {
    const t = setInterval(() => setToday(new Date()), 30000);
    return () => clearInterval(t);
  }, []);
  const dateKey = isoLocalDate(today);
  const dow = today.getDay(); // 0=Sun

  // Template (from dataBootstrap; admin edits propagate via realtime).
  // Wall-clock minutes-since-midnight, used to gate which groups appear.
  // Only groups whose triggerTime has come (now >= triggerTime) render
  // — earlier-in-the-day groups stay hidden until their time arrives.
  const nowMin = today.getHours() * 60 + today.getMinutes();
  const todayGroups = useChkMemo(() => {
    return (window.SAMPLE_CHECKLIST_GROUPS || [])
      .filter(g => !g.archived)
      .filter(g => (g.weekdays || []).includes(dow))
      .slice()
      .sort((a, b) => a.triggerTime.localeCompare(b.triggerTime));
  }, [dow]);
  const groups = useChkMemo(() => {
    return todayGroups.filter(g => triggerMinFor(g) <= nowMin);
  }, [todayGroups, nowMin]);
  // The very next group to fire, used in the "Nothing due yet" empty state.
  const nextUpcoming = useChkMemo(() => {
    return todayGroups.find(g => triggerMinFor(g) > nowMin) || null;
  }, [todayGroups, nowMin]);
  const items = useChkMemo(
    () => (window.SAMPLE_CHECKLIST_ITEMS || []).filter(i => !i.archived),
    []
  );
  const itemsByGroup = useChkMemo(() => {
    const m = {};
    items.forEach(i => { (m[i.groupId] = m[i.groupId] || []).push(i); });
    Object.values(m).forEach(arr => arr.sort((a, b) => a.sortOrder - b.sortOrder));
    return m;
  }, [items]);

  // Today's completions: { itemId: { id, staffId, completedAt } }
  const [completions, setCompletions] = useChk({});
  useChkEffect(() => {
    if (!window.supa) return;
    let cancelled = false;
    window.supa.from('checklist_completions').select('*')
      .eq('restaurant_id', window.RESTAURANT_ID)
      .eq('the_date', dateKey)
      .then(({ data }) => {
        if (cancelled || !data) return;
        const m = {};
        data.forEach(c => {
          m[c.item_id] = { id: c.id, staffId: c.staff_id, completedAt: c.completed_at };
        });
        setCompletions(m);
      });
    const ch = window.supa
      .channel('ipad-checklist-completions:' + window.RESTAURANT_ID + ':' + dateKey)
      .on('postgres_changes', {
        event: '*', schema: 'public', table: 'checklist_completions',
        filter: 'restaurant_id=eq.' + window.RESTAURANT_ID,
      }, (payload) => {
        const row = payload.new || payload.old;
        if (!row || row.the_date !== dateKey) return;
        setCompletions(prev => {
          const next = { ...prev };
          if (payload.eventType === 'DELETE') delete next[row.item_id];
          else next[row.item_id] = { id: row.id, staffId: row.staff_id, completedAt: row.completed_at };
          return next;
        });
      })
      .subscribe();
    return () => { cancelled = true; window.supa.removeChannel(ch); };
  }, [dateKey]);

  // Local "pending check" state — items the user has tapped but hasn't
  // confirmed yet (Complete button hasn't fired). Set of item ids.
  const [pending, setPending] = useChk(() => new Set());
  const togglePending = (itemId) => setPending(prev => {
    const next = new Set(prev);
    if (next.has(itemId)) next.delete(itemId); else next.add(itemId);
    return next;
  });

  // Uncheck an already-completed item.
  const uncomplete = async (itemId) => {
    const c = completions[itemId];
    if (!c) return;
    setCompletions(prev => { const n = { ...prev }; delete n[itemId]; return n; });
    if (window.supa) {
      const { error } = await window.supa.from('checklist_completions')
        .delete().eq('id', c.id);
      if (error) console.error('checklist_completions delete failed', error);
    }
  };

  // Staff picker visibility + handler.
  const [pickerOpen, setPickerOpen] = useChk(false);
  const openComplete = () => {
    if (pending.size === 0) return;
    setPickerOpen(true);
  };

  const confirmCompletion = async (staff) => {
    setPickerOpen(false);
    const ids = Array.from(pending);
    if (ids.length === 0 || !staff) return;
    // Optimistic: drop them into completions immediately.
    const stamp = new Date().toISOString();
    setCompletions(prev => {
      const next = { ...prev };
      ids.forEach(itemId => {
        next[itemId] = { id: 'tmp-' + itemId, staffId: staff.id, completedAt: stamp };
      });
      return next;
    });
    setPending(new Set());

    if (!window.supa) return;
    const rows = ids.map(itemId => ({
      restaurant_id: window.RESTAURANT_ID,
      the_date: dateKey,
      item_id: itemId,
      staff_id: staff.id,
      completed_at: stamp,
    }));
    const { error } = await window.supa.from('checklist_completions')
      .upsert(rows, { onConflict: 'restaurant_id,the_date,item_id' });
    if (error) {
      console.error('checklist_completions upsert failed', error);
      showToast && showToast({ message: 'Failed to save — try again', icon: 'x' });
      // Roll back optimistic insert; realtime won't fire because no row was written.
      setCompletions(prev => {
        const next = { ...prev };
        ids.forEach(itemId => { delete next[itemId]; });
        return next;
      });
    } else {
      // No success toast — the full-screen celebration (confetti +
      // hype phrases) already confirms the save, and the items
      // disappear from the pending list. Stacking a toast on top
      // created a redundant pill that visually overlapped the bottom
      // tab bar while the celebration was still playing. Error toasts
      // still fire above on the failure branch — those are meaningful.
      window.celebrateCompletion && window.celebrateCompletion();
    }
  };

  // Auto-clear pending checks when their backing items get completed from
  // another iPad (or the same one via realtime echo).
  useChkEffect(() => {
    setPending(prev => {
      let changed = false;
      const next = new Set(prev);
      next.forEach(id => {
        if (completions[id] && !String(completions[id].id).startsWith('tmp-')) {
          next.delete(id); changed = true;
        }
      });
      return changed ? next : prev;
    });
  }, [completions]);

  // Totals for the header.
  const totalItems = groups.reduce((a, g) => a + (itemsByGroup[g.id]?.length || 0), 0);
  const doneItems = groups.reduce((a, g) => a + (itemsByGroup[g.id] || []).filter(i => completions[i.id]).length, 0);

  return (
    <div style={{
      height: '100%', display: 'flex', flexDirection: 'column',
      background: 'var(--bg-page)', position: 'relative',
    }}>
      {/* Header — same shape as Prep / Delivery / Temp Log. */}
      <div style={{
        padding: '16px 24px',
        background: 'var(--bg-surface)',
        borderBottom: '1px solid var(--border-1)',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 11, color: 'var(--fg-3)', fontWeight: 500, letterSpacing: '0.06em', textTransform: 'uppercase' }}>
              {window.fmtTodayLabel()}
            </div>
            <div data-reload-tap style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.02em', color: 'var(--fg-1)', marginTop: 2, lineHeight: 1.1 }}>
              Daily Checklist
            </div>
          </div>
          <div style={{ fontSize: 14, color: 'var(--fg-2)', fontFamily: 'var(--font-num)' }}>
            <strong style={{ color: 'var(--fg-1)' }}>{doneItems}</strong>/{totalItems} done
          </div>
        </div>
      </div>

      {/* Scrollable group list */}
      <div style={{
        flex: 1, minHeight: 0, overflowY: 'auto',
        padding: '16px 32px',
        // Reserve room at the bottom for the floating Complete bar so
        // the last group doesn't get hidden under it.
        paddingBottom: pending.size > 0 ? 120 : 24,
      }}>
        {groups.length === 0 ? (
          <div style={{ padding: '60px 20px', textAlign: 'center', color: 'var(--fg-3)' }}>
            {(window.SAMPLE_CHECKLIST_GROUPS || []).length === 0 ? (
              <>
                <div style={{ fontSize: 15, marginBottom: 6 }}>Nothing scheduled today.</div>
                <div style={{ fontSize: 12.5 }}>Admin hasn't set up a checklist template yet.</div>
              </>
            ) : todayGroups.length === 0 ? (
              <>
                <div style={{ fontSize: 15, marginBottom: 6 }}>Nothing scheduled today.</div>
                <div style={{ fontSize: 12.5 }}>Today's weekday has no groups assigned.</div>
              </>
            ) : nextUpcoming ? (
              <>
                <div style={{ fontSize: 15, marginBottom: 6 }}>Nothing due yet today.</div>
                <div style={{ fontSize: 12.5 }}>
                  Next: <strong style={{ color: 'var(--fg-2)' }}>{nextUpcoming.name}</strong>
                  {' at '}
                  <strong style={{ color: 'var(--fg-2)' }}>{formatTime12(nextUpcoming.triggerTime)}</strong>.
                </div>
              </>
            ) : (
              <div style={{ fontSize: 15 }}>All today's groups are done.</div>
            )}
          </div>
        ) : groups.map(group => (
          <ChecklistGroupCard
            key={group.id}
            group={group}
            items={itemsByGroup[group.id] || []}
            completions={completions}
            pending={pending}
            onTapItem={togglePending}
            onUncomplete={uncomplete}
          />
        ))}
      </div>

      {/* Floating Complete CTA — shows only when there are pending checks */}
      {pending.size > 0 && (
        <div style={{
          position: 'absolute', left: 0, right: 0, bottom: 0,
          padding: '14px 32px calc(20px + env(safe-area-inset-bottom))',
          background: 'rgba(255,255,255,0.94)',
          backdropFilter: 'saturate(180%) blur(20px)',
          WebkitBackdropFilter: 'saturate(180%) blur(20px)',
          borderTop: '1px solid var(--border-1)',
          display: 'flex', alignItems: 'center', gap: 14,
        }}>
          <button
            onClick={() => setPending(new Set())}
            style={{
              padding: '12px 18px', borderRadius: 12,
              fontSize: 15, fontWeight: 500,
              background: 'var(--bg-sunken)', color: 'var(--fg-2)',
              border: 'none', cursor: 'pointer',
            }}
          >Cancel</button>
          <button
            onClick={openComplete}
            style={{
              flex: 1, height: 56, borderRadius: 12,
              background: '#1D4ED8', color: '#FFFFFF',
              fontSize: 17, fontWeight: 600,
              letterSpacing: '-0.01em',
              border: 'none', cursor: 'pointer',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 10,
              boxShadow: '0 6px 18px rgba(29,78,216,0.35)',
            }}
          >
            <Icon name="check" size={18} color="#fff" strokeWidth={2.4} />
            Complete {pending.size} {pending.size === 1 ? 'item' : 'items'}
          </button>
        </div>
      )}

      <StaffPicker
        open={pickerOpen}
        onClose={() => setPickerOpen(false)}
        onPick={confirmCompletion}
        title="Who completed these?"
        subtitle={`${pending.size} ${pending.size === 1 ? 'item' : 'items'} will be logged under this person.`}
      />
    </div>
  );
};

// ------------------------------------------------------------------
// One group's card — header + tappable items.
// ------------------------------------------------------------------
const ChecklistGroupCard = ({ group, items, completions, pending, onTapItem, onUncomplete }) => {
  const done = items.filter(i => completions[i.id]).length;
  const total = items.length;
  const complete = total > 0 && done === total;
  return (
    <div style={{
      background: 'var(--bg-surface)',
      border: '1px solid var(--border-2)',
      borderRadius: 16,
      marginBottom: 14,
      overflow: 'hidden',
    }}>
      <div style={{
        padding: '14px 18px',
        display: 'flex', alignItems: 'center', gap: 14,
        borderBottom: total > 0 ? '1px solid var(--border-2)' : 'none',
        background: complete ? 'rgba(45,106,42,0.06)' : 'transparent',
      }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 17, fontWeight: 600, color: 'var(--fg-1)', letterSpacing: '-0.01em' }}>
            {group.name}
          </div>
          <div style={{ fontSize: 12, color: 'var(--fg-3)', marginTop: 2, fontFamily: 'var(--font-num)' }}>
            {formatTime12(group.triggerTime)}
            {' · '}
            {done}/{total} done
          </div>
        </div>
        {complete && (
          <div style={{
            padding: '4px 10px', borderRadius: 999,
            background: '#2D6A2A', color: '#fff',
            fontSize: 11, fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase',
            display: 'inline-flex', alignItems: 'center', gap: 5,
          }}>
            <Icon name="check" size={12} color="#fff" strokeWidth={2.6} />
            Complete
          </div>
        )}
      </div>

      {items.length === 0 ? (
        <div style={{ padding: '14px 18px', fontSize: 13, color: 'var(--fg-3)' }}>
          No items in this group.
        </div>
      ) : items.map((item, idx) => (
        <ChecklistItemRow
          key={item.id}
          item={item}
          completion={completions[item.id]}
          isPending={pending.has(item.id)}
          isLast={idx === items.length - 1}
          onTap={() => onTapItem(item.id)}
          onUncomplete={() => onUncomplete(item.id)}
        />
      ))}
    </div>
  );
};

const ChecklistItemRow = ({ item, completion, isPending, isLast, onTap, onUncomplete }) => {
  const done = !!completion;
  const staff = done && (window.SAMPLE_STAFF || []).find(s => s.id === completion.staffId);
  const palette = done ? CHECK_PALETTE.done : (isPending ? CHECK_PALETTE.pending : null);

  return (
    <button
      onClick={done ? onUncomplete : onTap}
      style={{
        width: '100%', textAlign: 'left',
        display: 'flex', alignItems: 'center', gap: 14,
        padding: '14px 18px',
        background: done ? 'rgba(45,106,42,0.04)' : (isPending ? 'rgba(245,158,11,0.06)' : 'transparent'),
        borderBottom: isLast ? 'none' : '1px solid var(--border-2)',
        border: 'none',
        cursor: 'pointer',
        transition: 'background 120ms ease',
      }}
      onTouchStart={e => { e.currentTarget.style.background = 'rgba(0,0,0,0.04)'; }}
      onTouchEnd={e => { e.currentTarget.style.background = done ? 'rgba(45,106,42,0.04)' : (isPending ? 'rgba(245,158,11,0.06)' : 'transparent'); }}
    >
      <div style={{
        width: 28, height: 28, borderRadius: 8, flexShrink: 0,
        background: palette ? palette.bg : 'transparent',
        border: palette ? `2px solid ${palette.border}` : '2px solid var(--border-1)',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      }}>
        {(done || isPending) && (
          <Icon name="check" size={16} color={palette.tick} strokeWidth={2.6} />
        )}
      </div>
      <div style={{
        flex: 1, minWidth: 0,
        fontSize: 16, fontWeight: 500,
        color: done ? 'var(--fg-2)' : 'var(--fg-1)',
        textDecoration: done ? 'line-through' : 'none',
        letterSpacing: '-0.005em',
      }}>
        {item.name}
      </div>
      {done && staff && (
        <div style={{
          display: 'inline-flex', alignItems: 'center', gap: 8,
          padding: '4px 10px 4px 4px',
          borderRadius: 999,
          background: 'var(--bg-sunken)',
          flexShrink: 0,
        }}>
          <Avatar staff={staff} size={22} />
          <span style={{ fontSize: 12, color: 'var(--fg-2)', fontWeight: 500 }}>
            {staff.name.split(' ')[0]}
          </span>
        </div>
      )}
    </button>
  );
};

// ------------------------------------------------------------------
// Full-screen reminder — fires when a group's trigger_time arrives (or
// the reminder_minutes cadence re-fires it). Doesn't auto-complete the
// items; the only action is "Open Checklist" which dismisses the
// overlay and navigates the user to the Checklist tab.
// ------------------------------------------------------------------
const ChecklistReminderOverlay = ({ group, onOpenChecklist, onDismiss }) => {
  const [mounted, setMounted] = useChk(false);
  useChkEffect(() => {
    const raf = requestAnimationFrame(() => setMounted(true));
    return () => cancelAnimationFrame(raf);
  }, []);

  return (
    <div className="attn-flash" style={{
      position: 'absolute', inset: 0, zIndex: 900,
      // Flash between violet → pink → indigo to grab attention (was a static
      // purple). White text stays readable on all three.
      '--flash-a': '#7C3AED', '--flash-b': '#DB2777', '--flash-c': '#4F46E5',
      transition: 'opacity 260ms ease-out, transform 260ms ease-out',
      color: '#FFFFFF',
      display: 'flex', flexDirection: 'column',
      padding: '64px 56px 52px',
      overflow: 'hidden',
      opacity: mounted ? 1 : 0,
      transform: mounted ? 'scale(1)' : 'scale(0.97)',
    }}>
      <div style={{
        position: 'absolute', top: -180, right: -180,
        width: 480, height: 480, borderRadius: '50%',
        border: '1px solid rgba(255,255,255,0.12)',
        pointerEvents: 'none',
      }} />
      <div style={{
        position: 'absolute', top: -100, right: -100,
        width: 320, height: 320, borderRadius: '50%',
        border: '1px solid rgba(255,255,255,0.10)',
        pointerEvents: 'none',
      }} />

      <div style={{ display: 'flex', alignItems: 'center', gap: 14, position: 'relative', zIndex: 1 }}>
        <Icon name="check" size={22} color="#fff" strokeWidth={2.4} />
        <div style={{ fontSize: 13, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase' }}>
          Checklist reminder
        </div>
      </div>

      <div style={{ height: 1, background: 'rgba(255,255,255,0.18)', marginTop: 22, marginBottom: 28, position: 'relative', zIndex: 1 }} />

      <div style={{ fontSize: 16, opacity: 0.8, position: 'relative', zIndex: 1, fontFamily: 'var(--font-num)' }}>
        {formatTime12(group.triggerTime)}
      </div>

      <div style={{
        flex: 1,
        display: 'flex', flexDirection: 'column', justifyContent: 'center',
        position: 'relative', zIndex: 1,
      }}>
        <div style={{
          fontSize: 56, fontWeight: 500,
          letterSpacing: '-0.03em', lineHeight: 1.12,
          textWrap: 'balance', maxWidth: 640,
        }}>
          {group.name}
        </div>
        <div style={{ fontSize: 18, opacity: 0.78, marginTop: 14, maxWidth: 600 }}>
          Tap to open the checklist. Mark each item off once it's actually done — no shortcut to mark complete from here.
        </div>
      </div>

      <div style={{ display: 'flex', gap: 14, position: 'relative', zIndex: 1 }}>
        <button onClick={onDismiss} style={{
          padding: '0 24px', height: 88,
          borderRadius: 20,
          background: 'rgba(255,255,255,0.12)',
          color: '#FFFFFF',
          fontSize: 18, fontWeight: 500,
          border: 'none', cursor: 'pointer',
        }}>Dismiss</button>
        <button onClick={onOpenChecklist} style={{
          flex: 1, height: 88,
          borderRadius: 20,
          background: '#FFFFFF', color: '#5B21B6',
          fontSize: 24, fontWeight: 600,
          letterSpacing: '-0.01em',
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 12,
          cursor: 'pointer',
          boxShadow: '0 12px 32px rgba(0,0,0,0.25)',
          border: 'none',
        }}>
          <Icon name="check" size={24} color="#5B21B6" strokeWidth={2.4} />
          Open Checklist
        </button>
      </div>
    </div>
  );
};

// ------------------------------------------------------------------
// Helpers — kept local so the file is standalone-readable.
// ------------------------------------------------------------------
function triggerMinFor(g) {
  const [h, m] = (g.triggerTime || '00:00').split(':').map(Number);
  return (h || 0) * 60 + (m || 0);
}

function isoLocalDate(d) {
  return d.getFullYear() + '-' +
    String(d.getMonth() + 1).padStart(2, '0') + '-' +
    String(d.getDate()).padStart(2, '0');
}
function formatTime12(hhmm) {
  if (!hhmm) return '';
  const [h, m] = hhmm.split(':').map(Number);
  const ampm = h >= 12 ? 'PM' : 'AM';
  const h12 = ((h + 11) % 12) + 1;
  return `${h12}:${String(m).padStart(2, '0')} ${ampm}`;
}

// ------------------------------------------------------------------
// Reminder scheduler hook — checks every 30s whether any group is past
// its trigger_time today with incomplete items, and surfaces the
// next-to-fire group via the returned state. Polling-based (not
// timer-per-group) to keep it resilient to clock drift / tab sleep.
//
// Returns: [reminderGroup, dismissCurrent] — the App wraps the overlay
// with these. Dismissing marks the group as "shown" for `reminderMinutes`
// in localStorage so it won't fire again until that interval lapses.
// ------------------------------------------------------------------
function useChecklistReminder() {
  const [reminderGroupId, setReminderGroupId] = useChk(null);
  const lastShownRef = useChkRef({}); // { [groupId+'@'+dateKey]: ms epoch }
  const completionsRef = useChkRef({}); // { itemId: true } for today
  const tickRef = useChkRef(0);

  // Load completions for today; refresh every minute. This is cheap and
  // robust — full polling rather than realtime to avoid coupling the
  // reminder to App.jsx mount lifecycle.
  useChkEffect(() => {
    if (!window.supa) return;
    let cancelled = false;
    const refresh = async () => {
      const dk = isoLocalDate(new Date());
      const { data } = await window.supa.from('checklist_completions')
        .select('item_id')
        .eq('restaurant_id', window.RESTAURANT_ID)
        .eq('the_date', dk);
      if (cancelled) return;
      const m = {};
      (data || []).forEach(c => { m[c.item_id] = true; });
      completionsRef.current = m;
    };
    refresh();
    const t = setInterval(refresh, 60000);
    return () => { cancelled = true; clearInterval(t); };
  }, []);

  // Poll the schedule.
  useChkEffect(() => {
    const tick = () => {
      tickRef.current += 1;
      if (reminderGroupId) return; // already showing
      const now = new Date();
      const dk = isoLocalDate(now);
      const dow = now.getDay();
      const nowMin = now.getHours() * 60 + now.getMinutes();
      const groups = (window.SAMPLE_CHECKLIST_GROUPS || [])
        .filter(g => !g.archived)
        .filter(g => (g.weekdays || []).includes(dow));
      const items = (window.SAMPLE_CHECKLIST_ITEMS || []).filter(i => !i.archived);

      for (const g of groups) {
        const [h, m] = (g.triggerTime || '00:00').split(':').map(Number);
        const trigMin = h * 60 + m;
        if (nowMin < trigMin) continue; // not yet
        const myItems = items.filter(i => i.groupId === g.id);
        if (myItems.length === 0) continue; // nothing to remind about
        const incomplete = myItems.some(i => !completionsRef.current[i.id]);
        if (!incomplete) continue;

        const key = g.id + '@' + dk;
        const last = lastShownRef.current[key];
        if (last === undefined) {
          // First firing of the day — always pop once when the time
          // arrives, regardless of cadence.
          lastShownRef.current[key] = Date.now();
          setReminderGroupId(g.id);
          return;
        }
        // reminderMinutes === 0 means "fire once, never re-fire".
        const cadence = Number(g.reminderMinutes);
        if (!isFinite(cadence) || cadence <= 0) continue;
        const sinceMin = (Date.now() - last) / 60000;
        if (sinceMin >= cadence) {
          lastShownRef.current[key] = Date.now();
          setReminderGroupId(g.id);
          return;
        }
      }
    };
    tick();
    const t = setInterval(tick, 30000); // 30s poll keeps lag <= reminderMinutes+30s
    return () => clearInterval(t);
  }, [reminderGroupId]);

  const dismiss = () => setReminderGroupId(null);
  const group = (window.SAMPLE_CHECKLIST_GROUPS || []).find(g => g.id === reminderGroupId) || null;
  return [group, dismiss];
}

Object.assign(window, {
  ChecklistScreen,
  ChecklistReminderOverlay,
  useChecklistReminder,
});
