// Nordbank — Apply wizard. 6 steps + Success + InsuranceMockCard.

// -----------------------------------------------------------------------------
// Entry: <Apply /> mounts the wizard or success screen depending on state.signed.
// -----------------------------------------------------------------------------

function Apply() {
  useLang();
  const [app, dispatch] = useApplication();
  const user = useCurrentUser();

  // If signed + account created, the flow is done. Navigate to portal once.
  React.useEffect(() => {
    if (app.signed && app.accountCreated && user) {
      const handle = setTimeout(() => {
        dispatch({ type: 'RESET' });
        navigate('/portal');
      }, 50);
      return () => clearTimeout(handle);
    }
  }, [app.signed, app.accountCreated, user, dispatch]);

  // Step 7 — insurance checkout via Sherpa. Only entered when the user opted
  // into insurance in Step 5. Skipped silently otherwise.
  if (app.signed && app.insuranceSelected && !app.insurancePaid) {
    return <Step7Insurance app={app} dispatch={dispatch} />;
  }
  if (app.signed && !app.accountCreated) return <AccountSetup app={app} dispatch={dispatch} />;
  if (app.signed && app.accountCreated)  return null; // about to redirect
  return <ApplyWizard app={app} dispatch={dispatch} />;
}

function ApplyWizard({ app, dispatch }) {
  const lang = useLang();
  const stepEls = {
    1: <Step1Country app={app} dispatch={dispatch} lang={lang} />,
    2: <Step2Personal app={app} dispatch={dispatch} lang={lang} />,
    3: <Step3Employment app={app} dispatch={dispatch} lang={lang} />,
    4: <Step4BankId app={app} dispatch={dispatch} lang={lang} />,
    5: <Step5Offer app={app} dispatch={dispatch} lang={lang} />,
    6: <Step6Sign app={app} dispatch={dispatch} lang={lang} />,
  };
  return (
    <div className="max-w-3xl mx-auto px-4 md:px-6 py-10 md:py-14">
      <ProgressBar step={app.step} total={7} />
      <div key={app.step} className="nb-fade mt-8">
        {stepEls[app.step]}
      </div>
    </div>
  );
}

// -----------------------------------------------------------------------------
// Step navigation buttons
// -----------------------------------------------------------------------------

function StepNav({ onBack, onNext, nextDisabled, nextLabel, backLabel }) {
  return (
    <div className="mt-10 flex flex-col-reverse sm:flex-row sm:items-center gap-3">
      {onBack ? (
        <button
          type="button"
          onClick={onBack}
          className="inline-flex items-center justify-center gap-2 px-5 py-3 text-sm font-semibold text-navy-700 hover:text-navy-900 hover:bg-navy-50 rounded-full transition-colors"
        >
          <span aria-hidden="true">←</span>
          {backLabel || t('common.back')}
        </button>
      ) : <span />}
      <div className="flex-1" />
      <button
        type="button"
        onClick={onNext}
        disabled={nextDisabled}
        className="inline-flex items-center justify-center gap-2 bg-navy-900 hover:bg-navy-700 text-white text-sm font-semibold px-7 py-3 rounded-full transition-all shadow-card disabled:opacity-40 disabled:cursor-not-allowed disabled:shadow-none"
      >
        {nextLabel || t('common.continue')}
        <svg width="14" height="14" viewBox="0 0 14 14"><path d="M2 7 H12 M8 3 L12 7 L8 11" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/></svg>
      </button>
    </div>
  );
}

function StepHeader({ title, subtitle }) {
  return (
    <div className="mb-7">
      <h1 className="text-3xl md:text-4xl font-semibold tracking-tight text-navy-900" style={{ textWrap: 'balance' }}>
        {title}
      </h1>
      {subtitle && (
        <p className="mt-2 text-base text-navy-500 leading-relaxed max-w-2xl" style={{ textWrap: 'pretty' }}>
          {subtitle}
        </p>
      )}
    </div>
  );
}

// -----------------------------------------------------------------------------
// Step 1 — Country & loan amount
// -----------------------------------------------------------------------------

const TERM_OPTIONS = [1, 2, 3, 5, 7, 10, 15];

