// cal-apply.jsx — Multi-step apply flow with 3/4/5 step density variants

const { useState: aUS, useEffect: aUE, useMemo: aUM, useRef: aUR } = React;

// ─────────────────────────────────────────────────────────────
// Form state hook — persists to localStorage
// ─────────────────────────────────────────────────────────────
const LS_KEY = "cal_apply_v1";
const SEED_KEY = "cal_apply_seed";
const STEP_KEY = "cal_apply_step";

const EMPTY = {
  // Contact
  first: "", last: "", phone: "", email: "", dob: "", postal: "",
  // Vehicle
  vehiclePrice: "", buyTiming: "", vehicleType: "",
  // Employment
  empStatus: "", empLength: "", monthlyIncome: "", employer: "",
  // Credit
  creditScore: "", bankruptcy: "", cosigner: "", tradeIn: "",
  // Consent — granular, both required for submission per PIPEDA
  creditConsent: false,
  contactConsent: false,
};

// Consent text versioning — bump this string whenever the consent wording changes.
// Captured at submission time alongside timestamp + user agent + IP (server-side).
const CONSENT_VERSION = "v2.1-2026-06-02";

const CONSENT_TEXT = {
  credit: "I authorize Canadian Auto Loan to obtain my credit report from Equifax Canada and TransUnion Canada for the purpose of matching me with lender(s) for vehicle financing. I understand the credit check is performed only after Canadian Auto Loan has spoken with me and I have given verbal approval to proceed, and that I may withdraw this consent at any time before the check is performed by emailing info@canadianautoloan.net.",
  contact: "I agree to be contacted by Canadian Auto Loan by phone, email, or text message about my application. I understand I may withdraw this consent at any time by emailing info@canadianautoloan.net.",
};

function useApplyState() {
  const [data, setData] = aUS(() => {
    try {
      const raw = localStorage.getItem(LS_KEY);
      const seed = localStorage.getItem(SEED_KEY);
      const base = { ...EMPTY, ...(raw ? JSON.parse(raw) : {}) };
      if (seed) {
        const s = JSON.parse(seed);
        // mini-app hero seed
        if (s.first) base.first = base.first || s.first;
        if (s.last) base.last = base.last || s.last;
        if (s.postal) base.postal = base.postal || s.postal;
        // quiz seed
        if (s.credit) base.creditScore = base.creditScore || mapQuizCredit(s.credit);
        if (s.income) base.monthlyIncome = base.monthlyIncome || s.income;
        if (s.purpose) base.buyTiming = base.buyTiming || s.purpose;
        localStorage.removeItem(SEED_KEY);
      }
      return base;
    } catch { return EMPTY; }
  });
  aUE(() => {
    try { localStorage.setItem(LS_KEY, JSON.stringify(data)); } catch (_) {}
  }, [data]);
  const update = (patch) => setData((d) => ({ ...d, ...patch }));
  const reset = () => {
    setData(EMPTY);
    try {
      localStorage.removeItem(LS_KEY);
      localStorage.removeItem(STEP_KEY);
      localStorage.removeItem("cal_apply_submission_v1");
    } catch (_) {}
  };
  return [data, update, reset];
}

function mapQuizCredit(q) {
  return ({excellent:"excellent",good:"good",fair:"fair",building:"building",wip:"wip",idk:""})[q] || "";
}

// ─────────────────────────────────────────────────────────────
// Validators per step. Return {} if valid, {fieldKey: errorMsg} if not.
// ─────────────────────────────────────────────────────────────
const CA_POSTAL_RE = /^[A-CEGHJ-NPRSTVXYa-ceghj-npr-tvxy]\d[A-Za-z][\s-]?\d[A-Za-z]\d$/;
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;

function validateContact(d) {
  const e = {};
  if (!d.first?.trim()) e.first = "Please enter your first name.";
  if (!d.last?.trim()) e.last = "Please enter your last name.";

  if (!d.phone?.trim()) e.phone = "Please enter a phone number we can reach you at.";
  else {
    const digits = d.phone.replace(/\D/g, "");
    if (digits.length < 10 || digits.length > 11) {
      e.phone = "Please enter a 10-digit Canadian phone number.";
    }
  }

  if (!d.email?.trim()) e.email = "Please enter your email address.";
  else if (!EMAIL_RE.test(d.email.trim())) e.email = "That doesn't look like a valid email address.";

  if (!d.dob?.trim()) e.dob = "Please enter your date of birth.";
  else {
    const m = d.dob.match(/^(\d{4})-(\d{2})-(\d{2})$/);
    if (!m) e.dob = "Use a real date (yyyy-mm-dd).";
    else {
      const [, ys, ms, ds] = m;
      const y = +ys, mo = +ms, da = +ds;
      const date = new Date(y, mo - 1, da);
      if (date.getFullYear() !== y || date.getMonth() !== mo - 1 || date.getDate() !== da) {
        e.dob = "That date doesn't exist. Double-check the day and month.";
      } else {
        const now = new Date();
        let age = now.getFullYear() - y;
        const beforeBirthday = now.getMonth() < mo - 1 || (now.getMonth() === mo - 1 && now.getDate() < da);
        if (beforeBirthday) age--;
        if (date > now) e.dob = "Date of birth can't be in the future.";
        else if (age < 18) e.dob = "You must be 18 or older to apply.";
        else if (age > 110) e.dob = "That doesn't look right — please re-check.";
      }
    }
  }

  if (!d.postal?.trim()) e.postal = "Please enter your postal code.";
  else if (!CA_POSTAL_RE.test(d.postal.trim())) {
    e.postal = "Use a Canadian postal code, e.g. K1Z 7S8.";
  }
  return e;
}

function validateVehicle(d) {
  const e = {};
  if (!d.vehiclePrice) e.vehiclePrice = "Please pick a price range.";
  if (!d.buyTiming) e.buyTiming = "Please pick a timeline.";
  return e;
}

function validateEmployment(d) {
  const e = {};
  if (!d.empStatus) e.empStatus = "Please pick your current employment status.";
  if (!d.empLength) e.empLength = "Please pick how long you've been employed.";
  if (!d.monthlyIncome) e.monthlyIncome = "Please pick a monthly income range.";
  return e;
}

function validateCredit(d) {
  const e = {};
  if (!d.creditScore) e.creditScore = "Please pick your best-guess credit range.";
  if (!d.bankruptcy) e.bankruptcy = "Please answer the bankruptcy question.";
  return e;
}

function validateConsent(d) {
  const e = {};
  if (!d.creditConsent) e.creditConsent = "Please authorize the credit check to continue.";
  if (!d.contactConsent) e.contactConsent = "Please authorize contact to continue.";
  return e;
}

function validateStep(stepKey, d) {
  switch (stepKey) {
    case "contact": return validateContact(d);
    case "vehicle": return validateVehicle(d);
    case "employment": return validateEmployment(d);
    case "credit": return validateCredit(d);
    case "money": return { ...validateEmployment(d), ...validateCredit(d) };
    case "loan": return { ...validateVehicle(d), ...validateEmployment(d), ...validateCredit(d) };
    case "consent": return validateConsent(d);
    default: return {};
  }
}

// ─────────────────────────────────────────────────────────────
// CalDatePicker — branded date picker. Value is "yyyy-mm-dd" string.
// Trigger renders as a CalSelect-style button; popover has a year+month
// header (with quick year-decade jump for DOB use case), weekday row,
// and a 6-row day grid. min/max enforce bounds.
// ─────────────────────────────────────────────────────────────
const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
const WEEKDAYS = ["S","M","T","W","T","F","S"];

