// portal-actions.jsx — action screens: Profile, Enroll, Redeem, F&B, Wellness

// ═══════════════════════════════════════════════════════════════
// MEMBER PROFILE
// ═══════════════════════════════════════════════════════════════

function ProfileScreen({ memberId }) {
  const raw = useStore(s => s.members.find(m => m.id === memberId));
  const toast = useToast();
  const confirmDlg = useConfirm();
  const [modal, setModal] = React.useState(null); // {kind, ...payload}
  const [editing, setEditing] = React.useState(false);

  if (!raw) {
    return (
      <div>
        <PageHeader title="Member not found" eyebrow="404" />
        <Card flat><div className="pt-mute">No member with id <b>{memberId}</b>. <span className="pt-link" onClick={() => navigate('members')}>← Back to list</span></div></Card>
      </div>
    );
  }

  // Defensive normalisation — older or remote-hydrated members may be missing
  // nested objects. Black-screen-proof the entire ProfileScreen by always
  // working with a fully-populated copy.
  const member = Object.assign({
    benefits: {},
    spend: { rooms: 0, fnb: 0, wellness: 0, discount: 0 },
    points: { balance: 0, lifetime: 0, history: [] },
    redemptions: [],
    cardStatus: 'active',
    cardNumber: raw.id,
    status: 'active',
  }, raw);
  member.benefits = Object.assign({
    compNightsUsed: 0, compNightsTotal: 0, poolVisits: 0, gymVisits: 0,
    birthdayCakeUsed: false, anniversaryCakeUsed: false,
    suiteUpgradeUsed: 0, suiteUpgradeTotal: 0,
    hallUsesUsed: 0, hallUsesTotal: 0, hallHoursPerUse: 4,
    spaPassesUsed: 0, spaPassesTotal: 0,
  }, raw.benefits || {});
  member.spend = Object.assign({ rooms: 0, fnb: 0, wellness: 0, discount: 0 }, raw.spend || {});
  member.points = Object.assign({ balance: 0, lifetime: 0, history: [] }, raw.points || {});
  if (!Array.isArray(member.points.history)) member.points.history = [];
  if (!Array.isArray(member.redemptions)) member.redemptions = raw.redemptions || [];

  function doRedeem(kind, payload, successMsg) {
    const res = Store.redeem(member.id, kind, payload || {});
    if (res.ok) {
      toast.show(successMsg || 'Redemption recorded');
      setModal(null);
    } else {
      toast.show(res.error, { bad: true });
    }
  }

  const isWellness = member.type === 'wellness';
  const b = member.benefits;
  const compLeft = (b.compNightsTotal || 0) - (b.compNightsUsed || 0);

  return (
    <div>
      <div className="pt-row" style={{ gap: 8, marginBottom: 14, justifyContent: 'space-between' }}>
        <Button variant="ghost" size="sm" onClick={() => navigate('members')}>← Back to members</Button>
        <div className="pt-row" style={{ gap: 8, flexWrap: 'wrap' }}>
          <Button size="sm" onClick={() => printMemberCard(member)}>🖨 Print card</Button>
          {Store.can('edit') && (
            <Button
              size="sm"
              variant={member.status === 'active' ? 'ghost' : 'gold'}
              onClick={() => {
                const next = member.status === 'active' ? 'inactive' : 'active';
                Store.updateMember(member.id, { status: next });
                Store.log({
                  who: Store.getState().session.staff, role: Store.getState().session.role,
                  property: Store.getState().session.property,
                  action: `Membership ${next} · ${member.name}`, memberId: member.id,
                });
                toast.show(`Membership marked ${next}`);
              }}
              title="Toggle membership active/inactive"
            >
              {member.status === 'active' ? '⏸ Set inactive' : '▶ Set active'}
            </Button>
          )}
          {Store.can('edit') && <Button size="sm" onClick={() => setEditing(true)}>✎ Edit details</Button>}
          {member.deleted && Store.can('delete') && (
            <Button size="sm" onClick={() => {
              Store.restoreMember(member.id);
              toast.show(`Restored · ${member.name}`);
            }}>↺ Restore</Button>
          )}
          {!member.deleted && Store.can('delete') && (
            <Button size="sm" variant="danger" onClick={async () => {
              const ok = await confirmDlg(`${member.name} will be hidden from all lists but kept in the database. A Super Admin can restore them later.`, { title: `Delete ${member.name}?`, danger: true, confirmLabel: 'Delete' });
              if (ok) { Store.deleteMember(member.id); toast.show(`Deleted · ${member.name}`); navigate('members'); }
            }}>✕ Delete member</Button>
          )}
        </div>
      </div>

      <Card style={{ marginBottom: 14 }}>
        <div className="pt-row" style={{ gap: 22, alignItems: 'flex-start', flexWrap: 'wrap' }}>
          <Avatar name={member.name} type={member.type} photo={member.photo} size={88} />
          <div className="pt-col" style={{ flex: 1, gap: 8, minWidth: 240 }}>
            <div className="pt-row" style={{ gap: 8, flexWrap: 'wrap' }}>
              <TierBadge type={member.type} />
              <StatusBadge status={member.status} />
              <Badge>since {fmtDate(member.startDate)}</Badge>
              <Badge>renews {fmtDate(member.expiryDate)}</Badge>
            </div>
            <h1 className="pt-h1">{member.name}</h1>
            <div className="pt-row" style={{ gap: 18, flexWrap: 'wrap', fontSize: 13 }} className="pt-mute">
              <span>📞 {member.phone || '—'}</span>
              {member.email && <span>✉ {member.email}</span>}
              {member.dob && <span>🎂 {fmtDate(member.dob)}</span>}
              {member.anniversary && <span>💍 {member.anniversary}</span>}
              <span>📍 {member.property}</span>
            </div>
            <div className="pt-row" style={{ gap: 14, marginTop: 4, flexWrap: 'wrap' }}>
              <span style={{ fontSize: 13 }}><span className="pt-faint">Member ID </span><b className="pt-num" style={{ color: isWellness ? T.gold : T.silver }}>{member.id}</b></span>
              {member.cardNumber && member.cardNumber !== member.id && (
                <span style={{ fontSize: 13 }}><span className="pt-faint">Card # </span><b className="pt-num">{member.cardNumber}</b></span>
              )}
              <span style={{ fontSize: 13 }}><span className="pt-faint">Sales rep </span><b>{member.salesRep}</b></span>
            </div>
          </div>
          <div className="pt-col" style={{ alignItems: 'center', gap: 8, minWidth: 220 }}>
            <div style={{ width: 220 }}>
              <CardPreview member={member} />
            </div>
          </div>
        </div>
      </Card>

      {/* Quick redeem strip */}
      <div className="pt-row" style={{ gap: 8, marginBottom: 14, flexWrap: 'wrap' }}>
        {isWellness && Store.can('assign-room') && (
          <Button variant="gold" onClick={() => setModal({ kind: 'room' })} disabled={compLeft <= 0}>
            ✓ Redeem comp room{compLeft <= 0 ? ' · 0 left' : ` · ${compLeft} left`}
          </Button>
        )}
        <Button onClick={() => setModal({ kind: 'fnb' })}>% Apply F&B discount</Button>
        <Button onClick={() => setModal({ kind: 'pool' })}>🏊 Log pool</Button>
        <Button onClick={() => setModal({ kind: 'gym' })}>🏋 Log gym</Button>
        {isWellness && <Button onClick={() => setModal({ kind: 'hall' })} disabled={b.hallUsesUsed >= b.hallUsesTotal}>🏛 Hall use{b.hallUsesTotal ? ` · ${b.hallUsesTotal - b.hallUsesUsed} left` : ''}</Button>}
        {isWellness && <Button onClick={() => setModal({ kind: 'spa' })} disabled={b.spaPassesUsed >= b.spaPassesTotal}>💫 Spa pass{b.spaPassesTotal ? ` · ${b.spaPassesTotal - b.spaPassesUsed} left` : ''}</Button>}
        {isWellness && <Button onClick={() => setModal({ kind: 'birthday-cake' })} disabled={b.birthdayCakeUsed}>🎂 Birthday cake</Button>}
        {isWellness && <Button onClick={() => setModal({ kind: 'anniversary-cake' })} disabled={b.anniversaryCakeUsed}>💍 Anniv cake</Button>}
        {isWellness && <Button onClick={() => setModal({ kind: 'upgrade' })} disabled={b.suiteUpgradeUsed >= b.suiteUpgradeTotal}>⤴ Suite upgrade</Button>}
        {Store.can('assign-room') && <Button onClick={() => setModal({ kind: 'room-charge' })}>🛏 Paid stay</Button>}
        <Button onClick={() => setModal({ kind: 'wellness-charge' })}>~ Spa / wellness charge</Button>
      </div>

      {/* Benefits panel (Wellness only) */}
      {isWellness && (
        <Card gold style={{ marginBottom: 14, padding: 18 }}>
          <div className="pt-row" style={{ justifyContent: 'space-between', marginBottom: 14 }}>
            <h3 className="pt-h2">Wellness benefits</h3>
            <span className="pt-faint" style={{ fontSize: 12 }}>cycle ends {fmtDate(member.expiryDate)}</span>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 }}>
            <BenefitTile icon="🛏" label="Complimentary nights" value={`${compLeft}`} sub={`of ${b.compNightsTotal} · ${b.compNightsUsed} used`} progress={[b.compNightsUsed, b.compNightsTotal]} />
            <BenefitTile icon="🏛" label="Hall use" value={`${b.hallUsesTotal - b.hallUsesUsed} left`} sub={`${b.hallHoursPerUse} hrs each · ${b.hallUsesUsed}/${b.hallUsesTotal} used`} progress={[b.hallUsesUsed, b.hallUsesTotal]} muted={b.hallUsesUsed >= b.hallUsesTotal} />
            <BenefitTile icon="💫" label="Spa passes" value={`${b.spaPassesTotal - b.spaPassesUsed} left`} sub={`of ${b.spaPassesTotal} · ${b.spaPassesUsed} used`} progress={[b.spaPassesUsed, b.spaPassesTotal]} muted={b.spaPassesUsed >= b.spaPassesTotal} />
            <BenefitTile icon="⤴" label="Suite upgrade" value={`${b.suiteUpgradeTotal - b.suiteUpgradeUsed} left`} sub="subject to availability" muted={b.suiteUpgradeUsed >= b.suiteUpgradeTotal} />
            <BenefitTile icon="🎂" label="Birthday cake" value={b.birthdayCakeUsed ? 'Used' : '1 available'} sub={b.birthdayCakeUsed ? 'redeemed this cycle' : 'window opens on birthday'} muted={b.birthdayCakeUsed} />
            <BenefitTile icon="💍" label="Anniversary cake" value={b.anniversaryCakeUsed ? 'Used' : '1 available'} sub={b.anniversaryCakeUsed ? 'redeemed' : member.anniversary || 'no date set'} muted={b.anniversaryCakeUsed} />
            <BenefitTile icon="%" label="F&B discount" value="20% off" sub="every visit · both properties" />
            <BenefitTile icon="🏊" label="Pool / gym" value={`${b.poolVisits} / ${b.gymVisits}`} sub="unlimited · year-round" />
          </div>
        </Card>
      )}

      {/* Silver tier balance */}
      {!isWellness && (
        <Card flat style={{ marginBottom: 14 }}>
          <h3 className="pt-h3" style={{ marginBottom: 14 }}>Silver tier · 10% across the board</h3>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 }}>
            <BenefitTile icon="🛏" label="Room spend YTD" value={fmtNPR(member.spend.rooms)} />
            <BenefitTile icon="🍽" label="F&B spend YTD" value={fmtNPR(member.spend.fnb)} />
            <BenefitTile icon="~" label="Wellness YTD" value={fmtNPR(member.spend.wellness)} />
            <BenefitTile icon="%" label="Discount given" value={fmtNPR(member.spend.discount)} sub="lifetime savings to member" />
          </div>
        </Card>
      )}

      {/* Loyalty points panel */}
      <LoyaltyPanel member={member} />

      {/* Expiry warning */}
      <ExpiryBanner member={member} />

      {/* History */}
      <div style={{ display: 'grid', gridTemplateColumns: '1.5fr 1fr', gap: 14 }}>
        <Card flat>
          <div className="pt-row" style={{ justifyContent: 'space-between', marginBottom: 12 }}>
            <h3 className="pt-h3">Visit & redemption history</h3>
            <span className="pt-faint" style={{ fontSize: 12 }}>{(member.redemptions || []).length} entries</span>
          </div>
          {(member.redemptions || []).length === 0 ? (
            <div className="pt-faint" style={{ padding: 20, textAlign: 'center' }}>No activity yet. Use the action bar above to log a redemption.</div>
          ) : (
            <div className="pt-col" style={{ gap: 0 }}>
              {member.redemptions.map((r, i) => (
                <div key={r.id} style={{
                  display: 'flex', gap: 12, padding: '12px 0',
                  borderBottom: i < member.redemptions.length - 1 ? '1px solid ' + T.borderSoft : 'none',
                }}>
                  <div style={{ width: 60, fontSize: 11, color: T.textFaint, lineHeight: 1.3 }} className="pt-num">
                    <div>{new Date(r.ts).toLocaleDateString('en-GB', { day: '2-digit', month: 'short' })}</div>
                    <div>{new Date(r.ts).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })}</div>
                  </div>
                  <div style={{ width: 24, fontSize: 16 }}>{redemptionIcon(r.kind)}</div>
                  <div className="pt-col" style={{ flex: 1, lineHeight: 1.3 }}>
                    <div style={{ fontWeight: 500, fontSize: 13 }}>{r.label}</div>
                    <div className="pt-faint" style={{ fontSize: 11 }}>{r.property} · {r.staff}{r.note ? ' · ' + r.note : ''}</div>
                  </div>
                </div>
              ))}
            </div>
          )}
        </Card>

        <Card flat>
          <h3 className="pt-h3" style={{ marginBottom: 12 }}>Personal details</h3>
          <div className="pt-col" style={{ gap: 8, fontSize: 13 }}>
            <DetailRow label="Gender" value={member.gender || '—'} />
            <DetailRow label="DOB" value={fmtDate(member.dob)} />
            <DetailRow label="Anniversary" value={member.anniversary || '—'} />
            <DetailRow label="Nationality" value={member.nationality} />
            <DetailRow label="Address" value={member.address || '—'} />
            <DetailRow label="Citizenship · PAN" value={member.citizenship || '—'} />
            <hr className="pt-divider" style={{ margin: '8px 0' }} />
            <DetailRow label="Payment" value={`${member.paymentStatus} · ${member.paymentMethod}`} />
            {member.paymentAmount > 0 && <DetailRow label="Amount paid" value={fmtNPR(member.paymentAmount)} />}
            <DetailRow label="Enrolled by" value={member.salesRep} />
            <DetailRow label="Created" value={fmtDateTime(member.createdAt)} />
          </div>
        </Card>
      </div>

      {/* Redemption modals */}
      <RedeemModal modal={modal} member={member} onClose={() => setModal(null)} onConfirm={doRedeem} />
      <EditMemberModal open={editing} member={member} onClose={() => setEditing(false)} />
    </div>
  );
}

