/* ============================================================
   Mark Norman – Better Mortgage Solutions
   Site-wide design system for mark-norman.ca.
   Funnel palette (navy + gold), Inter, flat/spacious/premium:
   white space does the work, gold is a restrained accent.
   Funnels keep their own stylesheet (assets/funnel.css).
   ============================================================ */

/* ---------- Tokens ---------- */
:root {
  --navy: #173a70;
  --navy-2: #224c8f;
  --ink: #0f2748;
  --gold: #d8b56a;
  --gold-2: #ecd39b;
  --gold-deep: #8a6a24; /* AA on white (5.04:1) and paper (4.70:1) */
  --gold-tint: #f7efe0; /* icon-chip background (round-square, intent cards) */
  --white: #ffffff;
  --paper: #f6f7f9;
  --border: #dcdfe6;
  --hairline: #e6e9ef;
  --text: #333b49;
  --muted: #5f6672;
  --shadow: 0 18px 50px rgba(15, 39, 72, 0.12);
  --shadow-sm: 0 6px 18px rgba(15, 39, 72, 0.08);
  --radius: 16px;
  --radius-sm: 10px;
  --sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, Helvetica, sans-serif;
  --serif: 'Fraunces', Georgia, 'Times New Roman', serif;
  --container: 1120px;
  --gutter: clamp(18px, 4vw, 32px);
  --section-pad: clamp(48px, 8vw, 96px);
}

/* Breakpoint scale (mobile-first, min-width unless noted):
   600px  card grid 1→2 cols; trust-strip dividers appear
   720px  bottom CTA bar hidden at/above this (max-width: 719.98px)
   800px  footer stacks → 3 columns
   860px  hero + statement band split into 2 columns
   960px  card grid → 4 cols
   1250px desktop nav (drawer below) — deliberately NOT 960px; see the
          .header-inner comment above and the media query below for why */

/* Self-hosted Inter (variable) — no external font/CDN requests */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/site/fonts/inter.woff2') format('woff2');
}

/* Self-hosted Fraunces (static, subset) — display serif for hero/pull-quotes only */
@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url('/site/fonts/fraunces-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Fraunces';
  font-style: italic;
  font-weight: 400;
  font-display: optional;
  src: url('/site/fonts/fraunces-400i.woff2') format('woff2');
}

/* ---------- Base ---------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 84px;
}

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  color: var(--text);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
}

img, svg, video { max-width: 100%; height: auto; display: block; }

h1, h2, h3, h4 {
  margin: 0 0 0.5em;
  color: var(--navy);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.1rem, 1.45rem + 3vw, 3.4rem); font-weight: 700; }
h2 { font-size: clamp(1.55rem, 1.2rem + 1.8vw, 2.3rem); }
h3 { font-size: 1.15rem; letter-spacing: -0.01em; }

/* Display serif tier — true landing statements only (Home hero) */
.h1--hero {
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(2.6rem, 1.8rem + 3.6vw, 4.5rem);
  letter-spacing: -0.01em;
  line-height: 1.05;
}

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

a { color: var(--navy-2); text-underline-offset: 3px; }

ul, ol { margin: 0 0 1em; padding-left: 1.2em; }

button { font-family: inherit; }

/* Accessible focus. The transition is outline-color/-offset only (never a
   layout property), so it's a no-op under reduced motion once the global
   query below strips it — the ring itself still appears instantly. */
:focus-visible {
  outline: 3px solid var(--navy-2);
  outline-offset: 2px;
  border-radius: 3px;
  transition: outline-offset 0.12s ease, outline-color 0.12s ease;
}
.band :focus-visible,
.trust-strip :focus-visible,
.site-footer :focus-visible { outline-color: var(--gold); }

/* ---------- Layout helpers ---------- */
.container {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section-pad); }
.section--paper { background: var(--paper); }
/* emotional-beat sections breathe harder than the default rhythm */
.section--statement { padding-block: clamp(72px, 10vw, 140px); }

.section-head { max-width: 760px; margin-bottom: clamp(28px, 4vw, 48px); }
.section-head.center { margin-inline: auto; text-align: center; }
.section-head.center .lead { margin-inline: auto; }
/* second statement inside a merged statement section: hairline divider,
   heading sized between h2 and h3 */
.section-head--follow {
  margin-top: clamp(28px, 4vw, 48px);
  padding-top: clamp(28px, 4vw, 48px);
  border-top: 1px solid var(--hairline);
}
.section-head--follow h3 { font-size: clamp(1.25rem, 1.05rem + 1vw, 1.6rem); }

/* ---------- Editorial numbered sequence (Refinance) ----------
   Merges consecutive heading+text stripes into one bound sequence: an
   oversized ghost numeral sits beside each heading, hairlines separate
   the steps instead of full section padding repeating three times. */
.seq-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.seq-item {
  display: flex;
  gap: clamp(16px, 3vw, 32px);
  align-items: flex-start;
  padding-block: clamp(28px, 5vw, 44px);
}
.seq-item:first-child { padding-top: 0; }
.seq-item:last-child { padding-bottom: 0; }
.seq-item + .seq-item { border-top: 1px solid var(--hairline); }
.seq-num {
  flex: none;
  font-family: var(--serif);
  font-weight: 600;
  line-height: 1;
  font-size: clamp(40px, 3rem + 2vw, 64px);
  letter-spacing: -0.02em;
  /* gold-deep (#8a6a24) at low opacity — a pale gold-2 tint nearly
     disappears against paper/white, gold-deep at ~18% reads as a clear
     but recessive ghost numeral behind the heading */
  color: rgba(138, 106, 36, 0.2);
}
.seq-body { min-width: 0; }
.seq-body > :last-child { margin-bottom: 0; }
.seq-body .btn-link { margin-top: 8px; }
@media (max-width: 599.98px) {
  .seq-item { gap: 10px; }
  .seq-num { font-size: clamp(32px, 16vw, 44px); }
}

/* ---------- "How I work" ladder (Strategy) ----------
   Structure: ol.ladder > li.ladder-step > (span.ladder-num[aria-hidden] +
   span.ladder-body > (strong.ladder-name + span.ladder-desc)). Stays a real
   <ol> — screen readers already announce "1 of 5" etc from native list
   semantics, so the visual numeral is aria-hidden to avoid double-reading it.
   A single hairline runs the full height of the list behind the numeral
   column (one continuous line, not per-row math) so it stays correct no
   matter how long any one step's description runs; each numeral sits on an
   opaque background so the rule reads as passing behind it, not through it. */
.ladder {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 640px;
  position: relative;
}
.ladder::before {
  content: "";
  position: absolute;
  top: 28px;
  bottom: 28px;
  left: 36px;
  width: 1px;
  background: var(--hairline);
}
.ladder-step {
  display: flex;
  gap: clamp(16px, 3vw, 28px);
  padding-block: clamp(20px, 3vw, 28px);
}
.ladder-step:first-child { padding-top: 0; }
.ladder-step:last-child { padding-bottom: 0; }
.ladder-num {
  position: relative;
  flex: none;
  width: 72px;
  background: var(--white);
  font-family: var(--serif);
  font-weight: 600;
  font-size: 56px;
  line-height: 1;
  text-align: center;
  color: var(--gold-deep);
  font-variant-numeric: tabular-nums;
}
.ladder-body {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding-top: 8px;
  min-width: 0;
}
.ladder-name { font-size: 18px; font-weight: 700; color: var(--navy); }
.ladder-desc { font-size: 15px; line-height: 1.55; color: var(--muted); }
@media (max-width: 599.98px) {
  .ladder::before { left: 28px; }
  .ladder-num { width: 56px; font-size: 40px; }
}

.eyebrow {
  display: inline-block;
  margin-bottom: 10px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--navy-2);
}
/* Gold accent: deep gold on light backgrounds (AA-compliant),
   bright gold on navy/ink components via the scoped overrides below. */
.accent { color: var(--gold-deep); }
.band .accent,
.trust-strip .accent,
.site-footer .accent { color: var(--gold); }

.lead {
  font-size: clamp(1.05rem, 1rem + 0.4vw, 1.2rem);
  font-weight: 500;
  line-height: 1.65;
  color: var(--text);
  max-width: 56ch;
}

/* Pull-quote component — serif emotional beats; gold rule stands in for a left border */
.pullquote {
  margin: 0 auto;
  max-width: 26ch;
  text-align: center;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(1.5rem, 1.1rem + 2vw, 2.2rem);
  line-height: 1.3;
  color: var(--navy);
}
.pullquote::before {
  content: "";
  display: block;
  width: 40px;
  height: 2px;
  margin: 0 auto 20px;
  background: var(--gold);
}
/* support line directly under a pull-quote: stays a normal paragraph
   (never nested inside .pullquote) but keeps the same quiet, centered
   editorial register */
.pullquote + p {
  max-width: 46ch;
  margin: 20px auto 0;
  text-align: center;
  color: var(--muted);
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- Buttons ---------- */
.btn-gold, .btn-navy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 26px;
  border: none;
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.15s ease, transform 0.12s ease, box-shadow 0.15s ease;
}
.btn-gold { background: var(--gold); color: var(--ink); }
.btn-gold:hover { background: #cfa851; transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.btn-gold:active { transform: scale(0.97); }
.btn-navy { background: var(--navy); color: var(--white); }
.btn-navy:hover { background: var(--navy-2); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.btn-navy:active { transform: scale(0.97); }
.btn-sm { padding: 10px 18px; font-size: 12.5px; }

.btn-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding-block: 11px; /* ≥44px hit area for standalone text links */
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--navy);
  text-decoration: none;
}
.btn-link::after {
  content: "\2192"; /* → */
  color: var(--gold-deep); /* AA on white/paper; bright gold on dark below */
  font-size: 1.1em;
  transition: transform 0.15s ease;
}
.band .btn-link::after,
.trust-strip .btn-link::after,
.site-footer .btn-link::after { color: var(--gold); }
.btn-link:hover { text-decoration: underline; text-underline-offset: 4px; }
.btn-link:hover::after { transform: translateX(3px); }
/* on navy bands the link itself goes white */
.band .btn-link { color: var(--white); }
/* buttons keep their identity inside bands (.band a would otherwise
   recolor them): gold stays ink-on-gold; navy becomes a white-outlined
   ghost button (a navy fill would vanish against the navy band) */
.band .btn-gold { color: var(--ink); }
.band .btn-navy {
  color: var(--white);
  background: transparent;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.9);
}
.band .btn-navy:hover { background: rgba(255, 255, 255, 0.12); }

/* ---------- Skip link ---------- */
.skip-link {
  position: absolute;
  top: 10px; left: 10px;
  z-index: 100;
  padding: 10px 16px;
  background: var(--navy);
  color: var(--white);
  font-weight: 700;
  font-size: 14px;
  border-radius: var(--radius-sm);
  text-decoration: none;
  transform: translateY(-200%);
}
.skip-link:focus { transform: none; }

