// ====================================================================
// INVOICING & BILLING, Stripe-powered, embedded (no redirect)
// Two personas:
//   • FranchiseeBilling, local franchisee spend, per-location cards
//   • CorporateBilling , national / enterprise spend + franchisee health
//
// Payment-method model (per the product spec):
//   - Every LOCATION has exactly ONE default payment method.
//   - Each location may also carry ONE backup method (charged only if the
//     default declines).
//   - Multi-location franchisees can assign DIFFERENT methods per location.
//
// Stripe integration notes (this prototype renders a faithful, in-system
// mock of Stripe's PaymentElement so the design reviews without a live
// backend). The real wiring is documented inline in <StripeCardForm/>:
//   loadStripe(pk) → SetupIntent (off_session) → elements({clientSecret,
//   appearance}) → PaymentElement (paymentMethodOrder:['card']) →
//   confirmSetup({redirect:'if_required'}). Stripe renders its own
//   iframe-per-field, so we never nest it inside a custom <iframe>.
// ====================================================================

const { useState: useStateB, useEffect: useEffectB, useRef: useRefB } = React;
const TB = (...a) => window.toast(...a);
const fmtUSD = (n) => '$' + Number(n).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const fmtUSD0 = (n) => '$' + Number(n).toLocaleString('en-US');

// --------------------------------------------------------------------
// CARD BRAND MARKS, simple, geometric (no hand-drawn complex SVG)
// --------------------------------------------------------------------
function CardBrand({ brand, size = 'md' }) {
  const w = size === 'lg' ? 46 : size === 'sm' ? 30 : 38;
  const h = Math.round(w * 0.63);
  const base = {
    width: w, height: h, borderRadius: 5, display: 'inline-flex',
    alignItems: 'center', justifyContent: 'center', flexShrink: 0,
    fontFamily: 'var(--ff-display)', fontWeight: 700, letterSpacing: '-0.02em',
    border: '1px solid var(--line)', background: 'var(--paper)', overflow: 'hidden',
  };
  if (brand === 'visa') {
    return <span style={{ ...base, color: '#1A1F71', fontStyle: 'italic', fontSize: w * 0.34 }}>VISA</span>;
  }
  if (brand === 'mastercard') {
    return (
      <span style={{ ...base, gap: 0, background: '#18181a' }}>
        <span style={{ width: h * 0.52, height: h * 0.52, borderRadius: '50%', background: '#EB001B', marginRight: -h * 0.18 }} />
        <span style={{ width: h * 0.52, height: h * 0.52, borderRadius: '50%', background: '#F79E1B', opacity: 0.92 }} />
      </span>
    );
  }
  if (brand === 'amex') {
    return <span style={{ ...base, background: '#1F72CD', color: '#fff', fontSize: w * 0.2, lineHeight: 1, textAlign: 'center', letterSpacing: 0 }}>AMEX</span>;
  }
  if (brand === 'ach' || brand === 'bank') {
    return (
      <span style={{ ...base, background: '#001732', color: '#fff', fontSize: w * 0.3 }}>
        <svg viewBox="0 0 24 24" width={w * 0.5} height={w * 0.5} fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3 21h18M5 21V9l7-5 7 5v12M9 21v-6h6v6"/></svg>
      </span>
    );
  }
  return <span style={{ ...base, color: 'var(--ink-3)', fontSize: w * 0.24 }}>CARD</span>;
}

const BRAND_LABEL = { visa: 'Visa', mastercard: 'Mastercard', amex: 'Amex', ach: 'ACH', bank: 'Bank' };

// --------------------------------------------------------------------
// STRIPE LOCKUP, "Powered by Stripe" secure footer
// --------------------------------------------------------------------
function StripeSecureLockup() {
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, marginTop: 14, color: 'var(--ink-4)', fontSize: 11.5 }}>
      <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="4" y="10.5" width="16" height="10" rx="2"/><path d="M8 10.5V7a4 4 0 0 1 8 0v3.5"/></svg>
      <span>Encrypted &amp; secure · Powered by</span>
      <span style={{ fontFamily: 'var(--ff-display)', fontWeight: 700, color: '#635BFF', fontSize: 13, letterSpacing: '-0.02em' }}>stripe</span>
    </div>
  );
}

// --------------------------------------------------------------------
// STRIPE CARD FORM, faithful in-system mock of Stripe's PaymentElement
// Restricted to cards (paymentMethodOrder:['card']). Saves a method via
// a SetupIntent (no charge). Styled to LOCALACT via the Appearance API.
// --------------------------------------------------------------------
//
// REAL INTEGRATION (replace the mock submit below):
//
//   const stripe = await loadStripe(PUBLISHABLE_KEY);                 // js.stripe.com, never bundled
//   const { clientSecret } = await fetch('/api/stripe/setup-intent',  // SetupIntent, usage:'off_session'
//     { method: 'POST', body: JSON.stringify({ customer }) }).then(r => r.json());
//   const elements = stripe.elements({
//     clientSecret,
//     appearance: STRIPE_APPEARANCE,        // see below, matched to LOCALACT
//   });
//   const paymentElement = elements.create('payment', { paymentMethodOrder: ['card'] });
//   paymentElement.mount('#stripe-payment-element');   // Stripe renders its own iframes
//   // on submit:
//   const { error } = await stripe.confirmSetup({
//     elements,
//     confirmParams: { return_url: window.location.href },
//     redirect: 'if_required',              // cards confirm without leaving the page
//   });
//   // Server listens for setup_intent.succeeded → updates default PM.
//
// STRIPE_APPEARANCE (matched to the live LOCALACT palette):
//   { theme: 'stripe', variables: {
//       colorPrimary:'#002857', colorBackground:'#FFFFFF', colorText:'#2a2a2b',
//       colorDanger:'#d92d20', fontFamily:'Inter, sans-serif',
//       borderRadius:'10px', fontSizeBase:'14px' },
//     rules: { '.Input': { backgroundColor:'#ececee', border:'none', boxShadow:'none' },
//              '.Label': { color:'#6E6E73', fontSize:'12px', fontWeight:'500' } } }
//
function detectBrand(num) {
  const n = num.replace(/\s/g, '');
  if (/^4/.test(n)) return 'visa';
  if (/^(5[1-5]|2[2-7])/.test(n)) return 'mastercard';
  if (/^3[47]/.test(n)) return 'amex';
  return null;
}

