// ─────────────────────────────────────────────────────────────
// Invite a client — overlay sheet over a dimmed dashboard.
// Two-way contract:
//   1. Coach-initiated: this screen mints /coach_invites/{token} with
//      `invitedBy: 'coach'`, coach copies/shares the link with their client.
//      Client side (iOS Universal Link → claim) is a separate iOS task.
//   2. Client-initiated: instructions at the bottom — client opens the
//      LifeWheel app, generates an invite, sends the coach the link, coach
//      lands on Claim.html and accepts. Already wired end-to-end.
// ─────────────────────────────────────────────────────────────

const { useState: useS, useEffect: useE, useMemo: useM } = React;

// ── tiny atomic inputs (no UI primitive for these in ui.jsx) ──
function Field({ label, hint, error, children }) {
  const { c, fonts } = useLW();
  return (
    <label style={{ display: 'block' }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 6 }}>
        <span style={{ fontFamily: fonts.body, fontSize: 12, fontWeight: 600, letterSpacing: '0.08em', textTransform: 'uppercase', color: c.textTertiary }}>{label}</span>
        {hint && <span style={{ fontFamily: fonts.body, fontSize: 12, color: c.textTertiary, fontStyle: 'italic' }}>{hint}</span>}
      </div>
      {children}
      {error && <div style={{ marginTop: 6, fontSize: 12, color: c.error }}>{error}</div>}
    </label>
  );
}

function TextInput({ value, onChange, placeholder, type = 'text', autoFocus, multiline, rows = 3, style }) {
  const { c, fonts } = useLW();
  const [focus, setFocus] = useS(false);
  const Tag = multiline ? 'textarea' : 'input';
  const props = multiline ? { rows } : { type };
  return (
    <Tag
      {...props}
      value={value}
      onChange={(e) => onChange(e.target.value)}
      onFocus={() => setFocus(true)}
      onBlur={() => setFocus(false)}
      placeholder={placeholder}
      autoFocus={autoFocus}
      style={{
        width: '100%',
        background: c.bgSubtle,
        border: `1px solid ${focus ? c.borderStrong : c.borderSubtle}`,
        borderRadius: 10,
        padding: multiline ? '12px 14px' : '11px 14px',
        fontFamily: fonts.body,
        fontSize: 15,
        color: c.textPrimary,
        outline: 'none',
        resize: multiline ? 'vertical' : 'none',
        lineHeight: 1.5,
        transition: 'border-color 150ms ease',
        boxShadow: focus ? `0 0 0 3px ${hexToRgba(c.accent, 0.15)}` : 'none',
        boxSizing: 'border-box',
        ...style,
      }}
    />
  );
}

function WelcomeNoteField({ note, setNote, label, placeholder, cta }) {
  const { c, fonts } = useLW();
  const [showNote, setShowNote] = useS(false);
  if (!showNote) {
    return (
      <button onClick={() => setShowNote(true)} style={{
        alignSelf: 'flex-start',
        background: 'transparent', border: 'none', padding: '4px 0',
        color: c.accent, fontFamily: fonts.body, fontSize: 13, fontWeight: 600,
        cursor: 'pointer',
      }}>+ {cta}</button>
    );
  }
  return (
    <Field label={label} hint={`${note.length}/280`}>
      <TextInput value={note} onChange={(v) => setNote(v.slice(0, 280))} placeholder={placeholder} multiline rows={3} />
    </Field>
  );
}

// ── invite contract — defaults match coach_invite_data_contract memory ──
const DEFAULT_SCOPES = { wheel: true, habits: true, tasks: true, mood: true, journal: false, who5: false };
const INVITE_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days