/* ---------- Header ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--white);
  border-bottom: 1px solid var(--hairline);
}
.header-inner {
  display: flex;
  align-items: center;
  gap: 20px;
  min-height: 64px;
  position: relative; /* anchors the mobile drawer */
  /* R4 W1.1b: the header row gets its own, wider max-width than the
     sitewide 1120px `.container` column (this element carries both
     classes) — same specificity as .container's max-width, later in the
     file, so this wins. Logo + 6 nav links + the header micro-badge +
     phone + GET STARTED genuinely need more than 1120px at desktop
     widths without wrapping/overflow; body content sections keep the
     narrower 1120px column untouched (they don't carry .header-inner). */
  max-width: 1320px;
}
.header-logo { display: inline-flex; flex: 0 0 auto; }
.header-logo img { width: 150px; }

.site-nav a {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}
.site-nav a:hover { color: var(--navy-2); }
.site-nav a[aria-current="page"] {
  color: var(--navy);
  box-shadow: inset 0 -2px 0 var(--gold);
}
.site-nav .btn-gold { color: var(--ink); }
.site-nav .btn-gold:hover { color: var(--ink); }

/* Header phone (spec W5): quiet desktop tel: text link inside .site-nav
   (see .nav-phone below, desktop media query) + a 44x44 icon button next to
   the mobile burger — mobile-first, hidden at 1250px+ where .nav-phone takes
   over (see the desktop-nav breakpoint comment below — QA round 4 audit #1
   raised this from 960px). Owns the margin-left:auto that pushes the
   icon/burger pair to the header's right edge (mirrors the pre-existing
   .nav-toggle rule it replaces below), so only one flex item claims the
   auto margin. */
.header-phone-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: auto;
  width: 44px; height: 44px;
  border-radius: var(--radius-sm);
  color: var(--ink);
  transition: background-color 0.15s ease;
}
.header-phone-icon svg { width: 18px; height: 18px; }
.header-phone-icon:hover,
.header-phone-icon:focus-visible { background: var(--paper); }

/* hamburger — mobile-first: visible by default, hidden at 1250px+ (QA
   round 4 audit #1 — see the desktop-nav media query below) */