function Step1Country({ app, dispatch, lang }) {
  const q = React.useMemo(() => quoteLoan(app.amount, app.termYears), [app.amount, app.termYears]);
  const range = AMOUNT_RANGES[app.currency] || AMOUNT_RANGES.NOK;
  const pct = ((app.amount - range.min) / (range.max - range.min)) * 100;
  const termLabel = (n) => n === 1 ? t('apply.step1.termOneYear') : t('apply.step1.termYears', { n });
  return (
    <div>
      <StepHeader title={t('apply.step1.title')} subtitle={t('apply.step1.subtitle')} />

      <Card className="p-6 md:p-8">
        <div className="space-y-7">
          <div>
            <p className="text-sm font-medium text-navy-900 mb-3">{t('apply.step1.country')}</p>
            <CountryPills value={app.country} onChange={(c) => dispatch({ type: 'SET_COUNTRY', country: c })} lang={lang} />
          </div>

          <div>
            <div className="flex items-end justify-between mb-3">
              <p className="text-sm font-medium text-navy-900">{t('apply.step1.amount')}</p>
              <p className="text-2xl font-semibold text-navy-900 tnum">
                {formatMoney(app.amount, app.currency, lang)}
              </p>
            </div>
            <input
              type="range"
              className="nb-slider"
              min={range.min}
              max={range.max}
              step={range.step}
              value={app.amount}
              onChange={(e) => dispatch({ type: 'SET_AMOUNT', amount: parseInt(e.target.value, 10) })}
              style={{ '--pct': pct + '%' }}
              aria-label={t('apply.step1.amount')}
            />
            <div className="mt-2 flex items-center justify-between text-xs text-navy-300 tnum">
              <span>{formatMoney(range.min, app.currency, lang)}</span>
              <span>{formatMoney(range.max, app.currency, lang)}</span>
            </div>
          </div>

          <div>
            <p className="text-sm font-medium text-navy-900 mb-3">{t('apply.step1.term')}</p>
            <div className="flex flex-wrap gap-2">
              {TERM_OPTIONS.map(y => {
                const active = y === app.termYears;
                return (
                  <button
                    key={y}
                    type="button"
                    onClick={() => dispatch({ type: 'SET_TERM', termYears: y })}
                    className={'px-4 py-2 rounded-full text-sm font-semibold tnum transition-colors ' +
                      (active ? 'bg-navy-900 text-white' : 'bg-white text-navy-700 border hairline hover:bg-navy-50')}
                  >
                    {termLabel(y)}
                  </button>
                );
              })}
            </div>
          </div>
        </div>
      </Card>

      {/* Live preview — uses a plain div so the navy bg isn't overridden
          by Card's default `bg-white`. Monthly payment is computed live
          via quoteLoan(amount, termYears) which uses the annuity formula. */}
      <div className="mt-4 rounded-2xl shadow-card p-6 md:p-8 text-white" style={{ background: '#0B2545', borderColor: '#0B2545' }}>
        <div className="flex items-center justify-between flex-wrap gap-2">
          <p className="text-xs font-semibold tracking-[0.18em] uppercase" style={{ color: '#5BD2CE' }}>
            {t('apply.step1.monthly')}
          </p>
          <p className="text-[11px] tnum" style={{ color: 'rgba(255,255,255,0.6)' }}>
            {t('apply.step1.nominalRate')} {formatPercent(q.nominalRate, lang, 1)}
          </p>
        </div>
        <div className="mt-2 flex items-baseline gap-2">
          <span className="text-5xl font-semibold tracking-tight tnum text-white">{formatMoney(q.monthly, app.currency, lang, { fraction: 0 })}</span>
          <span className="text-sm" style={{ color: 'rgba(255,255,255,0.6)' }}>/ {t('apply.step5.insurancePremiumSuffix').replace('/','').trim() || 'mo'}</span>
        </div>
        <dl className="mt-6 grid grid-cols-2 gap-4 text-sm">
          <div>
            <dt style={{ color: 'rgba(255,255,255,0.6)' }}>{t('apply.step1.effectiveRate')}</dt>
            <dd className="mt-0.5 text-lg font-semibold tnum text-white">{formatPercent(q.effectiveRate, lang, 1)}</dd>
          </div>
          <div>
            <dt style={{ color: 'rgba(255,255,255,0.6)' }}>{t('apply.step1.totalCost')}</dt>
            <dd className="mt-0.5 text-lg font-semibold tnum text-white">{formatMoney(q.totalCost, app.currency, lang)}</dd>
          </div>
          <div className="col-span-2">
            <dt style={{ color: 'rgba(255,255,255,0.6)' }}>{t('apply.step1.totalPayable')}</dt>
            <dd className="mt-0.5 text-lg font-semibold tnum text-white">{formatMoney(q.totalPayable, app.currency, lang)}</dd>
          </div>
        </dl>
        <p className="mt-5 text-[11px] leading-relaxed" style={{ color: 'rgba(255,255,255,0.5)' }}>
          {t('apply.step1.disclaimer')}
        </p>
      </div>

      <StepNav onNext={() => dispatch({ type: 'NEXT_STEP' })} />
    </div>
  );
}

// -----------------------------------------------------------------------------
// Step 2 — Personal details
// -----------------------------------------------------------------------------

const SSN_PLACEHOLDER = { NO: '15087812345', DK: '150878-1234', SE: '19780815-1234', FI: '150878-123A' };

function isEmail(v) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v); }
function isPhone(v) { return /^[\d+\s\-()]{6,}$/.test(v.trim()); }