function freshToken() {
  // 22-char URL-safe base64 of 16 random bytes.
  const arr = new Uint8Array(16);
  crypto.getRandomValues(arr);
  let bin = '';
  for (let i = 0; i < arr.length; i++) bin += String.fromCharCode(arr[i]);
  return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

function buildInviteUrl(token) {
  // Always use the production host — the link is meant to be pasted into
  // the iOS app on a different device, so embedding `localhost:8765` from a
  // dev server would 404 in iMessage previews and break the test flow.
  // The path itself is just a token transport; iOS extracts the trailing
  // segment regardless of host.
  return `https://lifewheel.us/coach-invite/${token}?from=coach`;
}

// ── direction toggle (top of the sheet) ──
function DirectionTabs({ value, onChange, t }) {
  const { c, fonts } = useLW();
  const tabs = [
    { id: 'send',     label: t('invite.tab_send') },
    { id: 'receive',  label: t('invite.tab_receive') },
  ];
  return (
    <div role="tablist" style={{
      display: 'inline-flex',
      background: c.bgSubtle,
      border: `1px solid ${c.borderSubtle}`,
      padding: 4,
      borderRadius: 12,
      gap: 2,
    }}>
      {tabs.map(tab => {
        const active = tab.id === value;
        return (
          <button
            key={tab.id}
            role="tab"
            onClick={() => onChange(tab.id)}
            style={{
              padding: '8px 14px',
              border: 'none',
              background: active ? c.card : 'transparent',
              color: active ? c.textPrimary : c.textSecondary,
              fontFamily: fonts.body, fontSize: 13, fontWeight: 600,
              borderRadius: 8, cursor: 'pointer',
              boxShadow: active ? `0 1px 0 ${c.borderSubtle}` : 'none',
              transition: 'all 120ms ease',
            }}
          >{tab.label}</button>
        );
      })}
    </div>
  );
}

// ── Coach-initiated: generate a token + show the link ──
function SendInvite({ coach, t, onCreated }) {
  const { c, fonts } = useLW();
  const [clientName, setClientName] = useS('');
  const [note, setNote] = useS('');
  const [creating, setCreating] = useS(false);
  const [created, setCreated] = useS(null); // { token, url }
  const [error, setError] = useS(null);

  const generate = async () => {
    if (creating || !coach) return;
    setCreating(true); setError(null);
    try {
      const token = freshToken();
      const now = Date.now();
      const coachName = (coach.profile && coach.profile.displayName) || coach.email || 'Coach';
      const trimmedName = clientName.trim();
      const trimmedNote = note.trim();
      const updates = {
        [`/coach_invites/${token}`]: {
          token,
          invitedBy: 'coach',
          coachId: coach.uid,
          coachName,
          clientName: trimmedName || null,
          note: trimmedNote || null,
          defaultScopes: DEFAULT_SCOPES,
          status: 'pending',
          createdAt: now,
          expiresAt: now + INVITE_TTL_MS,
        },
        [`/coach_private/${coach.uid}/sentInvites/${token}`]: {
          token,
          createdAt: now,
          status: 'pending',
          clientName: trimmedName || null,
        },
      };
      await window.LWFB.db.ref('/').update(updates);
      setCreated({ token, url: buildInviteUrl(token) });
      onCreated && onCreated();
    } catch (err) {
      setError(err && err.message ? err.message : String(err));
    } finally {
      setCreating(false);
    }
  };

  if (created) {
    return <CreatedInviteCard
      created={created}
      clientName={clientName}
      t={t}
      onAnother={() => { setCreated(null); setClientName(''); setNote(''); setError(null); }}
    />;
  }

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
      <div style={{ fontFamily: fonts.body, fontSize: 14, lineHeight: 1.55, color: c.textSecondary }}>
        {t('invite.send_intro')}
      </div>
      <Field label={t('invite.client_name_label')} hint={t('invite.optional')}>
        <TextInput value={clientName} onChange={setClientName} placeholder={t('invite.client_name_placeholder')} autoFocus />
      </Field>
      <WelcomeNoteField
        note={note}
        setNote={setNote}
        label={t('invite.note_label')}
        placeholder={t('invite.note_placeholder')}
        cta={t('invite.add_note_cta')}
      />
      {error && (
        <div style={{
          padding: '10px 14px', borderRadius: 10,
          background: hexToRgba(c.error || c.flame, 0.10),
          border: `1px solid ${hexToRgba(c.error || c.flame, 0.3)}`,
          color: c.textPrimary, fontFamily: fonts.body, fontSize: 13,
        }}>{error}</div>
      )}
      <div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: 4 }}>
        <Button
          variant="primary" size="md"
          onClick={generate} disabled={creating}
          icon={<span style={{ fontSize: 14 }}>→</span>}
        >{creating ? t('invite.generating') : t('invite.generate_cta')}</Button>
      </div>
    </div>
  );
}

