:root {
  --navy: #1e3653;
  --surface: #f1f1f1;
  --accent: #b1d4e0;
  --teal: #033B4F;
  --mid: #345e7d;
  --black: #000000;
  --line-navy: rgba(177,212,224,0.25);
  --line-card: rgba(3,59,79,0.16);
  --err: #8f1d1d;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--navy);
  color: var(--surface);
  font: 16px/1.5 system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
}

/* Top brand bar — part of the navy page, set off by a light-blue-tint rule. The
   wordmark sits left, the professional login sits right on the SAME bar, so an
   existing client's bookmark reaches Log in without scrolling. */
.band {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 24px;
  border-bottom: 1px solid var(--line-navy);
}
.band .brand { color: var(--surface); font-weight: 700; letter-spacing: 0.5px; font-size: 20px; }
.band .brand .mark { color: var(--accent); }
/* One door for everyone — the system routes by role after sign-in. Off-white
   text on navy (10.89:1) inside a light-blue-bordered pill (7.82:1). */
.band-login, .band-login:visited {
  color: var(--surface);
  text-decoration: none;
  font-weight: 600;
  font-size: 15px;
  border: 1px solid var(--accent);
  border-radius: 6px;
  padding: 7px 14px;
}
.band-actions { display: flex; align-items: center; gap: 10px; }
/* The agent door, deliberately QUIETER than Log in beside it: light-blue text on
   navy (7.82:1, the pairing the menu labels already use) inside a dim tinted
   border, against Log in's off-white text in a solid light-blue border. It reads
   as secondary so Log in and the homeowner calls to action stay primary. */
.band-agent, .band-agent:visited {
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
  font-size: 14px;
  border: 1px solid var(--line-navy);
  border-radius: 6px;
  padding: 7px 12px;
  white-space: nowrap;
}
/* Phone width: the full label will not sit beside the wordmark AND Log in, so the
   band WRAPS and the two buttons take their own row. It stays in the header and
   keeps its whole label — cutting it to "For Agents" would drop the half that
   tells a loan officer it is for them, and there is no hamburger here to fold
   into. */
@media (max-width: 560px) {
  .band { flex-wrap: wrap; row-gap: 12px; }
  .band-actions { width: 100%; justify-content: flex-start; }
  .band-agent { font-size: 13px; padding: 6px 10px; }
}

.wrap { max-width: 560px; margin: 0 auto; padding: 32px 20px 56px; }
.wrap.wide { max-width: 1040px; }

/* Text that sits directly on the navy page reads in near-white / light-blue. */
h1 { font-size: 26px; margin: 0 0 6px; color: var(--surface); }
.sub { margin: 0 0 24px; color: var(--accent); }

/* Links. The theme never gave anchors a colour, so any unclassed link fell back
   to the browser default (blue on navy = 1.31:1, invisible; worse once visited).
   Default = the navy page: light-blue #b1d4e0 at 7.82:1. On the near-white card
   links take teal #033B4F at 10.67:1. Both are underlined so a link reads as one
   without relying on colour, and :visited is identical to unvisited — a purple
   visited link has no meaning in an app and breaks the palette. Button-styled
   anchors (.menu tabs, .link-btn, .continue, .pro-edit) opt out below: they read
   as controls, and only unclassed anchors take the card teal, so those are left
   with their own fills. */
a, a:visited { color: var(--accent); text-decoration: underline; }
.plate a:not([class]), .plate a:not([class]):visited,
.card a:not([class]), .card a:not([class]):visited,
.report a:not([class]), .report a:not([class]):visited { color: var(--teal); }

/* Near-white card: the primary data + input surface. */
.plate, .card {
  background: var(--surface);
  color: var(--navy);
  border-radius: 12px;
  padding: 22px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
}
.plate h2, .card h2 { color: var(--teal); margin: 0 0 16px; font-size: 18px; }
.plate + .plate, .card + .card { margin-top: 20px; }

/* ── Compliance disclaimer ───────────────────────────────────────────────────
   The persistent footer sits on the navy page below the content: surface text
   on navy (7.82:1 for the light-blue heading, higher for the body), both pinned
   in contrast.test.ts. The agree block (signup / accept / gate) carries its own
   light surface so its full text reads whether it sits inside a plate or, on the
   gate page, directly on navy. */
.disclaimer-footer { border-top: 1px solid var(--line-navy); margin-top: 40px; padding: 22px 20px 40px; }
.disclaimer-inner { max-width: 1040px; margin: 0 auto; color: var(--surface); font-size: 12px; line-height: 1.55; }
.disclaimer-heading { color: var(--accent); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; font-size: 12px; margin: 0 0 4px; }
.disclaimer-inner p { margin: 0; }

.agree-block { background: var(--surface); color: var(--navy); border-radius: 10px; padding: 16px 18px; margin: 16px 0; }
.agree-heading { color: var(--teal); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; font-size: 13px; margin: 0 0 6px; }
.agree-text { color: var(--navy); font-size: 13px; line-height: 1.55; margin: 0 0 12px; }
.agree-check { display: flex; gap: 10px; align-items: flex-start; color: var(--navy); font-weight: 600; }
.agree-check input { margin-top: 3px; }

