// screens-member.jsx — member-facing phone screens for BHCAC Attendance
// All screens are bilingual (EN + 中文). Designed for a 402×874 iPhone canvas.

const { useState, useEffect, useMemo } = React;

const D = window.BHCAC_DATA;

// ─────────────────────────────────────────────────────────────
// Screen 1: WELCOME — sign-in
// Mobile: dark hero with form on top.
// Desktop (≥ 768px): two-column — hero panel left, sign-in card right.
// onSignIn(user) is called with the authenticated user object.
// ─────────────────────────────────────────────────────────────
function useIsDesktop(breakpoint = 768) {
  const [match, setMatch] = useState(typeof window !== 'undefined' && window.innerWidth >= breakpoint);
  useEffect(() => {
    if (typeof window === 'undefined' || !window.matchMedia) return;
    const mql = window.matchMedia(`(min-width: ${breakpoint}px)`);
    const handler = (e) => setMatch(e.matches);
    setMatch(mql.matches);
    // Safari < 14 / older Android only support the deprecated addListener API.
    // Using addEventListener unconditionally throws there and blanks the page.
    if (typeof mql.addEventListener === 'function') {
      mql.addEventListener('change', handler);
      return () => mql.removeEventListener('change', handler);
    }
    if (typeof mql.addListener === 'function') {
      mql.addListener(handler);
      return () => mql.removeListener(handler);
    }
  }, [breakpoint]);
  return match;
}

function ScreenWelcome({ onSignIn, inviteBanner, prefillEmail }) {
  const AUTH = window.BHCAC_AUTH;
  const isDesktop = useIsDesktop();
  // If we landed here via ?invite-spouse=… the user almost always wants to
  // register (most spouses don't yet have an account); flip the default.
  const [mode, setMode] = useState(inviteBanner ? 'register' : 'login');
  const [email, setEmail] = useState(prefillEmail || '');
  const [password, setPassword] = useState('');
  const [name, setName] = useState('');
  const [zh, setZh] = useState('');
  // Family-account toggle. Default to 'individual' so we don't surprise
  // existing users; spouse-invite landings flip it to 'family' implicitly
  // because the second user joins via accept-family-invite, not register.
  const [accountType, setAccountType] = useState('individual');
  const [spouseEmail, setSpouseEmail] = useState('');
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState(null);
  const supportsPasskey = !!(AUTH && AUTH.browserSupportsPasskeys && AUTH.browserSupportsPasskeys());

  const submit = async (e) => {
    e?.preventDefault?.();
    setError(null); setSubmitting(true);
    try {
      const user = mode === 'register'
        ? await AUTH.register({
            email, password, en: name, zh,
            accountType,
            spouseEmail: accountType === 'family' ? spouseEmail : '',
          })
        : await AUTH.login({ email, password });
      onSignIn(user);
    } catch (err) {
      setError(err.message);
    } finally {
      setSubmitting(false);
    }
  };

  const passkey = async () => {
    setError(null); setSubmitting(true);
    try {
      const user = await AUTH.loginWithPasskey({ email: email || undefined });
      onSignIn(user);
    } catch (err) {
      setError(err.message);
    } finally {
      setSubmitting(false);
    }
  };

  const formState = {
    mode, setMode, email, setEmail, password, setPassword,
    name, setName, zh, setZh,
    accountType, setAccountType, spouseEmail, setSpouseEmail,
    submitting, error, setError,
    submit, passkey, supportsPasskey,
    inviteBanner,
  };

  return isDesktop
    ? <DesktopWelcome {...formState} />
    : <MobileWelcome {...formState} />;
}

// --- Mobile sign-in (full-bleed dark hero) ---
function MobileWelcome(p) {
  const inputStyle = {
    width: '100%', boxSizing: 'border-box',
    padding: '13px 16px',
    borderRadius: 12,
    border: '1px solid rgba(255,255,255,0.18)',
    background: 'rgba(255,255,255,0.08)',
    color: '#fff', fontSize: 15, fontFamily: 'Inter, sans-serif',
    outline: 'none',
  };
  return (
    <div style={{
      height: '100%', position: 'relative', overflow: 'hidden',
      background: 'var(--brand-navy)', color: '#fff',
      display: 'flex', flexDirection: 'column',
    }}>
      <div style={{
        position: 'absolute', inset: 0,
        backgroundImage: 'url("assets/cc-event-1.jpg")',
        backgroundSize: 'cover', backgroundPosition: 'center',
        opacity: 0.35,
      }} />
      <div style={{
        position: 'absolute', inset: 0,
        background: 'linear-gradient(to bottom, rgba(15,23,42,0.5) 0%, rgba(15,23,42,0.92) 55%, var(--brand-navy) 100%)',
      }} />

      <div style={{
        position: 'relative', zIndex: 2,
        height: '100%', overflowY: 'auto',
        display: 'flex', flexDirection: 'column',
        padding: 'max(40px, env(safe-area-inset-top, 28px)) 26px 28px',
      }}>
        <BrandRow dark />
        <FormHeader dark mode={p.mode} />
        <SigninForm dark inputStyle={inputStyle} {...p} />
        <div style={{
          marginTop: 24, fontSize: 11, color: 'rgba(255,255,255,0.45)', textAlign: 'center',
          fontFamily: "'Noto Sans TC', Inter, sans-serif",
          paddingTop: 14, borderTop: '1px solid rgba(255,255,255,0.08)',
        }}>
          2/4 Gladstone Rd, Castle Hill · 國語 9:15 · 粵語 11:00 · 英語 4:30
        </div>
      </div>
    </div>
  );
}

// --- Desktop sign-in (split-screen hero + form card) ---
function DesktopWelcome(p) {
  const inputStyle = {
    width: '100%', boxSizing: 'border-box',
    padding: '13px 16px',
    borderRadius: 12,
    border: '1px solid var(--border-strong)',
    background: 'var(--bg-1)',
    color: 'var(--fg-1)', fontSize: 15, fontFamily: 'Inter, sans-serif',
    outline: 'none',
    transition: 'border-color 150ms, box-shadow 150ms',
  };
  return (
    <div style={{
      height: '100%', display: 'grid',
      gridTemplateColumns: 'minmax(420px, 1fr) minmax(440px, 1fr)',
      background: 'var(--bg-1)',
    }}>
      {/* Left — pastoral hero */}
      <div style={{
        position: 'relative', overflow: 'hidden',
        background: 'var(--brand-navy)', color: '#fff',
        display: 'flex', flexDirection: 'column',
      }}>
        <div style={{
          position: 'absolute', inset: 0,
          backgroundImage: 'url("assets/congregation.jpg")',
          backgroundSize: 'cover', backgroundPosition: 'center',
          opacity: 0.45,
        }} />
        <div style={{
          position: 'absolute', inset: 0,
          background: 'linear-gradient(135deg, rgba(15,23,42,0.55) 0%, rgba(15,23,42,0.9) 65%, rgba(15,23,42,0.95) 100%)',
        }} />
        <div style={{
          position: 'relative', zIndex: 2,
          flex: 1, display: 'flex', flexDirection: 'column',
          padding: '48px 56px',
        }}>
          <BrandRow dark large />

          <div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', maxWidth: 520 }}>
            <Eyebrow color="var(--coral-400)" style={{ marginBottom: 16 }}>BHCAC · ATTENDANCE</Eyebrow>
            <h1 style={{
              fontSize: 'clamp(40px, 5vw, 64px)', fontWeight: 700, lineHeight: 1.05,
              letterSpacing: '-0.025em', margin: 0,
            }}>
              You are<br/>welcome here.
            </h1>
            <div style={{
              fontFamily: "'Noto Sans TC', sans-serif", fontSize: 'clamp(22px, 2.4vw, 30px)',
              fontWeight: 600, color: 'rgba(255,255,255,0.85)',
              marginTop: 12, lineHeight: 1.3,
            }}>
              歡迎來到迦南堂
            </div>
            <p style={{
              fontSize: 17, color: 'rgba(255,255,255,0.78)', lineHeight: 1.55,
              marginTop: 24, maxWidth: 460,
            }}>
              One app for the whole congregation — sign in to check your family in,
              register a visitor, or follow this Sunday's services.
            </p>
            <p style={{
              fontFamily: "'Noto Sans TC', sans-serif", fontSize: 14,
              color: 'rgba(255,255,255,0.55)', lineHeight: 1.6,
              marginTop: 14, maxWidth: 460,
            }}>
              一個應用程式，服侍全會眾 — 登入即可代家人簽到、登記新朋友，或了解本主日的崇拜安排。
            </p>
          </div>

          <div style={{
            display: 'flex', gap: 24, flexWrap: 'wrap',
            paddingTop: 24, borderTop: '1px solid rgba(255,255,255,0.1)',
            fontSize: 13, color: 'rgba(255,255,255,0.7)',
          }}>
            <ServiceTime time="9:15"  label="Mandarin · 國語" />
            <ServiceTime time="11:00" label="Cantonese · 粵語" />
            <ServiceTime time="4:30"  label="One Hope · 英語" />
            <div style={{ marginLeft: 'auto', alignSelf: 'flex-end', textAlign: 'right' }}>
              2/4 Gladstone Rd<br/>Castle Hill NSW
            </div>
          </div>
        </div>
      </div>

      {/* Right — form card */}
      <div style={{
        background: 'var(--bg-1)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        padding: '40px 48px', overflowY: 'auto',
      }}>
        <div style={{ width: '100%', maxWidth: 400 }}>
          <FormHeader mode={p.mode} />
          <SigninForm inputStyle={inputStyle} {...p} />
          <p style={{
            marginTop: 28, fontSize: 11, color: 'var(--fg-3)',
            lineHeight: 1.55, fontFamily: "'Noto Sans TC', Inter, sans-serif",
            paddingTop: 18, borderTop: '1px solid var(--border-subtle)',
          }}>
            By signing in you agree to BHCAC handling your information under the Australian Privacy Principles.
            <br/>本會將遵行澳洲保護個人私隱條例處理閣下資料。
          </p>
        </div>
      </div>
    </div>
  );
}

function BrandRow({ dark, large }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
      <img src="assets/bhcac-logo-red.jpg" style={{
        width: large ? 48 : 40, height: large ? 48 : 40,
        borderRadius: '50%', objectFit: 'cover',
        boxShadow: dark ? '0 0 0 2px rgba(255,255,255,0.15)' : '0 0 0 1px var(--border-subtle)',
      }} />
      <div>
        <div style={{ fontSize: large ? 18 : 15, fontWeight: 700, letterSpacing: '-0.01em', color: dark ? '#fff' : 'var(--fg-1)' }}>BHCAC</div>
        <div style={{
          fontSize: large ? 11 : 10, fontWeight: 500, letterSpacing: '0.12em',
          color: dark ? 'rgba(255,255,255,0.7)' : 'var(--fg-3)',
        }}>迦南堂 · ATTENDANCE</div>
      </div>
    </div>
  );
}

