// pantry-sheets-2.jsx — additional sheets for edit/move/meal/family/recipes/grocery

const { useState: useStateS2, useEffect: useEffectS2, useMemo: useMemoS2, useRef: useRefS2 } = React;

// ─────────────────────────────────────────────────────────────
// Shared field wrapper
// ─────────────────────────────────────────────────────────────
function Field2({ label, hint, palette, children }) {
  return (
    <div style={{ marginBottom: 14 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
        <div style={{
          fontSize: 11, fontFamily: 'Geist Mono, monospace', color: palette.ink3,
          letterSpacing: 0.06, textTransform: 'uppercase',
        }}>{label}</div>
        {hint && <div style={{ fontSize: 10.5, color: palette.ink3, fontStyle: 'italic' }}>{hint}</div>}
      </div>
      {children}
    </div>
  );
}
function stepBtn2(palette) {
  return {
    width: 44, height: 44, borderRadius: 12,
    background: palette.card, border: `1px solid ${palette.line}`,
    display: 'flex', alignItems: 'center', justifyContent: 'center',
    cursor: 'pointer', fontSize: 20, fontWeight: 400, color: palette.ink, userSelect: 'none',
  };
}
function SheetHeader({ palette, title, sub, onClose, accent }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '4px 0 18px' }}>
      {accent && (
        <div style={{
          width: 44, height: 44, borderRadius: 12,
          background: accent.bg, color: accent.ink,
          display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
        }}>
          {typeof accent.icon === 'string' ? <Icon name={accent.icon} size={20} color={accent.ink} /> : accent.icon}
        </div>
      )}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 17, fontWeight: 600, color: palette.ink, letterSpacing: -0.3, lineHeight: 1.2 }}>{title}</div>
        {sub && <div style={{ fontSize: 12, color: palette.ink3, marginTop: 2 }}>{sub}</div>}
      </div>
      <div onClick={onClose} style={{
        width: 32, height: 32, borderRadius: 10, background: palette.cardSub,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        cursor: 'pointer', border: `1px solid ${palette.line}`, color: palette.ink2, flexShrink: 0,
      }}>
        <Icon name="close" size={15} />
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// EditItemSheet — change name, qty, unit, expiry
// ─────────────────────────────────────────────────────────────
function EditItemSheet({ palette, lang, item, onClose, onSave }) {
  const [name, setName] = useStateS2(localized(lang, item.name));
  const [qty, setQty] = useStateS2(item.qty);
  const [unit, setUnit] = useStateS2(localized(lang, item.unit));
  const [days, setDays] = useStateS2(item.daysLeft);
  return (
    <div style={{ padding: '8px 20px 28px' }}>
      <SheetHeader palette={palette} onClose={onClose}
        title={lang === 'fr' ? 'Modifier' : 'Edit'}
        sub={lang === 'fr' ? 'Ajuste les détails' : 'Adjust details'}
        accent={{ bg: palette.primarySoft, ink: palette.primarySoftInk, icon: 'pen' }} />

      <Field2 palette={palette} label={t(lang, 'name')}>
        <input value={name} onChange={e => setName(e.target.value)} style={{
          width: '100%', height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
          padding: '0 14px', fontSize: 15, color: palette.ink, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box',
        }} />
      </Field2>

      <Field2 palette={palette} label={t(lang, 'setQty')}>
        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
          <div onClick={() => setQty(String(Math.max(0, (parseFloat(qty) || 0) - 1)))} style={stepBtn2(palette)}>−</div>
          <input value={qty} onChange={e => setQty(e.target.value)} style={{
            flex: 1, height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
            padding: '0 14px', fontSize: 17, color: palette.ink, outline: 'none', fontFamily: 'Geist Mono, monospace',
            fontWeight: 500, textAlign: 'center', boxSizing: 'border-box',
          }} />
          <div onClick={() => setQty(String((parseFloat(qty) || 0) + 1))} style={stepBtn2(palette)}>+</div>
          <input value={unit} onChange={e => setUnit(e.target.value)} style={{
            width: 80, height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
            padding: '0 12px', fontSize: 13, color: palette.ink2, fontFamily: 'Geist Mono, monospace',
            outline: 'none', textAlign: 'center', boxSizing: 'border-box',
          }} />
        </div>
      </Field2>

      <Field2 palette={palette} label={t(lang, 'expiry')}>
        <div style={{ display: 'flex', gap: 6 }}>
          {[1, 3, 7, 14, 30].map(d => {
            const a = days === d;
            return (
              <div key={d} onClick={() => setDays(d)} style={{
                flex: 1, padding: '10px 4px', borderRadius: 10, textAlign: 'center',
                background: a ? palette.ink : palette.card,
                color: a ? palette.bg : palette.ink2,
                border: `1px solid ${a ? palette.ink : palette.line}`,
                fontSize: 12, fontWeight: 500, cursor: 'pointer',
                fontFamily: 'Geist Mono, monospace',
              }}>{d}{t(lang, 'days')}</div>
            );
          })}
        </div>
      </Field2>

      <div onClick={() => onSave({ name: { fr: name, en: name }, qty, unit: { fr: unit, en: unit }, daysLeft: days })} style={{
        marginTop: 22, padding: '15px', borderRadius: 14,
        background: palette.primary, color: palette.primaryInk,
        textAlign: 'center', fontWeight: 500, fontSize: 15, cursor: 'pointer',
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
      }}>
        <Icon name="check" size={18} color={palette.primaryInk} />
        {t(lang, 'save')}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// MoveItemSheet — pick new location
// ─────────────────────────────────────────────────────────────
function MoveItemSheet({ palette, lang, item, onClose, onMove }) {
  const opts = [
    { id: 'fridge', label: t(lang, 'fridge'), emoji: '❄️', desc: lang === 'fr' ? 'Frais, 0–4°C' : 'Cold, 0–4°C' },
    { id: 'freezer', label: t(lang, 'freezer'), emoji: '🧊', desc: lang === 'fr' ? 'Gelé, −18°C' : 'Frozen, 0°F' },
    { id: 'pantry', label: t(lang, 'pantry'), emoji: '🗄️', desc: lang === 'fr' ? 'Sec, ambiant' : 'Dry, ambient' },
  ];
  return (
    <div style={{ padding: '8px 20px 28px' }}>
      <SheetHeader palette={palette} onClose={onClose}
        title={lang === 'fr' ? `Déplacer ${localized(lang, item.name)}` : `Move ${localized(lang, item.name)}`}
        sub={lang === 'fr' ? 'Choisis le nouvel emplacement' : 'Pick the new location'}
        accent={{ bg: palette.cardSub, ink: palette.ink2, icon: 'box' }} />
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {opts.map(o => {
          const cur = item.location === o.id;
          return (
            <div key={o.id} onClick={() => onMove(o.id)} style={{
              padding: '14px', borderRadius: 14,
              background: cur ? palette.primarySoft : palette.card,
              border: `1px solid ${cur ? palette.primary : palette.line}`,
              cursor: cur ? 'default' : 'pointer',
              display: 'flex', alignItems: 'center', gap: 14,
              opacity: cur ? 0.55 : 1,
            }}>
              <div style={{ fontSize: 28 }}>{o.emoji}</div>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 15, fontWeight: 600, color: palette.ink }}>{o.label}</div>
                <div style={{ fontSize: 11.5, color: palette.ink3, marginTop: 2, fontFamily: 'Geist Mono, monospace' }}>{o.desc}</div>
              </div>
              {cur ? (
                <span style={{ fontSize: 10.5, color: palette.primary, fontFamily: 'Geist Mono, monospace' }}>
                  {lang === 'fr' ? 'actuel' : 'current'}
                </span>
              ) : (
                <Icon name="chevR" size={16} color={palette.ink3} />
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// UsedSomeSheet — pick how much was used
// ─────────────────────────────────────────────────────────────
function UsedSomeSheet({ palette, lang, item, onClose, onConfirm }) {
  const cur = parseFloat(item.qty) || 1;
  const [amount, setAmount] = useStateS2(1);
  const unitLabel = localized(lang, item.unit);
  const presets = cur >= 4 ? [1, 2, Math.floor(cur / 2), cur] : [0.5, 1, 2, cur];
  const remaining = Math.max(0, cur - amount);
  return (
    <div style={{ padding: '8px 20px 28px' }}>
      <SheetHeader palette={palette} onClose={onClose}
        title={lang === 'fr' ? `Combien utilisé ?` : `How much used?`}
        sub={`${localized(lang, item.name)} · ${cur} ${unitLabel}`}
        accent={{ bg: palette.amberSoft, ink: palette.amberSoftInk, icon: 'fire' }} />

      <Field2 palette={palette} label={lang === 'fr' ? 'Retirer' : 'Remove'}>
        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
          <div onClick={() => setAmount(Math.max(0, +(amount - 0.5).toFixed(2)))} style={stepBtn2(palette)}>−</div>
          <input value={amount} onChange={e => setAmount(parseFloat(e.target.value) || 0)} type="number" step="0.5" style={{
            flex: 1, height: 56, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 14,
            padding: '0 14px', fontSize: 28, fontWeight: 600, color: palette.ink, outline: 'none',
            fontFamily: 'Geist Mono, monospace', textAlign: 'center', boxSizing: 'border-box',
          }} />
          <div onClick={() => setAmount(+(amount + 0.5).toFixed(2))} style={stepBtn2(palette)}>+</div>
          <div style={{
            padding: '0 14px', height: 56, display: 'flex', alignItems: 'center',
            background: palette.cardSub, border: `1px solid ${palette.line}`, borderRadius: 14,
            fontSize: 13, color: palette.ink2, fontFamily: 'Geist Mono, monospace',
          }}>{unitLabel}</div>
        </div>
      </Field2>

      <div style={{ display: 'flex', gap: 6, marginBottom: 16 }}>
        {presets.map((p, i) => (
          <div key={i} onClick={() => setAmount(p)} style={{
            flex: 1, padding: '8px 0', textAlign: 'center', borderRadius: 10,
            background: amount === p ? palette.ink : palette.cardSub,
            color: amount === p ? palette.bg : palette.ink2,
            border: `1px solid ${amount === p ? palette.ink : palette.line2}`,
            fontSize: 12, fontFamily: 'Geist Mono, monospace', fontWeight: 500, cursor: 'pointer',
          }}>{p === cur ? (lang === 'fr' ? 'Tout' : 'All') : p}</div>
        ))}
      </div>

      <div style={{
        padding: '12px 14px', borderRadius: 12, marginBottom: 16,
        background: remaining === 0 ? palette.redSoft : palette.primarySoft,
        color: remaining === 0 ? palette.redSoftInk : palette.primarySoftInk,
        display: 'flex', alignItems: 'center', gap: 10, fontSize: 12.5,
      }}>
        <Icon name={remaining === 0 ? 'trash' : 'check'} size={15} color={remaining === 0 ? palette.redSoftInk : palette.primarySoftInk} />
        <div style={{ flex: 1 }}>
          {remaining === 0
            ? (lang === 'fr' ? `Sera retiré de l'inventaire` : `Will be removed from inventory`)
            : (lang === 'fr' ? `Reste ${remaining} ${unitLabel}` : `${remaining} ${unitLabel} remaining`)}
        </div>
      </div>

      <div onClick={() => onConfirm(amount)} style={{
        padding: '15px', borderRadius: 14,
        background: amount > 0 ? palette.primary : palette.line, color: amount > 0 ? palette.primaryInk : palette.ink3,
        textAlign: 'center', fontWeight: 500, fontSize: 15, cursor: amount > 0 ? 'pointer' : 'default',
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
      }}>
        <Icon name="check" size={18} color={amount > 0 ? palette.primaryInk : palette.ink3} />
        {lang === 'fr' ? 'Confirmer' : 'Confirm'}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// MealEditSheet — pick recipe, cook, time, attendees
// ─────────────────────────────────────────────────────────────
function MealEditSheet({ palette, lang, target, household, recipes, inventory, onClose, onSave, onRemove }) {
  const existing = target.meal;
  const preset = target.presetRecipe; // arriving from a recipe's "To calendar"
  const [recipeId, setRecipeId] = useStateS2(existing ? existing.recipeId : (preset ? preset.id : null));
  const [cook, setCook] = useStateS2(existing ? existing.cook : household[0]?.id);
  const [time, setTime] = useStateS2(existing ? existing.time : (target.slot === 'breakfast' ? '08:00' : target.slot === 'lunch' ? '12:30' : '18:30'));
  const [attendees, setAttendees] = useStateS2(existing && existing.attendees ? existing.attendees : household.reduce((m, h) => ({ ...m, [h.id]: true }), {}));
  const [q, setQ] = useStateS2('');
  const [online, setOnline] = useStateS2([]);
  const [loadingOnline, setLoadingOnline] = useStateS2(false);

  // Online (TheMealDB) search so you can plan ANY recipe, not just the catalog.
  useEffectS2(() => {
    if (!q.trim() || !window.MealDB) { setOnline([]); return; }
    let alive = true; setLoadingOnline(true);
    window.MealDB.search(q.trim()).then(list => {
      if (!alive) return;
      setOnline(Array.isArray(list) ? list : []); setLoadingOnline(false);
    }).catch(() => { if (alive) setLoadingOnline(false); });
    return () => { alive = false; };
  }, [q]);

  const matchOf = window.matchRecipe || ((r) => ({ matched: 0, total: 1 }));
  const sorted = useMemoS2(() => {
    // Make sure a preset recipe (e.g. an online one) is always selectable.
    const base = preset && !recipes.some(r => r.id === preset.id) ? [preset, ...recipes] : recipes;
    const localIds = new Set(base.map(r => r.id));
    const merged = base.concat(online.filter(r => !localIds.has(r.id)));
    return merged
      .map(r => { const mm = matchOf(r, inventory); return { ...r, _match: mm.matched / Math.max(1, mm.total) }; })
      .filter(r => q ? localized(lang, r.name).toLowerCase().includes(q.toLowerCase()) : true)
      .sort((a, b) => b._match - a._match);
  }, [recipes, online, q, inventory, lang]);

  const slotLabel = t(lang, 'plan' + target.slot.charAt(0).toUpperCase() + target.slot.slice(1));
  const dayLabels = ['planMonL', 'planTueL', 'planWedL', 'planThuL', 'planFriL', 'planSatL', 'planSunL'];
  const dayLabel = target.date
    ? `${t(lang, dayLabels[weekdayIndex(target.date)])} ${dateNumber(target.date)}`
    : '';

  const presentCount = Object.values(attendees).filter(v => v === true).length;

  return (
    <div style={{ padding: '8px 20px 28px' }}>
      <SheetHeader palette={palette} onClose={onClose}
        title={existing ? (lang === 'fr' ? 'Modifier le repas' : 'Edit meal') : (lang === 'fr' ? 'Planifier le repas' : 'Plan meal')}
        sub={`${dayLabel} · ${slotLabel}`}
        accent={{ bg: palette.primarySoft, ink: palette.primarySoftInk, icon: 'calendar' }} />

      {/* Recipe picker */}
      <Field2 palette={palette} label={lang === 'fr' ? 'Recette' : 'Recipe'}>
        <div style={{
          background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
          padding: '8px 14px', display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8,
        }}>
          <Icon name="search" size={15} color={palette.ink3} />
          <input value={q} onChange={e => setQ(e.target.value)} placeholder={t(lang, 'searchPh')}
            style={{ flex: 1, border: 'none', outline: 'none', background: 'transparent', fontSize: 14, color: palette.ink, fontFamily: 'inherit' }} />
        </div>
        <div style={{ maxHeight: 200, overflowY: 'auto', display: 'flex', flexDirection: 'column', gap: 6 }}>
          {sorted.map(r => {
            const sel = recipeId === r.id;
            const pct = Math.round((r._match || 0) * 100);
            return (
              <div key={r.id} onClick={() => setRecipeId(r.id)} style={{
                display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px',
                background: sel ? palette.primarySoft : palette.card,
                border: `1.5px solid ${sel ? palette.primary : palette.line}`,
                borderRadius: 12, cursor: 'pointer',
              }}>
                <div style={{
                  width: 36, height: 36, borderRadius: 10, overflow: 'hidden', flexShrink: 0,
                  background: `linear-gradient(135deg, ${r.bg1 || palette.primary}, ${r.bg2 || palette.primary})`,
                  display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 18,
                }}>{r.image ? <img src={r.image} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} /> : (r.hero || '🍽')}</div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13, fontWeight: 500, color: palette.ink, letterSpacing: -0.1, display: 'flex', alignItems: 'center', gap: 6 }}>
                    <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{localized(lang, r.name)}</span>
                    {r.external && <span style={{ fontSize: 8.5, fontFamily: 'Geist Mono, monospace', padding: '1px 5px', borderRadius: 4, background: palette.primarySoft, color: palette.primarySoftInk, flexShrink: 0 }}>{lang === 'fr' ? 'EN LIGNE' : 'ONLINE'}</span>}
                  </div>
                  <div style={{ fontSize: 10.5, color: palette.ink3, marginTop: 1, fontFamily: 'Geist Mono, monospace' }}>
                    {r.minutes}m · {pct}% {t(lang, 'haveIt').toLowerCase()}
                  </div>
                </div>
                {sel && <Icon name="check" size={14} color={palette.primary} />}
              </div>
            );
          })}
          {loadingOnline && (
            <div style={{ padding: '8px 10px', fontSize: 11.5, color: palette.ink3, fontFamily: 'Geist Mono, monospace' }}>
              {t(lang, 'recLoadingOnline')}
            </div>
          )}
        </div>
      </Field2>

      {/* Cook */}
      <Field2 palette={palette} label={t(lang, 'planAssign')}>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {household.map(m => {
            const a = cook === m.id;
            return (
              <div key={m.id} onClick={() => setCook(m.id)} style={{
                display: 'inline-flex', alignItems: 'center', gap: 6,
                padding: '6px 10px 6px 6px', borderRadius: 999,
                background: a ? palette.ink : palette.card,
                color: a ? palette.bg : palette.ink2,
                border: `1px solid ${a ? palette.ink : palette.line}`,
                fontSize: 13, fontWeight: 500, cursor: 'pointer',
              }}>
                <div style={{
                  width: 22, height: 22, borderRadius: 11,
                  background: m.tone, color: '#fff',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontSize: 11, fontWeight: 600, fontFamily: 'Geist Mono, monospace',
                }}>{m.initial}</div>
                {m.name}
              </div>
            );
          })}
        </div>
      </Field2>

      {/* Time */}
      <Field2 palette={palette} label={t(lang, 'planTime')}>
        <input value={time} onChange={e => setTime(e.target.value)} type="time" style={{
          width: '100%', height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
          padding: '0 14px', fontSize: 16, color: palette.ink, outline: 'none',
          fontFamily: 'Geist Mono, monospace', boxSizing: 'border-box',
        }} />
      </Field2>

      {/* Attendees */}
      <Field2 palette={palette} label={lang === 'fr' ? `Participants (${presentCount}/${household.length})` : `Attendees (${presentCount}/${household.length})`}>
        <div style={{ background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12, overflow: 'hidden' }}>
          {household.map((m, i) => {
            const here = attendees[m.id] === true;
            return (
              <div key={m.id} onClick={() => setAttendees({ ...attendees, [m.id]: !here })} style={{
                display: 'flex', alignItems: 'center', gap: 10, padding: '10px 12px',
                borderBottom: i < household.length - 1 ? `1px solid ${palette.line2}` : 'none',
                cursor: 'pointer',
              }}>
                <div style={{
                  width: 22, height: 22, borderRadius: 6,
                  background: here ? palette.primary : 'transparent',
                  border: `1.5px solid ${here ? palette.primary : palette.line}`,
                  display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
                }}>
                  {here && <Icon name="check" size={13} color={palette.primaryInk} />}
                </div>
                <div style={{
                  width: 26, height: 26, borderRadius: 13,
                  background: m.tone, color: '#fff',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontSize: 11.5, fontWeight: 600, fontFamily: 'Geist Mono, monospace', flexShrink: 0,
                }}>{m.initial}</div>
                <div style={{ flex: 1, fontSize: 14, color: palette.ink, fontWeight: 500 }}>{m.name}</div>
                <div style={{ fontSize: 11, color: here ? palette.primary : palette.ink3, fontFamily: 'Geist Mono, monospace' }}>
                  {here ? (lang === 'fr' ? 'présent' : 'present') : (lang === 'fr' ? 'absent' : 'away')}
                </div>
              </div>
            );
          })}
        </div>
      </Field2>

      {/* Actions */}
      <div style={{ display: 'flex', gap: 8, marginTop: 16 }}>
        {existing && (
          <div onClick={() => onRemove({ date: target.date, slot: target.slot })} style={{
            padding: '15px 18px', borderRadius: 14,
            background: palette.redSoft, color: palette.redSoftInk,
            fontWeight: 500, fontSize: 13.5, cursor: 'pointer',
            display: 'flex', alignItems: 'center', gap: 6,
          }}>
            <Icon name="trash" size={15} color={palette.redSoftInk} />
            {t(lang, 'delete')}
          </div>
        )}
        <div onClick={() => recipeId && onSave({ date: target.date, slot: target.slot, recipeId, recipe: sorted.find(r => r.id === recipeId), cook, time, attendees })} style={{
          flex: 1, padding: '15px', borderRadius: 14,
          background: recipeId ? palette.primary : palette.line,
          color: recipeId ? palette.primaryInk : palette.ink3,
          textAlign: 'center', fontWeight: 500, fontSize: 14.5, cursor: recipeId ? 'pointer' : 'default',
          display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        }}>
          <Icon name="check" size={18} color={recipeId ? palette.primaryInk : palette.ink3} />
          {t(lang, 'save')}
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// InviteFamilySheet — add a household member
// ─────────────────────────────────────────────────────────────
function InviteFamilySheet({ palette, lang, onClose, onAdd }) {
  const [name, setName] = useStateS2('');
  const [email, setEmail] = useStateS2('');
  const [tone, setTone] = useStateS2('#B85149');
  const [busy, setBusy] = useStateS2(false);
  const [error, setError] = useStateS2('');
  const [inviteUrl, setInviteUrl] = useStateS2('');
  const [emailSent, setEmailSent] = useStateS2(false);
  const [emailErr, setEmailErr] = useStateS2('');
  const [copied, setCopied] = useStateS2(false);
  const tones = ['#B85149', '#C68A2E', '#4F8267', '#4A6FA5', '#7A5BB5', '#8B6B4A'];
  const tt = (fr, en) => (lang === 'fr' ? fr : en);

  const validEmail = (e) => /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(e.trim());
  const canSubmit = name.trim().length > 0 && validEmail(email) && !busy;
  const isAuthed = !!(window.PWAuth && window.PWAuth.api && window.PWAuth.current && window.PWAuth.current.user);

  const generate = async () => {
    setError('');
    // Always record the member locally so the UI shows them right away.
    if (onAdd) onAdd({
      id: 'u_' + Date.now(),
      name: name.trim(),
      tone,
      initial: name.trim().charAt(0).toUpperCase(),
      email: email.trim().toLowerCase(),
      pending: true,
    });
    if (!isAuthed) {
      // Local/guest mode — no server to mint a token. Explain and close.
      setError(tt(
        'Connecte-toi avec un compte pour générer un vrai lien d\'invitation.',
        'Sign in with an account to generate a real invite link.'));
      return;
    }
    setBusy(true);
    try {
      const res = await window.PWAuth.api.invite(email.trim().toLowerCase());
      // Prefer the server URL but rebuild on the current origin to be safe.
      const url = res.inviteUrl || (location.origin + '/?invite=' + res.inviteToken);
      setEmailSent(!!res.emailSent);
      setEmailErr(res.emailError || '');
      setInviteUrl(url);
    } catch (e) {
      setError(e.message || tt('Erreur lors de la génération du lien', 'Could not generate the link'));
    } finally {
      setBusy(false);
    }
  };

  const doCopy = async () => {
    try {
      await navigator.clipboard.writeText(inviteUrl);
      setCopied(true);
      setTimeout(() => setCopied(false), 1800);
    } catch (e) {
      // Fallback: select-less prompt
      window.prompt(tt('Copie le lien :', 'Copy the link:'), inviteUrl);
    }
  };

  const doShare = async () => {
    const text = tt(
      `${name.trim()}, rejoins ma famille sur PantryWise : ${inviteUrl}`,
      `${name.trim()}, join my household on PantryWise: ${inviteUrl}`);
    if (navigator.share) {
      try { await navigator.share({ title: 'PantryWise', text, url: inviteUrl }); } catch (e) {}
    } else {
      doCopy();
    }
  };

  // ── Result view: show the generated link with copy / share ──
  if (inviteUrl) {
    return (
      <div style={{ padding: '8px 20px 28px' }}>
        <SheetHeader palette={palette} onClose={onClose}
          title={tt('Invitation prête', 'Invite ready')}
          sub={t(lang, 'inviteHowto')}
          accent={{ bg: palette.primarySoft, ink: palette.primarySoftInk, icon: 'user' }} />

        <div style={{
          marginTop: 6, padding: '14px', borderRadius: 12, background: palette.cardSub,
          border: `1px solid ${palette.line}`, wordBreak: 'break-all',
          fontSize: 13, fontFamily: 'Geist Mono, monospace', color: palette.ink2, lineHeight: 1.5,
        }}>{inviteUrl}</div>
        <div style={{ marginTop: 6, fontSize: 11.5, color: palette.ink3 }}>{t(lang, 'inviteExpires')}</div>
        {emailSent && (
          <div style={{
            marginTop: 10, display: 'flex', alignItems: 'center', gap: 6,
            fontSize: 12.5, color: palette.primary, fontWeight: 500,
          }}>
            <Icon name="check" size={14} color={palette.primary} />
            {tt('Courriel envoyé à ', 'Email sent to ')}{email.trim().toLowerCase()}
          </div>
        )}
        {!emailSent && emailErr && (
          <div onClick={() => alert(emailErr)} style={{
            marginTop: 10, background: palette.amberSoft, color: palette.amberSoftInk,
            padding: '10px 12px', borderRadius: 10, fontSize: 12, lineHeight: 1.45, cursor: 'pointer',
          }}>
            {/^can only send testing/i.test(emailErr) || /verify a domain/i.test(emailErr)
              ? tt('Courriel auto non encore activé pour cette adresse (un domaine doit être vérifié dans Resend). Partage le lien ci-dessous — il marche pour tout le monde.',
                    'Auto-email not enabled for this address yet (a domain must be verified in Resend). Share the link below — it works for everyone.')
              : tt('Courriel non envoyé (tape pour le détail). Partage le lien en attendant.',
                   'Email not sent (tap for details). Share the link instead.')}
          </div>
        )}

        <div style={{ display: 'flex', gap: 10, marginTop: 16 }}>
          <div onClick={doCopy} style={{
            flex: 1, padding: '14px', borderRadius: 14, background: palette.cardSub,
            border: `1px solid ${palette.line}`, color: palette.ink, textAlign: 'center',
            fontWeight: 500, fontSize: 14.5, cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
          }}>
            <Icon name="check" size={16} color={copied ? palette.primary : palette.ink2} />
            {copied ? t(lang, 'linkCopied') : t(lang, 'copyLink')}
          </div>
          <div onClick={doShare} style={{
            flex: 1, padding: '14px', borderRadius: 14, background: palette.primary,
            color: palette.primaryInk, textAlign: 'center', fontWeight: 500, fontSize: 14.5, cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
          }}>
            <Icon name="plus" size={16} color={palette.primaryInk} />
            {t(lang, 'shareLink')}
          </div>
        </div>
      </div>
    );
  }

  return (
    <div style={{ padding: '8px 20px 28px' }}>
      <SheetHeader palette={palette} onClose={onClose}
        title={lang === 'fr' ? 'Inviter un membre' : 'Invite a member'}
        sub={lang === 'fr' ? 'Partage ton inventaire et tes plans' : 'Share inventory and meal plans'}
        accent={{ bg: palette.primarySoft, ink: palette.primarySoftInk, icon: 'user' }} />

      <Field2 palette={palette} label={lang === 'fr' ? 'Nom' : 'Name'}>
        <input value={name} onChange={e => setName(e.target.value)} placeholder={lang === 'fr' ? 'Ex : Camille' : 'e.g. Camille'} style={{
          width: '100%', height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
          padding: '0 14px', fontSize: 15, color: palette.ink, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box',
        }} />
      </Field2>

      <Field2 palette={palette} label={lang === 'fr' ? 'Courriel' : 'Email'}>
        <input value={email} onChange={e => setEmail(e.target.value)} placeholder="camille@gmail.com" type="email"
          autoCapitalize="none" autoCorrect="off" style={{
          width: '100%', height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
          padding: '0 14px', fontSize: 15, color: palette.ink, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box',
        }} />
      </Field2>

      <Field2 palette={palette} label={lang === 'fr' ? 'Couleur' : 'Color'}>
        <div style={{ display: 'flex', gap: 8 }}>
          {tones.map(c => (
            <div key={c} onClick={() => setTone(c)} style={{
              width: 36, height: 36, borderRadius: 18, background: c, cursor: 'pointer',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              border: tone === c ? `3px solid ${palette.ink}` : '3px solid transparent',
              boxSizing: 'border-box',
            }}>
              {tone === c && <Icon name="check" size={14} color="#fff" />}
            </div>
          ))}
        </div>
      </Field2>

      {error && (
        <div style={{
          marginTop: 14, background: palette.redSoft, color: palette.redSoftInk,
          padding: '10px 12px', borderRadius: 10, fontSize: 13, lineHeight: 1.4,
        }}>{error}</div>
      )}

      <div onClick={() => canSubmit && generate()} style={{
        marginTop: 18, padding: '15px', borderRadius: 14,
        background: canSubmit ? palette.primary : palette.line,
        color: canSubmit ? palette.primaryInk : palette.ink3,
        textAlign: 'center', fontWeight: 500, fontSize: 15, cursor: canSubmit ? 'pointer' : 'default',
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
      }}>
        <Icon name="plus" size={18} color={canSubmit ? palette.primaryInk : palette.ink3} />
        {busy ? t(lang, 'generatingLink') : (lang === 'fr' ? 'Créer le lien d\'invitation' : 'Create invite link')}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// AddRecipeSheet — manual + AI options + scan a recipe page
// ─────────────────────────────────────────────────────────────
function AddRecipeSheet({ palette, lang, onClose, onAdd, initial }) {
  // When editing an existing custom recipe, preload its fields (converting the
  // stored bilingual objects back to the editor's plain-string format).
  const ed = initial && initial.custom ? initial : null;
  const [mode, setMode] = useStateS2(ed ? 'manual' : 'pick'); // pick → manual / ai / scan
  const [name, setName] = useStateS2(ed ? localized(lang, ed.name) : '');
  const [hero, setHero] = useStateS2(ed ? (ed.hero || '🍽') : '🍽');
  const [minutes, setMinutes] = useStateS2(ed ? (ed.minutes || 20) : 20);
  const [portions, setPortions] = useStateS2(ed ? (ed.portions || 2) : 2);
  const [ingredients, setIngredients] = useStateS2(ed && ed.ingredients && ed.ingredients.length
    ? ed.ingredients.map(i => ({ name: localized(lang, i.name), qty: localized(lang, i.qty) }))
    : [{ name: '', qty: '' }]);
  const [steps, setSteps] = useStateS2(ed && ed.steps && ed.steps.length
    ? ed.steps.map(s => localized(lang, s))
    : ['']);
  const [aiPrompt, setAiPrompt] = useStateS2('');
  const [aiPhase, setAiPhase] = useStateS2('idle'); // idle → thinking → done
  const [importText, setImportText] = useStateS2('');

  // Parse pasted recipe text → title, ingredients, steps (best-effort).
  const parseImport = () => {
    const raw = importText.replace(/\r/g, '');
    const lines = raw.split('\n').map(l => l.trim()).filter(Boolean);
    if (!lines.length) return;
    const ingHead = /^(ingr[ée]dients?|ingredients)\s*:?\s*$/i;
    const stepHead = /^(pr[ée]paration|instructions?|m[ée]thode|directions?|[ée]tapes?|steps?)\s*:?\s*$/i;
    let title = lines[0];
    let iIng = lines.findIndex(l => ingHead.test(l));
    let iStep = lines.findIndex(l => stepHead.test(l));
    let ing = [], stp = [];
    if (iIng !== -1 && iStep !== -1 && iStep > iIng) {
      ing = lines.slice(iIng + 1, iStep);
      stp = lines.slice(iStep + 1);
      if (iIng > 0) title = lines[0];
    } else {
      // No headers: a line is an ingredient if short & starts with a quantity.
      const body = lines.slice(1);
      body.forEach(l => {
        const isIng = /^[\d½¼¾⅓⅔]|^(une?|deux|trois|a |an )/i.test(l) && l.length < 60 && !/[.!?]$/.test(l);
        (isIng ? ing : stp).push(l);
      });
    }
    // Strip leading bullets / step numbers.
    const clean = (l) => l.replace(/^[-•*]\s*/, '').replace(/^\d+[.)]\s*/, '').trim();
    const parsedIng = ing.map(clean).filter(Boolean).map(l => {
      const m = l.match(/^([\d½¼¾⅓⅔.,/ ]+\s*(?:g|kg|ml|l|tasse|tasses|cup|cups|c\.?\s*à\s*(?:soupe|th[ée])|tbsp|tsp|oz|lb|boîte|can|pcs|gousses?|tranches?)?)\s+(.+)$/i);
      if (m && m[2]) return { name: m[2].trim(), qty: m[1].trim() };
      return { name: l, qty: '' };
    });
    const parsedSteps = stp.map(clean).filter(s => s.length > 2);
    if (title) setName(title.replace(/^#+\s*/, ''));
    if (parsedIng.length) setIngredients(parsedIng);
    if (parsedSteps.length) setSteps(parsedSteps);
    setMode('manual'); // review & save
  };
  const [scanPhase, setScanPhase] = useStateS2('aim'); // aim → scanning → done

  const heroOptions = ['🍽', '🍝', '🥗', '🍲', '🍛', '🌮', '🍕', '🍔', '🥪', '🍳', '🥘', '🍜', '🥙', '🍱', '🍰'];

  useEffectS2(() => {
    if (aiPhase === 'thinking') {
      const t1 = setTimeout(() => {
        setName(lang === 'fr' ? 'Bol-burrito au poulet et avocat' : 'Chicken avocado burrito bowl');
        setHero('🌮');
        setMinutes(25);
        setIngredients([
          { name: lang === 'fr' ? 'Poulet' : 'Chicken', qty: '400 g' },
          { name: lang === 'fr' ? 'Avocat' : 'Avocado', qty: '1' },
          { name: lang === 'fr' ? 'Riz' : 'Rice', qty: '200 g' },
          { name: lang === 'fr' ? 'Haricots noirs' : 'Black beans', qty: '1 boîte' },
          { name: lang === 'fr' ? 'Maïs' : 'Corn', qty: '150 g' },
          { name: lang === 'fr' ? 'Lime' : 'Lime', qty: '1' },
        ]);
        setSteps([
          lang === 'fr' ? 'Faire cuire le riz selon les instructions.' : 'Cook rice per package directions.',
          lang === 'fr' ? 'Griller le poulet 6 min de chaque côté.' : 'Grill chicken 6 min per side.',
          lang === 'fr' ? 'Assembler dans des bols avec haricots, maïs, avocat tranché.' : 'Assemble in bowls with beans, corn, sliced avocado.',
          lang === 'fr' ? 'Servir avec un quartier de lime.' : 'Serve with a lime wedge.',
        ]);
        setAiPhase('done');
        setMode('manual');
      }, 2200);
      return () => clearTimeout(t1);
    }
  }, [aiPhase]);

  useEffectS2(() => {
    if (scanPhase === 'scanning') {
      const t1 = setTimeout(() => {
        setName(lang === 'fr' ? 'Tarte aux pommes de grand-maman' : 'Grandma\'s apple pie');
        setHero('🥧');
        setMinutes(70);
        setIngredients([
          { name: lang === 'fr' ? 'Pommes' : 'Apples', qty: '6' },
          { name: lang === 'fr' ? 'Pâte brisée' : 'Pie dough', qty: '2 abaisses' },
          { name: lang === 'fr' ? 'Sucre' : 'Sugar', qty: '180 g' },
          { name: lang === 'fr' ? 'Cannelle' : 'Cinnamon', qty: '1 c.t.' },
          { name: lang === 'fr' ? 'Beurre' : 'Butter', qty: '30 g' },
        ]);
        setSteps([
          lang === 'fr' ? 'Préchauffer le four à 200°C.' : 'Preheat oven to 400°F (200°C).',
          lang === 'fr' ? 'Éplucher et trancher les pommes, mélanger avec sucre et cannelle.' : 'Peel and slice apples, toss with sugar and cinnamon.',
          lang === 'fr' ? 'Foncer le moule avec la pâte, garnir.' : 'Line the pan with dough, fill.',
          lang === 'fr' ? 'Couvrir, cuire 50 min.' : 'Cover with top crust, bake 50 min.',
        ]);
        setScanPhase('done');
        setMode('manual');
      }, 2200);
      return () => clearTimeout(t1);
    }
  }, [scanPhase]);

  // ── pick mode
  if (mode === 'pick') {
    const opts = [
      { id: 'manual', icon: 'pen', title: lang === 'fr' ? 'Saisir manuellement' : 'Enter manually', desc: lang === 'fr' ? 'Ingrédients et étapes' : 'Ingredients and steps', tone: palette.primary },
      { id: 'ai', icon: 'sparkle', title: lang === 'fr' ? 'Générer avec IA' : 'Generate with AI', desc: lang === 'fr' ? 'Décris ce que tu veux cuisiner' : 'Describe what you want to cook', tone: palette.amber, ai: true },
      { id: 'scan', icon: 'camera', title: lang === 'fr' ? 'Scanner un livre' : 'Scan a cookbook', desc: lang === 'fr' ? 'Photo de la page' : 'Photo of the page', tone: palette.blue, ai: true },
      { id: 'import', icon: 'box', title: lang === 'fr' ? 'Importer (coller le texte)' : 'Import (paste text)', desc: lang === 'fr' ? 'Colle une recette d\'un site ou d\'une note' : 'Paste a recipe from a site or note', tone: palette.ink2 },
    ];
    return (
      <div style={{ padding: '8px 20px 28px' }}>
        <SheetHeader palette={palette} onClose={onClose}
          title={lang === 'fr' ? 'Nouvelle recette' : 'New recipe'}
          sub={lang === 'fr' ? 'Choisis ta méthode' : 'Pick a method'}
          accent={{ bg: palette.primarySoft, ink: palette.primarySoftInk, icon: 'chef' }} />
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {opts.map(m => (
            <div key={m.id} onClick={() => setMode(m.id)} style={{
              background: palette.card, border: `1px solid ${palette.line}`,
              borderRadius: 18, padding: 16, cursor: 'pointer',
              display: 'flex', alignItems: 'center', gap: 14,
            }}>
              <div style={{
                width: 46, height: 46, borderRadius: 14,
                background: hexToSoft(m.tone), color: m.tone,
                display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
              }}>
                <Icon name={m.icon} size={22} style="line" color={m.tone} />
              </div>
              <div style={{ flex: 1 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                  <div style={{ fontSize: 15, fontWeight: 600, color: palette.ink, letterSpacing: -0.2 }}>{m.title}</div>
                  {m.ai && <span style={{
                    fontSize: 9.5, fontFamily: 'Geist Mono, monospace', fontWeight: 600,
                    padding: '2px 6px', borderRadius: 5, background: palette.amber, color: '#fff', letterSpacing: 0.06,
                  }}>AI</span>}
                </div>
                <div style={{ fontSize: 12, color: palette.ink3, marginTop: 2 }}>{m.desc}</div>
              </div>
              <Icon name="chevR" size={16} color={palette.ink3} />
            </div>
          ))}
        </div>
      </div>
    );
  }

  // ── Import mode (paste text) ──
  if (mode === 'import') {
    return (
      <div style={{ padding: '8px 20px 28px' }}>
        <SheetHeader palette={palette} onClose={() => setMode('pick')}
          title={lang === 'fr' ? 'Importer une recette' : 'Import a recipe'}
          sub={lang === 'fr' ? 'Colle le texte — on le structure pour toi' : 'Paste the text — we structure it'}
          accent={{ bg: palette.primarySoft, ink: palette.primarySoftInk, icon: 'box' }} />
        <textarea value={importText} onChange={e => setImportText(e.target.value)} autoFocus
          placeholder={lang === 'fr'
            ? 'Titre\n\nIngrédients\n2 tasses de farine\n3 œufs\n…\n\nPréparation\n1. Mélanger…\n2. Cuire…'
            : 'Title\n\nIngredients\n2 cups flour\n3 eggs\n…\n\nInstructions\n1. Mix…\n2. Bake…'}
          style={{
            width: '100%', minHeight: 240, boxSizing: 'border-box', resize: 'vertical',
            background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 14,
            padding: '12px 14px', fontSize: 14, lineHeight: 1.5, color: palette.ink,
            outline: 'none', fontFamily: 'inherit', marginTop: 4,
          }} />
        <div style={{ fontSize: 11.5, color: palette.ink3, marginTop: 8, lineHeight: 1.4 }}>
          {lang === 'fr'
            ? 'Astuce : ajoute les en-têtes « Ingrédients » et « Préparation » pour un meilleur résultat. (L\'import direct par lien web n\'est pas possible côté navigateur.)'
            : 'Tip: include "Ingredients" and "Instructions" headers for best results. (Direct web-link import isn\'t possible from the browser.)'}
        </div>
        <div onClick={parseImport} style={{
          marginTop: 16, padding: '15px', borderRadius: 14,
          background: importText.trim() ? palette.primary : palette.line,
          color: importText.trim() ? palette.primaryInk : palette.ink3,
          textAlign: 'center', fontWeight: 500, fontSize: 15, cursor: importText.trim() ? 'pointer' : 'default',
          display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        }}>
          <Icon name="sparkle" size={17} color={importText.trim() ? palette.primaryInk : palette.ink3} />
          {lang === 'fr' ? 'Structurer la recette' : 'Structure the recipe'}
        </div>
      </div>
    );
  }

  // ── AI mode
  if (mode === 'ai') {
    return (
      <div style={{ padding: '8px 20px 28px' }}>
        <SheetHeader palette={palette} onClose={onClose}
          title={lang === 'fr' ? 'Générer avec IA' : 'Generate with AI'}
          sub={lang === 'fr' ? 'L\'IA propose selon ton frigo' : 'AI builds from your fridge'}
          accent={{ bg: palette.amberSoft, ink: palette.amberSoftInk, icon: 'sparkle' }} />
        <Field2 palette={palette} label={lang === 'fr' ? 'Que veux-tu cuisiner ?' : 'What do you want to cook?'}>
          <textarea value={aiPrompt} onChange={e => setAiPrompt(e.target.value)} rows={3}
            placeholder={lang === 'fr' ? 'Ex: quelque chose avec le poulet qui expire, rapide, mexicain' : 'e.g. something with the chicken that\'s expiring, quick, Mexican'}
            style={{
              width: '100%', background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
              padding: '12px 14px', fontSize: 14, color: palette.ink, outline: 'none',
              fontFamily: 'inherit', resize: 'none', boxSizing: 'border-box',
            }} />
        </Field2>
        {aiPhase === 'thinking' ? (
          <div style={{
            padding: '20px 16px', borderRadius: 14,
            background: palette.amberSoft, color: palette.amberSoftInk,
            display: 'flex', alignItems: 'center', gap: 12, marginTop: 8,
          }}>
            <div style={{
              width: 32, height: 32, borderRadius: 16, background: palette.amber, color: '#fff',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>
              <Icon name="sparkle" size={16} color="#fff" />
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 13.5, fontWeight: 600 }}>
                {lang === 'fr' ? 'L\'IA cherche…' : 'AI is thinking…'}
              </div>
              <div style={{ fontSize: 11.5, opacity: 0.8, marginTop: 2 }}>
                {lang === 'fr' ? 'Croise tes aliments et ton régime' : 'Crossing your items and diet'}
              </div>
            </div>
          </div>
        ) : (
          <div onClick={() => aiPrompt.trim() && setAiPhase('thinking')} style={{
            padding: '15px', borderRadius: 14,
            background: aiPrompt.trim() ? palette.amber : palette.line,
            color: aiPrompt.trim() ? '#fff' : palette.ink3,
            textAlign: 'center', fontWeight: 500, fontSize: 15, cursor: aiPrompt.trim() ? 'pointer' : 'default',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, marginTop: 8,
          }}>
            <Icon name="sparkle" size={18} color={aiPrompt.trim() ? '#fff' : palette.ink3} />
            {lang === 'fr' ? 'Générer la recette' : 'Generate recipe'}
          </div>
        )}
      </div>
    );
  }

  // ── Scan mode
  if (mode === 'scan') {
    return (
      <div style={{ padding: '8px 20px 28px' }}>
        <SheetHeader palette={palette} onClose={onClose}
          title={lang === 'fr' ? 'Scanner un livre' : 'Scan a cookbook'}
          sub={lang === 'fr' ? 'L\'IA lit ta page de recette' : 'AI reads your recipe page'}
          accent={{ bg: palette.blueSoft, ink: palette.blue, icon: 'camera' }} />
        <div style={{
          position: 'relative', borderRadius: 18, overflow: 'hidden',
          aspectRatio: '4 / 5', background: '#1A1F1C',
          backgroundImage: 'linear-gradient(180deg, #2C2823 0%, #1A1F1C 100%)',
        }}>
          {/* Fake recipe page */}
          <div style={{
            position: 'absolute', inset: '8% 12%',
            background: '#F5EFE0', borderRadius: 4,
            padding: '14px 12px', fontFamily: 'Georgia, serif',
            display: 'flex', flexDirection: 'column', gap: 6,
            boxShadow: '0 12px 32px rgba(0,0,0,0.4)',
            transform: 'rotate(-1.2deg)',
            filter: scanPhase === 'scanning' ? 'brightness(1.08)' : 'none',
          }}>
            <div style={{ fontSize: 12, color: '#5A4830', fontStyle: 'italic' }}>{lang === 'fr' ? 'Chapitre 4' : 'Chapter 4'}</div>
            <div style={{ fontSize: 16, color: '#1F1A12', fontWeight: 700 }}>{lang === 'fr' ? 'Tarte aux pommes' : 'Apple pie'}</div>
            <div style={{ fontSize: 9, color: '#5A4830', lineHeight: 1.5 }}>
              {Array.from({ length: 10 }).map((_, i) => (
                <div key={i} style={{ height: 6, background: '#5A4830', opacity: 0.18 - i * 0.005, marginBottom: 3, borderRadius: 1 }} />
              ))}
            </div>
          </div>
          {scanPhase === 'aim' && (
            <div style={{
              position: 'absolute', inset: '6% 10%', borderRadius: 6,
              border: '2px dashed rgba(255,255,255,0.5)',
            }} />
          )}
          {scanPhase === 'scanning' && (
            <>
              <div className="ai-scan-bar" style={{
                position: 'absolute', left: '8%', right: '8%', height: 50,
                background: `linear-gradient(180deg, transparent 0%, ${palette.amber}40 50%, transparent 100%)`,
                borderTop: `1.5px solid ${palette.amber}`,
              }} />
              <div style={{
                position: 'absolute', left: '50%', top: '50%', transform: 'translate(-50%,-50%)',
                padding: '8px 14px', borderRadius: 999,
                background: 'rgba(255,255,255,0.95)', color: '#1F2A24',
                fontSize: 12, fontFamily: 'Geist Mono, monospace', fontWeight: 500,
                display: 'flex', alignItems: 'center', gap: 8,
              }}>
                <Icon name="sparkle" size={14} color={palette.amber} />
                {lang === 'fr' ? 'Lecture en cours…' : 'Reading page…'}
              </div>
            </>
          )}
        </div>
        <div onClick={() => scanPhase === 'aim' && setScanPhase('scanning')} style={{
          marginTop: 14, padding: '15px', borderRadius: 14,
          background: scanPhase === 'aim' ? palette.ink : palette.line,
          color: scanPhase === 'aim' ? palette.bg : palette.ink3,
          textAlign: 'center', fontWeight: 500, fontSize: 15, cursor: scanPhase === 'aim' ? 'pointer' : 'default',
          display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        }}>
          <Icon name="camera" size={18} color={scanPhase === 'aim' ? palette.bg : palette.ink3} />
          {t(lang, 'tapToScan')}
        </div>
      </div>
    );
  }

  // ── Manual mode (also used after AI/scan to confirm)
  const canSave = name.trim().length > 0 && ingredients.some(ing => ing.name.trim());
  return (
    <div style={{ padding: '8px 20px 28px' }}>
      <SheetHeader palette={palette} onClose={onClose}
        title={lang === 'fr' ? 'Détails de la recette' : 'Recipe details'}
        sub={aiPhase === 'done' ? (lang === 'fr' ? 'Suggéré par IA — édite à ton goût' : 'AI draft — edit as you like') : scanPhase === 'done' ? (lang === 'fr' ? 'Lu depuis le livre — édite à ton goût' : 'Scanned — edit as you like') : null}
        accent={{ bg: palette.primarySoft, ink: palette.primarySoftInk, icon: 'pen' }} />

      <Field2 palette={palette} label={t(lang, 'name')}>
        <input value={name} onChange={e => setName(e.target.value)} placeholder={lang === 'fr' ? 'Ex : Soupe miso' : 'e.g. Miso soup'} style={{
          width: '100%', height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
          padding: '0 14px', fontSize: 15, color: palette.ink, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box',
        }} />
      </Field2>

      <Field2 palette={palette} label={lang === 'fr' ? 'Icône' : 'Icon'}>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {heroOptions.map(e => (
            <div key={e} onClick={() => setHero(e)} style={{
              width: 40, height: 40, borderRadius: 10, fontSize: 22,
              background: hero === e ? palette.primarySoft : palette.card,
              border: `1.5px solid ${hero === e ? palette.primary : palette.line}`,
              display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
            }}>{e}</div>
          ))}
        </div>
      </Field2>

      <div style={{ display: 'flex', gap: 12 }}>
        <Field2 palette={palette} label={t(lang, 'minutes')}>
          <input type="number" value={minutes} onChange={e => setMinutes(parseInt(e.target.value) || 0)} style={{
            width: 110, height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
            padding: '0 14px', fontSize: 17, color: palette.ink, outline: 'none', fontFamily: 'Geist Mono, monospace', boxSizing: 'border-box',
          }} />
        </Field2>
        <Field2 palette={palette} label={t(lang, 'portions')}>
          <input type="number" value={portions} onChange={e => setPortions(parseInt(e.target.value) || 1)} style={{
            width: 110, height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
            padding: '0 14px', fontSize: 17, color: palette.ink, outline: 'none', fontFamily: 'Geist Mono, monospace', boxSizing: 'border-box',
          }} />
        </Field2>
      </div>

      <Field2 palette={palette} label={t(lang, 'ingredients')}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          {ingredients.map((ing, i) => (
            <div key={i} style={{ display: 'flex', gap: 6 }}>
              <input value={ing.name} onChange={e => setIngredients(ingredients.map((x, j) => j === i ? { ...x, name: e.target.value } : x))}
                placeholder={lang === 'fr' ? 'Nom' : 'Name'} style={{
                flex: 1, height: 38, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 10,
                padding: '0 12px', fontSize: 13.5, color: palette.ink, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box',
              }} />
              <input value={ing.qty} onChange={e => setIngredients(ingredients.map((x, j) => j === i ? { ...x, qty: e.target.value } : x))}
                placeholder={lang === 'fr' ? 'Qté' : 'Qty'} style={{
                width: 90, height: 38, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 10,
                padding: '0 12px', fontSize: 13, color: palette.ink, outline: 'none', fontFamily: 'Geist Mono, monospace', boxSizing: 'border-box',
              }} />
              <div onClick={() => setIngredients(ingredients.filter((_, j) => j !== i))} style={{
                width: 38, height: 38, borderRadius: 10, background: palette.cardSub, border: `1px solid ${palette.line}`,
                display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', color: palette.ink3,
              }}>
                <Icon name="close" size={13} color={palette.ink3} />
              </div>
            </div>
          ))}
        </div>
        <div onClick={() => setIngredients([...ingredients, { name: '', qty: '' }])} style={{
          marginTop: 8, padding: '8px 12px', borderRadius: 10,
          border: `1px dashed ${palette.line}`, color: palette.ink2,
          fontSize: 12.5, fontWeight: 500, cursor: 'pointer', textAlign: 'center',
          display: 'inline-flex', alignItems: 'center', gap: 6,
        }}>
          <Icon name="plus" size={13} color={palette.ink2} />
          {lang === 'fr' ? 'Ajouter un ingrédient' : 'Add ingredient'}
        </div>
      </Field2>

      <Field2 palette={palette} label={t(lang, 'steps')}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          {steps.map((step, i) => (
            <div key={i} style={{ display: 'flex', gap: 6, alignItems: 'flex-start' }}>
              <div style={{
                width: 26, height: 26, borderRadius: 13, background: palette.primary, color: palette.primaryInk,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontSize: 11, fontWeight: 600, fontFamily: 'Geist Mono, monospace', flexShrink: 0, marginTop: 6,
              }}>{i + 1}</div>
              <textarea value={step} onChange={e => setSteps(steps.map((x, j) => j === i ? e.target.value : x))}
                rows={2} placeholder={lang === 'fr' ? `Étape ${i + 1}…` : `Step ${i + 1}…`}
                style={{
                  flex: 1, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 10,
                  padding: '8px 12px', fontSize: 13, color: palette.ink, outline: 'none', fontFamily: 'inherit',
                  resize: 'none', boxSizing: 'border-box',
                }} />
              <div onClick={() => setSteps(steps.filter((_, j) => j !== i))} style={{
                width: 32, height: 32, borderRadius: 8, background: palette.cardSub, border: `1px solid ${palette.line}`,
                display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', color: palette.ink3, marginTop: 4,
              }}>
                <Icon name="close" size={12} color={palette.ink3} />
              </div>
            </div>
          ))}
        </div>
        <div onClick={() => setSteps([...steps, ''])} style={{
          marginTop: 8, padding: '8px 12px', borderRadius: 10,
          border: `1px dashed ${palette.line}`, color: palette.ink2,
          fontSize: 12.5, fontWeight: 500, cursor: 'pointer', textAlign: 'center',
          display: 'inline-flex', alignItems: 'center', gap: 6,
        }}>
          <Icon name="plus" size={13} color={palette.ink2} />
          {lang === 'fr' ? 'Ajouter une étape' : 'Add step'}
        </div>
      </Field2>

      <div onClick={() => canSave && onAdd({
        id: 'cr_' + Date.now(),
        name: { fr: name, en: name },
        tagline: { fr: lang === 'fr' ? 'Recette perso' : 'My recipe', en: 'My recipe' },
        accent: palette.primary,
        bg1: palette.primary, bg2: palette.amber,
        minutes, portions, difficulty: { fr: 'Moyen', en: 'Medium' },
        prepTime: Math.ceil(minutes / 3), cookTime: minutes - Math.ceil(minutes / 3),
        hero,
        uses: [],
        mealType: minutes <= 12 ? 'breakfast' : (minutes <= 20 ? 'lunch' : 'dinner'),
        ingredients: ingredients.filter(ing => ing.name.trim()).map((ing, i) => ({
          id: 'ci_' + i, name: { fr: ing.name, en: ing.name },
          emoji: '🍴', qty: { fr: ing.qty, en: ing.qty }, missing: true,
        })),
        steps: steps.filter(s => s.trim()).map(s => ({ fr: s, en: s })),
        custom: true,
      })} style={{
        marginTop: 18, padding: '15px', borderRadius: 14,
        background: canSave ? palette.primary : palette.line,
        color: canSave ? palette.primaryInk : palette.ink3,
        textAlign: 'center', fontWeight: 500, fontSize: 15, cursor: canSave ? 'pointer' : 'default',
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
      }}>
        <Icon name="check" size={18} color={canSave ? palette.primaryInk : palette.ink3} />
        {t(lang, 'save')}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// AddGroceryDetailedSheet — name + qty + brand + store + autocomplete
// ─────────────────────────────────────────────────────────────
function AddGroceryDetailedSheet({ palette, lang, inventory, grocery, onClose, onAdd }) {
  const [name, setName] = useStateS2('');
  const [qty, setQty] = useStateS2('');
  const [unit, setUnit] = useStateS2('');
  const [brand, setBrand] = useStateS2('');
  const [store, setStore] = useStateS2('');
  const [notes, setNotes] = useStateS2('');

  // Build suggestions from inventory history + grocery + ITEM_SUGGESTIONS
  const allKnown = useMemoS2(() => {
    const set = new Map();
    inventory.forEach(i => set.set(localized(lang, i.name).toLowerCase(), { name: localized(lang, i.name), emoji: i.emoji, unit: localized(lang, i.unit) }));
    grocery.forEach(g => {
      const n = typeof g.name === 'string' ? g.name : localized(lang, g.name);
      set.set(n.toLowerCase(), { name: n, emoji: g.emoji, unit: '' });
    });
    ITEM_SUGGESTIONS.forEach(s => set.set(localized(lang, s.name).toLowerCase(), { name: localized(lang, s.name), emoji: s.emoji, unit: localized(lang, s.defaultUnit) }));
    return [...set.values()];
  }, [inventory, grocery, lang]);

  const filtered = name.trim()
    ? allKnown.filter(k => k.name.toLowerCase().includes(name.toLowerCase())).slice(0, 5)
    : [];

  const stores = ['IGA', 'Metro', 'Maxi', 'Provigo', 'Super C', 'Costco'];
  const brandSuggest = ['Compliments', 'Selection', 'Sélection', 'Sans nom', 'PC', 'Bio Naturel', 'Lactantia', 'Natrel'];

  const canAdd = name.trim().length > 0;

  return (
    <div style={{ padding: '8px 20px 28px' }}>
      <SheetHeader palette={palette} onClose={onClose}
        title={lang === 'fr' ? 'Ajouter à la liste' : 'Add to list'}
        sub={lang === 'fr' ? 'Quantité, marque, magasin' : 'Quantity, brand, store'}
        accent={{ bg: palette.primarySoft, ink: palette.primarySoftInk, icon: 'cart' }} />

      <Field2 palette={palette} label={t(lang, 'name')}>
        <input value={name} onChange={e => setName(e.target.value)} placeholder={t(lang, 'grocPh')} autoFocus style={{
          width: '100%', height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
          padding: '0 14px', fontSize: 15, color: palette.ink, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box',
        }} />
        {filtered.length > 0 && (
          <div style={{
            marginTop: 6, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
            overflow: 'hidden',
          }}>
            {filtered.map((k, i) => (
              <div key={i} onClick={() => { setName(k.name); if (k.unit) setUnit(k.unit); }} style={{
                display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px',
                borderBottom: i < filtered.length - 1 ? `1px solid ${palette.line2}` : 'none',
                cursor: 'pointer',
              }}>
                <div style={{ fontSize: 18 }}>{k.emoji}</div>
                <div style={{ flex: 1, fontSize: 13.5, color: palette.ink, fontWeight: 500 }}>{k.name}</div>
                <Icon name="search" size={11} color={palette.ink3} />
              </div>
            ))}
          </div>
        )}
      </Field2>

      <Field2 palette={palette} label={t(lang, 'setQty')}>
        <div style={{ display: 'flex', gap: 8 }}>
          <input value={qty} onChange={e => setQty(e.target.value)} placeholder={lang === 'fr' ? '2' : '2'} style={{
            flex: 1, height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
            padding: '0 14px', fontSize: 16, color: palette.ink, outline: 'none', fontFamily: 'Geist Mono, monospace', boxSizing: 'border-box', textAlign: 'center',
          }} />
          <input value={unit} onChange={e => setUnit(e.target.value)} placeholder={lang === 'fr' ? 'L · g · pcs' : 'L · g · pcs'} style={{
            flex: 1, height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
            padding: '0 14px', fontSize: 14, color: palette.ink, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box',
          }} />
        </div>
      </Field2>

      <Field2 palette={palette} label={lang === 'fr' ? 'Marque (optionnel)' : 'Brand (optional)'}>
        <input value={brand} onChange={e => setBrand(e.target.value)} placeholder={lang === 'fr' ? 'Ex : Lactantia, Sans nom' : 'e.g. Lactantia, Compliments'} style={{
          width: '100%', height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
          padding: '0 14px', fontSize: 14, color: palette.ink, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box',
        }} />
        <div style={{ display: 'flex', gap: 4, marginTop: 6, flexWrap: 'wrap' }}>
          {brandSuggest.slice(0, 4).map(b => (
            <div key={b} onClick={() => setBrand(b)} style={{
              padding: '4px 9px', borderRadius: 999,
              background: brand === b ? palette.ink : palette.cardSub,
              color: brand === b ? palette.bg : palette.ink3,
              border: `1px solid ${brand === b ? palette.ink : palette.line2}`,
              fontSize: 10.5, fontFamily: 'Geist Mono, monospace', fontWeight: 500, cursor: 'pointer',
            }}>{b}</div>
          ))}
        </div>
      </Field2>

      <Field2 palette={palette} label={lang === 'fr' ? 'Magasin (où l\'acheter)' : 'Store (where to buy)'}>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {stores.map(s => (
            <div key={s} onClick={() => setStore(store === s ? '' : s)} style={{
              padding: '7px 12px', borderRadius: 999,
              background: store === s ? palette.primary : palette.card,
              color: store === s ? palette.primaryInk : palette.ink2,
              border: `1px solid ${store === s ? palette.primary : palette.line}`,
              fontSize: 12.5, fontWeight: 500, cursor: 'pointer',
            }}>{s}</div>
          ))}
        </div>
        {store && (
          <div style={{
            marginTop: 8, padding: '8px 12px', borderRadius: 10,
            background: palette.amberSoft, color: palette.amberSoftInk,
            fontSize: 11.5, display: 'flex', alignItems: 'center', gap: 8,
          }}>
            <Icon name="sparkle" size={13} color={palette.amberSoftInk} />
            {lang === 'fr' ? `On vérifie les circulaires de ${store}…` : `Checking ${store} flyer…`}
          </div>
        )}
      </Field2>

      <Field2 palette={palette} label={lang === 'fr' ? 'Note (optionnel)' : 'Note (optional)'}>
        <input value={notes} onChange={e => setNotes(e.target.value)} placeholder={lang === 'fr' ? 'Ex : sans sucre, bio…' : 'e.g. unsweetened, organic…'} style={{
          width: '100%', height: 44, background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12,
          padding: '0 14px', fontSize: 14, color: palette.ink, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box',
        }} />
      </Field2>

      <div onClick={() => canAdd && onAdd({
        name: name.trim(),
        emoji: filtered[0]?.emoji || '🛒',
        aisle: 'fresh',
        qty: qty && unit ? `${qty} ${unit}` : (qty || ''),
        brand: brand.trim(),
        store: store,
        notes: notes.trim(),
        checked: false,
        source: 'manual',
      })} style={{
        marginTop: 18, padding: '15px', borderRadius: 14,
        background: canAdd ? palette.primary : palette.line,
        color: canAdd ? palette.primaryInk : palette.ink3,
        textAlign: 'center', fontWeight: 500, fontSize: 15, cursor: canAdd ? 'pointer' : 'default',
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
      }}>
        <Icon name="plus" size={18} color={canAdd ? palette.primaryInk : palette.ink3} />
        {t(lang, 'add')}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// AdminPanel — owner-only dashboard: users, accounts, activity, manage.
// Visible only when /api/admin/* returns 200 (gated by ADMIN_EMAIL server-side).
// ─────────────────────────────────────────────────────────────
function AdminPanel({ palette, lang, onClose }) {
  const [stats, setStats] = useStateS2(null);
  const [users, setUsers] = useStateS2(null);
  const [err, setErr] = useStateS2('');
  const [busy, setBusy] = useStateS2(true);
  const load = () => {
    setBusy(true); setErr('');
    const api = window.PWAuth && window.PWAuth.api;
    Promise.all([api.adminStats(), api.adminUsers()])
      .then(([s, u]) => { setStats(s); setUsers(u.users || []); })
      .catch(e => setErr(e.message || 'Erreur'))
      .finally(() => setBusy(false));
  };
  useEffectS2(() => { load(); }, []);
  const fmtDate = (ts) => { try { return ts ? new Date(ts).toLocaleDateString(lang === 'fr' ? 'fr-CA' : 'en-CA', { year: '2-digit', month: 'short', day: 'numeric' }) : '—'; } catch (e) { return '—'; } };
  const ago = (ts) => {
    if (!ts) return lang === 'fr' ? 'jamais' : 'never';
    const d = Math.floor((Date.now() - ts) / 86400000);
    if (d <= 0) return lang === 'fr' ? "aujourd'hui" : 'today';
    if (d === 1) return lang === 'fr' ? 'hier' : 'yesterday';
    return lang === 'fr' ? `il y a ${d}j` : `${d}d ago`;
  };
  const removeUser = (u) => {
    const name = u.name || u.email;
    if (!window.confirm(lang === 'fr' ? `Supprimer ${name} et ses données ? Irréversible.` : `Delete ${name} and their data? Irreversible.`)) return;
    window.PWAuth.api.adminDeleteUser(u.id).then(load).catch(e => alert(e.message || 'Erreur'));
  };
  const statCards = stats ? [
    { l: lang === 'fr' ? 'Comptes' : 'Accounts', v: stats.users },
    { l: lang === 'fr' ? 'Familles' : 'Households', v: stats.households },
    { l: lang === 'fr' ? 'Actifs 7j' : 'Active 7d', v: stats.activeUsers7d },
    { l: lang === 'fr' ? 'Inscrits 7j' : 'Signups 7d', v: stats.signups7d },
    { l: lang === 'fr' ? 'Inscrits 30j' : 'Signups 30d', v: stats.signups30d },
    { l: lang === 'fr' ? 'Notifs push' : 'Push subs', v: stats.pushSubs },
  ] : [];
  return (
    <div style={{ padding: '8px 20px 28px' }}>
      <SheetHeader palette={palette} onClose={onClose}
        title={lang === 'fr' ? 'Tableau de bord' : 'Admin dashboard'}
        sub={lang === 'fr' ? 'Comptes et activité' : 'Accounts and activity'}
        accent={{ bg: palette.primarySoft, ink: palette.primarySoftInk, icon: 'users' }} />

      {busy && <div style={{ padding: '40px 0', textAlign: 'center', color: palette.ink3, fontSize: 14 }}>{lang === 'fr' ? 'Chargement…' : 'Loading…'}</div>}
      {err && <div style={{ background: palette.redSoft, color: palette.redSoftInk, padding: '12px 14px', borderRadius: 12, fontSize: 13 }}>{err}</div>}

      {stats && (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8, marginBottom: 18 }}>
          {statCards.map((c, i) => (
            <div key={i} style={{ background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12, padding: '12px 10px', textAlign: 'center' }}>
              <div style={{ fontSize: 22, fontWeight: 700, color: palette.ink, letterSpacing: -0.4 }}>{c.v}</div>
              <div style={{ fontSize: 10, color: palette.ink3, marginTop: 2, fontFamily: 'Geist Mono, monospace', textTransform: 'uppercase', letterSpacing: 0.04 }}>{c.l}</div>
            </div>
          ))}
        </div>
      )}

      {users && (
        <>
          <div style={{ display: 'flex', alignItems: 'center', marginBottom: 8 }}>
            <div style={{ flex: 1, fontSize: 11, fontFamily: 'Geist Mono, monospace', color: palette.ink3, letterSpacing: 0.06, textTransform: 'uppercase' }}>
              {lang === 'fr' ? `Comptes · ${users.length}` : `Accounts · ${users.length}`}
            </div>
            <div onClick={load} style={{ fontSize: 12, color: palette.primary, cursor: 'pointer', fontWeight: 500 }}>{lang === 'fr' ? '↻ Rafraîchir' : '↻ Refresh'}</div>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {users.map(u => (
              <div key={u.id} style={{ background: palette.card, border: `1px solid ${palette.line}`, borderRadius: 12, padding: '10px 12px', display: 'flex', alignItems: 'center', gap: 10 }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13.5, fontWeight: 600, color: palette.ink, display: 'flex', alignItems: 'center', gap: 6 }}>
                    <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{u.name || '—'}</span>
                    {u.active_sessions > 0 && <span style={{ width: 7, height: 7, borderRadius: 4, background: palette.primary, flexShrink: 0 }} title="actif" />}
                  </div>
                  <div style={{ fontSize: 11.5, color: palette.ink2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{u.email}</div>
                  <div style={{ fontSize: 10.5, color: palette.ink3, marginTop: 2, fontFamily: 'Geist Mono, monospace' }}>
                    {lang === 'fr' ? 'inscrit' : 'joined'} {fmtDate(u.created_at)} · {lang === 'fr' ? 'vu' : 'seen'} {ago(u.last_session)}{u.household_size > 1 ? ` · 👥${u.household_size}` : ''}
                  </div>
                </div>
                <div onClick={() => removeUser(u)} style={{
                  width: 32, height: 32, borderRadius: 9, flexShrink: 0, cursor: 'pointer',
                  background: palette.redSoft, color: palette.redSoftInk,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}><Icon name="trash" size={14} color={palette.redSoftInk} /></div>
              </div>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

Object.assign(window, {
  EditItemSheet, MoveItemSheet, UsedSomeSheet, MealEditSheet,
  InviteFamilySheet, AddRecipeSheet, AddGroceryDetailedSheet, AdminPanel,
});