function CreatedInviteCard({ created, clientName, t, onAnother }) {
  const { c, fonts } = useLW();
  const [copied, setCopied] = useS(false);
  const copy = () => {
    try { navigator.clipboard.writeText(created.url); } catch {}
    setCopied(true);
    setTimeout(() => setCopied(false), 1800);
  };
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div style={{
        padding: '14px 16px', borderRadius: 12,
        background: c.accentMuted,
        border: `1px solid ${hexToRgba(c.accent, 0.4)}`,
        display: 'flex', alignItems: 'flex-start', gap: 12,
      }}>
        <span style={{
          flexShrink: 0, width: 26, height: 26, borderRadius: '50%',
          background: c.accent, color: c.textOnAccent,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 14, fontWeight: 700,
        }}>✓</span>
        <div style={{ flex: 1 }}>
          <div style={{ fontFamily: fonts.body, fontSize: 14, fontWeight: 600, color: c.textPrimary, marginBottom: 4 }}>
            {clientName.trim()
              ? t('invite.created_for_named', { name: clientName.trim() })
              : t('invite.created_unnamed')}
          </div>
          <div style={{ fontFamily: fonts.body, fontSize: 13, color: c.textSecondary, lineHeight: 1.5 }}>
            {t('invite.created_hint')}
          </div>
        </div>
      </div>

      <div style={{
        display: 'flex', alignItems: 'stretch',
        background: c.bgSubtle,
        border: `1px solid ${c.borderSubtle}`,
        borderRadius: 10, overflow: 'hidden',
      }}>
        <code style={{
          flex: 1, padding: '12px 14px',
          fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
          fontSize: 13, color: c.textPrimary,
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
          background: 'transparent',
        }}>{created.url}</code>
        <button onClick={copy} style={{
          padding: '0 18px',
          background: copied ? c.accent : hexToRgba(c.accent, 0.18),
          color: copied ? c.textOnAccent : c.accent,
          border: 'none', borderLeft: `1px solid ${c.borderSubtle}`,
          fontFamily: fonts.body, fontSize: 13, fontWeight: 700,
          letterSpacing: '0.05em', textTransform: 'uppercase',
          cursor: 'pointer', transition: 'all 120ms ease',
        }}>{copied ? '✓ ' + t('invite.copied') : t('invite.copy')}</button>
      </div>

      <div style={{
        fontFamily: fonts.body, fontSize: 12, color: c.textTertiary,
        lineHeight: 1.55, fontStyle: 'italic',
      }}>{t('invite.expiry_hint')}</div>

      <div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: 4 }}>
        <Button variant="ghost" size="md" onClick={onAnother}>
          {t('invite.create_another')}
        </Button>
      </div>
    </div>
  );
}

// ── Inverse direction: instructions for client-initiated invite ──
function ReceiveInvite({ t }) {
  const { c, fonts } = useLW();
  const steps = [
    t('invite.recv_step_1'),
    t('invite.recv_step_2'),
    t('invite.recv_step_3'),
    t('invite.recv_step_4'),
  ];
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
      <div style={{ fontFamily: fonts.body, fontSize: 14, lineHeight: 1.55, color: c.textSecondary }}>
        {t('invite.recv_intro')}
      </div>
      <ol style={{
        listStyle: 'none', padding: 0, margin: 0,
        display: 'flex', flexDirection: 'column', gap: 12,
      }}>
        {steps.map((s, i) => (
          <li key={i} style={{
            display: 'flex', alignItems: 'flex-start', gap: 12,
            padding: '12px 14px',
            background: c.bgSubtle,
            border: `1px solid ${c.borderSubtle}`,
            borderRadius: 10,
          }}>
            <span style={{
              flexShrink: 0,
              width: 22, height: 22, borderRadius: 6,
              background: c.accentMuted, color: c.accent,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontFamily: fonts.body, fontSize: 12, fontWeight: 700, marginTop: 1,
            }}>{i + 1}</span>
            <span style={{ flex: 1, fontFamily: fonts.body, fontSize: 14, lineHeight: 1.5, color: c.textPrimary }}>{s}</span>
          </li>
        ))}
      </ol>
      <div style={{
        fontFamily: fonts.body, fontSize: 12, color: c.textTertiary,
        lineHeight: 1.55, fontStyle: 'italic',
      }}>{t('invite.recv_hint')}</div>
    </div>
  );
}