.field { margin-bottom: 16px; }
.field label { display: block; font-weight: 600; margin-bottom: 6px; color: var(--navy); }
/* Style every text-like input by exclusion, not by enumerating types — a new
   field (number, tel, date, month, …) then gets the block layout automatically
   instead of falling back to an inline default. Only checkbox and radio, which
   genuinely need different treatment, are excluded. */
.field input:not([type="checkbox"]):not([type="radio"]) {
  width: 100%;
  padding: 11px 12px;
  border: 1px solid #b9c2cc;
  border-radius: 7px;
  background: #fff;
  color: var(--navy);
  font-size: 15px;
}
.field input:focus { outline: 2px solid var(--mid); border-color: var(--mid); }

.choices { display: flex; gap: 10px; flex-wrap: wrap; }
.choices label {
  flex: 1 1 0;
  min-width: 160px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 12px;
  border: 1px solid #b9c2cc;
  border-radius: 7px;
  background: #fff;
  color: var(--navy);
  font-weight: 500;
  cursor: pointer;
}
.choices.stack { flex-direction: column; }
.choices.stack label { min-width: 0; }

.pw { display: flex; align-items: stretch; gap: 8px; }
.pw input:not([type="checkbox"]):not([type="radio"]) { flex: 1 1 auto; width: auto; }
.pw-toggle {
  border: 1px solid #b9c2cc;
  border-radius: 7px;
  background: #fff;
  color: var(--mid);
  font-weight: 600;
  font-size: 13px;
  padding: 0 14px;
  cursor: pointer;
}

.err { color: var(--err); font-size: 13px; margin-top: 5px; }
.form-err {
  background: #f7dede;
  color: var(--err);
  border-radius: 7px;
  padding: 10px 12px;
  margin-bottom: 16px;
  font-size: 14px;
}

/* Primary action: mid-blue fill, near-white text (6.11:1). */
.btn {
  display: inline-block;
  width: 100%;
  padding: 13px 16px;
  border: 0;
  border-radius: 8px;
  background: var(--mid);
  color: var(--surface);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.3px;
  cursor: pointer;
}
.btn:hover { background: #2b4f6a; }

.alt { margin-top: 18px; text-align: center; color: var(--accent); }
.alt a { color: var(--accent); font-weight: 600; }

/* /app landing detail rows (professional). */
.detail { margin: 0 0 14px; }
.detail .k { color: var(--teal); font-weight: 600; font-size: 13px; text-transform: uppercase; letter-spacing: 0.4px; }
.detail .v { font-size: 18px; color: var(--navy); }
.signout {
  display: inline-block;
  background: var(--mid);
  color: var(--surface);
  border: 0;
  padding: 10px 16px;
  border-radius: 8px;
  font-weight: 700;
  cursor: pointer;
}
.signout:hover { background: #2b4f6a; }

.app-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 24px;
}
.app-head h1 { margin: 0 0 4px; }
.app-head .sub { margin: 0; }

.notice {
  background: var(--accent);
  color: var(--teal);
  border-radius: 7px;
  padding: 10px 12px;
  margin-bottom: 16px;
  font-size: 14px;
  font-weight: 600;
}

.hint { color: var(--mid); font-size: 13px; margin: -4px 0 16px; }
.opt { color: var(--mid); font-weight: 400; font-size: 12px; }

/* Professional client list. */
.clients { width: 100%; border-collapse: collapse; }
.clients th {
  text-align: left;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--teal);
  border-bottom: 1px solid var(--line-card);
  padding: 8px 10px;
}
.clients td { padding: 12px 10px; border-bottom: 1px solid var(--line-card); vertical-align: middle; }
.clients td a, .clients td a:visited { color: var(--teal); font-weight: 600; }
.clients td.actions { text-align: right; }
.row-action { display: inline; margin: 0; }
.link-btn, .link-btn:visited { background: none; border: 0; color: var(--mid); font-weight: 600; font-size: 14px; cursor: pointer; padding: 0; text-decoration: none; }
.link-btn.danger { color: var(--err); }
.link-btn:hover { text-decoration: underline; }

/* The call list: clients grouped worst-first. Plain headings and a one-line
   description per group — no colour alarm, the ORDER carries the priority. */
.call-group { margin-top: 22px; }
.call-group:first-child { margin-top: 0; }
.call-group h3 { margin: 0 0 2px; font-size: 16px; color: var(--teal); }
.call-group .sub { margin: 0 0 8px; }

/* Status pills. Complete on teal, Started on mid-blue, Invited on navy, all
   with near-white text (each pairing pinned in contrast.test.ts). */
.badge { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 700; letter-spacing: 0.3px; }
.badge.complete { background: var(--teal); color: var(--surface); }
.badge.started { background: var(--mid); color: var(--surface); }
.badge.invited { background: var(--navy); color: var(--surface); }
.badge.active { background: var(--teal); color: var(--surface); }
/* Expired offer marker (management surfaces only — a client never sees it): the
   error red with near-white text (err-on-surface pairing pinned in contrast).
   Featured: the scarce Rolodex slot, highlighted light-blue with navy text
   (navy-on-accent pairing pinned in contrast). */
.badge.expired { background: var(--err); color: var(--surface); }
.badge.featured { background: var(--accent); color: var(--navy); }
.row-expired td { opacity: 0.72; }