.nav-toggle {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px; height: 44px;
  padding: 10px;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.nav-toggle .nav-bar {
  display: block;
  width: 22px; height: 2px;
  background: var(--ink);
  border-radius: 2px;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav-toggle[aria-expanded="true"] .nav-bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-bar:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* mobile drawer — base state (mobile-first); desktop overrides at 1250px+
   (QA round 4 audit #1 — see the media query below) */
.site-nav {
  position: absolute;
  top: 100%;
  left: 0;  /* .container has no border, so this is the full viewport width */
  right: 0;
  display: flex;
  flex-direction: column;
  padding: 6px var(--gutter) 20px;
  background: var(--white);
  border-bottom: 1px solid var(--hairline);
  box-shadow: var(--shadow-sm);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s;
}
.site-nav.is-open { opacity: 1; visibility: visible; transform: none; }
.site-nav a {
  padding: 13px 2px;
  font-size: 16px;
  border-bottom: 1px solid var(--hairline);
}
.site-nav .btn-gold {
  margin-top: 16px;
  padding: 14px 26px;
  font-size: 14px;
  border-bottom: none;
  justify-content: center;
}
/* desktop-only quiet tel: link (see .header-phone-icon above for its mobile
   equivalent) — hidden in the mobile drawer so the phone number appears
   exactly once per breakpoint, never both the icon and this text link. */
.nav-phone { display: none; }

/* mobile drawer scrim — sits between page content and the header/drawer */
.nav-scrim {
  position: fixed;
  inset: 0;
  z-index: 49; /* below .site-header (50), above everything else */
  background: rgba(15, 39, 72, 0.45);
  border: none;
  padding: 0;
  cursor: default;
}
.nav-scrim[hidden] { display: none; }

/* desktop nav — deliberately 1250px, NOT the sitewide 960px content
   breakpoint (QA round 4 audit #1, punch list item a, re-measured for
   tranche A). MEASURED (Playwright, R8 tranche A): the 7-link nav
   (Next Home added after Mortgage Strategy) + phone + GET STARTED's
   natural (unwrapped) width breaks even at a ~1213px viewport — still
   72px of scrollWidth-over-innerWidth at 1141px and 13px at 1200px,
   zero from 1213px up (the pre-R8 6-link row's breakeven was ~1110px,
   hence the old 1140px value). 1250px keeps a ~35px safety margin above
   the measured 1213px breakeven (font-rendering/zoom variance — the
   same margin logic as the .header-g-badge threshold below). Below
   1250px the mobile drawer/burger serves instead, all the way up to the
   point the row fits; nothing else in the site keys off 960px for nav,
   so this is scoped to the rules directly touching header/nav layout. */
@media (min-width: 1250px) {
  .header-inner { min-height: 72px; }
  .header-logo img { width: 168px; }
  .header-phone-icon { display: none; }
  .nav-toggle { display: none; }
  .nav-scrim { display: none; }
  .site-nav {
    position: static;
    margin-left: auto;
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 26px;
    padding: 0;
    background: none;
    border-bottom: none;
    box-shadow: none;
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: none;
  }
  .site-nav a { padding: 0; font-size: 14.5px; border-bottom: none; }
  /* quiet tel: link — deliberately plainer than the primary nav items (muted
     color, no underline sweep, no aria-current concept) so it reads as
     secondary/ambient rather than another nav destination. */
  .nav-phone {
    display: inline-flex;
    align-items: center;
    color: var(--muted);
    font-weight: 500;
    margin-left: 4px;
  }
  .nav-phone:hover,
  .nav-phone:focus-visible { color: var(--navy-2); }
  /* underline sweep on hover/focus, desktop nav only — mobile drawer links
     keep the plain hairline-divider rows above, untouched. The current-page
     link already gets a static full underline via the box-shadow rule above
     (.site-nav a[aria-current="page"]), so it's excluded here to avoid a
     double underline; .nav-phone is excluded too, it gets its own quieter
     hover treatment right above instead. .g-badge (the header micro-badge,
     R4 W1) is excluded too — it's a compact stat lockup, not a text label,
     so the sweep underline would cut through its stars/numeral oddly. */
  .site-nav a:not([aria-current="page"]):not(.btn-gold):not(.nav-phone):not(.g-badge) { position: relative; }
  .site-nav a:not([aria-current="page"]):not(.btn-gold):not(.nav-phone):not(.g-badge)::after {
    content: "";
    position: absolute;
    left: 0; right: 0;
    bottom: -7px;
    height: 2px;
    background: var(--navy-2);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.2s ease;
  }
  .site-nav a:not([aria-current="page"]):not(.btn-gold):not(.nav-phone):not(.g-badge):hover::after,
  .site-nav a:not([aria-current="page"]):not(.btn-gold):not(.nav-phone):not(.g-badge):focus-visible::after {
    transform: scaleX(1);
  }
  .site-nav .btn-gold {
    margin: 0 0 0 6px;
    /* restore the pre-refactor computed values: .btn-sm padding, but
       font-size 14.5px because the base .site-nav a rule always outranked
       .btn-sm's 12.5px on desktop */
    padding: 10px 18px;
    font-size: 14.5px;
  }
}

/* Header micro-badge (spec R4 W1.1b): a .g-badge--small dropped into the
   nav between the primary links and the quiet phone link, linking to the
   recognition module (#reviews on Home, /#reviews everywhere else — see
   each page's href). Desktop-only, gated at 1590px rather than the
   spec's suggested 1100px — MEASURED, not guessed (R4 W1 originally
   landed on 1360px for the 6-link nav: badge breakeven ~1318px + margin;
   re-measured for R8 tranche A when the 7th "Next Home" link pushed the
   badge-bearing row's breakeven out to ~1550px — still 23px of
   scrollWidth-over-innerWidth at 1500px, 3px at 1540px, zero at 1550px,
   Playwright). 1590px keeps a ~40px safety margin above that measured
   1550px breakeven (font-rendering/zoom variance — same margin logic as
   the desktop-nav breakpoint above); common 1366-1536px laptop widths
   now sit below the threshold and simply don't show the badge, the same
   graceful drop the old 1360px gate already applied below ITS line.
   Below the threshold the badge is hidden entirely — the mobile header's 110px
   budget (check-mobile.mjs) is untouched by this component regardless
   (confirmed: the badge's ~19px content height sits comfortably inside
   the existing 72px .header-inner row at desktop widths too, so it never
   grows the header — .header-inner also got its own 1320px max-width,
   up from the sitewide 1120px .container column, purely so the row has
   room to breathe at the very top of this range). Selectors are
   `.site-nav .header-g-badge` (not the bare class) so this always wins
   the display-property tie against the plain `.g-badge { display:
   inline-flex }` base rule declared later in this file — same
   specificity as a bare class, so source order alone would otherwise let
   that later rule leak the badge onto mobile. */
.site-nav .header-g-badge { display: none; }
@media (min-width: 1590px) {
  .site-nav .header-g-badge {
    display: inline-flex;
    gap: 6px;
    padding-left: 12px;
    margin-left: 0;
    border-left: 1px solid var(--hairline);
  }
}

/* ---------- Trust strip (navy) ---------- */
.trust-strip { background: var(--navy); color: #eef1f7; }
.trust-strip .container {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  padding-block: 6px;
}
/* mobile-first: one centered line of short labels (.t-short), tight padding,
   no dividers; 600px+ shows the full labels (.t-long) with dividers.
   Both texts stay in the DOM — the short duplicates carry aria-hidden. */
.trust-strip .trust-item {
  display: inline-flex;
  align-items: center;
  padding: 1px 4px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.035em;
  text-transform: uppercase;
  white-space: nowrap;
}
.trust-strip .t-long { display: none; }
/* R4-hotfix-3: CCA item is a link (-> #reviews) with an award-seal glyph
   ahead of the text. It carries its full wording ("Consumer Choice Award
   2023–2027") directly as a text node — no t-long/t-short split — because
   that wording no longer abbreviates at any width (see the file header's
   "Trust strip" note in index.html's history: it used to shrink to "CCA
   2023–2027" on mobile; restored to the full phrase here). Quiet-link
   styling: the strip's white color is inherited by every other element,
   but the UA default <a> color/underline would otherwise win over that
   inheritance, so both are reset explicitly and only re-added on
   hover/focus. The seal is a cropped, 2x-resolution raster of the real
   /site/img/cca-2023-2027.png artwork (site/img/cca-seal-strip.png, 39x56
   physical px behind a 19x28 CSS box) — a client-reported bug (round-4
   hotfix-3) found the prior simplified vector medal (gold face + gold-deep
   ring/ribbon + a faint navy star) didn't read as the actual Consumer
   Choice Award seal at all. (That medal was also rendering smaller than
   its own rule intended — a `.trust-strip svg { width:13px; height:13px }`
   fallback rule elsewhere in this file had higher specificity than the
   `.trust-cca-seal` class rule and was winning, at 13x13 instead of the
   documented 18x19.5. Removed along with the <svg> — dead now that the
   seal is an <img>.) At the strip's small scale the real asset's ribbon
   lettering is illegible, but that's fine: the gold ring + red disc + red
   ribbon tails read clearly
   (screenshot-compared at actual size before shipping) and the adjacent
   text carries the words. The container's block padding and the item's
   block padding are both trimmed slightly (9px->6px, 2px->1px) to make
   room for the taller seal within the 110px header+strip mobile budget
   (scripts/check-mobile.mjs) without changing anything else about the
   strip's layout. */
.trust-strip a.trust-item { color: inherit; text-decoration: none; }
.trust-strip a.trust-item:hover,
.trust-strip a.trust-item:focus-visible { text-decoration: underline; text-underline-offset: 2px; }
/* R5 hotfix (Task 3a) — "overhang" treatment: the client pays for use of
   the CCA logo and wants it more prominent, so the seal is enlarged from
   28px to 44px tall (site/img/cca-seal-strip.png regenerated at 61x88
   physical px — still a clean 2x crop of the 44px/~30.5px CSS box, crisp
   on retina). Equal negative top/bottom margins (-8px each) keep the
   image's MARGIN box at exactly 28px — identical to the pre-hotfix
   height — so the flex row's line-box, and therefore the whole
   .trust-strip element's measured height, is byte-for-byte unchanged.
   Only the image's own painted content escapes that margin box (allowed
   by the strip/container/item's default overflow:visible), so the seal
   visually pokes ~1px past the strip's own top and bottom edges — the
   "breaking the edges" overhang look — while scripts/check-mobile.mjs's
   header+trust-strip <=110px gate sees no change at all (confirmed:
   107px at 390/360 before and after, 3px of headroom untouched). */
.trust-cca-seal { height: 44px; width: auto; margin-right: 4px; margin-top: -8px; margin-bottom: -8px; flex: 0 0 auto; }
@media (min-width: 600px) {
  .trust-strip .trust-item {
    padding-inline: 16px;
    font-size: 11.5px;
    letter-spacing: 0.07em;
  }
  .trust-strip .trust-item + .trust-item { border-left: 1px solid rgba(255, 255, 255, 0.25); }
  .trust-strip .t-long { display: inline; }
  .trust-strip .t-short { display: none; }
  .trust-cca-seal { margin-right: 7px; }
}
/* Mobile-only: trim the container's own side padding (it otherwise inherits
   the sitewide --gutter, 18px at these widths) so the wider real-seal item
   plus the "Since 2004" item keep fitting on one centered line at 360px —
   the narrowest width the mobile gate checks. Scoped to <600px only; the
   600px+ two-item desktop row already has plenty of room. */
@media (max-width: 599.98px) {
  .trust-strip .container { padding-inline: 10px; }
}
/* Board of Trade membership drops from the strip below 600px only (desktop
   keeps all three items) — the CCA item's now-unabbreviated wording plus
   its seal glyph already fill a two-item mobile line; a third item would
   force a wrap. */
@media (max-width: 599.98px) {
  .trust-strip .trust-item--bot { display: none; }
}

/* ---------- Breadcrumb (Learn index + article pages) ----------
   Structure: nav.breadcrumb > .container > ol > li > (a | span[aria-current]).
   An <ol> is the semantically correct list structure for a breadcrumb trail.
   The › separator is CSS-generated on the <li>, not on the link/span itself —
   putting it on the interactive element would pull the glyph into that
   element's accessible name in Chromium/Firefox. */
.breadcrumb { border-bottom: 1px solid var(--hairline); }
.breadcrumb .container { padding-block: 14px; }
.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}
.breadcrumb a { color: var(--muted); text-decoration: none; }
.breadcrumb a:hover { color: var(--navy-2); text-decoration: underline; text-underline-offset: 3px; }
.breadcrumb [aria-current="page"] { color: var(--text); font-weight: 600; }
.breadcrumb li:not(:last-child)::after {
  content: "\203A"; /* › */
  margin: 0 8px;
  color: var(--border);
  font-weight: 400;
}

/* ---------- Hero (split: text left / portrait right) ----------
   Structure: .hero > .container > .hero-grid > (.hero-copy + .hero-media > img) */
.hero { padding-block: var(--section-pad); }
.hero-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
}
.hero-copy .lead { margin-top: 18px; }
/* Grid items default to a content-based min-width, which lets the
   nowrap/overflow-x chip row's intrinsic (unwrapped) width blow out the
   1fr track and cause page-level horizontal scroll on mobile. Overriding
   it lets .intent-chip-row's own overflow-x:auto contain the scroll
   instead of the hero column stretching to fit it. */
.hero-copy { min-width: 0; }
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 24px;
  margin-top: 28px;
}
/* Mobile intent chip row (Home hero, spec R3 W5) — quick funnel/page
   shortcuts mirroring the 4 intent cards' primary CTAs, for thumb-reach
   right under the fold before the visitor scrolls to the full card grid.
   Desktop keeps the split hero clean: hidden at 960px+ where the card
   grid is already one scroll away. Horizontally scrollable (not wrapped)
   so it stays a single compact row instead of pushing the hero image down. */
.intent-chip-row { display: none; }
@media (max-width: 959px) {
  .intent-chip-row {
    display: flex;
    gap: 10px;
    margin-top: 20px;
    padding-bottom: 4px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .intent-chip-row::-webkit-scrollbar { display: none; }
}
.intent-chip {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  min-height: 44px; /* thumb-friendly tap target */
  padding: 0 18px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--white);
  font-size: 13px;
  font-weight: 700;
  color: var(--navy);
  text-decoration: none;
  white-space: nowrap;
  transition: border-color 0.15s ease, transform 0.12s ease;
}
.intent-chip:hover { border-color: var(--navy-2); }
.intent-chip:active { transform: scale(0.97); }
.hero-media img {
  width: 100%;
  border-radius: var(--radius);
  object-fit: cover;
}
@media (min-width: 860px) {
  .hero-grid { grid-template-columns: 1.05fr 0.95fr; }
}

/* Gold offset frame — the split-hero portrait (Home + About) gets a 1px gold
   border offset toward the bottom-right, behind the image. Scoped to .hero so
   mid-page sections that reuse .hero-media stay unframed. */
.hero .hero-media { position: relative; }
.hero .hero-media::before {
  content: "";
  position: absolute;
  inset: 12px -12px -12px 12px; /* mobile: smaller offset so it clears the 18px gutter */
  border: 1px solid var(--gold);
  border-radius: var(--radius);
  z-index: -1;
  pointer-events: none;
}
@media (min-width: 860px) {
  .hero .hero-media::before { inset: 24px -24px -24px 24px; }
}

/* Hero seal (spec R5 hotfix Task 3b) — the real CCA artwork, anchored as a
   corner "stamp" on the split-hero portrait (Home + About; both share the
   same .hero > .hero-media > img structure). Bottom-LEFT on purpose: the
   gold offset frame above (.hero-media::before) peeks out bottom-RIGHT, so
   the two decorations never collide. drop-shadow (not box-shadow) follows
   the artwork's own alpha silhouette — the seal is a roundel + ribbon tail,
   not a rectangle — so the shadow reads as the medal casting a shadow on
   the photo rather than a pasted-on box. Sized ~90-120px TALL (client ask;
   height, not width — the artwork is portrait-oriented, ~345:500, so
   clamping width instead would render a much taller badge than intended)
   via clamp so it holds that range from small phones through desktop
   rather than scaling arbitrarily with viewport. Never reveal-gated —
   it's proof, same rule as the recognition module elsewhere on these
   pages. Selector is `.hero-media .hero-seal`, not the bare class: the
   existing `.hero-media img { width:100% }` rule (line ~825) targets
   every img in this container, portrait included, and its specificity (1
   class + 1 type) otherwise beats a bare `.hero-seal` (1 class), silently
   blowing the seal up to the portrait's full width. Two classes
   deliberately out-specifies it. */
.hero-media .hero-seal {
  position: absolute;
  z-index: 2;
  bottom: 14px;
  left: 14px;
  height: clamp(90px, 9vw, 120px);
  width: auto;
  filter: drop-shadow(0 10px 22px rgba(15, 39, 72, 0.35));
  pointer-events: none;
}

/* ---------- Editorial hero (Strategy / Refinance / Home Equity / Debt) ----------
   No image: the statement IS the hero. Full clamp-scale serif h1, generous
   statement-scale padding, and a hero-scale echo of the .review-card gold
   quote mark — Mark's voice, staged. */
.hero--editorial {
  position: relative;
  overflow: hidden;
  padding-block: clamp(72px, 10vw, 140px);
  border-bottom: 1px solid var(--hairline);
}
.hero--editorial .hero-copy { position: relative; }
.hero--editorial .hero-copy::before {
  content: "\201C";
  content: "\201C" / ""; /* decorative — empty alt text where supported */
  position: absolute;
  top: -0.34em;
  left: -0.05em;
  z-index: -1;
  font-family: Georgia, 'Times New Roman', serif; /* matches .review-card's quote mark */
  font-size: clamp(120px, 24vw, 300px);
  line-height: 1;
  color: var(--gold);
  opacity: 0.22;
  pointer-events: none;
}
.hero--editorial .h1--hero { max-width: 20ch; }
.hero--editorial .hero-copy p { max-width: 34rem; }
/* wide screens: the standfirst + CTA step right of the headline's left edge —
   a magazine deck offset that keeps the full-width hero from going lopsided.
   The .eyebrow (R8: next-home's kicker line ABOVE the h1) is exempt — a
   kicker reads as part of the headline, so it stays on the h1's left edge
   rather than taking the deck offset meant for what follows the h1. */
@media (min-width: 1100px) {
  .hero--editorial .hero-copy > :not(h1) { margin-left: 26%; }
  .hero--editorial .hero-copy > .eyebrow { margin-left: 0; }
}

/* ---------- Card grid (intent cards: 4 → 2 → 1) ---------- */
.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}
@media (min-width: 600px) { .card-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 960px) {
  .card-grid { grid-template-columns: repeat(4, 1fr); }
  .card-grid--cols-2 { grid-template-columns: repeat(2, 1fr); }
  .card-grid--cols-3 { grid-template-columns: repeat(3, 1fr); }
}

.intent-card {
  position: relative; /* anchors the .card-link whole-card overlay */
  overflow: hidden; /* clips the top-accent bar to the card's rounded corners */
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: clamp(20px, 3vw, 28px);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
/* gold top-accent: hidden by default, fades in on hover/focus (opacity only —
   the bar is always present at 2px, so nothing shifts layout) */
.intent-card::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--gold);
  opacity: 0;
  transition: opacity 0.15s ease;
}
.intent-card:hover,
.intent-card:focus-within {
  border-color: var(--navy-2);
  box-shadow: var(--shadow-sm);
  transform: translateY(-4px);
}
.intent-card:hover::before,
.intent-card:focus-within::before { opacity: 1; }
/* icon chip: 48px rounded-square, tinted gold background, icon in gold-deep
   (AA-legible) rather than the lighter --gold used elsewhere against white */
.intent-card-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  margin-bottom: 6px;
  border-radius: 12px;
  background: var(--gold-tint);
}
.intent-card-icon svg { width: 22px; height: 22px; color: var(--gold-deep); }
.intent-card h3 { margin-bottom: 0; }
.intent-card p { font-size: 14.5px; line-height: 1.55; color: var(--muted); }
/* in-card links keep their compact padding: the .card-link overlay already
   makes the whole card the tap target, so the 11px standalone padding would
   only inflate card height */