function FormHeader({ dark, mode }) {
  const fg = dark ? '#fff' : 'var(--fg-1)';
  const fgMuted = dark ? 'rgba(255,255,255,0.85)' : 'var(--fg-3)';
  return (
    <div style={{ marginTop: dark ? 28 : 0, marginBottom: 24 }}>
      <Eyebrow color={dark ? 'var(--coral-400)' : 'var(--brand-coral)'} style={{ marginBottom: 10 }}>
        {mode === 'register' ? 'Create account · 建立帳戶' : 'Sign in · 登入'}
      </Eyebrow>
      <h2 style={{ fontSize: 28, fontWeight: 700, lineHeight: 1.15, letterSpacing: '-0.02em', margin: 0, color: fg }}>
        {mode === 'register' ? 'Welcome — let’s set you up.' : 'Welcome back.'}
      </h2>
      <div style={{
        fontFamily: "'Noto Sans TC', sans-serif", fontSize: 15, fontWeight: 500,
        color: fgMuted, marginTop: 6,
      }}>
        {mode === 'register' ? '歡迎，請建立您的帳戶' : '請登入以繼續'}
      </div>
    </div>
  );
}

function SigninForm({ dark, inputStyle, mode, setMode, email, setEmail, password, setPassword,
                     name, setName, zh, setZh,
                     accountType, setAccountType, spouseEmail, setSpouseEmail,
                     submitting, error, setError, submit, passkey, supportsPasskey,
                     inviteBanner }) {
  const dividerColor = dark ? 'rgba(255,255,255,0.1)' : 'var(--border-subtle)';
  const dividerText  = dark ? 'rgba(255,255,255,0.45)' : 'var(--fg-3)';
  const passkeyBtn = dark ? {
    background: 'rgba(255,255,255,0.08)', color: '#fff',
    border: '1px solid rgba(255,255,255,0.25)', backdropFilter: 'blur(8px)',
  } : {
    background: 'var(--bg-1)', color: 'var(--fg-1)',
    border: '1px solid var(--border-strong)',
  };
  const linkColor = dark ? 'var(--coral-400)' : 'var(--brand-coral)';
  const linkMuted = dark ? 'rgba(255,255,255,0.6)' : 'var(--fg-3)';

  // Reusable styling for the Individual / Family toggle pills.
  const togglePill = (active) => ({
    flex: 1, padding: '11px 12px',
    borderRadius: 12, cursor: 'pointer',
    background: active
      ? (dark ? 'rgba(244,63,94,0.22)' : 'var(--brand-warm)')
      : 'transparent',
    border: '1.5px solid ' + (active
      ? 'var(--brand-coral)'
      : (dark ? 'rgba(255,255,255,0.18)' : 'var(--border-subtle)')),
    color: active
      ? (dark ? '#fecaca' : 'var(--coral-700)')
      : (dark ? 'rgba(255,255,255,0.78)' : 'var(--fg-2)'),
    fontSize: 13, fontWeight: 700, fontFamily: 'Inter, sans-serif',
    textAlign: 'center',
    transition: 'all 200ms var(--ease-out)',
  });

  return (
    <>
      {inviteBanner && (
        <div style={{
          marginBottom: 14,
          padding: '10px 14px', borderRadius: 10,
          background: dark ? 'rgba(244,63,94,0.18)' : 'var(--brand-warm)',
          border: '1px solid ' + (dark ? 'rgba(244,63,94,0.4)' : 'rgba(244,63,94,0.3)'),
          color: dark ? '#fecaca' : '#7c2d12',
          fontSize: 13, lineHeight: 1.5,
        }}>
          {inviteBanner}
        </div>
      )}
      <form onSubmit={submit} style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
        {mode === 'register' && (
          <>
            <input style={inputStyle} type="text" placeholder="Your name · 姓名"
              value={name} onChange={e => setName(e.target.value)} required />
            <input style={inputStyle} type="text" placeholder="中文姓名 (optional)"
              value={zh} onChange={e => setZh(e.target.value)} />
          </>
        )}
        <input style={inputStyle} type="email" placeholder="Email · 電郵"
          value={email} onChange={e => setEmail(e.target.value)} required autoComplete="email" />

        {mode === 'register' && setAccountType && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 }}>
            <div style={{
              fontSize: 11, fontWeight: 700, letterSpacing: '0.08em',
              textTransform: 'uppercase',
              color: dark ? 'rgba(255,255,255,0.55)' : 'var(--fg-3)',
            }}>
              Account type · 帳戶類型
            </div>
            <div style={{ display: 'flex', gap: 8 }}>
              <button type="button" onClick={() => setAccountType('individual')}
                style={togglePill(accountType === 'individual')}>
                Individual · 個人
              </button>
              <button type="button" onClick={() => setAccountType('family')}
                style={togglePill(accountType === 'family')}>
                Family · 家庭
              </button>
            </div>
            {accountType === 'family' && (
              <>
                <div style={{
                  fontSize: 12, lineHeight: 1.5,
                  color: dark ? 'rgba(255,255,255,0.65)' : 'var(--fg-3)',
                  fontFamily: "'Noto Sans TC', Inter, sans-serif",
                }}>
                  Both of you will manage attendance together · 你們可以共同管理家人簽到
                </div>
                <input style={inputStyle} type="email"
                  placeholder="Spouse email · 配偶電郵 (optional · 選填)"
                  value={spouseEmail || ''} onChange={e => setSpouseEmail(e.target.value)}
                  autoComplete="off" />
              </>
            )}
          </div>
        )}

        <input style={inputStyle} type="password" placeholder="Password · 密碼"
          value={password} onChange={e => setPassword(e.target.value)} required
          autoComplete={mode === 'register' ? 'new-password' : 'current-password'}
          minLength={mode === 'register' ? 8 : undefined} />

        {error && (
          <div style={{
            fontSize: 13, color: dark ? '#fecaca' : 'var(--coral-700)',
            background: dark ? 'rgba(244,63,94,0.18)' : 'var(--coral-100)',
            border: dark ? '1px solid rgba(244,63,94,0.4)' : '1px solid rgba(244,63,94,0.3)',
            borderRadius: 10, padding: '10px 12px',
          }}>{error}</div>
        )}

        <button type="submit" disabled={submitting} style={{
          marginTop: 6, padding: '14px 22px',
          background: 'var(--brand-coral)', color: '#fff',
          border: 'none', borderRadius: 9999,
          fontSize: 15, fontWeight: 700, cursor: submitting ? 'wait' : 'pointer',
          fontFamily: 'Inter, sans-serif',
          opacity: submitting ? 0.7 : 1,
          boxShadow: '0 6px 16px -6px rgba(244,63,94,0.6)',
        }}>
          {submitting ? '…' : (mode === 'register' ? 'Create account · 建立帳戶' : 'Sign in · 登入')}
        </button>
      </form>

      {supportsPasskey && (
        <>
          <div style={{
            display: 'flex', alignItems: 'center', gap: 10,
            margin: '20px 0', color: dividerText, fontSize: 11, fontWeight: 600,
            letterSpacing: '0.1em', textTransform: 'uppercase',
          }}>
            <div style={{ flex: 1, height: 1, background: dividerColor }} />
            or · 或
            <div style={{ flex: 1, height: 1, background: dividerColor }} />
          </div>
          <button type="button" onClick={passkey} disabled={submitting} style={{
            width: '100%',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10,
            padding: '13px 22px', borderRadius: 9999,
            fontSize: 15, fontWeight: 600, cursor: submitting ? 'wait' : 'pointer',
            fontFamily: 'Inter, sans-serif',
            ...passkeyBtn,
          }}>
            <Icon name="key" size={18} />
            <span>Continue with passkey · 通行密鑰</span>
          </button>
        </>
      )}

      <div style={{ marginTop: 22, textAlign: 'center', fontSize: 13 }}>
        {mode === 'register' ? (
          <>
            <span style={{ color: linkMuted }}>Already have an account? </span>
            <button type="button" onClick={() => { setError(null); setMode('login'); }}
              style={{ color: linkColor, background: 'transparent', border: 'none', fontWeight: 600, cursor: 'pointer', fontSize: 13 }}>
              Sign in · 登入
            </button>
          </>
        ) : (
          <>
            <span style={{ color: linkMuted }}>New to BHCAC? </span>
            <button type="button" onClick={() => { setError(null); setMode('register'); }}
              style={{ color: linkColor, background: 'transparent', border: 'none', fontWeight: 600, cursor: 'pointer', fontSize: 13 }}>
              Create account · 建立帳戶
            </button>
          </>
        )}
      </div>
    </>
  );
}