/* The persistent banner when a professional is viewing a client's plan
   read-only. Light-blue band so it can't be mistaken for the client's own
   screen, with a clear way back to the list. */
.pro-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: var(--accent);
  border-radius: 12px;
  padding: 12px 16px;
  margin-bottom: 18px;
}
.pro-banner .pro-viewing { color: var(--teal); font-size: 15px; }
.pro-banner .pro-status {
  display: inline-block;
  margin-left: 8px;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--teal);
  color: var(--surface);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.3px;
}
.pro-banner .pro-back { color: var(--teal); font-weight: 700; white-space: nowrap; }

/* The link that turns the read-only view into edit mode. Filled so it reads as
   the deliberate action it is, not just another link. */
.pro-banner .pro-edit {
  background: var(--mid);
  color: var(--surface);
  font-weight: 700;
  white-space: nowrap;
  padding: 7px 14px;
  border-radius: 999px;
  text-decoration: none;
}

/* EDIT MODE looks deliberately unlike the read-only band: a solid dark fill
   instead of the light one, so a professional can never mistake reviewing for
   editing. */
.pro-banner.editing {
  background: var(--teal);
  border: 2px solid var(--accent);
}
.pro-banner.editing .pro-viewing { color: var(--surface); font-weight: 700; }
.pro-banner.editing .pro-back { color: var(--accent); }

/* Attribution on a progress row: a plain fact that this month was entered by
   the client's advisor, not an alert. */
.by-pro {
  margin-left: 8px;
  color: var(--mid);
  font-size: 12px;
  font-weight: 700;
  font-style: italic;
}

.empty { color: var(--navy); }
.empty p { margin: 0 0 6px; }
.empty-hint { color: var(--mid); font-size: 14px; }

/* ============================================================================
   Client shell — summary strip + menu, then a section body
   ========================================================================= */

.client-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}
.client-head .who-block { display: flex; flex-direction: column; gap: 2px; }
.client-head h1 { margin: 0; font-size: 22px; }
.client-head .who { color: var(--accent); font-size: 14px; }
.brand-link { color: var(--accent); text-decoration: underline; }
.head-actions { display: flex; align-items: center; gap: 14px; }

/* The advisor guide (/app/guide). Rendered markdown living inside a surface
   plate, so it reuses the card's navy body text and teal headings — the app's
   own styling, not a document dropped in. Every colour here is one already
   pinned in contrast.test.ts (navy/teal on surface, teal on accent). */
.guide-nav { margin-bottom: 16px; }
.guide { line-height: 1.62; }
.guide h1 { color: var(--teal); font-size: 24px; margin: 0 0 8px; }
.guide h2 {
  color: var(--teal);
  font-size: 19px;
  margin: 30px 0 12px;
  padding-top: 18px;
  border-top: 1px solid var(--line-card);
}
.guide h1 + p, .guide h2 + p, .guide h3 + p { margin-top: 0; }
.guide h3 { color: var(--navy); font-size: 16px; margin: 20px 0 8px; }
.guide p, .guide li { color: var(--navy); }
.guide ul, .guide ol { padding-left: 22px; margin: 10px 0; }
.guide li { margin: 4px 0; }
.guide table { width: 100%; border-collapse: collapse; margin: 14px 0; }
.guide th, .guide td {
  border: 1px solid var(--line-card);
  padding: 8px 10px;
  text-align: left;
  vertical-align: top;
  font-size: 14px;
  color: var(--navy);
}
.guide th { background: var(--accent); color: var(--teal); }
.guide blockquote {
  margin: 16px 0;
  padding: 12px 16px;
  background: var(--accent);
  border-left: 4px solid var(--teal);
  border-radius: 6px;
}
.guide blockquote p { color: var(--teal); margin: 0; }
.guide hr { border: 0; border-top: 1px solid var(--line-card); margin: 26px 0; }

/* Advisor support — a question box and the last answer. The answer is plain
   text from the guide-grounded bot, rendered escaped with its line breaks kept
   (pre-wrap) so paragraphs and lists in the reply survive. */
.field textarea {
  width: 100%;
  padding: 11px 12px;
  border: 1px solid #b9c2cc;
  border-radius: 7px;
  background: #fff;
  color: var(--navy);
  font-size: 15px;
  font-family: inherit;
  resize: vertical;
}
.field textarea:focus { outline: 2px solid var(--mid); border-color: var(--mid); }
.support-answer { white-space: pre-wrap; color: var(--navy); line-height: 1.6; }
.support-note { color: var(--mid); font-size: 13px; margin-top: 12px; }

/* Partner offers — on a surface plate, so teal/navy text. Framed as help, not
   deals; each carries its monthly saving. */
.partners { display: flex; flex-direction: column; gap: 10px; }
.partner {
  border: 1px solid var(--line-card);
  border-radius: 6px;
  padding: 10px 12px;
  background: var(--surface);
}
.partner-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.partner-name { color: var(--navy); font-weight: 700; font-size: 15px; }
.partner-saving { color: var(--teal); font-weight: 700; font-size: 14px; white-space: nowrap; }
.partner-offer { color: var(--navy); margin: 6px 0 4px; }
.partner-redeem { color: var(--mid); font-size: 13px; margin: 0; }
.partner-redeem-label { color: var(--teal); font-weight: 600; }
.partner-lead { color: var(--mid); font-size: 13px; margin: 0 0 10px; }
/* A server-generated QR code for the offer's redemption link. Black modules on a
   white ground (both authored by the SVG), so it scans regardless of surface. */