.intent-card .btn-link { margin-top: auto; padding-block: 8px 0; }
/* whole-card click affordance when the card's link carries .card-link */
.intent-card .card-link::before { content: ""; position: absolute; inset: 0; }
/* secondary in-card link (e.g. an alternate CTA below the main .card-link):
   position + z-index lift it above the .card-link::before whole-card overlay
   so it stays independently clickable instead of being swallowed by it. */
.intent-card .card-link-secondary { position: relative; z-index: 1; margin: 0; font-size: 13px; }

/* Card destination captions (spec R3 W7 #5, draft copy pending Mark): a one-line
   "here's where this takes you" caption under a Get Started card's primary
   link (and the safety-net row). Plain text, not a link, so — unlike
   .card-link-secondary — it needs no z-index treatment against the
   whole-card .card-link::before overlay. */
.intent-card .card-caption,
.safety-net-body .card-caption {
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
}

/* Home §7 merge (spec R3 W5): the specialized-solutions reprise now lives
   INSIDE the recognition/proof section, below the lockup + reviews grid, as
   a hairline-divided sub-row rather than its own full section (10→9). The
   two cards reuse .intent-card verbatim but get a compact modifier so the
   reprise reads as a lighter echo, not a second full-strength card grid. */
.proof-solutions {
  margin-top: clamp(28px, 4vw, 48px);
  padding-top: clamp(28px, 4vw, 48px);
  border-top: 1px solid var(--hairline);
}
.intent-card--compact { padding: clamp(16px, 2.2vw, 22px); gap: 8px; }
.intent-card--compact h3 { font-size: 1rem; }
.intent-card--compact p { font-size: 13.5px; }

/* ---------- Get Started two-tier refocus (spec R4 W4.3) ----------
   Round 3's 5-up card grid + quiet "I'm not sure yet" safety-net row
   (formerly here) duplicated 4 of Home's own intent cards. Round 4 slims
   this page to the two personas Home doesn't serve — Purchase and "I'm not
   sure yet" — as equal-weight LARGE primary cards, with everything else
   (rate/equity/debt/refinance/renewal) collapsed into a quiet direct-paths
   list below. */

/* Primary tier: a bespoke 1→2 col grid (not .card-grid's cols-2 modifier,
   which flips at 600px/960px) so exactly 2 large cards sit side-by-side
   starting at 720px — this page's own mobile/desktop split — and stack
   below it. */
.primary-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(20px, 3vw, 28px);
}
@media (min-width: 720px) {
  .primary-grid { grid-template-columns: repeat(2, 1fr); }
}
/* Primary-card scale-up: bigger icon chip + heading so Purchase and "I'm
   not sure yet" read as the page's main event, not just two more grid
   cells. */
.intent-card--primary { padding: clamp(28px, 4vw, 40px); gap: 14px; }
.intent-card--primary .intent-card-icon {
  width: 64px;
  height: 64px;
  margin-bottom: 8px;
  border-radius: 16px;
}
.intent-card--primary .intent-card-icon svg { width: 30px; height: 30px; }
.intent-card--primary h3 { font-size: clamp(1.3rem, 1.05rem + 1vw, 1.6rem); }
.intent-card--primary p { font-size: 1rem; }

/* Direct-paths row: a quiet, hairline-divided list — visually subordinate
   to the primary tier via reduced width, no icons, no gold accents, and a
   compact type scale. Each row is a single .btn-link (so it's picked up by
   both check-mobile.mjs's tap-target overlap check and site.js's bar
   band-guard — the same selector, `a.btn-link`, that already protects
   every other real CTA on the page) with title + caption typography
   overridden by source order below the base .btn-link rule. */
.path-group {
  max-width: 760px;
  margin: clamp(40px, 5vw, 56px) auto 0;
}
.path-group-label { display: block; text-align: center; }
.path-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--hairline);
}
.path-row { border-bottom: 1px solid var(--hairline); }
.path-row-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: 100%; /* ensures a full-width, unambiguous ≥44px tap target */
  padding: 16px 6px;
  text-transform: none;
  letter-spacing: normal;
  font-weight: 600;
  color: var(--ink);
}
.path-row-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  text-align: left;
}
.path-row-title { font-size: 15.5px; }
.path-row-caption { font-size: 12.5px; font-weight: 400; color: var(--muted); }
.path-row-link:hover .path-row-title,
.path-row-link:focus-visible .path-row-title { text-decoration: underline; text-underline-offset: 3px; }
@media (min-width: 720px) {
  /* title + caption share one line once there's room, keeping each row
     compact instead of growing taller than it needs to be */
  .path-row-text { flex-direction: row; align-items: baseline; gap: 14px; }
  .path-row-caption { flex: 1; }
}

/* ---------- Navy statement band ----------
   Structure: .band > .container > .band-grid > (text div + .band-media > img) */
.band {
  background: var(--navy);
  color: var(--white);
  padding-block: var(--section-pad);
}
.band--ink { background: var(--ink); }
.band h1, .band h2, .band h3 { color: var(--white); }
/* statement bands become serif moments — scaled up from the base h2 tier.
   h1 rides the same calmer statement scale (not the full .h1--hero landing
   scale) — the one page that puts a bare h1 inside a .band is Calculators'
   navy intro (spec R4 W5.1): a "fitted, calmer serif tier" appropriate to a
   utility page, rather than reusing the oversized hero treatment. */
.band h1, .band h2 {
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(2rem, 1.5rem + 2vw, 3.4rem);
}
.band p { color: rgba(255, 255, 255, 0.85); }
.band .eyebrow { color: var(--gold-2); }
.band a { color: var(--white); }

.band-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
}
.band-media img {
  width: 100%;
  border-radius: var(--radius);
  object-fit: cover;
}
@media (min-width: 860px) {
  .band-grid { grid-template-columns: 1.1fr 0.9fr; }
}

/* ---------- Signature full-bleed brand bands (Home + Strategy) ----------
   The mug leaves its rounded box: it becomes a full-bleed layer bleeding off
   one edge, with a navy gradient knockout keeping the copy zone pure navy.
   Structure: .band--signature > (img.band-signature-media + .container >
   .band-signature-copy). The ::after gradient sits between image and copy.
   Fixed min-heights + absolutely positioned media = zero layout shift. */
.band--signature {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  min-height: min(70vh, 720px);
}
.band--signature--sm { min-height: min(50vh, 560px); }
.band-signature-media {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 60%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
}
.band--signature::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, #173a70 42%, rgba(23, 58, 112, 0) 72%);
}
.band--signature .container {
  position: relative;
  z-index: 1;
  width: 100%; /* flex item would otherwise shrink-to-fit */
}
.band-signature-copy { width: fit-content; }
.band--signature .eyebrow { letter-spacing: 0.18em; margin-bottom: 14px; }
.band--signature h2 {
  max-width: 17ch; /* keeps the statement inside the solid-navy zone */
  margin-bottom: 0;
  font-size: clamp(2.1rem, 1.55rem + 2.3vw, 3.6rem);
}
/* mirrored echo variant (Strategy): desk scene bleeds off the LEFT edge,
   copy sits right — the mug in mug-desk lives in the image's left third */
.band--signature--flip .band-signature-media {
  right: auto; left: 0;
  width: 58%;
  object-position: 35% 60%;
}
.band--signature--flip::after {
  background: linear-gradient(270deg, #173a70 46%, rgba(23, 58, 112, 0) 88%);
}
.band--signature--flip .band-signature-copy { margin-left: auto; }
/* stacked mobile: dimmed full-bleed image behind the copy */
@media (max-width: 859.98px) {
  .band--signature,
  .band--signature--sm {
    min-height: 0;
    padding-block: clamp(64px, 14vw, 96px); /* the moment still breathes when stacked */
  }
  .band-signature-media,
  .band--signature--flip .band-signature-media {
    width: 100%;
    left: 0; right: 0;
    object-position: 50% 50%;
  }
  .band--signature::after,
  .band--signature--flip::after { background: rgba(15, 39, 72, 0.74); }
  .band--signature h2 { max-width: none; }
  .band--signature--flip .band-signature-copy { margin-left: 0; }
}

/* slim conversation bar: statement left, button right */
.band--slim { padding-block: clamp(26px, 4vw, 40px); }
.band-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 18px 28px;
}
.band-row h2, .band-row p { margin: 0; }

/* ---------- NL device (R4 W5.3) ----------
   Original line-art silhouette of the Narrows/Signal Hill (Cabot Tower on
   the hill, the harbour gap between the cliffs) — an ownable local visual
   device the site was missing despite Mark's own copy invoking Signal Hill.
   One consistent deployment pattern, applied via this modifier: a low-
   opacity watermark anchored bottom-LEFT of every qualifying navy band —
   the Home mug band (.band--signature) + every page's closing CTA band.
   Bottom-left (not bottom-right) is deliberate: the signature band's photo
   already occupies the right ~60% of that section, so bottom-right would
   sit directly under opaque imagery and never be visible; bottom-left is
   guaranteed solid navy on every band this ships on (signature bands keep
   their left zone image-free by design; closing bands center their copy,
   leaving both corners clear). Pure CSS background — no DOM node, no alt
   text needed (decorative only). `.container` gets an explicit stacking
   context above it so copy is never behind it, even where they overlap
   (opacity 0.16 means the device would never meaningfully compete with
   text contrast anyway — this is belt-and-suspenders). Static, not
   .reveal-gated: spec W3 permits animating it but doesn't require it, and
   a static watermark is simpler and keeps it out of the check-flick
   surface entirely. */
.band--device { position: relative; overflow: hidden; }
.band--device::before {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: clamp(150px, 30vw, 380px);
  aspect-ratio: 3 / 1;
  background: url('/site/img/nl-device.svg') no-repeat bottom left / contain;
  opacity: 0.16;
  pointer-events: none;
  /* z-index:1 (not the default auto/0) is required on .band--signature: its
     own ::after gradient overlay is opaque navy across the device's whole
     left-corner zone, and an auto/0 z-index there paints AFTER (i.e. over)
     a ::before with z-index:0 — see the file's stacking notes. Explicit 1
     lifts the device above that gradient everywhere it ships; .container's
     matching z-index:1 (below) still wins on DOM order, so copy stays on
     top. No pixel overlap with the signature band's photo (bottom-left
     device vs. the photo's right ~60% zone), so painting above it too is
     harmless. */
  z-index: 1;
}
.band--device .container { position: relative; z-index: 1; }

/* ---------- Action rows & centering utilities ---------- */
/* canonical CTA wrapper inside bands / closing sections */
.band-actions {
  margin-top: 28px;
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}
.section-head.center .band-actions { justify-content: center; }

/* Standardized closing-band credential line: a quiet footnote beneath the
   single CTA button, present on every page's final CTA band. Sits inside
   .section-head.center so it inherits the band's centering. Deliberately
   more muted than .band p's 0.85 alpha — a footnote, not a supporting line. */