// ── Real pending list (coach's outgoing + claimed invites) ──
function usePendingInvites(coach) {
  const [items, setItems] = useS(null); // null=loading, []=empty
  useE(() => {
    if (!coach || !window.LWFB) return;
    const ref = window.LWFB.db.ref(`/coach_private/${coach.uid}/sentInvites`);
    const onValue = (snap) => {
      if (!snap.exists()) { setItems([]); return; }
      const v = snap.val();
      const list = Object.entries(v)
        .map(([id, x]) => ({ id, ...x }))
        .sort((a, b) => Number(b.createdAt || 0) - Number(a.createdAt || 0));
      // Refresh status from /coach_invites/{token} so claimed items surface even
      // if the private mirror was written stale.
      Promise.all(list.map(async (item) => {
        try {
          const s = await window.LWFB.db.ref(`/coach_invites/${item.token}`).get();
          if (!s.exists()) return { ...item, status: item.status || 'pending', tombstone: true };
          const t = s.val();
          return { ...item, status: t.status || 'pending', expiresAt: t.expiresAt, claimedAt: t.claimedAt };
        } catch { return item; }
      })).then(setItems);
    };
    ref.on('value', onValue, () => setItems([]));
    return () => ref.off('value', onValue);
  }, [coach && coach.uid]);
  return items;
}

function PendingList({ items, coach, t }) {
  const { c, fonts } = useLW();
  if (items === null) {
    return <div style={{ fontFamily: fonts.body, fontSize: 13, color: c.textTertiary, fontStyle: 'italic', padding: '18px 0' }}>
      {t('invite.pending_loading')}
    </div>;
  }
  if (items.length === 0) {
    return <div style={{ fontFamily: fonts.body, fontSize: 13, color: c.textTertiary, fontStyle: 'italic', padding: '18px 0' }}>
      {t('invite.pending_empty')}
    </div>;
  }
  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      {items.map((it, i) => (
        <PendingRow key={it.id} item={it} coach={coach} t={t} last={i === items.length - 1} />
      ))}
    </div>
  );
}