.partner-qr { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; margin: 8px 0 4px; }
.partner-qr svg { width: 96px; height: 96px; display: block; }
.partner-qr-cap { color: var(--mid); font-size: 12px; }
.partner-apply { margin-top: 8px; }
/* The overview teaser's link through to the full marketplace. */
.partner-more { margin: 12px 0 0; }
/* The QR column in the vendor's marketplace table. */
.qr-cell svg { width: 84px; height: 84px; display: block; }
/* The vendor's uploaded card photo on their own screen. */
.card-photo { margin: 8px 0; }
.card-photo img { width: 96px; height: 96px; object-fit: cover; border-radius: 8px; display: block; }

/* THE ROLODEX — a business-card band at the BOTTOM of every client page. Sits
   directly on the navy page (below the plates), so its heading is near-white on
   navy (pinned in contrast); each card is a near-white surface with navy/teal/
   mid text (all pinned). A card with no photo shows company initials on a
   light-blue disc (navy on accent, pinned) — a missing photo never breaks it. */
.rolodex { margin: 28px 0 8px; }
.rolo-h { color: var(--surface); font-size: 15px; font-weight: 700; margin: 0 0 12px; }
.rolo-band { display: flex; flex-wrap: wrap; gap: 12px; }
.rolo-card {
  display: flex;
  gap: 12px;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line-card);
  border-radius: 8px;
  padding: 12px 14px;
  min-width: 220px;
  flex: 1 1 220px;
  max-width: 320px;
}
.rolo-photo { width: 56px; height: 56px; object-fit: cover; border-radius: 8px; flex: none; }
.rolo-initials {
  width: 56px;
  height: 56px;
  border-radius: 8px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent);
  color: var(--navy);
  font-weight: 700;
  font-size: 20px;
}
.rolo-lines { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.rolo-company { color: var(--navy); font-weight: 700; font-size: 15px; }
.rolo-name { color: var(--teal); font-weight: 600; font-size: 13px; }
.rolo-title { color: var(--mid); font-size: 12px; }
.rolo-phone, .rolo-phone:visited { color: var(--mid); font-size: 13px; text-decoration: underline; }
/* The MSIA link on the retirement section — a plain outbound button. It is a
   different kind of thing from the chart above it, so it is set off as its own
   element: a divider and generous space separate it from the chart legend. */
.msia {
  margin-top: 28px;
  padding-top: 24px;
  border-top: 1px solid var(--line-card);
}
.btn-link, .btn-link:visited {
  display: inline-block;
  background: var(--mid);
  color: var(--surface);
  font-weight: 600;
  font-size: 14px;
  text-decoration: none;
  padding: 8px 16px;
  border-radius: 6px;
}
.btn-link:hover { background: #2b4f6a; }
/* The MSIA call to action is the same button family, sized up so it reads as a
   deliberate call to action rather than an afterthought. */
.msia .btn-link { font-size: 16px; padding: 13px 30px; }
/* Its note reads as a caption, not body text: smaller, with room above so it is
   not pressed against the button. Overrides the shared .hint pull-up margin. */
.msia .hint { margin: 16px 0 0; font-size: 12px; }

/* The current logo on the details page + its upload/remove controls. */
.logo-current { display: flex; align-items: center; gap: 14px; margin-bottom: 12px; }
.logo-preview {
  max-height: 60px;
  max-width: 200px;
  object-fit: contain;
  background: var(--data, var(--surface));
  border: 1px solid var(--line-card);
  border-radius: 6px;
  padding: 6px 8px;
}
.logo-upload { display: flex; flex-direction: column; gap: 8px; }

/* The always-on summary strip: six figures, on the navy page, near-white
   values over light-blue labels, divided by light-blue-tint rules. */
.strip {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px;
  background: var(--line-navy);
  border: 1px solid var(--line-navy);
  border-radius: 12px;
  overflow: hidden;
  margin-bottom: 18px;
}
.strip .cell { background: var(--navy); padding: 14px 14px; min-width: 0; }
.strip .k { color: var(--accent); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.strip .v { color: var(--surface); font-size: 18px; font-weight: 700; line-height: 1.2; margin-top: 3px; }
.strip .v.soft { color: var(--accent); font-weight: 600; font-size: 15px; }

/* Menu bar (only in menu mode). Text links on navy; the current one is
   light-blue with an underline. */
.menu { display: flex; flex-wrap: wrap; gap: 4px 6px; margin-bottom: 22px; border-bottom: 1px solid var(--line-navy); }
.menu a {
  color: var(--surface);
  text-decoration: none;
  font-weight: 600;
  font-size: 15px;
  padding: 10px 14px;
  border-bottom: 3px solid transparent;
}
.menu a:hover { color: var(--accent); }
.menu a.current { color: var(--accent); border-bottom-color: var(--accent); }

/* Guided-setup progress (setup mode). */
.setup-progress { display: flex; gap: 8px; margin-bottom: 20px; flex-wrap: wrap; }
.setup-progress .step {
  display: flex; align-items: center; gap: 8px;
  color: var(--accent); font-size: 13px; font-weight: 600;
}
.setup-progress .dot {
  width: 22px; height: 22px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-navy); font-size: 12px;
}
.setup-progress .step.done .dot { background: var(--teal); color: var(--surface); border-color: var(--teal); }
.setup-progress .step.current .dot { background: var(--accent); color: var(--navy); border-color: var(--accent); }
.setup-progress .step.current { color: var(--surface); }
.setup-lead { color: var(--accent); margin: 0 0 16px; }
.setup-actions { display: flex; align-items: center; gap: 16px; margin-top: 18px; }
.setup-actions .continue {
  background: var(--mid); color: var(--surface); border: 0; border-radius: 8px;
  padding: 12px 22px; font-weight: 700; font-size: 15px; cursor: pointer; text-decoration: none; display: inline-block;
}
.setup-actions .continue:hover { background: #2b4f6a; }
.setup-actions .skip { color: var(--accent); font-size: 14px; }

/* Projection banner. A teal card (dark), near-white/ light-blue text. */
.projection {
  background: var(--teal);
  color: var(--surface);
  border-radius: 12px;
  padding: 24px;
  margin-bottom: 20px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}
.projection h2 { color: var(--accent); margin: 0 0 6px; font-size: 18px; }
.projection p { margin: 0; color: var(--surface); max-width: 54ch; }
/* proj-lead and proj-date render in TWO grounds: the navy-toned .projection
   banner above, and a near-white .plate on the Progress screen. The base rule is
   therefore the LIGHT-PLATE case (dark ink on --surface), with the banner
   overriding it just below. Written the other way round — light ink as the
   default — the Progress card painted the debt-free date in exactly its own
   background colour, 1.00:1, and no client could read their own date. Neither
   ground may be fixed by hardcoding a colour that breaks the other; both are
   asserted per-container in tests/contrast.test.ts. */
.proj-lead { font-size: 13px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--mid); }
.proj-date { font-size: 34px; font-weight: 800; color: var(--teal); line-height: 1.1; }
.projection .proj-lead { color: var(--accent); }
.projection .proj-date { color: var(--surface); }
.proj-dur { font-size: 14px; color: var(--accent); margin-top: 2px; }
/* The original plan's date and the estimate caveat sit inside the teal banner
   only, so both take colours already proven against it: --accent for the
   secondary line, --surface for the caveat, which must not read as a whisper. */
.proj-orig { font-size: 14px; color: var(--accent); margin-top: 8px; }
.proj-est { font-size: 13px; color: var(--surface); font-weight: 600; margin-top: 8px; max-width: 54ch; }
.proj-stats { display: flex; gap: 28px; flex-wrap: wrap; }
.proj-stat .k { font-size: 12px; text-transform: uppercase; letter-spacing: 0.4px; color: var(--accent); }
.proj-stat .v { font-size: 20px; font-weight: 700; color: var(--surface); }
.proj-warn { background: #5a1414; }
.proj-warn h2 { color: #f2c6c6; }
.proj-warn p { color: #f7e2e2; }

/* Net worth: read-only figures on a card. */
.networth { display: flex; gap: 28px; flex-wrap: wrap; }
.nw-item .k { font-size: 12px; text-transform: uppercase; letter-spacing: 0.4px; color: var(--mid); font-weight: 600; }
.nw-item .v { font-size: 22px; font-weight: 700; color: var(--navy); }
.nw-total .v { color: var(--teal); }

/* Compact financial tables. */
.fin-table { width: 100%; border-collapse: collapse; margin-bottom: 6px; }
.fin-table th {
  text-align: left; font-size: 12px; text-transform: uppercase; letter-spacing: 0.4px;
  color: var(--teal); border-bottom: 1px solid var(--line-card); padding: 8px 10px;
}
.fin-table td { padding: 11px 10px; border-bottom: 1px solid var(--line-card); vertical-align: middle; color: var(--navy); }
.fin-table .num { text-align: right; white-space: nowrap; }
.fin-table th.num { text-align: right; }
.fin-table .empty-row td { color: var(--mid); font-style: italic; text-align: left; }
.tag { display: inline-block; background: var(--teal); color: var(--surface); border-radius: 999px; padding: 1px 8px; font-size: 11px; font-weight: 700; }
.muted { color: var(--mid); font-size: 13px; margin-left: 6px; }
.inline-actions { display: inline-flex; gap: 12px; align-items: center; margin-left: 14px; }

.adder { margin-top: 10px; }
.adder > summary { cursor: pointer; color: var(--mid); font-weight: 600; font-size: 14px; padding: 6px 0; }
.row-form { margin-top: 12px; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 16px; }
.grid label { display: block; font-weight: 600; font-size: 14px; color: var(--navy); }
.grid .g-label { grid-column: 1 / -1; }
.grid .g-check { grid-column: 1 / -1; font-weight: 500; display: flex; align-items: center; gap: 8px; }
.grid input:not([type="checkbox"]):not([type="radio"]), .grid select {
  width: 100%; margin-top: 5px; padding: 10px 11px;
  border: 1px solid #b9c2cc; border-radius: 7px; background: #fff; color: var(--navy); font-size: 15px;
}
.grid input:focus, .grid select:focus { outline: 2px solid var(--mid); border-color: var(--mid); }
.edit-row td { background: #e2e7ec; }
.row-form-actions { display: flex; gap: 14px; align-items: center; margin-top: 14px; }

.btn-sm {
  margin-top: 14px; border: 0; border-radius: 8px; background: var(--mid); color: var(--surface);
  font-size: 14px; font-weight: 700; padding: 10px 18px; cursor: pointer;
}
.btn-sm:hover { background: #2b4f6a; }

.plan-form { margin-bottom: 8px; }
.sub-h { color: var(--teal); font-size: 15px; margin: 22px 0 10px; }

/* Framing note for the retirement projection — put where it is read. */
.frame {
  background: var(--accent);
  color: var(--teal);
  border-radius: 8px;
  padding: 12px 14px;
  font-size: 13px;
  line-height: 1.45;
  margin: 0 0 16px;
}

/* Retirement headline figures. */
.retire-figures { display: flex; gap: 30px; flex-wrap: wrap; margin-bottom: 8px; }
.retire-figures .k { font-size: 12px; text-transform: uppercase; letter-spacing: 0.4px; color: var(--mid); font-weight: 600; }
.retire-figures .v { font-size: 26px; font-weight: 800; color: var(--teal); }
.retire-figures .v.short { color: var(--err); }
.retire-note { color: var(--navy); margin: 6px 0 0; }

/* Estimated balances. The distinction between a figure the client reported and
   one we carried forward is carried by the WORD "estimated", never by colour or
   weight alone — it has to survive a greyscale print and a colourblind reader.
   All of it sits on the near-white plate. */
.est-note { color: var(--navy); font-weight: 600; margin: 10px 0 0; }
.estimates { color: var(--navy); margin: 8px 0 0; padding-left: 20px; }
.estimates li { margin: 4px 0; }
.est-label { font-weight: 700; }
.est-mark { color: var(--mid); font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px; font-size: 12px; }

/* Charts. */
.chart { margin: 8px 0 4px; }
.chart svg { width: 100%; height: auto; display: block; }
.chart .cap { color: var(--mid); font-size: 13px; margin: 4px 0 0; }
.legend { display: flex; gap: 18px; flex-wrap: wrap; margin: 8px 0 0; font-size: 13px; color: var(--navy); }
.legend .swatch { display: inline-block; width: 22px; height: 3px; vertical-align: middle; margin-right: 6px; }
.legend .swatch.accel { background: var(--teal); }
.legend .swatch.base { background: var(--mid); }

/* Plan comparison: the two-path summary and the collapsible per-debt schedules.
   The reached-month row is tinted with the light-blue accent (navy text on it
   clears 4.5:1) so the payment jump is easy to find. */
.fin-table.compare td:first-child { font-weight: 600; }
.sched-detail { border-top: 1px solid var(--line-card); }
.sched-detail > summary { cursor: pointer; color: var(--teal); font-weight: 700; font-size: 15px; padding: 12px 0; }
.sched-detail[open] > summary { border-bottom: 1px solid var(--line-card); margin-bottom: 6px; }
.sched-h { color: var(--mid); font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px; margin: 16px 0 6px; }
.fin-table.sched th, .fin-table.sched td { padding: 6px 10px; font-size: 13px; }
.fin-table.sched tr.reached td { background: var(--accent); color: var(--navy); font-weight: 700; }

/* Budget grid: a category total sits at the left margin; itemised lines under it
   are indented and set slightly smaller. The total row is emphasised. No colour
   carries judgement — every category reads the same. */
.fin-table.budget tr.budget-cat td:first-child { font-weight: 600; }
.fin-table.budget tr.budget-line td { font-size: 13px; color: var(--mid); border-bottom: none; }
.fin-table.budget tr.budget-line td.indent { padding-left: 26px; }
.fin-table.budget tr.budget-total td { border-top: 2px solid var(--line-card); font-weight: 700; }

/* Debt-to-income bars: mid-blue fill (6.11:1) on the card; the label rides
   inside or beside. No decorative track is required to read the value. */
.dti { list-style: none; margin: 6px 0 0; padding: 0; }
.dti li { margin: 0 0 12px; }
.dti .dti-top { display: flex; justify-content: space-between; font-size: 14px; color: var(--navy); margin-bottom: 4px; }
.dti .dti-pct { font-weight: 700; color: var(--teal); }
.dti .track { background: #dfe4e9; border-radius: 6px; height: 16px; overflow: hidden; }
.dti .fill { background: var(--mid); height: 100%; border-radius: 6px; }

/* The strip carries seven figures; step it down in even-ish rows rather than
   letting a single fixed count wrap awkwardly (7 into 4+3, then 2+2+2+1). */
@media (max-width: 980px) {
  .strip { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .grid { grid-template-columns: 1fr; }
  .projection { flex-direction: column; align-items: flex-start; }
  .strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---------------------------------------------------------------------------
   The client report: a printable web page. On screen it reads as one long
   document; the print rules below break it onto sensible pages so the headline
   never splits and the chart is never cut in half.
   --------------------------------------------------------------------------- */
/* A report is a document, so give it a light page of its own — on screen as
   well as on paper. It was styled for print (dark ink) but never given a screen
   surface, so it rendered its teal/navy text straight on the navy page at
   1.02:1: invisible. Now it sits on its own white sheet; every report colour is
   dark and pinned against this surface in contrast.test.ts. */
.report {
  max-width: 820px;
  margin: 0 auto;
  background: #fff;
  color: var(--navy);
  border-radius: 12px;
  padding: 32px 40px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.25);
}
.report-page { margin: 0 0 44px; }
.report h2 { color: var(--teal); border-bottom: 2px solid var(--line-card); padding-bottom: 6px; margin-top: 0; }
.report-note { color: var(--mid); font-size: 14px; margin: 6px 0 12px; }
.report-disclaimer { color: var(--mid); font-size: 13px; font-style: italic; margin-top: 18px; }

.report-cover { text-align: center; padding: 40px 0; }
.report-logo { display: block; max-height: 80px; max-width: 240px; object-fit: contain; margin: 0 auto 18px; }
.cover-pro { font-size: 22px; font-weight: 700; color: var(--navy); }
.cover-pro .brand-link { color: var(--navy); text-decoration: underline; }
.cover-biz { font-size: 16px; color: var(--mid); margin-top: 2px; }
.cover-title { font-size: 34px; color: var(--teal); margin: 44px 0 10px; border: 0; }
.cover-client { font-size: 20px; color: var(--navy); }
.cover-date { color: var(--mid); margin-top: 8px; }

.headline-grid { display: flex; gap: 40px; flex-wrap: wrap; align-items: flex-start; }
.hl-k { color: var(--mid); text-transform: uppercase; letter-spacing: .04em; font-size: 13px; }
.hl-date { font-size: 30px; font-weight: 700; color: var(--teal); margin: 2px 0 6px; }
.hl-vs { color: var(--navy); max-width: 22em; }
.headline-figs { display: flex; gap: 28px; }
.headline-figs .k, .report-figures .k { color: var(--mid); font-size: 13px; }
.headline-figs .v, .report-figures .v { font-size: 22px; font-weight: 700; color: var(--navy); }
.report-figures { display: flex; gap: 28px; flex-wrap: wrap; margin: 4px 0 8px; }
.report-figures .v.short { color: var(--err); }
.report-chart { margin: 12px 0; }

.report-closing .closing-contact { margin-top: 20px; padding: 16px 18px; background: var(--surface); border-radius: 8px; }
.closing-contact .k { color: var(--mid); font-size: 13px; text-transform: uppercase; letter-spacing: .04em; }
.contact-name { font-weight: 700; color: var(--navy); font-size: 18px; margin-top: 2px; }
.contact-biz { color: var(--mid); }
.contact-line { color: var(--teal); margin-top: 4px; }

/* ── The front door (GET /) ──────────────────────────────────────────────────
   The marketing page rotates its section GROUNDS so the page has rhythm instead
   of one flat navy field. With three dark grounds (navy/teal/mid) and one light
   (surface), rhythm only works if EVERY join is dark → light → dark, so a light
   ground sits between every pair of dark ones. Order top-to-bottom:
     navy → surface → teal → surface → mid → surface → navy(footer strip).
   Grounds (all measured, WCAG 2.1):
     .ground-navy    navy    bg, near-white text 10.89:1
     .ground-surface surface bg, navy       text 10.89:1
     .ground-teal    teal    bg, near-white text 10.67:1
     .ground-mid     mid     bg, near-white text  6.11:1
   RULE 1 (measured): navy #1e3653 and teal #033B4F must NEVER be adjacent
   (1.02:1 — one block, invisible seam). More strongly, NO two dark grounds are
   ever adjacent, so no join relies on a border to be legible. The seam rules
   below are kept purely as defence — in this order none of them ever fires.
   RULE 2: light-blue #b1d4e0 is used sparingly (links / one CTA outline), NEVER
   as a section ground. */
.front .section { padding: 64px 20px; }
.front .section-inner { max-width: 720px; margin: 0 auto; }
.front .section.wide .section-inner { max-width: 1040px; }

.front .ground-navy { background: var(--navy); color: var(--surface); }
.front .ground-surface { background: var(--surface); color: var(--navy); }
.front .ground-teal { background: var(--teal); color: var(--surface); }
.front .ground-mid { background: var(--mid); color: var(--surface); }

/* Headings take the section's own text colour; on a light ground the heading is
   teal (10.67:1), matching the card headings elsewhere. */
.front .section h2 { font-size: 26px; margin: 0 0 12px; color: inherit; }
.front .ground-surface h2 { color: var(--teal); }
.front .section p { margin: 0 0 14px; max-width: 40em; }
.front .section p:last-child { margin-bottom: 0; }

/* Defence only: were two dark grounds ever placed flush, this hairline seam
   keeps the join legible at the low dark-on-dark ratios. The rotation never
   places two dark grounds adjacent, so in practice none of these ever fires. */
.front .ground-navy + .ground-teal, .front .ground-teal + .ground-navy,
.front .ground-navy + .ground-mid, .front .ground-mid + .ground-navy,
.front .ground-teal + .ground-mid, .front .ground-mid + .ground-teal {
  border-top: 1px solid var(--line-navy);
}

/* Hero. The one place with primary calls to action. */
.front .hero h1 { font-size: 34px; margin: 0 0 14px; color: var(--surface); max-width: 16em; }
.front .hero .lede { color: var(--accent); font-size: 19px; max-width: 32em; }
.front .hero-actions { display: flex; justify-content: center; gap: 14px; flex-wrap: wrap; margin-top: 26px; }
/* Two-column hero: copy on the left, the amortisation graphic on the right,
   vertically centred against the copy. The graphic column is a fixed 440px at
   the hero's full 1160px width, so the SVG scales into a predictable slot. */
.front .hero .section-inner { max-width: 1160px; }
.front .hero-grid { display: grid; grid-template-columns: minmax(0, 1fr) 520px; gap: 48px; align-items: center; }
.front .hero-graphic { margin: 0; max-width: 100%; }
.front .hero-graphic svg { display: block; width: 100%; height: auto; }
/* A quiet, discreet path for professionals — a plain link, not a button. */
.front .hero-quiet { margin: 16px 0 0; font-size: 15px; }
/* Narrow screens (phones/tablets): two columns will not fit, so stack the
   graphic below the copy with clear space, as it was before. */
@media (max-width: 900px) {
  .front .hero-grid { grid-template-columns: 1fr; }
  .front .hero-graphic { margin: 32px 0 0; max-width: 560px; }
}
.front .cta {
  display: inline-block;
  padding: 13px 22px;
  border-radius: 8px;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.3px;
  text-decoration: none;
  border: 1px solid transparent;
}
/* Solid off-white fill with navy text (10.89:1) AND a navy border. The border is
   what bounds the button on a light surface ground — where an off-white fill
   would otherwise vanish — at 10.89:1, well past the 3:1 graphic minimum; on the
   dark grounds the fill itself carries the contrast. So one button treatment
   reads on every ground the page rotates through. */
.front .cta.primary, .front .cta.primary:visited {
  background: var(--surface);
  color: var(--navy);
  border-color: var(--navy);
}
/* The ghost (buy-it-myself) only ever sits on a dark ground: off-white text and
   border on the mid price band (>= 4.5:1 text, >= 3:1 border). */
.front .cta.ghost, .front .cta.ghost:visited {
  background: transparent;
  color: var(--surface);
  border-color: var(--surface);
}

/* Sub-headings inside a section take the section's own text colour; on a light
   ground that is teal (10.67:1), matching the h2 there. */
.front .section h3 { font-size: 19px; margin: 22px 0 8px; color: inherit; }
.front .ground-surface h3 { color: var(--teal); }

/* A plain bulleted list (the copy's short item runs). */
.front .plain-list { margin: 8px 0 14px; padding-left: 22px; }
.front .plain-list li { margin: 4px 0; }

/* The price call-out inside the "So here's the question" band. */
.front .price-block { margin: 22px 0; }
.front .price { font-size: 22px; font-weight: 700; margin-bottom: 8px; }

/* The closing compliance paragraph — smaller, quieter, on the light ground. */
.front .fine { font-size: 13px; color: var(--mid); max-width: 46em; }

/* Honeypot: kept in the DOM (a bot fills it) but off-screen and out of the tab
   order, so no real person ever sees or reaches it. Not display:none — some
   bots skip hidden fields, and this must look fillable to them. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Small feature list inside a section — plain, no decoration. */
.front .feature-list { list-style: none; padding: 0; margin: 8px 0 0; }
.front .feature-list li { padding: 8px 0; border-top: 1px solid var(--line-card); }
.front .ground-navy .feature-list li, .front .ground-teal .feature-list li,
.front .ground-mid .feature-list li { border-top-color: var(--line-navy); }
.front .feature-list li:first-child { border-top: 0; }
.front .feature-list b { display: block; }

.front .foot-links { margin: 0; font-size: 14px; }

/* Slim navy footer strip closes the page (dark), following the off-white "what
   it isn't" section (light) — the last dark → light → dark join. */
.front .footer-strip { padding: 22px 20px; }
.front .footer-strip .section-inner { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.front .footer-strip .foot-brand { font-weight: 700; letter-spacing: 0.5px; }
.front .footer-strip .foot-brand .mark { color: var(--accent); }

@media (max-width: 560px) {
  .front .section { padding: 44px 18px; }
  .front .footer-strip { padding: 20px 18px; }
  .front .hero h1 { font-size: 27px; }
}

@media print {
  .band { display: none; }
  main.wrap { margin: 0; padding: 0; max-width: none; }
  /* On paper the page IS the surface, so drop the on-screen sheet chrome. */
  .report { max-width: none; box-shadow: none; border-radius: 0; padding: 0; }
  /* Each report section starts a fresh page, so the six parts stay distinct and
     nothing important shares a page boundary. */
  .report-page { break-before: page; page-break-before: always; margin: 0; }
  .report-cover { break-before: auto; page-break-before: auto; }
  /* The headline, the chart and the cover must never be split across a page. */
  .report-headline, .report-chart, .report-chart-page, .cover-block { break-inside: avoid; page-break-inside: avoid; }
}