.band-credential {
  margin-top: 18px;
  font-size: 12px;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.6);
}

/* CTA microcopy row (spec R3 W7 #2, draft copy pending Mark): one small reassurance
   line under a PRIMARY funnel CTA — cost/time framing for internal tools,
   destination-naming for external handoffs. Lives on light backgrounds
   (Home hero, under .hero-actions) and dark .band closing bands alike;
   the .band override keeps it legible without competing with .band p's
   brighter 0.85 alpha or .band-credential's dimmer 0.6 footnote alpha. */
.cta-microcopy {
  width: 100%;
  margin-top: 10px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}
.band .cta-microcopy { color: rgba(255, 255, 255, 0.75); }

/* Quiet text-link in a closing band (spec R3 W5/W7 #7, sanctioned swap #2 —
   Refinance closing band). Originally the alternative beneath the band's
   single gold button; R8 tranche B removed that button on Mark's change
   request, so on Refinance this link is now the band's action. Still
   explicitly NOT a button: plain underlined text, sized down from
   .btn-link's bold-uppercase CTA treatment. */
.band-quiet-link {
  margin-top: 14px;
  font-size: 13.5px;
  color: rgba(255, 255, 255, 0.75);
}
.band-quiet-link a {
  color: var(--white);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.band-quiet-link a:hover { color: var(--gold-2); }

/* ---------- Recognition proof module (Home + About recognition, R4 W1.2) ----------
   Replaces the old, more fragmented .credential-lockup + sibling
   .google-reviews pairing with ONE contained paper-tinted card so the
   whole credibility story — award seal, aggregate rating, the CCA
   framing line, the outbound Google link, and the 3 review-card seeds —
   reads as a single designed block rather than stacked pieces. Never
   .reveal-gated (proof is never motion-gated). Structure:
     .proof-module[data-reviews] >
       .proof-module-head > (img.award-seal + .proof-module-summary >
         (.g-badge.g-badge--hero + p.proof-module-framing + a.btn-link))
       + .review-grid.card-grid.card-grid--cols-3 (the 3 review-card seeds,
         swapped live/curated by site.js exactly like before — only the
         container around it changed).
   Stacked/centered below 700px (seal above summary); seal-left,
   summary-right from 700px up. */
.proof-module {
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: clamp(28px, 4vw, 48px);
}
.proof-module-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  text-align: center;
  margin-bottom: clamp(28px, 4vw, 40px);
  padding-bottom: clamp(28px, 4vw, 40px);
  border-bottom: 1px solid var(--hairline);
}
.proof-module .award-seal {
  width: clamp(220px, 28vw, 260px);
  height: auto;
  margin: 0;
  flex: none;
}
.proof-module-summary {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  max-width: 46ch;
}
.proof-module-framing { margin: 0; font-size: 16px; color: var(--text); }
.proof-module-summary .btn-link { margin-top: 2px; }
@media (min-width: 700px) {
  .proof-module-head { flex-direction: row; text-align: left; }
  .proof-module-summary { align-items: flex-start; }
  .proof-module-summary .g-badge--hero { align-items: flex-start; text-align: left; }
}
/* ---------- Numbers band (Home + About, directly under the hero;
   spec R4 W1.6 / W5.4) ----------
   A slim, confident 4-stat row: big Fraunces tabular-numeral figure over
   a small muted Inter label, hairline dividers (CSS borders, not
   characters) between stats. NOT reveal-gated — proof is never
   motion-gated (spec W3); this must never be blank on a fast-flick
   scroll. The review stat reuses the existing .g-badge component's exact
   markup/classes (`.g-badge--band` is a CSS-only size modifier) so it's
   picked up for free by the already page-wide
   `document.querySelectorAll('.g-badge')` live-update loop in site.js —
   no JS changes needed. Wraps to 2x2 below ~860px (vertical hairline
   between columns, horizontal between rows), stacks to a single column
   with horizontal dividers below ~480px. */
.numbers-band {
  background: var(--white);
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  padding-block: clamp(24px, 4vw, 36px);
}
.numbers-band-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  list-style: none;
  margin: 0;
  padding: 0;
}
.numbers-band-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  text-align: center;
  padding-inline: 16px;
  border-left: 1px solid var(--hairline);
}
.numbers-band-stat:first-child { border-left: none; }
.numbers-band-figure {
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(21px, 2.4vw, 27px);
  line-height: 1.1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.numbers-band-label {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--muted);
  line-height: 1.3;
}
/* Review stat: the exact .g-badge markup, restyled to the band's own
   figure/label rhythm (rating leads at figure scale, small stars, then
   the muted count reads as the label). */
.numbers-band-stat .g-badge--band {
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.numbers-band-stat .g-badge--band .g-badge-rating {
  order: 1;
  font-size: clamp(21px, 2.4vw, 27px);
  font-weight: 600;
}
.numbers-band-stat .g-badge--band .g-badge-stars { order: 2; }
.numbers-band-stat .g-badge--band .g-badge-stars svg { width: 12px; height: 12px; }
.numbers-band-stat .g-badge--band .g-badge-count { order: 3; font-size: 12.5px; }
/* R4 hotfix 1: stacked layout — the count sits on its own line under the
   stars, so the inline "· " separator would read as an orphaned dot. */
.numbers-band-stat .g-badge--band .g-badge-count::before { content: none; }
@media (max-width: 859.98px) {
  .numbers-band-grid { grid-template-columns: repeat(2, 1fr); row-gap: 22px; }
  .numbers-band-stat { border-left: none; }
  .numbers-band-stat:nth-child(even) { border-left: 1px solid var(--hairline); }
  .numbers-band-stat:nth-child(n+3) { border-top: 1px solid var(--hairline); padding-top: 22px; }
}
@media (max-width: 479.98px) {
  .numbers-band-grid { grid-template-columns: 1fr; row-gap: 18px; }
  .numbers-band-stat { border-left: none; padding-inline: 0; }
  .numbers-band-stat:nth-child(n+2) { border-top: 1px solid var(--hairline); padding-top: 18px; }
}

/* ---------- Money-page award lockup (.award-inline; spec R4 W5.4) ----------
   A quiet seal + one-line credential dropped in/under the hero block near
   the FIRST primary CTA on refinance, home-equity and debt-consolidation
   (inside .hero-copy, directly after .hero-actions) and, on Calculators
   (no .hero-actions in its intro), after the jump-link tag row at the
   foot of the intro/hero-equivalent section — same DOM position
   (end of hero content) for consistency across all 4 pages. Small and
   muted by design so it never competes with the CTA above it; the
   seal's fixed intrinsic width/height means no layout shift. Home/About
   don't get this — they already carry the full .proof-module. NOT
   reveal-gated (proof is never motion-gated, spec W3). */
.award-inline {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 22px;
}
.award-inline--center { justify-content: center; margin-inline: auto; }
.award-inline-seal {
  width: clamp(56px, 6vw, 72px);
  height: auto;
  flex: none;
}
.award-inline-line {
  margin: 0;
  max-width: 26ch;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--muted);
}

.chip-row--center { justify-content: center; }
.tag-row--center { justify-content: center; }
.hero-actions.center { justify-content: center; }
/* compact section (e.g. the Learn filter bar) */
.section--slim { padding-block: clamp(20px, 3vw, 32px); }
/* lead paragraph directly after an icon list gets breathing room */
.icon-list + .lead { margin-top: 24px; }

/* ---------- Icon list rows ---------- */
.icon-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 14px;
}
.icon-list li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-size: 16px;
  color: var(--text);
}
.icon-list li strong { color: var(--ink); }
.icon-list svg {
  width: 22px; height: 22px;
  flex: 0 0 auto;
  margin-top: 2px;
  color: var(--gold);
}
.band .icon-list li { color: rgba(255, 255, 255, 0.88); }
.band .icon-list li strong { color: var(--white); }
/* two-column layout for longer dash lists inside a band (e.g. Refinance's
   "when I would NOT recommend" — 7 short items read better paired up) */
@media (min-width: 860px) {
  .icon-list--cols-2 {
    grid-template-columns: 1fr 1fr;
    column-gap: clamp(28px, 4vw, 48px);
  }
}

/* ---------- Icon chip grid (thin gold line icons + tiny labels) ---------- */
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 22px 30px;
}
.chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  width: 96px;
  text-align: center;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--ink);
}
.chip svg { width: 30px; height: 30px; color: var(--gold); }
.band .chip { color: rgba(255, 255, 255, 0.92); }
@media (max-width: 599.98px) {
  .chip-row { gap: 18px 14px; justify-content: center; }
  .chip { width: 88px; }
}

/* ---------- Typographic ledger (Strategy's "more than the rate" list) ----------
   Replaces an 11-chip icon cloud: a single full-width list of 11 rows, each
   a factor (left) + one-line qualifier (right), hairline below. Below 860px
   there's no room for name + qualifier on one line, so each row is a single
   stacked column (name, then qualifier underneath); at 860px+ the row itself
   becomes two columns spanning the full container width, which is what
   actually gives an 11/16-word qualifier room to sit on one line beside the
   name (a narrower newspaper-style side-by-side split of the *list* would
   starve that line of width). Rows with a matching Learn article get the
   quiet gold-arrow link treatment — same arrow glyph/hover as .btn-link, but
   at the ledger's own 16/600 size rather than .btn-link's bold-uppercase CTA
   weight, so linked and unlinked rows read at the same visual weight. */
.ledger {
  list-style: none;
  margin: 0 0 clamp(24px, 3vw, 32px);
  padding: 0;
}
.ledger-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-block: 14px;
  border-bottom: 1px solid var(--hairline);
}
.ledger-row:last-child { border-bottom: none; }
.ledger-name { font-size: 16px; font-weight: 600; color: var(--navy); }
a.ledger-name {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  text-decoration: none;
}
a.ledger-name::after {
  content: "\2192"; /* → */
  color: var(--gold-deep);
  font-size: 0.85em;
  transition: transform 0.15s ease;
}
a.ledger-name:hover { text-decoration: underline; text-underline-offset: 3px; }
a.ledger-name:hover::after { transform: translateX(3px); }
.ledger-note { font-size: 14.5px; color: var(--muted); }
@media (min-width: 860px) {
  .ledger-row {
    flex-direction: row;
    align-items: baseline;
    justify-content: space-between;
    gap: 24px;
  }
  .ledger-name { flex: 0 1 auto; }
  .ledger-note { flex: 0 1 auto; text-align: right; }
}

/* ---------- Lender wall (full-color chip grid; spec R4 W2) ----------
   Rebuild of the old grayscale/72%-opacity treatment — the client's
   stated complaint was that it "apologizes" for having 12 recognizable
   Canadian lender brands. Every logo now renders full-color, exactly
   as authored (no filter/opacity), inside a uniform white chip card
   (~150x72 content box). Chips use a FIXED-HEIGHT img box + object-fit:
   contain — not per-logo width classes — so mixed native aspect ratios
   (24-40px tall in the old markup) read at the same optical weight.
   Structure: ul.logo-row > li.logo-chip > img. Never .reveal-gated —
   the wall counts as proof (spec W3).
   Mobile (<720px, R4-hotfix-2): a continuous auto-drift marquee — see
   the ".logo-marquee" section below — replaces the old horizontally
   scrollable snap row (12 chips don't crush down to illegible
   thumbnails at phone widths). This base .logo-row rule still carries
   the scroll-snap styling below because it's also the prefers-reduced-
   motion fallback (marquee off -> back to a manually scrollable row).
   >=720px: 4-col grid (3 rows). >=1100px: 6-col grid (2 rows). The
   :not(.logo-row--compact) guard on the grid breakpoints keeps the
   money-page compact variant (below) out of the grid at every width. */