function StripeCardForm({ onSaved, onCancel, assignTarget }) {
  const [num, setNum] = useStateB('');
  const [exp, setExp] = useStateB('');
  const [cvc, setCvc] = useStateB('');
  const [name, setName] = useStateB('');
  const [zip, setZip] = useStateB('');
  const [role, setRole] = useStateB('default'); // default | backup | library
  const [submitting, setSubmitting] = useStateB(false);
  const [focus, setFocus] = useStateB(null);
  const brand = detectBrand(num);
  const isAmex = brand === 'amex';

  const fmtNum = (v) => {
    const d = v.replace(/\D/g, '').slice(0, isAmex ? 15 : 16);
    if (isAmex) return d.replace(/(\d{4})(\d{0,6})(\d{0,5})/, (m, a, b, c) => [a, b, c].filter(Boolean).join(' '));
    return d.replace(/(\d{4})(?=\d)/g, '$1 ').trim();
  };
  const fmtExp = (v) => {
    const d = v.replace(/\D/g, '').slice(0, 4);
    return d.length > 2 ? d.slice(0, 2) + ' / ' + d.slice(2) : d;
  };

  const digits = num.replace(/\s/g, '').length;
  const valid = digits >= (isAmex ? 15 : 16) && exp.replace(/\D/g, '').length === 4 &&
                cvc.length >= (isAmex ? 4 : 3) && name.trim().length > 1 && zip.replace(/\D/g, '').length >= 5;

  function submit() {
    if (!valid || submitting) return;
    setSubmitting(true);
    // MOCK: stand-in for stripe.confirmSetup({ redirect:'if_required' }).
    setTimeout(() => {
      const last4 = num.replace(/\s/g, '').slice(-4);
      onSaved({ id: 'pm_' + Date.now(), brand: brand || 'card', last4, exp: exp || '– / –', holder: name.trim(), added: 'Just now', role });
    }, 1100);
  }

  const fieldWrap = (active, danger) => ({
    background: 'var(--cream-2)', borderRadius: 10,
    border: '1.5px solid ' + (danger ? 'var(--bad)' : active ? 'var(--brand-navy)' : 'transparent'),
    boxShadow: active ? '0 0 0 3px rgba(29,76,181,0.12)' : 'none',
    transition: 'border-color .12s, box-shadow .12s', display: 'flex', alignItems: 'center',
  });
  const inputStyle = { flex: 1, border: 'none', outline: 'none', background: 'transparent', padding: '12px 14px', fontSize: 14, color: 'var(--ink)', fontFamily: 'var(--ff-ui)', minWidth: 0 };
  const labelStyle = { fontSize: 12, fontWeight: 500, color: 'var(--ink-3)', marginBottom: 6, display: 'block' };

  return (
    <div>
      {/* This whole block stands in for #stripe-payment-element. Stripe.js
          would mount its own iframe-per-field here, never wrapped in a
          custom <iframe>. Layout & styling mirror the PaymentElement. */}
      <div id="stripe-payment-element" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div>
          <label style={labelStyle}>Card number</label>
          <div style={fieldWrap(focus === 'num')}>
            <input style={inputStyle} inputMode="numeric" placeholder="1234 1234 1234 1234"
                   value={num} onFocus={() => setFocus('num')} onBlur={() => setFocus(null)}
                   onChange={e => setNum(fmtNum(e.target.value))} />
            <span style={{ paddingRight: 12, display: 'flex', gap: 4, alignItems: 'center' }}>
              {brand ? <CardBrand brand={brand} size="sm" /> : <>
                <CardBrand brand="visa" size="sm" /><CardBrand brand="mastercard" size="sm" /><CardBrand brand="amex" size="sm" />
              </>}
            </span>
          </div>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
          <div>
            <label style={labelStyle}>Expiration</label>
            <div style={fieldWrap(focus === 'exp')}>
              <input style={inputStyle} inputMode="numeric" placeholder="MM / YY"
                     value={exp} onFocus={() => setFocus('exp')} onBlur={() => setFocus(null)}
                     onChange={e => setExp(fmtExp(e.target.value))} />
            </div>
          </div>
          <div>
            <label style={labelStyle}>CVC</label>
            <div style={fieldWrap(focus === 'cvc')}>
              <input style={inputStyle} inputMode="numeric" placeholder={isAmex ? '4 digits' : 'CVC'}
                     value={cvc} onFocus={() => setFocus('cvc')} onBlur={() => setFocus(null)}
                     onChange={e => setCvc(e.target.value.replace(/\D/g, '').slice(0, isAmex ? 4 : 3))} />
              <span style={{ paddingRight: 12, color: 'var(--ink-5)' }}>
                <svg viewBox="0 0 24 24" width="22" height="16" fill="none" stroke="currentColor" strokeWidth="1.6"><rect x="2" y="5" width="20" height="14" rx="2.5"/><path d="M2 9h20" strokeWidth="2.4"/></svg>
              </span>
            </div>
          </div>
        </div>
        <div>
          <label style={labelStyle}>Cardholder name</label>
          <div style={fieldWrap(focus === 'name')}>
            <input style={inputStyle} placeholder="Full name on card"
                   value={name} onFocus={() => setFocus('name')} onBlur={() => setFocus(null)}
                   onChange={e => setName(e.target.value)} />
          </div>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
          <div>
            <label style={labelStyle}>Country</label>
            <div style={{ ...fieldWrap(false), padding: '0' }}>
              <select style={{ ...inputStyle, appearance: 'none', cursor: 'pointer' }} defaultValue="US">
                <option value="US">United States</option>
                <option value="CA">Canada</option>
              </select>
            </div>
          </div>
          <div>
            <label style={labelStyle}>ZIP</label>
            <div style={fieldWrap(focus === 'zip')}>
              <input style={inputStyle} inputMode="numeric" placeholder="ZIP code"
                     value={zip} onFocus={() => setFocus('zip')} onBlur={() => setFocus(null)}
                     onChange={e => setZip(e.target.value.replace(/\D/g, '').slice(0, 5))} />
            </div>
          </div>
        </div>
      </div>

      {/* Role within the location's wallet, the product's default/backup model */}
      {assignTarget && (
        <div style={{ marginTop: 18, padding: '14px 16px', background: 'var(--terra-softer)', border: '1px solid var(--terra-soft)', borderRadius: 10 }}>
          <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink)', marginBottom: 2 }}>Use this card for {assignTarget}</div>
          <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginBottom: 10 }}>Pick how it's applied. You can change this anytime.</div>
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            {[
              { k: 'default', t: 'Set as default', s: 'Charged first each month' },
              { k: 'backup', t: 'Set as backup', s: 'Used only if default declines' },
              { k: 'library', t: 'Save to wallet only', s: 'Assign later' },
            ].map(o => (
              <button key={o.k} onClick={() => setRole(o.k)} style={{
                flex: '1 1 120px', textAlign: 'left', padding: '10px 12px', borderRadius: 9, cursor: 'pointer',
                border: '1.5px solid ' + (role === o.k ? 'var(--brand-navy)' : 'var(--line)'),
                background: role === o.k ? 'var(--paper)' : 'var(--paper)',
                boxShadow: role === o.k ? '0 0 0 3px rgba(29,76,181,0.10)' : 'none',
              }}>
                <div style={{ fontSize: 12.5, fontWeight: 600, color: role === o.k ? 'var(--brand-navy)' : 'var(--ink)' }}>{o.t}</div>
                <div style={{ fontSize: 10.5, color: 'var(--ink-4)', marginTop: 2 }}>{o.s}</div>
              </button>
            ))}
          </div>
        </div>
      )}

      <div style={{ display: 'flex', gap: 8, marginTop: 18 }}>
        {onCancel && <button className="btn btn-ghost btn-sm" style={{ flex: '0 0 auto' }} onClick={onCancel}>Cancel</button>}
        <button className="btn btn-primary btn-sm" style={{ flex: 1, justifyContent: 'center', opacity: valid ? 1 : 0.5, pointerEvents: submitting ? 'none' : 'auto' }} onClick={submit}>
          {submitting ? 'Saving securely…' : 'Save card'}
        </button>
      </div>
      <StripeSecureLockup />
    </div>
  );
}