function EditMemberModal({ open, member, onClose }) {
  const toast = useToast();
  const [form, setForm] = React.useState(member);
  React.useEffect(() => { setForm(member); }, [member, open]);
  if (!open) return null;
  const set = (k, v) => setForm(f => Object.assign({}, f, { [k]: v }));

  return (
    <Modal open title={`Edit · ${member.name}`} onClose={onClose} width={680} footer={
      <>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="gold" onClick={() => {
          Store.updateMember(member.id, {
            name: form.name, phone: form.phone, email: form.email,
            address: form.address, gender: form.gender,
            dob: form.dob, anniversary: form.anniversary,
            nationality: form.nationality, citizenship: form.citizenship,
            property: form.property, status: form.status,
            cardNumber: form.cardNumber,
            startDate: form.startDate, expiryDate: form.expiryDate,
            salesRep: form.salesRep,
          });
          Store.log({ who: Store.getState().session.staff, role: Store.getState().session.role,
            property: Store.getState().session.property,
            action: `Member details edited · ${form.name}`, memberId: member.id });
          toast.show('Member updated');
          onClose();
        }}>Save changes</Button>
      </>
    }>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        <Input label="Full name" value={form.name} onChange={v => set('name', v)} />
        <Input label="Phone" value={form.phone} onChange={v => set('phone', v)} />
        <Input label="Email" value={form.email} onChange={v => set('email', v)} />
        <Input label="Address" value={form.address} onChange={v => set('address', v)} />
        <Select label="Gender" value={form.gender || ''} onChange={v => set('gender', v)} options={[{ value: '', label: '—' }, { value: 'F', label: 'Female' }, { value: 'M', label: 'Male' }, { value: 'O', label: 'Other' }]} />
        <Input label="Date of birth" type="date" value={form.dob} onChange={v => set('dob', v)} />
        <Input label="Anniversary (MM-DD)" value={form.anniversary} onChange={v => set('anniversary', v)} />
        <Input label="Nationality" value={form.nationality} onChange={v => set('nationality', v)} />
        <Input label="Citizenship · PAN" value={form.citizenship} onChange={v => set('citizenship', v)} />
        <Select label="Home property" value={form.property} onChange={v => set('property', v)} options={['Biratchowk', 'Damak']} />
        <Input label="Start date" type="date" value={form.startDate} onChange={v => set('startDate', v)} />
        <Input label="Expiry" type="date" value={form.expiryDate} onChange={v => set('expiryDate', v)} />
        <Select label="Membership status" value={form.status} onChange={v => set('status', v)} options={['active', 'inactive', 'expired']} />
        <Input label="Physical card #" value={form.cardNumber || ''} onChange={v => set('cardNumber', v)} placeholder="from the printed card" />
        <Input label="Sales rep" value={form.salesRep} onChange={v => set('salesRep', v)} />
      </div>
      <div className="pt-faint" style={{ fontSize: 11, marginTop: 12 }}>
        ↳ tier is immutable. To change tier, soft-delete and re-enroll the member.
      </div>
    </Modal>
  );
}

function DetailRow({ label, value }) {
  return (
    <div className="pt-row" style={{ justifyContent: 'space-between', gap: 12 }}>
      <span className="pt-faint">{label}</span>
      <span style={{ fontWeight: 500, textAlign: 'right' }}>{value}</span>
    </div>
  );
}

function BenefitTile({ icon, label, value, sub, progress, muted }) {
  return (
    <div style={{
      background: 'rgba(255,255,255,0.025)',
      border: '1px solid ' + T.borderSoft,
      borderRadius: 10,
      padding: 14,
      opacity: muted ? 0.55 : 1,
    }}>
      <div className="pt-row" style={{ justifyContent: 'space-between', marginBottom: 6 }}>
        <span style={{ fontSize: 18 }}>{icon}</span>
        <div className="pt-eyebrow" style={{ fontSize: 10, textAlign: 'right' }}>{label}</div>
      </div>
      <div className="pt-display pt-num" style={{ fontSize: 24, fontWeight: 600, lineHeight: 1.1 }}>{value}</div>
      {sub && <div className="pt-faint" style={{ fontSize: 11, marginTop: 4 }}>{sub}</div>}
      {progress && <div style={{ marginTop: 8 }}><Progress value={progress[0]} max={progress[1]} gold /></div>}
    </div>
  );
}

function redemptionIcon(kind) {
  return { room: '🛏', 'birthday-cake': '🎂', 'anniversary-cake': '💍', upgrade: '⤴', pool: '🏊', gym: '🏋', hall: '🏛', spa: '💫', fnb: '%', 'room-charge': '🛏', 'wellness-charge': '~' }[kind] || '◆';
}

// ─── Reusable redemption modal ────────────────────────────────
function RedeemModal({ modal, member, onClose, onConfirm }) {
  if (!modal) return null;
  const isWellness = member.type === 'wellness';
  const discountPct = isWellness ? 0.20 : 0.10;

  if (modal.kind === 'room') {
    return <RoomModal member={member} onClose={onClose} onConfirm={onConfirm} />;
  }
  if (modal.kind === 'fnb') {
    return <FnBQuickModal member={member} onClose={onClose} onConfirm={onConfirm} />;
  }
  if (modal.kind === 'room-charge') {
    return <RoomChargeModal member={member} onClose={onClose} onConfirm={onConfirm} />;
  }
  if (modal.kind === 'wellness-charge') {
    return <WellnessChargeModal member={member} onClose={onClose} onConfirm={onConfirm} />;
  }
  if (modal.kind === 'hall') {
    return <HallModal member={member} onClose={onClose} onConfirm={onConfirm} />;
  }
  if (modal.kind === 'spa') {
    return <SpaModal member={member} onClose={onClose} onConfirm={onConfirm} />;
  }
  // Simple confirm for pool/gym/cake/upgrade
  const label = {
    pool: ['Log pool entry', 'Logs a pool visit for ' + member.name + '.'],
    gym: ['Log gym entry', 'Logs a gym visit for ' + member.name + '.'],
    'birthday-cake': ['Redeem birthday cake', 'Marks the complimentary birthday cake as redeemed. Notify kitchen separately.'],
    'anniversary-cake': ['Redeem anniversary cake', 'Marks the anniversary cake as redeemed.'],
    upgrade: ['Apply suite upgrade', 'Marks the complimentary suite upgrade as used (subject to availability).'],
  }[modal.kind];
  return (
    <Modal open title={label[0]} onClose={onClose} footer={
      <>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="gold" onClick={() => onConfirm(modal.kind, {}, label[0] + ' · ' + member.name)}>✓ Confirm</Button>
      </>
    }>
      <div className="pt-mute">{label[1]}</div>
      <div className="pt-faint" style={{ fontSize: 12, marginTop: 10 }}>Action logged to audit log automatically.</div>
    </Modal>
  );
}

function RoomModal({ member, onClose, onConfirm }) {
  const [room, setRoom] = React.useState('');
  const [note, setNote] = React.useState('');
  const compLeft = member.benefits.compNightsTotal - member.benefits.compNightsUsed;
  return (
    <Modal open title={`Redeem complimentary room · ${member.name}`} onClose={onClose} footer={
      <>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="gold" disabled={!room}
          onClick={() => onConfirm('room', { room, note }, 'Comp room redeemed · 1 night')}>
          ✓ Confirm 1 night
        </Button>
      </>
    }>
      <Card flat style={{ background: T.goldSoft, borderColor: T.goldBorder, marginBottom: 14 }}>
        <div className="pt-row" style={{ justifyContent: 'space-between' }}>
          <div className="pt-col">
            <div className="pt-eyebrow">Balance</div>
            <div className="pt-display" style={{ fontSize: 28, color: T.gold }}>{compLeft} <span className="pt-faint" style={{ fontSize: 14 }}>/ {member.benefits.compNightsTotal}</span></div>
            <div className="pt-faint" style={{ fontSize: 12 }}>complimentary nights remaining</div>
          </div>
          <div className="pt-col" style={{ alignItems: 'flex-end' }}>
            <div className="pt-faint" style={{ fontSize: 12 }}>after redemption</div>
            <div className="pt-display" style={{ fontSize: 22, color: T.text }}>{compLeft - 1}</div>
          </div>
        </div>
      </Card>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        <Input label="Room number" value={room} onChange={setRoom} placeholder="e.g. 214" />
        <Input label="Note (optional)" value={note} onChange={setNote} placeholder="e.g. anniversary stay" />
      </div>
      <div className="pt-faint" style={{ fontSize: 11, marginTop: 12 }}>↳ writes to PMS room assignment · logged against {member.id}</div>
    </Modal>
  );
}