.logo-row {
  display: flex;
  gap: 14px;
  list-style: none;
  margin: 0;
  padding: 14px 8px 22px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
}
.logo-chip {
  flex: 0 0 128px;
  scroll-snap-align: start;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 72px;
  padding: 14px 18px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
}
.logo-chip img {
  display: block;
  width: 100%;
  height: 40px;
  object-fit: contain;
}
@media (min-width: 720px) {
  .logo-row:not(.logo-row--compact) {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    overflow: visible;
    scroll-snap-type: none;
    padding: 0;
  }
}
@media (min-width: 1100px) {
  .logo-row:not(.logo-row--compact) { grid-template-columns: repeat(6, 1fr); }
}
/* Count-stat lead-in above the Home wall grid (spec W2) */
.logo-row-stat {
  margin: 0 0 22px;
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(19px, 2vw, 23px);
  color: var(--ink);
}

/* ---------- Money-page compact lender row (spec R4 W2) ----------
   One trust zone: sits directly above the review strip on refinance/
   home-equity/debt-consolidation (not calculators — its strip zone is
   already dense with the award lockup + jump chips, per spec). 6-8 of
   the most recognizable logos, smaller chips, always a single row
   (see the :not(.logo-row--compact) guard above — it never becomes a
   grid). R4-hotfix-2: this row now marquees continuously at every
   width (.logo-marquee--compact below) instead of being manually
   scrollable — the justify-content rules immediately below go dormant
   while the marquee is active (its ul is a content-sized flex item,
   so there's never leftover space to justify) and only matter again
   under the prefers-reduced-motion fallback, where the row reverts to
   a plain scrollable ul and can regain extra width to center within. */
.logo-row--compact { justify-content: flex-start; }
@media (min-width: 960px) {
  /* guaranteed to fit without scrolling at this container width, so
     centering is safe — flex centering + overflow can strand content
     off-screen when it doesn't fit, so this stays start-aligned below */
  .logo-row--compact { justify-content: center; }
}
.logo-row--compact .logo-chip {
  flex: 0 0 118px;
  height: 58px;
  padding: 12px 15px;
}
.logo-row--compact .logo-chip img { height: 32px; }
.logo-row-lead {
  margin: 0 0 16px;
  text-align: center;
  font-size: 14px;
  font-weight: 600;
  color: var(--muted);
}

/* ---------- Lender logo marquee (continuous drift; R4-hotfix-2) ----------
   Wraps ul.logo-row in .logo-marquee (viewport: clips + edge-fades) >
   .logo-marquee-track (the element that actually animates) > TWO copies
   of the SAME ul.logo-row back to back. Because the two copies are
   pixel-identical, animating the track translateX(0 -> -50%) loops with
   no visible seam — a continuous slow drift, not a slide carousel. The
   2nd ul carries aria-hidden="true" (decoration only; the 1st keeps all
   real list semantics, so nothing is duplicated for assistive tech).
   Scope:
     - Money-page compact rows (.logo-marquee--compact, wrapping
       ul.logo-row.logo-row--compact) marquee at EVERY width — they were
       already a single row everywhere (28s loop, narrower content).
     - Home's plain .logo-row marquees ONLY below 720px (35s loop, wider
       content). Every rule that makes it move lives inside the
       max-width:719.98px query below, so at >=720px .logo-marquee and
       .logo-marquee-track carry no box styling at all — the >=720px
       grid rule above (.logo-row:not(.logo-row--compact)) is completely
       untouched, and the only thing that happens at >=720px is the 2nd
       (duplicate) ul being display:none'd so it can't render a second
       grid underneath the first.
   Pause: :hover/:focus-within cover mouse + keyboard; phones have no
   hover, so site.js toggles .is-touch-paused on pointerdown/up (see
   "Lender logo marquee: pause drift on touch" there).
   prefers-reduced-motion: reduce fully reverts to the pre-marquee static
   scrollable row — both wrapper divs collapse via display:contents,
   handing the visible ul.logo-row back its original overflow-x:auto /
   scroll-snap-type:x behavior (from the base .logo-row rule above); the
   duplicate ul stays hidden either way. */
@keyframes logo-marquee-drift {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
.logo-marquee .logo-row {
  overflow: visible;
  scroll-snap-type: none;
}
.logo-marquee:hover .logo-marquee-track,
.logo-marquee:focus-within .logo-marquee-track,
.logo-marquee.is-touch-paused .logo-marquee-track {
  animation-play-state: paused;
}
.logo-marquee--compact {
  position: relative;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 24px, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(to right, transparent, #000 24px, #000 calc(100% - 24px), transparent);
}
.logo-marquee--compact .logo-marquee-track {
  display: flex;
  width: max-content;
  animation: logo-marquee-drift 28s linear infinite;
}
@media (max-width: 719.98px) {
  .logo-marquee:not(.logo-marquee--compact) {
    position: relative;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to right, transparent, #000 24px, #000 calc(100% - 24px), transparent);
    mask-image: linear-gradient(to right, transparent, #000 24px, #000 calc(100% - 24px), transparent);
  }
  .logo-marquee:not(.logo-marquee--compact) .logo-marquee-track {
    display: flex;
    width: max-content;
    animation: logo-marquee-drift 35s linear infinite;
  }
}
@media (min-width: 720px) {
  .logo-marquee:not(.logo-marquee--compact) .logo-row[aria-hidden="true"] {
    display: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .logo-marquee,
  .logo-marquee-track { display: contents !important; animation: none !important; }
  .logo-marquee .logo-row[aria-hidden="true"] { display: none !important; }
  .logo-marquee .logo-row:not([aria-hidden="true"]) {
    overflow-x: auto !important;
    overflow-y: hidden !important;
    scroll-snap-type: x mandatory !important;
  }
}

/* ---------- Review cards ----------
   Structure: .review-card > (blockquote + cite); gold quote mark is ::before */
.review-card {
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 3vw, 28px);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.review-card::before {
  content: "\201C";
  font-family: Georgia, 'Times New Roman', serif;
  font-size: 44px;
  line-height: 1;
  color: var(--gold);
}
.review-card blockquote {
  margin: 6px 0 16px;
  font-size: 15.5px;
  line-height: 1.65;
  color: var(--text);
}
.review-card cite {
  margin-top: auto;
  font-style: normal;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--muted);
}
/* star row + author/time footer, added by the reviews script (site.js) when
   /api/reviews returns real data — the quote-mark ::before above still leads */
.review-card .stars { color: var(--gold-deep); font-size: 15px; letter-spacing: 2px; margin-bottom: 2px; }
.review-card .review-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-top: auto;
}
.review-card .review-meta cite { margin-top: 0; }
.review-card .review-time { font-size: 12.5px; color: var(--muted); white-space: nowrap; }
/* Manual testimonial override (Task 19) — same .review-card shell,
   .review-location fills the .review-time slot (author left, location
   right); no .stars row is ever added for these cards. */
.review-card--manual .review-location { font-size: 12.5px; color: var(--muted); white-space: nowrap; }

/* ---------- Reviews wrapper (dynamic — Tasks 16, 19; R4 W1) ----------
   Home now server-renders 3 static .review-card seeds (verbatim Google
   reviews) instead of shipping empty/hidden — the block is never [hidden]
   on Home. site.js REPLACES (clears, then re-appends — never duplicates)
   the seeded cards once either the manual testimonials override or a
   successful /api/reviews fetch resolves with usable data, so the static
   seeds are the true no-JS/slow-JS/failed-fetch fallback rather than a
   loading placeholder. .review-grid composes with .card-grid/
   .card-grid--cols-3 (defined above) for the up-to-3-up layout —
   .review-card styles are reused unchanged. */
.google-reviews {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
}
.google-reviews[hidden] { display: none; }
.google-reviews .review-grid { align-self: stretch; }

/* ---------- Money-page review strip (R4 W1.3) ----------
   .review-strip is a compact `.google-reviews` variant for the trust zone
   directly above refinance/home-equity/debt-consolidation/calculators'
   closing CTA band: a plain .g-badge + 2 review cards (this round's seed
   pairing varies per page — see each page's markup comment) + a quiet
   "Read all N reviews" link. [data-reviews]-wired exactly like the
   Home/About proof module, just capped at 2 cards via
   data-reviews-max="2" (site.js reads that attribute; MAX_REVIEWS stays
   the default for pages that don't set it). 2-up from 860px — narrower
   than .card-grid--cols-3's 960px breakpoint, since 2 cards have room to
   pair up sooner than 3 do. */
.review-strip { gap: 22px; }
.review-strip .review-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}
@media (min-width: 860px) {
  .review-strip .review-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- Aggregate review badge (.g-badge) ----------
   Compact inline lockup — decorative 5-gold-star row (SVG, aria-hidden)
   + Fraunces rating numeral + muted Inter review count. Numbers are
   BAKED INTO every instance's HTML as a static fallback (see the
   "REVIEW-COUNT: static fallback" marker comment next to each one) so
   the badge never renders empty, with JS off or before /api/reviews
   resolves. One .visually-hidden span per badge carries the full
   accessible phrase ("Rated 5.0 out of 5 across 59 Google reviews");
   every visual child is aria-hidden so assistive tech gets that single
   clean announcement instead of the stars/numeral/count read out
   separately. site.js's renderBadge() rebuilds this exact structure
   into every `.g-badge` on the page (not just ones inside
   [data-reviews]) when live data resolves — deploy sites beyond the one
   proof-of-life placement (Home hero) land in a later round-4 task.
   Two sizes: default, and `--small` for header-scale use. */
.g-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
}
a.g-badge:hover .g-badge-rating,
a.g-badge:hover .g-badge-count { color: var(--navy-2); }
.g-badge-stars {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  color: var(--gold-deep);
}
.g-badge-stars svg { width: 15px; height: 15px; flex: none; }
/* R4 hotfix 1: the multicolor Google "G" mark sits first inside
   .g-badge-stars (before the 5 gold stars) — the shared `svg` sizing rule
   above already scales it in lockstep with the stars for every variant
   (header --small 12px, default 15px, --hero 20px, --band 12px); this just
   adds breathing room before the star row. */