function parseISODate(s) {
  if (!s) return null;
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
  if (!m) return null;
  const d = new Date(+m[1], +m[2] - 1, +m[3]);
  if (d.getFullYear() !== +m[1] || d.getMonth() !== +m[2] - 1 || d.getDate() !== +m[3]) return null;
  return d;
}
function toISO(d) {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const da = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${da}`;
}
function fmtLong(d) {
  return d.toLocaleDateString("en-CA", { year: "numeric", month: "long", day: "numeric" });
}

function CalDatePicker({ value, onChange, min, max, placeholder = "Select date" }) {
  const minDate = aUM(() => parseISODate(min), [min]);
  const maxDate = aUM(() => parseISODate(max), [max]);
  const selected = aUM(() => parseISODate(value), [value]);
  const [open, setOpen] = aUS(false);
  // What month/year the calendar is currently showing
  const initView = selected || maxDate || new Date();
  const [viewYear, setViewYear] = aUS(initView.getFullYear());
  const [viewMonth, setViewMonth] = aUS(initView.getMonth());
  const [yearPick, setYearPick] = aUS(false);
  const [drop, setDrop] = aUS("down"); // "up" | "down" — direction the popover opens
  const rootRef = aUR(null);
  const btnRef = aUR(null);

  // When opening, snap view to the current selection (or max if no selection)
  aUE(() => {
    if (open) {
      const view = selected || maxDate || new Date();
      setViewYear(view.getFullYear());
      setViewMonth(view.getMonth());
      setYearPick(false);
      // Decide direction: open upward if not enough room below.
      const POP_H = 380;
      const r = btnRef.current?.getBoundingClientRect();
      if (r) {
        const spaceBelow = window.innerHeight - r.bottom;
        const spaceAbove = r.top;
        setDrop(spaceBelow < POP_H && spaceAbove > spaceBelow ? "up" : "down");
      }
    }
  }, [open]);

  // Outside click + Esc to close
  aUE(() => {
    if (!open) return;
    const onDoc = (e) => { if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === "Escape") { setOpen(false); btnRef.current?.focus(); } };
    document.addEventListener("mousedown", onDoc);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("mousedown", onDoc);
      document.removeEventListener("keydown", onKey);
    };
  }, [open]);

  // Build the day grid for viewYear/viewMonth
  const grid = aUM(() => {
    const first = new Date(viewYear, viewMonth, 1);
    const startDay = first.getDay(); // 0 = Sunday
    const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
    const cells = [];
    // Lead-in from previous month
    for (let i = 0; i < startDay; i++) {
      const d = new Date(viewYear, viewMonth, -(startDay - 1 - i));
      cells.push({ date: d, inMonth: false });
    }
    for (let i = 1; i <= daysInMonth; i++) {
      cells.push({ date: new Date(viewYear, viewMonth, i), inMonth: true });
    }
    // Tail-out to fill 6 rows (42 cells)
    while (cells.length < 42) {
      const last = cells[cells.length - 1].date;
      cells.push({ date: new Date(last.getFullYear(), last.getMonth(), last.getDate() + 1), inMonth: false });
    }
    return cells;
  }, [viewYear, viewMonth]);

  const isDisabled = (d) => {
    if (minDate && d < minDate) return true;
    if (maxDate && d > maxDate) return true;
    return false;
  };
  const isSameDay = (a, b) => a && b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
  const today = new Date();

  const stepMonth = (delta) => {
    let y = viewYear, m = viewMonth + delta;
    if (m < 0) { m = 11; y--; }
    if (m > 11) { m = 0; y++; }
    setViewYear(y); setViewMonth(m);
  };

  // Years to show in the year-pick grid: 12 around current view, clamped to min/max
  const yearGrid = aUM(() => {
    const start = Math.floor(viewYear / 12) * 12;
    return Array.from({ length: 12 }, (_, i) => start + i);
  }, [viewYear]);
  const yearAllowed = (y) => {
    if (minDate && y < minDate.getFullYear()) return false;
    if (maxDate && y > maxDate.getFullYear()) return false;
    return true;
  };

  return (
    <div ref={rootRef} className="cal-date" data-open={open || undefined} data-drop={drop}>
      <button
        ref={btnRef}
        type="button"
        className="cal-select-trigger cal-date-trigger"
        aria-haspopup="dialog"
        aria-expanded={open}
        onClick={() => setOpen(o => !o)}
      >
        <Icon name="clock" size={16} stroke={1.8} style={{color:"var(--text-soft)",flex:"0 0 16px"}}/>
        <span className={`cal-select-value tnum ${selected ? "" : "is-placeholder"}`}>
          {selected ? toISO(selected) : placeholder}
        </span>
        {selected && (
          <span className="cal-date-long">{fmtLong(selected)}</span>
        )}
        <Icon name="chevronDown" size={16} className="cal-select-chevron" />
      </button>

      {open && (
        <div className="cal-date-pop" role="dialog" aria-label="Choose a date">
          <div className="cal-date-head">
            <button type="button" className="cal-date-nav" onClick={() => yearPick ? setViewYear(y => y - 12) : stepMonth(-1)} aria-label={yearPick ? "Previous decade" : "Previous month"}>
              <Icon name="arrowLeft" size={14}/>
            </button>
            <button type="button" className="cal-date-pivot" onClick={() => setYearPick(p => !p)} aria-label="Switch year">
              <span className="cal-date-mo">{yearPick ? `${yearGrid[0]} – ${yearGrid[11]}` : MONTHS[viewMonth]}</span>
              {!yearPick && <span className="cal-date-yr tnum">{viewYear}</span>}
              <Icon name="chevronDown" size={12} style={{
                transform: yearPick ? "rotate(180deg)" : "none",
                transition: "transform .2s ease",
                color: "var(--text-soft)",
              }}/>
            </button>
            <button type="button" className="cal-date-nav" onClick={() => yearPick ? setViewYear(y => y + 12) : stepMonth(1)} aria-label={yearPick ? "Next decade" : "Next month"}>
              <Icon name="arrowRight" size={14}/>
            </button>
          </div>

          {yearPick ? (
            <div className="cal-date-years">
              {yearGrid.map(y => {
                const disabled = !yearAllowed(y);
                const isCurrent = y === viewYear;
                return (
                  <button
                    key={y}
                    type="button"
                    className="cal-date-year tnum"
                    data-current={isCurrent || undefined}
                    disabled={disabled}
                    onClick={() => { setViewYear(y); setYearPick(false); }}
                  >{y}</button>
                );
              })}
            </div>
          ) : (
            <>
              <div className="cal-date-weekdays">
                {WEEKDAYS.map((d, i) => <div key={i}>{d}</div>)}
              </div>
              <div className="cal-date-grid">
                {grid.map(({date, inMonth}, i) => {
                  const disabled = isDisabled(date);
                  const isSel = isSameDay(date, selected);
                  const isToday = isSameDay(date, today);
                  return (
                    <button
                      key={i}
                      type="button"
                      className="cal-date-cell tnum"
                      data-out={!inMonth || undefined}
                      data-today={isToday || undefined}
                      data-selected={isSel || undefined}
                      disabled={disabled}
                      onClick={() => { onChange(toISO(date)); setOpen(false); btnRef.current?.focus(); }}
                    >
                      {date.getDate()}
                    </button>
                  );
                })}
              </div>
              <div className="cal-date-foot">
                <button type="button" className="cal-date-quick" onClick={() => {
                  const d = new Date();
                  if (maxDate && d > maxDate) return;
                  if (minDate && d < minDate) return;
                  onChange(toISO(d)); setOpen(false); btnRef.current?.focus();
                }}>
                  Today
                </button>
                <button type="button" className="cal-date-quick cal-date-quick-clear" onClick={() => { onChange(""); setOpen(false); btnRef.current?.focus(); }}>
                  Clear
                </button>
              </div>
            </>
          )}
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Step definitions per density
// ─────────────────────────────────────────────────────────────
const STEPS_5 = [
  { key: "contact", label: "Contact" },
  { key: "vehicle", label: "Vehicle" },
  { key: "employment", label: "Employment" },
  { key: "credit", label: "Credit" },
  { key: "consent", label: "Review & consent" },
];
const STEPS_4 = [
  { key: "contact", label: "Contact" },
  { key: "vehicle", label: "Vehicle" },
  { key: "money", label: "Income & credit" },
  { key: "consent", label: "Review & consent" },
];
const STEPS_3 = [
  { key: "contact", label: "About you" },
  { key: "loan", label: "Your loan" },
  { key: "consent", label: "Review & consent" },
];

function getSteps(density) {
  if (density === 3) return STEPS_3;
  if (density === 4) return STEPS_4;
  return STEPS_5;
}

// ─────────────────────────────────────────────────────────────
// Field helpers — reusable controls
// ─────────────────────────────────────────────────────────────
function Field({ label, required, hint, error, children, htmlFor, labelExtra, fieldKey }) {
  return (
    <div className="field" data-field={fieldKey || undefined} data-has-error={error ? true : undefined}>
      <div className="field-label-row">
        <label className="field-label" htmlFor={htmlFor}>
          {label} {required && <span className="req">required</span>}
        </label>
        {labelExtra}
      </div>
      {children}
      {error
        ? <div className="field-error" role="alert"><Icon name="info" size={12} stroke={2}/>{error}</div>
        : hint ? <div className="field-hint">{hint}</div> : null}
    </div>
  );
}

function RadioGroup({ value, onChange, options, columns = 1 }) {
  const cls = columns === 2 ? "choice-grid choice-grid-2" : columns === 3 ? "choice-grid choice-grid-3" : "choice-grid";
  return (
    <div className={cls}>
      {options.map((o) => {
        const v = typeof o === "string" ? o : o.value;
        const t = typeof o === "string" ? o : o.label;
        const d = typeof o === "string" ? null : o.desc;
        const suf = typeof o === "string" ? null : o.suffix;
        return (
          <button key={v} type="button"
            className={`choice ${value === v ? "selected" : ""}`}
            onClick={() => onChange(v)}
          >
            <span className="choice-radio" />
            <span className="choice-body">
              <span className="choice-title">{t}</span>
              {d && <span className="choice-desc">{d}</span>}
            </span>
            {suf && <span className="choice-suffix tnum">{suf}</span>}
          </button>
        );
      })}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Tooltip
// ─────────────────────────────────────────────────────────────
function WhyTooltip({ text }) {
  const [open, setOpen] = aUS(false);
  const ref = aUR(null);
  const btnRef = aUR(null);
  aUE(() => {
    if (!open) return;
    const onClick = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    document.addEventListener("mousedown", onClick);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("mousedown", onClick);
      document.removeEventListener("keydown", onKey);
    };
  }, [open]);
  // When the popup closes, release focus so :focus state doesn't visually persist
  aUE(() => {
    if (!open && btnRef.current && document.activeElement === btnRef.current) {
      btnRef.current.blur();
    }
  }, [open]);
  return (
    <span ref={ref} className="why-tip-wrap">
      <button
        ref={btnRef}
        type="button"
        className="why-tip-btn"
        onClick={(e) => { e.stopPropagation(); e.preventDefault(); setOpen(o => !o); }}
        aria-haspopup="dialog"
        aria-expanded={open}
      >
        <Icon name="help" size={13} stroke={2} />
        <span>Why we ask</span>
      </button>
      {open && (
        <div className="why-tip-pop anim-fadeup" role="dialog">
          <p style={{margin:0}}>{text}</p>
          <button type="button" className="why-tip-close" onClick={() => setOpen(false)}>Got it</button>
        </div>
      )}
      <style>{`
        .why-tip-wrap {
          position: relative; display: inline-flex;
          flex: 0 0 auto;
        }
        .why-tip-btn {
          appearance: none; border: 1px solid transparent; background: transparent;
          color: var(--text-soft); font-size: 12.5px; font-weight: 500;
          display: inline-flex; align-items: center; gap: 5px;
          cursor: pointer; padding: 4px 8px; border-radius: 6px;
          transition: color .15s ease, background .15s ease, border-color .15s ease;
          white-space: nowrap;
        }
        .why-tip-btn:hover { color: var(--primary); background: var(--primary-tint); }
        .why-tip-btn:focus { outline: none; }
        .why-tip-btn:focus-visible { border-color: var(--border-focus); box-shadow: var(--ring); }
        .why-tip-btn[aria-expanded="true"] { color: var(--primary); background: var(--primary-tint); }
        .why-tip-pop {
          position: absolute; top: 100%; right: 0;
          background: var(--text); color: white;
          padding: 14px 16px;
          border-radius: 10px;
          font-size: 13.5px; line-height: 1.5;
          width: 300px; z-index: 30;
          box-shadow: var(--shadow-lg);
          margin-top: 8px;
        }
        .why-tip-pop::before {
          content: ""; position: absolute; top: -5px; right: 18px;
          width: 10px; height: 10px; background: var(--text);
          transform: rotate(45deg);
        }
        .why-tip-close {
          margin-top: 10px; padding: 5px 10px;
          background: rgba(255,255,255,.12); border: 0; border-radius: 6px;
          color: white; font-size: 12.5px; font-weight: 500;
          cursor: pointer;
        }
        .why-tip-close:hover { background: rgba(255,255,255,.22); }
      `}</style>
    </span>
  );
}

// ─────────────────────────────────────────────────────────────
// Step renderers (individual blocks composed into steps)
// ─────────────────────────────────────────────────────────────

function ContactBlock({ data, update, errors = {} }) {
  return (
    <div className="stack gap-6">
      <div>
        <div className="h4" style={{marginBottom: 14}}>Name</div>
        <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:12}}>
          <Field label="First name" required fieldKey="first" error={errors.first}>
            <input className="input" value={data.first} onChange={e=>update({first:e.target.value})} placeholder="Maya" autoComplete="given-name"/>
          </Field>
          <Field label="Last name" required fieldKey="last" error={errors.last}>
            <input className="input" value={data.last} onChange={e=>update({last:e.target.value})} placeholder="Tremblay" autoComplete="family-name"/>
          </Field>
        </div>
      </div>

      <Field label="Phone" required fieldKey="phone" error={errors.phone} hint="10-digit Canadian number we can text or call you at.">
        <input className="input tnum" value={data.phone} onChange={e=>update({phone:e.target.value})} placeholder="(613) 555-0142" inputMode="tel" autoComplete="tel" type="tel"/>
      </Field>

      <Field label="Email" required fieldKey="email" error={errors.email}>
        <input className="input" type="email" value={data.email} onChange={e=>update({email:e.target.value})} placeholder="maya@example.com" autoComplete="email" inputMode="email" autoCapitalize="off" spellCheck={false} />
      </Field>

      <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:12}} className="contact-bottom-grid">
        <Field label="Date of birth" required fieldKey="dob" error={errors.dob} hint="You must be 18 or older to apply.">
          <CalDatePicker
            value={data.dob}
            onChange={(v) => update({dob: v})}
            min="1915-01-01"
            max={new Date().toISOString().slice(0,10)}
            placeholder="yyyy-mm-dd"
          />
        </Field>
        <Field label="Postal code" required fieldKey="postal" error={errors.postal} hint="Ontario or Quebec only at this time.">
          <input
            className="input tnum"
            value={data.postal}
            onChange={e=>update({postal:e.target.value.toUpperCase()})}
            placeholder="K1Z 7S8"
            maxLength={7}
            autoComplete="postal-code"
            inputMode="text"
            autoCapitalize="characters"
            spellCheck={false}
          />
        </Field>
      </div>
      <style>{`
        @media (max-width: 520px) { .contact-bottom-grid { grid-template-columns: 1fr !important; } }
      `}</style>
    </div>
  );
}

function VehicleBlock({ data, update, errors = {} }) {
  return (
    <div className="stack gap-6">
      <Field label="Vehicle price range" required fieldKey="vehiclePrice" error={errors.vehiclePrice}>
        <RadioGroup
          value={data.vehiclePrice}
          onChange={(v)=>update({vehiclePrice:v})}
          columns={2}
          options={[
            { value:"5-15", label:"$5,000 – $15,000" },
            { value:"15-25", label:"$15,000 – $25,000" },
            { value:"25-35", label:"$25,000 – $35,000" },
            { value:"35-50", label:"$35,000 – $50,000" },
            { value:"50+", label:"$50,000+" },
            { value:"flex", label:"I'm flexible" },
          ]}
        />
      </Field>

      <Field label="When are you planning to buy?" required fieldKey="buyTiming" error={errors.buyTiming}>
        <RadioGroup
          value={data.buyTiming}
          onChange={(v)=>update({buyTiming:v})}
          columns={2}
          options={[
            { value:"This week", label:"This week", desc:"As fast as possible" },
            { value:"This month", label:"This month" },
            { value:"Next 1–3 months", label:"Next 1–3 months" },
            { value:"Just exploring", label:"Just exploring" },
          ]}
        />
      </Field>

      <Field label="Vehicle type preference" hint="Optional. We can help with whatever fits your needs.">
        <RadioGroup
          value={data.vehicleType}
          onChange={(v)=>update({vehicleType:v})}
          columns={2}
          options={[
            { value:"sedan", label:"Sedan / coupe" },
            { value:"suv", label:"SUV / crossover" },
            { value:"truck", label:"Pickup truck" },
            { value:"any", label:"No preference" },
          ]}
        />
      </Field>
    </div>
  );
}

function EmploymentBlock({ data, update, errors = {} }) {
  return (
    <div className="stack gap-6">
      <Field label="Current employment status" required fieldKey="empStatus" error={errors.empStatus}>
        <RadioGroup
          value={data.empStatus}
          onChange={(v)=>update({empStatus:v})}
          columns={2}
          options={[
            { value:"ft", label:"Full-time employed" },
            { value:"pt", label:"Part-time employed" },
            { value:"selfemp", label:"Self-employed" },
            { value:"contract", label:"Contract / gig" },
            { value:"retired", label:"Retired / pension" },
            { value:"other", label:"Other" },
          ]}
        />
      </Field>

      <Field label="Length of employment" required fieldKey="empLength" error={errors.empLength}>
        <RadioGroup
          value={data.empLength}
          onChange={(v)=>update({empLength:v})}
          columns={2}
          options={[
            { value:"<6", label:"Less than 6 months" },
            { value:"6-24", label:"6 months – 2 years" },
            { value:"2-5", label:"2 – 5 years" },
            { value:"5+", label:"5+ years" },
          ]}
        />
      </Field>

      <Field label="Monthly income (after taxes)" required fieldKey="monthlyIncome" error={errors.monthlyIncome}>
        <CalSelect
          value={data.monthlyIncome}
          onChange={(v) => update({monthlyIncome: v})}
          placeholder="Select a range"
          options={[
            "Under $2,000",
            "$2,000 – $3,500",
            "$3,500 – $5,000",
            "$5,000 – $7,500",
            "$7,500 – $10,000",
            "Over $10,000",
          ]}
        />
      </Field>

      <Field label="Employer name" hint="Optional. We use this to confirm employment if your file moves forward.">
        <input className="input" value={data.employer} onChange={e=>update({employer:e.target.value})} placeholder="e.g. City of Ottawa"/>
      </Field>
    </div>
  );
}

function CreditBlock({ data, update, errors = {} }) {
  return (
    <div className="stack gap-6">
      <Field label="Estimated credit score" required fieldKey="creditScore" error={errors.creditScore} hint="Best guess is fine. We only run a credit check after we've spoken with you.">
        <RadioGroup
          value={data.creditScore}
          onChange={(v)=>update({creditScore:v})}
          options={[
            { value:"excellent", label:"Excellent", suffix:"720+" },
            { value:"good", label:"Good", suffix:"680–719" },
            { value:"fair", label:"Fair", suffix:"620–679" },
            { value:"building", label:"Building", suffix:"580–619" },
            { value:"wip", label:"Work-in-progress", suffix:"579 & under" },
            { value:"unknown", label:"I don't know", suffix:"That's OK" },
          ]}
        />
      </Field>

      <Field
        label="Have you had a bankruptcy or consumer proposal?"
        labelExtra={<WhyTooltip text="Lenders ask this; answering honestly helps us match you to lenders who say yes. We do not share this beyond the lender(s) reviewing your application." />}
        required
        fieldKey="bankruptcy"
        error={errors.bankruptcy}
      >
        <RadioGroup
          value={data.bankruptcy}
          onChange={(v)=>update({bankruptcy:v})}
          columns={3}
          options={[
            { value:"never", label:"Never" },
            { value:"past", label:"In the past" },
            { value:"current", label:"Currently in one" },
          ]}
        />
      </Field>

      <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:16}} className="cred-bottom">
        <Field label="Do you have a co-signer?">
          <RadioGroup
            value={data.cosigner}
            onChange={(v)=>update({cosigner:v})}
            columns={2}
            options={[
              { value:"yes", label:"Yes" },
              { value:"no", label:"No" },
            ]}
          />
        </Field>
        <Field label="Do you have a trade-in?">
          <RadioGroup
            value={data.tradeIn}
            onChange={(v)=>update({tradeIn:v})}
            columns={2}
            options={[
              { value:"yes", label:"Yes" },
              { value:"no", label:"No" },
            ]}
          />
        </Field>
      </div>
      <style>{`
        @media (max-width: 640px) { .cred-bottom { grid-template-columns: 1fr !important; } }
      `}</style>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Review summary
// ─────────────────────────────────────────────────────────────
function ReviewSummary({ data, onEdit }) {
  const fmt = (v, fallback="—") => v || fallback;
  const sections = [
    { key: "contact", label: "Contact",
      rows: [
        ["Name", fmt(`${data.first} ${data.last}`.trim())],
        ["Phone", fmt(data.phone)],
        ["Email", fmt(data.email)],
        ["Date of birth", fmt(data.dob)],
        ["Postal code", fmt(data.postal)],
      ]},
    { key: "vehicle", label: "Vehicle",
      rows: [
        ["Price range", fmt(({
          "5-15":"$5,000 – $15,000",
          "15-25":"$15,000 – $25,000",
          "25-35":"$25,000 – $35,000",
          "35-50":"$35,000 – $50,000",
          "50+":"$50,000+",
          "flex":"Flexible",
        })[data.vehiclePrice])],
        ["Timing", fmt(data.buyTiming)],
        ["Type preference", fmt(({sedan:"Sedan / coupe",suv:"SUV / crossover",truck:"Pickup truck",any:"No preference"})[data.vehicleType])],
      ]},
    { key: "employment", label: "Employment",
      rows: [
        ["Status", fmt(({ft:"Full-time",pt:"Part-time",selfemp:"Self-employed",contract:"Contract / gig",retired:"Retired / pension",other:"Other"})[data.empStatus])],
        ["Length", fmt(({"<6":"< 6 months","6-24":"6 mo – 2 yr","2-5":"2 – 5 yr","5+":"5+ yr"})[data.empLength])],
        ["Monthly income", fmt(data.monthlyIncome)],
        ["Employer", fmt(data.employer)],
      ]},
    { key: "credit", label: "Credit",
      rows: [
        ["Estimated score", fmt(({excellent:"Excellent (720+)",good:"Good (680–719)",fair:"Fair (620–679)",building:"Building (580–619)",wip:"Work-in-progress (579 & under)",unknown:"Unknown"})[data.creditScore])],
        ["Bankruptcy / proposal", fmt(({never:"Never",past:"In the past",current:"Currently in one"})[data.bankruptcy])],
        ["Co-signer", fmt(({yes:"Yes",no:"No"})[data.cosigner])],
        ["Trade-in", fmt(({yes:"Yes",no:"No"})[data.tradeIn])],
      ]},
  ];
  return (
    <div className="stack gap-6">
      <div className="h4">Review your information</div>
      <p className="small" style={{margin:"-12px 0 0"}}>
        Make sure everything looks right. You can edit any section.
      </p>
      {sections.map((s) => (
        <div key={s.key} className="review-section">
          <div className="review-h">
            <b>{s.label}</b>
            <button type="button" className="btn btn-ghost btn-sm" onClick={()=>onEdit(s.key)}>
              <Icon name="edit" size={12}/>Edit
            </button>
          </div>
          <dl className="review-rows">
            {s.rows.map(([k,v]) => (
              <React.Fragment key={k}>
                <dt>{k}</dt>
                <dd className={v === "—" ? "soft" : ""}>{v}</dd>
              </React.Fragment>
            ))}
          </dl>
        </div>
      ))}
      <style>{`
        .review-section {
          border: 1px solid var(--border); border-radius: var(--r-lg);
          background: var(--surface-muted);
        }
        .review-h {
          display: flex; justify-content: space-between; align-items: center;
          padding: 14px 16px; border-bottom: 1px solid var(--border);
          font-size: 14px;
        }
        .review-h b { font-weight: 600; }
        .review-rows {
          display: grid; grid-template-columns: 160px 1fr; gap: 8px 16px;
          margin: 0; padding: 14px 16px; font-size: 14px;
        }
        @media (max-width: 520px) { .review-rows { grid-template-columns: 1fr; gap: 2px 0; }
          .review-rows dt { padding-top: 8px; }
          .review-rows dd { padding-bottom: 8px; border-bottom: 1px solid var(--border); }
          .review-rows > *:last-child { border-bottom: 0; }
        }
        .review-rows dt { color: var(--text-soft); margin: 0; }
        .review-rows dd { color: var(--text); font-weight: 500; margin: 0; }
        .review-rows dd.soft { color: var(--text-soft); font-weight: 400; }
      `}</style>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Credit check consent block (final apply step)
// ─────────────────────────────────────────────────────────────
function ConsentBlock({ data, update, errors = {} }) {
  const [explainerOpen, setExplainerOpen] = aUS(false);
  const consentErr = errors.creditConsent || errors.contactConsent;
  return (
    <div className="card" data-field="creditConsent" data-has-error={consentErr ? true : undefined} style={{
      border: "1.5px solid var(--primary)",
      background: "var(--primary-tint)",
      padding: 0,
    }}>
      <div style={{padding:"20px 24px",borderBottom:"1px solid color-mix(in oklch, var(--primary) 18%, transparent)"}}>
        <div style={{display:"flex",alignItems:"center",gap:10}}>
          <span style={{
            width:32,height:32,borderRadius:"50%",
            background:"var(--primary)",color:"white",
            display:"grid",placeItems:"center"
          }}><Icon name="shield" size={16} /></span>
          <div>
            <div className="h3" style={{fontSize:17}}>Consent &amp; authorization</div>
            <div className="small">Both authorizations required to submit your application</div>
          </div>
        </div>
      </div>

      <div style={{padding: 24}} className="stack gap-4">
        <p style={{margin:0,fontSize:14.5,lineHeight:1.6,color:"var(--text)"}}>
          To process your application, we need two authorizations: one to run a credit check,
          and one to contact you about your file. You can withdraw either at any time.
        </p>

        <button type="button" className="explainer-toggle" onClick={()=>setExplainerOpen(!explainerOpen)}>
          <Icon name="help" size={14}/>
          <span>What is a credit check?</span>
          <Icon name="chevronDown" size={14} style={{transform: explainerOpen ? "rotate(180deg)":"none", transition: "transform .2s ease", marginLeft:"auto"}}/>
        </button>
        {explainerOpen && (
          <div className="explainer-body">
            <p style={{margin:"0 0 10px",fontSize:13.5,lineHeight:1.6,color:"var(--text)"}}>
              A credit check lets our lenders see your credit history and current standing so
              they can decide what financing they can offer you. We run it through{" "}
              <b>Equifax Canada</b> and <b>TransUnion Canada</b>.
            </p>
            <ul style={{margin:0,padding:"0 0 0 18px",fontSize:13.5,lineHeight:1.6,color:"var(--text-muted)"}}>
              <li>We only run the check after we've spoken with you and you've given approval to proceed.</li>
              <li>It's used solely to match you with a lender for vehicle financing.</li>
              <li>Your information is shared only with the lender(s) reviewing your application — never marketing partners.</li>
            </ul>
          </div>
        )}

        <label className="consent-row" data-error={errors.creditConsent ? true : undefined}>
          <input
            type="checkbox" checked={data.creditConsent}
            onChange={e => update({creditConsent: e.target.checked})}
            className="consent-cb"
          />
          <span>
            <span className="consent-row-h">
              <b>Credit check authorization</b>
              <span className="consent-pill pill pill-neutral">Required</span>
            </span>
            <span className="consent-row-body">
              I authorize Canadian Auto Loan to obtain my credit report from{" "}
              <b style={{color:"var(--text)"}}>Equifax Canada</b> and{" "}
              <b style={{color:"var(--text)"}}>TransUnion Canada</b> for the purpose of matching
              me with lender(s) for vehicle financing. I understand the credit check is run after
              Canadian Auto Loan has spoken with me and I have approved proceeding.
              {" "}<Link to="/credit-consent" style={{color:"var(--primary)",textDecoration:"underline",textUnderlineOffset:2}}>Read the full disclosure</Link>.
            </span>
          </span>
        </label>

        <label className="consent-row" data-error={errors.contactConsent ? true : undefined}>
          <input
            type="checkbox" checked={data.contactConsent}
            onChange={e => update({contactConsent: e.target.checked})}
            className="consent-cb"
          />
          <span>
            <span className="consent-row-h">
              <b>Contact authorization</b>
              <span className="consent-pill pill pill-neutral">Required</span>
            </span>
            <span className="consent-row-body">
              I agree to be contacted by Canadian Auto Loan by <b style={{color:"var(--text)"}}>phone, email, or text message</b> about
              my application. I understand I may withdraw this consent at any time by emailing{" "}
              <b style={{color:"var(--text)"}}>info@canadianautoloan.net</b>.
            </span>
          </span>
        </label>

        <div className="consent-meta">
          <Icon name="lock" size={13} style={{flex:"0 0 13px",marginTop:2,color:"var(--text-soft)"}}/>
          <span>
            For audit purposes, we record the <b>date &amp; time</b>, <b>IP address</b>,{" "}
            <b>browser information</b>, and the <b>exact consent text version</b> shown to you
            when you submit. <span className="tnum">{CONSENT_VERSION}</span>
          </span>
        </div>
      </div>

      <style>{`
        .explainer-toggle {
          appearance: none; border: 1px solid color-mix(in oklch, var(--primary) 30%, transparent);
          background: var(--surface); border-radius: 10px;
          display: flex; align-items: center; gap: 8px;
          padding: 10px 14px; font-size: 13.5px; color: var(--text); cursor: pointer;
          font-weight: 500; text-align: left;
        }
        .explainer-toggle:hover { background: var(--primary-tint); }
        .explainer-body { background: var(--surface); border-radius: 10px; padding: 16px; border: 1px solid var(--border); }
        @media (max-width: 520px) { .explainer-grid { grid-template-columns: 1fr !important; } }
        .explainer-col { padding: 4px 0; }
        .consent-row {
          display: flex; gap: 14px; align-items: flex-start;
          padding: 18px;
          background: var(--surface); border: 1.5px solid var(--border-strong);
          border-radius: 12px;
          cursor: pointer;
          transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
        }
        .consent-row:hover { border-color: var(--primary); }
        .consent-row:has(input:checked) { border-color: var(--primary); background: white; box-shadow: 0 0 0 3px color-mix(in oklch, var(--primary) 15%, transparent); }
        .consent-row > span:last-child { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
        .consent-row-h { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
        .consent-row-h b { font-size: 15px; font-weight: 600; }
        .consent-pill { font-size: 10.5px !important; padding: 2px 8px !important; letter-spacing: .04em; text-transform: uppercase; }
        .consent-row-body { font-size: 13.5px; line-height: 1.55; color: var(--text-muted); }
        .consent-cb { appearance: none; -webkit-appearance: none; width: 22px; height: 22px; flex: 0 0 22px; border: 1.5px solid var(--border-strong); border-radius: 6px; background: white; position: relative; margin-top: 2px; cursor: pointer; transition: all .15s; }
        .consent-cb:hover { border-color: var(--primary); }
        .consent-cb:checked { background: var(--primary); border-color: var(--primary); }
        .consent-cb:checked::after { content: ""; position: absolute; left: 6px; top: 3px; width: 6px; height: 11px; border-right: 2px solid white; border-bottom: 2px solid white; transform: rotate(45deg); }

        .consent-meta {
          display: flex; gap: 8px; align-items: flex-start;
          padding: 12px 14px;
          background: var(--surface-muted);
          border: 1px dashed var(--border-strong);
          border-radius: 10px;
          font-size: 12px; line-height: 1.5; color: var(--text-muted);
          margin-top: 4px;
        }
        .consent-meta b { color: var(--text); font-weight: 600; }
      `}</style>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Main Apply flow
// ─────────────────────────────────────────────────────────────
function Apply({ density = 5 }) {
  const { path, go } = useRoute();
  const [data, update, reset] = useApplyState();
  const steps = getSteps(density);

  // Current step key from URL: /apply/<key>
  const m = path.match(/^\/apply\/?(.*)$/);
  const curKey = m ? m[1] : "";

  // If no step key, resume where the user left off (or go to first step).
  aUE(() => {
    if (curKey === "") {
      let resume = "";
      try { resume = localStorage.getItem(STEP_KEY) || ""; } catch (_) {}
      // Only resume to a step that exists in the current density's flow.
      const valid = resume && steps.some(s => s.key === resume) && resume !== "done";
      go(`/apply/${valid ? resume : steps[0].key}`);
    }
  }, [curKey, density]);

  // Persist the latest visited step so click-out / click-back resumes here.
  aUE(() => {
    if (!curKey || curKey === "done") return;
    if (!steps.some(s => s.key === curKey)) return;
    try { localStorage.setItem(STEP_KEY, curKey); } catch (_) {}
  }, [curKey]);

  // Done state
  if (curKey === "done") {
    return <ApplySubmitted reset={reset} />;
  }
  // While we're about to redirect, render nothing — avoids flicker
  if (curKey === "") {
    return null;
  }

  const curIdx = Math.max(0, steps.findIndex(s => s.key === curKey));
  const step = steps[curIdx];
  if (!step) {
    // Unknown step → bounce to first
    go(`/apply/${steps[0].key}`);
    return null;
  }

  // Validation state. Errors only populate after the user clicks Continue once;
  // then they auto-clear field-by-field as the user fixes them.
  const [attempted, setAttempted] = aUS(false);
  const errors = aUM(() => attempted ? validateStep(step?.key, data) : {}, [attempted, step?.key, data]);
  // Reset attempted state when navigating between steps.
  aUE(() => { setAttempted(false); }, [curKey]);

  // Submission state. `submitting` blocks the Submit button while POST is in
  // flight; `submitError` surfaces a retry banner on failure.
  const [submitting, setSubmitting] = aUS(false);
  const [submitError, setSubmitError] = aUS(null);

  const scrollToFirstError = (errs) => {
    const firstKey = Object.keys(errs)[0];
    if (!firstKey) return;
    requestAnimationFrame(() => {
      const el = document.querySelector(`[data-field="${firstKey}"]`);
      if (!el) return;
      const rect = el.getBoundingClientRect();
      const stickyTop = (document.querySelector('.apply-header')?.getBoundingClientRect().bottom || 0);
      const offset = window.scrollY + rect.top - stickyTop - 16;
      window.scrollTo({ top: Math.max(0, offset), behavior: "smooth" });
      // Focus the offending control for keyboard users
      const focusable = el.querySelector('input, button, select, [tabindex]');
      if (focusable && typeof focusable.focus === "function") {
        setTimeout(() => focusable.focus({ preventScroll: true }), 400);
      }
    });
  };

  const goStep = (i) => go(`/apply/${steps[Math.max(0, Math.min(steps.length-1, i))].key}`);
  const next = async () => {
    if (submitting) return;
    const errs = validateStep(step.key, data);
    if (Object.keys(errs).length > 0) {
      setAttempted(true);
      scrollToFirstError(errs);
      return;
    }
    if (curIdx !== steps.length - 1) {
      goStep(curIdx + 1);
      return;
    }

    // Final step — build submission payload and POST to the application intake
    // endpoint. The endpoint forwards the lead to BKD via email; the consent
    // audit record travels inside the payload. Client IP is captured server-side.
    const submission = {
      submittedAt: new Date().toISOString(),
      consentVersion: CONSENT_VERSION,
      creditConsent: {
        accepted: data.creditConsent,
        text: CONSENT_TEXT.credit,
      },
      contactConsent: {
        accepted: data.contactConsent,
        text: CONSENT_TEXT.contact,
      },
      clientMetadata: {
        userAgent: navigator.userAgent,
        language: navigator.language,
        languages: Array.isArray(navigator.languages) ? navigator.languages.slice(0, 4) : [],
        platform: navigator.platform,
        timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
        timezoneOffsetMinutes: new Date().getTimezoneOffset(),
        screen: `${window.screen?.width || 0}×${window.screen?.height || 0} @${window.devicePixelRatio || 1}x`,
        viewport: `${window.innerWidth}×${window.innerHeight}`,
        referrer: document.referrer || "(direct)",
        // ipAddress: captured server-side at submission endpoint.
      },
      application: { ...data },
    };

    setSubmitting(true);
    setSubmitError(null);

    try {
      const ctrl = new AbortController();
      const timeoutId = setTimeout(() => ctrl.abort(), 15000);
      const res = await fetch("/api/submit-application", {
        method: "POST",
        headers: { "Content-Type": "application/json", "Accept": "application/json" },
        body: JSON.stringify(submission),
        signal: ctrl.signal,
      });
      clearTimeout(timeoutId);
      const body = await res.json().catch(() => ({ ok: false, error: "invalid_response" }));
      if (!res.ok || !body.ok) {
        const detail = body?.error || `http_${res.status}`;
        setSubmitting(false);
        setSubmitError(detail);
        return;
      }
      // Server-confirmed submission: store the audit record with the server ID,
      // navigate to the success page. Form draft is left in place until the
      // ApplySubmitted screen clears it.
      const stored = { ...submission, submissionId: body.submissionId, emailId: body.emailId };
      try {
        localStorage.setItem("cal_apply_submission_v1", JSON.stringify(stored));
      } catch (_) {}
      setSubmitting(false);
      go("/apply/done");
    } catch (err) {
      setSubmitting(false);
      setSubmitError(err?.name === "AbortError" ? "timeout" : "network");
    }
  };
  const back = () => goStep(curIdx - 1);

  const errorCount = Object.keys(errors).length;

  return (
    <div className="apply-page">
      <div className="apply-shell">
        <ApplyHeader steps={steps} curIdx={curIdx} />

        <div className="apply-body container">
          <div className="apply-grid">
            <main className="apply-main">
              <div key={curKey}>
                <ApplyStepHeader step={step} curIdx={curIdx} total={steps.length} />
                {errorCount > 0 && (
                  <div className="apply-error-banner" role="alert">
                    <Icon name="info" size={18} stroke={2.5}/>
                    <div>
                      <b>{errorCount === 1 ? "One thing needs your attention" : `${errorCount} things need your attention`}</b>
                      <span> · We've highlighted what's missing or needs fixing below.</span>
                    </div>
                  </div>
                )}
                {submitError && (
                  <div className="apply-error-banner" role="alert" style={{background:"var(--danger-soft)",borderColor:"color-mix(in oklch, var(--danger) 25%, transparent)"}}>
                    <Icon name="info" size={18} stroke={2.5}/>
                    <div>
                      <b>We couldn't submit your application.</b>
                      <span> · {submitError === "timeout" ? "The request timed out." : submitError === "network" ? "Check your connection and try again." : "Something went wrong on our end."} You can retry now, or call us at <b className="tnum">(613) 700-8641</b>. Your information has been saved locally so you won't lose it.</span>
                    </div>
                  </div>
                )}
                <div className="mt-8">
                  {step.key === "contact" && <ContactBlock data={data} update={update} errors={errors} />}
                  {step.key === "vehicle" && <VehicleBlock data={data} update={update} errors={errors} />}
                  {step.key === "employment" && <EmploymentBlock data={data} update={update} errors={errors} />}
                  {step.key === "credit" && <CreditBlock data={data} update={update} errors={errors} />}
                  {step.key === "money" && (
                    <div className="stack gap-12">
                      <EmploymentBlock data={data} update={update} errors={errors} />
                      <hr className="hairline" />
                      <CreditBlock data={data} update={update} errors={errors} />
                    </div>
                  )}
                  {step.key === "loan" && (
                    <div className="stack gap-12">
                      <VehicleBlock data={data} update={update} errors={errors} />
                      <hr className="hairline" />
                      <EmploymentBlock data={data} update={update} errors={errors} />
                      <hr className="hairline" />
                      <CreditBlock data={data} update={update} errors={errors} />
                    </div>
                  )}
                  {step.key === "consent" && (
                    <div className="stack gap-8">
                      <ReviewSummary
                        data={data}
                        onEdit={(k) => {
                          const t = steps.find(s => s.key === k);
                          if (t) go(`/apply/${t.key}`);
                          else {
                            if (density === 3) {
                              if (k === "contact") go("/apply/contact");
                              else go("/apply/loan");
                            } else if (density === 4) {
                              if (k === "contact") go("/apply/contact");
                              else if (k === "vehicle") go("/apply/vehicle");
                              else go("/apply/money");
                            }
                          }
                        }}
                      />
                      <ConsentBlock data={data} update={update} errors={errors} />
                    </div>
                  )}
                </div>

                <ApplyFooter
                  curIdx={curIdx} total={steps.length}
                  onBack={curIdx > 0 ? back : null}
                  onNext={next}
                  isLast={curIdx === steps.length - 1}
                  submitting={submitting}
                />
              </div>
            </main>
            <aside className="apply-side">
              <ApplySidePanel data={data} step={step} />
            </aside>
          </div>
        </div>
      </div>

      <style>{`
        .apply-page { background: var(--bg); min-height: 100vh; }
        .apply-shell { min-height: calc(100vh - var(--nav-h)); }
        .apply-body { padding: 32px 0 80px; }
        .apply-grid {
          display: grid; grid-template-columns: 1fr 320px; gap: 48px; align-items: flex-start;
        }
        @media (max-width: 880px) { .apply-grid { grid-template-columns: 1fr; gap: 32px; }
          .apply-side { order: 1; } }
        .apply-error-banner {
          display: flex; align-items: flex-start; gap: 12px;
          padding: 14px 18px;
          margin-top: 24px;
          background: var(--danger-soft);
          color: var(--danger);
          border: 1px solid color-mix(in oklch, var(--danger) 28%, transparent);
          border-radius: 12px;
          font-size: 14px; line-height: 1.5;
        }
        .apply-error-banner b { font-weight: 600; }
        .apply-error-banner > svg { flex: 0 0 18px; margin-top: 1px; }
        .apply-error-banner span { color: color-mix(in oklch, var(--danger) 80%, var(--text)); }
      `}</style>
    </div>
  );
}

function ApplyHeader({ steps, curIdx }) {
  const pct = ((curIdx + 1) / steps.length) * 100;
  return (
    <div className="apply-header">
      <div className="container">
        <div className="apply-header-inner">
          <div className="apply-prog">
            <div className="apply-prog-meta">
              <div className="tiny" style={{textTransform:"uppercase",letterSpacing:".08em",fontWeight:600,color:"var(--text-soft)"}}>Application</div>
              <div style={{fontSize:14,fontWeight:600,marginTop:2}}>
                Step <span className="tnum">{curIdx+1}</span> of <span className="tnum">{steps.length}</span>
                <span className="soft" style={{fontWeight:500,marginLeft:8}}>· {steps[curIdx]?.label}</span>
              </div>
            </div>
            <div className="apply-prog-bar">
              <div className="apply-prog-fill" style={{width: `${pct}%`}} />
            </div>
          </div>
          <div className="apply-save">
            <Icon name="check" size={14} stroke={2.5}/>
            <span>Saved</span>
          </div>
        </div>

        <div className="apply-stepper" aria-hidden="true">
          {steps.map((s, i) => (
            <div key={s.key} className={`step ${i < curIdx ? "done" : i === curIdx ? "active" : ""}`}>
              <div className="step-bar"></div>
              <div className="step-label">{s.label}</div>
            </div>
          ))}
        </div>
      </div>
      <style>{`
        .apply-header { background: var(--surface); border-bottom: 1px solid var(--border); padding: 18px 0 24px; position: sticky; top: var(--nav-h); z-index: 50; backdrop-filter: blur(14px); }
        @media (max-width: 640px) {
          .apply-header { padding: 14px 0 16px; }
        }
        .apply-header-inner { display: flex; justify-content: space-between; align-items: center; gap: 24px; }
        .apply-prog { flex: 1; min-width: 0; max-width: 520px; }
        .apply-prog-bar { height: 4px; background: var(--surface-muted); border-radius: 999px; overflow: hidden; margin-top: 8px; }
        .apply-prog-fill { height: 100%; background: linear-gradient(90deg, var(--primary), oklch(0.58 0.2 35)); transition: width .4s cubic-bezier(.4,0,.2,1); border-radius: 999px; }
        .apply-save { display: flex; gap: 6px; align-items: center; padding: 6px 10px; border-radius: 999px; background: var(--success-soft); color: var(--success); font-size: 12.5px; font-weight: 500; }
        .apply-stepper { display: flex; gap: 12px; margin-top: 24px; }
        @media (max-width: 880px) { .apply-stepper .step-label { display: none; }
          .apply-save span { display: none; } .apply-save { padding: 6px; }
        }
        @media (max-width: 640px) { .apply-stepper { display: none; } }
      `}</style>
    </div>
  );
}

function ApplyStepHeader({ step, curIdx, total }) {
  const titles = {
    contact: ["Let's start with how to reach you.", "We use this to contact you about your application — never for marketing."],
    vehicle: ["Tell us about the vehicle.", "We'll match financing to the price range you're aiming for."],
    employment: ["Income & employment.", "Lenders look at your overall stability, not just a credit number."],
    credit: ["Your credit picture.", "Be honest — we work with every credit profile."],
    money: ["Income, employment & credit.", "Lenders look at your overall stability, not just a credit number."],
    loan: ["About your loan.", "Vehicle, income and credit picture in one place."],
    consent: ["Review & authorize.", "Almost done. Make sure everything looks right, then provide both consents to submit your application."],
  };
  const [t, sub] = titles[step.key] || ["Continue", ""];
  return (
    <div>
      <div className="eyebrow tnum">Step {curIdx+1} / {total}</div>
      <h1 className="h1 mt-4" style={{fontSize: "clamp(28px, 3.4vw, 38px)"}}>{t}</h1>
      <p className="lede mt-4">{sub}</p>
    </div>
  );
}

function ApplyFooter({ curIdx, total, onBack, onNext, isLast, submitting }) {
  return (
    <div className="apply-footer-actions">
      <div className="apply-footer-left">
        {onBack && (
          <button className="btn btn-secondary btn-lg" onClick={onBack}>
            <Icon name="arrowLeft" size={16}/>Back
          </button>
        )}
        <button className="btn btn-ghost btn-lg apply-save-btn" onClick={() => {
          window.location.hash = "#/";
        }}>
          Save &amp; exit
        </button>
      </div>
      <button
        className="btn btn-primary btn-lg apply-continue-btn"
        onClick={onNext}
        disabled={submitting}
        aria-busy={submitting ? "true" : undefined}
      >
        {submitting ? "Submitting…" : (isLast ? "Submit application" : "Continue")}
        {!submitting && <Icon name="arrowRight" size={16}/>}
      </button>

      <style>{`
        .apply-footer-actions {
          display: flex; justify-content: space-between; align-items: center;
          margin-top: 40px; padding-top: 32px;
          border-top: 1px solid var(--border);
          gap: 12px; flex-wrap: wrap;
        }
        .apply-footer-left { display: flex; gap: 8px; flex-wrap: wrap; }
        .apply-continue-btn { min-width: 180px; }
        @media (max-width: 640px) {
          .apply-footer-actions {
            flex-direction: column-reverse;
            align-items: stretch;
            gap: 10px;
            margin-top: 28px; padding-top: 20px;
          }
          .apply-continue-btn { width: 100%; padding: 16px 22px; font-size: 16.5px; }
          .apply-footer-left { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
          .apply-footer-left .btn { width: 100%; }
          .apply-footer-left:has(:only-child) { grid-template-columns: 1fr; }
        }
      `}</style>
    </div>
  );
}

function ApplySidePanel({ data, step }) {
  return (
    <div className="stack gap-4 apply-side-content">
      <div className="card" style={{padding: 20, background: "var(--primary-tint)", border: "1px solid color-mix(in oklch, var(--primary) 22%, transparent)"}}>
        <div style={{display:"flex",gap:10,alignItems:"flex-start"}}>
          <Icon name="shield" size={18} style={{color:"var(--primary)",marginTop:2}}/>
          <div>
            <div style={{fontWeight:600,fontSize:14, color:"var(--primary)"}}>No surprise credit checks</div>
            <div className="small mt-2" style={{fontSize: 13, lineHeight:1.5}}>
              We only run a credit check after we've spoken with you and you've approved proceeding. Nothing happens to your credit just from applying.
            </div>
          </div>
        </div>
      </div>

      <div className="card" style={{padding: 20}}>
        <div style={{display:"flex",gap:12,alignItems:"center"}}>
          <div style={{width:44,height:44,borderRadius:"50%",background:"linear-gradient(135deg, oklch(0.88 0.09 25), oklch(0.78 0.13 35))",color:"white",fontWeight:600,fontSize:14,display:"grid",placeItems:"center"}}>EC</div>
          <div>
            <div style={{fontWeight:600,fontSize:14}}>Ecaterina</div>
            <div className="small">Sales lead</div>
          </div>
        </div>
        <p style={{margin:"14px 0 0",fontSize:13.5,lineHeight:1.55,color:"var(--text-muted)"}}>
          "Stuck on a question? I'll personally review your file after you submit. Reach me at the number below if you'd rather talk it through."
        </p>
        <div className="mt-4 stack gap-2 small">
          <div style={{display:"flex",gap:8,alignItems:"center"}}><Icon name="phone" size={13}/><b className="tnum">(613) 700-8641</b></div>
          <div style={{display:"flex",gap:8,alignItems:"center"}}><Icon name="clock" size={13}/>Mon–Fri, 10 AM – 5 PM</div>
        </div>
      </div>

      <div className="card" style={{padding: 20}}>
        <div style={{display:"flex",gap:8,alignItems:"center",marginBottom:8}}>
          <Icon name="lock" size={14} style={{color:"var(--text-muted)"}}/>
          <span style={{fontSize:13,fontWeight:600}}>Your info is encrypted</span>
        </div>
        <p style={{margin:0,fontSize:12.5,color:"var(--text-muted)",lineHeight:1.55}}>
          Bank-level TLS in transit and at rest. We share your file only with lenders reviewing your application — never marketing partners.
        </p>
      </div>

      <style>{`
        @media (max-width: 880px) { .apply-side-content { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
          .apply-side-content > :nth-child(3) { grid-column: span 2; }
        }
        @media (max-width: 600px) { .apply-side-content { grid-template-columns: 1fr; }
          .apply-side-content > * { grid-column: span 1; }
        }
      `}</style>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Submitted state
// ─────────────────────────────────────────────────────────────
function ApplySubmitted({ reset }) {
  const [submission, setSubmission] = aUS(null);
  aUE(() => {
    try {
      const raw = localStorage.getItem("cal_apply_submission_v1");
      if (raw) setSubmission(JSON.parse(raw));
    } catch (_) {}
  }, []);
  const fmtDate = (iso) => {
    if (!iso) return "—";
    try {
      const d = new Date(iso);
      return d.toLocaleString("en-CA", {
        year: "numeric", month: "short", day: "2-digit",
        hour: "2-digit", minute: "2-digit", second: "2-digit",
        timeZoneName: "short",
      });
    } catch { return iso; }
  };
  return (
    <div className="container" style={{padding: "80px 0", maxWidth: 720}}>
      <div className="text-center">
        <div style={{
          width: 80, height: 80, borderRadius: "50%",
          background: "var(--success-soft)", color: "var(--success)",
          display: "grid", placeItems: "center", margin: "0 auto 24px",
        }}>
          <Icon name="check" size={40} stroke={2.5}/>
        </div>
        <h1 className="h-display" style={{fontSize:"clamp(32px, 4vw, 48px)"}}>
          Application received.
        </h1>
        <p className="lede-lg mt-6" style={{maxWidth: 540, margin: "20px auto 0"}}>
          A real human will review your file within 24 hours. We'll text and email
          you with what comes next — usually a quick call to confirm a few details
          before we shop the network.
        </p>
        <div className="mt-8" style={{display:"flex",gap:12,flexWrap:"wrap",justifyContent:"center"}}>
          <Link to="/" className="btn btn-primary btn-lg">Back to home</Link>
          <button className="btn btn-secondary btn-lg" onClick={reset}>Start over</button>
        </div>
      </div>

      <div className="mt-16 card card-pad-lg">
        <div className="h3 mb-4">What happens next</div>
        <ol style={{listStyle:"none",padding:0,margin:0,display:"flex",flexDirection:"column",gap:16}}>
          {[
            ["Within 24 hours", "Ecaterina or another sales lead reviews your file and pings you with any questions."],
            ["1–3 business days", "We match you with lenders in our network and bring back the best rate available for your profile."],
            ["After pre-approval", "Pick a vehicle from our partner inventory. We arrange door-to-door delivery anywhere in Ontario or Quebec."],
          ].map(([when, what], i) => (
            <li key={when} style={{display:"flex",gap:16,alignItems:"flex-start"}}>
              <div className="tnum" style={{
                width: 32, height: 32, borderRadius: "50%",
                background: "var(--primary-tint)", color: "var(--primary)",
                display: "grid", placeItems: "center", fontWeight: 600, fontSize: 13,
                flex: "0 0 32px",
              }}>{i+1}</div>
              <div>
                <div style={{fontWeight:600,fontSize:15}}>{when}</div>
                <p style={{margin:"4px 0 0",color:"var(--text-muted)",fontSize:14.5,lineHeight:1.55}}>{what}</p>
              </div>
            </li>
          ))}
        </ol>
      </div>

      {submission && <ConsentAuditPanel submission={submission} fmtDate={fmtDate} />}
    </div>
  );
}

function ConsentAuditPanel({ submission, fmtDate }) {
  const [open, setOpen] = aUS(false);
  return (
    <div className="mt-8 audit-panel">
      <button
        type="button"
        className="audit-toggle"
        onClick={() => setOpen(o => !o)}
        aria-expanded={open}
      >
        <Icon name="shield" size={15} style={{color:"var(--text-muted)"}}/>
        <span style={{flex:1,textAlign:"left"}}>
          <b>Consent record</b>
          <span className="audit-meta-inline">
            Recorded {fmtDate(submission.submittedAt)} · version{" "}
            <span className="tnum">{submission.consentVersion}</span>
          </span>
        </span>
        <Icon name="chevronDown" size={14} style={{transform: open ? "rotate(180deg)" : "none", transition: "transform .2s ease", color:"var(--text-soft)"}}/>
      </button>
      {open && (
        <div className="audit-body">
          <dl className="audit-rows">
            <dt>Submitted at</dt>
            <dd className="tnum">{fmtDate(submission.submittedAt)}</dd>
            <dt>Consent version</dt>
            <dd className="tnum">{submission.consentVersion}</dd>
            <dt>Soft credit check</dt>
            <dd>
              <span className={`pill ${submission.creditConsent.accepted ? "pill-success" : "pill-warning"}`} style={{fontSize:11}}>
                {submission.creditConsent.accepted ? "Authorized" : "Not authorized"}
              </span>
            </dd>
            <dt>Contact authorization</dt>
            <dd>
              <span className={`pill ${submission.contactConsent.accepted ? "pill-success" : "pill-warning"}`} style={{fontSize:11}}>
                {submission.contactConsent.accepted ? "Authorized" : "Not authorized"}
              </span>
            </dd>
            <dt>Browser</dt>
            <dd className="audit-mono">{submission.clientMetadata.userAgent}</dd>
            <dt>Language</dt>
            <dd className="tnum">{submission.clientMetadata.language}</dd>
            <dt>Timezone</dt>
            <dd className="tnum">{submission.clientMetadata.timezone} (UTC{submission.clientMetadata.timezoneOffsetMinutes <= 0 ? "+" : "-"}{String(Math.abs(submission.clientMetadata.timezoneOffsetMinutes / 60)).padStart(2,"0")}:00)</dd>
            <dt>Screen / viewport</dt>
            <dd className="tnum">{submission.clientMetadata.screen} · vp {submission.clientMetadata.viewport}</dd>
            <dt>Referrer</dt>
            <dd className="audit-mono">{submission.clientMetadata.referrer}</dd>
            <dt>IP address</dt>
            <dd className="soft">(captured server-side at submission)</dd>
          </dl>

          <details className="audit-consent-text">
            <summary>View consent text shown at submission</summary>
            <div className="audit-consent-block">
              <div className="audit-consent-h">Soft credit check authorization</div>
              <p>{submission.creditConsent.text}</p>
            </div>
            <div className="audit-consent-block">
              <div className="audit-consent-h">Contact authorization</div>
              <p>{submission.contactConsent.text}</p>
            </div>
          </details>
        </div>
      )}
      <style>{`
        .audit-panel {
          border: 1px solid var(--border);
          border-radius: var(--r-lg);
          background: var(--surface);
          overflow: hidden;
        }
        .audit-toggle {
          appearance: none; border: 0; background: transparent;
          width: 100%; display: flex; align-items: center; gap: 10px;
          padding: 16px 20px;
          cursor: pointer;
          font-family: inherit; font-size: 14px;
          color: var(--text);
        }
        .audit-toggle:hover { background: var(--surface-muted); }
        .audit-toggle b { font-weight: 600; display: block; }
        .audit-meta-inline { display: block; font-size: 12.5px; color: var(--text-soft); margin-top: 2px; }
        .audit-body { padding: 4px 20px 20px; }
        .audit-rows {
          display: grid; grid-template-columns: 180px 1fr; gap: 10px 16px;
          margin: 0; padding: 16px 0;
          border-top: 1px solid var(--border);
          font-size: 13.5px;
        }
        @media (max-width: 560px) {
          .audit-rows { grid-template-columns: 1fr; gap: 2px 0; }
          .audit-rows dt { padding-top: 8px; }
          .audit-rows dd { padding-bottom: 8px; border-bottom: 1px solid var(--border); }
          .audit-rows > *:last-child { border-bottom: 0; }
        }
        .audit-rows dt { color: var(--text-soft); margin: 0; font-weight: 500; }
        .audit-rows dd { color: var(--text); margin: 0; min-width: 0; word-break: break-word; }
        .audit-mono { font-family: var(--font-mono); font-size: 12.5px; color: var(--text-muted); }
        .audit-consent-text {
          margin-top: 8px;
          border-top: 1px solid var(--border);
          padding-top: 16px;
        }
        .audit-consent-text summary {
          cursor: pointer; font-size: 13.5px; color: var(--primary); font-weight: 500;
          list-style: none; user-select: none;
          padding: 4px 0;
        }
        .audit-consent-text summary::-webkit-details-marker { display: none; }
        .audit-consent-text summary::before {
          content: "▸"; display: inline-block; margin-right: 8px;
          transition: transform .15s ease; color: var(--text-soft);
        }
        .audit-consent-text[open] summary::before { transform: rotate(90deg); }
        .audit-consent-block {
          margin-top: 14px; padding: 14px; background: var(--surface-muted);
          border-radius: 8px; font-size: 13px; line-height: 1.55;
        }
        .audit-consent-h { font-weight: 600; color: var(--text); font-size: 13px; margin-bottom: 6px; }
        .audit-consent-block p { margin: 0; color: var(--text-muted); }
      `}</style>
    </div>
  );
}

Object.assign(window, { Apply, ApplySubmitted, ConsentAuditPanel, useApplyState, getSteps });