function FnBQuickModal({ member, onClose, onConfirm }) {
  const [amount, setAmount] = React.useState('');
  const [billNo, setBillNo] = React.useState('');
  const [outlet, setOutlet] = React.useState('The Verandah · ' + member.property);
  const [overridePct, setOverridePct] = React.useState('');
  const canOverride = Store.can('override-discount');
  const isWellness = member.type === 'wellness';
  const tierPct = isWellness ? 0.20 : 0.10;
  const effectivePct = canOverride && Number(overridePct) > 0 ? Number(overridePct) / 100 : tierPct;
  const amt = Number(amount) || 0;
  const discount = Math.round(amt * effectivePct);
  const finalAmt = amt - discount;
  return (
    <Modal open title="Apply F&B discount" onClose={onClose} width={520} footer={
      <>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="gold" disabled={!amt || !billNo}
          onClick={() => onConfirm('fnb', { amount: amt, billNo, note: outlet, pctOverride: canOverride && Number(overridePct) > 0 ? Number(overridePct) : null }, `F&B ${(effectivePct * 100).toFixed(0)}% applied · ${fmtNPR(discount)} saved`)}>
          ✓ Apply {(effectivePct * 100).toFixed(0)}% & save
        </Button>
      </>
    }>
      <div className="pt-row" style={{ gap: 10, marginBottom: 14, alignItems: 'center' }}>
        <Avatar name={member.name} type={member.type} photo={member.photo} size={40} />
        <div className="pt-col" style={{ flex: 1, lineHeight: 1.2 }}>
          <div style={{ fontWeight: 600 }}>{member.name}</div>
          <div className="pt-faint pt-num" style={{ fontSize: 12 }}>{member.id} · {member.phone}</div>
        </div>
        <TierBadge type={member.type} />
        <Badge variant="gold">{(effectivePct * 100).toFixed(0)}%</Badge>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 12 }}>
        <Input label="Aegis bill no." value={billNo} onChange={setBillNo} placeholder="e.g. B-44219" />
        <Input label="Bill amount · NPR" value={amount} onChange={setAmount} placeholder="3,420" />
      </div>
      <Input label="Outlet / table" value={outlet} onChange={setOutlet} />
      {canOverride && (
        <div style={{ marginTop: 12, padding: 10, background: 'rgba(200,162,90,0.06)', border: '1px solid ' + T.goldBorder, borderRadius: 8 }}>
          <div className="pt-row" style={{ gap: 10, alignItems: 'center' }}>
            <div className="pt-col" style={{ flex: 1, lineHeight: 1.3 }}>
              <div style={{ fontWeight: 600, fontSize: 12, color: T.gold }}>Discount override · Super Admin</div>
              <div className="pt-faint" style={{ fontSize: 11 }}>Standard cap for this tier is {(tierPct * 100).toFixed(0)}%. Leave blank to use it.</div>
            </div>
            <div style={{ width: 100 }}>
              <input className="pt-input" value={overridePct} onChange={e => setOverridePct(e.target.value)} placeholder={(tierPct * 100).toFixed(0)} />
            </div>
            <span className="pt-faint" style={{ fontSize: 12 }}>%</span>
          </div>
        </div>
      )}
      {amt > 0 && (
        <Card flat style={{ marginTop: 14, background: T.goldSoft, borderColor: T.goldBorder }}>
          <div className="pt-row" style={{ justifyContent: 'space-between' }}>
            <span className="pt-mute">Bill</span>
            <span className="pt-num">{fmtNPR(amt)}</span>
          </div>
          <div className="pt-row" style={{ justifyContent: 'space-between' }}>
            <span className="pt-mute">Discount · {(effectivePct * 100).toFixed(0)}%</span>
            <span className="pt-num pt-gold">− {fmtNPR(discount)}</span>
          </div>
          <hr className="pt-divider" style={{ margin: '8px 0' }} />
          <div className="pt-row" style={{ justifyContent: 'space-between' }}>
            <span style={{ fontWeight: 600 }}>Final payable</span>
            <span className="pt-display pt-num" style={{ fontSize: 24 }}>{fmtNPR(finalAmt)}</span>
          </div>
        </Card>
      )}
      <div className="pt-faint" style={{ fontSize: 11, marginTop: 12 }}>↳ amount & Aegis bill # are stored so accounts can reconcile.</div>
    </Modal>
  );
}

function RoomChargeModal({ member, onClose, onConfirm }) {
  const [nights, setNights] = React.useState('1');
  const [rate, setRate] = React.useState('6800');
  const [room, setRoom] = React.useState('');
  const total = (Number(nights) || 0) * (Number(rate) || 0);
  const pct = member.type === 'wellness' ? 0 : 0.10;
  const discount = Math.round(total * pct);
  return (
    <Modal open title="Paid room stay" onClose={onClose} footer={
      <>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="gold" disabled={!total} onClick={() => onConfirm('room-charge', { nights: Number(nights), rate: Number(rate), room }, 'Room charge recorded')}>✓ Save</Button>
      </>
    }>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 }}>
        <Input label="Nights" value={nights} onChange={setNights} placeholder="1" />
        <Input label="Rate · NPR/night" value={rate} onChange={setRate} placeholder="6,800" />
        <Input label="Room no." value={room} onChange={setRoom} placeholder="optional" />
      </div>
      {total > 0 && (
        <Card flat style={{ marginTop: 14 }}>
          <div className="pt-row" style={{ justifyContent: 'space-between' }}>
            <span className="pt-mute">Subtotal</span>
            <span className="pt-num">{fmtNPR(total)}</span>
          </div>
          {pct > 0 && (
            <div className="pt-row" style={{ justifyContent: 'space-between' }}>
              <span className="pt-mute">Silver 10% discount</span>
              <span className="pt-num pt-gold">− {fmtNPR(discount)}</span>
            </div>
          )}
          {member.type === 'wellness' && <div className="pt-faint" style={{ fontSize: 11, marginTop: 4 }}>↳ Wellness members get comp nights instead of room discount.</div>}
          <hr className="pt-divider" style={{ margin: '8px 0' }} />
          <div className="pt-row" style={{ justifyContent: 'space-between' }}>
            <span style={{ fontWeight: 600 }}>Final</span>
            <span className="pt-display pt-num" style={{ fontSize: 22 }}>{fmtNPR(total - discount)}</span>
          </div>
        </Card>
      )}
    </Modal>
  );
}

function WellnessChargeModal({ member, onClose, onConfirm }) {
  const [amount, setAmount] = React.useState('');
  const [service, setService] = React.useState('Spa · 60-min massage');
  const amt = Number(amount) || 0;
  const discount = Math.round(amt * 0.10);
  return (
    <Modal open title="Spa / wellness charge" onClose={onClose} footer={
      <>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="gold" disabled={!amt} onClick={() => onConfirm('wellness-charge', { amount: amt, note: service }, 'Wellness charge recorded')}>✓ Save</Button>
      </>
    }>
      <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 12 }}>
        <Input label="Service" value={service} onChange={setService} />
        <Input label="Amount · NPR" value={amount} onChange={setAmount} placeholder="3,500" />
      </div>
      {amt > 0 && (
        <Card flat style={{ marginTop: 14 }}>
          <div className="pt-row" style={{ justifyContent: 'space-between' }}>
            <span className="pt-mute">Service</span>
            <span className="pt-num">{fmtNPR(amt)}</span>
          </div>
          <div className="pt-row" style={{ justifyContent: 'space-between' }}>
            <span className="pt-mute">Member 10% discount</span>
            <span className="pt-num pt-gold">− {fmtNPR(discount)}</span>
          </div>
          <hr className="pt-divider" style={{ margin: '8px 0' }} />
          <div className="pt-row" style={{ justifyContent: 'space-between' }}>
            <span style={{ fontWeight: 600 }}>Final</span>
            <span className="pt-display pt-num" style={{ fontSize: 22 }}>{fmtNPR(amt - discount)}</span>
          </div>
        </Card>
      )}
    </Modal>
  );
}

function HallModal({ member, onClose, onConfirm }) {
  const [eventName, setEventName] = React.useState('');
  const left = member.benefits.hallUsesTotal - member.benefits.hallUsesUsed;
  const hours = member.benefits.hallHoursPerUse;
  return (
    <Modal open title={`Hall use · ${member.name}`} onClose={onClose} footer={
      <>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="gold" disabled={left <= 0} onClick={() => onConfirm('hall', { event: eventName || 'Hall booking' }, `Hall booked · ${hours} hrs`)}>✓ Confirm booking</Button>
      </>
    }>
      <Card flat style={{ background: T.goldSoft, borderColor: T.goldBorder, marginBottom: 14 }}>
        <div className="pt-row" style={{ justifyContent: 'space-between' }}>
          <div className="pt-col">
            <div className="pt-eyebrow">Hall passes</div>
            <div className="pt-display" style={{ fontSize: 28, color: T.gold }}>{left} <span className="pt-faint" style={{ fontSize: 14 }}>/ {member.benefits.hallUsesTotal}</span></div>
            <div className="pt-faint" style={{ fontSize: 12 }}>remaining · each booking · {hours} hrs</div>
          </div>
          <div className="pt-col" style={{ alignItems: 'flex-end' }}>
            <div className="pt-faint" style={{ fontSize: 12 }}>after this booking</div>
            <div className="pt-display" style={{ fontSize: 22 }}>{Math.max(0, left - 1)}</div>
          </div>
        </div>
      </Card>
      <Input label="Event / purpose" value={eventName} onChange={setEventName} placeholder="e.g. family birthday · corporate meet" />
      <div className="pt-faint" style={{ fontSize: 11, marginTop: 10 }}>
        ↳ each Wellness member is entitled to {member.benefits.hallUsesTotal} hall bookings of {hours} hrs each per cycle.
      </div>
    </Modal>
  );
}

const SPA_SERVICES = [
  '60-min full body massage',
  '90-min full body massage',
  'Aromatherapy massage',
  'Hot stone massage',
  'Deep tissue massage',
  'Swedish massage',
  'Thai massage',
  'Couples massage',
  'Foot reflexology · 45 min',
  'Head + neck + shoulder · 30 min',
  'Express facial · 30 min',
  'Signature facial · 60 min',
  'Body scrub',
  'Body wrap',
  'Manicure',
  'Pedicure',
  'Other (specify in note)',
];

function SpaModal({ member, onClose, onConfirm }) {
  const [service, setService] = React.useState(SPA_SERVICES[0]);
  const [note, setNote] = React.useState('');
  const left = member.benefits.spaPassesTotal - member.benefits.spaPassesUsed;
  const finalService = service === 'Other (specify in note)' && note ? note : service;
  return (
    <Modal open title={`Spa pass · ${member.name}`} onClose={onClose} footer={
      <>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="gold" disabled={left <= 0} onClick={() => onConfirm('spa', { service: finalService, note: finalService }, 'Spa pass redeemed · ' + finalService)}>✓ Redeem pass</Button>
      </>
    }>
      <Card flat style={{ background: T.goldSoft, borderColor: T.goldBorder, marginBottom: 14 }}>
        <div className="pt-row" style={{ justifyContent: 'space-between' }}>
          <div className="pt-col">
            <div className="pt-eyebrow">Spa passes</div>
            <div className="pt-display" style={{ fontSize: 28, color: T.gold }}>{left} <span className="pt-faint" style={{ fontSize: 14 }}>/ {member.benefits.spaPassesTotal}</span></div>
            <div className="pt-faint" style={{ fontSize: 12 }}>complimentary passes remaining</div>
          </div>
          <div className="pt-col" style={{ alignItems: 'flex-end' }}>
            <div className="pt-faint" style={{ fontSize: 12 }}>after redemption</div>
            <div className="pt-display" style={{ fontSize: 22 }}>{Math.max(0, left - 1)}</div>
          </div>
        </div>
      </Card>
      <Select label="Spa service" value={service} onChange={setService} options={SPA_SERVICES} />
      {service === 'Other (specify in note)' && (
        <div style={{ marginTop: 10 }}>
          <Input label="Service description" value={note} onChange={setNote} placeholder="e.g. 90-min couple massage with hot stones" />
        </div>
      )}
    </Modal>
  );
}

// ═══════════════════════════════════════════════════════════════
// ENROLL
// ═══════════════════════════════════════════════════════════════