function PendingRow({ item, coach, t, last }) {
  const { c, fonts } = useLW();
  const [busy, setBusy] = useS(false);
  const expired = item.expiresAt && Number(item.expiresAt) < Date.now();
  const status = item.status || 'pending';
  const url = buildInviteUrl(item.token);
  const [copied, setCopied] = useS(false);

  const meta = (() => {
    if (status === 'claimed')   return { label: t('invite.status_claimed'),  fg: 'accent',        dot: 'accent' };
    if (status === 'cancelled') return { label: t('invite.status_cancelled'), fg: 'textTertiary', dot: 'textTertiary' };
    if (expired)                return { label: t('invite.status_expired'),  fg: 'flame',         dot: 'flame' };
    return                             { label: t('invite.status_pending'),  fg: 'textSecondary', dot: 'textTertiary' };
  })();

  const cancel = async () => {
    if (busy) return;
    setBusy(true);
    try {
      await window.LWFB.db.ref('/').update({
        [`/coach_invites/${item.token}/status`]:    'cancelled',
        [`/coach_invites/${item.token}/cancelledAt`]: Date.now(),
        [`/coach_private/${coach.uid}/sentInvites/${item.token}/status`]: 'cancelled',
      });
    } catch (err) {
      console.warn('[invite] cancel failed', err);
    } finally {
      setBusy(false);
    }
  };

  const copy = () => {
    try { navigator.clipboard.writeText(url); } catch {}
    setCopied(true);
    setTimeout(() => setCopied(false), 1500);
  };

  const sentAgo = relInviteTime(item.createdAt, t);
  const name = (item.clientName && String(item.clientName).trim()) || t('invite.unnamed_client');

  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 14,
      padding: '14px 0',
      borderBottom: last ? 'none' : `1px solid ${c.borderSubtle}`,
    }}>
      <div style={{
        flexShrink: 0, width: 36, height: 36, borderRadius: '50%',
        background: c.accentMuted, color: c.accent,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: fonts.body, fontSize: 13, fontWeight: 700,
      }}>{initialsFrom(name)}</div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          fontFamily: fonts.body, fontSize: 14, fontWeight: 600, color: c.textPrimary,
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        }}>{name}</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 2, flexWrap: 'wrap' }}>
          <span style={{ width: 6, height: 6, borderRadius: '50%', background: c[meta.dot] || c.textTertiary }} />
          <span style={{ fontFamily: fonts.body, fontSize: 12, color: c[meta.fg] || c.textSecondary }}>{meta.label}</span>
          <span style={{ fontFamily: fonts.body, fontSize: 12, color: c.textTertiary }}>· {sentAgo}</span>
        </div>
      </div>
      {(status === 'pending' && !expired) && (
        <div style={{ display: 'flex', gap: 6 }}>
          <button onClick={copy} style={{
            background: 'transparent', border: `1px solid ${c.borderSubtle}`,
            color: c.textSecondary, padding: '6px 10px', borderRadius: 8,
            fontFamily: fonts.body, fontSize: 12, fontWeight: 600, cursor: 'pointer',
          }}>{copied ? '✓ ' + t('invite.copied') : t('invite.copy_link_short')}</button>
          <button onClick={cancel} disabled={busy} style={{
            background: 'transparent', border: `1px solid ${c.borderSubtle}`,
            color: c.textTertiary, padding: '6px 10px', borderRadius: 8,
            fontFamily: fonts.body, fontSize: 12, fontWeight: 600,
            cursor: busy ? 'progress' : 'pointer',
            opacity: busy ? 0.6 : 1,
          }}>{t('invite.cancel')}</button>
        </div>
      )}
    </div>
  );
}

function relInviteTime(ms, t) {
  if (!ms) return '';
  const today0 = new Date(); today0.setHours(0, 0, 0, 0);
  const then0 = new Date(Number(ms)); then0.setHours(0, 0, 0, 0);
  const days = Math.round((today0.getTime() - then0.getTime()) / 86_400_000);
  if (days <= 0) return t('invite.time_today');
  return t(pluralKey('invite.time_day_ago', days), { n: days });
}

// Pluralization with the same RU rules used by claim.jsx — 1 / 2-4 / 5+.
// Resolves a base key like "invite.time_day_ago" to "_one", "_few", or "_other".
function pluralKey(base, n) {
  const lang = window.LWLang ? window.LWLang.lang() : 'en';
  if (lang !== 'ru' && lang !== 'uk') {
    return n === 1 ? `${base}_one` : `${base}_other`;
  }
  const m10 = Math.abs(n) % 10;
  const m100 = Math.abs(n) % 100;
  if (m10 === 1 && m100 !== 11) return `${base}_one`;
  if (m10 >= 2 && m10 <= 4 && (m100 < 12 || m100 > 14)) return `${base}_few`;
  return `${base}_other`;
}

function initialsFrom(name) {
  const parts = String(name || '').trim().split(/\s+/).slice(0, 2);
  return parts.map(p => p[0] ? p[0].toUpperCase() : '').join('') || '·';
}