function Step2Personal({ app, dispatch }) {
  const p = app.personal;
  const set = (patch) => dispatch({ type: 'SET_PERSONAL', patch });
  const [touched, setTouched] = React.useState({});
  const errors = {
    name: p.name.trim() ? '' : t('common.required'),
    dob: p.dob ? '' : t('common.required'),
    ssn: p.ssn.trim() ? '' : t('common.required'),
    email: !p.email ? t('common.required') : (!isEmail(p.email) ? '—' : ''),
    phone: !p.phone ? t('common.required') : (!isPhone(p.phone) ? '—' : ''),
    address: p.address.trim() ? '' : t('common.required'),
    postcode: p.postcode.trim() ? '' : t('common.required'),
    city: p.city.trim() ? '' : t('common.required'),
  };
  const valid = Object.values(errors).every(e => !e);

  const errOf = (key) => touched[key] ? errors[key] : '';

  return (
    <div>
      <StepHeader title={t('apply.step2.title')} subtitle={t('apply.step2.subtitle')} />
      <Card className="p-6 md:p-8">
        <div className="grid sm:grid-cols-2 gap-5">
          <div className="sm:col-span-2">
            <Field label={t('apply.step2.name')} required htmlFor="f-name" error={errOf('name')}>
              <TextInput id="f-name" value={p.name} onChange={v => set({ name: v })} placeholder={t('apply.step2.namePh')} autoComplete="name" onBlur={() => setTouched(s => ({...s, name: true}))} />
            </Field>
          </div>
          <Field label={t('apply.step2.dob')} required htmlFor="f-dob" error={errOf('dob')}>
            <TextInput id="f-dob" type="date" value={p.dob} onChange={v => set({ dob: v })} onBlur={() => setTouched(s => ({...s, dob: true}))} />
          </Field>
          <Field label={t('apply.step2.ssn')} required htmlFor="f-ssn" hint={t('apply.step2.ssnHelp')} error={errOf('ssn')}>
            <TextInput id="f-ssn" value={p.ssn} onChange={v => set({ ssn: v })} placeholder={SSN_PLACEHOLDER[app.country]} inputMode="numeric" onBlur={() => setTouched(s => ({...s, ssn: true}))} />
          </Field>
          <Field label={t('apply.step2.email')} required htmlFor="f-email" error={errOf('email')}>
            <TextInput id="f-email" type="email" value={p.email} onChange={v => set({ email: v })} placeholder={t('apply.step2.emailPh')} autoComplete="email" onBlur={() => setTouched(s => ({...s, email: true}))} />
          </Field>
          <Field label={t('apply.step2.phone')} required htmlFor="f-phone" error={errOf('phone')}>
            <TextInput id="f-phone" type="tel" value={p.phone} onChange={v => set({ phone: v })} placeholder="+47 900 00 000" autoComplete="tel" onBlur={() => setTouched(s => ({...s, phone: true}))} />
          </Field>
          <div className="sm:col-span-2">
            <Field label={t('apply.step2.address')} required htmlFor="f-addr" error={errOf('address')}>
              <TextInput id="f-addr" value={p.address} onChange={v => set({ address: v })} placeholder={t('apply.step2.addressPh')} autoComplete="street-address" onBlur={() => setTouched(s => ({...s, address: true}))} />
            </Field>
          </div>
          <Field label={t('apply.step2.postcode')} required htmlFor="f-pc" error={errOf('postcode')}>
            <TextInput id="f-pc" value={p.postcode} onChange={v => set({ postcode: v })} placeholder="0150" autoComplete="postal-code" onBlur={() => setTouched(s => ({...s, postcode: true}))} />
          </Field>
          <Field label={t('apply.step2.city')} required htmlFor="f-city" error={errOf('city')}>
            <TextInput id="f-city" value={p.city} onChange={v => set({ city: v })} placeholder="Oslo" autoComplete="address-level2" onBlur={() => setTouched(s => ({...s, city: true}))} />
          </Field>
        </div>
      </Card>
      <StepNav
        onBack={() => dispatch({ type: 'PREV_STEP' })}
        onNext={() => {
          if (!valid) {
            // Mark all as touched to reveal errors.
            setTouched({ name:true, dob:true, ssn:true, email:true, phone:true, address:true, postcode:true, city:true });
            return;
          }
          dispatch({ type: 'NEXT_STEP' });
        }}
      />
    </div>
  );
}

// -----------------------------------------------------------------------------
// Step 3 — Employment & income
// -----------------------------------------------------------------------------

function Step3Employment({ app, dispatch, lang }) {
  const e = app.employment;
  const set = (patch) => dispatch({ type: 'SET_EMPLOYMENT', patch });
  const valid = e.status && e.housing && e.income > 0 && (
    (e.status === 'student' || e.status === 'retired' || e.status === 'unemployed') ||
    (e.employer.trim() && e.startDate)
  );
  const showEmployerFields = !['student','retired','unemployed',''].includes(e.status);

  const statusOptions = [
    ['fulltime',     t('apply.step3.statusFulltime')],
    ['parttime',     t('apply.step3.statusParttime')],
    ['selfemployed', t('apply.step3.statusSelfemployed')],
    ['retired',      t('apply.step3.statusRetired')],
    ['student',      t('apply.step3.statusStudent')],
    ['unemployed',   t('apply.step3.statusUnemployed')],
  ];
  const housingOptions = [
    ['own',      t('apply.step3.housingOwn')],
    ['mortgage', t('apply.step3.housingMortgage')],
    ['rent',     t('apply.step3.housingRent')],
    ['other',    t('apply.step3.housingOther')],
  ];

  return (
    <div>
      <StepHeader title={t('apply.step3.title')} subtitle={t('apply.step3.subtitle')} />
      <Card className="p-6 md:p-8">
        <div className="space-y-5">
          <Field label={t('apply.step3.status')} required htmlFor="f-status">
            <SelectInput id="f-status" value={e.status} onChange={v => set({ status: v })}>
              <option value="" disabled>—</option>
              {statusOptions.map(([k, l]) => <option key={k} value={k}>{l}</option>)}
            </SelectInput>
          </Field>

          {showEmployerFields && (
            <div className="grid sm:grid-cols-2 gap-5 nb-fade">
              <Field label={t('apply.step3.employer')} required htmlFor="f-empl">
                <TextInput id="f-empl" value={e.employer} onChange={v => set({ employer: v })} placeholder="Statoil ASA" />
              </Field>
              <Field label={t('apply.step3.startDate')} required htmlFor="f-since">
                <TextInput id="f-since" type="month" value={e.startDate} onChange={v => set({ startDate: v })} />
              </Field>
            </div>
          )}

          <div className="grid sm:grid-cols-2 gap-5">
            <Field label={t('apply.step3.income')} required htmlFor="f-inc">
              <CurrencyInput id="f-inc" value={e.income} currency={app.currency} lang={lang} onChange={v => set({ income: v })} placeholder="600000" />
            </Field>
            <Field label={t('apply.step3.otherDebts')} htmlFor="f-deb">
              <CurrencyInput id="f-deb" value={e.otherDebts} currency={app.currency} lang={lang} onChange={v => set({ otherDebts: v })} placeholder="0" />
            </Field>
          </div>

          <Field label={t('apply.step3.housing')} required htmlFor="f-hou">
            <SelectInput id="f-hou" value={e.housing} onChange={v => set({ housing: v })}>
              <option value="" disabled>—</option>
              {housingOptions.map(([k, l]) => <option key={k} value={k}>{l}</option>)}
            </SelectInput>
          </Field>
        </div>
      </Card>
      <StepNav
        onBack={() => dispatch({ type: 'PREV_STEP' })}
        onNext={() => valid && dispatch({ type: 'NEXT_STEP' })}
        nextDisabled={!valid}
      />
    </div>
  );
}

// -----------------------------------------------------------------------------
// Step 4 — BankID stub
// -----------------------------------------------------------------------------