function EnrollScreen() {
  const session = useStore(s => s.session);
  const toast = useToast();
  const [form, setForm] = React.useState({
    type: 'wellness',
    memberId: '',                 // Manual — equal to physical card # (printed on plastic)
    name: '', phone: '', email: '', address: '', gender: '',
    dob: '', anniversary: '', nationality: 'Nepali', citizenship: '',
    property: session.property,
    photo: null,                  // data URL after compression
    paymentProof: null,           // data URL of payment proof image
    startDate: new Date().toISOString().slice(0, 10),
    expiryDate: '',
    salesRep: session.staff,
    paymentMethod: 'cash',
    paymentAmount: 50000,
    paymentStatus: 'paid',
  });
  const [errors, setErrors] = React.useState({});
  const set = (k, v) => setForm(f => Object.assign({}, f, { [k]: v }));

  React.useEffect(() => {
    // auto-set expiry to +1yr from start
    if (form.startDate) {
      const d = new Date(form.startDate);
      d.setFullYear(d.getFullYear() + 1);
      set('expiryDate', d.toISOString().slice(0, 10));
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [form.startDate]);
  React.useEffect(() => {
    set('paymentAmount', form.type === 'wellness' ? 50000 : form.type === 'gold' ? 25000 : 0);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [form.type]);

  function submit() {
    const e = {};
    if (!form.name.trim()) e.name = 'required';
    if (!form.phone.trim()) e.phone = 'required';
    if (!form.property) e.property = 'pick a property';
    const memberId = String(form.memberId || '').trim().toUpperCase();
    if (!memberId) e.memberId = 'required — same as physical card #';
    else if (!/^[A-Z0-9_-]{3,32}$/.test(memberId)) e.memberId = '3-32 chars, letters/digits/-/_ only';
    const all = Store.getState().members; // include deleted to catch reused IDs
    // dup phone check (active only)
    const active = all.filter(m => !m.deleted);
    const existing = active.find(m => m.phone && form.phone && m.phone.replace(/\D/g, '') === form.phone.replace(/\D/g, ''));
    if (existing) e.phone = 'phone already used · ' + existing.id;
    // dup member ID / card # check (across active AND deleted to avoid id collisions)
    if (memberId) {
      const dup = all.find(m => m.id === memberId || m.cardNumber === memberId);
      if (dup) e.memberId = `already in use · ${dup.name} (${dup.id})${dup.deleted ? ' [deleted]' : ''}`;
    }
    if (Object.keys(e).length) { setErrors(e); toast.show('Fix the highlighted fields', { bad: true }); return; }
    // Submit — id and cardNumber are the same value.
    const payload = Object.assign({}, form, { id: memberId, cardNumber: memberId });
    const res = Store.addMember(payload);
    if (!res.ok) { setErrors({ memberId: res.error }); toast.show(res.error, { bad: true }); return; }
    toast.show(`Member created · ${memberId}`);
    setTimeout(() => navigate('members/' + memberId), 250);
  }

  // Compress an uploaded image to ~80% quality, max 1024px long side.
  async function handlePhotoUpload(file, field) {
    if (!file) return;
    if (!/^image\/(jpeg|jpg|png|webp)$/i.test(file.type)) {
      toast.show('Please upload a JPG, PNG or WebP image', { bad: true });
      return;
    }
    const dataUrl = await new Promise((resolve, reject) => {
      const r = new FileReader();
      r.onload = () => resolve(r.result);
      r.onerror = reject;
      r.readAsDataURL(file);
    });
    const img = new Image();
    await new Promise((resolve, reject) => { img.onload = resolve; img.onerror = reject; img.src = dataUrl; });
    const MAX = 1024;
    const ratio = Math.min(1, MAX / Math.max(img.width, img.height));
    const w = Math.round(img.width * ratio), h = Math.round(img.height * ratio);
    const canvas = document.createElement('canvas');
    canvas.width = w; canvas.height = h;
    canvas.getContext('2d').drawImage(img, 0, 0, w, h);
    const compressed = canvas.toDataURL('image/jpeg', 0.8);
    set(field, compressed);
    toast.show('Image attached · compressed to 80%');
  }

  return (
    <div>
      <PageHeader title="Enroll a new member" eyebrow="Sign-up" sub="Creates profile → activates benefits → adds to Member list">
        <Button variant="ghost" onClick={() => navigate('members')}>Cancel</Button>
        <Button variant="gold" onClick={submit}>✓ Create member</Button>
      </PageHeader>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 16 }}>
        <div className="pt-col" style={{ gap: 14 }}>
          <Card flat>
            <h3 className="pt-h3" style={{ marginBottom: 14 }}>1 · Choose tier</h3>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12 }}>
              <TierPick selected={form.type === 'silver'} onClick={() => set('type', 'silver')}
                tier="silver" price="Free" perks={['10% off rooms', '10% off F&B', '10% off wellness', 'Both properties']} />
              <TierPick selected={form.type === 'gold'} onClick={() => set('type', 'gold')}
                tier="gold" price="NPR 25,000 / yr" perks={['15% off F&B', '15% off wellness', '2 complimentary nights', 'Birthday cake', '2 complimentary spa passes', 'Pool + gym year-round']} />
              <TierPick selected={form.type === 'wellness'} onClick={() => set('type', 'wellness')}
                tier="wellness" price="NPR 50,000 / yr" perks={['10 complimentary nights', '20% off F&B', 'Birthday + anniversary cake', 'Suite upgrade · subject to availability', '3 hall bookings · 4 hrs each', '5 complimentary spa passes', 'Pool + gym year-round', 'Priority status']} />
            </div>
          </Card>

          <Card flat>
            <h3 className="pt-h3" style={{ marginBottom: 14 }}>2 · Personal</h3>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
              <Input label="Full name *" value={form.name} onChange={v => set('name', v)} error={errors.name} />
              <Input label="Phone *" value={form.phone} onChange={v => set('phone', v)} error={errors.phone} placeholder="+977 98-…" hint="must be unique" />
              <Input label="Email" value={form.email} onChange={v => set('email', v)} />
              <Input label="Address" value={form.address} onChange={v => set('address', v)} />
              <Select label="Gender" value={form.gender} onChange={v => set('gender', v)} options={[{ value: '', label: '—' }, { value: 'F', label: 'Female' }, { value: 'M', label: 'Male' }, { value: 'O', label: 'Other' }]} />
              <Input label="Date of birth" type="date" value={form.dob} onChange={v => set('dob', v)} hint="enables birthday cake reminder" />
              <Input label="Anniversary (MM-DD)" value={form.anniversary} onChange={v => set('anniversary', v)} placeholder="07-14" />
              <Input label="Nationality" value={form.nationality} onChange={v => set('nationality', v)} />
              <Input label="Citizenship · PAN · VAT" value={form.citizenship} onChange={v => set('citizenship', v)} />
            </div>
          </Card>

          <Card flat>
            <h3 className="pt-h3" style={{ marginBottom: 14 }}>3 · Membership</h3>
            <div style={{
              background: 'rgba(200,162,90,0.06)', border: '1px solid ' + T.goldBorder,
              borderRadius: 10, padding: 14, marginBottom: 14,
            }}>
              <Input
                label="Member ID / Physical card # *"
                value={form.memberId}
                onChange={v => set('memberId', String(v || '').toUpperCase())}
                error={errors.memberId}
                placeholder="e.g. NW-001234 or SIL-000507"
                hint="Type the number printed on the plastic card. This becomes the permanent member ID."
              />
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 12 }}>
              <Select label="Home property *" value={form.property} onChange={v => set('property', v)} options={['Biratchowk', 'Damak']} error={errors.property} />
              <Input label="Sales rep" value={form.salesRep} onChange={v => set('salesRep', v)} />
              <Input label="Start date" type="date" value={form.startDate} onChange={v => set('startDate', v)} />
              <Input label="Expiry" type="date" value={form.expiryDate} onChange={v => set('expiryDate', v)} hint="auto · 1 yr from start" />
            </div>
          </Card>

          {(form.type === 'wellness' || form.type === 'gold') && (
            <Card flat>
              <h3 className="pt-h3" style={{ marginBottom: 14 }}>4 · Payment</h3>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12, marginBottom: 14 }}>
                <Select label="Method" value={form.paymentMethod} onChange={v => set('paymentMethod', v)} options={['cash', 'card', 'eSewa', 'Khalti', 'bank transfer']} />
                <Input label="Amount · NPR" value={form.paymentAmount} onChange={v => set('paymentAmount', v)} />
                <Select label="Status" value={form.paymentStatus} onChange={v => set('paymentStatus', v)} options={['paid', 'pending', 'failed']} />
              </div>
              <PhotoField
                label="Payment proof (receipt / screenshot)"
                value={form.paymentProof}
                onPick={file => handlePhotoUpload(file, 'paymentProof')}
                onClear={() => set('paymentProof', null)}
              />
            </Card>
          )}

          <Card flat>
            <h3 className="pt-h3" style={{ marginBottom: 14 }}>5 · Member photo</h3>
            <PhotoField
              label="Profile photo (auto-compressed to 80%)"
              value={form.photo}
              onPick={file => handlePhotoUpload(file, 'photo')}
              onClear={() => set('photo', null)}
            />
          </Card>
        </div>

        <div className="pt-col" style={{ gap: 12, position: 'sticky', top: 24, alignSelf: 'flex-start' }}>
          <Card>
            <h3 className="pt-h3" style={{ marginBottom: 10 }}>Card preview</h3>
            <CardPreview form={form} />
          </Card>
          <Card flat>
            <div className="pt-eyebrow" style={{ marginBottom: 10 }}>On save</div>
            <div className="pt-col pt-mute" style={{ gap: 6, fontSize: 12 }}>
              <div>✓ member ID = card # you typed</div>
              <div>✓ generate QR code</div>
              <div>✓ activate benefits</div>
              <div>✓ add to Member list</div>
              <div>✓ log to audit</div>
              <div className="pt-faint">○ WhatsApp welcome (config)</div>
            </div>
          </Card>
        </div>
      </div>
    </div>
  );
}

function TierPick({ tier, price, perks, selected, onClick }) {
  const gold = tier === 'wellness' || tier === 'gold';
  return (
    <div onClick={onClick} style={{
      padding: 16, borderRadius: 12,
      border: '1.5px solid ' + (selected ? (gold ? T.gold : T.text) : T.borderSoft),
      background: selected ? (gold ? T.goldSoft : 'rgba(255,255,255,0.04)') : 'rgba(255,255,255,0.015)',
      cursor: 'pointer',
      transition: 'border-color .15s, background .15s',
    }}>
      <div className="pt-row" style={{ justifyContent: 'space-between', marginBottom: 8 }}>
        <TierBadge type={tier} />
        <div className="pt-display" style={{ fontSize: gold ? 18 : 20, color: gold ? T.gold : T.text }}>{price}</div>
      </div>
      <ul style={{ paddingLeft: 18, margin: '4px 0 0', fontSize: 13, color: T.textMute, lineHeight: 1.7 }}>
        {perks.map(p => <li key={p}>{p}</li>)}
      </ul>
      {selected && <div style={{ marginTop: 10, fontSize: 12, color: gold ? T.gold : T.text, fontWeight: 600 }}>✓ Selected</div>}
    </div>
  );
}

function CardPreview({ form, member }) {
  // Accept either a draft form or a saved member.
  const src = member || form || {};
  const t = src.type;
  const gold = (t === 'wellness' || t === 'gold');
  const tierName = t === 'wellness' ? 'WELLNESS' : t === 'gold' ? 'GOLD' : 'SILVER';
  const prefix = t === 'wellness' ? 'WEL' : t === 'gold' ? 'GLD' : 'SIL';
  const cardNo = src.cardNumber || src.id || (prefix + '-XXXXXX');
  const memberId = src.id || (prefix + '-XXXXXX');
  return (
    <div style={{
      background: gold
        ? 'linear-gradient(135deg, #1a1207 0%, #2c1f0c 60%, #1a1207 100%)'
        : 'linear-gradient(135deg, #0d1830 0%, #1a2a4a 60%, #0d1830 100%)',
      border: '1px solid ' + (gold ? T.goldBorder : T.borderSoft),
      borderRadius: 12,
      padding: 16,
      aspectRatio: '1.586',
      position: 'relative',
      overflow: 'hidden',
    }}>
      <div className="pt-row" style={{ justifyContent: 'space-between', alignItems: 'flex-start' }}>
        <div className="pt-row" style={{ gap: 8 }}>
          <Mark size={22} />
          <div className="pt-display" style={{ fontSize: 17, fontWeight: 600, color: gold ? T.goldBright : T.text }}>Nepalirika</div>
        </div>
        <div style={{ fontSize: 9, letterSpacing: '0.18em', fontWeight: 700, color: gold ? T.goldBright : T.silver }}>
          {tierName}
        </div>
      </div>
      <div className="pt-row" style={{ marginTop: 14, justifyContent: 'space-between', alignItems: 'flex-end', gap: 10 }}>
        <div className="pt-row" style={{ minWidth: 0, gap: 10, alignItems: 'flex-end' }}>
          {src.photo && (
            <img src={src.photo} alt="" style={{
              width: 42, height: 42, borderRadius: 6, objectFit: 'cover',
              border: '1px solid ' + (gold ? T.goldBorder : T.borderSoft),
            }} />
          )}
          <div className="pt-col" style={{ minWidth: 0 }}>
            <div className="pt-faint" style={{ fontSize: 8, letterSpacing: '0.15em', textTransform: 'uppercase' }}>Member</div>
            <div style={{ fontWeight: 600, fontSize: 14, marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: 160 }}>
              {src.name || 'New Member'}
            </div>
            <div className="pt-num" style={{ fontSize: 11, color: gold ? T.gold : T.silver, letterSpacing: '0.08em', marginTop: 3 }}>
              ID · {memberId}
            </div>
            <div className="pt-num" style={{ fontSize: 10, color: gold ? T.goldBright : T.text, letterSpacing: '0.06em', marginTop: 2, opacity: 0.85 }}>
              Card · {cardNo}
            </div>
          </div>
        </div>
        <div style={{ background: '#fff', padding: 4, borderRadius: 4 }}>
          <QRMark size={50} seed={5} />
        </div>
      </div>
    </div>
  );
}