// --------------------------------------------------------------------
// ADD / UPDATE CARD MODAL, hosts the Stripe form (no redirect)
// --------------------------------------------------------------------
function AddCardModal({ open, onClose, onSaved, mode = 'add', assignTarget }) {
  return (
    <window.Modal open={open} onClose={onClose} width={520}
      title={mode === 'update' ? 'Update payment method' : 'Add payment method'}
      sub="Securely save a card with Stripe. No charge is made now, we store it for future invoices.">
      <StripeCardForm assignTarget={assignTarget}
        onSaved={(card) => { onSaved && onSaved(card); onClose(); TB('Card saved', `${BRAND_LABEL[card.brand] || 'Card'} •••• ${card.last4} saved via Stripe.`, 'ok'); }}
        onCancel={onClose} />
    </window.Modal>
  );
}

// --------------------------------------------------------------------
// PAYMENT METHOD CHIP, compact saved-card row
// --------------------------------------------------------------------
function MethodChip({ card, role }) {
  if (!card) return <span style={{ fontSize: 12.5, color: 'var(--ink-5)', fontStyle: 'italic' }}>None set</span>;
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
      <CardBrand brand={card.brand} size="sm" />
      <span style={{ fontSize: 12.5, color: 'var(--ink)', fontFamily: 'var(--ff-mono)' }}>•••• {card.last4}</span>
      {role && <span className={`badge ${role === 'default' ? 'badge-info' : 'badge-neutral'}`} style={{ fontSize: 9.5 }}>{role}</span>}
    </span>
  );
}

// --------------------------------------------------------------------
// SUMMARY STAT CARDS, balance due + last payment
// --------------------------------------------------------------------
function SummaryCards({ balance, dueDate, autopay, last }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 20 }}>
      <div className="card card-pad" style={{ background: 'linear-gradient(135deg, var(--terra-softer), var(--paper) 70%)' }}>
        <div style={{ fontSize: 11, letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--ink-3)', fontWeight: 600, marginBottom: 8 }}>Current balance due</div>
        <div style={{ fontFamily: 'var(--ff-display)', fontSize: 40, fontWeight: 600, letterSpacing: '-0.02em', lineHeight: 1 }}>{fmtUSD(balance)}</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 12 }}>
          <span className="badge badge-info">Due {dueDate}</span>
          {autopay && <span style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>· auto-pays via {autopay}</span>}
        </div>
      </div>
      <div className="card card-pad">
        <div style={{ fontSize: 11, letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--ink-3)', fontWeight: 600, marginBottom: 8 }}>Last payment</div>
        <div style={{ fontFamily: 'var(--ff-display)', fontSize: 40, fontWeight: 600, letterSpacing: '-0.02em', lineHeight: 1, color: 'var(--ink)' }}>{fmtUSD(last.amount)}</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, fontSize: 12.5, color: 'var(--ink-3)' }}>
          <span className="badge badge-ok">● Paid {last.date}</span>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><CardBrand brand={last.brand} size="sm" /> •••• {last.last4}</span>
        </div>
      </div>
    </div>
  );
}

// --------------------------------------------------------------------
// UPCOMING INVOICE, line-item preview for the current period
// --------------------------------------------------------------------
function UpcomingInvoice({ invNo, period, lines, dueDate, locationName, locationPicker }) {
  const total = lines.reduce((s, l) => s + l.amt, 0);
  return (
    <div className="card" style={{ overflow: 'hidden', marginBottom: 20 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16, padding: '18px 22px', borderBottom: '1px solid var(--line)', flexWrap: 'wrap' }}>
        <div>
          <div style={{ fontFamily: 'var(--ff-display)', fontSize: 17, fontWeight: 600, color: 'var(--ink)' }}>Upcoming invoice · {invNo}</div>
          <div style={{ fontSize: 12.5, color: 'var(--ink-3)', marginTop: 3 }}>Billing period {period}{locationName ? ` · ${locationName}` : ''}</div>
        </div>
        {locationPicker}
      </div>
      <table className="tbl">
        <thead><tr><th>Service</th><th>Description</th><th style={{ textAlign: 'right' }}>Amount</th></tr></thead>
        <tbody>
          {lines.map((l, i) => (
            <tr key={i}>
              <td className="strong">{l.svc}</td>
              <td className="muted">{l.desc}</td>
              <td className="strong" style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)' }}>{fmtUSD(l.amt)}</td>
            </tr>
          ))}
        </tbody>
      </table>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 22px', background: 'var(--cream)', borderTop: '1px solid var(--line)' }}>
        <div style={{ fontSize: 12.5, color: 'var(--ink-3)' }}>Total due <strong style={{ color: 'var(--ink)' }}>{dueDate}</strong></div>
        <div style={{ fontFamily: 'var(--ff-display)', fontSize: 26, fontWeight: 600, letterSpacing: '-0.01em' }}>{fmtUSD(total)}</div>
      </div>
    </div>
  );
}