function ServiceTime({ time, label }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.01em', color: '#fff' }}>{time}</div>
      <div style={{ fontSize: 12, fontFamily: "'Noto Sans TC', sans-serif", color: 'rgba(255,255,255,0.65)' }}>{label}</div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Screen 2: TODAY (member home)
// ─────────────────────────────────────────────────────────────
function computeRecentSundays(user, checkIns) {
  const today = new Date();
  const todaySunday = new Date(today);
  if (todaySunday.getDay() !== 0) {
    todaySunday.setDate(todaySunday.getDate() - todaySunday.getDay());
  }
  const items = [];
  for (let i = 4; i >= 0; i--) {
    const d = new Date(todaySunday);
    d.setDate(d.getDate() - i * 7);
    items.push(d);
  }
  const memberIdSet = new Set([user?.id, user?.authUserId, user?.memberId].filter(Boolean));
  const userDates = new Set(
    (checkIns || []).filter(c => memberIdSet.has(c.member_id)).map(c => c.date)
  );
  const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  return items.map(d => {
    const dateStr = d.toISOString().slice(0, 10);
    const isToday = d.toDateString() === new Date().toDateString();
    const isThisSunday = d.toDateString() === todaySunday.toDateString();
    return {
      date: dateStr,
      label: (isToday || isThisSunday) ? 'Today' : `${d.getDate()} ${months[d.getMonth()]}`,
      here: userDates.has(dateStr),
      today: isToday || isThisSunday,
    };
  });
}

function greetingFor(d) {
  const h = d.getHours();
  if (h < 12) return 'Good morning';
  if (h < 17) return 'Good afternoon';
  return 'Good evening';
}

// Sydney timezone helpers — services are scheduled in local time, so we
// compare everything in Australia/Sydney rather than UTC.
function todayDateInSydney(now = new Date()) {
  return new Intl.DateTimeFormat('en-CA', { timeZone: 'Australia/Sydney' }).format(now);
}
function nowMinutesInSydney(now = new Date()) {
  const fmt = new Intl.DateTimeFormat('en-GB', { timeZone: 'Australia/Sydney', hour: '2-digit', minute: '2-digit', hour12: false });
  const [hh, mm] = fmt.format(now).split(':').map(Number);
  return hh * 60 + mm;
}
function parseServiceTimeMinutes(timeStr) {
  if (!timeStr) return null;
  const m = String(timeStr).match(/(\d{1,2}):(\d{2})\s*(AM|PM)?/i);
  if (!m) return null;
  let h = parseInt(m[1]);
  const min = parseInt(m[2]);
  const ap = (m[3] || '').toUpperCase();
  if (ap === 'PM' && h < 12) h += 12;
  if (ap === 'AM' && h === 12) h = 0;
  return h * 60 + min;
}
// Default window: opens 20 min before service, stays open 180 min after.
function checkinWindow(serviceTime, now = new Date(), beforeMin = 20, afterMin = 180) {
  const eventMin = parseServiceTimeMinutes(serviceTime);
  if (eventMin == null) return { state: 'unknown', open: true };
  const nowMin = nowMinutesInSydney(now);
  const opensAt = eventMin - beforeMin;
  const closesAt = eventMin + afterMin;
  if (nowMin < opensAt) return { state: 'too-early', open: false, opensAt, eventMin };
  if (nowMin > closesAt) return { state: 'too-late', open: false, closesAt, eventMin };
  return { state: 'open', open: true, eventMin };
}
function formatHM(totalMinutes) {
  if (totalMinutes == null) return '';
  const h24 = Math.floor(totalMinutes / 60);
  const m = totalMinutes % 60;
  const ap = h24 >= 12 ? 'PM' : 'AM';
  const h12 = h24 === 0 ? 12 : h24 > 12 ? h24 - 12 : h24;
  return `${h12}:${String(m).padStart(2, '0')} ${ap}`;
}
function formatTodayLabel(date = new Date()) {
  const days = ['SUN','MON','TUE','WED','THU','FRI','SAT'];
  const months = ['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'];
  const dayName = days[date.getDay()] === 'SUN' ? 'SUNDAY' : days[date.getDay()];
  const isSunday = date.getDay() === 0;
  return `${dayName} · ${date.getDate()} ${months[date.getMonth()]} ${date.getFullYear()}${isSunday ? ' · 主日' : ''}`;
}

function ScreenToday({ user, family, members, events = [], congregations = [], checkIns, onCheckIn, onOpenAdmin, onOpenSettings, onSignOut, onAddPasskey, onManageFamily }) {
  const today = todayDateInSydney();
  const familyMembers = members.filter(m => family && m.family === family.id);
  const hasFamily = familyMembers.length > 0;
  const roster = hasFamily
    ? familyMembers
    : (user?.id ? [members.find(m => m.id === user.id) || user].filter(Boolean) : []);
  const checkedInIds = new Set(checkIns.filter(c => c.date === today).map(c => c.member_id));
  const rosterCheckedCount = roster.filter(m => checkedInIds.has(m.id)).length;
  const allRosterChecked = roster.length > 0 && rosterCheckedCount === roster.length;

  // Congregations represented across the roster
  const congList = congregations.length > 0 ? congregations : (D.congregations || []);
  const rosterCongregationIds = [...new Set(roster.map(m => m.congregation))];
  // Events scheduled for today (i.e., a Sunday recurring event or a special
  // event whose date matches today). The user's congregation must appear on
  // at least one of them for check-in to be available.
  const todayEvents = events.filter(e => e.date === today);
  const myCongregation = congList.find(s => s.id === user?.congregation);
  const myEventsToday = todayEvents.filter(e =>
    Array.isArray(e.congregations) && e.congregations.includes(user?.congregation));
  // Prefer open window → next upcoming → most recent past (mirrors backend priority)
  const myEventToday = myEventsToday.find(e => checkinWindow(e.time).open)
    || myEventsToday.find(e => checkinWindow(e.time).state === 'too-early')
    || myEventsToday[myEventsToday.length - 1]
    || null;
  const eventTime = myEventToday?.time || myCongregation?.defaultTime;
  const myWindow = myEventToday
    ? checkinWindow(eventTime)
    : { state: 'no-service', open: false };
  const checkinAllowed = myWindow.open;

  const [selectedForCheckin, setSelectedForCheckin] = useState(() =>
    new Set(roster.filter(m => !checkedInIds.has(m.id)).map(m => m.id))
  );
  const toggleForCheckin = (id) => {
    if (checkedInIds.has(id)) return;
    setSelectedForCheckin(s => { const n = new Set(s); n.has(id) ? n.delete(id) : n.add(id); return n; });
  };

  return (
    <div className="today-root" style={{ height: '100%', overflowY: 'auto', background: 'var(--bg-2)' }}>
      {/* Header — solid navy so the headline always reads cleanly */}
      <div className="today-hero" style={{
        background: 'linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f766e 120%)',
        color: '#fff',
        padding: '52px 24px 28px',
        position: 'relative',
      }}>
        <div className="today-hero-inner">
          <div className="mobile-only" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 28 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <img src="assets/bhcac-logo-red.jpg" style={{ width: 32, height: 32, borderRadius: '50%', objectFit: 'cover' }} />
              <div>
                <div style={{ fontSize: 13, fontWeight: 700, letterSpacing: '-0.01em' }}>BHCAC</div>
                <div style={{ fontSize: 10, color: 'rgba(255,255,255,0.65)', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Attendance</div>
              </div>
            </div>
            <CircleBtn icon="user" onClick={onOpenSettings} dark />
          </div>

          <Eyebrow color="var(--coral-400)" className="hero-eyebrow" style={{ marginBottom: 8 }}>
            {formatTodayLabel()}
          </Eyebrow>
          <h1 style={{ fontSize: 'clamp(28px, 3vw, 36px)', fontWeight: 700, letterSpacing: '-0.02em', margin: 0, lineHeight: 1.15, color: '#fff' }}>
            {greetingFor(new Date())}, {user.en.split(' ')[0]}.
          </h1>
          {user.zh && (
            <div style={{ fontFamily: "'Noto Sans TC', sans-serif", fontSize: 16, color: 'rgba(255,255,255,0.78)', marginTop: 6 }}>
              {user.zh}，主日平安。
            </div>
          )}
        </div>
      </div>

      <div className="today-body">
        <div className="today-main">

      {/* Family check-in card — sits cleanly below the hero (no negative margin
          so the eyebrow always renders fully visible on small viewports). */}
      <div className="floating-card" style={{ padding: '20px 16px 0', position: 'relative', zIndex: 5 }}>
        <Card padding={0} accent={allRosterChecked ? 'var(--brand-teal)' : 'var(--brand-coral)'} style={{ overflow: 'hidden' }}>
          <div style={{ padding: '20px 20px 16px' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16 }}>
              <div>
                <Eyebrow color={allRosterChecked ? 'var(--teal-700)' : 'var(--brand-coral)'} style={{ marginBottom: 6 }}>
                  {hasFamily ? `Your Family · 您的家庭` : 'Your Check-in · 您的簽到'}
                </Eyebrow>
                <h3 style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.015em', margin: 0 }}>
                  {hasFamily ? family.en : user.en}
                </h3>
                {hasFamily && (
                  <div style={{ fontSize: 13, color: 'var(--fg-3)', fontFamily: "'Noto Sans TC', sans-serif", marginTop: 2 }}>{family.zh}</div>
                )}
              </div>
              {allRosterChecked && (
                <Pill tone="success" icon="check">All Here · 全到</Pill>
              )}
            </div>

            {/* Member chips — tap to toggle who you're checking in */}
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 14 }}>
              {familyMembers.map(m => {
                const wasChecked = checkedInIds.has(m.id);
                const isSelected = selectedForCheckin.has(m.id);
                return (
                  <div key={m.id}
                    onClick={() => !wasChecked && toggleForCheckin(m.id)}
                    style={{
                      display: 'flex', alignItems: 'center', gap: 8,
                      padding: '6px 12px 6px 6px',
                      background: wasChecked ? 'rgba(13,148,136,0.08)' : isSelected ? 'rgba(244,63,94,0.06)' : 'var(--bg-2)',
                      borderRadius: 9999,
                      border: `1px solid ${wasChecked ? 'rgba(13,148,136,0.2)' : isSelected ? 'rgba(244,63,94,0.25)' : 'var(--border-subtle)'}`,
                      cursor: wasChecked ? 'default' : 'pointer',
                      opacity: !wasChecked && !isSelected ? 0.5 : 1,
                      transition: 'all 150ms',
                    }}>
                    <Avatar member={m} size={26} />
                    <div style={{ fontSize: 13, fontWeight: 600 }}>{m.en.split(' ')[0]}</div>
                    {wasChecked && <Icon name="check" size={13} color="var(--teal-700)" strokeWidth={3} style={{ marginRight: 2 }} />}
                    {!wasChecked && isSelected && <Icon name="check" size={13} color="var(--brand-coral)" strokeWidth={3} style={{ marginRight: 2 }} />}
                  </div>
                );
              })}
            </div>

            {!allRosterChecked ? (
              <Btn variant="primary" full size="lg"
                trailingIcon={(checkinAllowed && (!hasFamily || selectedForCheckin.size > 0)) ? 'arrow-right' : null}
                disabled={!checkinAllowed || (hasFamily && selectedForCheckin.size === 0)}
                onClick={() => {
                  if (!checkinAllowed) return;
                  onCheckIn(hasFamily ? [...selectedForCheckin] : undefined);
                }}>
                {checkinAllowed
                  ? hasFamily
                    ? `Check in ${selectedForCheckin.size} ${selectedForCheckin.size === 1 ? 'person' : 'people'} · 簽到`
                    : 'Check in · 簽到'
                  : myWindow.state === 'too-early'
                    ? `Opens at ${formatHM(myWindow.opensAt)} · 簽到尚未開放`
                    : myWindow.state === 'too-late'
                      ? 'Closed for today · 今日簽到已結束'
                      : myWindow.state === 'no-service'
                        ? 'No service today · 今日沒有聚會'
                        : 'Check in · 簽到'}
              </Btn>
            ) : (
              <Btn variant="outline" full size="md" icon="edit" onClick={() => onCheckIn()}>
                Edit check-in · 修改簽到
              </Btn>
            )}
            {/* Family management lives in Account → Family members now,
                NOT on the Today card. The card stays focused on check-in. */}
            {myWindow.state === 'too-early' && eventTime && (
              <div style={{ fontSize: 12, color: 'var(--fg-3)', textAlign: 'center', marginTop: 8, lineHeight: 1.5 }}>
                {myEventToday?.en || myCongregation?.en} starts at {eventTime}.<br/>
                Check-in opens 20 min before · 崇拜開始前 20 分鐘開放簽到
              </div>
            )}
            {myWindow.state === 'no-service' && (
              <div style={{ fontSize: 12, color: 'var(--fg-3)', textAlign: 'center', marginTop: 8, lineHeight: 1.5 }}>
                There's no service scheduled for today.<br/>
                Next: {(() => {
                  const next = events.filter(e => e.date > today && e.congregations?.includes(user?.congregation))[0];
                  return next ? `${next.en} · ${next.date} ${next.time || ''}` : 'TBA';
                })()}
              </div>
            )}
          </div>

          {/* Congregation times row */}
          {rosterCongregationIds.length > 0 && (
            <div style={{
              background: 'var(--bg-2)', padding: '12px 20px',
              display: 'flex', flexDirection: 'column', gap: 8,
              borderTop: '1px solid var(--border-subtle)',
            }}>
              {rosterCongregationIds.map(cid => {
                const s = congList.find(x => x.id === cid);
                if (!s) return null;
                const congEventsToday = todayEvents.filter(e => Array.isArray(e.congregations) && e.congregations.includes(cid));
                const congEventToday = congEventsToday.find(e => checkinWindow(e.time).open)
                  || congEventsToday.find(e => checkinWindow(e.time).state === 'too-early')
                  || congEventsToday[congEventsToday.length - 1]
                  || null;
                const displayTime = congEventToday?.time || s.defaultTime;
                const displayLabel = congEventToday?.en || s.en;
                const displayZh = congEventToday?.zh || s.zh;
                const w = checkinWindow(displayTime);
                return (
                  <div key={cid} style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 13, flexWrap: 'wrap' }}>
                    <Icon name="clock" size={14} color={`var(--brand-${s.accent})`} />
                    <span style={{ fontWeight: 600 }}>{displayTime}</span>
                    <span style={{ color: 'var(--fg-3)' }}>·</span>
                    <span>{displayLabel}</span>
                    {displayZh && <span style={{ color: 'var(--fg-3)', fontFamily: "'Noto Sans TC', sans-serif" }}>· {displayZh}</span>}
                    {w.state === 'open' && <Pill tone="success" style={{ fontSize: 9, padding: '2px 7px' }}>OPEN</Pill>}
                  </div>
                );
              })}
            </div>
          )}
        </Card>
      </div>
        </div>{/* /.today-main */}
        <div className="today-side">

      {/* This week's events */}
      {events.length > 0 && (
        <div style={{ padding: '32px 16px 16px' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 12, padding: '0 4px' }}>
            <div>
              <Eyebrow style={{ marginBottom: 4 }}>This Week · 本週</Eyebrow>
              <h2 style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.015em', margin: 0 }}>Special events</h2>
            </div>
          </div>
          <div className="events-strip" style={{ display: 'flex', gap: 12, overflowX: 'auto', padding: '4px 4px 12px', margin: '0 -4px', scrollSnapType: 'x mandatory' }}>
            {events.slice(0, 3).map((e, i) => (
              <EventCard key={e.id || i} event={e}
                accent={['var(--brand-coral)', 'var(--brand-teal)', 'var(--brand-navy)'][i % 3]} />
            ))}
          </div>
        </div>
      )}

      {/* History strip — real data, hidden when there's nothing yet */}
      {(() => {
        const recent = computeRecentSundays(user, checkIns);
        const hasAnyHistory = recent.some(s => s.here);
        if (!hasAnyHistory) return null;
        const past4 = recent.slice(0, 4);
        const attended = past4.filter(s => s.here).length;
        return (
          <div style={{ padding: '8px 16px 24px' }}>
            <div style={{ marginBottom: 12, padding: '0 4px' }}>
              <Eyebrow style={{ marginBottom: 4 }}>Your Journey · 您的旅程</Eyebrow>
              <h2 style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.015em', margin: 0 }}>Recent Sundays</h2>
            </div>
            <Card padding={16}>
              <div style={{ display: 'flex', justifyContent: 'space-around', alignItems: 'flex-end', height: 70, marginBottom: 12 }}>
                {recent.map((w, i) => (
                  <div key={w.date} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, flex: 1 }}>
                    <div style={{
                      width: 36, height: w.here ? 50 : 14,
                      borderRadius: 6,
                      background: w.today ? 'repeating-linear-gradient(45deg, var(--coral-100), var(--coral-100) 4px, #fff 4px, #fff 8px)' :
                                  w.here ? 'var(--brand-coral)' : 'var(--border-subtle)',
                      border: w.today ? '1.5px dashed var(--brand-coral)' : 'none',
                    }} />
                    <div style={{ fontSize: 10, fontWeight: 600, color: w.today ? 'var(--brand-coral)' : 'var(--fg-3)', letterSpacing: '0.02em' }}>
                      {w.label}
                    </div>
                  </div>
                ))}
              </div>
              <div style={{ fontSize: 13, color: 'var(--fg-2)', textAlign: 'center', borderTop: '1px solid var(--border-subtle)', paddingTop: 12 }}>
                <span style={{ fontWeight: 600, color: 'var(--brand-navy)' }}>{attended} of {past4.length} Sundays</span> attended this past month · 過去{past4.length}週出席{attended}次
              </div>
            </Card>
          </div>
        );
      })()}

      {/* Footer admin link (if user is admin) */}
      {user.isAdmin && (
        <div style={{ padding: '0 16px 24px' }}>
          <Card padding={16} interactive onClick={onOpenAdmin} style={{ background: 'var(--brand-navy)', color: '#fff', border: 'none' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <div style={{ width: 40, height: 40, borderRadius: 10, background: 'rgba(244,63,94,0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Icon name="settings" size={20} color="#fff" />
              </div>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 14, fontWeight: 700 }}>Admin tools · 管理員工具</div>
                <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.65)' }}>Members, events, reports</div>
              </div>
              <Icon name="chevron-right" size={18} color="rgba(255,255,255,0.5)" />
            </div>
          </Card>
        </div>
      )}

      <div style={{ height: 40 }} />
        </div>{/* /.today-side */}
      </div>{/* /.today-body */}
    </div>
  );
}