function Step4BankId({ app, dispatch }) {
  const [busy, setBusy] = React.useState(false);
  const [activeProvider, setActiveProvider] = React.useState(null);
  const verified = app.bankIdVerified;

  const verifyWith = (provider) => {
    if (busy || verified) return;
    setBusy(true);
    setActiveProvider(provider);
    setTimeout(() => {
      setBusy(false);
      dispatch({ type: 'SET_BANKID_VERIFIED', value: true });
    }, 1800);
  };

  // Primary provider is determined by country.
  const primary = (() => {
    switch (app.country) {
      case 'NO': return { id: 'BankID',  label: t('apply.step4.bankidCta') };
      case 'DK': return { id: 'MitID',   label: t('apply.step4.mitidCta') };
      case 'SE': return { id: 'BankID',  label: t('apply.step4.seBankidCta') };
      case 'FI': return { id: 'FTN',     label: t('apply.step4.ftnCta') };
      default:   return { id: 'BankID',  label: t('apply.step4.bankidCta') };
    }
  })();
  const others = [
    { id: 'BankID', label: t('apply.step4.bankidCta'), country: 'NO' },
    { id: 'MitID',  label: t('apply.step4.mitidCta'),  country: 'DK' },
    { id: 'BankID', label: t('apply.step4.seBankidCta'), country: 'SE' },
    { id: 'FTN',    label: t('apply.step4.ftnCta'),     country: 'FI' },
  ].filter(o => o.country !== app.country);

  return (
    <div>
      <StepHeader title={t('apply.step4.title')} subtitle={t('apply.step4.subtitle')} />

      <Card className="p-6 md:p-8">
        <BankIdButton
          label={busy && activeProvider === primary.id ? t('apply.step4.verifying', { provider: primary.id }) : (verified ? t('apply.step4.verified') : primary.label)}
          busy={busy && activeProvider === primary.id}
          success={verified}
          onClick={() => verifyWith(primary.id)}
          disabled={verified}
        />

        {verified && (
          <p className="mt-4 text-sm text-success font-medium flex items-center gap-2 nb-fade">
            <svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
              <circle cx="8" cy="8" r="7" stroke="currentColor" strokeWidth="1.5" fill="none"/>
              <path d="M5 8 L7.2 10.2 L11 6" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
            </svg>
            {t('apply.step4.verifiedHint')}
          </p>
        )}

        {!verified && (
          <div className="mt-7">
            <div className="flex items-center gap-3 text-xs uppercase tracking-wider text-navy-300 font-semibold">
              <span className="h-px flex-1 bg-navy-100" />
              {t('apply.step4.altLabel')}
              <span className="h-px flex-1 bg-navy-100" />
            </div>
            <div className="mt-4 grid sm:grid-cols-3 gap-3">
              {others.map((o, i) => (
                <button
                  key={i}
                  type="button"
                  onClick={() => verifyWith(o.id)}
                  disabled={busy}
                  className="relative flex items-center justify-center gap-2 px-3 py-3 rounded-xl border hairline bg-white hover:bg-surface text-sm font-medium text-navy-900 disabled:opacity-50 transition-colors"
                >
                  <span className="text-base" aria-hidden="true">{COUNTRY_META[o.country].flag}</span>
                  <span>{o.id}</span>
                  {busy && activeProvider === o.id && <Spinner color="navy" size={14}/>}
                </button>
              ))}
            </div>
          </div>
        )}
      </Card>

      <StepNav
        onBack={() => dispatch({ type: 'PREV_STEP' })}
        onNext={() => verified && dispatch({ type: 'NEXT_STEP' })}
        nextDisabled={!verified}
      />
    </div>
  );
}

// -----------------------------------------------------------------------------
// Step 5 — Offer + Insurance mock card (this is the Sherpa embed slot)
// -----------------------------------------------------------------------------

function Step5Offer({ app, dispatch, lang }) {
  const q = React.useMemo(() => quoteLoan(app.amount, app.termYears), [app.amount, app.termYears]);
  const monthly = q.monthly;

  return (
    <div>
      <StepHeader title={t('apply.step5.title')} subtitle={t('apply.step5.subtitle')} />

      {/* Offer summary card */}
      <Card className="p-6 md:p-8 relative overflow-hidden">
        <div className="absolute right-0 top-0 px-3 py-1 m-4 rounded-full bg-success/10 text-success text-[11px] font-semibold tracking-wider uppercase">
          {t('apply.step5.approvedBadge')}
        </div>
        <p className="text-xs font-semibold tracking-[0.18em] uppercase text-navy-500">
          {t('apply.step5.summaryAmount')}
        </p>
        <p className="mt-2 text-4xl md:text-5xl font-semibold tracking-tight text-navy-900 tnum">
          {formatMoney(app.amount, app.currency, lang)}
        </p>
        <dl className="mt-6 grid grid-cols-2 sm:grid-cols-4 gap-y-5 gap-x-4">
          <SummaryStat label={t('apply.step5.summaryTerm')} value={`${app.termYears} ${t('apply.step1.termYears',{n: app.termYears}).replace(/^[\d\s]*/,'').trim() || 'yr'}`}/>
          <SummaryStat label={t('apply.step5.summaryMonthly')} value={formatMoney(q.monthly, app.currency, lang, { fraction: 0 })}/>
          <SummaryStat label={t('apply.step5.summaryRate')} value={formatPercent(q.effectiveRate, lang, 1)}/>
          <SummaryStat label={t('apply.step5.summaryTotal')} value={formatMoney(q.totalPayable, app.currency, lang)}/>
        </dl>
      </Card>

      {/* Insurance section — Sherpa embed slot */}
      <section
        id="insurance-embed-slot"
        className="mt-4 rounded-2xl border hairline p-6 md:p-8 bg-white shadow-card"
      >
        {/*
          TODO: Sherpa integration mounts here.
          Replace <InsuranceMockCard/> with sherpa-embed iframe or Sherpa.js mount.
          Pass via URL params or postMessage:
            - loanAmount, termYears, monthlyInstalment
            - country, currency, lang
            - personal.{name, dob, ssn, email}
          Listen for embed event 'quote-selected' with { premium, productId, quoteId }.
          On event: dispatch({ type: 'SET_INSURANCE', selected: true, premium, quoteId, productId }).
        */}
        <InsuranceMockCard
          app={app}
          dispatch={dispatch}
          monthly={monthly}
          lang={lang}
        />
      </section>

      <StepNav
        onBack={() => dispatch({ type: 'PREV_STEP' })}
        onNext={() => dispatch({ type: 'NEXT_STEP' })}
      />
    </div>
  );
}