// --------------------------------------------------------------------
// INVOICE HISTORY, paginated table with status + PDF link
// invoice.invoice_pdf is a signed, time-limited URL, fetched fresh.
// --------------------------------------------------------------------
function InvoiceHistory({ rows, pageSize = 6 }) {
  const [shown, setShown] = useStateB(pageSize);
  const dl = (id) => TB('Preparing PDF', `${id}, fetching a fresh signed link from Stripe.`, 'info');
  const statusBadge = (s) => {
    if (s === 'Paid') return <span className="badge badge-ok">● Paid</span>;
    if (s === 'Open') return <span className="badge badge-warn">● Open</span>;
    return <span className="badge badge-bad">● Failed</span>;
  };
  return (
    <div className="card" style={{ overflow: 'hidden' }}>
      <table className="tbl">
        <thead><tr><th>Invoice</th><th>Period</th><th style={{ textAlign: 'right' }}>Amount</th><th>Status</th><th /></tr></thead>
        <tbody>
          {rows.slice(0, shown).map((r, i) => (
            <tr key={i}>
              <td className="mono muted">{r.no}</td>
              <td>{r.period}{r.location ? <span style={{ color: 'var(--ink-4)', fontSize: 11 }}> · {r.location}</span> : ''}</td>
              <td className="strong" style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)' }}>{fmtUSD(r.amount)}</td>
              <td>{statusBadge(r.status)}</td>
              <td style={{ textAlign: 'right' }}>
                <button className="btn btn-quiet btn-xs" onClick={() => dl(r.no)}>
                  <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={{ marginRight: 4, verticalAlign: -2 }}><path d="M12 3v12m0 0l-4-4m4 4l4-4M5 21h14"/></svg>
                  PDF
                </button>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
      {shown < rows.length && (
        <div style={{ padding: '12px', borderTop: '1px solid var(--line)', textAlign: 'center' }}>
          <button className="btn btn-ghost btn-sm" onClick={() => setShown(s => s + pageSize)}>Load more · {rows.length - shown} older</button>
        </div>
      )}
    </div>
  );
}

window.BillingShared = { CardBrand, StripeCardForm, AddCardModal, SummaryCards, UpcomingInvoice, InvoiceHistory, MethodChip, BRAND_LABEL, fmtUSD, fmtUSD0 };

// ====================================================================
// ASSIGN MODAL, choose default/backup for a location from the wallet,
// or add a new card inline (Stripe, no redirect).
// ====================================================================
function AssignModal({ open, onClose, location, role, wallet, currentId, otherId, onPick, onAddCard }) {
  const [sel, setSel] = useStateB(currentId || null);
  const [adding, setAdding] = useStateB(false);
  useEffectB(() => { if (open) { setSel(currentId || null); setAdding(false); } }, [open, currentId]);
  if (!open) return null;
  const isBackup = role === 'backup';
  return (
    <window.Modal open={open} onClose={onClose} width={520}
      title={`${isBackup ? 'Backup' : 'Default'} payment method`}
      sub={`${location.name} · ${isBackup ? 'used only if the default declines' : 'charged first each billing cycle'}`}
      footer={!adding && <>
        <button className="btn btn-ghost btn-sm" onClick={onClose}>Cancel</button>
        <button className="btn btn-primary btn-sm" onClick={() => { onPick(sel); onClose(); TB('Payment method updated', `${location.name} · ${isBackup ? 'backup' : 'default'} set.`, 'ok'); }}>Save</button>
      </>}>
      {!adding ? <>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {isBackup && (
            <button onClick={() => setSel(null)} style={rowSelStyle(sel === null)}>
              <span style={{ fontSize: 13, color: 'var(--ink-2)' }}>No backup method</span>
              <Radio on={sel === null} />
            </button>
          )}
          {wallet.map(c => {
            const disabled = c.id === otherId;
            return (
              <button key={c.id} disabled={disabled} onClick={() => setSel(c.id)}
                style={{ ...rowSelStyle(sel === c.id), opacity: disabled ? 0.45 : 1, cursor: disabled ? 'not-allowed' : 'pointer' }}>
                <span style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                  <CardBrand brand={c.brand} />
                  <span style={{ textAlign: 'left' }}>
                    <span style={{ display: 'block', fontSize: 13, fontWeight: 600, color: 'var(--ink)' }}>{BRAND_LABEL[c.brand] || 'Card'} •••• {c.last4}</span>
                    <span style={{ display: 'block', fontSize: 11, color: 'var(--ink-4)' }}>{c.holder} · exp {c.exp}{disabled ? ' · used as ' + (isBackup ? 'default' : 'backup') : ''}</span>
                  </span>
                </span>
                <Radio on={sel === c.id} />
              </button>
            );
          })}
        </div>
        <button className="btn btn-ghost btn-sm" style={{ width: '100%', marginTop: 12, justifyContent: 'center' }} onClick={() => setAdding(true)}>＋ Add a new card</button>
      </> : (
        <div>
          <button className="btn btn-quiet btn-xs" style={{ marginBottom: 12 }} onClick={() => setAdding(false)}>← Back to saved cards</button>
          <StripeCardForm
            onSaved={(card) => { onAddCard(card); setSel(card.id); setAdding(false); TB('Card saved', `${BRAND_LABEL[card.brand] || 'Card'} •••• ${card.last4} added to wallet.`, 'ok'); }}
            onCancel={() => setAdding(false)} />
        </div>
      )}
    </window.Modal>
  );
}
function rowSelStyle(on) {
  return {
    display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%',
    padding: '12px 14px', borderRadius: 10, cursor: 'pointer', textAlign: 'left',
    border: '1.5px solid ' + (on ? 'var(--brand-navy)' : 'var(--line)'),
    background: on ? 'var(--terra-softer)' : 'var(--paper)',
    boxShadow: on ? '0 0 0 3px rgba(29,76,181,0.10)' : 'none',
  };
}
function Radio({ on }) {
  return <span style={{ width: 18, height: 18, borderRadius: '50%', flexShrink: 0, border: '2px solid ' + (on ? 'var(--brand-navy)' : 'var(--line-2)'), display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
    {on && <span style={{ width: 9, height: 9, borderRadius: '50%', background: 'var(--brand-navy)' }} />}
  </span>;
}

// ====================================================================
// LOCATION BILLING CARD, one card per location: default + backup
// ====================================================================
function LocationBillingCard({ loc, wallet, assign, onChange, single }) {
  const def = wallet.find(c => c.id === assign.default);
  const bak = wallet.find(c => c.id === assign.backup);
  const Method = ({ label, card, role, hint }) => (
    <div style={{ flex: 1, minWidth: 220 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
        <span style={{ fontSize: 10.5, letterSpacing: '.06em', textTransform: 'uppercase', fontWeight: 700, color: role === 'default' ? 'var(--brand-navy)' : 'var(--ink-4)' }}>{label}</span>
        <button className="btn btn-quiet btn-xs" onClick={() => onChange(loc.id, role)}>{card ? 'Change' : 'Add'}</button>
      </div>
      {card ? (
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 14px', border: '1px solid var(--line)', borderRadius: 10, background: 'var(--paper)' }}>
          <CardBrand brand={card.brand} size="lg" />
          <div style={{ minWidth: 0 }}>
            <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink)', fontFamily: 'var(--ff-mono)' }}>•••• {card.last4}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-4)' }}>{card.holder} · exp {card.exp}</div>
          </div>
        </div>
      ) : (
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '12px 14px', border: '1px dashed var(--line-2)', borderRadius: 10, color: 'var(--ink-4)', fontSize: 12.5 }}>
          {hint}
        </div>
      )}
    </div>
  );
  return (
    <div className="card card-pad" style={{ marginBottom: 12 }}>
      {!single && (
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16, gap: 12, flexWrap: 'wrap' }}>
          <div>
            <div style={{ fontFamily: 'var(--ff-display)', fontSize: 17, fontWeight: 600, color: 'var(--ink)' }}>{loc.name}</div>
            <div style={{ fontSize: 12, color: 'var(--ink-4)' }}>{loc.city} · {fmtUSD0(loc.monthly)}/mo est.</div>
          </div>
          {def ? <span className="badge badge-ok">● auto-pay on</span> : <span className="badge badge-warn">● needs a card</span>}
        </div>
      )}
      <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap' }}>
        <Method label="Default" card={def} role="default" hint="No card, invoices can't auto-pay" />
        <Method label="Backup" card={bak} role="backup" hint="No backup, add one for resilience" />
      </div>
    </div>
  );
}