function CircleBtn({ icon, onClick, dark, badge }) {
  return (
    <button onClick={onClick} style={{
      width: 40, height: 40, borderRadius: '50%',
      background: dark ? 'rgba(255,255,255,0.12)' : 'var(--bg-1)',
      border: dark ? '1px solid rgba(255,255,255,0.2)' : '1px solid var(--border-subtle)',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      cursor: 'pointer', position: 'relative',
      backdropFilter: dark ? 'blur(8px)' : 'none',
    }}>
      <Icon name={icon} size={18} color={dark ? '#fff' : 'var(--brand-navy)'} />
      {badge != null && (
        <div style={{
          position: 'absolute', top: -2, right: -2,
          background: 'var(--brand-coral)', color: '#fff',
          fontSize: 9, fontWeight: 700, padding: '2px 5px',
          borderRadius: 9999, minWidth: 16, height: 16,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          boxShadow: '0 0 0 2px var(--brand-navy)',
        }}>{badge}</div>
      )}
    </button>
  );
}

function EventCard({ event, accent, image, registered }) {
  if (!event) return null;
  const date = event.date || '';
  const month = date.slice(5, 7);
  const day = date.slice(8, 10);
  const monthLabel = ({ '01':'JAN','02':'FEB','03':'MAR','04':'APR','05':'MAY','06':'JUN','07':'JUL','08':'AUG','09':'SEP','10':'OCT','11':'NOV','12':'DEC' })[month] || '';
  const reg = event.registered || 0;
  const cap = event.capacity || 0;
  const pct = cap > 0 ? Math.min(100, (reg / cap) * 100) : 0;
  return (
    <div style={{
      flexShrink: 0, width: 220, scrollSnapAlign: 'start',
      borderRadius: 'var(--radius-xl)', overflow: 'hidden',
      background: 'var(--bg-1)',
      boxShadow: 'var(--shadow-md)',
      border: '1px solid var(--border-subtle)',
    }}>
      <div style={{
        height: 100,
        background: image ? `linear-gradient(180deg, transparent, rgba(15,23,42,0.5)), url("${image}")` : `linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 70%, white))`,
        backgroundSize: 'cover', backgroundPosition: 'center',
        position: 'relative',
      }}>
        <div style={{ position: 'absolute', top: 10, left: 10 }}>
          <Pill tone={registered ? 'success' : 'navy'} icon={registered ? 'check' : 'calendar'} style={{ background: 'rgba(255,255,255,0.95)' }}>
            {registered ? 'Registered' : (day && monthLabel ? `${day} ${monthLabel}` : 'Soon')}
          </Pill>
        </div>
      </div>
      <div style={{ padding: 14 }}>
        <div style={{ fontSize: 14, fontWeight: 700, lineHeight: 1.2, letterSpacing: '-0.01em' }}>{event.en || 'Event'}</div>
        {event.zh && <div style={{ fontSize: 12, color: 'var(--fg-3)', fontFamily: "'Noto Sans TC', sans-serif", marginTop: 2 }}>{event.zh}</div>}
        {event.time && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 4, marginTop: 6, fontSize: 12, fontWeight: 600, color: 'var(--brand-navy)' }}>
            <Icon name="clock" size={12} color="var(--fg-3)" />
            <span>{event.time}</span>
          </div>
        )}
        {cap > 0 && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 10, fontSize: 11, color: 'var(--fg-3)' }}>
            <Icon name="users" size={12} />
            <span>{reg}/{cap}</span>
            <div style={{ flex: 1, height: 4, background: 'var(--border-subtle)', borderRadius: 4, marginLeft: 4 }}>
              <div style={{ width: `${pct}%`, height: '100%', background: accent, borderRadius: 4 }}/>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Screen 3: CHECK-IN — pick who's here