function SummaryStat({ label, value }) {
  return (
    <div>
      <dt className="text-xs text-navy-500">{label}</dt>
      <dd className="mt-1 text-base sm:text-lg font-semibold text-navy-900 tnum">{value}</dd>
    </div>
  );
}

function InsuranceMockCard({ app, dispatch, monthly, lang }) {
  const premium = insurancePremium(app.amount, app.termYears);
  const selected = app.insuranceSelected;
  const choose = (yes) => {
    dispatch({
      type: 'SET_INSURANCE',
      selected: yes,
      premium: yes ? premium : 0,
      quoteId: yes ? 'MOCK-' + Math.floor(Math.random() * 1e6).toString(36).toUpperCase() : null,
      productId: yes ? 'sherpa.loan-protection.v1' : null,
    });
  };

  return (
    <div>
      <div className="flex items-start justify-between gap-4">
        <div className="flex items-start gap-4">
          <div className="h-11 w-11 rounded-xl bg-teal-50 text-teal-700 flex items-center justify-center shrink-0">
            <svg width="22" height="22" viewBox="0 0 22 22" aria-hidden="true">
              <path d="M11 2 L18 5 V11 C18 15 11 19 11 19 C11 19 4 15 4 11 V5 Z" stroke="currentColor" strokeWidth="1.6" fill="none"/>
              <path d="M8 11 L10.4 13.4 L14.5 8.5" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
            </svg>
          </div>
          <div className="flex-1">
            <h3 className="text-lg font-semibold text-navy-900">{t('apply.step5.insuranceTitle')}</h3>
            <p className="mt-1 text-sm text-navy-500 leading-relaxed max-w-lg">
              {t('apply.step5.insuranceCopy')}
            </p>
          </div>
        </div>
      </div>

      <div className="mt-6 grid sm:grid-cols-2 gap-3">
        <InsuranceChoice
          checked={selected === true}
          onClick={() => choose(true)}
          title={t('apply.step5.addYes')}
          detail={`${t('apply.step5.insurancePremiumPrefix')}${formatMoney(premium, app.currency, lang, { fraction: 2 })} ${t('apply.step5.insurancePremiumSuffix')}`}
        />
        <InsuranceChoice
          checked={selected === false && app.insurancePremium === 0}
          onClick={() => choose(false)}
          title={t('apply.step5.addNo')}
          detail=""
        />
      </div>
    </div>
  );
}

function InsuranceChoice({ checked, onClick, title, detail }) {
  return (
    <button
      type="button"
      onClick={onClick}
      role="radio"
      aria-checked={checked}
      className={'text-left rounded-xl border-2 p-4 transition-all flex items-start gap-3 ' +
        (checked
          ? 'border-navy-900 bg-navy-50/40'
          : 'border-transparent hairline-border bg-white hover:bg-surface') +
        ' shadow-inset'}
      style={!checked ? { boxShadow: 'inset 0 0 0 1px rgba(11,37,69,0.08)' } : undefined}
    >
      <span className={'mt-0.5 h-5 w-5 rounded-full border-2 flex items-center justify-center shrink-0 ' +
        (checked ? 'border-navy-900' : 'border-navy-200')}>
        {checked && <span className="h-2.5 w-2.5 rounded-full bg-navy-900" />}
      </span>
      <span className="flex-1">
        <span className="block text-sm font-semibold text-navy-900">{title}</span>
        {detail && <span className="mt-0.5 block text-sm text-navy-500 tnum">{detail}</span>}
      </span>
    </button>
  );
}

// -----------------------------------------------------------------------------
// Step 6 — Review & sign
// -----------------------------------------------------------------------------

