// Nordbank — Customer Portal. Sidebar nav + sub-pages:
//   /portal              → Overview (default redirect target after login)
//   /portal/loans        → My loans
//   /portal/payments     → Payments
//   /portal/insurance    → Loan-protection policies + Sherpa embed slot
//   /portal/documents    → Documents
//   /portal/profile      → Profile
//   /portal/settings     → Settings
//
// Auth-gated by app.jsx — anonymous users are redirected to /login.

// ---------------------------------------------------------------------------
// Sidebar layout
// ---------------------------------------------------------------------------

const PORTAL_NAV = [
  { to: '/portal',           key: 'overview',   icon: 'overview'   },
  { to: '/portal/loans',     key: 'loans',      icon: 'loans'      },
  { to: '/portal/payments',  key: 'payments',   icon: 'payments'   },
  { to: '/portal/insurance', key: 'insurance',  icon: 'insurance'  },
  { to: '/portal/documents', key: 'documents',  icon: 'documents'  },
  { to: '/portal/profile',   key: 'profile',    icon: 'profile'    },
  { to: '/portal/settings',  key: 'settings',   icon: 'settings'   },
];

function PortalIcon({ kind }) {
  const props = { width: 18, height: 18, viewBox: '0 0 20 20', fill: 'none', 'aria-hidden': true };
  switch (kind) {
    case 'overview':  return <svg {...props}><rect x="3" y="3" width="6" height="6" rx="1.5" stroke="currentColor" strokeWidth="1.5"/><rect x="11" y="3" width="6" height="6" rx="1.5" stroke="currentColor" strokeWidth="1.5"/><rect x="3" y="11" width="6" height="6" rx="1.5" stroke="currentColor" strokeWidth="1.5"/><rect x="11" y="11" width="6" height="6" rx="1.5" stroke="currentColor" strokeWidth="1.5"/></svg>;
    case 'loans':     return <svg {...props}><rect x="2.5" y="5" width="15" height="11" rx="2" stroke="currentColor" strokeWidth="1.5"/><path d="M2.5 9 H17.5" stroke="currentColor" strokeWidth="1.5"/><circle cx="13.5" cy="12.5" r="1" fill="currentColor"/></svg>;
    case 'payments':  return <svg {...props}><path d="M3 6 H17 M7 3 V6 M13 14 V17 M3 14 H17 M14 6 L17 9 L14 12 M6 14 L3 11 L6 8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>;
    case 'insurance': return <svg {...props}><path d="M10 2 L17 5 V11 C17 15 10 18 10 18 C10 18 3 15 3 11 V5 Z" stroke="currentColor" strokeWidth="1.5"/><path d="M7 10 L9.5 12.5 L13.5 8" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>;
    case 'documents': return <svg {...props}><path d="M5 2 H12 L16 6 V17 A1 1 0 0 1 15 18 H5 A1 1 0 0 1 4 17 V3 A1 1 0 0 1 5 2 Z" stroke="currentColor" strokeWidth="1.5"/><path d="M12 2 V6 H16" stroke="currentColor" strokeWidth="1.5"/><path d="M7 10 H13 M7 13 H13" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/></svg>;
    case 'profile':   return <svg {...props}><circle cx="10" cy="7" r="3" stroke="currentColor" strokeWidth="1.5"/><path d="M3 17 C3 13.5 6.5 12 10 12 C13.5 12 17 13.5 17 17" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>;
    case 'settings':  return <svg {...props}><circle cx="10" cy="10" r="2.5" stroke="currentColor" strokeWidth="1.5"/><path d="M10 2 L10 4 M10 16 L10 18 M2 10 L4 10 M16 10 L18 10 M4.3 4.3 L5.7 5.7 M14.3 14.3 L15.7 15.7 M4.3 15.7 L5.7 14.3 M14.3 5.7 L15.7 4.3" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/></svg>;
    default: return null;
  }
}