// ─────────────────────────────────────────────────────────────
function ScreenCheckIn({ user, family, members, congregations = [], events = [], checkIns, onConfirm, onBack }) {
  const today = todayDateInSydney();
  // Roster = the family members if family exists, otherwise just the user.
  // Make sure the user themselves is always present (handles solo accounts).
  const rosterRaw = family
    ? members.filter(m => m.family === family.id)
    : [];
  const userMember = members.find(m => m.id === user?.id) || (user?.id ? user : null);
  const roster = userMember && !rosterRaw.some(m => m.id === userMember.id)
    ? [userMember, ...rosterRaw]
    : rosterRaw;
  const hasFamily = !!family && rosterRaw.length > 0;

  const checkedInIds = new Set(checkIns.filter(c => c.date === today).map(c => c.member_id));
  const [selected, setSelected] = useState(() => new Set(roster.filter(m => !checkedInIds.has(m.id)).map(m => m.id)));
  const congList = congregations.length > 0 ? congregations : (D.congregations || []);

  const toggle = (id) => {
    const s = new Set(selected);
    if (s.has(id)) s.delete(id); else s.add(id);
    setSelected(s);
  };

  const rosterLabel = hasFamily
    ? `${family.en}${family.zh ? ' · ' + family.zh : ''}`
    : 'Just me · 您自己';

  const getEventTimeForCong = (congId) => {
    const evs = events.filter(e => e.date === today && Array.isArray(e.congregations) && e.congregations.includes(congId));
    const ev = evs.find(e => checkinWindow(e.time).open)
      || evs.find(e => checkinWindow(e.time).state === 'too-early')
      || evs[evs.length - 1];
    return ev?.time || null;
  };

  // Sunday school note: only for the regular cantonese 11am service
  const regularCantoneseId = `e-regular-cantonese-${today}`;
  const showSundaySchoolNote = roster.some(m => m.congregation === 'cantonese')
    && events.some(e => e.id === regularCantoneseId);

  return (
    <div className="focused-screen" style={{ height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--bg-2)' }}>
      {/* Header */}
      <div style={{ padding: '60px 20px 16px', background: 'var(--bg-1)', borderBottom: '1px solid var(--border-subtle)' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
          <CircleBtn icon="chevron-left" onClick={onBack} />
          <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--fg-3)' }}>Step 1 of 2</div>
          <div style={{ width: 40 }} />
        </div>
        <Eyebrow style={{ marginBottom: 6 }}>Check In · 簽到</Eyebrow>
        <h1 style={{ fontSize: 26, fontWeight: 700, letterSpacing: '-0.02em', margin: 0, lineHeight: 1.15 }}>Who's here today?</h1>
        <div style={{ fontFamily: "'Noto Sans TC', sans-serif", fontSize: 16, color: 'var(--fg-3)', marginTop: 4 }}>誰來了？</div>
      </div>

      {/* List */}
      <div style={{ flex: 1, overflowY: 'auto', padding: '20px 16px' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 12, padding: '0 4px' }}>
          <Eyebrow color="var(--brand-teal)">{rosterLabel}</Eyebrow>
          {roster.length > 1 && (
            <button
              onClick={() => setSelected(new Set(roster.map(m => m.id)))}
              style={{ fontSize: 12, fontWeight: 600, color: 'var(--brand-teal)', background: 'transparent', border: 'none', cursor: 'pointer' }}>
              Select all
            </button>
          )}
        </div>

        <Card padding={0}>
          {roster.map((m, i) => {
            const wasChecked = checkedInIds.has(m.id);
            const isSelected = selected.has(m.id);
            const cong = congList.find(s => s.id === m.congregation);
            return (
              <div key={m.id}
                onClick={() => !wasChecked && toggle(m.id)}
                style={{
                  display: 'flex', alignItems: 'center', gap: 14,
                  padding: '14px 18px',
                  borderBottom: i < roster.length - 1 ? '1px solid var(--border-subtle)' : 'none',
                  cursor: wasChecked ? 'default' : 'pointer',
                  background: isSelected && !wasChecked ? 'rgba(244,63,94,0.04)' : 'transparent',
                  opacity: wasChecked ? 0.6 : 1,
                }}>
                <Avatar member={m} size={44} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 15, fontWeight: 700, letterSpacing: '-0.01em' }}>{m.en}</div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, color: 'var(--fg-3)', marginTop: 2 }}>
                    {m.zh && <><span style={{ fontFamily: "'Noto Sans TC', sans-serif" }}>{m.zh}</span><span>·</span></>}
                    <span>{cong ? `${cong.en} · ${getEventTimeForCong(m.congregation) || cong.defaultTime}` : '—'}</span>
                  </div>
                </div>
                <div style={{
                  width: 28, height: 28, borderRadius: '50%',
                  background: wasChecked ? 'var(--brand-teal)' : isSelected ? 'var(--brand-coral)' : 'transparent',
                  border: wasChecked ? 'none' : isSelected ? 'none' : '2px solid var(--border-strong)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  transition: 'all 200ms var(--ease-out)',
                }}>
                  {(isSelected || wasChecked) && <Icon name="check" size={16} color="#fff" strokeWidth={3.5} />}
                </div>
              </div>
            );
          })}
        </Card>

        {/* Add other person */}
        <button style={{
          marginTop: 12, width: '100%',
          background: 'transparent', border: '1.5px dashed var(--border-strong)',
          borderRadius: 'var(--radius-xl)', padding: '14px',
          display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
          fontSize: 14, fontWeight: 600, color: 'var(--brand-navy)',
          cursor: 'pointer',
        }}>
          <Icon name="plus" size={16} /> Add someone with us · 加入新朋友
        </button>

        {/* Sunday school note — only for regular Cantonese 11am service */}
        {showSundaySchoolNote && (
          <div style={{ marginTop: 20, padding: '14px 16px', background: 'var(--brand-warm)', borderRadius: 'var(--radius-lg)', display: 'flex', gap: 10 }}>
            <Icon name="heart" size={16} color="var(--brand-coral)" style={{ marginTop: 2, flexShrink: 0 }} />
            <div style={{ fontSize: 12.5, color: '#7c2d12', lineHeight: 1.45 }}>
              Children's Sunday School at 11:00 takes place upstairs. Drop-off opens 10 minutes early.
              <span style={{ display: 'block', fontFamily: "'Noto Sans TC', sans-serif", color: '#9a3412', marginTop: 2 }}>兒童主日學在樓上，11:00 開始；提前 10 分鐘可送到。</span>
            </div>
          </div>
        )}
      </div>

      {/* Sticky CTA */}
      <div style={{ padding: '12px 16px 28px', background: 'var(--bg-1)', borderTop: '1px solid var(--border-subtle)', boxShadow: '0 -10px 20px rgba(15,23,42,0.04)' }}>
        <Btn variant="primary" full size="lg" trailingIcon="arrow-right"
          disabled={selected.size === 0}
          onClick={() => onConfirm([...selected])}>
          Check in {selected.size} {selected.size === 1 ? 'person' : 'people'} · 確認簽到
        </Btn>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Screen 4: CONFIRMATION