// Reusable image-upload field with thumbnail preview.
function PhotoField({ label, value, onPick, onClear }) {
  const ref = React.useRef(null);
  return (
    <div className="pt-col" style={{ gap: 8 }}>
      <div className="pt-row" style={{ alignItems: 'flex-start', gap: 12 }}>
        {value ? (
          <img src={value} alt="" style={{
            width: 84, height: 84, objectFit: 'cover', borderRadius: 8,
            border: '1px solid ' + T.borderSoft,
          }} />
        ) : (
          <div style={{
            width: 84, height: 84, borderRadius: 8, border: '1px dashed ' + T.borderSoft,
            display: 'flex', alignItems: 'center', justifyContent: 'center', color: T.textFaint, fontSize: 12,
          }}>No image</div>
        )}
        <div className="pt-col" style={{ flex: 1, gap: 4 }}>
          <div className="pt-faint" style={{ fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.06em' }}>{label}</div>
          <div className="pt-mute" style={{ fontSize: 12 }}>JPG · PNG · WebP · auto-resized to 1024px, JPEG q=80</div>
          <div className="pt-row" style={{ gap: 6, marginTop: 6 }}>
            <Button size="sm" onClick={() => ref.current && ref.current.click()}>
              {value ? 'Replace' : 'Upload'}
            </Button>
            {value && onClear && (
              <Button size="sm" variant="ghost" onClick={onClear}>Remove</Button>
            )}
          </div>
          <input
            ref={ref}
            type="file"
            accept="image/jpeg,image/png,image/webp"
            style={{ display: 'none' }}
            onChange={e => { const f = e.target.files && e.target.files[0]; e.target.value = ''; if (f) onPick(f); }}
          />
        </div>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════
// REDEEM (entry point — pick a member, then pick a benefit)
// ═══════════════════════════════════════════════════════════════

function RedeemScreen() {
  const members = useStore(s => s.members.filter(m => !m.deleted));
  const [q, setQ] = React.useState('');
  const [picked, setPicked] = React.useState(null);

  // Search matches name + member ID + card # + phone + email. ID and card # are
  // typically the same value now; we still check both for legacy members.
  const matches = q.length === 0
    ? members.slice(0, 8)
    : members.filter(m => {
        const n = q.toLowerCase();
        return m.name.toLowerCase().includes(n)
          || (m.id || '').toLowerCase().includes(n)
          || (m.cardNumber || '').toLowerCase().includes(n)
          || (m.phone || '').includes(q)
          || (m.email || '').toLowerCase().includes(n);
      }).slice(0, 8);

  if (picked) {
    return <ProfileScreen memberId={picked} />;
  }

  return (
    <div>
      <PageHeader title="Redeem benefits" eyebrow="Verification" sub="Find a member · then pick the benefit to apply" />

      <Card style={{ marginBottom: 16 }}>
        <div className="pt-row" style={{ gap: 12, alignItems: 'center' }}>
          <div style={{ flex: 1, position: 'relative' }}>
            <input
              className="pt-input"
              placeholder="Search name · card no · phone · email"
              value={q}
              onChange={e => setQ(e.target.value)}
              autoFocus
              style={{ paddingLeft: 40, fontSize: 16, padding: '14px 12px 14px 40px' }}
            />
            <span style={{ position: 'absolute', left: 14, top: '50%', transform: 'translateY(-50%)', color: T.textFaint, fontSize: 18 }}>⌕</span>
          </div>
        </div>
      </Card>

      <Card flat style={{ padding: 0 }}>
        <div style={{ padding: '8px 16px', fontSize: 11, letterSpacing: '0.08em', textTransform: 'uppercase', color: T.textMute, borderBottom: '1px solid ' + T.borderSoft }}>
          {q ? `${matches.length} matches` : 'Recent · suggestions'}
        </div>
        <div>
          {matches.map(m => (
            <div key={m.id} onClick={() => setPicked(m.id)} style={{
              display: 'flex', gap: 14, alignItems: 'center', padding: '14px 16px',
              borderBottom: '1px solid ' + T.borderSoft,
              cursor: 'pointer',
              transition: 'background .12s',
            }}
              onMouseEnter={e => e.currentTarget.style.background = 'rgba(200,162,90,0.05)'}
              onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
            >
              <Avatar name={m.name} type={m.type} photo={m.photo} size={42} />
              <div className="pt-col" style={{ flex: 1, lineHeight: 1.3, minWidth: 0 }}>
                <div className="pt-row" style={{ gap: 10, flexWrap: 'wrap' }}>
                  <span style={{ fontWeight: 600, fontSize: 15 }}>{m.name}</span>
                  <TierBadge type={m.type} />
                  <StatusBadge status={m.status} />
                </div>
                <div className="pt-faint pt-num" style={{ fontSize: 12 }}>
                  <b style={{ color: m.type === 'wellness' ? T.gold : T.silver }}>{m.cardNumber || m.id}</b>
                  · {m.phone || '—'} · {m.property}
                </div>
              </div>
              {m.type === 'wellness' && (
                <div className="pt-col" style={{ alignItems: 'flex-end', lineHeight: 1.2 }}>
                  <div className="pt-display" style={{ fontSize: 18, color: T.gold }}>{m.benefits.compNightsTotal - m.benefits.compNightsUsed}</div>
                  <div className="pt-faint" style={{ fontSize: 10 }}>nights left</div>
                </div>
              )}
              <span className="pt-mute" style={{ fontSize: 20 }}>→</span>
            </div>
          ))}
          {matches.length === 0 && (
            <div style={{ padding: 40, textAlign: 'center', color: T.textFaint }}>
              No matches. <span className="pt-link" onClick={() => navigate('enroll')}>Enroll new member →</span>
            </div>
          )}
        </div>
      </Card>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════
// F&B DEDICATED SCREEN (Aegis paste flow)
// ═══════════════════════════════════════════════════════════════

// Outlet catalog per property — first dropdown is property, second filters by it.
const OUTLETS_BY_PROPERTY = {
  Biratchowk: ['Chiya Cafe', 'Elements Sky Bar', 'Latitude Restaurant', 'Pool Bar'],
  Damak: ['Baithak Restaurant & Bar'],
};

function FnBScreen() {
  const members = useStore(s => s.members.filter(m => !m.deleted));
  const session = useStore(s => s.session);
  const settings = useStore(s => s.settings || {});
  const outlets = useStore(s => s.outlets || []);
  const toast = useToast();
  const [q, setQ] = React.useState('');
  const [memberId, setMemberId] = React.useState(null);
  const [billNo, setBillNo] = React.useState('');
  const [amount, setAmount] = React.useState('');
  const [discountAmt, setDiscountAmt] = React.useState('');         // manual NPR
  const [discountTouched, setDiscountTouched] = React.useState(false);
  const [property, setProperty] = React.useState(session.property || 'Biratchowk');
  // Outlets cascade: pull from settings/outlets (DB-backed) first; fallback to baked-in.
  const dbOutletsForProperty = outlets.filter(o => o.property === property && o.active !== false);
  const outletOptions = dbOutletsForProperty.length > 0
    ? dbOutletsForProperty.map(o => ({ id: o.id, name: o.name, taxPct: o.taxPct, kind: o.kind }))
    : (OUTLETS_BY_PROPERTY[property] || []).map(name => ({ id: name, name, taxPct: 13, kind: 'fnb' }));
  const [outletId, setOutletId] = React.useState(outletOptions[0] ? outletOptions[0].id : '');
  const [table, setTable] = React.useState('');

  // Reset outlet whenever property changes
  React.useEffect(() => {
    const opts = outlets.filter(o => o.property === property && o.active !== false);
    const dbFirst = opts[0] ? opts[0].id : (OUTLETS_BY_PROPERTY[property] || [''])[0];
    setOutletId(dbFirst || '');
  }, [property, outlets]);

  const outlet = outletOptions.find(o => o.id === outletId) || outletOptions[0] || { name: '', taxPct: 13, kind: 'fnb' };

  const member = members.find(m => m.id === memberId);
  const matches = q.length > 0 ? members.filter(m => m.name.toLowerCase().includes(q.toLowerCase()) || m.id.toLowerCase().includes(q.toLowerCase()) || (m.phone || '').includes(q) || (m.cardNumber || '').toLowerCase().includes(q.toLowerCase())).slice(0, 5) : [];
  const isWellness = member && member.type === 'wellness';
  // Suggested % from Settings; serves as a STARTING POINT for the discount amount.
  // Once the staff types a discount NPR amount, that overrides the suggestion.
  const discPct = settings.tierDiscountPct || { silver: 10, gold: 15, wellness: 20 };
  const suggestedPct = (member
    ? (member.type === 'wellness' ? (discPct.wellness ?? 20)
       : member.type === 'gold' ? (discPct.gold ?? 15)
       : (discPct.silver ?? 10))
    : 0) / 100;
  const amt = Number(amount) || 0;
  const suggestedDiscount = Math.round(amt * suggestedPct);
  // Effective discount: manual entry wins if touched; otherwise use suggestion.
  const discount = discountTouched ? (Number(discountAmt) || 0) : suggestedDiscount;
  const finalAmt = Math.max(0, amt - discount);
  // Loyalty points the member will earn from this bill (1 pt per NPR of net spend).
  const earnPerNpr = ((settings.loyalty || {}).earnPerNpr) ?? 1;
  const pointsEarn = Math.floor(finalAmt * earnPerNpr);

  // Apply suggested discount whenever amount changes (until staff overrides).
  React.useEffect(() => {
    if (!discountTouched) setDiscountAmt(String(suggestedDiscount || ''));
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [amt, suggestedPct]);

  function apply() {
    if (!member) return toast.show('Pick a member', { bad: true });
    if (!amt) return toast.show('Enter bill amount', { bad: true });
    if (!billNo) return toast.show('Paste Aegis bill #', { bad: true });
    if (discount > amt) return toast.show('Discount cannot exceed bill amount', { bad: true });
    // Post a real bill — this earns loyalty points AND shows up in Reports + Bills.
    const res = Store.postBillManual({
      memberId: member.id,
      outletId: outlet.id,
      outletName: outlet.name,
      property,
      gross: amt,
      discount,                       // manual NPR
      vatPct: outlet.taxPct || 13,
      note: `${billNo}${table ? ' · ' + table : ''}`,
      staff: session.staff,
    });
    if (res.ok) {
      toast.show(`Bill posted · ${fmtNPR(discount)} discount · +${pointsEarn} pts earned`);
      setBillNo(''); setAmount(''); setDiscountAmt(''); setDiscountTouched(false);
      setTable(''); setMemberId(null); setQ('');
    } else toast.show(res.error || 'Could not post bill', { bad: true });
  }

  return (
    <div>
      <PageHeader title="F&B · apply member discount" eyebrow="Restaurant" sub="Verify member → paste Aegis bill # + amount → apply 10% (Silver) or 20% (Wellness)" />

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 14 }}>
        {/* Step 1 */}
        <Card>
          <StepHeader n={1} title="Find member" done={!!member} />
          {!member ? (
            <>
              <Input label="Search · name · card # / member ID · phone" value={q} onChange={setQ} placeholder="type to search" />
              <div className="pt-col" style={{ marginTop: 12, gap: 6 }}>
                {matches.map(m => (
                  <div key={m.id} onClick={() => { setMemberId(m.id); setQ(''); }} style={{
                    display: 'flex', gap: 10, padding: 10, borderRadius: 8,
                    border: '1px solid ' + T.borderSoft, cursor: 'pointer',
                  }}>
                    <Avatar name={m.name} type={m.type} photo={m.photo} size={32} />
                    <div className="pt-col" style={{ lineHeight: 1.2, flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 13, fontWeight: 600 }}>{m.name}</div>
                      <div className="pt-faint pt-num" style={{ fontSize: 11 }}>
                        <b style={{ color: m.type === 'wellness' ? T.gold : T.silver }}>{m.cardNumber || m.id}</b>
                        {m.phone ? ` · ${m.phone}` : ''}
                      </div>
                    </div>
                    <TierBadge type={m.type} />
                  </div>
                ))}
                {q && matches.length === 0 && <div className="pt-faint" style={{ fontSize: 12, textAlign: 'center', padding: 12 }}>No matches.</div>}
              </div>
            </>
          ) : (
            <>
              <div className="pt-row" style={{ gap: 12 }}>
                <Avatar name={member.name} type={member.type} photo={member.photo} size={48} />
                <div className="pt-col" style={{ flex: 1, lineHeight: 1.2, minWidth: 0 }}>
                  <div style={{ fontWeight: 600, fontSize: 15 }}>{member.name}</div>
                  <div className="pt-num" style={{ fontSize: 12, color: member.type === 'wellness' ? T.gold : T.silver, fontWeight: 600 }}>
                    {member.cardNumber || member.id}
                  </div>
                  <div className="pt-row" style={{ gap: 6, marginTop: 4 }}>
                    <TierBadge type={member.type} /><StatusBadge status={member.status} />
                  </div>
                </div>
              </div>
              <hr className="pt-divider" style={{ margin: '14px 0' }} />
              <div className="pt-row" style={{ justifyContent: 'space-between', fontSize: 13 }}>
                <span className="pt-mute">F&B discount</span>
                <b className="pt-gold">{Math.round(suggestedPct * 100)}% off (suggested)</b>
              </div>
              <div className="pt-row" style={{ justifyContent: 'space-between', fontSize: 13, marginTop: 6 }}>
                <span className="pt-mute">YTD F&B saved</span>
                <span className="pt-num">{fmtNPR(member.spend.discount || 0)}</span>
              </div>
              <Button variant="ghost" size="sm" onClick={() => setMemberId(null)} style={{ marginTop: 12, width: '100%' }}>change member</Button>
            </>
          )}
        </Card>

        {/* Step 2 */}
        <Card>
          <StepHeader n={2} title="Paste bill from Aegis" done={!!billNo && !!amt} />
          <div className="pt-col" style={{ gap: 10 }}>
            <Input label="Aegis bill no." value={billNo} onChange={setBillNo} placeholder="B-44219" />
            <Input label="Bill amount · NPR (gross)" value={amount} onChange={setAmount} placeholder="3,420" />
            <div>
              <Input
                label={`Discount amount · NPR${member ? ` (suggested ${Math.round(suggestedPct * 100)}% = ${fmtNPR(suggestedDiscount)})` : ''}`}
                value={discountAmt}
                onChange={(v) => { setDiscountAmt(v); setDiscountTouched(true); }}
                placeholder="0"
                hint="Type the actual NPR amount. The tier % is just a suggestion."
              />
              {discountTouched && member && (
                <div className="pt-faint" style={{ fontSize: 11, marginTop: 4 }}>
                  ↩ <span className="pt-link" onClick={() => { setDiscountAmt(String(suggestedDiscount)); setDiscountTouched(false); }}>
                    Reset to suggested {Math.round(suggestedPct * 100)}%
                  </span>
                </div>
              )}
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
              <Select label="Property" value={property} onChange={setProperty} options={['Biratchowk', 'Damak']} />
              <Select label="Outlet" value={outletId} onChange={setOutletId} options={outletOptions.map(o => ({ value: o.id, label: o.name }))} />
            </div>
            <Input label="Table / Room" value={table} onChange={setTable} placeholder="Tbl 12" />
          </div>
          <div className="pt-faint" style={{ fontSize: 11, marginTop: 10 }}>↳ Finalise bill in Aegis first, then copy the bill # & amount here.</div>
        </Card>

        {/* Step 3 */}
        <Card gold>
          <StepHeader n={3} title="Confirm & apply" gold done={!!member && !!billNo && !!amt} />
          {member && amt > 0 ? (
            <>
              <div className="pt-col" style={{ gap: 8 }}>
                <div className="pt-row" style={{ justifyContent: 'space-between' }}>
                  <span className="pt-mute">Bill amount (gross)</span>
                  <span className="pt-display pt-num" style={{ fontSize: 22 }}>{fmtNPR(amt)}</span>
                </div>
                <div className="pt-row" style={{ justifyContent: 'space-between' }}>
                  <span className="pt-mute">Discount applied</span>
                  <span className="pt-display pt-num pt-gold" style={{ fontSize: 18 }}>− {fmtNPR(discount)}</span>
                </div>
                <hr className="pt-divider" />
                <div className="pt-row" style={{ justifyContent: 'space-between', alignItems: 'baseline' }}>
                  <span style={{ fontWeight: 600 }}>Final payable</span>
                  <span className="pt-display pt-num" style={{ fontSize: 32 }}>{fmtNPR(finalAmt)}</span>
                </div>
                <div className="pt-row" style={{ justifyContent: 'space-between', fontSize: 13 }}>
                  <span className="pt-mute">Loyalty points earned</span>
                  <span className="pt-num pt-gold" style={{ fontWeight: 600 }}>+ {pointsEarn.toLocaleString()} pts</span>
                </div>
              </div>
              <Button variant="gold" size="lg" style={{ width: '100%', marginTop: 16 }} onClick={apply}>
                ✓ Post bill & apply discount
              </Button>
              <div className="pt-faint" style={{ fontSize: 11, marginTop: 8, textAlign: 'center' }}>
                Bill saved · audit logged · loyalty points added to {member.name}'s balance
              </div>
            </>
          ) : (
            <div className="pt-faint" style={{ padding: 20, textAlign: 'center', fontSize: 13 }}>
              Complete steps 1 & 2 to see the discount calculation.
            </div>
          )}
        </Card>
      </div>
    </div>
  );
}

function StepHeader({ n, title, done, gold }) {
  return (
    <div className="pt-row" style={{ gap: 10, marginBottom: 14, alignItems: 'center' }}>
      <div style={{
        width: 28, height: 28, borderRadius: '50%',
        background: done ? (gold ? T.gold : T.text) : T.surface3,
        color: done ? T.bg : T.textMute,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontWeight: 700, fontSize: 13,
      }}>{done ? '✓' : n}</div>
      <h3 className="pt-h3" style={{ flex: 1 }}>{title}</h3>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════
// WELLNESS LOG  (quick pool / gym entry)
// ═══════════════════════════════════════════════════════════════

function WellnessScreen() {
  const members = useStore(s => s.members);
  const toast = useToast();
  const [q, setQ] = React.useState('');
  const [mode, setMode] = React.useState('pool'); // pool | gym | hall | spa

  const matches = q.length > 0
    ? members.filter(m => {
        const n = q.toLowerCase();
        return m.name.toLowerCase().includes(n)
          || (m.id || '').toLowerCase().includes(n)
          || (m.cardNumber || '').toLowerCase().includes(n)
          || (m.phone || '').includes(q);
      }).slice(0, 6)
    : [];

  function logEntry(m) {
    const res = Store.redeem(m.id, mode, {});
    if (res.ok) {
      const labels = { pool: '🏊 Pool', gym: '🏋 Gym', hall: '🏛 Hall', spa: '💫 Spa' };
      toast.show(`${labels[mode]} entry · ${m.name}`);
      setQ('');
    } else {
      toast.show(res.error, { bad: true });
    }
  }

  const todayCount = members.reduce((sum, m) => {
    return sum + (m.redemptions || []).filter(r => r.kind === mode && new Date(r.ts).toDateString() === new Date().toDateString()).length;
  }, 0);

  function balanceFor(m) {
    if (mode === 'pool') return { count: m.benefits.poolVisits, label: 'this year' };
    if (mode === 'gym') return { count: m.benefits.gymVisits, label: 'this year' };
    if (mode === 'hall') return { count: m.benefits.hallUsesTotal - m.benefits.hallUsesUsed, label: `of ${m.benefits.hallUsesTotal} left` };
    if (mode === 'spa') return { count: m.benefits.spaPassesTotal - m.benefits.spaPassesUsed, label: `of ${m.benefits.spaPassesTotal} left` };
    return { count: 0, label: '' };
  }
  const isLimited = mode === 'hall' || mode === 'spa';
  const restricted = mode === 'hall' || mode === 'spa'; // wellness-only

  return (
    <div>
      <PageHeader title="Facilities · log entry" eyebrow="Pool · Gym · Hall · Spa" sub="Quick log — type a name or scan card → entry recorded." />

      <Card style={{ marginBottom: 16 }}>
        <div className="pt-row" style={{ gap: 4, marginBottom: 14, padding: 4, background: 'rgba(255,255,255,0.04)', borderRadius: 10, width: 'fit-content', flexWrap: 'wrap' }}>
          {[['pool', '🏊 Pool'], ['gym', '🏋 Gym'], ['hall', '🏛 Hall · 4h'], ['spa', '💫 Spa pass']].map(([m, label]) => (
            <button key={m} className={mode === m ? 'pt-btn pt-btn-gold pt-btn-sm' : 'pt-btn pt-btn-ghost pt-btn-sm'}
              onClick={() => setMode(m)} style={{ minWidth: 100 }}>{label}</button>
          ))}
          <span className="pt-mute" style={{ marginLeft: 12, alignSelf: 'center', fontSize: 12 }}>{todayCount} entries today</span>
        </div>
        {restricted && (
          <div className="pt-faint" style={{ fontSize: 12, marginBottom: 10 }}>
            ↳ {mode === 'hall' ? 'Hall bookings (3 × 4 hrs each) are a Wellness-tier benefit.' : 'Spa passes (5 per year) are a Wellness-tier benefit.'} Silver members are not eligible.
          </div>
        )}
        <div style={{ position: 'relative' }}>
          <input className="pt-input" placeholder="Search name · card no · phone · email" value={q} onChange={e => setQ(e.target.value)} autoFocus
            style={{ paddingLeft: 40, fontSize: 16, padding: '14px 12px 14px 40px' }} />
          <span style={{ position: 'absolute', left: 14, top: '50%', transform: 'translateY(-50%)', color: T.textFaint, fontSize: 18 }}>⌕</span>
        </div>
      </Card>

      {matches.length > 0 && (
        <Card flat style={{ marginBottom: 16 }}>
          <div className="pt-col" style={{ gap: 8 }}>
            {matches.map(m => {
              const bal = balanceFor(m);
              const blocked = restricted && m.type !== 'wellness';
              const empty = isLimited && bal.count <= 0;
              return (
                <div key={m.id} style={{
                  display: 'flex', gap: 12, padding: 12, alignItems: 'center',
                  border: '1px solid ' + T.borderSoft, borderRadius: 10,
                  opacity: blocked ? 0.5 : 1,
                }}>
                  <Avatar name={m.name} type={m.type} photo={m.photo} size={40} />
                  <div className="pt-col" style={{ flex: 1, lineHeight: 1.2 }}>
                    <div style={{ fontWeight: 600 }}>{m.name}</div>
                    <div className="pt-faint pt-num" style={{ fontSize: 12 }}>{m.id} · {m.property}</div>
                  </div>
                  <TierBadge type={m.type} />
                  <div className="pt-col" style={{ alignItems: 'flex-end', lineHeight: 1.2, marginRight: 4 }}>
                    <div className="pt-display pt-num" style={{ fontSize: 16, color: empty ? T.bad : T.text }}>{bal.count}</div>
                    <div className="pt-faint" style={{ fontSize: 10 }}>{bal.label}</div>
                  </div>
                  <Button variant="gold" onClick={() => logEntry(m)} disabled={blocked || empty}>✓ Log {mode}</Button>
                </div>
              );
            })}
          </div>
        </Card>
      )}

      <Card flat>
        <h3 className="pt-h3" style={{ marginBottom: 12 }}>Today's {mode} entries</h3>
        <TodayActivity kind={mode} members={members} />
      </Card>
    </div>
  );
}

function TodayActivity({ kind, members }) {
  const today = new Date().toDateString();
  const entries = [];
  members.forEach(m => {
    (m.redemptions || []).forEach(r => {
      if (r.kind === kind && new Date(r.ts).toDateString() === today) {
        entries.push({ ...r, member: m });
      }
    });
  });
  entries.sort((a, b) => new Date(b.ts) - new Date(a.ts));

  if (entries.length === 0) {
    return <div className="pt-faint" style={{ padding: 20, textAlign: 'center' }}>No entries logged today yet.</div>;
  }
  return (
    <div className="pt-col" style={{ gap: 0 }}>
      {entries.map((e, i) => (
        <div key={e.id} style={{
          display: 'flex', gap: 12, alignItems: 'center', padding: '10px 0',
          borderBottom: i < entries.length - 1 ? '1px solid ' + T.borderSoft : 'none',
        }}>
          <span className="pt-num pt-faint" style={{ fontSize: 12, width: 56 }}>{new Date(e.ts).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })}</span>
          <Avatar name={e.member.name} type={e.member.type} photo={e.member.photo} size={30} />
          <div style={{ flex: 1, fontWeight: 500 }}>{e.member.name}</div>
          <span className="pt-faint pt-num" style={{ fontSize: 12 }}>{e.member.id}</span>
          <span className="pt-faint" style={{ fontSize: 12 }}>{e.property}</span>
        </div>
      ))}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════
// LOYALTY POINTS PANEL (member profile)
// ═══════════════════════════════════════════════════════════════
function LoyaltyPanel({ member }) {
  const [show, setShow] = React.useState(false);
  const pts = member.points || { balance: 0, lifetime: 0, history: [] };
  const valueInNpr = Math.floor((pts.balance || 0) / 100);
  return (
    <Card gold style={{ marginBottom: 14, padding: 18 }}>
      <div className="pt-row" style={{ justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 12 }}>
        <div className="pt-row" style={{ gap: 18, flexWrap: 'wrap' }}>
          <div className="pt-col" style={{ lineHeight: 1.15 }}>
            <div className="pt-eyebrow">Loyalty points</div>
            <div className="pt-display pt-num" style={{ fontSize: 30, color: T.gold }}>
              {(pts.balance || 0).toLocaleString()}
            </div>
            <div className="pt-faint" style={{ fontSize: 11 }}>
              ≈ NPR {valueInNpr.toLocaleString()} redeemable (100 pts = NPR 1)
            </div>
          </div>
          <div className="pt-col" style={{ lineHeight: 1.15 }}>
            <div className="pt-eyebrow">Lifetime earned</div>
            <div className="pt-display pt-num" style={{ fontSize: 20 }}>{(pts.lifetime || 0).toLocaleString()}</div>
            <div className="pt-faint" style={{ fontSize: 11 }}>NPR 1 spent = 1 pt earned</div>
          </div>
          <div className="pt-col" style={{ lineHeight: 1.15 }}>
            <div className="pt-eyebrow">Tier rate</div>
            <div className="pt-display" style={{ fontSize: 18 }}>{member.type === 'wellness' ? '1.0×' : '1.0×'}</div>
            <div className="pt-faint" style={{ fontSize: 11 }}>same rate both tiers</div>
          </div>
        </div>
        <Button size="sm" onClick={() => setShow(v => !v)}>
          {show ? 'Hide history' : `Show history (${(pts.history || []).length})`}
        </Button>
      </div>
      {show && (
        <div style={{ marginTop: 14, maxHeight: 260, overflowY: 'auto', borderTop: '1px solid ' + T.goldBorder, paddingTop: 10 }}>
          {(pts.history || []).length === 0 ? (
            <div className="pt-faint" style={{ fontSize: 12, textAlign: 'center', padding: 12 }}>No points activity yet.</div>
          ) : (
            <table className="pt-table" style={{ fontSize: 12 }}>
              <thead><tr><th>When</th><th>Type</th><th style={{ textAlign: 'right' }}>Amount</th><th>Ref</th></tr></thead>
              <tbody>
                {pts.history.map(p => (
                  <tr key={p.id}>
                    <td className="pt-num">{fmtDateTime(p.ts)}</td>
                    <td>{p.kind === 'earn' ? '＋ Earn' : '− Redeem'}</td>
                    <td className="pt-num" style={{ textAlign: 'right', color: p.amount > 0 ? T.good : '#fca5a5' }}>
                      {p.amount > 0 ? '+' : ''}{p.amount}
                    </td>
                    <td className="pt-faint">{p.ref || '—'}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
      )}
    </Card>
  );
}

// ═══════════════════════════════════════════════════════════════
// EXPIRY BANNER + RENEWAL FLOW
// ═══════════════════════════════════════════════════════════════
function daysUntil(dateStr) {
  if (!dateStr) return null;
  const d = new Date(dateStr); if (isNaN(d)) return null;
  return Math.floor((d.getTime() - Date.now()) / 86400000);
}

function ExpiryBanner({ member }) {
  const toast = useToast();
  const [renewing, setRenewing] = React.useState(false);
  const days = daysUntil(member.expiryDate);
  if (days == null) return null;

  let tone = null, label = '';
  if (days < 0) { tone = 'bad'; label = `Membership expired ${-days} day${days === -1 ? '' : 's'} ago — renew to restore benefits`; }
  else if (days <= 7) { tone = 'warn'; label = `Membership expires in ${days} day${days === 1 ? '' : 's'} — renewal due`; }
  else if (days <= 30) { tone = 'info'; label = `Membership expires in ${days} days — nudge to renew next visit`; }
  if (!tone) return null;

  const bg = tone === 'bad' ? 'rgba(239,68,68,0.10)' : tone === 'warn' ? 'rgba(234,179,8,0.10)' : 'rgba(124,158,255,0.08)';
  const fg = tone === 'bad' ? '#fca5a5' : tone === 'warn' ? '#fcd34d' : '#bfd0ff';
  const bord = tone === 'bad' ? 'rgba(239,68,68,0.3)' : tone === 'warn' ? 'rgba(234,179,8,0.3)' : 'rgba(124,158,255,0.3)';
  return (
    <div style={{
      marginBottom: 14, padding: '12px 16px', borderRadius: 10,
      background: bg, border: '1px solid ' + bord, color: fg,
      display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap',
    }}>
      <div style={{ fontSize: 13 }}>⚠ {label}</div>
      {Store.can('edit') && (
        <Button size="sm" variant="gold" onClick={() => setRenewing(true)}>Renew membership</Button>
      )}
      <RenewModal open={renewing} member={member} onClose={() => setRenewing(false)}
        onDone={() => { toast.show('Membership renewed · expires in 1 year'); setRenewing(false); }} />
    </div>
  );
}

function RenewModal({ open, member, onClose, onDone }) {
  if (!open) return null;
  const today = new Date();
  const fromExisting = member.expiryDate && daysUntil(member.expiryDate) > 0;
  const startDate = fromExisting ? member.expiryDate : today.toISOString().slice(0, 10);
  const expiryDate = (() => {
    const d = new Date(startDate); d.setFullYear(d.getFullYear() + 1);
    return d.toISOString().slice(0, 10);
  })();
  const tierPrice = member.type === 'wellness' ? 50000 : member.type === 'gold' ? 25000 : 0;
  const [amount, setAmount] = React.useState(tierPrice);
  const [method, setMethod] = React.useState('cash');
  return (
    <Modal open title={`Renew · ${member.name}`} onClose={onClose} width={460} footer={
      <>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="gold" onClick={() => {
          // Reset benefits for the new cycle, extend dates, log audit
          const freshBenefits = member.type === 'wellness'
            ? { compNightsUsed: 0, compNightsTotal: 10, poolVisits: 0, gymVisits: 0,
                birthdayCakeUsed: false, anniversaryCakeUsed: false,
                suiteUpgradeUsed: 0, suiteUpgradeTotal: 1,
                hallUsesUsed: 0, hallUsesTotal: 3, hallHoursPerUse: 4,
                spaPassesUsed: 0, spaPassesTotal: 5 }
            : { compNightsUsed: 0, compNightsTotal: 0, poolVisits: 0, gymVisits: 0,
                birthdayCakeUsed: false, anniversaryCakeUsed: false,
                suiteUpgradeUsed: 0, suiteUpgradeTotal: 0,
                hallUsesUsed: 0, hallUsesTotal: 0, hallHoursPerUse: 4,
                spaPassesUsed: 0, spaPassesTotal: 0 };
          Store.updateMember(member.id, {
            startDate, expiryDate,
            paymentAmount: Number(amount) || 0, paymentMethod: method, paymentStatus: 'paid',
            status: 'active', benefits: freshBenefits,
          });
          Store.log({ who: Store.getState().session.staff, role: Store.getState().session.role,
            property: Store.getState().session.property,
            action: `Renewed · ${member.name} · NPR ${Number(amount).toLocaleString()} · expires ${expiryDate}`,
            memberId: member.id });
          onDone();
        }}>✓ Confirm renewal</Button>
      </>
    }>
      <div className="pt-col" style={{ gap: 12 }}>
        <div className="pt-mute" style={{ fontSize: 13 }}>
          New cycle starts <b>{fmtDate(startDate)}</b> and ends <b>{fmtDate(expiryDate)}</b>.
          All benefit counters reset for the new cycle.
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          <Input label="Amount · NPR" value={amount} onChange={setAmount} />
          <Select label="Method" value={method} onChange={setMethod} options={['cash', 'card', 'eSewa', 'Khalti', 'bank transfer']} />
        </div>
        <div className="pt-faint" style={{ fontSize: 11 }}>
          Logged to audit · payment status set to paid · benefits reset for the new cycle.
        </div>
      </div>
    </Modal>
  );
}

// ═══════════════════════════════════════════════════════════════
// PRINT CARD (opens a window with a print-ready card layout)
// ═══════════════════════════════════════════════════════════════
function printMemberCard(member) {
  const gold = member.type === 'wellness';
  const tierBg = gold
    ? 'linear-gradient(135deg, #1a1207 0%, #2c1f0c 60%, #1a1207 100%)'
    : 'linear-gradient(135deg, #0d1830 0%, #1a2a4a 60%, #0d1830 100%)';
  const tierColor = gold ? '#e0bb70' : '#c0cbe0';
  const tierBorder = gold ? '#c8a25a' : '#3a4868';
  const tierName = gold ? 'WELLNESS' : 'SILVER';
  const photoTag = member.photo
    ? `<img src="${member.photo}" alt="" style="width:60px;height:60px;border-radius:8px;object-fit:cover;border:1px solid ${tierBorder}" />`
    : '';
  const html = `<!doctype html><html><head><meta charset="utf-8"><title>Card · ${member.name}</title>
<style>
  @page { size: 86mm 54mm; margin: 0; }
  body { margin: 0; padding: 0; background: #fff; font-family: 'Helvetica Neue', Arial, sans-serif; }
  .card {
    width: 86mm; height: 54mm; box-sizing: border-box; padding: 5mm; color: #fff;
    background: ${tierBg}; border: 1px solid ${tierBorder};
    display: flex; flex-direction: column; justify-content: space-between;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .head { display: flex; justify-content: space-between; align-items: center; }
  .brand { font-family: 'Cormorant Garamond', Georgia, serif; font-size: 18px; color: ${tierColor}; font-weight: 600; }
  .tier { font-size: 9px; letter-spacing: 3px; font-weight: 700; color: ${tierColor}; }
  .body { display: flex; gap: 8px; align-items: flex-end; }
  .info { flex: 1; line-height: 1.25; }
  .label { font-size: 7px; letter-spacing: 2px; color: rgba(255,255,255,0.5); text-transform: uppercase; }
  .name { font-size: 13px; font-weight: 600; margin-top: 1px; }
  .id { font-size: 10px; color: ${tierColor}; margin-top: 2px; letter-spacing: 1px; }
  .card-no { font-size: 9px; color: ${tierColor}; margin-top: 1px; opacity: 0.9; letter-spacing: 1px; }
  .footer { font-size: 6px; letter-spacing: 1.5px; color: rgba(255,255,255,0.45); text-align: center; margin-top: 2mm; text-transform: uppercase; }
  @media print { .toolbar { display: none; } }
  .toolbar { padding: 12px; background: #f3f4f6; display: flex; gap: 8px; justify-content: center; }
  .btn { padding: 8px 14px; font-size: 13px; border: 1px solid #ccc; background: #fff; border-radius: 6px; cursor: pointer; }
</style></head><body>
<div class="toolbar">
  <button class="btn" onclick="window.print()">🖨 Print this card</button>
  <button class="btn" onclick="window.close()">Close</button>
</div>
<div style="padding:20px;background:#0a1424;display:flex;justify-content:center;">
<div class="card">
  <div class="head">
    <div class="brand">Nepalirika</div>
    <div class="tier">${tierName}</div>
  </div>
  <div class="body">
    ${photoTag}
    <div class="info">
      <div class="label">Member</div>
      <div class="name">${(member.name || '').replace(/</g,'&lt;')}</div>
      <div class="id">ID · ${member.id}</div>
      <div class="card-no">CARD · ${member.cardNumber || member.id}</div>
    </div>
  </div>
  <div class="footer">Valid until ${member.expiryDate || '—'} · property ${member.property}</div>
</div>
</div>
<script>setTimeout(function(){ window.print(); }, 350);</script>
</body></html>`;
  const w = window.open('', '_blank', 'width=520,height=420');
  if (!w) { window._appNotify && window._appNotify('Pop-up blocked. Allow pop-ups for this site to print cards.', { icon: 'warn', title: 'Pop-up blocked' }); return; }
  w.document.write(html);
  w.document.close();
}

// ═══════════════════════════════════════════════════════════════
// VOID / REVERSAL ENTRIES (append-only audit pattern)
// ═══════════════════════════════════════════════════════════════
function VoidBillModal({ open, bill, onClose, onDone }) {
  const [reason, setReason] = React.useState('');
  if (!open || !bill) return null;
  return (
    <Modal open title={`Void bill · ${bill.id}`} onClose={onClose} width={500} footer={
      <>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="danger" disabled={reason.trim().length < 4}
          onClick={() => {
            // Post a reversal entry. Bills remain in DB; audit captures both.
            Store.log({
              who: Store.getState().session.staff, role: Store.getState().session.role,
              property: bill.property,
              action: `VOID bill · ${bill.id} · NPR ${(bill.total || 0).toLocaleString()} · reason: ${reason}`,
              memberId: bill.memberId,
            });
            // Restore spend + points on the member (best-effort client-side)
            const state = Store.getState();
            const m = state.members.find(x => x.id === bill.memberId);
            if (m) {
              const spend = Object.assign({ rooms: 0, fnb: 0, wellness: 0, discount: 0 }, m.spend || {});
              // refund discount and net spend
              spend.discount = Math.max(0, (spend.discount || 0) - (bill.discount || 0));
              // best-effort: reduce the fnb bucket (most common case)
              spend.fnb = Math.max(0, (spend.fnb || 0) - (bill.netAfterPoints || bill.net || 0));
              const newPoints = {
                balance: Math.max(0, (m.points && m.points.balance || 0) - (bill.points && bill.points.earned || 0) + (bill.points && bill.points.redeemed || 0)),
                lifetime: Math.max(0, (m.points && m.points.lifetime || 0) - (bill.points && bill.points.earned || 0)),
                history: [
                  { id: 'p' + Date.now(), ts: new Date().toISOString(), kind: 'redeem',
                    amount: -(bill.points && bill.points.earned || 0), billId: bill.id, ref: 'VOID · ' + reason },
                  ...((m.points && m.points.history) || []),
                ],
              };
              Store.updateMember(m.id, { spend, points: newPoints });
            }
            onDone();
            onClose();
          }}>✕ Void bill</Button>
      </>
    }>
      <div className="pt-mute" style={{ fontSize: 13, marginBottom: 12 }}>
        Voiding posts a <b>reversal entry</b> to the audit log. The original bill stays in the database.
        Member spend and points are restored to pre-bill state.
      </div>
      <Input label="Reason for void *" value={reason} onChange={setReason}
        placeholder="e.g. wrong member, double-billed, customer complaint" />
      <div className="pt-faint" style={{ fontSize: 11, marginTop: 10 }}>
        Bill · {bill.id} · NPR {(bill.total || 0).toLocaleString()} · {bill.outletName || ''}
      </div>
    </Modal>
  );
}

// ═══════════════════════════════════════════════════════════════
// CSV IMPORT (existing members)
// ═══════════════════════════════════════════════════════════════
function parseCSV(text) {
  // Simple CSV parser: handles quoted fields with commas.
  const rows = []; let cur = []; let field = ''; let inQ = false;
  for (let i = 0; i < text.length; i++) {
    const c = text[i];
    if (inQ) {
      if (c === '"' && text[i+1] === '"') { field += '"'; i++; }
      else if (c === '"') inQ = false;
      else field += c;
    } else {
      if (c === '"') inQ = true;
      else if (c === ',') { cur.push(field); field = ''; }
      else if (c === '\n' || c === '\r') {
        if (field || cur.length) { cur.push(field); rows.push(cur); cur = []; field = ''; }
        if (c === '\r' && text[i+1] === '\n') i++;
      } else field += c;
    }
  }
  if (field || cur.length) { cur.push(field); rows.push(cur); }
  return rows.filter(r => r.length && r.some(x => x.trim()));
}

function CsvImportModal({ open, onClose, onDone }) {
  const [text, setText] = React.useState('');
  const [preview, setPreview] = React.useState([]);
  const [errors, setErrors] = React.useState([]);
  const fileRef = React.useRef(null);

  function loadFile(file) {
    if (!file) return;
    const r = new FileReader();
    r.onload = () => { setText(String(r.result || '')); };
    r.readAsText(file);
  }

  React.useEffect(() => {
    if (!text) { setPreview([]); setErrors([]); return; }
    const rows = parseCSV(text);
    if (rows.length < 2) { setPreview([]); setErrors(['Need a header row + at least one data row.']); return; }
    const header = rows[0].map(h => h.trim().toLowerCase());
    const need = ['name', 'phone', 'type'];
    const missing = need.filter(n => !header.includes(n));
    if (missing.length) { setErrors([`Missing columns: ${missing.join(', ')}`]); setPreview([]); return; }
    const idx = (k) => header.indexOf(k);
    const items = rows.slice(1).map((r, i) => {
      const row = (k) => (idx(k) >= 0 ? (r[idx(k)] || '').trim() : '');
      const type = row('type').toLowerCase() === 'wellness' ? 'wellness' : 'silver';
      return {
        _line: i + 2,
        type,
        name: row('name'),
        phone: row('phone'),
        email: row('email'),
        gender: row('gender'),
        dob: row('dob'),
        anniversary: row('anniversary'),
        address: row('address'),
        property: ['Biratchowk', 'Damak'].includes(row('property')) ? row('property') : 'Biratchowk',
        cardNumber: row('card') || row('cardnumber') || row('card_number') || '',
        startDate: row('startdate') || row('start_date') || new Date().toISOString().slice(0, 10),
        paymentAmount: Number(row('payment') || row('payment_amount') || 0),
      };
    });
    setPreview(items);
    const errs = items.filter(x => !x.name || !x.phone).map(x => `Line ${x._line}: name + phone required`);
    setErrors(errs);
  }, [text]);

  function doImport() {
    let ok = 0, fail = 0;
    preview.forEach(item => {
      try { Store.addMember(item); ok++; } catch (e) { fail++; }
    });
    onDone(ok, fail);
    onClose();
  }

  if (!open) return null;
  return (
    <Modal open title="Bulk import members from CSV" onClose={onClose} width={800} footer={
      <>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="gold" disabled={preview.length === 0 || errors.length > 0} onClick={doImport}>
          Import {preview.length} member{preview.length === 1 ? '' : 's'}
        </Button>
      </>
    }>
      <div className="pt-mute" style={{ fontSize: 13, marginBottom: 12 }}>
        Upload a CSV with these columns: <code>name, phone, type, email, gender, dob, anniversary, address, property, cardNumber, startDate, paymentAmount</code>.
        Only <b>name, phone, type</b> are required. <code>type</code> must be <b>wellness</b> or <b>silver</b>.
      </div>
      <div className="pt-row" style={{ gap: 8, marginBottom: 10 }}>
        <Button size="sm" onClick={() => fileRef.current && fileRef.current.click()}>Pick CSV file</Button>
        <input ref={fileRef} type="file" accept=".csv,text/csv" style={{ display: 'none' }}
          onChange={e => { const f = e.target.files && e.target.files[0]; e.target.value = ''; if (f) loadFile(f); }} />
        <Button size="sm" variant="ghost" onClick={() => { setText(''); setPreview([]); setErrors([]); }}>Clear</Button>
      </div>
      <textarea value={text} onChange={e => setText(e.target.value)} placeholder="…or paste CSV here"
        style={{
          width: '100%', minHeight: 110, padding: 10, fontFamily: 'monospace', fontSize: 12,
          background: 'rgba(255,255,255,0.04)', color: T.text, border: '1px solid ' + T.borderSoft, borderRadius: 8,
        }} />
      {errors.length > 0 && (
        <div style={{ marginTop: 10, padding: 10, background: 'rgba(239,68,68,0.08)', border: '1px solid rgba(239,68,68,0.3)', borderRadius: 8, color: '#fca5a5', fontSize: 12 }}>
          {errors.map((e, i) => <div key={i}>⚠ {e}</div>)}
        </div>
      )}
      {preview.length > 0 && (
        <div style={{ marginTop: 12, maxHeight: 260, overflowY: 'auto' }}>
          <div className="pt-faint" style={{ fontSize: 11, marginBottom: 6 }}>{preview.length} rows ready to import</div>
          <table className="pt-table" style={{ fontSize: 12 }}>
            <thead><tr><th>#</th><th>Name</th><th>Phone</th><th>Tier</th><th>Property</th><th>Card #</th></tr></thead>
            <tbody>
              {preview.slice(0, 50).map(p => (
                <tr key={p._line}>
                  <td className="pt-faint">L{p._line}</td>
                  <td>{p.name}</td>
                  <td className="pt-num">{p.phone}</td>
                  <td><TierBadge type={p.type} /></td>
                  <td className="pt-mute">{p.property}</td>
                  <td className="pt-num">{p.cardNumber || '—'}</td>
                </tr>
              ))}
            </tbody>
          </table>
          {preview.length > 50 && <div className="pt-faint" style={{ fontSize: 11, padding: 6 }}>… and {preview.length - 50} more</div>}
        </div>
      )}
    </Modal>
  );
}

// ═══════════════════════════════════════════════════════════════
// CSV EXPORT helpers
// ═══════════════════════════════════════════════════════════════
function toCSV(rows, columns) {
  const esc = (v) => {
    if (v == null) return '';
    const s = String(v);
    return /[",\n\r]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
  };
  const header = columns.map(c => esc(c.label || c.key)).join(',');
  const body = rows.map(r => columns.map(c => esc(typeof c.get === 'function' ? c.get(r) : r[c.key])).join(',')).join('\n');
  return header + '\n' + body;
}
function downloadCSV(filename, csv) {
  const blob = new Blob(["﻿" + csv], { type: 'text/csv;charset=utf-8;' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url; a.download = filename; document.body.appendChild(a); a.click();
  setTimeout(() => { URL.revokeObjectURL(url); a.remove(); }, 100);
}

Object.assign(window, {
  ProfileScreen, EnrollScreen, RedeemScreen, FnBScreen, WellnessScreen, EditMemberModal,
  LoyaltyPanel, ExpiryBanner, RenewModal, VoidBillModal, CsvImportModal,
  printMemberCard, parseCSV, toCSV, downloadCSV, daysUntil,
});
