/* ============================================================================
   MOWDEW — Billing kit (shared)
   Recurring billing data + the reusable components behind the billing flows:
   FreqSelector · BillDayStepper · NextInvoiceNote · TimeToBillCard ·
   DueRow / HeldRow · VisitTag (verified vs logged) · PdfInvoice (US Letter).
   Used by the owner desktop app, the owner mobile app, the design system page
   and the standalone PDF / email artifacts. Exposed on window.

   Load AFTER mowdew-kit.jsx (and mowdew-owner-data.jsx where CUSTOMERS is
   needed at render time — nothing here touches CUSTOMERS at load time).
   ========================================================================= */

/* ─── Business profile — what prints on the PDF ─────────────────────── */
const BIZPROFILE = {
  name: 'Marta Lawn & Pool',
  owner: 'Diego Marta',
  addr: '2210 Middlefield Rd',
  city: 'Palo Alto, CA 94301',
  phone: '(650) 555-0144',
  email: 'diego@martalawn.co',
  zelle: 'diego@martalawn.co',
  venmo: '@diego-marta',
  paypal: 'paypal.me/martalawn',
};

/* ─── Frequencies ───────────────────────────────────────────────────── */
const FREQS = ['Monthly', 'Every 3 months', 'Every 6 months', 'Yearly', 'Manual'];
const FREQ_SHORT = { 'Monthly': 'Monthly', 'Every 3 months': 'Every 3 mo', 'Every 6 months': 'Every 6 mo', 'Yearly': 'Yearly', 'Manual': 'Manual' };

/* Next-due preview for the billing card (prototype world: today = May 12, 2026) */
function nextDueLabel(freq, day) {
  if (freq === 'Manual') return null;
  if (freq === 'Monthly') return (day > 12 ? 'May' : 'Jun') + ` ${day}, 2026`;
  if (freq === 'Every 3 months') return `Aug ${day}, 2026`;
  if (freq === 'Every 6 months') return `Nov ${day}, 2026`;
  return `May ${day}, 2027`;
}

/* ─── The May 12 billing run ────────────────────────────────────────────
   Owner-confirmed, no cron: these are the invoices Mowdew has READY, built
   from recorded visits. Nothing sends until the owner taps the button.     */
const BILLRUN = {
  dateLabel: 'Tuesday, May 12',
  issued: 'May 12, 2026',
  dueDate: 'May 26, 2026',
  rows: [
    { cid: 'c2', inv: 'INV-0007', period: 'Apr 12 – May 12, 2026',
      visits: [
        { d: 'Apr 21', verified: true }, { d: 'Apr 28', verified: true },
        { d: 'May 5', verified: true }, { d: 'May 12', verified: true },
      ] },
    { cid: 'c4', inv: 'INV-0008', period: 'Feb 12 – May 12, 2026',
      visits: [
        { d: 'Feb 24', verified: true },
        { d: 'Mar 18', verified: false, by: 'Diego' },
        { d: 'Apr 12', verified: true },
      ] },
    { cid: 'c7', inv: 'INV-0009', period: 'Apr 12 – May 12, 2026',
      visits: [
        { d: 'Apr 14', verified: true }, { d: 'Apr 21', verified: true },
        { d: 'Apr 28', verified: true }, { d: 'May 5', verified: true },
      ] },
  ],
  held: ['c3', 'c8', 'c5', 'c6', 'c1'],
};
function runAmount(row, c) { return row.visits.length * c.price; }
/* one-shot run state for the prototype: once sent, the action item hides */
function runPending() { return window.__mowdewRunSent ? [] : BILLRUN.rows; }
function markRunSent() { window.__mowdewRunSent = true; }
function runTotal(customers) {
  return runPending().reduce((s, r) => { const c = customers.find(x => x.id === r.cid); return s + (c ? runAmount(r, c) : 0); }, 0);
}
function heldWhy(c) {
  return c.freq === 'Manual' ? 'Manual — you bill when ready' : `${c.freq} — next due ${c.nextDue}`;
}