// ─────────────────────────────────────────────────────────────
function ScreenConfirm({ family, members, justCheckedIds, onDone }) {
  const justChecked = members.filter(m => justCheckedIds.includes(m.id));
  const kids = justChecked.filter(m => m.age < 13);

  return (
    <div className="focused-screen" style={{ height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--bg-2)' }}>
      {/* Top hero */}
      <div style={{
        padding: '70px 24px 40px', textAlign: 'center',
        background: 'linear-gradient(180deg, var(--brand-teal) 0%, #0a7569 100%)',
        color: '#fff', position: 'relative', overflow: 'hidden',
      }}>
        <div style={{
          position: 'absolute', inset: 0,
          backgroundImage: 'radial-gradient(circle at 30% 20%, rgba(255,255,255,0.15), transparent 50%), radial-gradient(circle at 70% 80%, rgba(255,255,255,0.1), transparent 50%)',
        }} />
        <div style={{ position: 'relative', zIndex: 2 }}>
          <div style={{
            width: 80, height: 80, borderRadius: '50%',
            background: 'rgba(255,255,255,0.18)', backdropFilter: 'blur(8px)',
            border: '1px solid rgba(255,255,255,0.3)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            margin: '0 auto 20px',
          }}>
            <Icon name="check" size={42} strokeWidth={3} color="#fff" />
          </div>
          <h1 style={{ fontSize: 32, fontWeight: 700, letterSpacing: '-0.02em', margin: 0, lineHeight: 1.1 }}>
            You're all set.
          </h1>
          <div style={{ fontFamily: "'Noto Sans TC', sans-serif", fontSize: 22, color: 'rgba(255,255,255,0.9)', marginTop: 8, fontWeight: 600 }}>
            簽到完成。
          </div>
          <p style={{ fontSize: 14, color: 'rgba(255,255,255,0.8)', marginTop: 14, maxWidth: 280, marginLeft: 'auto', marginRight: 'auto', lineHeight: 1.5 }}>
            Welcome, {family.en}. We're so glad you're here today.
          </p>
        </div>
      </div>

      {/* Summary card */}
      <div style={{ padding: '0 16px', marginTop: -20, position: 'relative', zIndex: 5 }}>
        <Card padding={0}>
          <div style={{ padding: 18 }}>
            <Eyebrow style={{ marginBottom: 10 }}>Checked in · 已簽到</Eyebrow>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
              {justChecked.map(m => {
                const s = D.congregations.find(x => x.id === m.congregation);
                return (
                  <div key={m.id} style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                    <Avatar member={m} size={36} ring ringColor="var(--brand-teal)" />
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 14, fontWeight: 700 }}>{m.en} <span style={{ fontWeight: 500, color: 'var(--fg-3)', fontFamily: "'Noto Sans TC', sans-serif" }}>· {m.zh}</span></div>
                      <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>{s ? `${s.en} · ${s.defaultTime}` : ''}</div>
                    </div>
                    <Icon name="check" size={18} color="var(--brand-teal)" strokeWidth={3} />
                  </div>
                );
              })}
            </div>
          </div>

          {/* Kids name-tag printout teaser */}
          {kids.length > 0 && (
            <div style={{
              borderTop: '1px solid var(--border-subtle)',
              padding: '14px 18px',
              background: 'var(--brand-warm)',
              display: 'flex', alignItems: 'center', gap: 12,
            }}>
              <div style={{ width: 36, height: 36, borderRadius: 8, background: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Icon name="sticker" size={18} color="var(--brand-coral)" />
              </div>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 13, fontWeight: 700, color: '#7c2d12' }}>Pick up name stickers at the kids' table</div>
                <div style={{ fontSize: 12, color: '#9a3412', fontFamily: "'Noto Sans TC', sans-serif" }}>請到兒童桌領取名牌貼紙</div>
              </div>
            </div>
          )}
        </Card>
      </div>

      {/* Verse for the day */}
      <div style={{ padding: '24px 24px 16px', textAlign: 'center', flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
        <Eyebrow style={{ marginBottom: 12 }}>Verse for today · 今日經文</Eyebrow>
        <div style={{
          fontFamily: "'Merriweather', serif", fontStyle: 'italic',
          fontSize: 18, color: 'var(--fg-2)', lineHeight: 1.45,
          maxWidth: 320, margin: '0 auto',
        }}>
          "Ask and it will be given to you; seek and you will find."
        </div>
        <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--fg-3)', marginTop: 10, letterSpacing: '0.05em' }}>
          MATTHEW 7:7 · 馬太福音 7:7
        </div>
      </div>

      {/* Bottom actions */}
      <div style={{ padding: '12px 16px 28px', background: 'var(--bg-1)', borderTop: '1px solid var(--border-subtle)' }}>
        <Btn variant="primary" full size="lg" onClick={onDone}>Done · 完成</Btn>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Screen 5: SETTINGS — change password, manage passkeys, sign out
// ─────────────────────────────────────────────────────────────
function ScreenSettings({ user, onBack, onLogout, onAddPasskey, onChangePassword, onManageFamily }) {
  const [open, setOpen] = useState(null); // 'password' | 'passkey' | null
  const [current, setCurrent] = useState('');
  const [next, setNext] = useState('');
  const [confirm, setConfirm] = useState('');
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState(null);
  const [success, setSuccess] = useState(null);
  const supportsPasskey = !!(window.BHCAC_AUTH?.browserSupportsPasskeys?.());

  const submitPw = async (e) => {
    e.preventDefault();
    setError(null); setSuccess(null);
    if (next.length < 8) { setError('New password must be at least 8 characters.'); return; }
    if (next !== confirm) { setError('New passwords do not match.'); return; }
    setSubmitting(true);
    try {
      await onChangePassword({ currentPassword: current, newPassword: next });
      setSuccess('Password updated · 密碼已更新');
      setCurrent(''); setNext(''); setConfirm('');
      setOpen(null);
    } catch (err) {
      setError(err.message);
    } finally {
      setSubmitting(false);
    }
  };

  const inputStyle = {
    width: '100%', boxSizing: 'border-box',
    padding: '12px 14px', borderRadius: 12,
    border: '1px solid var(--border-strong)',
    background: 'var(--bg-1)', fontSize: 15, fontFamily: 'Inter, sans-serif',
    outline: 'none',
  };

  return (
    <div className="focused-screen" style={{ height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--bg-2)' }}>
      <div style={{ padding: '60px 20px 18px', background: 'var(--bg-1)', borderBottom: '1px solid var(--border-subtle)' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
          <CircleBtn icon="chevron-left" onClick={onBack} />
          <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--fg-3)' }}>Account · 帳戶</div>
          <div style={{ width: 40 }} />
        </div>
        <h1 style={{ fontSize: 24, fontWeight: 700, letterSpacing: '-0.02em', margin: 0 }}>{user.en}</h1>
        <div style={{ fontSize: 13, color: 'var(--fg-3)', marginTop: 4 }}>
          {user.email} {user.role === 'superadmin' && <Pill tone="coral" style={{ marginLeft: 8, fontSize: 9, padding: '2px 8px' }}>SUPERADMIN</Pill>}
        </div>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', padding: '18px 16px' }}>
        {success && (
          <div style={{ padding: '10px 14px', marginBottom: 14, background: 'rgba(13,148,136,0.1)', borderRadius: 10, color: 'var(--teal-700)', fontSize: 13, fontWeight: 600 }}>
            {success}
          </div>
        )}

        {/* Family members — page link to the dedicated screen for adding /
            editing the people on your roster. Same screen the inviter and
            spouse share, so updates are visible to both managers. */}
        {onManageFamily && (
          <Card padding={0} style={{ marginBottom: 12 }}>
            <button onClick={onManageFamily} style={rowBtnStyle(false)}>
              <Icon name="users" size={18} color="var(--brand-coral)" />
              <div style={{ flex: 1, textAlign: 'left' }}>
                <div style={{ fontSize: 14, fontWeight: 600 }}>Family members · 家庭成員</div>
                <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>Add or edit the people on your roster</div>
              </div>
              <Icon name="chevron-right" size={16} color="var(--fg-3)" />
            </button>
          </Card>
        )}

        {/* Family / Co-managers — visible to every signed-in user. Individual
            accounts get auto-promoted to a family the moment they invite a
            spouse, so this entry point belongs at the top of Account. */}
        <CoManagerCard userEmail={user.email} userFamilyId={user.familyId} />

        <Card padding={0} style={{ marginBottom: 12 }}>
          <button onClick={() => { setOpen(open === 'password' ? null : 'password'); setError(null); setSuccess(null); }}
            style={rowBtnStyle(open === 'password')}>
            <Icon name="key" size={18} color="var(--brand-navy)" />
            <div style={{ flex: 1, textAlign: 'left' }}>
              <div style={{ fontSize: 14, fontWeight: 600 }}>Change password · 更改密碼</div>
              <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>{user.hasPassword ? 'Update your sign-in password' : 'Set a sign-in password (you currently use passkey only)'}</div>
            </div>
            <Icon name={open === 'password' ? 'chevron-down' : 'chevron-right'} size={16} color="var(--fg-3)" />
          </button>
          {open === 'password' && (
            <form onSubmit={submitPw} style={{ padding: 16, borderTop: '1px solid var(--border-subtle)', display: 'flex', flexDirection: 'column', gap: 10 }}>
              {user.hasPassword && (
                <input style={inputStyle} type="password" placeholder="Current password · 現有密碼"
                  value={current} onChange={e => setCurrent(e.target.value)} required autoComplete="current-password" />
              )}
              <input style={inputStyle} type="password" placeholder="New password · 新密碼 (8+ characters)"
                value={next} onChange={e => setNext(e.target.value)} required autoComplete="new-password" minLength={8} />
              <input style={inputStyle} type="password" placeholder="Confirm new password · 確認新密碼"
                value={confirm} onChange={e => setConfirm(e.target.value)} required autoComplete="new-password" />
              {error && (
                <div style={{ fontSize: 13, color: 'var(--coral-700)', background: 'var(--coral-100)', padding: '8px 12px', borderRadius: 8 }}>
                  {error}
                </div>
              )}
              <Btn variant="primary" type="submit" full size="md" disabled={submitting}>
                {submitting ? 'Updating…' : 'Update password · 儲存'}
              </Btn>
            </form>
          )}
        </Card>

        <Card padding={0} style={{ marginBottom: 12 }}>
          <div style={{ ...rowBtnStyle(false), cursor: 'default' }}>
            <Icon name="fingerprint" size={18} color="var(--brand-teal)" />
            <div style={{ flex: 1, textAlign: 'left' }}>
              <div style={{ fontSize: 14, fontWeight: 600 }}>Passkeys · 通行密鑰</div>
              <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>
                {user.passkeyCount > 0
                  ? `${user.passkeyCount} passkey${user.passkeyCount === 1 ? '' : 's'} registered on your devices`
                  : 'Skip passwords by adding a passkey to this device'}
              </div>
            </div>
            {supportsPasskey && (
              <button onClick={onAddPasskey} style={{
                padding: '6px 12px', borderRadius: 9999,
                background: 'var(--brand-teal)', color: '#fff',
                border: 'none', fontSize: 12, fontWeight: 700, cursor: 'pointer',
              }}>+ Add</button>
            )}
          </div>
        </Card>

        <button onClick={onLogout} style={{
          width: '100%', marginTop: 18, padding: '14px',
          background: 'var(--bg-1)', color: 'var(--coral-700)',
          border: '1px solid var(--coral-100)', borderRadius: 12,
          fontSize: 14, fontWeight: 700, cursor: 'pointer',
          fontFamily: 'Inter, sans-serif',
          display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        }}>
          <Icon name="log-out" size={16} />
          Sign out · 登出
        </button>
      </div>
    </div>
  );
}

function rowBtnStyle(active) {
  return {
    width: '100%',
    display: 'flex', alignItems: 'center', gap: 12,
    padding: '14px 16px',
    background: 'transparent', border: 'none',
    fontFamily: 'inherit', cursor: 'pointer',
    color: 'var(--fg-1)',
  };
}

// ─────────────────────────────────────────────────────────────
// Screen: FAMILY MEMBERS — manage everyone in your family.
// Reachable from Today via "Manage family · 管理家庭".
// Both managing users (typically father+mother) see + edit the same list.
// ─────────────────────────────────────────────────────────────
function ScreenFamilyMembers({ onBack, congregations = [], showToast }) {
  const congList = congregations.length > 0 ? congregations : (D.congregations || []);
  const [members, setMembers] = useState(null); // null = loading
  const [editing, setEditing] = useState(null); // member object or 'new'
  const [error, setError] = useState(null);

  const refresh = async () => {
    setError(null);
    try {
      const list = await window.BHCAC_API.listFamilyMembers();
      setMembers(Array.isArray(list) ? list : (list?.members || []));
    } catch (err) {
      setError(err.message);
      setMembers([]);
    }
  };

  useEffect(() => { refresh(); }, []);

  const handleSave = async (form) => {
    if (editing && editing !== 'new' && editing.id) {
      const updated = await window.BHCAC_API.updateFamilyMember(editing.id, form);
      // Backend returns the bare member object.
      const m = updated?.member || updated || {};
      setMembers(ms => (ms || []).map(x => x.id === editing.id ? { ...x, ...m } : x));
      showToast?.(`Updated ${m.en || form.en} · 已更新`);
    } else {
      const created = await window.BHCAC_API.addFamilyMember(form);
      const m = created?.member || created || {};
      setMembers(ms => [...(ms || []), m]);
      showToast?.(`Added ${m.en || form.en} · 已加入`);
    }
    setEditing(null);
  };

  const handleDelete = async (m) => {
    if (!window.confirm(`Remove ${m.en} from your family? · 確定移除？`)) return;
    try {
      await window.BHCAC_API.deleteFamilyMember(m.id);
      setMembers(ms => (ms || []).filter(x => x.id !== m.id));
      showToast?.(`${m.en} removed · 已移除`);
    } catch (err) {
      showToast?.(`Delete failed: ${err.message}`, 'error');
    }
  };

  const list = members || [];

  return (
    <div className="focused-screen" style={{ height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--bg-2)' }}>
      <div style={{ padding: '60px 20px 18px', background: 'var(--bg-1)', borderBottom: '1px solid var(--border-subtle)' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
          <CircleBtn icon="chevron-left" onClick={onBack} />
          <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--fg-3)' }}>Family · 家庭</div>
          <div style={{ width: 40 }} />
        </div>
        <h1 style={{ fontSize: 24, fontWeight: 700, letterSpacing: '-0.02em', margin: 0 }}>Your family</h1>
        <div style={{ fontFamily: "'Noto Sans TC', sans-serif", fontSize: 14, color: 'var(--fg-3)', marginTop: 4 }}>
          您的家庭成員 — 任何家人帳戶都可以新增或編輯
        </div>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', padding: '16px' }}>
        {error && (
          <div style={{ padding: '10px 12px', marginBottom: 12, background: 'var(--coral-100)', color: 'var(--coral-700)', borderRadius: 8, fontSize: 13 }}>
            {error}
          </div>
        )}

        {members === null ? (
          <div style={{ textAlign: 'center', padding: 40, color: 'var(--fg-3)', fontSize: 13 }}>Loading…</div>
        ) : list.length === 0 ? (
          <Card padding={20} style={{ textAlign: 'center' }}>
            <Icon name="users" size={28} color="var(--fg-3)" />
            <div style={{ fontSize: 14, fontWeight: 700, marginTop: 8 }}>No family members yet · 尚未有家人</div>
            <div style={{ fontSize: 12, color: 'var(--fg-3)', marginTop: 4 }}>
              Add a family member below to start checking them in together.
            </div>
          </Card>
        ) : (
          <Card padding={0}>
            {list.map((m, i) => {
              const cong = congList.find(c => c.id === m.congregation);
              return (
                <div key={m.id} style={{
                  display: 'flex', alignItems: 'center', gap: 12,
                  padding: '12px 16px',
                  borderBottom: i < list.length - 1 ? '1px solid var(--border-subtle)' : 'none',
                }}>
                  <Avatar member={m} size={40} />
                  <button onClick={() => setEditing(m)} style={{
                    flex: 1, minWidth: 0, textAlign: 'left',
                    background: 'transparent', border: 'none', cursor: 'pointer',
                    padding: 0, fontFamily: 'inherit',
                  }}>
                    <div style={{ fontSize: 14, fontWeight: 700, letterSpacing: '-0.01em', display: 'flex', gap: 6, alignItems: 'baseline' }}>
                      <span>{m.en}</span>
                      {m.zh && <span style={{ fontSize: 12, color: 'var(--fg-3)', fontFamily: "'Noto Sans TC', sans-serif", fontWeight: 500 }}>{m.zh}</span>}
                    </div>
                    <div style={{ display: 'flex', gap: 8, alignItems: 'center', fontSize: 11, color: 'var(--fg-3)', marginTop: 3, flexWrap: 'wrap' }}>
                      {m.role && <span>{m.role}</span>}
                      {cong && <Pill tone={cong.accent === 'coral' ? 'coral' : cong.accent === 'teal' ? 'teal' : 'navy'} style={{ fontSize: 9, padding: '2px 7px' }}>{cong.en}</Pill>}
                      <Pill tone={m.pref === 'paper' ? 'paper' : 'online'} style={{ fontSize: 9, padding: '2px 7px' }}>
                        {m.pref === 'paper' ? 'PAPER' : 'ONLINE'}
                      </Pill>
                    </div>
                  </button>
                  <button onClick={() => setEditing(m)} title="Edit" style={{
                    padding: '6px 8px', borderRadius: 9999,
                    background: 'transparent', color: 'var(--brand-navy)',
                    border: '1px solid var(--border-subtle)', cursor: 'pointer',
                  }}>
                    <Icon name="edit" size={13} />
                  </button>
                  <button onClick={() => handleDelete(m)} title="Remove" style={{
                    padding: '6px 8px', borderRadius: 9999,
                    background: 'transparent', color: 'var(--coral-700)',
                    border: '1px solid var(--coral-100)', cursor: 'pointer',
                  }}>
                    <Icon name="x" size={13} />
                  </button>
                </div>
              );
            })}
          </Card>
        )}

        <div style={{ marginTop: 14, padding: 14, background: 'var(--brand-warm)', borderRadius: 'var(--radius-lg)', display: 'flex', gap: 10 }}>
          <Icon name="sparkles" size={16} color="var(--brand-coral)" style={{ marginTop: 1, flexShrink: 0 }} />
          <div style={{ fontSize: 12, color: '#7c2d12', lineHeight: 1.5 }}>
            Both family-account users see and edit this list. Add your kids here so either parent can check them in.
            <span style={{ display: 'block', fontFamily: "'Noto Sans TC', sans-serif", color: '#9a3412', marginTop: 2 }}>
              兩位家庭管理人都能看到並編輯這名單，新增小朋友後任一位都可代為簽到。
            </span>
          </div>
        </div>
      </div>

      <div style={{ padding: '12px 16px 28px', background: 'var(--bg-1)', borderTop: '1px solid var(--border-subtle)' }}>
        <Btn variant="primary" full size="lg" icon="plus" onClick={() => setEditing('new')}>
          Add family member · 新增家人
        </Btn>
      </div>

      {editing && (
        <FamilyMemberFormModal
          member={editing === 'new' ? null : editing}
          congregations={congList}
          onCancel={() => setEditing(null)}
          onSave={handleSave} />
      )}
    </div>
  );
}

const FAMILY_ROLES = [
  { id: 'Father',      en: 'Father',      zh: '父親' },
  { id: 'Mother',      en: 'Mother',      zh: '母親' },
  { id: 'Son',         en: 'Son',         zh: '兒子' },
  { id: 'Daughter',    en: 'Daughter',    zh: '女兒' },
  { id: 'Grandfather', en: 'Grandfather', zh: '祖父' },
  { id: 'Grandmother', en: 'Grandmother', zh: '祖母' },
  { id: 'Other',       en: 'Other',       zh: '其他' },
];
const FAMILY_ROLE_IDS = new Set(FAMILY_ROLES.map(r => r.id));

function FamilyMemberFormModal({ member, congregations, onCancel, onSave }) {
  // Existing 'Member' / free-text values from before the dropdown landed are
  // mapped to 'Other' so the select renders something sensible.
  const initialRole = member?.role && FAMILY_ROLE_IDS.has(member.role)
    ? member.role
    : (member?.role ? 'Other' : '');
  const [form, setForm] = useState({
    en: member?.en || '',
    zh: member?.zh || '',
    role: initialRole,
    congregation: member?.congregation || (congregations[0]?.id || 'cantonese'),
    pref: member?.pref || 'online',
  });
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState(null);
  const update = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const submit = async (e) => {
    e?.preventDefault?.();
    setError(null);
    if (!form.en.trim()) { setError('Name is required.'); return; }
    setSaving(true);
    try {
      await onSave(form);
    } catch (err) {
      setError(err.message);
      setSaving(false);
    }
  };

  const inputStyle = {
    width: '100%', boxSizing: 'border-box',
    padding: '11px 14px', borderRadius: 12,
    border: '1px solid var(--border-strong)',
    background: 'var(--bg-1)', fontSize: 14, fontFamily: 'Inter, sans-serif',
    outline: 'none',
  };

  return (
    <div onClick={onCancel} style={{
      position: 'fixed', inset: 0, zIndex: 9000,
      background: 'rgba(15,23,42,0.55)',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: 16,
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        background: 'var(--bg-1)', borderRadius: 16,
        width: '100%', maxWidth: 460,
        maxHeight: '90vh', overflowY: 'auto',
        boxShadow: 'var(--shadow-2xl)',
      }}>
        <div style={{ padding: '18px 20px 14px', borderBottom: '1px solid var(--border-subtle)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <Eyebrow>{member ? 'Edit family member · 編輯' : 'Add family member · 新增'}</Eyebrow>
          <CircleBtn icon="x" onClick={onCancel} />
        </div>
        <form onSubmit={submit} style={{ padding: 18, display: 'flex', flexDirection: 'column', gap: 10 }}>
          <input style={inputStyle} type="text" placeholder="Name (EN) · 英文姓名"
            value={form.en} onChange={e => update('en', e.target.value)} required />
          <input style={inputStyle} type="text" placeholder="中文姓名 (optional)"
            value={form.zh} onChange={e => update('zh', e.target.value)} />
          <select
            style={{ ...inputStyle, appearance: 'auto' }}
            value={form.role}
            onChange={e => update('role', e.target.value)}>
            <option value="">Relationship · 關係 (optional)</option>
            {FAMILY_ROLES.map(r => (
              <option key={r.id} value={r.id}>{r.en} · {r.zh}</option>
            ))}
          </select>

          <div style={{ marginTop: 4 }}>
            <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--fg-3)', marginBottom: 6 }}>
              Congregation · 崇拜
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8 }}>
              {congregations.map(c => (
                <button key={c.id} type="button" onClick={() => update('congregation', c.id)} style={{
                  padding: '10px 8px', borderRadius: 12, textAlign: 'left',
                  background: form.congregation === c.id ? 'var(--bg-1)' : 'transparent',
                  border: '1.5px solid ' + (form.congregation === c.id ? `var(--brand-${c.accent})` : 'var(--border-subtle)'),
                  cursor: 'pointer', fontFamily: 'inherit',
                  boxShadow: form.congregation === c.id ? `0 0 0 3px color-mix(in oklab, var(--brand-${c.accent}) 18%, transparent)` : 'none',
                }}>
                  <div style={{ fontSize: 12, fontWeight: 700 }}>{c.en}</div>
                  <div style={{ fontSize: 10, color: 'var(--fg-3)', fontFamily: "'Noto Sans TC', sans-serif" }}>{c.zh}</div>
                </button>
              ))}
            </div>
          </div>

          <div style={{ marginTop: 4 }}>
            <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--fg-3)', marginBottom: 6 }}>
              Check-in preference · 簽到方式
            </div>
            <div style={{ display: 'flex', gap: 8 }}>
              {[
                { id: 'online', en: 'Online', zh: '線上' },
                { id: 'paper',  en: 'Paper',  zh: '紙本' },
              ].map(p => (
                <button key={p.id} type="button" onClick={() => update('pref', p.id)} style={{
                  flex: 1, padding: '10px 12px', borderRadius: 12,
                  background: form.pref === p.id ? 'var(--brand-warm)' : 'transparent',
                  border: '1.5px solid ' + (form.pref === p.id ? 'var(--brand-coral)' : 'var(--border-subtle)'),
                  fontSize: 13, fontWeight: 700, cursor: 'pointer', fontFamily: 'inherit',
                }}>{p.en} · <span style={{ fontFamily: "'Noto Sans TC', sans-serif", fontWeight: 500 }}>{p.zh}</span></button>
              ))}
            </div>
          </div>

          {error && <div style={{ fontSize: 12, color: 'var(--coral-700)', background: 'var(--coral-100)', padding: '8px 12px', borderRadius: 8 }}>{error}</div>}
          <div style={{ display: 'flex', gap: 8, marginTop: 6 }}>
            <Btn variant="outline" size="md" onClick={onCancel} type="button" style={{ flex: 1 }}>Cancel</Btn>
            <Btn variant="primary" size="md" type="submit" disabled={saving} style={{ flex: 2 }}>
              {saving ? 'Saving…' : (member ? 'Save · 儲存' : 'Add · 新增')}
            </Btn>
          </div>
        </form>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Screen: SET A NEW PASSWORD
// Used in two flows:
//   - Force-password-change (member.forcePasswordChange after admin temp reset)
//   - Public reset-password landing (?reset-password=<token>)
// Distinguish via the `mode` prop ('force' | 'reset').
//   - force: requires an active session, calls auth.changePassword
//            (server skips current-pw because forcePasswordChange === 1).
//   - reset: public, takes a token, calls auth.resetPassword.
// ─────────────────────────────────────────────────────────────
function ScreenSetPassword({ mode = 'force', token, onSuccess, onCancel }) {
  const [next, setNext] = useState('');
  const [confirm, setConfirm] = useState('');
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState(null);

  const submit = async (e) => {
    e?.preventDefault?.();
    setError(null);
    if (next.length < 8) { setError('Password must be at least 8 characters.'); return; }
    if (next !== confirm) { setError('Passwords do not match.'); return; }
    setSubmitting(true);
    try {
      if (mode === 'reset') {
        if (!token) throw new Error('Reset token missing.');
        await window.BHCAC_AUTH.resetPassword({ token, newPassword: next });
      } else {
        // Force-change: server skips current-pw check when force flag is true.
        await window.BHCAC_AUTH.changePassword({ newPassword: next });
      }
      await onSuccess?.();
    } catch (err) {
      setError(err.message);
      setSubmitting(false);
    }
  };

  const inputStyle = {
    width: '100%', boxSizing: 'border-box',
    padding: '13px 16px', borderRadius: 12,
    border: '1px solid var(--border-strong)',
    background: 'var(--bg-1)', fontSize: 15, fontFamily: 'Inter, sans-serif',
    outline: 'none',
  };

  const subtitle = mode === 'reset'
    ? 'You followed a reset link from a church admin. Choose a new password to continue.'
    : 'Your password was reset by an admin. Please choose a new one to continue.';

  return (
    <div className="focused-screen" style={{ height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--bg-2)' }}>
      <div style={{ padding: '60px 20px 18px', background: 'var(--bg-1)', borderBottom: '1px solid var(--border-subtle)' }}>
        <Eyebrow style={{ marginBottom: 6 }}>{mode === 'reset' ? 'Reset password · 重設密碼' : 'Required · 必須更改'}</Eyebrow>
        <h1 style={{ fontSize: 24, fontWeight: 700, letterSpacing: '-0.02em', margin: 0, lineHeight: 1.15 }}>
          Set a new password · 設定新密碼
        </h1>
        <div style={{ fontSize: 13, color: 'var(--fg-3)', marginTop: 8, lineHeight: 1.5 }}>
          {subtitle}
        </div>
      </div>
      <div style={{ flex: 1, overflowY: 'auto', padding: '24px 20px' }}>
        <form onSubmit={submit} style={{ display: 'flex', flexDirection: 'column', gap: 12, maxWidth: 420, margin: '0 auto' }}>
          <input style={inputStyle} type="password"
            placeholder="New password · 新密碼 (8+ characters)"
            value={next} onChange={e => setNext(e.target.value)}
            required autoComplete="new-password" minLength={8} autoFocus />
          <input style={inputStyle} type="password"
            placeholder="Confirm new password · 確認新密碼"
            value={confirm} onChange={e => setConfirm(e.target.value)}
            required autoComplete="new-password" />
          {error && (
            <div style={{ fontSize: 13, color: 'var(--coral-700)', background: 'var(--coral-100)', padding: '10px 12px', borderRadius: 10 }}>
              {error}
            </div>
          )}
          <Btn variant="primary" type="submit" size="lg" full disabled={submitting}>
            {submitting ? 'Saving…' : 'Save password · 儲存密碼'}
          </Btn>
          {mode === 'reset' && onCancel && (
            <button type="button" onClick={onCancel} style={{
              marginTop: 6, padding: 10, background: 'transparent', border: 'none',
              fontFamily: 'inherit', fontSize: 13, color: 'var(--fg-3)', cursor: 'pointer',
            }}>
              Cancel
            </button>
          )}
        </form>
      </div>
    </div>
  );
}

// Settings card: shared family management. Shows current co-managers
// (other accounts on the same family_id) + pending invites + a form to
// invite a spouse (or replace an outstanding one). Visible to every
// signed-in user — individual accounts see the same form and get
// auto-promoted to a family the moment they submit one.
function CoManagerCard({ userEmail, userFamilyId }) {
  const [state, setState] = useState(null); // { isFamily, coManagers, pendingInvites }
  const [email, setEmail] = useState('');
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState(null);
  const [success, setSuccess] = useState(null);

  const refresh = async () => {
    try {
      const r = await window.BHCAC_API.familyState();
      setState(r);
    } catch (err) {
      // 401 etc. — fall back to a "not family" view so the form still works.
      setState({ familyId: userFamilyId || null, isFamily: !!userFamilyId, coManagers: [], pendingInvites: [] });
    }
  };

  useEffect(() => { refresh(); }, []);

  const inputStyle = {
    width: '100%', boxSizing: 'border-box',
    padding: '12px 14px', borderRadius: 12,
    border: '1px solid var(--border-strong)',
    background: 'var(--bg-1)', fontSize: 14, fontFamily: 'Inter, sans-serif',
    outline: 'none',
  };

  const submitInvite = async (e) => {
    e?.preventDefault?.();
    setError(null); setSuccess(null);
    const target = email.trim().toLowerCase();
    if (!target || !target.includes('@')) { setError('Enter a valid email.'); return; }
    if (target === String(userEmail || '').toLowerCase()) { setError("You can't invite yourself."); return; }
    setSubmitting(true);
    try {
      const r = await window.BHCAC_API.inviteSpouse(target);
      if (r?.alreadyMember) {
        setSuccess('They already belong to your family · 已是家人');
      } else if (r?.emailSent === false) {
        // Resend rejected or timed out. Tell the inviter honestly so they
        // share the copy/share link below instead of waiting on a mail
        // that won't arrive.
        setError(`Email couldn't be delivered to ${target}. Use the copy/share link below to send it via WhatsApp/SMS. · 電郵未能送達，請使用下方連結分享。`);
      } else {
        setSuccess(`Invite sent to ${target} · 邀請已寄出`);
      }
      setEmail('');
      await refresh();
    } catch (err) {
      setError(err.message);
    } finally {
      setSubmitting(false);
    }
  };

  const revokeInvite = async (target) => {
    if (!window.confirm) {
      // confirm suppressed in some PWA contexts — proceed without prompting
    } else if (!window.confirm(`Cancel the invite to ${target}?`)) {
      return;
    }
    setError(null); setSuccess(null);
    try {
      await window.BHCAC_API.revokeSpouseInvite(target);
      setSuccess(`Invite to ${target} cancelled · 已取消`);
      await refresh();
    } catch (err) {
      setError(err.message);
    }
  };

  const isFamily = state?.isFamily ?? !!userFamilyId;
  const coManagers = state?.coManagers || [];
  const pendingInvites = state?.pendingInvites || [];
  // Existing pending invite (if any) — there's only ever one because the
  // invite endpoint revokes prior ones for the same email.
  const pending = pendingInvites[0];

  return (
    <Card padding={16} style={{ marginBottom: 12 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
        <Icon name="users" size={18} color="var(--brand-coral)" />
        <div>
          <div style={{ fontSize: 14, fontWeight: 700 }}>Family · 家庭</div>
          <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>
            {isFamily
              ? 'You and your spouse can both manage attendance · 您與配偶共同管理'
              : 'Invite your spouse so you both manage attendance · 邀請配偶共同管理'}
          </div>
        </div>
      </div>

      {/* Current co-managers — only shown when there's > 1 */}
      {isFamily && coManagers.length > 0 && (
        <div style={{ marginBottom: 12 }}>
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--fg-3)', marginBottom: 6 }}>
            Co-managers · 管理人 ({coManagers.length}/2)
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {coManagers.map(cm => (
              <div key={cm.id} style={{
                display: 'flex', alignItems: 'center', gap: 10,
                padding: '8px 12px', background: 'var(--bg-2)', borderRadius: 10,
              }}>
                <div style={{ fontSize: 13, fontWeight: 600, flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis' }}>
                  {cm.en || cm.email}
                  {cm.isYou && <span style={{ marginLeft: 6, fontSize: 11, color: 'var(--fg-3)' }}>(you)</span>}
                </div>
                <div style={{ fontSize: 11, color: 'var(--fg-3)', overflow: 'hidden', textOverflow: 'ellipsis', minWidth: 0 }}>
                  {cm.email}
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* Pending invite — including the copyable link, because email
          delivery is unreliable (spam folder, typos, etc.) and most
          users will get it to their spouse faster via WhatsApp/SMS. */}
      {pending && (
        <div style={{ marginBottom: 12, padding: 14, background: 'var(--brand-warm)', borderRadius: 10 }}>
          <div style={{ fontSize: 12, color: '#7c2d12', marginBottom: 6 }}>
            <strong>Pending invite · 待接受邀請</strong>
          </div>
          <div style={{ fontSize: 13, fontWeight: 600, color: '#7c2d12', marginBottom: 10 }}>
            {pending.email}
          </div>

          {pending.link && (
            <div style={{ marginBottom: 10 }}>
              <div style={{ fontSize: 11, color: '#7c2d12', marginBottom: 6, lineHeight: 1.45 }}>
                We emailed them a link, but emails sometimes get lost. <strong>The fastest way</strong> is to copy this link below and send it via WhatsApp/SMS:
              </div>
              <div style={{
                display: 'flex', gap: 6, alignItems: 'stretch',
                background: 'var(--bg-1)', border: '1px solid var(--coral-100)', borderRadius: 8,
                padding: '6px 8px',
              }}>
                <input readOnly value={pending.link}
                  onFocus={e => e.target.select()}
                  style={{
                    flex: 1, minWidth: 0,
                    background: 'transparent', border: 'none', outline: 'none',
                    fontSize: 11, fontFamily: 'ui-monospace, SFMono-Regular, monospace',
                    color: 'var(--fg-2)',
                  }} />
                <button type="button" onClick={async () => {
                  try {
                    if (navigator.clipboard?.writeText) {
                      await navigator.clipboard.writeText(pending.link);
                    } else {
                      // Fallback for older Safari
                      const ta = document.createElement('textarea');
                      ta.value = pending.link;
                      document.body.appendChild(ta); ta.select();
                      document.execCommand('copy');
                      document.body.removeChild(ta);
                    }
                    setSuccess('Link copied · 已複製');
                  } catch { setError('Could not copy. Long-press the link to select it.'); }
                }} style={{
                  background: 'var(--brand-coral)', color: '#fff',
                  border: 'none', borderRadius: 6, padding: '6px 12px',
                  fontSize: 12, fontWeight: 700, cursor: 'pointer', whiteSpace: 'nowrap',
                }}>Copy</button>
              </div>
              {/* Native share — if the browser supports it (most mobile do) */}
              {typeof navigator !== 'undefined' && navigator.share && (
                <button type="button" onClick={async () => {
                  try {
                    await navigator.share({
                      title: 'BHCAC Attendance — family invite',
                      text: 'Join my BHCAC Attendance family — sign in with this link:',
                      url: pending.link,
                    });
                  } catch { /* user cancelled */ }
                }} style={{
                  marginTop: 6, width: '100%',
                  background: 'transparent', color: 'var(--brand-coral)',
                  border: '1px solid var(--coral-100)', borderRadius: 8,
                  fontSize: 12, fontWeight: 600, padding: '6px 12px', cursor: 'pointer',
                }}>Share via… · 分享</button>
              )}
            </div>
          )}

          <button onClick={() => revokeInvite(pending.email)} style={{
            background: 'transparent', border: '1px solid var(--coral-100)',
            color: 'var(--coral-700)', borderRadius: 9999,
            fontSize: 12, fontWeight: 600,
            padding: '6px 12px', cursor: 'pointer',
          }}>
            Cancel invite · 取消邀請
          </button>
        </div>
      )}

      {/* Invite / re-invite form — always shown unless family is full */}
      {coManagers.length < 2 && (
        <form onSubmit={submitInvite} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--fg-3)' }}>
            {pending ? 'Send a new invite (replaces above)' : 'Invite your spouse · 邀請配偶'}
          </div>
          <input style={inputStyle} type="email"
            placeholder={pending ? `Different email than ${pending.email}` : 'Spouse email · 配偶電郵'}
            value={email} onChange={e => setEmail(e.target.value)} required />
          {error && (
            <div style={{ fontSize: 12, color: 'var(--coral-700)', background: 'var(--coral-100)', padding: '8px 12px', borderRadius: 8 }}>
              {error}
            </div>
          )}
          {success && (
            <div style={{ fontSize: 12, color: 'var(--teal-700)', background: 'rgba(13,148,136,0.1)', padding: '8px 12px', borderRadius: 8 }}>
              {success}
            </div>
          )}
          <Btn variant="primary" type="submit" size="md" full icon="mail" disabled={submitting}>
            {submitting ? 'Sending…' : (pending ? 'Resend with different email · 重新發送' : 'Send invite · 發送邀請')}
          </Btn>
        </form>
      )}

      {coManagers.length >= 2 && (
        <div style={{ fontSize: 12, color: 'var(--fg-3)', padding: '8px 12px', background: 'var(--bg-2)', borderRadius: 8 }}>
          Your family already has the maximum of 2 logins · 家庭已有最多 2 位管理人
        </div>
      )}

      <div style={{ fontSize: 11, color: 'var(--fg-3)', marginTop: 10, lineHeight: 1.5 }}>
        Invite link expires in 14 days. {isFamily ? `Family id: ${state?.familyId?.slice(0, 12) || ''}…` : 'A family will be created when you send the first invite.'}
      </div>
    </Card>
  );
}

Object.assign(window, {
  ScreenWelcome, ScreenToday, ScreenCheckIn, ScreenConfirm, ScreenSettings,
  ScreenFamilyMembers, ScreenSetPassword, CircleBtn,
});