.g-badge-glogo { margin-right: 3px; }
.g-badge-rating {
  font-family: var(--serif);
  font-weight: 600;
  font-size: 17px;
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
/* R4 hotfix 1: the "· " separator used to be baked into every count
   span's text (broke the stacked --hero/--band variants, where the count
   sits on its own line under the stars and the dot reads as an orphan).
   It's now a CSS-only prefix, suppressed for those two stacked variants
   below. */
.g-badge-count {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
}
.g-badge-count::before { content: "· "; }
.g-badge--small .g-badge-stars svg { width: 12px; height: 12px; }
.g-badge--small .g-badge-rating { font-size: 14px; }
.g-badge--small .g-badge-count { font-size: 12px; }

/* Home hero placement (proof-of-life, spec R4 W1.1): its own row directly
   under the eyebrow, clear of both the eyebrow above and the h1 below. */
.hero-copy .g-badge { margin: 0 0 16px; }

/* --hero: the recognition proof module's rating summary (spec R4 W1.2) —
   same DOM/JS as every other .g-badge (renderBadge() rebuilds this exact
   structure, so live updates land here unchanged), just restyled bigger
   and stacked: the Fraunces numeral leads at display scale, gold stars
   underneath, then the muted review count — reordered via `order` rather
   than reordering the DOM, since the DOM order (stars, rating, count)
   still matches the single clean a11y announcement carried by the
   .visually-hidden label. Centered by default (mobile/Home module);
   .proof-module-head's 700px+ row layout flips it to left-aligned (see
   that component's own media query). */
.g-badge--hero {
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-align: center;
}
.g-badge--hero .g-badge-rating {
  order: 1;
  font-size: clamp(44px, 7vw, 64px);
  font-weight: 600;
}
.g-badge--hero .g-badge-stars { order: 2; }
.g-badge--hero .g-badge-stars svg { width: 20px; height: 20px; }
.g-badge--hero .g-badge-count { order: 3; font-size: 15px; }
/* R4 hotfix 1: same orphan-dot fix as --band above — stacked count line,
   no inline separator needed. */
.g-badge--hero .g-badge-count::before { content: none; }

/* ---------- Article cards (Learn) ----------
   Structure: .article-card > (.article-card-icon + .tag + h3 + p +
   .btn-link) — flat, no wrapper div, mirroring .intent-card's own DOM
   shape. R4 W5.2: grid cards drop their photo entirely (the round-3 build
   round-robinned 4 stock lifestyle photos across 9 cards with zero topical
   correlation) in favour of a topic-icon header: the SAME 48px rounded-
   square gold-tint chip used by the Home intent cards, one stroke icon per
   category (articles sharing a category reuse the same icon — the icon
   marks the topic, not the individual piece; see site/learn/index.html for
   the per-category mapping). The featured article above the grid keeps its
   photo. Padding/hover now mirror .intent-card (gold top-accent bar,
   lift-on-hover, generous padding) so the cards read as a deliberate
   system — substantial on their own — rather than a stripped-down
   downgrade. */
.article-card {
  position: relative; /* anchors the .card-link whole-card overlay */
  overflow: hidden; /* clips the top-accent bar to the card's rounded corners */
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: clamp(20px, 2.6vw, 26px);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.article-card::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--gold);
  opacity: 0;
  transition: opacity 0.15s ease;
}
.article-card:hover {
  border-color: var(--navy-2);
  box-shadow: var(--shadow-sm);
  transform: translateY(-4px);
}
.article-card:hover::before { opacity: 1; }
.article-card-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  margin-bottom: 2px;
  border-radius: 12px;
  background: var(--gold-tint);
}
.article-card-icon svg { width: 22px; height: 22px; color: var(--gold-deep); }
.article-card h3 { font-size: 1.05rem; margin-bottom: 0; }
.article-card p { font-size: 14px; line-height: 1.55; color: var(--muted); }
.article-card .btn-link { margin-top: auto; padding-block: 6px 0; font-size: 12px; }
.article-card .card-link::before { content: ""; position: absolute; inset: 0; }
.article-card[hidden] { display: none; }
[data-empty-msg] { margin-inline: auto; text-align: center; }
[data-empty-msg][hidden] { display: none; }

/* ---------- Featured article (Learn index) ----------
   The newest post by JSON-LD datePublished gets a full-width treatment
   above the grid: bigger image, quiet category label, headline promoted to
   h2, dek, byline/date. The grid cards below moved to the topic-icon system
   (R4 W5.2) — the featured article is now the ONLY place a Learn photo
   still appears, and keeps its own img + body + .card-link whole-body
   overlay structure, 2-col from 760px up. */
.featured-article {
  position: relative;
  display: grid;
  grid-template-columns: 1fr;
  gap: 22px;
  margin-bottom: clamp(32px, 5vw, 56px);
  padding-bottom: clamp(32px, 5vw, 56px);
  border-bottom: 1px solid var(--hairline);
}
.featured-article-media {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: var(--radius);
}
.featured-article-body { display: flex; flex-direction: column; gap: 8px; }
.featured-article-body h2 { font-size: clamp(1.5rem, 1.2rem + 1.6vw, 2.1rem); margin-bottom: 0; }
.featured-article-body p { font-size: 16px; line-height: 1.55; color: var(--muted); }
.featured-article-body .byline {
  margin-top: 2px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.featured-article-body .byline a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.featured-article .btn-link { margin-top: 4px; }
.featured-article .card-link::before { content: ""; position: absolute; inset: 0; }
@media (min-width: 760px) {
  .featured-article { grid-template-columns: 1.15fr 1fr; align-items: center; gap: clamp(28px, 4vw, 48px); }
  .featured-article-media { aspect-ratio: 4 / 3; }
}

/* category chips / filter tags */
.tag-row { display: flex; flex-wrap: wrap; gap: 10px; }
.tag {
  display: inline-flex;
  align-items: center;
  padding: 10px 16px; /* ≥40px hit area for filter chips */
  line-height: 1.6;   /* button.tag would otherwise sit at ~1.2 */
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--white);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease, transform 0.12s ease;
}
.tag:hover { border-color: var(--navy-2); }
.tag:active { transform: scale(0.97); }
/* .tag keeps its own white pill + ink text inside a navy .band (Calculators'
   new intro band, R4 W5.1) — without this, the generic `.band a { color:
   white }` rule (higher specificity than bare `.tag`) would paint the label
   white-on-white and make the jump chips unreadable. */
.band .tag { color: var(--ink); }
/* .tag's own display:inline-flex would otherwise outrank the UA [hidden]
   rule (same specificity, author origin wins) — needed so the Learn
   zero-match chip fix (site/learn/index.html) actually hides the chip. */
.tag[hidden] { display: none; }
.tag.is-active, .tag[aria-pressed="true"] {
  background: var(--navy);
  border-color: var(--navy);
  color: var(--white);
}
/* Learn cards' category label reads as a quiet text eyebrow, not a filter
   chip — the .tag-row buttons above keep the pill treatment; this scopes
   the override to the label sitting inside a card body. */
.article-card .tag,
.featured-article-body .tag {
  display: inline-block;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gold-deep);
  cursor: default;
}

/* ---------- Article body (Learn detail pages) ---------- */
.prose { max-width: 68ch; margin-inline: auto; }
/* long literal URLs (e.g. privacy policy) must wrap, not overflow phones */
.prose a { overflow-wrap: anywhere; }
.prose h3 { margin-top: 1.4em; }
.prose p, .prose li { font-size: 16.5px; line-height: 1.7; }
.prose img { border-radius: var(--radius); margin-block: 1.5em; }
.prose blockquote {
  margin: 1.5em 0;
  padding: 4px 0 4px 20px;
  border-left: 3px solid var(--gold);
  color: var(--ink);
  font-weight: 500;
}
.prose .byline { color: var(--muted); font-size: 14px; font-weight: 600; letter-spacing: 0.02em; }
.prose .byline a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* Article header block — wraps the h1 + byline on article-detail pages;
   hairline rule below separates the masthead from the body copy. */
.article-head {
  padding-bottom: clamp(20px, 3vw, 28px);
  margin-bottom: clamp(28px, 4vw, 44px);
  border-bottom: 1px solid var(--hairline);
}
.article-head h1 { margin-bottom: 0.3em; }
.article-head .byline { margin-top: 0; }

/* Numbered h2s: small gold-deep decimal-leading-zero numeral to the left of
   each body heading (CSS counter). Scoped to `.article-head ~ h2` — general
   siblings of the article masthead within the same .prose parent — rather
   than the old `.prose > h2`, which numbered ANY direct-child h2 of ANY
   .prose block, including non-article prose (e.g. Refinance's renewal-FAQ
   `.prose` div, which has no .article-head and picked up a stray "01").
   Only Learn articles carry .article-head, so only their body h2s number.
   The Related-reading aside's h2 stays excluded too: it's nested inside
   `.related`, so it's a grandchild of the shared parent, not a sibling —
   the `~` combinator only matches direct siblings, same protection the old
   `>` combinator gave against it. */
.prose { counter-reset: articleh2; }
.article-head ~ h2 {
  counter-increment: articleh2;
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-top: 1.6em;
}
.article-head ~ h2::before {
  content: counter(articleh2, decimal-leading-zero);
  flex: none;
  font-size: 0.5em;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: var(--gold-deep);
  font-variant-numeric: tabular-nums;
}

/* ---------- Article closing CTA + Related reading ---------- */
/* Article CTA block (spec R3 W5): now sits ABOVE Related reading — the
   primary conversion moment right after the body copy, with its own
   hairline divider so it reads as a deliberate close, not a trailing
   afterthought. */
.article-cta {
  margin-top: 2.5em;
  padding-top: 1.5em;
  border-top: 1px solid var(--hairline);
}

/* Related reading (spec R3 W5): restyled quieter now that it follows the
   CTA — smaller uppercase eyebrow-style label, muted link color — so it
   reads as a secondary, browse-later list rather than competing with the
   CTA above it. */
.related { margin-top: 2em; padding-top: 1.5em; border-top: 1px solid var(--hairline); }
.related h2 {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.9em;
}
.related ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.related a { color: var(--muted); font-weight: 500; text-decoration: none; }
.related a:hover { color: var(--navy-2); text-decoration: underline; text-underline-offset: 3px; }

/* ---------- Service-page "Learn more" contextual links into /learn articles ---------- */
.learn-more { margin-top: 0.75em; font-size: 14.5px; font-weight: 600; color: var(--muted); }
.learn-more a { color: var(--navy-2); }

/* ---------- Quiet intent-entry pointer under a card-grid (R4 W4.2:
   Home's renewal entry point) — a centered single line, lighter than
   .learn-more's left-aligned service-page treatment, sitting under a
   4-up .card-grid without disturbing its balance. ---------- */
.intent-note { margin-top: clamp(20px, 3vw, 28px); text-align: center; font-size: 14.5px; font-weight: 600; color: var(--muted); }
.intent-note a { color: var(--navy-2); }

/* ---------- Maturity note (R8 tranche B: Mark's repositioned renewal
   message) — the compact centered block under Home's 4-up .card-grid
   that replaced the old two-way renewal/next-home cross-links line
   (which used the single-line .intent-note treatment above; that class
   stays for its other consumers). Small headline + one line + a quiet
   .btn-link — catches maturity-driven scanners without competing with
   the cards above or the statement section below. ---------- */