// ── Main sheet ──
function InviteSheet({ onClose }) {
  const { c: cBase, fonts: fontsBase, mode, t } = useLW();
  // Brutalism theme override
  const [brutMode, setBrutMode] = window.LWBrutal.useBrutMode();
  const c = window.LWBrutal.makeC(cBase, brutMode);
  const fonts = { ...fontsBase, ...window.LWBrutal.fonts };
  window.__brutMode = brutMode; window.__brutSetMode = setBrutMode;
  window.__brutC = c; window.__brutFonts = fonts;
  const { isMobile } = useViewport();
  const [tab, setTab] = useS('send');
  const [coach, setCoach] = useS(null);
  const pending = usePendingInvites(coach);

  useE(() => {
    if (!window.LWAuth) return;
    return window.LWAuth.onAuthChanged(setCoach);
  }, []);

  // Esc to close
  useE(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 1000,
      background: hexToRgba('#000000', 0.55),
      backdropFilter: 'blur(8px)',
      display: 'flex',
      alignItems: isMobile ? 'flex-end' : 'center',
      justifyContent: 'center',
      padding: isMobile ? 0 : 24,
      overflow: 'auto',
    }} onClick={onClose}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: '100%',
        maxWidth: 640,
        maxHeight: isMobile ? '94vh' : '88vh',
        background: c.card,
        border: `1px solid ${c.borderDefault}`,
        borderRadius: isMobile ? '16px 16px 0 0' : 18,
        boxShadow: c.shadowModal,
        overflow: 'hidden',
        display: 'flex', flexDirection: 'column',
      }}>
        {/* header */}
        <div style={{
          padding: isMobile ? '18px 20px 16px' : '22px 28px 18px',
          borderBottom: `1px solid ${c.borderSubtle}`,
          display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16,
        }}>
          <div>
            <div style={{ fontFamily: fonts.body, fontSize: 11, fontWeight: 700, letterSpacing: '0.16em', textTransform: 'uppercase', color: c.textTertiary, marginBottom: 6 }}>
              {t('invite.eyebrow')}
            </div>
            <h2 style={{ fontFamily: fonts.display, fontWeight: 500, fontSize: isMobile ? 24 : 28, letterSpacing: '-0.015em', margin: 0, lineHeight: 1.1, color: c.textPrimary }}>
              {t('invite.title_pre')} <em style={{ fontStyle: 'italic', color: c.accent, fontWeight: 400 }}>{t('invite.title_em')}</em>
            </h2>
          </div>
          <button onClick={onClose} aria-label={t('invite.close')} style={{
            flexShrink: 0,
            background: 'transparent', border: `1px solid ${c.borderSubtle}`,
            width: 36, height: 36, borderRadius: '50%',
            color: c.textSecondary, cursor: 'pointer',
            fontSize: 18, display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>✕</button>
        </div>

        {/* body */}
        <div style={{ flex: 1, overflow: 'auto' }}>
          <div style={{ padding: isMobile ? '20px' : '24px 28px' }}>
            <div style={{ marginBottom: 22 }}>
              <DirectionTabs value={tab} onChange={setTab} t={t} />
            </div>
            {tab === 'send'    && <SendInvite coach={coach} t={t} />}
            {tab === 'receive' && <ReceiveInvite t={t} />}
          </div>

          <div style={{ padding: isMobile ? '0 20px 20px' : '0 28px 28px' }}>
            <div style={{
              display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
              paddingTop: 22, borderTop: `1px solid ${c.borderSubtle}`, marginBottom: 4,
            }}>
              <h3 style={{ fontFamily: fonts.display, fontWeight: 500, fontSize: 18, margin: 0, color: c.textPrimary }}>
                <em style={{ fontStyle: 'italic', color: c.textSecondary, fontWeight: 400 }}>{t('invite.history_em')}</em> {t('invite.history_title')}
              </h3>
              {Array.isArray(pending) && pending.length > 0 && (
                <span style={{ fontFamily: fonts.body, fontSize: 12, color: c.textTertiary, letterSpacing: '0.06em', textTransform: 'uppercase', fontWeight: 600 }}>
                  {t(pluralKey('invite.history_count', pending.length), { n: pending.length })}
                </span>
              )}
            </div>
            <PendingList items={pending} coach={coach} t={t} />
          </div>
        </div>
      </div>
    </div>
  );
}

window.InviteSheet = InviteSheet;