/* ─── FreqSelector — "How often do you bill {Name}?" ─────────────────────
   compact=false: chip/segment grid (desktop). compact=true: tappable field
   that expands an inline list (mobile). Aqua = the billing accent.          */
function FreqSelector({ value, onChange, compact = false }) {
  const [open, setOpen] = React.useState(false);
  if (!compact) {
    return (
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 8 }}>
        {FREQS.map(f => {
          const on = value === f;
          return (
            <button key={f} onClick={() => onChange(f)}
              style={{ padding: '11px 6px', borderRadius: 11, cursor: 'pointer', fontFamily: 'var(--font-body)', fontSize: 12.5, fontWeight: 700, letterSpacing: '-0.01em',
                border: on ? '2px solid var(--aqua-500)' : '1.5px solid var(--ink-200)',
                background: on ? 'var(--aqua-50)' : '#fff', color: on ? 'var(--aqua-700)' : 'var(--ink-700)' }}>
              {FREQ_SHORT[f]}
            </button>
          );
        })}
      </div>
    );
  }
  return (
    <div>
      <button onClick={() => setOpen(o => !o)}
        style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 10, padding: '13px 14px', borderRadius: 'var(--radius-md)', border: open ? '2px solid var(--aqua-500)' : '1.5px solid var(--ink-200)', background: '#fff', cursor: 'pointer', textAlign: 'left' }}>
        <Icon name="history" size={18} style={{ color: 'var(--aqua-700)', flexShrink: 0 }} />
        <span style={{ flex: 1, fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 15, letterSpacing: '-0.01em', color: 'var(--ink-900)' }}>{value}</span>
        <Icon name="chevron-down" size={18} style={{ color: 'var(--ink-400,#A8A498)', transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .18s' }} />
      </button>
      {open && (
        <div className="card" style={{ marginTop: 8, overflow: 'hidden' }}>
          {FREQS.map((f, i) => {
            const on = value === f;
            return (
              <div key={f} onClick={() => { onChange(f); setOpen(false); }}
                style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', borderTop: i ? '1px solid var(--ink-100)' : 'none', cursor: 'pointer', background: on ? 'var(--aqua-50)' : '#fff' }}>
                <span style={{ flex: 1, fontWeight: 700, fontSize: 14, color: on ? 'var(--aqua-700)' : 'var(--ink-900)' }}>{f}</span>
                {on && <Icon name="check" size={16} strokeWidth={2.6} style={{ color: 'var(--aqua-700)' }} />}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

/* ─── BillDayStepper — "Bill on day N of the month" (1–28) ──────────── */
function BillDayStepper({ day, onChange }) {
  const Step = ({ dir, label }) => (
    <button onClick={() => onChange(Math.min(28, Math.max(1, day + dir)))} aria-label={label}
      style={{ width: 38, height: 38, borderRadius: 10, border: '1.5px solid var(--ink-200)', background: '#fff', color: 'var(--ink-700)', display: 'grid', placeItems: 'center', cursor: 'pointer', fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 18, lineHeight: 1, flexShrink: 0 }}>
      {dir < 0 ? '−' : '+'}
    </button>
  );
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
      <span style={{ fontSize: 13.5, color: 'var(--ink-700)', fontWeight: 600 }}>Bill on day</span>
      <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
        <Step dir={-1} label="Earlier day" />
        <span style={{ minWidth: 44, textAlign: 'center', fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 20, letterSpacing: '-0.02em', background: 'var(--paper)', border: '1px solid var(--ink-100)', borderRadius: 10, padding: '6px 8px' }}>{day}</span>
        <Step dir={1} label="Later day" />
      </span>
      <span style={{ fontSize: 13.5, color: 'var(--ink-700)', fontWeight: 600 }}>of the month</span>
    </div>
  );
}

/* ─── NextInvoiceNote — the calm aqua line ──────────────────────────── */
function NextInvoiceNote({ date }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, fontWeight: 700, color: 'var(--aqua-700)' }}>
      <Icon name="calendar" size={15} strokeWidth={2.3} />
      Next invoice {date}
    </div>
  );
}

/* ─── SaveButton — primary with a saved ✓ state ─────────────────────── */
function SaveButton({ label, savedLabel = 'Saved', onSave, block = false, style = {} }) {
  const [state, setState] = React.useState('idle'); // idle | saving | saved
  const click = () => {
    if (state !== 'idle') return;
    setState('saving');
    setTimeout(() => { setState('saved'); onSave && onSave(); setTimeout(() => setState('idle'), 2200); }, 550);
  };
  const saved = state === 'saved';
  return (
    <button onClick={click} className={(saved ? 'btn btn-secondary' : 'btn btn-primary') + (block ? ' btn-block' : '')}
      style={{ minHeight: 44, ...(saved ? { color: 'var(--green-700)', background: 'var(--green-50)', border: '1px solid var(--green-100)' } : {}), ...style }}>
      {state === 'saving'
        ? <span className="tn-pulse" style={{ display: 'inline-flex' }}><Icon name="clock" size={17} /></span>
        : <Icon name={saved ? 'check-circle' : 'check'} size={17} />}
      {state === 'saving' ? 'Saving…' : saved ? savedLabel : label}
    </button>
  );
}

/* ─── TimeToBillCard — the dashboard / money action item ─────────────────
   Aqua-tinted, calendar glyph. Renders nothing when count is 0 (zero state
   = hidden). compact = mobile width.                                        */
function TimeToBillCard({ count, total, onOpen, compact = false, style = {} }) {
  if (!count) return null;
  return (
    <button onClick={onOpen} data-comment-anchor="time-to-bill-card"
      style={{ width: '100%', display: 'flex', alignItems: 'center', gap: compact ? 12 : 14, padding: compact ? 14 : '15px 18px', borderRadius: 'var(--radius-xl)', border: '1px solid var(--aqua-100)', background: 'var(--aqua-50)', cursor: 'pointer', textAlign: 'left', boxShadow: 'var(--shadow-sm)', ...style }}>
      <span style={{ width: compact ? 40 : 44, height: compact ? 40 : 44, borderRadius: 12, background: 'var(--aqua-500)', display: 'grid', placeItems: 'center', color: '#fff', flexShrink: 0, boxShadow: 'var(--shadow-aqua)' }}>
        <Icon name="calendar" size={compact ? 20 : 22} strokeWidth={2.3} />
      </span>
      <span style={{ flex: 1, minWidth: 0 }}>
        <span style={{ display: 'block', fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: compact ? 14.5 : 15.5, letterSpacing: '-0.015em', color: 'var(--ink-900)' }}>
          Time to bill — {count} customer{count === 1 ? '' : 's'} due
        </span>
        <span style={{ display: 'block', fontSize: compact ? 12 : 12.5, color: 'var(--aqua-700)', fontWeight: 600, marginTop: 1 }}>
          Review who's being billed, then send them all.
        </span>
      </span>
      {total != null && <Amount cents={total} size={compact ? 15 : 17} />}
      <Icon name="chevron-right" size={18} style={{ color: 'var(--aqua-700)', flexShrink: 0 }} />
    </button>
  );
}

/* ─── Checkbox (run rows) ───────────────────────────────────────────── */
function RunCheck({ on }) {
  return (
    <span style={{ width: 24, height: 24, borderRadius: 8, border: on ? 'none' : '2px solid var(--ink-200)', background: on ? 'var(--aqua-700)' : 'transparent', display: 'grid', placeItems: 'center', color: '#fff', flexShrink: 0 }}>
      {on && <Icon name="check" size={15} strokeWidth={3} />}
    </span>
  );
}

/* ─── DueRow — selectable billing-run row ───────────────────────────── */
function DueRow({ c, row, checked, onToggle, first = false, compact = false }) {
  const amt = runAmount(row, c);
  return (
    <div onClick={onToggle}
      style={{ display: 'flex', alignItems: 'center', gap: 12, padding: compact ? '13px 14px' : '13px 18px', borderTop: first ? 'none' : '1px solid var(--ink-100)', cursor: 'pointer', opacity: checked ? 1 : 0.5, background: '#fff' }}>
      <RunCheck on={checked} />
      <Avatar name={c.name} size={compact ? 38 : 40} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontWeight: 700, fontSize: compact ? 14 : 14.5, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{c.name}</div>
        <div style={{ fontSize: 12, color: 'var(--ink-500)', marginTop: 1 }}>{c.freq} · due May 12</div>
      </div>
      <div style={{ textAlign: 'right', flexShrink: 0 }}>
        <Amount cents={amt} size={compact ? 15 : 16} />
        <div style={{ fontSize: 11, color: 'var(--ink-400,#A8A498)', marginTop: 1 }}>{row.visits.length} visits</div>
      </div>
    </div>
  );
}

/* ─── HeldRow — clearly shows WHY someone is skipped ────────────────── */
function HeldRow({ c, first = false, compact = false }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: compact ? '12px 14px' : '12px 18px', borderTop: first ? 'none' : '1px solid var(--ink-100)', background: '#fff' }}>
      <Avatar name={c.name} size={compact ? 34 : 36} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontWeight: 700, fontSize: compact ? 13.5 : 14, color: 'var(--ink-700)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{c.name}</div>
        <div style={{ fontSize: 12, color: 'var(--ink-500)', marginTop: 1 }}>{heldWhy(c)}</div>
      </div>
      <span style={{ fontFamily: 'var(--font-mono)', fontSize: 10, fontWeight: 600, letterSpacing: '.08em', color: 'var(--ink-500)', background: 'var(--ink-50)', border: '1px solid var(--ink-100)', borderRadius: 7, padding: '3px 8px', flexShrink: 0 }}>HELD</span>
    </div>
  );
}

/* ─── VisitTag — the verified / logged wording decision ─────────────────
   Verified (geofence check-in)  -> sky-blue shield "Verified on-site".
   Manually logged (bad GPS etc) -> neutral ink notes "Logged by {Pro}".
   Honest both ways: never implies GPS proof it doesn't have, never makes
   real work look second-class.                                             */
function VisitTag({ verified, by = 'Diego', size = 11.5 }) {
  if (verified) {
    return (
      <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: size, fontWeight: 700, color: 'var(--blue-700)', whiteSpace: 'nowrap' }}>
        <Icon name="shield" size={size} strokeWidth={2.4} />Verified on-site
      </span>
    );
  }
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: size, fontWeight: 600, color: 'var(--ink-500)', whiteSpace: 'nowrap' }}>
      <Icon name="notes" size={size} strokeWidth={2.2} />Logged by {by}
    </span>
  );
}