.maturity-note { margin-top: clamp(24px, 4vw, 36px); text-align: center; }
.maturity-note h3 { margin-bottom: 0.25em; font-size: clamp(1.05rem, 0.95rem + 0.5vw, 1.25rem); }
.maturity-note p { margin-inline: auto; max-width: 52ch; font-size: 14.5px; color: var(--muted); }
.maturity-note .btn-link { margin-top: 10px; }

/* ---------- Calculator cards (/calculators) ----------
   Structure: .calc > (form.calc-grid > .calc-field* ) + button.calc-btn
              + .calc-result[aria-live] + p.calc-disclaimer
   .calc-result holds either a neutral placeholder/guard message
   (.calc-msg) or a computed result (.calc-result-primary +
   .calc-result-label + .calc-result-details? + .calc-cta), all built
   via DOM APIs (textContent) in calculators.js — never innerHTML. */
.calc {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: clamp(22px, 3vw, 32px);
}
.calc-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
}
@media (min-width: 600px) {
  .calc-grid { grid-template-columns: repeat(2, 1fr); gap: 20px 24px; }
}
.calc-field { display: flex; flex-direction: column; gap: 6px; }
.calc-field label { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.calc-field .calc-hint { font-size: 12px; color: var(--muted); }
.calc-field input,
.calc-field select {
  min-height: 44px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--white);
  color: var(--text);
  font-family: var(--sans);
  font-size: 15px;
}
.calc-field input:focus-visible,
.calc-field select:focus-visible { outline: 3px solid var(--navy-2); outline-offset: 1px; }
.calc-btn { margin-top: 22px; width: 100%; }
@media (min-width: 600px) { .calc-btn { width: auto; } }

.calc-result {
  margin-top: 24px;
  padding-top: 22px;
  border-top: 1px solid var(--hairline);
}
/* Result block fade-in (Task 9): calculators.js re-triggers this class on
   every render by removing it, forcing reflow, then re-adding it, so a
   fresh submit always fades the rebuilt block in. Opacity-only keyframe —
   the reduced-motion query below already collapses animation-duration to
   ~0 site-wide, so this is inert (instant) for reduced-motion users. */
@keyframes calc-result-in { from { opacity: 0; } to { opacity: 1; } }
.calc-result.is-revealing { animation: calc-result-in 0.4s ease both; }
.calc-msg { font-size: 14.5px; color: var(--muted); }
.calc-result-primary,
.calc-result-detail-value {
  font-variant-numeric: tabular-nums;
}
.calc-result-primary {
  font-size: clamp(1.6rem, 1.25rem + 1.4vw, 2.1rem);
  font-weight: 800;
  color: var(--navy);
  line-height: 1.15;
}
/* AA-compliant warning red (5.9:1 on white) for guard/ineligible states */
.calc-result-primary--warn { color: #b3261e; }
.calc-result-label { margin-top: 6px; font-size: 14px; line-height: 1.55; color: var(--muted); }
.calc-result-details {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px 20px;
  margin-top: 18px;
}
@media (max-width: 479.98px) { .calc-result-details { grid-template-columns: 1fr; } }
.calc-result-detail { display: flex; flex-direction: column; gap: 2px; }
.calc-result-detail-value { font-size: 1.05rem; font-weight: 700; color: var(--ink); }
.calc-result-detail-label { font-size: 12.5px; color: var(--muted); }
.calc-cta { margin-top: 20px; }
.calc-disclaimer { margin-top: 16px; font-size: 12.5px; line-height: 1.6; color: var(--muted); }

/* Interpretive result line (spec R3 W7 #3, draft copy pending Mark): a short
   plain-language "what this means" sentence rendered by calculators.js
   after the numeric details, before the Book-a-Call CTA — distinct from
   .calc-result-label (which states what the number IS). Non-numeric by
   design; reuses the same hairline-rule convention as .proof-solutions. */
.calc-result-interpretive {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
  font-size: 14px;
  line-height: 1.6;
  color: var(--text);
}

/* ---------- Calendar embed ---------- */
/* Booking reassurance (spec R3 W5 + W7 #4, draft copy pending Mark): small
   avatar-crop of Mark + "what happens on the call" bullets + a credential
   line, above the booking iframe. The 120px circular crop is a distinct
   usage class from the two full-bleed mug-band moments (Home, Strategy) —
   mug exclusivity there is untouched by this smaller, different avatar. */
.calendar-reassurance {
  display: flex;
  gap: 20px;
  align-items: flex-start;
  max-width: 560px;
  margin: 0 auto clamp(28px, 4vw, 40px);
  text-align: left;
}
.calendar-avatar {
  flex: none;
  width: 120px;
  height: 120px;
  border-radius: 50%;
  object-fit: cover;
}
.calendar-reassurance-body h2 { font-size: 1.05rem; margin-bottom: 10px; }
.calendar-reassurance-body ul {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 14.5px;
  color: var(--muted);
}
.calendar-reassurance-body ul li { padding-left: 18px; position: relative; }
.calendar-reassurance-body ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--gold-deep);
}
/* Response-time promise (spec R4 W6, draft copy pending Mark): a quiet
   personal-voice line between the "what happens on the call" bullets and
   the credential line — heavier than the muted credential text (this is a
   commitment, not a badge) but still a single small line, not a banner. */
.calendar-promise { font-size: 13.5px; font-weight: 600; color: var(--ink); margin: 0 0 10px; }
.calendar-credential { font-size: 12px; letter-spacing: 0.02em; color: var(--muted); }
@media (max-width: 599.98px) {
  .calendar-reassurance { flex-direction: column; align-items: center; text-align: center; }
  .calendar-reassurance-body ul { align-items: center; }
  .calendar-reassurance-body ul li { padding-left: 0; }
  .calendar-reassurance-body ul li::before { display: none; }
}
.calendar-embed { width: 100%; }
.calendar-embed iframe {
  width: 100%;
  min-height: 680px;
  border: none;
  display: block;
}

/* ---------- Footer (ink) ---------- */
.site-footer {
  background: var(--ink);
  color: rgba(255, 255, 255, 0.78);
  padding-block: clamp(44px, 6vw, 64px) 28px;
  font-size: 14.5px;
  line-height: 1.7;
}
.footer-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 36px;
}
/* R4 audit #5: added a 4th (Explore) column for site wayfinding/SEO — the
   3-column desktop layout got a 2-up midpoint so Contact/Explore and
   Links/Brand pair off before there's room for all four side by side. */
@media (min-width: 800px) {
  .footer-grid { grid-template-columns: 1.2fr 1fr; gap: 40px 48px; }
}
@media (min-width: 1100px) {
  .footer-grid { grid-template-columns: 1.2fr 1fr 1fr 0.9fr; gap: 48px; }
}
.footer-heading {
  margin-bottom: 14px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-2);
}
.site-footer p { margin-bottom: 6px; }
.site-footer a { color: var(--white); text-decoration: none; }
.site-footer a:hover { text-decoration: underline; text-underline-offset: 3px; }
.footer-links { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
/* R4 audit #5: the Premiere Mortgage Centre badge used to sit raw on the
   ink footer — a contained chip frames it. R4 hotfix 1: the wordmark itself
   is white (designed for dark backgrounds), so a white chip washed it out —
   swapped to a subtle translucent frame that stays dark so the white
   lettering reads as designed. */
.footer-brand-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 14px 18px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--radius);
}
.footer-brand .premiere-logo { width: 150px; display: block; }
.footer-bottom {
  margin-top: clamp(32px, 5vw, 48px);
  padding-top: 22px;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.footer-bottom .small-print {
  font-size: 12.5px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.62);
  max-width: 72ch;
}

/* Social icon row: 44px square tap targets (WCAG-sized, unchanged on mobile), muted on ink until hover/focus turns them white with a soft tint */
.social-row { display: flex; gap: 10px; margin-top: 14px; }
.social-link {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-sm);
  color: rgba(255, 255, 255, 0.78);
  transition: background-color 0.15s ease, color 0.15s ease;
}
.social-link svg { width: 20px; height: 20px; }
.social-link:hover,
.social-link:focus-visible {
  color: var(--white);
  background: rgba(255, 255, 255, 0.08);
}

/* ---------- Mobile bottom CTA bar (<720px only) ---------- */
.bottom-cta { display: none; }
@media (max-width: 719.98px) {
  .bottom-cta {
    display: block;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 40;
    padding: 10px var(--gutter);
    padding-bottom: max(10px, env(safe-area-inset-bottom));
    background: rgba(255, 255, 255, 0.96);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    border-top: 1px solid var(--hairline);
    /* slides away whenever a primary CTA / footer is on screen or any tap
       target enters its band (site.js); the reduced-motion block below
       already disables every transition site-wide. R4 hotfix 1: opacity
       added alongside the slide so hide/show reads as a soft fade+slide
       rather than a hard snap — pairs with site.js's ~400ms re-show
       hysteresis to keep the bar from blinking during continuous scroll. */
    transition: transform 0.2s ease, opacity 0.2s ease;
  }
  .bottom-cta--hidden { transform: translateY(110%); opacity: 0; pointer-events: none; }
  .bottom-cta .btn-gold { display: flex; width: 100%; }
  /* keep page content and footer clear of the fixed bar */
  body { padding-bottom: calc(74px + env(safe-area-inset-bottom)); }
  html { scroll-padding-bottom: 90px; }
  /* R4 W4.3 refocus re-tune (replaces the old .safety-net 320px hack,
     removed with that row): the closing band's "Book a Call" button sits
     ~131px of scroll-fraction away from tripping the bar's near-duplicate
     guard (site.js, NEAR_MARGIN) at the page's 50% scroll checkpoint once
     the direct-paths list is between it and the primary tier — below that
     threshold the button lands inside the guard's zone every time and the
     bar stays hidden. 240px clears it with a real margin (~109px of
     slack), not a hairline value one geometry tweak from flipping back —
     verified against both check-mobile.mjs viewports (390x844, 360x800):
     GS shows the bar at 3/5 scroll checkpoints (50/75/100%) on both,
     clearing the >=3 floor identically rather than by coincidence at one
     width. 0% and 25% stay hidden by construction — 0% because the tall
     "I'm not sure yet" primary card overlaps the bar's own strip on
     initial load, 25% because the dense direct-paths list is still
     scrolling through that same strip — both are real tap-target
     overlaps the bar is correctly protecting. */
  .path-group { margin-bottom: 240px; }
}

/* ---------- Reveal-on-scroll (Task 9 motion layer) ----------
   Safety architecture: .reveal is ONLY pre-hidden inside .js-motion, and
   site.js adds .js-motion to <html> ONLY when
   matchMedia('(prefers-reduced-motion: reduce)') does NOT match — as early
   in its execution as possible. So:
     - no-JS users: .js-motion is never added -> .reveal never hides
       anything -> the static page is complete on arrival.
     - reduced-motion users: same -> .reveal never hides anything.
     - everyone else: elements start faded/offset, then site.js's
       IntersectionObserver adds .is-visible (once, then unobserves) to
       fade/slide them in — including anything already in the initial
       viewport, whose first observer callback fires before paint. */
.js-motion .reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.55s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.55s cubic-bezier(0.16, 1, 0.3, 1);
}
.js-motion .reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    transition: none !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}