function Step6Sign({ app, dispatch, lang }) {
  const q = React.useMemo(() => quoteLoan(app.amount, app.termYears), [app.amount, app.termYears]);
  const [signing, setSigning] = React.useState(false);
  const sign = async () => {
    if (!app.termsAccepted || signing) return;
    setSigning(true);
    // Mimic eID signature latency.
    await new Promise(r => setTimeout(r, 1500));
    const confirmationNumber = makeConfirmationNumber();
    const snapshot = {
      ...app,
      signed: true,
      confirmationNumber,
    };
    try {
      const id = await dbSaveApplication(snapshot, q);
      dispatch({ type: 'SET_SIGNED_WITH', confirmationNumber, applicationId: id });
    } catch (e) {
      console.error('[nordbank] failed to save application to DB', e);
      // Still mark signed so the user isn't stuck.
      dispatch({ type: 'SET_SIGNED', value: true, confirmationNumber });
    }
  };
  const signLabel = (() => {
    switch (app.country) {
      case 'DK': return t('apply.step6.sign').replace('BankID','MitID').replace('FTN','MitID');
      case 'FI': return t('apply.step6.sign').replace('BankID','FTN').replace('MitID','FTN');
      default:   return t('apply.step6.sign');
    }
  })();

  return (
    <div>
      <StepHeader title={t('apply.step6.title')} subtitle={t('apply.step6.subtitle')} />

      <Card className="p-6 md:p-8">
        <h3 className="text-xs font-semibold tracking-[0.18em] uppercase text-navy-500">{t('apply.step6.loanSection')}</h3>
        <dl className="mt-4 divide-y hairline">
          <SummaryRow label={t('apply.step5.summaryAmount')} value={formatMoney(app.amount, app.currency, lang)}/>
          <SummaryRow label={t('apply.step5.summaryTerm')} value={`${app.termYears} ${t('apply.step1.termYears',{n: app.termYears}).replace(/^[\d\s]*/,'').trim() || 'yr'}`}/>
          <SummaryRow label={t('apply.step5.summaryMonthly')} value={formatMoney(q.monthly, app.currency, lang, { fraction: 0 })}/>
          <SummaryRow label={t('apply.step5.summaryRate')} value={formatPercent(q.effectiveRate, lang, 1)}/>
          <SummaryRow label={t('apply.step5.summaryTotal')} value={formatMoney(q.totalPayable, app.currency, lang)}/>
        </dl>

        <h3 className="mt-7 text-xs font-semibold tracking-[0.18em] uppercase text-navy-500">{t('apply.step6.insuranceSection')}</h3>
        <dl className="mt-4 divide-y hairline">
          {app.insuranceSelected ? (
            <>
              <SummaryRow label={t('apply.step5.insuranceTitle')} value={`+${formatMoney(app.insurancePremium, app.currency, lang, { fraction: 2 })}${t('apply.step5.insurancePremiumSuffix')}`}/>
            </>
          ) : (
            <SummaryRow label={t('apply.step5.insuranceTitle')} value={t('apply.step6.insuranceNone')} subtle/>
          )}
        </dl>

        <label className="mt-7 flex items-start gap-3 cursor-pointer">
          <input
            type="checkbox"
            checked={app.termsAccepted}
            onChange={(e) => dispatch({ type: 'SET_TERMS', value: e.target.checked })}
            className="mt-0.5 h-4.5 w-4.5 rounded border-navy-200 accent-navy-900"
            style={{ accentColor: '#0B2545' }}
          />
          <span className="text-sm text-navy-700 leading-relaxed">{t('apply.step6.terms')}</span>
        </label>
      </Card>

      <div className="mt-10 flex flex-col-reverse sm:flex-row sm:items-center gap-3">
        <button
          type="button"
          onClick={() => dispatch({ type: 'PREV_STEP' })}
          className="inline-flex items-center justify-center gap-2 px-5 py-3 text-sm font-semibold text-navy-700 hover:text-navy-900 hover:bg-navy-50 rounded-full transition-colors"
        >
          <span aria-hidden="true">←</span>
          {t('common.back')}
        </button>
        <div className="flex-1" />
        <BankIdButton
          label={signing ? t('apply.step6.signing') : signLabel}
          busy={signing}
          onClick={sign}
          disabled={!app.termsAccepted}
        />
      </div>
    </div>
  );
}

function SummaryRow({ label, value, subtle }) {
  return (
    <div className="flex items-baseline justify-between py-3">
      <dt className={'text-sm ' + (subtle ? 'text-navy-500' : 'text-navy-700')}>{label}</dt>
      <dd className="text-sm font-semibold text-navy-900 tnum">{value}</dd>
    </div>
  );
}

// -----------------------------------------------------------------------------
// Step 7 — Sherpa insurance checkout. Only entered when insurance was selected.
//   1. POST /v1/policy/embed-checkout with the wizard's data.
//   2. Mount the embed iframe at the returned URL.
//   3. Listen for postMessage events from the embed:
//        SHERPA_PAYMENT_SUCCESS → SET_INSURANCE_PAID → continue to account setup
//        SHERPA_PAYMENT_FAILED  → show retry / skip controls
//        SHERPA_HEIGHT_CHANGE   → resize iframe to fit content
//   4. When the UI language changes, postMessage SET_CONTEXT with the new
//      country/displayCurrency.
// -----------------------------------------------------------------------------