function PortalLayout({ children }) {
  const user = useCurrentUser();
  const route = useRoute();
  const [mobileOpen, setMobileOpen] = React.useState(false);
  React.useEffect(() => { setMobileOpen(false); }, [route]);
  useScrollTop();

  if (!user) return null; // app.jsx gates this

  const sidebar = (
    <aside className="flex flex-col h-full" aria-label="Portal navigation">
      <div className="px-5 py-5 border-b hairline">
        <RouterLink to="/" className="inline-flex"><NordbankLogo /></RouterLink>
        <p className="mt-3 text-[10px] font-semibold tracking-[0.18em] uppercase text-navy-300">
          {t('nav.portal')}
        </p>
      </div>
      <nav className="flex-1 px-3 py-4 space-y-0.5 overflow-y-auto">
        {PORTAL_NAV.map(item => {
          const active = item.to === '/portal'
            ? route === '/portal' || route === '/portal/'
            : route === item.to || route.startsWith(item.to + '/');
          return (
            <RouterLink
              key={item.key}
              to={item.to}
              className={'group flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors ' +
                (active
                  ? 'bg-navy-900 text-white'
                  : 'text-navy-700 hover:bg-navy-50')}
            >
              <span className={active ? 'text-white' : 'text-navy-500 group-hover:text-navy-900'}>
                <PortalIcon kind={item.icon}/>
              </span>
              <span>{t('portal.nav.' + item.key)}</span>
            </RouterLink>
          );
        })}
      </nav>
      <div className="px-5 py-4 border-t hairline">
        <p className="text-[10px] font-semibold tracking-[0.14em] uppercase text-navy-300">
          {t('portal.signedInAs')}
        </p>
        <p className="mt-1 text-sm text-navy-900 truncate" title={user.email}>{user.email}</p>
        <button
          type="button"
          onClick={() => { logoutUser(); navigate('/'); }}
          className="mt-3 inline-flex items-center gap-2 text-sm font-semibold text-navy-700 hover:text-danger transition-colors"
        >
          <svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true">
            <path d="M9 4 V2 H2 V12 H9 V10" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
            <path d="M5 7 H13 M10 4 L13 7 L10 10" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
          {t('portal.signOut')}
        </button>
      </div>
    </aside>
  );

  return (
    <div className="min-h-screen flex flex-col bg-surface">
      {/* Mobile topbar */}
      <header className="md:hidden sticky top-0 z-40 bg-white/90 backdrop-blur border-b hairline">
        <div className="flex items-center justify-between h-16 px-4">
          <RouterLink to="/portal" className="shrink-0"><NordbankLogo /></RouterLink>
          <div className="flex items-center gap-2">
            <LangSwitcher />
            <button
              type="button"
              onClick={() => setMobileOpen(o => !o)}
              className="p-2 rounded-lg text-navy-900 hover:bg-navy-50"
              aria-label="Toggle navigation"
              aria-expanded={mobileOpen}
            >
              <svg width="22" height="22" viewBox="0 0 22 22"><path d="M4 7 H18 M4 11 H18 M4 15 H18" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/></svg>
            </button>
          </div>
        </div>
        {mobileOpen && (
          <div className="border-t hairline bg-white py-2">
            {sidebar}
          </div>
        )}
      </header>

      <div className="flex-1 md:grid md:grid-cols-[260px_1fr]">
        {/* Desktop sidebar */}
        <div className="hidden md:block bg-white border-r hairline">
          <div className="sticky top-0 h-screen flex flex-col">
            {sidebar}
          </div>
        </div>

        {/* Main */}
        <div>
          {/* Desktop topbar */}
          <div className="hidden md:flex items-center justify-end h-16 px-8 bg-white border-b hairline">
            <LangSwitcher />
          </div>
          <main className="px-4 md:px-8 py-8 md:py-10 max-w-5xl">
            {children}
          </main>
        </div>
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Data hook: loads the logged-in user's applications from SQLite.
// ---------------------------------------------------------------------------

function usePortalData() {
  const user = useCurrentUser();
  const [rows, setRows] = React.useState(null);
  const [refresh, setRefresh] = React.useState(0);
  React.useEffect(() => {
    if (!user) { setRows([]); return; }
    let cancelled = false;
    dbListApplicationsForUser(user.id).then(r => { if (!cancelled) setRows(r); });
    return () => { cancelled = true; };
  }, [user && user.id, refresh]);
  return { rows: rows || [], loading: rows === null, refresh: () => setRefresh(x => x + 1) };
}

// Convenience: shorthand for the "primary" loan (newest).
function primaryLoan(rows) { return rows && rows[0]; }

// Friendly "next payment" date — first of next month.
function nextPaymentDate(signedAt) {
  const d = signedAt ? new Date(signedAt) : new Date();
  return new Date(d.getFullYear(), d.getMonth() + 1, 1);
}
function payoffDate(signedAt, termYears) {
  const d = signedAt ? new Date(signedAt) : new Date();
  return new Date(d.getFullYear() + (termYears || 0), d.getMonth() + 1, 1);
}
function formatDate(date, lang) {
  try {
    return new Intl.DateTimeFormat(localeFor(lang), { day: 'numeric', month: 'short', year: 'numeric' }).format(date);
  } catch (e) { return date.toISOString().slice(0, 10); }
}

// ---------------------------------------------------------------------------
// Page: Overview
// ---------------------------------------------------------------------------

function PortalOverview() {
  const lang = useLang();
  const user = useCurrentUser();
  const { rows, loading } = usePortalData();
  if (loading) return <PortalLoading/>;

  const loan = primaryLoan(rows);
  const greetingName = (loan && loan.data && loan.data.personal && loan.data.personal.name && loan.data.personal.name.split(' ')[0]) ||
    (user && user.email.split('@')[0]);

  return (
    <div className="nb-fade">
      <h1 className="text-3xl md:text-4xl font-semibold tracking-tight text-navy-900" style={{ textWrap: 'balance' }}>
        {t('portal.greeting', { name: greetingName })}
      </h1>
      <p className="mt-2 text-navy-500">{t('portal.subgreeting')}</p>

      {!loan ? (
        <EmptyLoanState />
      ) : (
        <>
          <div className="mt-8 grid sm:grid-cols-2 gap-4">
            <StatCard
              label={t('portal.overview.totalBorrowed')}
              value={formatMoney(loan.amount, loan.currency, lang)}
              hint={`${t('portal.overview.contractNo')} ${loan.confirmation_number}`}
            />
            <StatCard
              label={t('portal.overview.monthlyPayment')}
              value={formatMoney(loan.monthly, loan.currency, lang, { fraction: 0 })}
              hint={t('portal.overview.dueOn', { date: formatDate(nextPaymentDate(loan.signed_at), lang) })}
            />
          </div>

          <div className="mt-8 grid lg:grid-cols-3 gap-4">
            {/* Loan summary card */}
            <Card className="p-6 lg:col-span-2">
              <div className="flex items-baseline justify-between gap-3">
                <h2 className="text-lg font-semibold text-navy-900">{t('portal.loans.title')}</h2>
                <RouterLink to="/portal/loans" className="text-sm font-semibold text-navy-700 hover:text-teal-700">
                  {t('portal.loans.viewContract')} →
                </RouterLink>
              </div>
              <LoanCardRow loan={loan} lang={lang}/>
            </Card>

            {/* Recent activity */}
            <Card className="p-6">
              <h2 className="text-lg font-semibold text-navy-900">{t('portal.overview.recent')}</h2>
              <ol className="mt-4 space-y-4">
                <ActivityItem
                  date={formatDate(new Date(loan.signed_at), lang)}
                  title={t('portal.overview.act.signed')}
                  detail={`${t('apply.success.refLabel')}: ${loan.confirmation_number}`}
                  dot="success"
                />
                <ActivityItem
                  date={formatDate(new Date(loan.signed_at + 8 * 3600 * 1000), lang)}
                  title={t('portal.overview.act.disbursing')}
                  detail={formatMoney(loan.amount, loan.currency, lang)}
                  dot="info"
                />
                <ActivityItem
                  date={formatDate(new Date(loan.signed_at - 60 * 1000), lang)}
                  title={t('portal.overview.act.welcome')}
                  detail={user.email}
                  dot="muted"
                />
              </ol>
            </Card>
          </div>
        </>
      )}
    </div>
  );
}

function StatCard({ label, value, hint }) {
  return (
    <Card className="p-6">
      <p className="text-xs font-semibold tracking-[0.14em] uppercase text-navy-500">{label}</p>
      <p className="mt-2 text-3xl md:text-4xl font-semibold tnum text-navy-900">{value}</p>
      {hint && <p className="mt-2 text-xs text-navy-500 tnum">{hint}</p>}
    </Card>
  );
}

function ActivityItem({ date, title, detail, dot }) {
  const color = dot === 'success' ? 'bg-success' : dot === 'info' ? 'bg-teal-500' : 'bg-navy-200';
  return (
    <li className="flex gap-3">
      <span className={'mt-1.5 h-2 w-2 rounded-full shrink-0 ' + color} aria-hidden="true"/>
      <div className="min-w-0 flex-1">
        <p className="text-sm font-semibold text-navy-900">{title}</p>
        <p className="mt-0.5 text-xs text-navy-500 truncate">{detail}</p>
        <p className="mt-0.5 text-[11px] text-navy-300 tnum">{date}</p>
      </div>
    </li>
  );
}

function LoanCardRow({ loan, lang }) {
  return (
    <div className="mt-4 grid sm:grid-cols-2 gap-x-6 gap-y-3">
      <Detail label={t('apply.step5.summaryAmount')} value={formatMoney(loan.amount, loan.currency, lang)} />
      <Detail label={t('apply.step5.summaryTerm')} value={`${loan.term_years} ${t('apply.step1.termYears',{n:loan.term_years}).replace(/^[\d\s]*/,'').trim() || 'yr'}`} />
      <Detail label={t('apply.step5.summaryMonthly')} value={formatMoney(loan.monthly, loan.currency, lang, { fraction: 0 })} />
      <Detail label={t('apply.step5.summaryRate')} value={formatPercent(loan.effective_rate, lang, 1)} />
      <Detail label={t('portal.overview.payoffDate')} value={formatDate(payoffDate(loan.signed_at, loan.term_years), lang)} />
      <Detail label={t('portal.loans.statusActive')} value={<StatusPill kind="active" label={t('portal.loans.statusPending')}/>} valueIsNode/>
    </div>
  );
}

function Detail({ label, value, valueIsNode }) {
  return (
    <div>
      <dt className="text-xs text-navy-500">{label}</dt>
      <dd className="mt-0.5 text-sm font-semibold text-navy-900 tnum">{valueIsNode ? value : value}</dd>
    </div>
  );
}

function StatusPill({ kind = 'active', label }) {
  const cls = kind === 'active'
    ? 'bg-success/10 text-success'
    : kind === 'pending'
      ? 'bg-teal-50 text-teal-700'
      : 'bg-navy-50 text-navy-500';
  return (
    <span className={'inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md text-[11px] font-semibold tracking-wider uppercase ' + cls}>
      <span className="h-1.5 w-1.5 rounded-full bg-current" aria-hidden="true"/>
      {label}
    </span>
  );
}

function EmptyLoanState() {
  return (
    <Card className="mt-8 p-10 text-center">
      <div className="mx-auto h-12 w-12 rounded-xl bg-navy-50 text-navy-500 flex items-center justify-center">
        <svg width="22" height="22" viewBox="0 0 22 22" aria-hidden="true"><rect x="2.5" y="5" width="17" height="13" rx="2.5" stroke="currentColor" strokeWidth="1.6"/><path d="M2.5 9 H19.5" stroke="currentColor" strokeWidth="1.6"/></svg>
      </div>
      <p className="mt-4 text-navy-700">{t('portal.overview.noLoans')}</p>
      <RouterLink to="/apply" className="mt-5 inline-flex items-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('portal.overview.applyCta')}
      </RouterLink>
    </Card>
  );
}

function PortalLoading() {
  return (
    <div className="py-20 flex items-center justify-center text-navy-500">
      <Spinner color="navy" size={28}/>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Page: My loans
// ---------------------------------------------------------------------------

function PortalLoans() {
  const lang = useLang();
  const { rows, loading } = usePortalData();
  if (loading) return <PortalLoading/>;

  return (
    <div className="nb-fade">
      <PageHeader title={t('portal.loans.title')} />
      {rows.length === 0 ? (
        <EmptyLoanState />
      ) : (
        <div className="mt-8 space-y-4">
          {rows.map(loan => (
            <Card key={loan.id} className="p-6">
              <div className="flex items-start justify-between flex-wrap gap-3">
                <div>
                  <p className="text-xs font-semibold tracking-[0.14em] uppercase text-navy-500">{t('portal.overview.contractNo')}</p>
                  <p className="mt-1 text-base text-navy-900 font-semibold tnum">{loan.confirmation_number}</p>
                </div>
                <StatusPill kind="pending" label={t('portal.loans.statusPending')}/>
              </div>
              <LoanCardRow loan={loan} lang={lang}/>
              <div className="mt-6 flex gap-3">
                <button className="text-sm font-semibold text-navy-900 hover:text-teal-700">{t('portal.loans.viewContract')} →</button>
                <span className="text-navy-200">·</span>
                <button className="text-sm font-semibold text-navy-700 hover:text-navy-900">{t('portal.loans.payEarly')}</button>
              </div>
            </Card>
          ))}
        </div>
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Page: Payments — full amortisation schedule, paid/upcoming distinction,
// hero "next payment" card, top-line stats, payment history.
// ---------------------------------------------------------------------------

function PortalPayments() {
  const lang = useLang();
  const { rows, loading } = usePortalData();
  const [showAll, setShowAll] = React.useState(false);
  const loan = primaryLoan(rows);

  // For each month i:
  //   interest_i  = balance * (nominal / 12)
  //   principal_i = monthly - interest_i
  //   balance_i+1 = balance - principal_i
  const schedule = React.useMemo(() => {
    if (!loan) return [];
    const months = Math.max(1, (loan.term_years || 1) * 12);
    const nominal = NOMINAL_RATE;
    const monthlyRate = nominal / 12;
    let balance = loan.amount;
    const now = Date.now();
    const out = [];
    for (let i = 0; i < months; i++) {
      const interest = balance * monthlyRate;
      const principal = Math.max(0, loan.monthly - interest);
      balance = Math.max(0, balance - principal);
      const due = new Date(loan.signed_at);
      due.setMonth(due.getMonth() + i + 1);
      due.setDate(1);
      let status = 'upcoming';
      if (due.getTime() < now) status = 'paid';
      else if (due.getTime() - now < 14 * 24 * 3600 * 1000) status = 'due';
      out.push({ n: i + 1, due, amount: loan.monthly, principal, interest, balance, status });
    }
    return out;
  }, [loan]);

  if (loading) return <PortalLoading/>;

  if (!loan) {
    return (
      <div className="nb-fade">
        <PageHeader title={t('portal.payments.title')} />
        <EmptyLoanState/>
      </div>
    );
  }

  // Derived stats.
  const paidRows  = schedule.filter(r => r.status === 'paid');
  const totalPaid = paidRows.reduce((s, r) => s + r.amount, 0);
  const principalPaid = paidRows.reduce((s, r) => s + r.principal, 0);
  const remaining = Math.max(0, loan.amount - principalPaid);
  const progressPct = Math.round((paidRows.length / schedule.length) * 100);
  const next = schedule.find(r => r.status !== 'paid');
  const visible = showAll ? schedule : schedule.slice(0, 6);

  return (
    <div className="nb-fade">
      <PageHeader title={t('portal.payments.title')} />

      {/* Hero: next payment */}
      {next && (
        <div className="mt-8 rounded-2xl shadow-card p-6 md:p-8 text-white relative overflow-hidden" style={{ background: '#0B2545' }}>
          {/* Soft accent shape */}
          <svg className="absolute -right-10 -top-10 opacity-[0.12] pointer-events-none" width="240" height="240" viewBox="0 0 240 240" aria-hidden="true">
            <circle cx="180" cy="60" r="100" fill="#13B5B1"/>
          </svg>
          <div className="relative">
            <p className="text-xs font-semibold tracking-[0.18em] uppercase" style={{ color: '#5BD2CE' }}>
              {t('portal.payments.nextPayment')}
            </p>
            <div className="mt-3 flex flex-wrap items-end gap-x-8 gap-y-4 justify-between">
              <div>
                <p className="text-5xl font-semibold tracking-tight tnum text-white">
                  {formatMoney(next.amount, loan.currency, lang, { fraction: 0 })}
                </p>
                <p className="mt-2 text-sm" style={{ color: 'rgba(255,255,255,0.7)' }}>
                  {t('portal.payments.due')} <span className="font-semibold text-white tnum">{formatDate(next.due, lang)}</span>
                  <span className="mx-2 opacity-50">·</span>
                  {t('portal.payments.installmentN', { n: next.n })} <span className="opacity-70">{t('portal.payments.ofN', { n: schedule.length })}</span>
                </p>
              </div>
              <button className="bg-white text-navy-900 hover:bg-teal-50 text-sm font-semibold px-6 py-3 rounded-full transition-colors shadow-card">
                {t('portal.payments.payNow')}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Stats row */}
      <div className="mt-4 grid sm:grid-cols-3 gap-4">
        <Card className="p-5">
          <p className="text-xs font-semibold tracking-[0.14em] uppercase text-navy-500">{t('portal.payments.totalPaid')}</p>
          <p className="mt-2 text-2xl font-semibold text-navy-900 tnum">
            {formatMoney(totalPaid, loan.currency, lang, { fraction: 0 })}
          </p>
          <p className="mt-1 text-xs text-navy-500 tnum">{paidRows.length} {t('portal.payments.ofN', { n: schedule.length })}</p>
        </Card>
        <Card className="p-5">
          <p className="text-xs font-semibold tracking-[0.14em] uppercase text-navy-500">{t('portal.payments.remaining')}</p>
          <p className="mt-2 text-2xl font-semibold text-navy-900 tnum">
            {formatMoney(remaining, loan.currency, lang, { fraction: 0 })}
          </p>
          <p className="mt-1 text-xs text-navy-500 tnum">
            {formatPercent(loan.effective_rate, lang, 1)} {t('apply.step1.effectiveRate').toLowerCase()}
          </p>
        </Card>
        <Card className="p-5">
          <p className="text-xs font-semibold tracking-[0.14em] uppercase text-navy-500">{t('portal.payments.progress')}</p>
          <p className="mt-2 text-2xl font-semibold text-navy-900 tnum">{progressPct}%</p>
          <div className="mt-2 h-1.5 rounded-full bg-navy-50 overflow-hidden">
            <div className="h-full bg-navy-900 transition-[width] duration-500" style={{ width: progressPct + '%' }}/>
          </div>
        </Card>
      </div>

      {/* Schedule table */}
      <Card className="mt-4 overflow-hidden">
        <div className="flex items-center justify-between px-6 py-4 border-b hairline">
          <h2 className="text-base font-semibold text-navy-900">{t('portal.payments.scheduleTitle')}</h2>
          {schedule.length > 6 && (
            <button
              type="button"
              onClick={() => setShowAll(v => !v)}
              className="text-sm font-semibold text-navy-700 hover:text-teal-700"
            >
              {showAll ? t('portal.payments.viewLess') : t('portal.payments.viewAll')}
            </button>
          )}
        </div>
        <div className="overflow-x-auto">
          <table className="w-full text-sm">
            <thead className="bg-surface text-navy-500">
              <tr>
                <th className="text-left px-6 py-3 font-semibold text-xs tracking-[0.14em] uppercase">#</th>
                <th className="text-left px-6 py-3 font-semibold text-xs tracking-[0.14em] uppercase">{t('portal.payments.due')}</th>
                <th className="text-right px-6 py-3 font-semibold text-xs tracking-[0.14em] uppercase">{t('portal.payments.principal')}</th>
                <th className="text-right px-6 py-3 font-semibold text-xs tracking-[0.14em] uppercase">{t('portal.payments.interest')}</th>
                <th className="text-right px-6 py-3 font-semibold text-xs tracking-[0.14em] uppercase">{t('apply.step5.summaryMonthly')}</th>
                <th className="text-right px-6 py-3 font-semibold text-xs tracking-[0.14em] uppercase">{t('portal.payments.balance')}</th>
                <th className="text-right px-6 py-3 font-semibold text-xs tracking-[0.14em] uppercase">Status</th>
              </tr>
            </thead>
            <tbody className="divide-y hairline">
              {visible.map(row => (
                <tr key={row.n} className={'hover:bg-surface/60 ' + (row.status === 'due' ? 'bg-teal-50/40' : '')}>
                  <td className="px-6 py-3.5 text-navy-900 tnum font-medium">{row.n}</td>
                  <td className="px-6 py-3.5 text-navy-700 tnum">{formatDate(row.due, lang)}</td>
                  <td className="px-6 py-3.5 text-right text-navy-700 tnum">{formatMoney(row.principal, loan.currency, lang, { fraction: 0 })}</td>
                  <td className="px-6 py-3.5 text-right text-navy-500 tnum">{formatMoney(row.interest, loan.currency, lang, { fraction: 0 })}</td>
                  <td className="px-6 py-3.5 text-right text-navy-900 tnum font-semibold">{formatMoney(row.amount, loan.currency, lang, { fraction: 0 })}</td>
                  <td className="px-6 py-3.5 text-right text-navy-500 tnum">{formatMoney(row.balance, loan.currency, lang, { fraction: 0 })}</td>
                  <td className="px-6 py-3.5 text-right">
                    <PaymentStatusPill status={row.status}/>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </Card>

      {/* Payment history */}
      <div className="mt-10">
        <h2 className="text-base font-semibold text-navy-900">{t('portal.payments.history')}</h2>
        <Card className="mt-3 overflow-hidden">
          <ul className="divide-y hairline">
            <PaymentHistoryItem
              direction="in"
              title={t('portal.payments.disbursement')}
              subtitle={t('portal.payments.receivedFrom')}
              date={formatDate(new Date(loan.signed_at + 8 * 3600 * 1000), lang)}
              amount={formatMoney(loan.amount, loan.currency, lang)}
            />
            {paidRows.length > 0 ? paidRows.slice(-3).reverse().map(r => (
              <PaymentHistoryItem
                key={r.n}
                direction="out"
                title={t('portal.payments.installmentN', { n: r.n })}
                subtitle={t('portal.payments.paidVia', { method: t('portal.payments.method') })}
                date={formatDate(r.due, lang)}
                amount={'−' + formatMoney(r.amount, loan.currency, lang, { fraction: 0 })}
              />
            )) : null}
          </ul>
        </Card>
      </div>
    </div>
  );
}

function PaymentStatusPill({ status }) {
  if (status === 'paid') {
    return (
      <span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md bg-success/10 text-success text-[11px] font-semibold tracking-wider uppercase">
        <svg width="10" height="10" viewBox="0 0 10 10" aria-hidden="true"><path d="M2 5 L4.2 7.2 L8 3.5" stroke="currentColor" strokeWidth="1.8" fill="none" strokeLinecap="round" strokeLinejoin="round"/></svg>
        {t('portal.payments.statusPaid')}
      </span>
    );
  }
  if (status === 'due') {
    return (
      <span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md bg-teal-50 text-teal-700 text-[11px] font-semibold tracking-wider uppercase">
        <span className="h-1.5 w-1.5 rounded-full bg-current" aria-hidden="true"/>
        {t('portal.payments.statusDue')}
      </span>
    );
  }
  return (
    <span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md bg-navy-50 text-navy-500 text-[11px] font-semibold tracking-wider uppercase">
      {t('portal.payments.statusUpcoming')}
    </span>
  );
}

function PaymentHistoryItem({ direction, title, subtitle, date, amount }) {
  const isIn = direction === 'in';
  return (
    <li className="flex items-center gap-4 px-6 py-4">
      <div className={'h-10 w-10 rounded-full flex items-center justify-center shrink-0 ' +
        (isIn ? 'bg-success/10 text-success' : 'bg-navy-50 text-navy-700')}>
        <svg width="18" height="18" viewBox="0 0 18 18" aria-hidden="true">
          {isIn
            ? <path d="M9 3 V13 M4 8 L9 13 L14 8" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
            : <path d="M9 15 V5 M4 10 L9 5 L14 10" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
          }
        </svg>
      </div>
      <div className="flex-1 min-w-0">
        <p className="text-sm font-semibold text-navy-900 truncate">{title}</p>
        <p className="mt-0.5 text-xs text-navy-500 truncate">{subtitle} · {date}</p>
      </div>
      <p className={'text-sm font-semibold tnum ' + (isIn ? 'text-success' : 'text-navy-900')}>{amount}</p>
    </li>
  );
}

// ---------------------------------------------------------------------------
// Page: Insurance — the Sherpa embed slot for the portal lives here.
// ---------------------------------------------------------------------------

function PortalInsurance() {
  const lang = useLang();
  const { rows, loading } = usePortalData();
  if (loading) return <PortalLoading/>;
  const policies = rows.filter(r => r.insurance_selected);

  return (
    <div className="nb-fade">
      <PageHeader title={t('portal.insurance.title')} subtitle={t('portal.insurance.copy')} />

      {/*
        TODO: Sherpa customer-portal mount.
        Option A — Sherpa.js:
          <div id="sherpa-portal-root"></div>
          <script src="https://embed.sherpa.../sherpa.js"></script>
          <script>Sherpa.mount('#sherpa-portal-root', { product:'customer-portal', token }); </script>
        Option B — React wrapper (when available).
        Until wired: render policy summary below.
      */}
      <div id="sherpa-portal-root">
        {policies.length === 0 ? (
          <Card className="mt-8 p-10 text-center">
            <div className="mx-auto h-12 w-12 rounded-xl bg-teal-50 text-teal-700 flex items-center justify-center">
              <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"/>
              </svg>
            </div>
            <p className="mt-4 text-navy-700">{t('portal.insurance.empty')}</p>
          </Card>
        ) : (
          <div className="mt-8 space-y-4">
            {policies.map(p => (
              <Card key={p.id} className="p-6">
                <div className="flex items-start gap-4 flex-wrap">
                  <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"/>
                      <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 min-w-0">
                    <h3 className="text-lg font-semibold text-navy-900">{t('apply.step5.insuranceTitle')}</h3>
                    <p className="mt-1 text-sm text-navy-500">{t('apply.step5.insuranceCopy')}</p>
                  </div>
                  <StatusPill kind="active" label={t('portal.insurance.statusActive')}/>
                </div>
                <dl className="mt-5 grid sm:grid-cols-3 gap-x-6 gap-y-3">
                  <Detail label={t('portal.insurance.policyNo')} value={'POL-' + p.confirmation_number.replace(/^NB-/, '')} />
                  <Detail label={t('portal.insurance.coverageLabel')} value={formatMoney(p.monthly, p.currency, lang, { fraction: 0 })} />
                  <Detail label={t('portal.insurance.coveredFor')} value={p.confirmation_number} />
                </dl>
                <div className="mt-6 flex items-center gap-3">
                  <button className="text-sm font-semibold text-navy-900 hover:text-teal-700">{t('portal.insurance.fileClaim')} →</button>
                </div>
              </Card>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Page: Documents — mock list per loan.
// ---------------------------------------------------------------------------

function PortalDocuments() {
  const lang = useLang();
  const { rows, loading } = usePortalData();
  if (loading) return <PortalLoading/>;

  const docs = rows.flatMap(loan => {
    const base = [
      { kind: 'contract', label: t('portal.documents.contract'),  date: loan.signed_at,        ref: loan.confirmation_number },
      { kind: 'sepa',     label: t('portal.documents.sepa'),      date: loan.signed_at - 3000, ref: loan.confirmation_number },
      { kind: 'welcome',  label: t('portal.documents.welcome'),   date: loan.signed_at + 60000,ref: loan.confirmation_number },
    ];
    if (loan.insurance_selected) {
      base.push({ kind: 'policy', label: t('portal.documents.policy'), date: loan.signed_at + 30000, ref: 'POL-' + loan.confirmation_number.replace(/^NB-/, '') });
    }
    return base;
  });

  return (
    <div className="nb-fade">
      <PageHeader title={t('portal.documents.title')} />
      {docs.length === 0 ? (
        <Card className="mt-8 p-10 text-center"><p className="text-navy-500">{t('portal.documents.empty')}</p></Card>
      ) : (
        <Card className="mt-8 overflow-hidden">
          <ul className="divide-y hairline">
            {docs.map((d, i) => (
              <li key={i} className="flex items-center gap-4 px-6 py-4 hover:bg-surface/60">
                <div className="h-10 w-10 rounded-lg bg-navy-50 text-navy-700 flex items-center justify-center shrink-0">
                  <svg width="18" height="18" viewBox="0 0 20 20" aria-hidden="true"><path d="M5 2 H12 L16 6 V17 A1 1 0 0 1 15 18 H5 A1 1 0 0 1 4 17 V3 A1 1 0 0 1 5 2 Z" stroke="currentColor" strokeWidth="1.5" fill="none"/><path d="M12 2 V6 H16" stroke="currentColor" strokeWidth="1.5" fill="none"/></svg>
                </div>
                <div className="flex-1 min-w-0">
                  <p className="text-sm font-semibold text-navy-900 truncate">{d.label}</p>
                  <p className="text-xs text-navy-500 tnum">{d.ref} · {formatDate(new Date(d.date), lang)}</p>
                </div>
                <button className="text-sm font-semibold text-teal-700 hover:underline">{t('portal.documents.download')}</button>
              </li>
            ))}
          </ul>
        </Card>
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Page: Profile
// ---------------------------------------------------------------------------

function PortalProfile() {
  const lang = useLang();
  const { rows, loading } = usePortalData();
  const user = useCurrentUser();
  if (loading) return <PortalLoading/>;
  const loan = primaryLoan(rows);
  const p = (loan && loan.data && loan.data.personal) || {};
  const e = (loan && loan.data && loan.data.employment) || {};

  return (
    <div className="nb-fade">
      <PageHeader title={t('portal.profile.title')} subtitle={t('portal.profile.editHint')} />
      <div className="mt-8 grid lg:grid-cols-2 gap-4">
        <Card className="p-6">
          <h2 className="text-xs font-semibold tracking-[0.14em] uppercase text-navy-500">{t('portal.profile.personal')}</h2>
          <dl className="mt-4 space-y-3">
            <ProfileRow label={t('apply.step2.name')} value={p.name || '—'} />
            <ProfileRow label={t('apply.step2.dob')} value={p.dob || '—'} />
            <ProfileRow label={t('apply.step2.ssn')} value={maskSsn(p.ssn)} />
          </dl>
        </Card>
        <Card className="p-6">
          <h2 className="text-xs font-semibold tracking-[0.14em] uppercase text-navy-500">{t('portal.profile.contact')}</h2>
          <dl className="mt-4 space-y-3">
            <ProfileRow label={t('apply.step2.email')} value={user.email} />
            <ProfileRow label={t('apply.step2.phone')} value={p.phone || '—'} />
          </dl>
        </Card>
        <Card className="p-6 lg:col-span-2">
          <h2 className="text-xs font-semibold tracking-[0.14em] uppercase text-navy-500">{t('portal.profile.address')}</h2>
          <dl className="mt-4 grid sm:grid-cols-3 gap-x-6 gap-y-3">
            <ProfileRow label={t('apply.step2.address')} value={p.address || '—'} />
            <ProfileRow label={t('apply.step2.postcode')} value={p.postcode || '—'} />
            <ProfileRow label={t('apply.step2.city')} value={p.city || '—'} />
          </dl>
        </Card>
        <Card className="p-6 lg:col-span-2">
          <h2 className="text-xs font-semibold tracking-[0.14em] uppercase text-navy-500">{t('apply.step3.title')}</h2>
          <dl className="mt-4 grid sm:grid-cols-3 gap-x-6 gap-y-3">
            <ProfileRow label={t('apply.step3.status')} value={e.status ? t('apply.step3.status' + e.status[0].toUpperCase() + e.status.slice(1)) || e.status : '—'} />
            <ProfileRow label={t('apply.step3.employer')} value={e.employer || '—'} />
            <ProfileRow label={t('apply.step3.income')} value={e.income ? formatMoney(e.income, loan.currency, lang) : '—'} />
            <ProfileRow label={t('apply.step3.housing')} value={e.housing || '—'} />
            <ProfileRow label={t('apply.step3.startDate')} value={e.startDate || '—'} />
          </dl>
        </Card>
      </div>
    </div>
  );
}

function ProfileRow({ label, value }) {
  return (
    <div className="grid grid-cols-3 gap-2 items-baseline">
      <dt className="text-xs text-navy-500">{label}</dt>
      <dd className="col-span-2 text-sm text-navy-900 tnum">{value}</dd>
    </div>
  );
}

function maskSsn(ssn) {
  if (!ssn) return '—';
  const s = String(ssn);
  if (s.length <= 4) return s;
  return s.slice(0, 6) + '••••' + s.slice(-1);
}

// ---------------------------------------------------------------------------
// Page: Settings
// ---------------------------------------------------------------------------

function PortalSettings() {
  return (
    <div className="nb-fade">
      <PageHeader title={t('portal.settings.title')} />
      <div className="mt-8 grid gap-4">
        <Card className="p-6">
          <h2 className="text-xs font-semibold tracking-[0.14em] uppercase text-navy-500">{t('portal.settings.language')}</h2>
          <div className="mt-4"><LangSwitcher/></div>
        </Card>
        <Card className="p-6">
          <h2 className="text-xs font-semibold tracking-[0.14em] uppercase text-navy-500">{t('portal.settings.security')}</h2>
          <ul className="mt-4 divide-y hairline">
            <li className="flex items-center justify-between py-3">
              <span className="text-sm text-navy-700">{t('portal.settings.changePassword')}</span>
              <button className="text-sm font-semibold text-navy-900 hover:text-teal-700">→</button>
            </li>
            <li className="flex items-center justify-between py-3">
              <span className="text-sm text-danger">{t('portal.settings.deleteAccount')}</span>
              <button className="text-sm font-semibold text-danger hover:text-danger/80">→</button>
            </li>
          </ul>
        </Card>
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Page chrome
// ---------------------------------------------------------------------------

function PageHeader({ title, subtitle }) {
  return (
    <div>
      <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-navy-500 max-w-2xl" style={{ textWrap: 'pretty' }}>{subtitle}</p>}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Router: pick a page based on the route, mount inside PortalLayout.
// ---------------------------------------------------------------------------

function Portal() {
  useLang();
  const route = useRoute();
  let page;
  if (route === '/portal' || route === '/portal/') page = <PortalOverview/>;
  else if (route.startsWith('/portal/loans'))      page = <PortalLoans/>;
  else if (route.startsWith('/portal/payments'))   page = <PortalPayments/>;
  else if (route.startsWith('/portal/insurance'))  page = <PortalInsurance/>;
  else if (route.startsWith('/portal/documents')) page = <PortalDocuments/>;
  else if (route.startsWith('/portal/profile'))    page = <PortalProfile/>;
  else if (route.startsWith('/portal/settings'))   page = <PortalSettings/>;
  else page = <PortalOverview/>;
  return <PortalLayout>{page}</PortalLayout>;
}

Object.assign(window, {
  Portal, PortalLayout, PortalOverview, PortalLoans, PortalPayments,
  PortalInsurance, PortalDocuments, PortalProfile, PortalSettings,
});