// ====================================================================
// FRANCHISEE BILLING, local spend, per-location payment methods
// ====================================================================
function FranchiseeBilling({ variant = 'star' }) {
  const isSingle = variant === 'bare';
  const defaultWallet = (single) => single ? [
    { id: 'pm_1', brand: 'visa',       last4: '7781', exp: '05 / 27', holder: 'Danny Ruiz', added: 'Feb 2024' },
    { id: 'pm_2', brand: 'mastercard', last4: '2245', exp: '09 / 26', holder: 'Danny Ruiz', added: 'Aug 2024' },
  ] : [
    { id: 'pm_1', brand: 'visa',       last4: '4242', exp: '08 / 27', holder: 'Alicia Chen',        added: 'Apr 2024' },
    { id: 'pm_2', brand: 'mastercard', last4: '5318', exp: '11 / 26', holder: 'Alicia Chen',        added: 'Jan 2025' },
    { id: 'pm_3', brand: 'amex',       last4: '1009', exp: '03 / 28', holder: "FreshNest LLC", added: 'Mar 2026' },
  ];
  const [wallet, setWallet] = useStateB(() => defaultWallet(isSingle));
  const LOCS = isSingle ? [
    { id: 1, name: 'Plano Legacy', city: 'Plano, TX', monthly: 300 },
  ] : [
    { id: 1, name: 'Austin Downtown',       city: 'Austin, TX',      monthly: 3572 },
    { id: 2, name: 'Austin North',          city: 'Round Rock, TX',  monthly: 2410 },
    { id: 3, name: 'San Antonio Riverwalk', city: 'San Antonio, TX', monthly: 1980 },
    { id: 4, name: 'Houston Galleria',      city: 'Houston, TX',     monthly: 4120 },
  ];
  const defaultAssign = (single) => single
    ? { 1: { default: 'pm_1', backup: 'pm_2' } }
    : { 1: { default: 'pm_1', backup: 'pm_2' }, 2: { default: 'pm_2', backup: 'pm_1' }, 3: { default: 'pm_3', backup: null }, 4: { default: 'pm_1', backup: 'pm_3' } };
  const [assign, setAssign] = useStateB(() => defaultAssign(isSingle));
  const [assignFor, setAssignFor] = useStateB(null); // { locId, role }
  const [selInv, setSelInv] = useStateB(1);
  useEffectB(() => { setWallet(defaultWallet(isSingle)); setAssign(defaultAssign(isSingle)); setSelInv(1); setAssignFor(null); }, [variant]);
  const [billTab, setBillTab] = useStateB('invoices'); // invoices | methods

  const portfolioBalance = LOCS.reduce((s, l) => s + l.monthly, 0);

  // Upcoming-invoice line items, scaled per selected location
  const invLinesFor = (loc) => {
    if (isSingle) return [
      { svc: 'Google Paid Search', desc: 'Managed search · June', amt: 250 },
      { svc: 'LOCALACT + Local Team', desc: 'Platform + strategist · June', amt: 50 },
    ];
    const f = loc.monthly / 3572; // Austin Downtown is the reference mix
    return [
      { svc: 'Google Paid Ads', desc: 'Management fee · June', amt: Math.round(525 * f) },
      { svc: 'Local SEO',       desc: 'LSEO Standard · June',  amt: Math.round(449 * f) },
      { svc: 'Listings Management', desc: '42 directories · June', amt: 79 },
      { svc: 'LOCALACT + Local Team', desc: 'Platform + strategist · June', amt: 149 },
      { svc: 'PPC Media',       desc: 'Google Ads + Performance Max spend', amt: Math.round(1850 * f) },
      { svc: 'LSA Media',       desc: 'Local Services Ads leads', amt: Math.round(420 * f) },
    ];
  };
  const selLoc = LOCS.find(l => l.id === selInv);

  const history = isSingle ? [
    { no: '#INV-26-0512', period: 'May 2026',   amount: 300, status: 'Paid' },
    { no: '#INV-26-0411', period: 'April 2026', amount: 300, status: 'Paid' },
    { no: '#INV-26-0310', period: 'March 2026', amount: 300, status: 'Paid' },
    { no: '#INV-26-0209', period: 'Feb 2026',   amount: 300, status: 'Paid' },
    { no: '#INV-26-0108', period: 'Jan 2026',   amount: 300, status: 'Paid' },
  ] : [
    { no: '#INV-26-0512', period: 'May 2026',   amount: 12082, status: 'Paid', location: '4 locations' },
    { no: '#INV-26-0411', period: 'April 2026', amount: 11920, status: 'Paid', location: '4 locations' },
    { no: '#INV-26-0310', period: 'March 2026', amount: 3572,  status: 'Paid', location: 'Austin Downtown' },
    { no: '#INV-26-0309', period: 'March 2026', amount: 2410,  status: 'Paid', location: 'Austin North' },
    { no: '#INV-26-0308', period: 'March 2026', amount: 1980,  status: 'Failed', location: 'San Antonio Riverwalk' },
    { no: '#INV-26-0307', period: 'March 2026', amount: 4120,  status: 'Paid', location: 'Houston Galleria' },
    { no: '#INV-26-0206', period: 'Feb 2026',   amount: 11540, status: 'Paid', location: '4 locations' },
    { no: '#INV-26-0105', period: 'Jan 2026',   amount: 11210, status: 'Paid', location: '4 locations' },
  ];

  const single = LOCS.length === 1;
  const ar = assignFor ? assign[assignFor.locId] : null;

  function addCardToWallet(card) { setWallet(w => [...w, { ...card }]); }
  function pickFor(locId, role, cardId) {
    setAssign(a => ({ ...a, [locId]: { ...a[locId], [role]: cardId } }));
  }

  return (
    <div className="page active">
      <div className="page-header">
        <div>
          <div className="page-title">Invoice &amp; billing.</div>
          <div className="page-sub">{LOCS.length} location{LOCS.length > 1 ? 's' : ''} · June 2026 · due July 1</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost btn-sm" onClick={() => TB('Statement export', 'CSV of all invoices across your locations is generating.', 'info')}>Export statement</button>
          <button className="btn btn-primary btn-sm" onClick={() => setBillTab('methods')}>Manage payment methods</button>
        </div>
      </div>

      <div className="tabs">
        {[['invoices', 'Invoices'], ['methods', single ? 'Payment method' : 'Payment methods']].map(([k, l]) => (
          <button key={k} className={`tab ${billTab === k ? 'active' : ''}`} onClick={() => setBillTab(k)}>{l}</button>
        ))}
      </div>

      {billTab === 'invoices' && (
        <>
          <SummaryCards
            balance={portfolioBalance}
            dueDate="July 1, 2026"
            autopay={isSingle ? 'card on file' : 'cards on file'}
            last={{ amount: isSingle ? 300 : 12082, date: 'Jun 1', brand: 'visa', last4: isSingle ? '7781' : '4242' }} />

          {/* ---- Upcoming invoice ---- */}
          <div className="section-label">Upcoming invoice</div>
          <UpcomingInvoice
            invNo="#INV-26-0601"
            period="June 1 – June 30"
            locationName={selLoc.name}
            dueDate="July 1, 2026"
            lines={invLinesFor(selLoc)}
            locationPicker={
              <select value={selInv} onChange={e => setSelInv(Number(e.target.value))}
                style={{ padding: '8px 12px', borderRadius: 8, border: '1px solid var(--line)', background: 'var(--paper)', fontSize: 12.5, fontWeight: 600, color: 'var(--ink)', cursor: 'pointer' }}>
                {LOCS.map(l => <option key={l.id} value={l.id}>{l.name}</option>)}
              </select>
            } />

          {/* ---- History ---- */}
          <div className="section-label">Invoice history</div>
          <InvoiceHistory rows={history} />
        </>
      )}

      {billTab === 'methods' && (
        <>
          <div className="section-label">{single ? 'Payment method' : 'Payment methods by location'}</div>
          {!single && (
            <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10, marginBottom: 14, padding: '12px 16px', background: 'var(--terra-softer)', border: '1px solid var(--terra-soft)', borderRadius: 10, fontSize: 12.5, color: 'var(--ink-3)', lineHeight: 1.5 }}>
              <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="var(--brand-navy)" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0, marginTop: 1 }}><circle cx="12" cy="12" r="9"/><path d="M12 8v5m0 3h.01"/></svg>
              <span>Each location has <strong style={{ color: 'var(--ink)' }}>one default card</strong> charged on the 1st, plus an optional <strong style={{ color: 'var(--ink)' }}>backup</strong> used only if the default declines. You can use a different card for each location.</span>
            </div>
          )}
          {LOCS.map(loc => (
            <LocationBillingCard key={loc.id} loc={loc} wallet={wallet} assign={assign[loc.id] || { default: null, backup: null }} single={single}
              onChange={(locId, role) => setAssignFor({ locId, role })} />
          ))}
        </>
      )}

      <AssignModal
        open={!!assignFor}
        onClose={() => setAssignFor(null)}
        location={assignFor ? LOCS.find(l => l.id === assignFor.locId) : LOCS[0]}
        role={assignFor ? assignFor.role : 'default'}
        wallet={wallet}
        currentId={ar ? ar[assignFor.role] : null}
        otherId={ar ? ar[assignFor.role === 'default' ? 'backup' : 'default'] : null}
        onPick={(cardId) => pickFor(assignFor.locId, assignFor.role, cardId)}
        onAddCard={addCardToWallet} />
    </div>
  );
}
window.FranchiseeBilling = FranchiseeBilling;