function Step7Insurance({ app, dispatch }) {
  const lang = useLang();
  const iframeRef = React.useRef(null);
  const [status, setStatus] = React.useState('loading'); // loading | ready | failed
  const [retryNonce, setRetryNonce] = React.useState(0);
  const [session, setSession] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [iframeHeight, setIframeHeight] = React.useState(700);

  // 1. Create the checkout session on mount (and on retry).
  React.useEffect(() => {
    let cancelled = false;
    setStatus('loading');
    setError(null);
    (async () => {
      try {
        const sess = await createSherpaCheckout(app, app.insurancePremium);
        if (cancelled) return;
        setSession(sess);
        dispatch({
          type: 'SET_INSURANCE_CHECKOUT',
          embedCheckoutSessionId: sess.embedCheckoutSessionId,
          policyId: sess.policyId,
        });
        setStatus('ready');
      } catch (e) {
        if (cancelled) return;
        console.error('[sherpa] create checkout failed', e);
        setError(e.message || String(e));
        setStatus('failed');
      }
    })();
    return () => { cancelled = true; };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [retryNonce]);

  // 2. Listen for postMessage events from the embed.
  React.useEffect(() => {
    const handler = (e) => {
      if (e.origin !== SHERPA_EMBED_ORIGIN) return;
      if (iframeRef.current && e.source !== iframeRef.current.contentWindow) return;
      const data = e.data;
      if (!data || typeof data !== 'object') return;
      if (data.type === 'SHERPA_PAYMENT_SUCCESS') {
        dispatch({ type: 'SET_INSURANCE_PAID', policyId: data.policyId });
      } else if (data.type === 'SHERPA_PAYMENT_FAILED') {
        console.error('[sherpa] payment failed:', data.reason);
        setError(data.reason || 'Payment failed');
        setStatus('failed');
      } else if (data.type === 'SHERPA_HEIGHT_CHANGE' && typeof data.px === 'number') {
        setIframeHeight(Math.max(300, data.px));
      }
    };
    window.addEventListener('message', handler);
    return () => window.removeEventListener('message', handler);
  }, [dispatch]);

  // 3. When language changes, push the new country/displayCurrency to the embed.
  React.useEffect(() => {
    if (status !== 'ready') return;
    const ctx = sherpaContextForLang(lang);
    const iframe = iframeRef.current;
    if (!iframe || !iframe.contentWindow) return;
    const id = setTimeout(() => {
      try {
        iframe.contentWindow.postMessage(
          { source: 'sherpa-host', type: 'SET_CONTEXT', payload: ctx },
          SHERPA_EMBED_ORIGIN
        );
      } catch (e) { /* iframe might still be navigating */ }
    }, 200);
    return () => clearTimeout(id);
  }, [lang, status, session]);

  // Expose updateSherpaContext globally so the host page can call it.
  React.useEffect(() => {
    window.updateSherpaContext = function (country, displayCurrency) {
      const iframe = iframeRef.current;
      if (!iframe || !iframe.contentWindow) return;
      iframe.contentWindow.postMessage(
        { source: 'sherpa-host', type: 'SET_CONTEXT', payload: { country, displayCurrency } },
        SHERPA_EMBED_ORIGIN
      );
    };
  }, []);

  const skip = () => dispatch({
    type: 'SET_INSURANCE',
    selected: false,
    premium: 0,
    quoteId: null,
    productId: null,
  });

  return (
    <div className="max-w-3xl mx-auto px-4 md:px-6 py-10 md:py-14">
      <ProgressBar step={7} total={7} />
      <div className="nb-fade mt-8">
        <StepHeader title={t('apply.step7.title')} subtitle={t('apply.step7.subtitle')} />

        {status === 'loading' && (
          <Card className="p-10 flex flex-col items-center justify-center text-center">
            <Spinner color="navy" size={28} />
            <p className="mt-4 text-navy-500 text-sm">{t('apply.step7.loading')}</p>
          </Card>
        )}

        {status === 'failed' && (
          <Card className="p-8 text-center">
            <div className="mx-auto h-12 w-12 rounded-xl bg-danger/10 text-danger flex items-center justify-center">
              <svg width="22" height="22" viewBox="0 0 22 22" aria-hidden="true">
                <circle cx="11" cy="11" r="9" stroke="currentColor" strokeWidth="1.6" fill="none"/>
                <path d="M8 8 L14 14 M14 8 L8 14" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
              </svg>
            </div>
            <p className="mt-4 text-navy-700 text-sm">{t('apply.step7.error')}</p>
            {error && <p className="mt-2 text-xs text-navy-300 font-mono break-all">{error}</p>}
            <div className="mt-6 flex flex-col sm:flex-row justify-center gap-3">
              <button
                type="button"
                onClick={() => setRetryNonce(n => n + 1)}
                className="inline-flex items-center justify-center gap-2 bg-navy-900 hover:bg-navy-700 text-white text-sm font-semibold px-6 py-3 rounded-full transition-colors"
              >
                {t('apply.step7.retry')}
              </button>
              <button
                type="button"
                onClick={skip}
                className="text-sm font-semibold text-navy-700 hover:text-navy-900 px-3 py-3"
              >
                {t('apply.step7.skip')}
              </button>
            </div>
          </Card>
        )}

        {status === 'ready' && session && (
          <Card className="p-2 sm:p-3 overflow-hidden">
            <iframe
              ref={iframeRef}
              id="sherpa-checkout-iframe"
              title="sherpa-checkout"
              src={SHERPA_EMBED_ORIGIN + '/checkout/' + session.embedCheckoutSessionId}
              width="100%"
              height={iframeHeight}
              style={{ border: 0, display: 'block', borderRadius: 12 }}
              allow="payment"
            />
          </Card>
        )}

        <p className="mt-4 text-[11px] text-navy-300 text-center flex items-center justify-center gap-1.5">
          <svg width="11" height="11" viewBox="0 0 12 12" aria-hidden="true">
            <path d="M3 5 V4 A3 3 0 0 1 9 4 V5 M2 5 H10 V10 H2 Z" stroke="currentColor" strokeWidth="1" fill="none" strokeLinejoin="round"/>
          </svg>
          {t('apply.step7.poweredBy')}
        </p>

        {status !== 'failed' && (
          <div className="mt-6 text-center">
            <button
              type="button"
              onClick={skip}
              className="text-xs text-navy-300 hover:text-navy-500 underline-offset-2 hover:underline"
            >
              {t('apply.step7.skip')}
            </button>
          </div>
        )}
      </div>
    </div>
  );
}

function AccountSetup({ app, dispatch }) {
  const lang = useLang();
  const q = React.useMemo(() => quoteLoan(app.amount, app.termYears), [app.amount, app.termYears]);
  const [password, setPassword] = React.useState('');
  const [confirm, setConfirm] = React.useState('');
  const [show, setShow] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState('');
  const strength = passwordStrength(password);

  const passwordOk = password.length >= 8;
  const confirmOk  = confirm && confirm === password;
  const canSubmit  = passwordOk && confirmOk && !busy && app.personal.email;

  async function submit(e) {
    e.preventDefault();
    if (!canSubmit) return;
    setError('');
    setBusy(true);
    try {
      const user = await registerUser(app.personal.email, password);
      if (app.applicationId) {
        await dbLinkApplicationToUser(app.applicationId, user.id);
      }
      dispatch({ type: 'SET_ACCOUNT_CREATED', value: true });
    } catch (err) {
      if (err && err.message === 'email-in-use') {
        // Account already exists for this email — try to log them in instead.
        try {
          const u = await loginUser(app.personal.email, password);
          if (app.applicationId) await dbLinkApplicationToUser(app.applicationId, u.id);
          dispatch({ type: 'SET_ACCOUNT_CREATED', value: true });
          return;
        } catch (e2) {
          setError(t('auth.login.error'));
        }
      } else {
        console.error(err);
        setError(t('auth.login.error'));
      }
    } finally {
      setBusy(false);
    }
  }

  return (
    <div className="nb-fade max-w-2xl mx-auto px-4 md:px-6 py-14 md:py-20">
      {/* Celebratory header */}
      <div className="text-center">
        <div className="mx-auto h-14 w-14 rounded-full bg-success/10 text-success flex items-center justify-center">
          <svg width="28" height="28" viewBox="0 0 32 32" aria-hidden="true">
            <circle cx="16" cy="16" r="14" stroke="currentColor" strokeOpacity="0.2" strokeWidth="2" fill="none"/>
            <path d="M10 16 L14.5 20.5 L23 12" stroke="currentColor" strokeWidth="3" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </div>
        <h1 className="mt-5 text-3xl md:text-4xl font-semibold tracking-tight text-navy-900" style={{ textWrap: 'balance' }}>
          {t('apply.success.title')}
        </h1>
        <p className="mt-3 text-base text-navy-500 leading-relaxed max-w-md mx-auto" style={{ textWrap: 'pretty' }}>
          {t('apply.success.body')}
        </p>
      </div>

      {/* Loan summary */}
      <Card className="mt-8 p-6">
        <dl className="divide-y hairline">
          <SummaryRow label={t('apply.success.refLabel')} value={app.confirmationNumber || '—'} />
          <SummaryRow label={t('apply.step5.summaryAmount')} value={formatMoney(app.amount, app.currency, lang)} />
          <SummaryRow label={t('apply.step5.summaryMonthly')}
            value={formatMoney(q.monthly, app.currency, lang, { fraction: 0 })} />
          {app.insuranceSelected && (
            <SummaryRow label={t('apply.step5.insuranceTitle')}
              value={`+${formatMoney(app.insurancePremium, app.currency, lang, { fraction: 2 })}${t('apply.step5.insurancePremiumSuffix')}`} />
          )}
        </dl>
      </Card>

      {/* Password creation */}
      <div className="mt-8">
        <h2 className="text-xl font-semibold text-navy-900">{t('auth.createPassword.title')}</h2>
        <p className="mt-2 text-sm text-navy-500 leading-relaxed">{t('auth.createPassword.subtitle')}</p>
      </div>

      <Card className="mt-4 p-6 md:p-8">
        <form onSubmit={submit} className="space-y-5">
          <Field label={t('apply.step2.email')} htmlFor="acct-email">
            <TextInput id="acct-email" type="email" value={app.personal.email} onChange={()=>{}} disabled readOnly />
          </Field>

          <div>
            <Field label={t('auth.createPassword.password')} required htmlFor="acct-pw">
              <div className="relative">
                <input
                  id="acct-pw"
                  type={show ? 'text' : 'password'}
                  value={password}
                  onChange={e => setPassword(e.target.value)}
                  autoComplete="new-password"
                  className="w-full rounded-xl border hairline bg-white pl-3.5 pr-16 py-2.5 text-sm text-navy-900 shadow-inset focus:border-teal-500 transition-colors"
                />
                <button type="button" onClick={() => setShow(s => !s)} className="absolute right-2 top-1/2 -translate-y-1/2 text-xs font-semibold text-navy-500 hover:text-navy-900 px-2 py-1 rounded">
                  {show ? t('auth.createPassword.hide') : t('auth.createPassword.show')}
                </button>
              </div>
            </Field>
            <PasswordStrengthMeter value={password} strength={strength}/>
            {password && !passwordOk && (
              <p className="mt-1 text-xs text-danger">{t('auth.createPassword.tooShort')}</p>
            )}
          </div>

          <Field label={t('auth.createPassword.confirm')} required htmlFor="acct-pw2"
            error={confirm && !confirmOk ? t('auth.createPassword.mismatch') : ''}>
            <input
              id="acct-pw2"
              type={show ? 'text' : 'password'}
              value={confirm}
              onChange={e => setConfirm(e.target.value)}
              autoComplete="new-password"
              className="w-full rounded-xl border hairline bg-white px-3.5 py-2.5 text-sm text-navy-900 shadow-inset focus:border-teal-500 transition-colors"
            />
          </Field>

          {error && <p className="text-sm text-danger">{error}</p>}

          <button
            type="submit"
            disabled={!canSubmit}
            className="w-full bg-navy-900 hover:bg-navy-700 text-white text-sm font-semibold py-3 rounded-full transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
          >
            {busy && <Spinner />}
            {busy ? t('auth.createPassword.busy') : t('auth.createPassword.cta')}
          </button>

          <p className="text-[11px] text-navy-300 leading-relaxed text-center">
            <svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true" className="inline-block mr-1 align-[-1px]">
              <path d="M3 5 V4 A3 3 0 0 1 9 4 V5 M2 5 H10 V10 H2 Z" stroke="currentColor" strokeWidth="1" fill="none" strokeLinejoin="round"/>
            </svg>
            {t('auth.createPassword.hint')}
          </p>
        </form>
      </Card>
    </div>
  );
}

function PasswordStrengthMeter({ value, strength }) {
  const segments = [0,1,2,3];
  const labels = ['auth.createPassword.strength.0','auth.createPassword.strength.1','auth.createPassword.strength.2','auth.createPassword.strength.3','auth.createPassword.strength.4'];
  const colors  = ['#D64545','#D64545','#E08A2F','#1F9D6C','#1F9D6C'];
  if (!value) return null;
  return (
    <div className="mt-2">
      <div className="flex gap-1" aria-hidden="true">
        {segments.map(i => (
          <span key={i} className="h-1 flex-1 rounded-full"
            style={{ background: i < strength ? colors[strength] : 'rgba(11,37,69,0.10)' }} />
        ))}
      </div>
      <p className="mt-1 text-xs tnum" style={{ color: colors[strength] }}>
        {t(labels[strength])}
      </p>
    </div>
  );
}


Object.assign(window, {
  Apply, ApplyWizard, Step1Country, Step2Personal, Step3Employment,
  Step4BankId, Step5Offer, Step6Sign, Step7Insurance, AccountSetup, InsuranceMockCard,
});