/* ─── Droplet mark — logo-less fallback for the PDF header ──────────── */
function DropletMark({ size = 54 }) {
  return (
    <span style={{ width: size, height: size, borderRadius: Math.round(size * 0.29), background: 'var(--aqua-50)', border: '1px solid var(--aqua-100)', display: 'grid', placeItems: 'center', color: 'var(--aqua-700)', flexShrink: 0 }}>
      <Icon name="droplet" size={Math.round(size * 0.5)} strokeWidth={2.2} />
    </span>
  );
}

/* ─── PdfInvoice — the printable artifact (US Letter, 816×1056 @96dpi) ───
   inv: { inv, period, issued, due, customer:{name,addr,city}, lines:[{d,
   label, verified, by, cents}], total }. logoless toggles the fallback.    */
function PdfInvoice({ inv, biz = BIZPROFILE, logoless = false }) {
  const mono = { fontFamily: 'var(--font-mono)', fontSize: 9.5, fontWeight: 600, letterSpacing: '.12em', color: 'var(--ink-500)', textTransform: 'uppercase' };
  const hasLogged = inv.lines.some(l => !l.verified);
  return (
    <div className="tn" style={{ width: 816, height: 1056, background: '#fff', color: 'var(--ink-900)', display: 'flex', flexDirection: 'column', padding: '58px 64px 44px', boxSizing: 'border-box', fontFamily: 'var(--font-body)' }}>
      {/* header */}
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 18 }}>
        {logoless ? <DropletMark size={54} /> : <Logo size={54} />}
        <div style={{ flex: 1 }}>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 25, letterSpacing: '-0.025em', lineHeight: 1.1 }}>{biz.name}</div>
          <div style={{ fontSize: 12.5, color: 'var(--ink-500)', marginTop: 5, lineHeight: 1.55 }}>
            {biz.addr} · {biz.city}<br />{biz.phone} · {biz.email}
          </div>
        </div>
        <div style={{ textAlign: 'right' }}>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 34, letterSpacing: '-0.02em', lineHeight: 1 }}>INVOICE</div>
          <div style={{ fontFamily: 'var(--font-mono)', fontSize: 13, fontWeight: 600, letterSpacing: '.08em', color: 'var(--aqua-700)', marginTop: 7 }}>{inv.inv}</div>
          <div style={{ fontSize: 12, color: 'var(--ink-500)', marginTop: 7, lineHeight: 1.6 }}>
            Issued {inv.issued}<br /><b style={{ color: 'var(--ink-900)' }}>Due {inv.due}</b>
          </div>
        </div>
      </div>

      {/* bill-to + period */}
      <div style={{ display: 'flex', gap: 28, marginTop: 34, paddingTop: 22, borderTop: '2px solid var(--ink-900)' }}>
        <div style={{ flex: 1 }}>
          <div style={mono}>Bill to</div>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 17, marginTop: 6 }}>{inv.customer.name}</div>
          <div style={{ fontSize: 13, color: 'var(--ink-700)', marginTop: 2 }}>{inv.customer.addr} · {inv.customer.city}</div>
        </div>
        <div style={{ textAlign: 'right' }}>
          <div style={mono}>Service period</div>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 15, marginTop: 6 }}>{inv.period}</div>
          <div style={{ fontSize: 12.5, color: 'var(--ink-500)', marginTop: 2 }}>{inv.lines.length} visits, every one on the record</div>
        </div>
      </div>

      {/* line items */}
      <div style={{ marginTop: 28 }}>
        <div style={{ display: 'grid', gridTemplateColumns: '92px 1fr 170px 110px', gap: 12, padding: '0 0 9px', borderBottom: '1.5px solid var(--ink-200)' }}>
          <span style={mono}>Date</span><span style={mono}>Service</span><span style={mono}>Record</span><span style={{ ...mono, textAlign: 'right' }}>Amount</span>
        </div>
        {inv.lines.map((l, i) => (
          <div key={i} style={{ display: 'grid', gridTemplateColumns: '92px 1fr 170px 110px', gap: 12, alignItems: 'center', padding: '12px 0', borderBottom: '1px solid var(--ink-100)' }}>
            <span style={{ fontFamily: 'var(--font-mono)', fontSize: 12, fontWeight: 600, color: 'var(--ink-700)' }}>{l.d}</span>
            <span style={{ fontSize: 13.5, fontWeight: 700 }}>{l.label}</span>
            <span>{l.verified
              ? <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 12, fontWeight: 700, color: 'var(--blue-700)', whiteSpace: 'nowrap' }}><Icon name="shield" size={12.5} strokeWidth={2.4} />Verified on-site</span>
              : <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 12, fontWeight: 600, color: 'var(--ink-500)', whiteSpace: 'nowrap' }}><Icon name="notes" size={12.5} strokeWidth={2.2} />Logged by {l.by || 'your pro'}</span>}
            </span>
            <span style={{ textAlign: 'right', fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 14, fontVariantNumeric: 'tabular-nums' }}>{fmtMoney(l.cents)}</span>
          </div>
        ))}
        {/* total */}
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'flex-end', gap: 16, padding: '16px 0 0' }}>
          <span style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 16, letterSpacing: '-0.01em' }}>Total due</span>
          <span style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 30, letterSpacing: '-0.03em', fontVariantNumeric: 'tabular-nums' }}>{fmtMoney(inv.total)}</span>
        </div>
      </div>

      {/* how to pay */}
      <div style={{ marginTop: 26, border: '1.5px solid var(--ink-200)', borderRadius: 16, padding: '18px 22px' }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 12 }}>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 15, letterSpacing: '-0.01em' }}>How to pay</div>
          <div style={{ fontSize: 12, color: 'var(--ink-500)' }}>Any of these reaches {biz.owner.split(' ')[0]} directly — please pay by {inv.due}.</div>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14, marginTop: 14 }}>
          {[['zelle', biz.zelle], ['venmo', biz.venmo], ['paypal', biz.paypal]].map(([rail, handle]) => (
            <div key={rail} style={{ display: 'flex', alignItems: 'center', gap: 10, background: 'var(--paper)', border: '1px solid var(--ink-100)', borderRadius: 12, padding: '10px 12px' }}>
              <RailChip rail={rail} size={30} />
              <div style={{ minWidth: 0 }}>
                <div style={{ fontWeight: 700, fontSize: 12.5 }}>{RAIL[rail].label}</div>
                <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10.5, color: 'var(--ink-500)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{handle}</div>
              </div>
            </div>
          ))}
        </div>
        <div style={{ fontSize: 11.5, color: 'var(--ink-500)', marginTop: 12 }}>
          Payments go straight to {biz.name} — Mowdew never touches the money.
        </div>
      </div>

      {/* verification footnote */}
      <div style={{ marginTop: 18, fontSize: 11, color: 'var(--ink-500)', lineHeight: 1.6 }}>
        <b style={{ color: 'var(--blue-700)' }}>Verified on-site</b> — confirmed by an automatic location check-in at your property.
        {hasLogged && <> · <b style={{ color: 'var(--ink-700)' }}>Logged</b> — recorded by your pro when GPS couldn't confirm. Still real work, still on the record.</>}
      </div>

      {/* footer */}
      <div style={{ marginTop: 'auto', paddingTop: 18, borderTop: '1px solid var(--ink-100)', display: 'flex', alignItems: 'center', gap: 10 }}>
        {logoless ? <DropletMark size={26} /> : <Logo size={26} />}
        <span style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 13, letterSpacing: '-0.01em' }}>Every visit, on the record.</span>
        <span style={{ marginLeft: 'auto', fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.08em', color: 'var(--ink-300)' }}>SENT WITH MOWDEW · MOWDEW.COM</span>
      </div>
    </div>
  );
}

/* Build a PdfInvoice model from a billing-run row + a CUSTOMERS record */
function invModelFromRun(row, c) {
  return {
    inv: row.inv, period: row.period, issued: BILLRUN.issued, due: BILLRUN.dueDate,
    customer: { name: c.name, addr: c.addr, city: c.city },
    lines: row.visits.map(v => ({ d: v.d, label: c.svc, verified: v.verified, by: v.by, cents: c.price })),
    total: runAmount(row, c),
  };
}

Object.assign(window, {
  BIZPROFILE, FREQS, FREQ_SHORT, nextDueLabel, BILLRUN, runAmount, heldWhy,
  runPending, markRunSent, runTotal,
  FreqSelector, BillDayStepper, NextInvoiceNote, SaveButton, TimeToBillCard,
  RunCheck, DueRow, HeldRow, VisitTag, DropletMark, PdfInvoice, invModelFromRun,
});