// ====================================================================
// MESSAGE OWNER MODAL
// ====================================================================
function MessageOwnerModal({ target, onClose }) {
  const defaultMsg =
    'Hi ' + target.owner + ',\n\nThis is a note from the FreshNest corporate team regarding your ' + target.unit + ' location.\n\nYour payment method on file has ' + target.issue.toLowerCase() + '. To avoid any interruption to your LOCALACT services, please update your card at your earliest convenience.\n\nYou can update your payment method directly in LOCALACT under Invoice & Billing → Payment Methods. If you need any help, reply to this message and we will assist you.\n\nThank you,\nFreshNest Corporate';
  const [msg, setMsg] = useStateB(defaultMsg);
  const [via, setVia] = useStateB('email');
  const [sending, setSending] = useStateB(false);
  function send() {
    setSending(true);
    setTimeout(() => { onClose(); TB('Message sent', target.owner + ' · ' + target.unit + ' notified via ' + via + '.', 'ok'); }, 900);
  }
  return (
    <window.Modal open={true} onClose={onClose} width={560}
      title={'Message owner · ' + target.unit}
      sub={target.owner + ' · ' + target.issue}
      footer={<>
        <button className="btn btn-ghost btn-sm" onClick={onClose}>Cancel</button>
        <button className="btn btn-primary btn-sm" style={{ opacity: sending ? 0.6 : 1 }} onClick={send}>
          {sending ? 'Sending…' : 'Send via ' + via}
        </button>
      </>}>
      <div style={{ marginBottom: 14 }}>
        <div style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 8 }}>Send via</div>
        <div style={{ display: 'flex', gap: 8 }}>
          {['email', 'SMS', 'email + SMS'].map(v => (
            <button key={v} onClick={() => setVia(v)} style={{
              padding: '7px 14px', borderRadius: 8, fontSize: 12.5, fontWeight: 600, cursor: 'pointer',
              border: '1.5px solid ' + (via === v ? 'var(--brand-navy)' : 'var(--line)'),
              background: via === v ? 'var(--terra-softer)' : 'var(--paper)',
              color: via === v ? 'var(--brand-navy)' : 'var(--ink-2)',
              boxShadow: via === v ? '0 0 0 3px rgba(29,76,181,0.10)' : 'none',
            }}>{v}</button>
          ))}
        </div>
      </div>
      <div>
        <div style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 8 }}>Message</div>
        <textarea value={msg} onChange={e => setMsg(e.target.value)} rows={10}
          style={{ width: '100%', padding: '12px 14px', border: '1.5px solid var(--line)', borderRadius: 10, fontSize: 13, lineHeight: 1.65, color: 'var(--ink)', background: 'var(--cream)', resize: 'vertical', boxSizing: 'border-box', fontFamily: 'var(--ff-ui)' }} />
        <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 6 }}>Pre-filled, edit freely before sending.</div>
      </div>
    </window.Modal>
  );
}

// CORPORATE BILLING, national / enterprise spend + franchisee health
// ====================================================================
function CorporateBilling() {
  const [wallet, setWallet] = useStateB([
    { id: 'pm_ach', brand: 'ach',  last4: '8821', exp: 'ACH',     holder: 'FreshNest Corp · Wells Fargo', added: 'Jan 2024' },
    { id: 'pm_vc',  brand: 'visa', last4: '6610', exp: '02 / 28', holder: 'FreshNest Corporate Card',     added: 'Jun 2024' },
  ]);
  const [corpAssign, setCorpAssign] = useStateB({ default: 'pm_ach', backup: 'pm_vc' });
  const [assignFor, setAssignFor] = useStateB(null);
  const [msgTarget, setMsgTarget] = useStateB(null);
  const corpLoc = { id: 0, name: 'FreshNest Corporate', city: 'National account' };

  // Group 1, corp-funded location programs
  const locationPrograms = [
    { svc: 'Corp-funded Google Search baseline', desc: '700 enrolled locations × $100/mo', amt: 70000 },
    { svc: 'Management fee · corp-funded media',  desc: '10% on $70,000 corp-funded location media', amt: 7000 },
  ];
  // Group 2, national media programs
  const nationalMedia = [
    { svc: 'National Brand Search',   desc: 'Portfolio-wide brand keyword program · Google Ads',      amt: 18000 },
    { svc: 'National Social',         desc: 'Always-on Meta + Instagram · all 700 locations',         amt: 42000 },
    { svc: 'Enterprise Programmatic', desc: 'CTV + display · DMA targeting across full footprint',    amt: 65000 },
  ];
  // Group 3, enterprise services
  const enterpriseServices = [
    { svc: 'Brand-wide Local SEO',            desc: 'Foundation tier · 700 active locations',                   amt: 17050 },
    { svc: 'AI Search Optimization',          desc: 'GEO + LLM citation program · pilot wave (48 locations)',   amt: 8400  },
    { svc: 'LOCALACT Enterprise + Reporting', desc: 'Platform license · Central Reporting & BI',                amt: 4200  },
    { svc: 'Reputation Management (brand)',   desc: 'Aggregated review monitoring + escalations',               amt: 3400  },
    { svc: 'Listings Master Sync',            desc: 'Source-of-truth push · Google / Yelp / Apple / Meta',      amt: 2840  },
    { svc: 'Strategy Director · Priya R.',    desc: 'Quarterly playbook + monthly governance',                  amt: 3500  },
  ];
  const totalCorporate = [...locationPrograms, ...nationalMedia, ...enterpriseServices].reduce((s, l) => s + l.amt, 0);

  const declined = [
    { unit: 'Mountain View, CA', owner: 'Devon Walsh',    issue: 'Card declined 3 days ago',      due: 487 },
    { unit: 'Tempe, AZ',         owner: 'Marisol Ortega', issue: 'Card expired · awaiting update', due: 312 },
    { unit: 'Boise, ID',         owner: 'Henrik Nordby',  issue: 'Declined twice this month',      due: 621 },
  ];
  const totalOutstanding = declined.reduce((s, r) => s + r.due, 0);

  const history = [
    { no: '#CORP-26-05', period: 'May 2026',      amount: 238980, status: 'Paid' },
    { no: '#CORP-26-04', period: 'April 2026',    amount: 239390, status: 'Open' },
    { no: '#CORP-26-03', period: 'March 2026',    amount: 238980, status: 'Paid' },
    { no: '#CORP-26-02', period: 'February 2026', amount: 237420, status: 'Paid' },
    { no: '#CORP-26-01', period: 'January 2026',  amount: 236110, status: 'Paid' },
  ];

  const def = wallet.find(c => c.id === corpAssign.default);
  const bak = wallet.find(c => c.id === corpAssign.backup);
  function addCardToWallet(card) { setWallet(w => [...w, { ...card }]); }

  function InvoiceGroup({ title, lines }) {
    const sub = lines.reduce((s, l) => s + l.amt, 0);
    return (
      <>
        <tr><td colSpan={3} style={{ padding: '10px 22px 4px', fontSize: 10.5, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--ink-3)', background: 'var(--cream)' }}>{title}</td></tr>
        {lines.map((l, i) => (
          <tr key={i}>
            <td className="strong">{l.svc}</td>
            <td className="muted">{l.desc}</td>
            <td className="strong" style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)' }}>{fmtUSD(l.amt)}</td>
          </tr>
        ))}
        <tr>
          <td colSpan={2} style={{ padding: '5px 22px 12px', fontSize: 11, color: 'var(--ink-4)', fontStyle: 'italic', borderBottom: '2px solid var(--cream-3)' }}>Subtotal</td>
          <td style={{ padding: '5px 22px 12px', textAlign: 'right', fontFamily: 'var(--ff-mono)', fontSize: 12, color: 'var(--ink-3)', borderBottom: '2px solid var(--cream-3)' }}>{fmtUSD(sub)}</td>
        </tr>
      </>
    );
  }

  return (
    <div className="page active">
      <div className="page-header">
        <div>
          <div className="page-title">Corporate billing.</div>
          <div className="page-sub">National &amp; enterprise spend · June 2026 · due July 1</div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost btn-sm" onClick={() => TB('Statement export', 'CSV of all corporate invoices generating.', 'info')}>Export statement</button>
          <button className="btn btn-primary btn-sm" onClick={() => setAssignFor('default')}>Manage payment methods</button>
        </div>
      </div>

      <SummaryCards
        balance={totalCorporate}
        dueDate="July 1, 2026"
        autopay={def ? BRAND_LABEL[def.brand] + ' •••• ' + def.last4 : null}
        last={{ amount: 238980, date: 'Jun 1', brand: 'ach', last4: '8821' }} />

      {/* ---- Corporate wallet: exactly 2 methods ---- */}
      <div className="section-label">Account payment methods</div>
      <div className="card card-pad" style={{ marginBottom: 22 }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10, marginBottom: 16, padding: '10px 14px', background: 'var(--terra-softer)', border: '1px solid var(--terra-soft)', borderRadius: 10, fontSize: 12.5, color: 'var(--ink-3)', lineHeight: 1.5 }}>
          <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="var(--brand-navy)" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0, marginTop: 1 }}><circle cx="12" cy="12" r="9"/><path d="M12 8v5m0 3h.01"/></svg>
          <span>The corporate account holds <strong style={{ color: 'var(--ink)' }}>exactly two payment methods</strong>, a default and a backup. All corporate-funded services bill here. Franchisee services bill directly to each location.</span>
        </div>
        <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap' }}>
          {[{ label: 'Default', card: def, role: 'default' }, { label: 'Backup', card: bak, role: 'backup' }].map(m => (
            <div key={m.role} style={{ flex: 1, minWidth: 240 }}>
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
                <span style={{ fontSize: 10.5, letterSpacing: '.06em', textTransform: 'uppercase', fontWeight: 700, color: m.role === 'default' ? 'var(--brand-navy)' : 'var(--ink-4)' }}>{m.label}</span>
                <button className="btn btn-quiet btn-xs" onClick={() => setAssignFor(m.role)}>{m.card ? 'Change' : 'Add'}</button>
              </div>
              {m.card ? (
                <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '14px 16px', border: '1px solid var(--line)', borderRadius: 10 }}>
                  <CardBrand brand={m.card.brand} size="lg" />
                  <div style={{ minWidth: 0 }}>
                    <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--ink)' }}>{BRAND_LABEL[m.card.brand]} •••• {m.card.last4}</div>
                    <div style={{ fontSize: 11, color: 'var(--ink-4)' }}>{m.card.holder} · {m.card.exp === 'ACH' ? 'bank transfer' : 'exp ' + m.card.exp}</div>
                  </div>
                </div>
              ) : (
                <div style={{ padding: '14px 16px', border: '1px dashed var(--line-2)', borderRadius: 10, color: 'var(--ink-4)', fontSize: 12.5 }}>No {m.label.toLowerCase()} method on file</div>
              )}
            </div>
          ))}
        </div>
      </div>

      {/* ---- Grouped corporate invoice ---- */}
      <div className="section-label">Corporate invoice · upcoming</div>
      <div className="card" style={{ overflow: 'hidden', marginBottom: 20 }}>
        <div style={{ padding: '18px 22px', borderBottom: '1px solid var(--line)' }}>
          <div style={{ fontFamily: 'var(--ff-display)', fontSize: 17, fontWeight: 600, color: 'var(--ink)' }}>Invoice #CORP-26-06</div>
          <div style={{ fontSize: 12.5, color: 'var(--ink-3)', marginTop: 3 }}>Billing period June 1 – June 30, 2026</div>
        </div>
        <table className="tbl">
          <thead><tr><th>Service</th><th>Description</th><th style={{ textAlign: 'right' }}>Amount</th></tr></thead>
          <tbody>
            <InvoiceGroup title="Corp-funded location programs" lines={locationPrograms} />
            <InvoiceGroup title="National media programs" lines={nationalMedia} />
            <InvoiceGroup title="Enterprise services" lines={enterpriseServices} />
          </tbody>
        </table>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 22px', background: 'var(--cream)', borderTop: '1px solid var(--line)' }}>
          <div style={{ fontSize: 12.5, color: 'var(--ink-3)' }}>Total due <strong style={{ color: 'var(--ink)' }}>July 1, 2026</strong> · auto-pays via {def ? BRAND_LABEL[def.brand] + ' •••• ' + def.last4 : 'card on file'}</div>
          <div style={{ fontFamily: 'var(--ff-display)', fontSize: 26, fontWeight: 600, letterSpacing: '-0.01em' }}>{fmtUSD(totalCorporate)}</div>
        </div>
      </div>

      {/* ---- Franchisee billing health ---- */}
      <div className="section-label">Franchisee billing health</div>
      <div className="card card-pad" style={{ marginBottom: 14, borderLeft: '3px solid var(--bad)' }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, marginBottom: 14 }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 32, height: 32, borderRadius: 8, background: 'var(--bad)', color: '#fff', fontSize: 18, fontWeight: 700, flexShrink: 0 }}>!</span>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: 'var(--ff-display)', fontSize: 17, fontWeight: 600, color: 'var(--ink)' }}>3 franchisees with payment issues</div>
            <div style={{ fontSize: 12.5, color: 'var(--ink-3)', marginTop: 2 }}>{fmtUSD(totalOutstanding)} outstanding · these locations risk a service pause if unresolved by July 5.</div>
          </div>
          <button className="btn btn-ghost btn-sm" onClick={() => TB('Messages queued', 'Card-update request sent to all 3 owners via email + SMS.', 'ok')}>Message all owners</button>
        </div>
        <div style={{ border: '1px solid var(--line)', borderRadius: 8, overflow: 'hidden' }}>
          {declined.map((r, i) => (
            <div key={r.unit} style={{ display: 'grid', gridTemplateColumns: '1.4fr 1.6fr 0.9fr auto', gap: 12, padding: '14px 16px', borderBottom: i === declined.length - 1 ? 'none' : '1px solid var(--line)', alignItems: 'center', fontSize: 13 }}>
              <div>
                <div style={{ color: 'var(--ink)', fontWeight: 600 }}>{r.unit}</div>
                <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 1 }}>{r.owner}</div>
              </div>
              <div style={{ color: 'var(--bad)', fontWeight: 600, fontSize: 12.5 }}>{r.issue}</div>
              <div style={{ fontFamily: 'var(--ff-mono)', color: 'var(--ink)', fontWeight: 600 }}>{fmtUSD(r.due)}</div>
              <div>
                <button className="btn btn-primary btn-xs" onClick={() => setMsgTarget(r)}>Message owner</button>
              </div>
            </div>
          ))}
        </div>
      </div>
      <div style={{ marginBottom: 22, padding: '12px 16px', background: 'var(--paper)', border: '1px solid var(--line)', borderRadius: 10, display: 'flex', alignItems: 'center', gap: 18, fontSize: 12.5, flexWrap: 'wrap' }}>
        <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}><span style={{ width: 8, height: 8, borderRadius: '50%', background: 'var(--ok)' }} /><strong>697 of 700</strong> <span style={{ color: 'var(--ink-3)' }}>on auto-pay</span></span>
        <span style={{ color: 'var(--line-2)' }}>·</span>
        <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}><span style={{ width: 8, height: 8, borderRadius: '50%', background: 'var(--bad)' }} /><strong>3 declined</strong> <span style={{ color: 'var(--ink-3)' }}>this month</span></span>
        <span style={{ color: 'var(--line-2)' }}>·</span>
        <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}><span style={{ width: 8, height: 8, borderRadius: '50%', background: 'var(--marigold)' }} /><strong>{fmtUSD0(totalOutstanding)}</strong> <span style={{ color: 'var(--ink-3)' }}>outstanding</span></span>
      </div>

      {/* ---- Corporate invoice history ---- */}
      <div className="section-label">Corporate invoice history</div>
      <InvoiceHistory rows={history} />

      <AssignModal
        open={!!assignFor}
        onClose={() => setAssignFor(null)}
        location={corpLoc}
        role={assignFor || 'default'}
        wallet={wallet}
        currentId={assignFor ? corpAssign[assignFor] : null}
        otherId={assignFor ? corpAssign[assignFor === 'default' ? 'backup' : 'default'] : null}
        onPick={(cardId) => setCorpAssign(a => ({ ...a, [assignFor]: cardId }))}
        onAddCard={addCardToWallet} />

      {msgTarget && <MessageOwnerModal target={msgTarget} onClose={() => setMsgTarget(null)} />}
    </div>
  );
}
window.CorporateBilling = CorporateBilling;
