/* =========================================================================
   Portal — Veteran Strong member system foundation

   Loaded after styles.css on every portal/member page (dashboard, credit,
   report, debts, protection, account, my-assets, benefits, vs-plus, etc.).

   Contents:
     1. Tokens   — `--portal-*` namespace, separate from `--vs-*` marketing.
     2. Reset    — minimal portal-specific reset (font, tabular nums).
     3. Components — the 8 canonical components from portal-components.md.
     4. Motion respect — prefers-reduced-motion override at the bottom.

   Naming convention: BEM, prefixed `.portal-*`. Marketing uses `.mkt-*`.
   Source of truth for changes: portal-tokens.md + portal-components.md.
   ========================================================================= */


/* ============================================================
   1. TOKENS
   ============================================================ */

:root {
  /* Surfaces — light warm neutral, easy on eyes for long sessions */
  --portal-bg: #f5f3ee;
  --portal-surface: #ffffff;
  --portal-surface-warm: #faf8f3;
  --portal-surface-soft: #f0ede5;

  /* Ink — softer hierarchy, less aggressive contrast */
  --portal-ink: #15171a;
  --portal-ink-secondary: #4a4d47;
  --portal-ink-muted: #7a7d75;
  --portal-ink-disabled: #b8b8ae;

  /* Lines — barely-there hairlines */
  --portal-hairline: #ebe6dd;
  --portal-hairline-strong: #d8d3c5;

  /* Brand */
  --portal-olive: #4a5b2d;
  --portal-olive-hover: #3e4d26;   /* primary-button hover — calibrated darken (Principle 7) */
  --portal-olive-deep: #324019;    /* pressed / deep accents */
  --portal-olive-tint: rgba(74, 91, 45, 0.06);
  --portal-olive-shadow: rgba(74, 91, 45, 0.4);  /* button-lift hover shadow */

  /* Dark hero — olive-tinted, NOT neutral grey. Brand carries into every layer. */
  --portal-slate: #1d2316;
  --portal-slate-deep: #161a10;
  --portal-slate-line: rgba(255, 255, 255, 0.08);
  --portal-slate-text: rgba(255, 255, 255, 0.6);
  --portal-slate-bright: rgba(255, 255, 255, 0.92);

  /* Gold — communicative accent ONLY (score band marker, premium state) */
  --portal-gold: #c9a24a;
  --portal-gold-soft: rgba(201, 162, 74, 0.16);

  /* Status semantic */
  --portal-success: #2f4313;
  --portal-success-bg: #dfe8ce;
  --portal-warn: #6b3f0a;
  --portal-warn-bg: #fbe6c4;
  --portal-danger: #6e2c10;
  --portal-danger-bg: #f4cdba;

  /* Type families (mirrors styles.css for portability).
     Pastel #43 (2026-07-09): every number in the portal now renders in
     Graphik — --portal-font-display repoints so all numeric consumers flip
     in one move. Stratum survives ONLY as --portal-font-brand for wordmark /
     badge lockups (benefit-card tier badge, --display button variant). */
  --portal-font-display: "Graphik", system-ui, sans-serif;
  --portal-font-brand: "Stratum No2", "Graphik", system-ui, sans-serif;
  --portal-font-body: "Graphik", system-ui, sans-serif;

  /* Type scale tokens — Sprint 8.1 (full token coverage).
     Floor 14px for Veterans 65+ readability. Multiples of 2.
     Closed vocabulary: 7 text + 6 numeric = 13 tokens, 10 unique values.
     See portal-tokens.md for role mapping + use cases.
     Rule: every font-size in CSS must be a var(--portal-text-* | --portal-num-*). */

  /* Text scale (7 tokens) — for prose, titles, labels */
  --portal-text-display: 48px;     /* billboard hero titles (login, auth, hero pages) */
  --portal-text-hero: 32px;        /* page h1 */
  --portal-text-section: 24px;     /* section h2 */
  --portal-text-card-title: 20px;  /* card h3 */
  --portal-text-body: 18px;        /* body & card body — 65+ floor for prose */
  --portal-text-label: 16px;       /* labels, links, form values, button text */
  --portal-text-caption: 14px;     /* eyebrows (uppercase tracked), meta, hints — FLOOR */

  /* Numeric display (6 tokens) — large display numbers, tabular-nums */
  --portal-num-mega: 144px;        /* the single big page-defining number (credit score) */
  --portal-num-feature: 96px;      /* feature stat numbers (activated, pact summary) */
  --portal-num-headline: 64px;     /* prominent headline numbers (net worth, flow steps) */
  --portal-num-display: 48px;      /* standard display number */
  --portal-num-stat: 32px;         /* tile values in grids */
  --portal-num-data: 24px;         /* compact stats + OTP cells */

  /* Aliases — old token names map to new values for backward compatibility. */
  --portal-text-hero-title: var(--portal-text-hero);
  --portal-text-section-title: var(--portal-text-section);
  --portal-text-card-body: var(--portal-text-body);
  --portal-text-link: var(--portal-text-label);
  /* Pastel #264-311 (2026-08-02) — Li marked eyebrows across the portal
     "still too small". Eyebrows split off the caption tier and step to the
     next token in the closed scale (16). Captions/fine print stay at 14. */
  --portal-text-eyebrow: var(--portal-text-label);
  --portal-text-meta: var(--portal-text-caption);
  --portal-num-hero: var(--portal-num-display);
  --portal-num-tile: var(--portal-num-stat);

  /* Control sizes — affordance > minimalism (Principle 11) */
  --portal-control-h: 56px;
  --portal-filter-h: 40px;

  /* Spacing — 4-px grid */
  --portal-space-1: 4px;
  --portal-space-2: 8px;
  --portal-space-3: 10px;
  --portal-space-4: 12px;
  --portal-space-5: 14px;
  --portal-space-6: 16px;
  --portal-space-7: 20px;
  --portal-space-8: 24px;
  --portal-space-10: 28px;
  --portal-space-12: 32px;
  --portal-space-14: 36px;
  --portal-space-16: 40px;

  /* Radii */
  --portal-radius-chip: 4px;
  --portal-radius-button: 8px;
  --portal-radius-card: 12px;
  --portal-radius-card-lg: 16px;
  --portal-radius-panel: 18px;
  --portal-radius-pill: 999px;

  /* Shadows — restrained. Default is no shadow (use border for definition). */
  --portal-shadow-card: 0 1px 0 rgba(14, 15, 12, 0.04), 0 1px 2px rgba(14, 15, 12, 0.06);
  --portal-shadow-card-hover: 0 2px 0 rgba(14, 15, 12, 0.04), 0 8px 24px -8px rgba(14, 15, 12, 0.16);

  /* Motion — short, calm, ease-out only */
  --portal-ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --portal-dur-fast: 160ms;
  --portal-dur-base: 220ms;
}


/* ============================================================
   2. RESET — minimal, portal-specific
   ============================================================ */

.portal-num {
  font-variant-numeric: tabular-nums;
}

/* ============================================================
   2.1 CREDIT-CLUSTER SHARED REFRESH (Phase 5, Sprint 5)
   Applies to all pages with body class .portal-body--credit
   (credit, debts, report, protection, optimal-path, alerts).
   Pages must load portal.css to opt in. Breadcrumb token alignment +
   page bg only — page-specific content stays legacy.
   ============================================================ */
.portal-body--credit {
  background: var(--portal-bg) !important;
}
/* Breadcrumb styling moved to the canonical .portal-crumbs component
   (pages.css §14, Sprint 9f) — no per-body dialects. */

/* The mobile section picker. Desktop shows all six tabs on one row and has
   no need of it; the element never paints above the breakpoint, so desktop
   output is unchanged. Shown and styled in portal-mobile.css. */
.portal-subnav__picker { display: none; }

/* ----- Credit-cluster sub-nav refresh (pill tabs centered) ----- */
.portal-body--credit .portal-subnav {
  background: transparent;
  margin-top: var(--portal-space-6);
  border-bottom: 1px solid var(--portal-hairline);
}
.portal-body--credit .portal-subnav__inner {
  max-width: 1280px;
  padding: 0 var(--portal-space-12);
  gap: var(--portal-space-1);
}
.portal-body--credit .portal-subnav__link {
  display: inline-flex;
  align-items: center;
  height: 48px;
  padding: 0 var(--portal-space-6);
  border-radius: 0;
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-caption);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--portal-ink-muted);
  text-decoration: none;
  opacity: 1;
  border-bottom: 3px solid transparent;
  transition: color var(--portal-dur-base) var(--portal-ease-out), border-color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-body--credit .portal-subnav__link:hover { color: var(--portal-ink); opacity: 1; }
.portal-body--credit .portal-subnav__link--active,
.portal-body--credit .portal-subnav__link--active:hover {
  background: transparent;
  color: var(--portal-ink);
  font-weight: 600;
  opacity: 1;
  border-bottom-color: var(--portal-olive);
}

/* ----- Credit-cluster shared content tokens (.vs-* mapped to portal) ----- */
.portal-body--credit .vs-container,
.portal-body--credit .vs-main {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 var(--portal-space-12);
}
.portal-body--credit .vs-section {
  padding: var(--portal-space-12) 0;
}
.portal-body--credit .vs-eyebrow {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-olive);
}
.portal-body--credit .vs-eyebrow--on-dark {
  color: var(--portal-gold);
}
.portal-body--credit .vs-h1 {
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-hero-title);
  line-height: 1.1;
  letter-spacing: -0.5px;
  color: var(--portal-ink);
  margin: 0;
}
.portal-body--credit .vs-h2 {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-section-title);
  letter-spacing: -0.3px;
  color: var(--portal-ink);
  margin: 0;
}
.portal-body--credit .vs-h3 {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-card-title);
  letter-spacing: -0.2px;
  color: var(--portal-ink);
  margin: 0;
  line-height: 1.3;
}
.portal-body--credit .vs-lead {
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-secondary);
}
.portal-body--credit .vs-measure-narrow { max-width: 64ch; }
.portal-body--credit .vs-mx-auto { margin-left: auto; margin-right: auto; }
.portal-body--credit .vs-text-center { text-align: center; }
.portal-body--credit .vs-mt-3 { margin-top: var(--portal-space-3); }
.portal-body--credit .vs-mt-6 { margin-top: var(--portal-space-6); }
.portal-body--credit .vs-caption {
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
}
.portal-body--credit .vs-card {
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card-lg);
  padding: var(--portal-space-10) var(--portal-space-12);
  box-shadow: 0 1px 0 rgba(14, 15, 12, 0.02);
}
.portal-body--credit .vs-card--lg { padding: var(--portal-space-12); }
.portal-body--credit .vs-card__sub {
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-secondary);
  margin: 0;
  max-width: 64ch;
}
.portal-body--credit .vs-grid {
  display: grid;
  gap: var(--portal-space-6);
}
.portal-body--credit .vs-grid--3 { grid-template-columns: repeat(3, 1fr); }
.portal-body--credit .vs-grid--4 { grid-template-columns: repeat(4, 1fr); }

/* Stat tiles in credit cluster */
.portal-body--credit .dash-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--portal-space-5);
}
.portal-body--credit .vs-stat {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-2);
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card);
  padding: var(--portal-space-6) var(--portal-space-7);
  text-decoration: none;
  color: inherit;
  box-shadow: 0 1px 0 rgba(14, 15, 12, 0.02);
  transition: border-color var(--portal-dur-base) var(--portal-ease-out), transform var(--portal-dur-base) var(--portal-ease-out);
}
.portal-body--credit .vs-stat--link:hover {
  border-color: var(--portal-hairline-strong);
  transform: translateY(-1px);
}
.portal-body--credit .vs-stat__label {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-eyebrow);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--portal-ink-muted);
  margin: 0;
}
.portal-body--credit .vs-stat__value {
  font-family: var(--portal-font-display);
  font-weight: 500;
  font-size: var(--portal-text-hero);
  letter-spacing: -0.6px;
  color: var(--portal-ink);
  margin: 0;
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}
.portal-body--credit .vs-stat__sub {
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
  margin: 0;
}
/* .vs-stat__glyph removed 2026-07-09 (Pastel #45/#46) — the lettered
   circles on stat link rows read as clutter; the hint row + hover carry
   the affordance. */
.portal-body--credit .vs-stat__hint {
  font-size: var(--portal-text-caption);
  font-weight: 500;
  color: var(--portal-ink-secondary);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: var(--portal-space-3);
  padding-top: var(--portal-space-3);
  border-top: 1px solid var(--portal-hairline);
  transition: color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-body--credit .vs-stat__arrow {
  transition: transform var(--portal-dur-base) var(--portal-ease-out);
}
.portal-body--credit .vs-stat:hover .vs-stat__hint { color: var(--portal-ink); }
.portal-body--credit .vs-stat:hover .vs-stat__arrow { transform: translateX(3px); }

/* Primary button */
.portal-body--credit .vs-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  height: var(--portal-control-h);
  padding: 0 var(--portal-space-7);
  background: var(--portal-olive);
  color: #fff !important;
  border: 0;
  border-radius: var(--portal-radius-button);
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-label);
  letter-spacing: 0;
  text-transform: none;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--portal-dur-base) var(--portal-ease-out), transform var(--portal-dur-base) var(--portal-ease-out), box-shadow var(--portal-dur-base) var(--portal-ease-out);
}
.portal-body--credit .vs-btn:hover {
  background: var(--portal-olive-hover);
  color: #fff !important;
  transform: translateY(-1px);
  box-shadow: 0 10px 24px -10px var(--portal-olive-shadow);
}
.portal-body--credit .vs-btn--ghost {
  background: transparent;
  color: var(--portal-olive-deep) !important;
  border: 1px solid var(--portal-olive);
}
.portal-body--credit .vs-btn--ghost:hover {
  background: var(--portal-olive);
  color: #fff !important;
}
.portal-body--credit .vs-btn--sm {
  height: 40px;
  padding: 0 var(--portal-space-5);
  font-size: var(--portal-text-caption);
}

/* ============================================================
   2.2 BENEFIT-PAGE SHARED REFRESH (Sprint 6)
   Applies to body classes .bp-agero / .bp-afba / .bp-fox / .bp-adt
   for cross-page consistency: page bg, button text-transform fixes,
   FAQ accordion refresh, partner card refinements.
   ============================================================ */
.bp-agero,
.bp-afba,
.bp-fox,
.bp-adt {
  background: var(--portal-bg) !important;
}

/* Remove uppercase from all bespoke benefit-page CTAs */
.bp-agero .agero-btn,
.bp-agero .agero-driver-form__save,
.bp-agero .agero-driver-form__cancel,
.bp-afba .afba-btn,
.bp-fox .fox-btn,
.bp-adt .adt-btn {
  text-transform: none !important;
  letter-spacing: 0 !important;
  font-family: var(--portal-font-body) !important;
  font-weight: 600 !important;
}

/* Primary CTAs (--primary variants) — olive bg + cream text */
.bp-agero .agero-btn--primary,
.bp-afba .afba-btn--primary,
.bp-fox .fox-btn--primary,
.bp-adt .adt-btn--primary {
  background: var(--portal-olive) !important;
  color: #fff !important;
  border: 0 !important;
  border-radius: var(--portal-radius-button) !important;
  height: var(--portal-control-h) !important;
  padding: 0 var(--portal-space-7) !important;
  font-size: var(--portal-text-label) !important;
  display: inline-flex !important;
  align-items: center;
  gap: var(--portal-space-2);
  transition: background var(--portal-dur-base) var(--portal-ease-out), transform var(--portal-dur-base) var(--portal-ease-out), box-shadow var(--portal-dur-base) var(--portal-ease-out) !important;
}
.bp-agero .agero-btn--primary:hover,
.bp-afba .afba-btn--primary:hover,
.bp-fox .fox-btn--primary:hover,
.bp-adt .adt-btn--primary:hover {
  background: var(--portal-olive-hover) !important;
  color: #fff !important;
  transform: translateY(-1px);
  box-shadow: 0 10px 24px -10px var(--portal-olive-shadow);
}

/* Continue button on agero wizard */
.bp-agero #wizard-continue,
.bp-agero .agero-wizard__continue {
  text-transform: none !important;
  letter-spacing: 0 !important;
  font-weight: 600 !important;
  background: var(--portal-olive) !important;
  color: #fff !important;
  border-radius: var(--portal-radius-button) !important;
}

/* Form inputs across benefit pages — token alignment */
.bp-agero select,
.bp-agero input[type="text"],
.bp-agero input[type="number"],
.bp-agero input[type="email"],
.bp-afba select,
.bp-afba input[type="text"],
.bp-fox select,
.bp-fox input[type="text"],
.bp-adt select,
.bp-adt input[type="text"] {
  font-family: var(--portal-font-body) !important;
  font-size: var(--portal-text-label) !important;
  border-radius: var(--portal-radius-button) !important;
}

/* Section heads across benefit pages — eyebrow + title pattern */
.bp-agero .agero-section__head,
.bp-afba .afba-section__head,
.bp-fox .fox-section__head,
.bp-adt .adt-section__head {
  font-family: var(--portal-font-body) !important;
}

/* FAQ accordion refresh — common pattern across benefit pages */
.bp-agero details summary,
.bp-afba details summary,
.bp-fox details summary,
.bp-adt details summary {
  cursor: pointer;
  list-style: none;
  font-family: var(--portal-font-body) !important;
}
.bp-agero details summary::-webkit-details-marker,
.bp-afba details summary::-webkit-details-marker,
.bp-fox details summary::-webkit-details-marker,
.bp-adt details summary::-webkit-details-marker { display: none; }


/* ============================================================
   3.1 PORTAL NAV — canonical rules live in pages.css (~line 3659+)

   The nav is owned by `portal-chrome.js`, which injects the markup
   into `<header data-portal-nav>` on every portal page. Because
   portal.css is NOT loaded on every portal page, the canonical nav
   rules must live in pages.css (loaded globally) using --vs-* tokens.

   Do NOT redefine `.portal-nav*` here — edits go in pages.css.
   ============================================================ */


/* ============================================================
   3.2 PORTAL HERO — dark olive-tinted panel, score-anchored

   Grid: 1fr | 1px divider | 1fr.
   Left: greeting block. Right: score number + band rail (300-850) +
   state. Gold marker on the rail is the ONLY decorative gold and it
   communicates exact position.
   ============================================================ */

.portal-hero {
  background: var(--portal-slate);
  background-image:
    radial-gradient(120% 160% at 100% 0%, rgba(201, 162, 74, 0.07) 0%, transparent 55%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.02) 0%, rgba(0, 0, 0, 0.05) 100%);
  color: var(--portal-slate-bright);
  border-radius: var(--portal-radius-panel);
  padding: var(--portal-space-10) var(--portal-space-14);
  display: grid;
  grid-template-columns: 1fr 1px 1fr;
  gap: var(--portal-space-16);
  align-items: center;
}

.portal-hero__divider {
  height: 72%;
  background: var(--portal-slate-line);
  align-self: center;
}

.portal-hero__greet {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-2);
}
.portal-hero__eyebrow {
  font-size: var(--portal-text-eyebrow);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--portal-gold);
  margin: 0;
}
.portal-hero__title {
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-hero-title);
  line-height: 1.1;
  letter-spacing: -0.5px;
  color: var(--portal-slate-bright);
  margin: 0;
}
.portal-hero__meta {
  font-size: var(--portal-text-label);
  color: var(--portal-slate-text);
  margin: 0;
}

.portal-hero__score {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-3);
}
.portal-hero__score-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--portal-space-8);
}
.portal-hero__score-label {
  font-size: var(--portal-text-eyebrow);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--portal-slate-text);
}

/* Score-version info toggle (Pastel #219/#223) — the label says
   CREDIT SCORE in plain language; clicking the i reveals the scoring
   model. Click-to-open (not hover-only) for the 65+ audience. */
.portal-hero__score-label-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
}
.portal-hero__score-info {
  appearance: none;
  border: 0;
  padding: var(--portal-space-2);
  margin: -8px 0;
  background: transparent;
  border-radius: var(--portal-radius-pill);
  display: grid;
  place-items: center;
  color: var(--portal-slate-text);
  cursor: pointer;
  transition: color var(--portal-dur-fast) var(--portal-ease-out);
}
.portal-hero__score-info:hover {
  color: var(--portal-slate-bright);
}
.portal-hero__score-info[aria-expanded="true"] {
  color: var(--portal-gold);
}
.portal-hero__score-info-pop {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 6;
  padding: var(--portal-space-2) var(--portal-space-4);
  background: var(--portal-surface);
  color: var(--portal-ink);
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  border-radius: var(--portal-radius-button);
  box-shadow: var(--portal-shadow-card-hover);
  white-space: nowrap;
}

/* Equifax attribution on the score panel (Pastel #218). */
.portal-hero__score-brand {
  display: flex;
  align-items: center;
  gap: var(--portal-space-2);
  margin-top: var(--portal-space-6);
  font-size: var(--portal-text-caption);
  color: var(--portal-slate-text);
}
.portal-hero__score-brand img {
  display: block;
  height: 18px;
  width: auto;
  opacity: 0.9;
}
.portal-hero__score-delta {
  font-size: var(--portal-text-link);
  font-weight: 500;
  color: var(--portal-gold);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.portal-hero__score-number {
  font-family: var(--portal-font-display);
  font-weight: 500;
  font-size: var(--portal-num-hero);
  line-height: 0.92;
  letter-spacing: -2.4px;
  color: var(--portal-slate-bright);
  font-variant-numeric: tabular-nums;
  margin: 0;
}
.portal-hero__score-band {
  position: relative;
  height: 6px;
  background: var(--portal-slate-line);
  border-radius: 3px;
  margin-top: 6px;
}
.portal-hero__score-band-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0.5) 0%, var(--portal-gold) 100%);
  border-radius: 3px;
}
.portal-hero__score-band-marker {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 12px;
  height: 12px;
  background: var(--portal-gold);
  border-radius: 50%;
  box-shadow: 0 0 0 3px var(--portal-slate);
}
.portal-hero__score-band-scale {
  display: flex;
  justify-content: space-between;
  margin-top: 6px;
  font-size: var(--portal-text-eyebrow);
  color: var(--portal-slate-text);
  font-variant-numeric: tabular-nums;
}
.portal-hero__score-state {
  display: flex;
  gap: var(--portal-space-2);
  align-items: center;
  font-size: var(--portal-text-card-body);
  color: var(--portal-slate-bright);
  margin-top: var(--portal-space-1);
}
.portal-hero__score-state-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--portal-gold);
}


/* ============================================================
   3.3 PORTAL STAT — compact tile with mini-visualisation

   Every tile MUST justify its footprint with a viz. No big empty cards.
   Variants for mini-viz: segmented bar / check row / timeline dots.
   ============================================================ */

.portal-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--portal-space-4);
}

.portal-stat {
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card);
  padding: var(--portal-space-6) var(--portal-space-7);
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-3);
  text-decoration: none;
  color: inherit;
  /* Principle 5 — equal heights AND widths in a row */
  width: 100%;
  height: 100%;
  transition: border-color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-stat:hover {
  border-color: var(--portal-hairline-strong);
}

.portal-stat__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/* Optional leading icon (tinted disc). Used by tiles that carry no
   mini-viz — the icon is what justifies the footprint instead. */
.portal-stat__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--portal-radius-pill);
  background: var(--portal-olive-tint);
  color: var(--portal-olive-deep);
  flex-shrink: 0;
}
.portal-stat__icon svg { width: 24px; height: 24px; }
.portal-stat__label {
  font-size: var(--portal-text-label);
  font-weight: 500;
  color: var(--portal-ink-muted);
  margin: 0;
}
.portal-stat__meta {
  font-size: var(--portal-text-meta);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--portal-ink-secondary);
}
.portal-stat__num {
  font-family: var(--portal-font-display);
  font-weight: 500;
  font-size: var(--portal-num-tile);
  line-height: 1;
  letter-spacing: -0.7px;
  color: var(--portal-ink);
  font-variant-numeric: tabular-nums;
  margin: var(--portal-space-1) 0;
}

/* Sub-line (under the number) — used by --compact variant on portal-activated
   to label what the number represents in plain English. */
.portal-stat__sub {
  font-size: var(--portal-text-caption);
  font-weight: 500;
  color: var(--portal-ink-muted);
  margin: 0;
  line-height: 1.35;
}

/* Variant: --compact — no head, no viz, smaller number. Used for net-worth
   tiles on portal-activated where 4 tiles share a row and we need density. */
.portal-stat--compact {
  padding: var(--portal-space-5) var(--portal-space-6);
  gap: var(--portal-space-2);
}
/* Round-3 readability bump (2026-07-09): the compact tiles were the one
   place stat type dipped below the senior floor — label 14→16, number
   24→32, sub 14→16. */
.portal-stat--compact .portal-stat__label {
  font-size: var(--portal-text-label);
  line-height: 1.3;
}
.portal-stat--compact .portal-stat__num {
  font-size: var(--portal-num-stat);
  letter-spacing: -0.6px;
}
.portal-stat--compact .portal-stat__sub {
  font-size: var(--portal-text-label);
}

/* Variant: --summary — one card, 2–3 related facts as full-width rows
   (Pastel #178/#181/#183, redesigned). Columns were tried first and failed:
   in a ~400px card, three columns force labels like "Derogatory marks" to
   wrap, so nothing aligns. Rows give every label the full width, and the
   values right-align into a single scan column.
   Anatomy: head = identity disc + title (+ optional info), then rows of
   label ... value, plus an optional footer link.                          */
.portal-stat--summary {
  padding: var(--portal-space-8);
  gap: var(--portal-space-6);
}
.portal-stat--summary .portal-stat__head {
  display: flex;
  align-items: center;
  gap: var(--portal-space-4);
}
.portal-stat--summary .portal-stat__label {
  font-size: var(--portal-text-card-title);
  font-weight: 600;
  color: var(--portal-ink);
  /* take the free space so the info glyph is pushed to the far edge */
  margin-right: auto;
}
.portal-stat__info {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--portal-ink-muted);
}
.portal-stat__info svg { width: 100%; height: 100%; }

/* Rows — a <dl>; each row is label + right-aligned value, optionally with
   a full-width meter beneath. */
.portal-stat__rows {
  margin: 0;
  display: flex;
  flex-direction: column;
}
.portal-stat__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: var(--portal-space-2) var(--portal-space-6);
  /* Pastel #230 (Jaehan/Li, 30 Jul) — lower information density on all
     credit screens: rows breathe more (14 → 20) and both columns step
     up a token (label 16 → 18 body, value 24 → 32 num-stat). */
  padding: var(--portal-space-7) 0;
  border-top: 1px solid var(--portal-hairline);
}
.portal-stat__row:first-child { border-top: 0; padding-top: 0; }
.portal-stat__row:last-child { padding-bottom: 0; }

.portal-stat__row-label {
  margin: 0;
  font-size: var(--portal-text-body);
  font-weight: 500;
  color: var(--portal-ink-muted);
}
.portal-stat__row-value {
  margin: 0;
  font-family: var(--portal-font-display);
  font-weight: 500;
  font-size: var(--portal-num-stat);
  line-height: 1;
  letter-spacing: -0.6px;
  color: var(--portal-ink);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
/* State words ("On", "Active") are values, not numerals — same size so the
   value column stays even, olive so the positive state reads at a glance. */
.portal-stat__row-value--state { color: var(--portal-olive-deep); }
.portal-stat__row-value--muted { color: var(--portal-ink-muted); }

/* Optional meter under a ratio row (the one metric that is a proportion). */
.portal-stat__meter {
  grid-column: 1 / -1;
  height: 6px;
  margin-top: var(--portal-space-1);
  background: var(--portal-hairline);
  border-radius: var(--portal-radius-pill);
  overflow: hidden;
}
.portal-stat__meter span {
  display: block;
  height: 100%;
  background: var(--portal-olive);
  border-radius: inherit;
  transition: width var(--portal-dur-slow, 600ms) var(--portal-ease-out);
}

/* Footer link affordance. The whole tile is the <a>, so this renders as a
   span — never nest an anchor inside an anchor. */
.portal-stat__link {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  margin-top: auto;
  padding-top: var(--portal-space-5);
  border-top: 1px solid var(--portal-hairline);
  font-size: var(--portal-text-link);
  font-weight: 600;
  color: var(--portal-olive);
}
.portal-stat--summary:hover .portal-stat__link { color: var(--portal-olive-hover); }

@media (max-width: 768px) {
  .portal-stat--summary { padding: var(--portal-space-7); }
}


/* Variant: segmented bar (for distributions) */
.portal-stat__viz {
  display: flex;
  align-items: center;
  gap: var(--portal-space-3);
}
.portal-stat__bar {
  flex: 1;
  height: 4px;
  background: var(--portal-surface-soft);
  border-radius: 2px;
  overflow: hidden;
  display: flex;
}
.portal-stat__bar-seg {
  height: 100%;
}
.portal-stat__bar-seg--review { background: var(--portal-warn); }
.portal-stat__bar-seg--high   { background: var(--portal-danger); }
.portal-stat__bar-seg--info   { background: var(--portal-olive); }
.portal-stat__bar-seg--warn   { background: var(--portal-warn-bg); }
.portal-stat__sub {
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
  white-space: nowrap;
  margin: 0;
}

/* Variant: timeline dots (for sparse time-series) */
.portal-stat__timeline {
  display: flex;
  gap: var(--portal-space-1);
}
.portal-stat__timeline-dot {
  flex: 1;
  height: 6px;
  background: var(--portal-surface-soft);
  border-radius: 1.5px;
}
.portal-stat__timeline-dot--marked {
  background: var(--portal-olive);
}

/* Variant: check row (for binary state) */
.portal-stat__check-row {
  display: flex;
  align-items: center;
  gap: var(--portal-space-2);
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
}
.portal-stat__check-row svg {
  width: 14px;
  height: 14px;
  color: var(--portal-olive);
}


/* ============================================================
   3.4 PORTAL HISTORY — score trend panel
   ============================================================ */

.portal-history {
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card-lg);
  padding: var(--portal-space-10) var(--portal-space-12);
}
.portal-history__head {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--portal-space-8);
  align-items: start;
  margin-bottom: var(--portal-space-6);
}
.portal-history__head h2 {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-section);
  letter-spacing: -0.3px;
  line-height: 1.2;
  margin: 0 0 var(--portal-space-1);
  color: var(--portal-ink);
}
.portal-history__sub {
  font-size: var(--portal-text-label);
  color: var(--portal-ink-muted);
  margin: 0;
}
.portal-history__delta-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--portal-space-1);
}
.portal-history__delta-label {
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-ink-muted);
}
.portal-history__delta {
  font-family: var(--portal-font-display);
  font-weight: 500;
  font-size: var(--portal-num-stat);
  letter-spacing: -0.6px;
  line-height: 1;
  color: var(--portal-olive);
  font-variant-numeric: tabular-nums;
}
.portal-history__chart {
  position: relative;
}
.portal-history__chart svg {
  width: 100%;
  height: auto;
  aspect-ratio: 600 / 220;
  display: block;
}
.portal-history__chart-meta {
  display: flex;
  justify-content: space-between;
  margin-top: var(--portal-space-3);
  padding: 0 var(--portal-space-3);
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.16em;
  color: var(--portal-ink-muted);
  text-transform: uppercase;
}


/* ============================================================
   3.5 PORTAL FILTER — pill row, fixed-height flex centering

   Non-active pills MUST have their own surface (white + hairline) so
   they don't blend into the page background.
   ============================================================ */

.portal-filters {
  display: flex;
  gap: 6px;
}

.portal-filter {
  height: var(--portal-filter-h);
  padding: 0 var(--portal-space-6);
  display: inline-flex;
  align-items: center;
  font-size: var(--portal-text-label);
  font-weight: 500;
  color: var(--portal-ink-secondary);
  background: var(--portal-surface);
  border: 1.5px solid var(--portal-hairline-strong);
  border-radius: var(--portal-radius-pill);
  font-family: inherit;
  cursor: pointer;
  transition:
    color var(--portal-dur-base) var(--portal-ease-out),
    border-color var(--portal-dur-base) var(--portal-ease-out),
    background var(--portal-dur-base) var(--portal-ease-out);
}
.portal-filter:not(.portal-filter--active):hover {
  color: var(--portal-ink);
  border-color: var(--portal-hairline-strong);
}
.portal-filter--active,
.portal-filter--active:hover {
  background: var(--portal-ink);
  color: #fff;
  border-color: var(--portal-ink);
}
.portal-filter__count {
  margin-left: 6px;
  opacity: 0.6;
  font-variant-numeric: tabular-nums;
}


/* ============================================================
   3.6 PORTAL ALERT LIST — CSS grid for true column alignment

   Grid: 80px (chip) | 1fr (content) | auto (cta). Chip width never
   determines where text starts. CTA is text + arrow, no chip
   background — stays visible on row hover.
   ============================================================ */

.portal-alerts {
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card-lg);
  overflow: hidden;
}

.portal-alert {
  display: grid;
  grid-template-columns: 88px 1fr auto;
  gap: var(--portal-space-8);
  align-items: center;
  padding: var(--portal-space-7) var(--portal-space-8);
  border-bottom: 1px solid var(--portal-hairline);
  text-decoration: none;
  color: inherit;
  min-height: 72px;
  transition: background var(--portal-dur-base) var(--portal-ease-out);
}
.portal-alert:last-child {
  border-bottom: 0;
}
.portal-alert:hover {
  background: var(--portal-surface-warm);
}

/* The row may be a <button> when the list drives an on-page detail panel
   instead of navigating (alerts.html). Reset the UA button chrome but keep
   the base rule's bottom hairline. */
button.portal-alert {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  font: inherit;
  text-align: left;
  background: transparent;
  border-top: 0;
  border-left: 0;
  border-right: 0;
  cursor: pointer;
}
/* Selected row in a master–detail list. */
.portal-alert[aria-pressed="true"] {
  background: var(--portal-surface-warm);
  box-shadow: inset 3px 0 0 var(--portal-olive);
}

/* ============================================================
   PORTAL CHIP — THE status chip. One spec for every uppercase
   status label on a light surface (Active, severity, plan state).
   Sprint 9h: replaces per-component chip dialects (the pact-active
   ::after hack, account-overview greens, 700/0.08em alert chips).
   Spec: portal-components.md §18.
   ============================================================ */
.portal-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  padding: 6px 12px;
  border-radius: 999px;
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-caption);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
}
.portal-chip__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
.portal-chip--success {
  background: var(--portal-success-bg);
  color: var(--portal-success);
}
.portal-chip--warn {
  background: var(--portal-warn-bg);
  color: var(--portal-warn);
}
.portal-chip--danger {
  background: var(--portal-danger-bg);
  color: var(--portal-danger);
}
.portal-chip--neutral {
  background: var(--portal-surface-soft);
  color: var(--portal-ink-secondary);
}
.portal-chip--gold {
  background: var(--portal-gold-soft);
  color: var(--portal-olive-deep);
}

/* Alert severity chips consume the SAME spec (same role, same type —
   only the semantic variant colors differ, set below). */
.portal-alert__chip {
  padding: 6px 12px;
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  justify-self: start;
}
.portal-alert__chip--review {
  background: var(--portal-warn-bg);
  color: var(--portal-warn);
}
.portal-alert__chip--high,
.portal-alert__chip--blocked {
  background: var(--portal-danger-bg);
  color: var(--portal-danger);
}
.portal-alert__chip--info {
  background: var(--portal-olive-tint);
  color: var(--portal-olive-deep);
}

.portal-alert__content {
  min-width: 0;
}
/* display:block so these work whether authored as <p> or <span> — as spans
   they would otherwise run together on one line. */
.portal-alert__title {
  display: block;
  font-size: var(--portal-text-card-title);
  font-weight: 500;
  color: var(--portal-ink);
  margin: 0 0 var(--portal-space-1);
  line-height: 1.3;
}
.portal-alert__source {
  display: block;
  font-size: var(--portal-text-label);
  color: var(--portal-ink-muted);
  margin: 0;
}

.portal-alert__cta {
  font-size: var(--portal-text-link);
  font-weight: 500;
  color: var(--portal-ink-secondary);
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-1);
  transition: color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-alert__cta svg {
  width: 12px;
  height: 12px;
  transition: transform var(--portal-dur-base) var(--portal-ease-out);
}
.portal-alert:hover .portal-alert__cta {
  color: var(--portal-ink);
}
.portal-alert:hover .portal-alert__cta svg {
  transform: translateX(3px);
}


/* ============================================================
   3.7 PORTAL QUICK TILE — icon + title + sub, restrained
   ============================================================ */

.portal-quick {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--portal-space-4);
}

.portal-quick-tile {
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: 14px;
  padding: var(--portal-space-8);
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-4);
  text-decoration: none;
  color: inherit;
  /* Principle 5 — equal heights AND widths in a row */
  width: 100%;
  height: 100%;
  transition:
    border-color var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out);
}
.portal-quick-tile:hover {
  border-color: var(--portal-hairline-strong);
  transform: translateY(-1px);
}
.portal-quick-tile__icon {
  width: 32px;
  height: 32px;
  color: var(--portal-olive);
}
/* Variant: numbered marker instead of an icon (for "how it works"-style
   step grids). Same card archetype, marker substitutes icon. */
.portal-quick-tile__number {
  font-family: var(--portal-font-display);
  font-weight: 500;
  font-size: var(--portal-text-hero);
  line-height: 1;
  letter-spacing: -0.6px;
  color: var(--portal-olive);
  font-variant-numeric: tabular-nums;
}
.portal-quick-tile__title {
  font-size: var(--portal-text-card-title);
  font-weight: 600;
  color: var(--portal-ink);
  margin: 0;
  line-height: 1.3;
}
.portal-quick-tile__sub {
  font-size: var(--portal-text-card-body);
  line-height: 1.55;
  color: var(--portal-ink-muted);
  margin: 0;
}

/* --feature: marketing-feature treatment. The icon sits in a 56px
   olive-tint coin so it reads at arm's length (65+ audience) — the
   bare 32px default stays for compact dashboard quick-actions. */
.portal-quick-tile--feature {
  padding: var(--portal-space-10);
  gap: var(--portal-space-5);
}
.portal-quick-tile--feature .portal-quick-tile__icon {
  width: 56px;
  height: 56px;
  padding: var(--portal-space-5);
  box-sizing: border-box;
  background: var(--portal-olive-tint);
  border-radius: var(--portal-radius-card-lg);
}

/* --spotlight: ONE dark accent tile to break up an otherwise white
   grid (use once per grid — it marks the marquee item). */
.portal-quick-tile--spotlight {
  background: linear-gradient(180deg, var(--portal-slate) 0%, var(--portal-slate-deep) 100%);
  border-color: var(--portal-slate-deep);
}
.portal-quick-tile--spotlight:hover {
  border-color: var(--portal-slate-deep);
}
.portal-quick-tile--spotlight .portal-quick-tile__icon {
  color: var(--portal-gold);
}
.portal-quick-tile--spotlight.portal-quick-tile--feature .portal-quick-tile__icon {
  background: rgba(255, 255, 255, 0.08);
}
.portal-quick-tile--spotlight .portal-quick-tile__title {
  color: var(--portal-slate-bright);
}
.portal-quick-tile--spotlight .portal-quick-tile__sub {
  color: var(--portal-slate-text);
}

/* When a quick-tile lives inside a UL/OL li, make the li stretch + the
   tile fill the cell so heights stay equal across the row. */
.portal-quick > li,
.portal-quick-grid > li {
  display: flex;
}


/* ============================================================
   3.8 PORTAL SECTION HEAD — title row above a major section
   ============================================================ */

/* Section heads — transition moments (Principle 13). Composition:
   eyebrow + title + optional sub on the left, link on the right.
   Bottom-aligned across the row so eyebrow visually anchors. */
.portal-section__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  margin-bottom: var(--portal-space-8);
  gap: var(--portal-space-12);
}
.portal-section__head-text {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-1);
  min-width: 0;
}
.portal-section__eyebrow {
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-eyebrow);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-olive);
  margin: 0;
}
.portal-section__title {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-section);
  letter-spacing: -0.3px;
  color: var(--portal-ink);
  margin: 0;
  line-height: 1.15;
}
.portal-section__sub {
  font-size: var(--portal-text-body);
  color: var(--portal-ink-muted);
  margin: var(--portal-space-1) 0 0;
  max-width: 56ch;
  line-height: 1.5;
}
.portal-section__link {
  font-size: var(--portal-text-label);
  font-weight: 500;
  color: var(--portal-ink-secondary);
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-1);
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
  padding-bottom: 4px;
  transition: color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-section__link:hover {
  color: var(--portal-ink);
}
.portal-section__link svg {
  width: 14px;
  height: 14px;
  transition: transform var(--portal-dur-base) var(--portal-ease-out);
}
.portal-section__link:hover svg {
  transform: translateX(3px);
}


/* ============================================================
   3.9 PORTAL BENEFIT CARD — photo-led card with bulleted offer

   For benefits showcases (portal home, all-benefits hub, scheduled
   state). Photo on top, title + bullet list + text-link CTA below.
   Equal heights in a grid via height:100% (Principle 5).
   ============================================================ */

/* Sprint 9l (2026-06-17) — redesign:
   Photo went from 16:9 billboard (~280px on a 2-col grid) to a 140px
   accent strip — image now supports the brand, doesn't dominate it.
   Logo moved from "centered on photo" (low contrast, fighting busy
   photography) to a white plaque bottom-left — always legible. CTA
   upgraded from grey text-link to a ghost-button-style affordance that
   fills with olive on card hover (the whole <a> is still the link). */
/* Figma redesign (Sprint 10, node 14036-26461): white shell with INSET
   rounded media, centered partner logo, dashed offer box with tier badge +
   value column, check-bullet list with hairline dividers, and a full-width
   olive CTA. One canonical render across portal / all-benefits / activated. */
.portal-benefit-card {
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  /* 24px per Figma node 14036-26461 — intentionally above the radii token
     scale; the benefit card is the one panel spec'd at 24. */
  border-radius: 24px;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  padding: var(--portal-space-6) var(--portal-space-6) var(--portal-space-8);
  transition:
    border-color var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out),
    box-shadow var(--portal-dur-base) var(--portal-ease-out);
}
.portal-benefit-card:hover {
  border-color: var(--portal-hairline-strong);
  transform: translateY(-3px);
  box-shadow: 0 18px 36px -22px var(--portal-olive-shadow);
}

/* Media — inset rounded photo band. The logo floats centered over a
   bottom-weighted scrim so the brand mark is the hero of the band. */
.portal-benefit-card__media {
  position: relative;
  height: 164px;
  overflow: hidden;
  background: var(--portal-slate);
  border-radius: var(--portal-radius-card-lg);
  flex-shrink: 0;
}
.portal-benefit-card__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Bias the visible band toward the upper portion of the photo, where
     human subjects and key composition usually sit. Center-default crops
     mid-torso and reads weirdly. */
  object-position: center 28%;
  /* Global darken (Figma blends the photo at ~80% over a dark base) so the
     band reads dark even at its brightest point. */
  filter: brightness(0.78);
  transition: transform var(--portal-dur-base) var(--portal-ease-out);
}
.portal-benefit-card:hover .portal-benefit-card__media img {
  transform: scale(1.04);
}
/* Scrim — Figma's overlay stops verbatim (0.05 → 0.5 @66% → ~black at the
   bottom edge) so the centered logo seats on any photography. */
.portal-benefit-card__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom,
    rgba(0, 0, 0, 0.05) 2.5%,
    rgba(0, 0, 0, 0.5) 66.5%,
    rgba(0, 0, 0, 0.92) 98.5%);
  pointer-events: none;
  z-index: 1;
}

/* Logo — white-on-transparent partner mark, CENTERED on the photo band
   (Figma redesign; was bottom-left). Strong double drop-shadow gives a
   soft halo for legibility on any photo background. Per-brand HEIGHT
   calibration balances optical mass — wordmarks small, stacked marks
   taller to match visual weight. */
.portal-benefit-card__logo {
  position: absolute !important;
  inset: 0 !important;
  margin: auto !important;
  z-index: 2;
  display: block;
  box-sizing: content-box !important;
  width: auto !important;
  height: 36px !important;
  max-width: 70% !important;
  padding: 0 !important;
  background: transparent !important;
  border: 0 !important;
  border-radius: 0 !important;
  object-fit: contain !important;
  object-position: center center !important;
  box-shadow: none !important;
  filter:
    drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55))
    drop-shadow(0 3px 10px rgba(0, 0, 0, 0.55)) !important;
}
/* Per-brand height calibration — sized to Figma's proportions on the 164px
   band (Equifax wordmark = 36px there; stacked marks proportionally taller
   to match optical mass). */
.portal-benefit-card__logo[alt="Equifax"]    { height: 36px !important; }
.portal-benefit-card__logo[alt="State Farm"] { height: 36px !important; }
.portal-benefit-card__logo[alt="AFBA"]       { height: 52px !important; }
.portal-benefit-card__logo[alt="Fox Nation"] { height: 56px !important; }
.portal-benefit-card__logo[alt="NewDay Home"] { height: 72px !important; }
.portal-benefit-card__logo[alt="ADT"]         { height: 44px !important; }
.portal-benefit-card__logo[alt="NewDay 100 VA Cash Out Loan"] { height: 52px !important; }

.portal-benefit-card__body {
  /* Figma: 25px stack gap between media / title / offer box / bullets,
     20px before the CTA — snapped to the 4px grid (24 / 20). */
  padding: var(--portal-space-8) 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-8);
  flex: 1;
}
.portal-benefit-card__title {
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-section);
  font-weight: 600;
  letter-spacing: -0.3px;
  line-height: 1.15;
  color: var(--portal-ink);
  margin: 0;
}
/* Body summary — short paragraph (default). Senior-friendly readable.
   Use this OR the bullet list, not both. Default is summary for
   refinement; bullets are a fallback when feature parity matters. */
.portal-benefit-card__summary {
  font-size: var(--portal-text-card-body);
  color: var(--portal-ink-muted);
  line-height: 1.55;
  margin: 0;
  flex: 1;
  /* Truncate after 3 lines so card heights stay consistent and long
     summaries don't push the CTA way down. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Check-bullet list (Figma redesign) — the card's substance. Filled olive
   circle-check icon, body-size copy, hairline divider between rows. */
.portal-benefit-card__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  flex: 1;
}
.portal-benefit-card__list li {
  font-size: var(--portal-text-body);
  font-weight: 500;
  color: var(--portal-ink);
  line-height: 1.3;
  display: grid;
  grid-template-columns: 20px 1fr;
  gap: var(--portal-space-4);
  align-items: center;
  padding: var(--portal-space-2) 0;
}
.portal-benefit-card__list li + li {
  border-top: 1px solid var(--portal-hairline);
}
.portal-benefit-card__list li::before {
  content: "";
  display: block;
  width: 20px;
  height: 20px;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><circle cx='10' cy='10' r='10' fill='%234a5b2d'/><path d='M5.8 10.4 L8.6 13.2 L14.2 7.2' stroke='white' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>") center / 20px no-repeat;
}
/* CTA — full-width solid olive button (Figma redesign). The whole card is
   the link; this is the visual action anchor at the bottom. */
.portal-benefit-card__cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--portal-space-7); /* Figma: 20px between label and arrow */
  align-self: stretch;
  margin-top: auto;
  min-height: 56px;
  padding: var(--portal-space-4) var(--portal-space-6);
  background: var(--portal-olive);
  color: #ffffff;
  border: 0;
  border-radius: var(--portal-radius-button);
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-card-title);
  letter-spacing: 0;
  line-height: 1.2;
  text-transform: none;
  transition:
    background-color var(--portal-dur-fast) var(--portal-ease-out),
    transform var(--portal-dur-fast) var(--portal-ease-out);
}
.portal-benefit-card__cta svg {
  width: 18px;
  height: 18px;
  transition: transform var(--portal-dur-fast) var(--portal-ease-out);
}
.portal-benefit-card:hover .portal-benefit-card__cta {
  background: var(--portal-olive-hover);
}
.portal-benefit-card:hover .portal-benefit-card__cta svg {
  transform: translateX(3px);
}
.portal-benefit-card:active .portal-benefit-card__cta {
  transform: scale(0.98);
}

@media (prefers-reduced-motion: reduce) {
  .portal-benefit-card,
  .portal-benefit-card__media img,
  .portal-benefit-card__cta svg { transition: none; }
  .portal-benefit-card:hover { transform: none; box-shadow: none; }
  .portal-benefit-card:hover .portal-benefit-card__media img { transform: none; }
  .portal-benefit-card:hover .portal-benefit-card__cta svg { transform: none; }
}

/* Value box — dashed offer panel below the title (Figma redesign, node
   14036-26461). Tier badge + bold offer line in the main column; member /
   paid offers add a right "worth" column ($ amount over a label) behind a
   vertical divider. Olive = Free, gold = For all members, slate = Paid. */
.portal-benefit-card__valuebox {
  /* Dashed border painted as an SVG background layer (first layer of each
     tier's background-image below) — CSS `dashed` can't match Figma's
     dash/gap rhythm; the SVG rect uses stroke-dasharray 7 6 at 1.5px
     (stroke-width 3, outer half clipped), rx matching the 10px radius. */
  border: 0;
  border-radius: 10px; /* Figma node 14036-26461 */
  padding: var(--portal-space-7) var(--portal-space-6);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--portal-space-4);
}
.portal-benefit-card__valuebox-main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--portal-space-6);
  min-width: 0;
}
.portal-benefit-card__tier {
  flex-shrink: 0;
  padding: var(--portal-space-1) var(--portal-space-3);
  border-radius: var(--portal-radius-chip);
  /* Graphik medium — tier badges left the Stratum brand set (round 3):
     uppercase Stratum read too loud at chip size. */
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-card-title);
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1.1;
}
.portal-benefit-card__benefit {
  margin: 0;
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-card-title);
  font-weight: 600;
  line-height: 1.3;
  color: var(--portal-ink);
  /* Offer copy is written to fit ONE line at 3-up card width (keeps every
     value box the same height without reserving a phantom second line).
     When editing partners-data.js `benefit`, keep it short enough. */
}
/* Worth column — Stratum $ amount over a small label, split from the main
   column by a vertical rule. Only rendered for member / paid offers. */
.portal-benefit-card__valuebox-worth {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-2);
  padding-left: var(--portal-space-6);
  border-left: 1.5px solid rgba(0, 0, 0, 0.4); /* Figma divider */
}
.portal-benefit-card__value {
  font-family: var(--portal-font-display);
  font-size: var(--portal-num-stat);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.6px;
  line-height: 1;
  color: var(--portal-ink);
}
.portal-benefit-card__value-label {
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-body);
  font-weight: 600;
  color: var(--portal-ink);
}
/* Tier skins — dash stroke + tint gradient are Figma-verbatim (node
   14036-26461): free = army-green on #e0e4d9→white, member = dark-khaki on
   #faf2e0→white, paid = ash on #e3e3e3→white. Each background stacks the
   dash SVG over the tint gradient. */
/* Free — olive */
.portal-benefit-card__valuebox--free {
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><rect width='100%25' height='100%25' rx='10' fill='none' stroke='%23576038' stroke-width='3' stroke-dasharray='7 6'/></svg>"),
    linear-gradient(115deg, #e0e4d9 11%, #ffffff 106%);
}
.portal-benefit-card__valuebox--free .portal-benefit-card__tier {
  background: var(--portal-olive);
  color: #ffffff;
}
/* For all members — gold */
.portal-benefit-card__valuebox--member {
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><rect width='100%25' height='100%25' rx='10' fill='none' stroke='%237e6f3b' stroke-width='3' stroke-dasharray='7 6'/></svg>"),
    linear-gradient(115deg, #faf2e0 11%, #ffffff 106%);
}
.portal-benefit-card__valuebox--member .portal-benefit-card__tier {
  background: #fac957; /* Figma golden-yellow — brighter than --portal-gold on purpose */
  color: #1f2410;
}
/* Paid (ADT, AFBA) — slate, distinct from the gold member offers */
.portal-benefit-card__valuebox--paid {
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><rect width='100%25' height='100%25' rx='10' fill='none' stroke='%239b9b90' stroke-width='3' stroke-dasharray='7 6'/></svg>"),
    linear-gradient(115deg, #e3e3e3 11%, #ffffff 106%);
}
.portal-benefit-card__valuebox--paid .portal-benefit-card__tier {
  background: var(--portal-slate);
  color: #ffffff;
}
/* On-dark variant — benefit-page hero (dark photo banner): a solid surface
   card that lifts off the hero. The tier chip + text keep their colour from
   the --free / --member / --paid modifier applied alongside it. Defined after
   the tier blocks so the solid surface wins over their tint backgrounds. */
.portal-benefit-card__valuebox--on-dark {
  background: var(--portal-surface);
  border-color: transparent;
  box-shadow: 0 16px 36px -18px rgba(0, 0, 0, 0.55);
  max-width: 440px;
  padding: var(--portal-space-4) var(--portal-space-5);
}


/* ============================================================
   3.9a1 BENEFIT BRIEF (`.portal-benefit-brief`) — the reduced
   benefit card. Team feedback (2026-08-02): the catalog cards carry
   too much at once (photo band + dashed offer box + 3 bullets + full
   CTA). This is the same benefit reduced to the four things that
   decide a click: WHO it's from (logo), WHAT it is (title), WHAT you
   get (one line), and the affordance (plus). Reference: the Solutek
   service-card row — icon slot, title, short line, circular plus,
   with the hovered card filling brand colour.

   Not a `--variant` of .portal-benefit-card: it drops four of that
   component's five children, so it is its own block. Both are
   canonical — .portal-benefit-card stays the merchandising card
   (portal home, activated row, partner pages); this one is the
   catalog/browse card.
   ============================================================ */
.portal-benefit-brief {
  display: flex;
  flex-direction: column;
  width: 100%;
  min-height: 296px;
  padding: var(--portal-space-10) var(--portal-space-8) var(--portal-space-8);
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  /* Same 24 as .portal-benefit-card — the two read as one family. */
  border-radius: 24px;
  text-decoration: none;
  color: inherit;
  transition:
    background-color var(--portal-dur-fast) ease,
    border-color var(--portal-dur-fast) ease,
    transform var(--portal-dur-base) var(--portal-ease-out),
    box-shadow var(--portal-dur-base) var(--portal-ease-out);
}

/* Logo slot — fixed 48px band so every card in a row shares one optical
   baseline regardless of how tall the partner mark is. */
.portal-benefit-brief__logo {
  display: flex;
  align-items: center;
  height: 48px;
  flex-shrink: 0;
}
.portal-benefit-brief__logo img {
  display: block;
  width: auto;
  height: 28px;
  /* Generous enough that no calibrated mark ever hits the cap — a clamped
     width would letterbox the mark inside its box and silently shrink it
     below its calibrated height (State Farm and AFBA both did at 152). */
  max-width: 176px;
  object-fit: contain;
  object-position: left center;
  /* Partner marks ship white-on-transparent (they were cut for the dark
     photo band). `brightness(0)` flattens each mark to a single tone, then
     `invert()` sets that tone: 0 = black, 0.38 ≈ #616161 (grey, quiet enough
     that the marks sit behind the titles), 1 = white for the olive hover
     fill. One lever, and the same filter function list at both ends so the
     hover swap interpolates instead of snapping. */
  filter: brightness(0) invert(0.38);
  transition: filter var(--portal-dur-fast) ease;
}
/* Partners that ship a full-colour mark on a TRANSPARENT background opt out
   of the flatten and render in brand colour (`logoColor` in
   partners-data.js). A colour asset on a white/opaque plate must NOT use
   this — the plate would show as a white box on the card and, worse, become
   a white block under the hover invert. */
.portal-benefit-brief__logo img[data-logo-color] { filter: none; }
/* Per-brand height calibration — one table, keyed off each mark's aspect
   ratio so every logo lands on roughly the same optical mass in the row.
   Wide wordmarks (Equifax 5.4:1, TaxHawk 4.6:1, AFBA 4.3:1, NewDay 100
   4.7:1) sit at the 28px default; the extra-wide State Farm mark (6.9:1)
   comes down a step; squarer/stacked marks go up. Calibrate here, never
   per page. */
.portal-benefit-brief__logo img[alt="State Farm"]  { height: 24px; }
/* Pastel #320 — Fox Nation is a near-square mark (1.07:1), so at 40px it read
   noticeably smaller than the wide wordmarks beside it. Fills the 48px slot. */
.portal-benefit-brief__logo img[alt="Fox Nation"]  { height: 48px; }
.portal-benefit-brief__logo img[alt="NewDay Home"] { height: 44px; }
/* ADT's colour lockup is the blue knockout block (1.3:1), not the boxless
   wordmark the white asset carries (2:1) — it needs the block treatment. */
.portal-benefit-brief__logo img[alt="ADT"]         { height: 36px; }

.portal-benefit-brief__title {
  margin: var(--portal-space-8) 0 0;
  font-family: var(--portal-font-body);
  /* Same role as .portal-benefit-card__title — same token. */
  font-size: var(--portal-text-section);
  font-weight: 600;
  letter-spacing: -0.3px;
  line-height: 1.2;
  color: var(--portal-ink);
  transition: color var(--portal-dur-fast) ease;
}
/* The one line that matters — partners-data.js `benefit` (what you get).
   Ink-secondary, not ink-muted: at 18px this line is the card's only piece
   of prose and muted read too light against the title. */
.portal-benefit-brief__line {
  margin: var(--portal-space-4) 0 0;
  font-size: var(--portal-text-body);
  font-weight: 400;
  line-height: 1.5;
  color: var(--portal-ink-secondary);
  transition: color var(--portal-dur-fast) ease;
}

/* Action row — pinned to the bottom. The plus is the affordance; the slot
   beside it holds EITHER the reveal-on-hover label (default) OR the
   Activated pill (activated cards). Never both, so the row can never
   collide at 4-up width. */
.portal-benefit-brief__action {
  display: flex;
  align-items: center;
  gap: var(--portal-space-4);
  margin-top: auto;
  padding-top: var(--portal-space-8);
}
.portal-benefit-brief__plus {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--portal-radius-pill);
  /* Neutral soft surface, not --portal-olive-tint: the tint is 6% olive and
     the circle disappeared against the white card. */
  background: var(--portal-surface-soft);
  color: var(--portal-olive);
  transition:
    background-color var(--portal-dur-fast) ease,
    color var(--portal-dur-fast) ease;
}
.portal-benefit-brief__plus svg {
  width: 16px;
  height: 16px;
}
.portal-benefit-brief__label {
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--portal-olive);
  white-space: nowrap;
  /* Hidden by default, revealed on hover. Opacity + transform only — the
     label always occupies its slot, so the reveal never reflows the row. */
  opacity: 0;
  transform: translateX(-6px);
  transition:
    opacity var(--portal-dur-fast) var(--portal-ease-out),
    transform var(--portal-dur-fast) var(--portal-ease-out),
    color var(--portal-dur-fast) ease;
}
/* Activated pill — mirrors .portal-benefit-card__active-pill's skin at the
   14px caption floor (that component still carries a pre-token 12px). */
.portal-benefit-brief__pill {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-1);
  height: 28px;
  padding: 0 var(--portal-space-3);
  border-radius: var(--portal-radius-pill);
  background: var(--portal-olive);
  color: #ffffff;
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  transition:
    background-color var(--portal-dur-fast) ease,
    color var(--portal-dur-fast) ease;
}
.portal-benefit-brief__pill svg { flex: 0 0 auto; }

/* Hover — the card fills olive and every child inverts. Gated: on touch,
   :hover latches after a tap and the card would stay filled. */
@media (hover: hover) and (pointer: fine) {
  .portal-benefit-brief:hover {
    background: var(--portal-olive);
    border-color: var(--portal-olive);
    transform: translateY(-3px);
    box-shadow: 0 18px 36px -22px var(--portal-olive-shadow);
  }
  .portal-benefit-brief:hover .portal-benefit-brief__logo img {
    filter: brightness(0) invert(1);
  }
  .portal-benefit-brief:hover .portal-benefit-brief__title { color: #ffffff; }
  .portal-benefit-brief:hover .portal-benefit-brief__line { color: rgba(255, 255, 255, 0.78); }
  .portal-benefit-brief:hover .portal-benefit-brief__plus {
    background: #ffffff;
    color: var(--portal-olive);
  }
  .portal-benefit-brief:hover .portal-benefit-brief__label {
    opacity: 1;
    transform: translateX(0);
    color: #ffffff;
  }
  .portal-benefit-brief:hover .portal-benefit-brief__pill {
    background: rgba(255, 255, 255, 0.18);
    color: #ffffff;
  }
}
/* No hover to reveal the label — on touch, and at the 1-up / 2-up widths
   where the card is wide enough to carry it, the label just stays visible. */
@media (hover: none), (pointer: coarse), (max-width: 900px) {
  .portal-benefit-brief__label { opacity: 1; transform: none; }
}
.portal-benefit-brief:active { transform: translateY(-1px) scale(0.99); }
.portal-benefit-brief:focus-visible {
  outline: none;
  border-color: var(--portal-olive);
  box-shadow: 0 0 0 4px var(--portal-olive-tint);
}
/* Activated — 2px olive edge on a light-green field. Both the border and the
   #f4f6ec tint are lifted verbatim from .portal-benefit-card--activated so
   the two cards signal the same state the same way. */
.portal-benefit-brief--activated {
  background: #f4f6ec;
  border-width: 2px;
  border-color: var(--portal-olive);
  padding: calc(var(--portal-space-10) - 1px) calc(var(--portal-space-8) - 1px) calc(var(--portal-space-8) - 1px);
}

@media (prefers-reduced-motion: reduce) {
  .portal-benefit-brief { transition: background-color var(--portal-dur-fast) ease, border-color var(--portal-dur-fast) ease; }
  .portal-benefit-brief:hover { transform: none; box-shadow: none; }
  .portal-benefit-brief:active { transform: none; }
  /* Keep the opacity reveal (it aids comprehension); drop the movement. */
  .portal-benefit-brief__label {
    transform: none;
    transition: opacity var(--portal-dur-fast) ease, color var(--portal-dur-fast) ease;
  }
}

/* Tablet / mobile — cards get roomier text room, so the logo can breathe. */
@media (max-width: 720px) {
  .portal-benefit-brief { min-height: 0; }
}


/* ============================================================
   3.9a3 PROTECTION PATH (`.portal-medals`) — the portal's flagship
   action driver (Slav, 2026-08-02; static-card revision same day,
   structure per Slav's badge-cards reference: framed media panel
   with the medal, tag chip, bold title, description, full-width
   action). Three protection medals — Credit / Car / Taxes.

   An earned medal renders in full colour with a gold halo and an
   ambient sheen sweep on an olive-tinted media panel; an unearned
   one sits in grayscale (fully opaque) on a neutral panel. One coin
   asset per medal, two states — colour is never a second image,
   it's the removal of a filter.

   State is stamped by portal-medals.js (`is-earned` on each <li>,
   href swap). CSS owns everything visual.
   ============================================================ */
.portal-medals__path {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--portal-space-7);
  align-items: stretch;
}
.portal-medals__step { display: flex; }

/* The card — one static link, reference structure. */
.portal-medals__card {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-4);
  padding: var(--portal-space-7);
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: 24px; /* benefit-card family */
  text-decoration: none;
  color: inherit;
  box-shadow: 0 1px 0 rgba(14, 15, 12, 0.02), 0 20px 40px -30px rgba(74, 91, 45, 0.35);
  transition:
    border-color var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out),
    box-shadow var(--portal-dur-base) var(--portal-ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .portal-medals__card:hover {
    border-color: var(--portal-hairline-strong);
    transform: translateY(-3px);
    box-shadow: 0 1px 0 rgba(14, 15, 12, 0.02), 0 24px 48px -30px rgba(74, 91, 45, 0.45);
  }
}
.portal-medals__card:focus-visible {
  outline: none;
  border-color: var(--portal-olive);
  box-shadow: 0 0 0 4px var(--portal-olive-tint);
}
.portal-medals__card:active { transform: translateY(-1px) scale(0.99); }
/* Earned card: same signal pair as .portal-benefit-brief--activated. */
.portal-medals__step.is-earned .portal-medals__card {
  border: 2px solid var(--portal-olive);
  padding: calc(var(--portal-space-7) - 1px);
}

/* Media panel — the medal's stage. Neutral while locked; olive-tinted
   once earned (the panel celebrates with the coin). */
.portal-medals__media {
  position: relative;
  display: grid;
  place-items: center;
  height: 224px;
  border-radius: var(--portal-radius-card-lg);
  background: linear-gradient(180deg, var(--portal-surface-soft) 0%, var(--portal-surface-warm) 100%);
  transition: background-color var(--portal-dur-base) ease;
  flex-shrink: 0;
}
.portal-medals__step.is-earned .portal-medals__media {
  background: linear-gradient(180deg, #e6ebd7 0%, #f4f6ec 100%);
}
/* Tag chip — top-right of the media panel, like the reference's
   category tag. Communicates the deal, not decoration. */
.portal-medals__tag {
  position: absolute;
  top: var(--portal-space-4);
  right: var(--portal-space-4);
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 var(--portal-space-4);
  border-radius: var(--portal-radius-pill);
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  font-size: var(--portal-text-caption);
  font-weight: 600;
  line-height: 1;
  color: var(--portal-ink-secondary);
  white-space: nowrap;
}

/* Coin — the medal artwork. Circle-clipped sheen; gold halo behind
   earned coins (state, per Principle 3). */
.portal-medals__coin {
  position: relative;
  width: 176px;
  height: 176px;
  border-radius: 50%;
}
.portal-medals__coin::before {
  content: "";
  position: absolute;
  inset: -20px;
  border-radius: 50%;
  background: radial-gradient(closest-side, var(--portal-gold-soft) 0%, rgba(201, 162, 74, 0.06) 55%, transparent 72%);
  opacity: 0;
  transition: opacity var(--portal-dur-base) ease;
}
.portal-medals__coin img {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* Locked = the medal you haven't earned yet: drained to silver but
     fully OPAQUE — translucency let the panel show through the coin. */
  filter: grayscale(1) contrast(0.78) brightness(1.16);
  transition: filter 480ms var(--portal-ease-out);
}
/* Sheen — a soft light band sweeping across the earned medal. */
.portal-medals__coin::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: linear-gradient(115deg,
    transparent 42%,
    rgba(255, 255, 255, 0.0) 44%,
    rgba(255, 255, 255, 0.38) 50%,
    rgba(255, 255, 255, 0.0) 56%,
    transparent 58%);
  background-size: 320% 100%;
  background-position: 120% 0;
  opacity: 0;
  pointer-events: none;
}
.portal-medals__step.is-earned .portal-medals__coin::before { opacity: 1; }
.portal-medals__step.is-earned .portal-medals__coin img { filter: none; }
.portal-medals__step.is-earned .portal-medals__coin::after {
  opacity: 1;
  animation: portal-medal-sheen 4400ms var(--portal-ease-out) 600ms infinite;
}
/* Live earn moment (vs:state:change mid-session): one proud pop. */
.portal-medals__step.is-earned.is-just-earned .portal-medals__coin img {
  animation: portal-medal-pop 640ms var(--portal-ease-out);
}
@keyframes portal-medal-sheen {
  0%   { background-position: 120% 0; }
  38%  { background-position: -120% 0; }
  100% { background-position: -120% 0; }
}
@keyframes portal-medal-pop {
  0%   { transform: scale(0.92); }
  55%  { transform: scale(1.05); }
  100% { transform: scale(1); }
}

.portal-medals__step-title {
  margin: var(--portal-space-2) 0 0;
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-section);
  font-weight: 600;
  letter-spacing: -0.3px;
  line-height: 1.2;
  color: var(--portal-ink);
}
.portal-medals__partner {
  margin: 0;
  font-size: var(--portal-text-label);
  line-height: 1.5;
  color: var(--portal-ink-secondary);
  /* Two reserved lines so the action row sits on one baseline across
     the three cards regardless of copy wrap (Principle 5). */
  min-height: 48px;
}

/* Action slot — full-width, bottom-pinned. Button CTA when locked,
   gold status bar when earned. Both live in the markup; CSS shows
   exactly one. Equal 48px heights keep the row aligned. */
.portal-medals__cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--portal-space-3);
  align-self: stretch;
  margin-top: auto;
  min-height: 48px;
  padding: 0 var(--portal-space-6);
  border-radius: var(--portal-radius-button);
  background: var(--portal-olive);
  color: #ffffff;
  font-size: var(--portal-text-label);
  font-weight: 600;
  line-height: 1;
  transition:
    background-color var(--portal-dur-fast) ease,
    transform var(--portal-dur-fast) var(--portal-ease-out);
}
.portal-medals__cta svg {
  width: 14px;
  height: 14px;
  transition: transform var(--portal-dur-fast) var(--portal-ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .portal-medals__card:hover .portal-medals__cta { background: var(--portal-olive-hover); }
  .portal-medals__card:hover .portal-medals__cta svg { transform: translateX(3px); }
}
.portal-medals__card:active .portal-medals__cta { transform: scale(0.98); }
.portal-medals__pill {
  display: none;
  align-items: center;
  justify-content: center;
  gap: var(--portal-space-2);
  align-self: stretch;
  margin-top: auto;
  min-height: 48px;
  padding: 0 var(--portal-space-6);
  border-radius: var(--portal-radius-button);
  background: var(--portal-gold-soft);
  color: var(--portal-ink);
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  /* Pixel-perfect centering: line box collapsed to the glyphs, and the
     trailing letter-space compensated so left/right optical padding
     matches. */
  line-height: 1;
}
.portal-medals__pill svg { flex: 0 0 auto; color: var(--portal-olive); }
.portal-medals__pill span {
  line-height: 1;
  margin-right: -0.04em;
}
.portal-medals__step.is-earned .portal-medals__pill { display: flex; }
.portal-medals__step.is-earned .portal-medals__cta { display: none; }

@media (prefers-reduced-motion: reduce) {
  .portal-medals__card,
  .portal-medals__card:hover { transform: none; }
  .portal-medals__coin::after { animation: none !important; opacity: 0 !important; }
  .portal-medals__step.is-earned.is-just-earned .portal-medals__coin img { animation: none; }
  .portal-medals__cta svg { transition: none; }
}

/* Tablet — media + coin step down. */
@media (max-width: 1180px) {
  .portal-medals__media { height: 192px; }
  .portal-medals__coin { width: 144px; height: 144px; }
}
/* Mobile — same structure, one card per row. */
@media (max-width: 720px) {
  .portal-medals__path { grid-template-columns: 1fr; gap: var(--portal-space-6); }
  .portal-medals__media { height: 192px; }
  .portal-medals__coin { width: 144px; height: 144px; }
  .portal-medals__partner { min-height: 0; }
}


/* ============================================================
   3.9a2 INCOME GATE — covers pre-qualification surfaces until the
   member uploads a W-2 / 1040 (portal-income-gate.js). The host
   keeps its layout; its content blurs behind a centered cover card
   with the upload CTA. Round 3 — income left the profile wizard.
   ============================================================ */
.income-gate-host { position: relative; }
.income-gate-host.is-income-locked > :not([data-income-cover]) {
  filter: blur(10px) saturate(0.8);
  pointer-events: none;
  user-select: none;
}
.income-gate-cover {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  padding: var(--portal-space-6);
}
/* display: grid above would defeat the [hidden] attribute the gate JS
   sets after the upload — without this, the cover stayed painted over
   the revealed offer. */
.income-gate-cover[hidden] { display: none; }
.income-gate-cover__card {
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card-lg);
  box-shadow: 0 24px 56px -24px rgba(14, 15, 12, 0.4);
  padding: var(--portal-space-10);
  max-width: 440px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--portal-space-4);
}
.income-gate-cover__icon {
  display: inline-grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--portal-olive-tint);
  color: var(--portal-olive-deep);
}
.income-gate-cover__title {
  margin: 0;
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-card-title);
  font-weight: 600;
  line-height: 1.25;
  color: var(--portal-ink);
}
.income-gate-cover__body {
  margin: 0;
  font-size: var(--portal-text-label);
  line-height: 1.5;
  color: var(--portal-ink-secondary);
}
.income-gate-cover__card .portal-btn { margin-top: var(--portal-space-2); }

/* Upload dialog — one document, one action. */
.income-dialog { position: fixed; inset: 0; z-index: 120; }
.income-dialog[hidden] { display: none; }
body.income-dialog-open { overflow: hidden; }
.income-dialog__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(14, 15, 12, 0.55);
}
.income-dialog__panel {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(560px, calc(100vw - 32px));
  background: var(--portal-surface);
  border-radius: var(--portal-radius-card-lg);
  border-top: 3px solid var(--portal-olive);
  box-shadow: 0 32px 80px -24px rgba(14, 15, 12, 0.5);
  padding: var(--portal-space-10);
}
.income-dialog__close { position: absolute; top: 16px; right: 16px; }
.income-dialog__eyebrow {
  margin: 0 0 var(--portal-space-2);
  font-size: var(--portal-text-eyebrow);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-olive);
}
.income-dialog__title {
  margin: 0 0 var(--portal-space-3);
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-section);
  font-weight: 600;
  letter-spacing: -0.3px;
  line-height: 1.2;
  color: var(--portal-ink);
}
.income-dialog__body {
  margin: 0 0 var(--portal-space-6);
  font-size: var(--portal-text-body);
  line-height: 1.5;
  color: var(--portal-ink-secondary);
}
.income-dialog__drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--portal-space-3);
  padding: var(--portal-space-8) var(--portal-space-6);
  border: 2px dashed var(--portal-hairline-strong);
  border-radius: var(--portal-radius-card);
  background: var(--portal-surface-soft);
  color: var(--portal-ink-secondary);
  text-align: center;
  cursor: pointer;
  position: relative;
  transition: border-color var(--portal-dur-base) var(--portal-ease-out), background var(--portal-dur-base) var(--portal-ease-out);
}
.income-dialog__drop:hover { border-color: var(--portal-olive); }
.income-dialog__drop.is-filled {
  border-color: var(--portal-olive);
  border-style: solid;
  background: var(--portal-olive-tint);
  color: var(--portal-olive-deep);
}
.income-dialog__file {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}
.income-dialog__drop-label {
  font-size: var(--portal-text-label);
  font-weight: 500;
  line-height: 1.4;
  max-width: 36ch;
  overflow-wrap: anywhere;
}
.income-dialog__meta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--portal-space-2);
  margin: var(--portal-space-4) 0 var(--portal-space-6);
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
}
.income-dialog__actions {
  display: flex;
  justify-content: center;
  gap: var(--portal-space-4);
}
.income-dialog__skip {
  margin: var(--portal-space-4) 0 0;
  text-align: center;
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
}
@media (prefers-reduced-motion: reduce) {
  .income-dialog__drop { transition: none; }
}

/* ============================================================
   3.9b PORTAL OFFER PANEL — dark pre-qualification offer card.

   Promoted from the page-scoped `.bnft-active__offer` on
   newday-home-new-active (Sprint 10, 2026-07-02) so the unlocked
   dashboard's "You're pre-qualified" section and the benefit
   active pages render the SAME offer presentation.

   Slate panel, gold→olive top rule, centered stack:
   eyebrow → display amount → sub line → perk pill →
   two-stat row → CTA (canonical .portal-btn --on-dark) → meta.
   ============================================================ */
.portal-offer-panel {
  background: var(--portal-slate);
  color: var(--portal-slate-bright);
  border-radius: var(--portal-radius-panel);
  padding: var(--portal-space-12);
  text-align: center;
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.portal-offer-panel::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--portal-gold), var(--portal-olive));
}
/* Optional product title — names the loan when the panel appears outside
   its product page (e.g. the dashboard pre-qual grid, where two offers
   sit side by side and need per-card identity). */
.portal-offer-panel__title {
  margin: 0 0 var(--portal-space-5);
  padding-bottom: var(--portal-space-4);
  border-bottom: 1px solid var(--portal-slate-line);
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-card-title);
  letter-spacing: -0.2px;
  line-height: 1.3;
  color: var(--portal-slate-bright);
}
.portal-offer-panel__eyebrow {
  margin: 0 0 var(--portal-space-2);
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-gold);
}
.portal-offer-panel__num {
  margin: 0 0 var(--portal-space-2);
  font-size: var(--portal-num-display);
  font-weight: 500;
  letter-spacing: -1.2px;
  line-height: 1;
  color: var(--portal-slate-bright);
  font-family: var(--portal-font-display);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.portal-offer-panel__sub {
  margin: 0 0 var(--portal-space-6);
  font-size: var(--portal-text-body);
  /* Full white, not --portal-slate-text — 60% white was too dim on the
     slate panel for the 65+ audience (round-3 review). */
  color: #ffffff;
}
/* Included-value perk — golden pill highlighting what comes free with
   the offer. Reads as a "yours free" line next to the loan terms. */
.portal-offer-panel__perk {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  margin: var(--portal-space-4) 0 0;
  padding: 6px 12px;
  background: rgba(203, 165, 92, 0.18);
  color: var(--portal-gold);
  border-radius: 999px;
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.02em;
}
.portal-offer-panel__perk svg {
  flex: none;
  /* Optical alignment: geometric centering leaves the star ~1px high of
     the lowercase text's visual center. */
  position: relative;
  top: 1px;
}
.portal-offer-panel__rates {
  display: flex;
  justify-content: space-between;
  gap: var(--portal-space-8);
  margin: var(--portal-space-6) 0 var(--portal-space-8);
  padding: 0 var(--portal-space-4);
}
.portal-offer-panel__rate { text-align: left; }
.portal-offer-panel__rate-label {
  margin: 0;
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--portal-gold);
}
.portal-offer-panel__rate-value {
  margin: var(--portal-space-2) 0 0;
  font-size: var(--portal-text-card-title);
  font-weight: 600;
  color: var(--portal-slate-bright);
  font-variant-numeric: tabular-nums;
}
.portal-offer-panel__meta {
  margin: var(--portal-space-6) 0 0;
  font-size: var(--portal-text-caption);
  /* Full white — same senior-readability call as __sub above. */
  color: #ffffff;
}
/* Submitted state — newday-approval.js swaps the panel CTA for this block
   after the member authorizes full approval (Pastel #152). Communicates
   the hand-off: a Loan Expert calls next. */
.portal-offer-panel__submitted {
  display: flex;
  align-items: center;
  gap: var(--portal-space-6);
  margin: var(--portal-space-6) 0 0;
  padding: var(--portal-space-5) var(--portal-space-6);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--portal-slate-line);
  border-radius: var(--portal-radius-card);
  text-align: left;
}
.portal-offer-panel__submitted-icon {
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--portal-olive);
  color: #ffffff;
}
.portal-offer-panel__submitted-title {
  margin: 0;
  font-size: var(--portal-text-label);
  font-weight: 600;
  line-height: 1.3;
  color: #ffffff;
}
.portal-offer-panel__submitted-sub {
  margin: var(--portal-space-1) 0 0;
  font-size: var(--portal-text-caption);
  line-height: 1.4;
  /* Full white — same senior-readability call as __sub above. */
  color: #ffffff;
}


/* ============================================================
   3.10y CONCIERGE PRESENCE BAND (Sprint 9o) — the member's
   personal expert, always one tap away. Compact identity +
   status + a primary action; used on the dashboard and inline.
   The full relationship lives on concierge.html (.con-* there).
   ============================================================ */
.portal-concierge {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--portal-space-5) var(--portal-space-7);
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card-lg);
  padding: var(--portal-space-6) var(--portal-space-7);
}
.portal-concierge__person { display: flex; align-items: center; gap: var(--portal-space-4); flex-shrink: 0; }
.portal-concierge__avatar-wrap { position: relative; flex-shrink: 0; }
.portal-concierge__avatar {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  background: var(--portal-surface-soft);
}
/* Online dot — shared by the band and the hub hero. */
.portal-concierge__status {
  position: absolute;
  right: 0;
  bottom: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #5a8a2c;
  border: 2px solid var(--portal-surface);
}
.portal-concierge__id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.portal-concierge__eyebrow {
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-eyebrow);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--portal-olive);
}
.portal-concierge__name {
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-card-title);
  font-weight: 600;
  color: var(--portal-ink);
  line-height: 1.2;
}
.portal-concierge__line {
  flex: 1 1 240px;
  min-width: 0;
  margin: 0;
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-body);
  line-height: 1.5;
  color: var(--portal-ink-secondary);
}
.portal-concierge__line strong { color: var(--portal-ink); font-weight: 600; }
.portal-concierge__actions { display: flex; align-items: center; gap: var(--portal-space-5); flex-shrink: 0; flex-wrap: wrap; }
.portal-concierge__phone {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-label);
  font-weight: 600;
  color: var(--portal-olive-deep);
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--portal-dur-base) var(--portal-ease-out);
}
/* --lg variant — the phone as a first-class action (last-message band):
   "Call me at:" label + card-title-size number so the 65+ member can
   read it at arm's length. */
.portal-concierge__phone--lg {
  font-size: var(--portal-text-card-title);
  gap: var(--portal-space-3);
}
.portal-concierge__phone--lg svg { width: 20px; height: 20px; }
.portal-concierge__phone-label {
  font-size: var(--portal-text-label);
  font-weight: 500;
  color: var(--portal-ink-secondary);
}
@media (hover: hover) and (pointer: fine) {
  .portal-concierge__phone:hover { color: var(--portal-ink); }
}

/* ============================================================
   3.10y' PORTAL VIDEO HERO — standalone welcome walkthrough card.
   Landscape poster of the concierge in-office + overlay copy + a
   large play button. Sibling of `.portal-concierge` (not merged).
   ============================================================ */
.portal-video-hero {
  position: relative;
  isolation: isolate;
}
/* --split variant — copy in a left-hand column (~30 %), poster on the
   right (~70 %), both wrapped in a shared card so they read as one
   component. Copy sits on the page surface instead of overlaid on
   the image so the explainer character reads as-shot without a scrim
   competing for attention. */
.portal-video-hero--split { display: block; }
.portal-video-hero__panel {
  display: grid;
  grid-template-columns: minmax(240px, 32fr) minmax(0, 68fr);
  gap: var(--portal-space-8, 32px);
  align-items: center;
  padding: var(--portal-space-7, 28px);
  background: var(--portal-surface, #fbf9f2);
  border: 1px solid var(--portal-hairline, #e4e0d6);
  border-radius: var(--portal-radius-card-lg);
  box-shadow: 0 1px 0 rgba(14, 15, 12, 0.02), 0 20px 48px -32px rgba(14, 15, 12, 0.28);
}
/* Cap the video's max height so the panel doesn't stretch tall when the
   viewport is wide. Aspect ratio is still 16:9 (see .portal-video-hero__frame). */
.portal-video-hero__panel .portal-video-hero__frame {
  width: 100%;
  max-width: 720px;
  margin-left: auto;
  max-height: 405px;
}
.portal-video-hero__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  min-width: 0;
  padding-left: var(--portal-space-3, 8px);
}
/* Watch-video CTA inherits the canonical .portal-btn styling (12px
   corner radius, olive fill, Graphik body). Only positional tweaks live
   here — a small gap above so the button doesn't hug the sub copy. */
.portal-video-hero__cta { margin-top: var(--portal-space-3, 8px); }
.portal-video-hero__frame {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border: 2px solid var(--portal-olive, #5a6b3f);
  border-radius: var(--portal-radius-card-lg);
  text-decoration: none;
  color: #fff;
  background: #0e0f0c;
  box-shadow:
    0 2px 0 rgba(14, 15, 12, 0.04),
    0 24px 56px -28px rgba(14, 15, 12, 0.4);
}
.portal-video-hero__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  z-index: 0;
  transition: transform var(--portal-dur-slow, 480ms) var(--portal-ease-out);
}
.portal-video-hero__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, rgba(14, 15, 12, 0.78) 0%, rgba(14, 15, 12, 0.55) 32%, rgba(14, 15, 12, 0.12) 62%, rgba(14, 15, 12, 0) 100%),
    linear-gradient(180deg, rgba(14, 15, 12, 0) 55%, rgba(14, 15, 12, 0.55) 100%);
  z-index: 1;
  pointer-events: none;
}
/* Meta block — used by the LEGACY overlay layout (kept for pages that
   still nest copy inside the frame). The split variant renders copy
   inside .portal-video-hero__copy instead, styled below. */
.portal-video-hero__meta {
  position: absolute;
  left: var(--portal-space-8);
  bottom: var(--portal-space-7);
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 52%;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
}
.portal-video-hero__eyebrow {
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-eyebrow);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.88);
  margin: 0;
}
.portal-video-hero__title {
  font-family: var(--portal-font-body);
  font-size: 40px;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.6px;
  color: #ffffff;
  margin: 0;
}
.portal-video-hero__sub {
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-body);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.9);
  max-width: 46ch;
  margin: 0;
}
/* Split variant recolors — copy is now on the light page surface, not
   the dark video overlay, so eyebrow/title/sub take the standard
   portal text tones. */
.portal-video-hero--split .portal-video-hero__eyebrow { color: var(--portal-olive); }
.portal-video-hero--split .portal-video-hero__title {
  color: var(--portal-ink);
  text-shadow: none;
  font-size: 36px;
  letter-spacing: -0.5px;
}
.portal-video-hero--split .portal-video-hero__sub {
  color: var(--portal-ink-secondary);
  text-shadow: none;
}
/* Play button lives in the bottom-right corner (out of the character's
   way) as an olive medallion with a white glyph — inverts the earlier
   white-on-olive treatment so the button reads as a brand action. */
.portal-video-hero__play {
  position: absolute;
  right: var(--portal-space-6, 24px);
  bottom: var(--portal-space-6, 24px);
  z-index: 2;
  width: 68px;
  height: 68px;
  border-radius: 50%;
  background: var(--portal-olive, #5a6b3f);
  color: #ffffff;
  display: grid;
  place-items: center;
  box-shadow:
    0 0 0 10px rgba(90, 107, 63, 0.18),
    0 22px 44px -12px rgba(0, 0, 0, 0.5);
  transition: transform var(--portal-dur-base) var(--portal-ease-out), box-shadow var(--portal-dur-base) var(--portal-ease-out), background var(--portal-dur-base) var(--portal-ease-out);
}
.portal-video-hero__play svg { margin-left: 4px; }
/* Duration pill — fixed height + flex-centered so digit metrics
   (which have low descenders) don't drift off the optical midline.
   The padding is horizontal only; vertical centering is handled by
   height + align-items. */
.portal-video-hero__duration {
  position: absolute;
  top: var(--portal-space-6);
  right: var(--portal-space-6);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 28px;
  padding: 0 12px;
  border-radius: 999px;
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-caption);
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.02em;
  color: #fff;
  background: rgba(14, 15, 12, 0.62);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
/* Nudge the visual midline up by 1px — numeric glyphs (2, 4, 7) sit
   below the geometric center of the em box in most sans-serifs; the
   colon and lowercase-height cap combine to make the block look like
   it drops in the pill. A 1px transform corrects the optical drift. */
.portal-video-hero__duration { line-height: 28px; padding-top: 1px; }
@media (hover: hover) and (pointer: fine) {
  .portal-video-hero__frame:hover .portal-video-hero__poster { transform: scale(1.02); }
  .portal-video-hero__frame:hover .portal-video-hero__play {
    transform: scale(1.06);
    background: var(--portal-olive-deep, #3f4828);
    box-shadow:
      0 0 0 12px rgba(90, 107, 63, 0.22),
      0 24px 48px -12px rgba(0, 0, 0, 0.55);
  }
}
.portal-video-hero__frame:focus-visible {
  outline: 3px solid var(--portal-olive);
  outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
  .portal-video-hero__poster,
  .portal-video-hero__play { transition: none; }
}
@media (max-width: 900px) {
  .portal-video-hero__frame { aspect-ratio: 16 / 10; border-radius: var(--portal-radius-card); }
  .portal-video-hero__title { font-size: 26px; }
  .portal-video-hero__meta { left: var(--portal-space-6); right: var(--portal-space-6); bottom: var(--portal-space-6); max-width: 100%; }
  .portal-video-hero__play { width: 68px; height: 68px; }

  /* Stack the split layout below 900px so copy stays readable + the
     video keeps its aspect ratio without shrinking too narrow. */
  .portal-video-hero__panel {
    grid-template-columns: 1fr;
    gap: var(--portal-space-6, 24px);
    padding: var(--portal-space-6, 24px);
  }
  .portal-video-hero--split .portal-video-hero__title { font-size: 28px; }
  .portal-video-hero__play { width: 56px; height: 56px; right: var(--portal-space-5, 16px); bottom: var(--portal-space-5, 16px); }
}

/* ============================================================
   3.10y'' PORTAL CONCIERGE WELCOME LAYOUT (welcome mode + last-message mode)
   Vertical stack inside the existing .portal-concierge card:
   header (person) → message → footer (phone + CTA).
   ============================================================ */
.portal-concierge[data-concierge-band="welcome"],
.portal-concierge[data-concierge-band="last-message"] {
  display: block;
  padding: var(--portal-space-6) var(--portal-space-7);
}
.portal-concierge__welcome {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-4);
}
.portal-concierge__welcome-head { display: block; }
.portal-concierge__welcome-msg {
  margin: 0;
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-secondary);
  max-width: 68ch;
}
.portal-concierge__welcome-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--portal-space-5);
  flex-wrap: wrap;
  margin-top: var(--portal-space-2);
  padding-top: var(--portal-space-4);
  border-top: 1px solid var(--portal-hairline);
}

/* Last-message band — quoted concierge note w/ olive left rule so it reads as
   a message, not body copy. Timestamp on a caption line below. */
.portal-concierge__last-msg {
  margin: 0;
  padding: var(--portal-space-4) var(--portal-space-6);
  background: rgba(90, 107, 63, 0.06);
  border-left: 3px solid var(--portal-olive);
  border-radius: 0 var(--portal-radius-card) var(--portal-radius-card) 0;
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-2);
}
.portal-concierge__last-msg-text {
  margin: 0;
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink);
  max-width: 68ch;
}
.portal-concierge__last-msg-time {
  margin: 0;
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-caption);
  font-weight: 500;
  color: var(--portal-ink-muted);
  letter-spacing: 0;
}
@media (max-width: 640px) {
  .portal-concierge[data-concierge-band="welcome"],
  .portal-concierge[data-concierge-band="last-message"] { padding: var(--portal-space-5) var(--portal-space-5); }
  .portal-concierge__welcome-foot { flex-direction: column; align-items: stretch; gap: var(--portal-space-3); }
  .portal-concierge__welcome-foot .portal-concierge__phone { justify-content: center; }
}


/* ============================================================
   3.10 PORTAL SPOTLIGHT — 2-column media + copy + primary CTA

   For sections that deserve more presence than a card: referral,
   premium tier upgrade, success stories. The dark-panel variant
   echoes the hero aesthetic; the surface variant is calmer.
   ============================================================ */

.portal-spotlight {
  background: var(--portal-surface);
  background-image: linear-gradient(180deg, var(--portal-surface) 0%, var(--portal-surface-warm) 100%);
  border: 1px solid var(--portal-hairline);
  border-top: 3px solid var(--portal-olive);  /* Principle 2 — panel identity */
  border-radius: var(--portal-radius-card-lg);
  /* Real shadow so the panel separates from the cream page bg
     (Principle 2 — hairline alone is invisible on cream). */
  box-shadow:
    0 2px 0 rgba(14, 15, 12, 0.03),
    0 24px 56px -28px rgba(74, 91, 45, 0.24);
  display: grid;
  grid-template-columns: 1fr 1fr;
  overflow: hidden;
  align-items: stretch;
}
.portal-spotlight__media {
  position: relative;
  min-height: 320px;
  background: var(--portal-surface-soft);
  overflow: hidden;
}
.portal-spotlight__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.portal-spotlight__copy {
  padding: var(--portal-space-12);
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-4);
  align-items: start;
  justify-content: center;
}
.portal-spotlight__eyebrow {
  font-size: var(--portal-text-eyebrow);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-olive);
  margin: 0;
}
.portal-spotlight__title {
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-hero);
  line-height: 1.15;
  letter-spacing: -0.5px;
  color: var(--portal-ink);
  margin: 0;
}
.portal-spotlight__title strong {
  font-weight: 600;
  color: var(--portal-olive-deep);
}
.portal-spotlight__body {
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-secondary);
  margin: 0;
  max-width: 56ch;
}
.portal-spotlight__cta {
  height: var(--portal-control-h);
  padding: 0 var(--portal-space-7);
  background: var(--portal-olive);
  color: #fff;
  border-radius: var(--portal-radius-button);
  font-size: var(--portal-text-label);
  font-weight: 600;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  margin-top: var(--portal-space-2);
  box-shadow: 0 0 0 0 transparent;
  transition:
    background var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out),
    box-shadow var(--portal-dur-base) var(--portal-ease-out);
}
.portal-spotlight__cta:hover {
  background: var(--portal-olive-hover);
  color: #fff;  /* re-assert against global a:hover specificity (0,1,1) */
  transform: translateY(-1px);
  box-shadow: 0 10px 24px -10px var(--portal-olive-shadow);
}
.portal-spotlight__cta svg {
  width: 14px;
  height: 14px;
  transition: transform var(--portal-dur-base) var(--portal-ease-out);
}
.portal-spotlight__cta:hover svg {
  transform: translateX(3px);
}


/* ============================================================
   3.10b PORTAL FAQ — canonical accordion FAQ (Sprint 9c)

   2-column "copy + accordion" pattern. Replaces the inline duplicates
   that previously lived in portal.html and credit-activate.html as
   .phome-faq / .ca-faq with identical CSS.

   Structure:
     <section class="portal-faq">
       <div class="portal-faq__copy">
         <p class="portal-faq__eyebrow">FAQs</p>
         <h2 class="portal-faq__title">…</h2>
         <p class="portal-faq__body">…</p>
         <a class="portal-faq__link" href="…">All FAQs →</a>
       </div>
       <ul class="portal-faq__list" role="list">
         <li>
           <button class="portal-faq__btn" aria-expanded="false">
             <span class="portal-faq__q">…</span>
             <span class="portal-faq__icon">
               <svg class="portal-faq__icon-plus">+</svg>
               <svg class="portal-faq__icon-x">×</svg>
             </span>
           </button>
           <div class="portal-faq__a">…</div>
         </li>
       </ul>
     </section>
   ============================================================ */

.portal-faq {
  display: grid;
  grid-template-columns: 360px 1fr;
  gap: var(--portal-space-16);
  align-items: start;
}
.portal-faq__copy {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-3);
}
.portal-faq__eyebrow {
  font-size: var(--portal-text-eyebrow);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-olive);
  margin: 0;
}
.portal-faq__title {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-section-title);
  letter-spacing: -0.2px;
  color: var(--portal-ink);
  margin: 0;
  line-height: 1.25;
}
.portal-faq__body {
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-muted);
  margin: 0;
}
.portal-faq__link {
  align-self: start;
  font-size: var(--portal-text-link);
  font-weight: 500;
  color: var(--portal-ink-secondary);
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-1);
  text-decoration: none;
  margin-top: var(--portal-space-2);
  transition: color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-faq__link:hover { color: var(--portal-ink); }
.portal-faq__link svg {
  width: 12px; height: 12px;
  transition: transform var(--portal-dur-base) var(--portal-ease-out);
}
.portal-faq__link:hover svg { transform: translateX(2px); }

.portal-faq__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.portal-faq__list li + li {
  border-top: 1px solid var(--portal-hairline);
}
.portal-faq__btn {
  width: 100%;
  background: transparent;
  border: 0;
  padding: var(--portal-space-6) 0;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--portal-space-4);
  align-items: center;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: var(--portal-ink);
}
.portal-faq__q {
  font-size: var(--portal-text-card-title);
  font-weight: 500;
  color: var(--portal-ink);
  line-height: 1.3;
}
.portal-faq__icon {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1.5px solid var(--portal-hairline-strong);
  color: var(--portal-ink-secondary);
  display: inline-grid;
  place-items: center;
  position: relative;
  flex-shrink: 0;
  transition: border-color var(--portal-dur-base) var(--portal-ease-out), color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-faq__icon svg {
  position: absolute;
  width: 12px; height: 12px;
  transition: transform var(--portal-dur-base) var(--portal-ease-out), opacity var(--portal-dur-base) var(--portal-ease-out);
}
.portal-faq__icon-x { opacity: 0; }
.portal-faq__btn[aria-expanded="true"] .portal-faq__icon-plus { opacity: 0; transform: rotate(45deg); }
.portal-faq__btn[aria-expanded="true"] .portal-faq__icon-x { opacity: 1; }
.portal-faq__btn[aria-expanded="true"] .portal-faq__icon {
  border-color: var(--portal-ink);
  color: var(--portal-ink);
}
.portal-faq__a {
  padding: 0 0 var(--portal-space-7);
  font-size: var(--portal-text-body);
  line-height: 1.6;
  color: var(--portal-ink-secondary);
  max-width: 64ch;
}
.portal-faq__a a { color: var(--portal-olive-deep); text-decoration: underline; }

@media (max-width: 720px) {
  .portal-faq { grid-template-columns: 1fr; gap: var(--portal-space-8); }
}


/* ============================================================
   3.10c PORTAL STEPPER — canonical step indicator (Sprint 9c B.1)

   Numbered step rail used in multi-step flows. Replaces .ca-stepper
   (credit-activate, display-only) and .signup__stepper (signup,
   clickable). One canonical, two variants.

   Structure:
     <ol class="portal-stepper" aria-label="Activation progress">
       <li class="portal-stepper__item portal-stepper__item--active">
         <button class="portal-stepper__btn" aria-current="step">
           <span class="portal-stepper__num">
             <span class="portal-stepper__num-text">1</span>
             <svg class="portal-stepper__check">…</svg>
           </span>
           <span class="portal-stepper__label">Name</span>
         </button>
       </li>
       <!-- repeat … -->
     </ol>

   Variants:
     --readonly   non-clickable (display only) — use <span> instead of
                  <button> for the inner element; layout identical.

   Item states (set per <li>):
     --active   the current step (olive num, bold label)
     --done     a completed step (olive ring, check icon, connector
                line behind it turns olive)
     (default)  upcoming step (muted num + label, grey connector)
   ============================================================ */

.portal-stepper {
  display: flex;
  align-items: center;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
  width: 100%;
}

.portal-stepper__item {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 0;
  min-width: 0;
}

/* Connector line between items (last has none) */
.portal-stepper__item::after {
  content: "";
  flex: 1;
  height: 2px;
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.12);
  transition: background var(--portal-dur-base) var(--portal-ease-out);
}
.portal-stepper__item:last-child::after {
  display: none;
}
.portal-stepper__item--done::after {
  background: var(--portal-olive);
}

/* Clickable variant (signup) — also styled as a layout wrapper for readonly */
.portal-stepper__btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 8px 8px;
  background: transparent;
  border: 0;
  border-radius: 12px;
  cursor: pointer;
  min-height: 48px;
  font: inherit;
  color: inherit;
  text-align: left;
  flex-shrink: 0;
  transition: opacity var(--portal-dur-base) var(--portal-ease-out);
}
.portal-stepper__btn:disabled {
  cursor: default;
}
.portal-stepper__btn:not(:disabled):hover {
  opacity: 0.78;
}
.portal-stepper__btn:focus-visible {
  outline: 2px solid var(--portal-olive);
  outline-offset: 2px;
}
.portal-stepper__btn:active:not(:disabled) {
  transform: scale(0.97);
}

.portal-stepper__num {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 50%;
  background: #fff;
  border: 1.5px solid rgba(0, 0, 0, 0.2);
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-label);
  color: rgba(0, 0, 0, 0.55);
  transition:
    background var(--portal-dur-base) var(--portal-ease-out),
    border-color var(--portal-dur-base) var(--portal-ease-out),
    color var(--portal-dur-base) var(--portal-ease-out);
}

.portal-stepper__num-text,
.portal-stepper__check {
  grid-area: 1 / 1;
  transition:
    opacity var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out);
}

.portal-stepper__check {
  opacity: 0;
  transform: scale(0.6);
}

.portal-stepper__label {
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-label);
  line-height: 1.2;
  color: rgba(0, 0, 0, 0.55);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color var(--portal-dur-base) var(--portal-ease-out);
}

/* States */
.portal-stepper__item--active .portal-stepper__num {
  background: var(--portal-olive);
  border-color: var(--portal-olive);
  color: #fff;
}
.portal-stepper__item--active .portal-stepper__label {
  color: var(--portal-ink);
  font-weight: 600;
}

.portal-stepper__item--done .portal-stepper__num {
  background: var(--portal-olive-tint);
  border-color: var(--portal-olive);
  color: var(--portal-olive);
}
.portal-stepper__item--done .portal-stepper__num-text {
  opacity: 0;
  transform: scale(0.6);
}
.portal-stepper__item--done .portal-stepper__check {
  opacity: 1;
  transform: scale(1);
}
.portal-stepper__item--done .portal-stepper__label {
  color: var(--portal-ink-secondary);
}

/* Variant: --vertical — stacks num above label in equal-width columns,
   with connectors running horizontally between the circles. Used on
   signup.html where the stepper is at the top of the page and needs
   to be more visible (labels under each step) rather than inline. */
.portal-stepper--vertical {
  display: grid;
  grid-template-columns: repeat(var(--portal-stepper-cols, 4), 1fr);
  gap: var(--portal-space-2);
  align-items: start;
}
.portal-stepper--vertical .portal-stepper__item {
  flex-direction: column;
  align-items: center;
  gap: var(--portal-space-2);
  position: relative;
}
.portal-stepper--vertical .portal-stepper__item::after {
  /* Connector runs horizontally between this num and the next one */
  content: "";
  position: absolute;
  top: 16px;            /* aligns with num center (32px / 2) */
  left: calc(50% + 18px);
  right: calc(-50% + 18px);
  height: 2px;
  background: rgba(0, 0, 0, 0.12);
  z-index: 0;
  flex: none;
  border-radius: 2px;
  transition: background var(--portal-dur-base) var(--portal-ease-out);
}
.portal-stepper--vertical .portal-stepper__item--done::after {
  background: var(--portal-olive);
}
.portal-stepper--vertical .portal-stepper__btn {
  flex-direction: column;
  align-items: center;
  gap: var(--portal-space-2);
  padding: 0;
  position: relative;
  z-index: 1;
}
.portal-stepper--vertical .portal-stepper__num {
  width: 28px;
  height: 28px;
}
.portal-stepper--vertical .portal-stepper__label {
  font-size: var(--portal-text-caption);
  letter-spacing: 0.04em;
}


/* ============================================================
   3.11 BENEFIT LOCK CARD — gate state, JS-injected

   Injected by `portal-benefit-gate.js` when a benefit is gated
   (`[data-benefit-gate-requires="military|credit"]`) and the matching
   gate isn't open. Replaces the form/wizard area with this card +
   a 3-step tracker (Military / Credit / Tax).

   Class name `.benefit-lock-card` is preserved from pages.css for
   JS-injection compatibility — these rules win on source order
   (portal.css loads after pages.css) at equal specificity.
   ============================================================ */

/* Stacked composition (Principle 13) — icon ABOVE body, full-width content,
   no orphan whitespace. Stronger elevation than a typical card (Principle 11)
   because this is a STATE panel interrupting the user's intended flow. */
.benefit-lock-card {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--portal-space-5);
  padding: var(--portal-space-10);
  background: var(--portal-surface);
  background-image: linear-gradient(180deg, var(--portal-surface) 0%, var(--portal-surface-warm) 100%);
  border: 1px solid var(--portal-hairline);
  border-top: 3px solid var(--portal-olive);  /* state accent — gives the card identity */
  border-radius: var(--portal-radius-card-lg);
  box-shadow:
    0 2px 0 rgba(14, 15, 12, 0.03),
    0 32px 64px -28px rgba(74, 91, 45, 0.32);
  max-width: 560px;
  width: 100%;
}

.benefit-lock-card__icon {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--portal-olive);
  color: #fff;
  box-shadow: 0 10px 24px -10px rgba(74, 91, 45, 0.5);
  flex-shrink: 0;
  align-self: flex-start;  /* hugs the left edge, body flows full-width below */
}
.benefit-lock-card__icon svg { width: 24px; height: 24px; }

.benefit-lock-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-3);
  min-width: 0;
}

.benefit-lock-card__eyebrow {
  margin: 0;
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-olive);
}

.benefit-lock-card__title {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-section-title);
  line-height: 1.25;
  letter-spacing: -0.2px;
  color: var(--portal-ink);
  margin: 0;
}

.benefit-lock-card__copy {
  font-family: var(--portal-font-body);
  font-weight: 400;
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-secondary);
  margin: 0;
}

.benefit-lock-card__steps {
  list-style: none;
  margin: var(--portal-space-2) 0 var(--portal-space-1);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-3);
}

.benefit-lock-card__step {
  display: flex;
  align-items: center;
  gap: var(--portal-space-3);
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-card-body);
  color: var(--portal-ink-muted);
}

.benefit-lock-card__step-dot {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 1.5px solid var(--portal-hairline-strong);
  background: var(--portal-surface);
  font-weight: 600;
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
  font-variant-numeric: tabular-nums;
}

.benefit-lock-card__step.is-done { color: var(--portal-ink-secondary); }
.benefit-lock-card__step.is-done .benefit-lock-card__step-dot {
  background: var(--portal-olive);
  border-color: var(--portal-olive);
  color: #fff;
}
.benefit-lock-card__step.is-active { color: var(--portal-ink); font-weight: 600; }
.benefit-lock-card__step.is-active .benefit-lock-card__step-dot {
  border-color: var(--portal-olive);
  color: var(--portal-olive);
  background: var(--portal-surface);
  box-shadow: 0 0 0 3px var(--portal-olive-tint);
}

.benefit-lock-card__cta {
  height: var(--portal-control-h);
  padding: 0 var(--portal-space-7);
  background: var(--portal-olive);
  color: #fff;
  border-radius: var(--portal-radius-button);
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-label);
  letter-spacing: 0;
  text-transform: none;
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  align-self: flex-start;
  margin-top: var(--portal-space-3);
  text-decoration: none;
  box-shadow: 0 0 0 0 transparent;
  /* Calibrated hover (Principle 7) — subtle darken + translateY + soft
     olive-tinted shadow. Olive-deep is too dramatic for hover. */
  transition:
    background var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out),
    box-shadow var(--portal-dur-base) var(--portal-ease-out);
}
.benefit-lock-card__cta:hover {
  background: var(--portal-olive-hover);
  /* Explicit re-set: the global `a:hover { color: var(--vs-ink) }` in
     styles.css has specificity (0,1,1) which would otherwise hijack our
     white text on hover. Always declare color on primary-button hover. */
  color: #fff;
  transform: translateY(-1px);
  box-shadow: 0 10px 24px -10px var(--portal-olive-shadow);
}
.benefit-lock-card__cta:focus-visible {
  outline: 2px solid var(--portal-olive);
  outline-offset: 3px;
}
.benefit-lock-card__cta-arrow {
  display: inline-flex;
  transition: transform var(--portal-dur-base) var(--portal-ease-out);
}
.benefit-lock-card__cta-arrow svg { width: 16px; height: 16px; }
.benefit-lock-card__cta:hover .benefit-lock-card__cta-arrow {
  transform: translateX(3px);
}

@media (max-width: 560px) {
  .benefit-lock-card {
    padding: var(--portal-space-8);
    gap: var(--portal-space-4);
  }
}


/* ============================================================
   3.12 ONBOARDING SYSTEM — bar + sheet (wizard) Direction B refresh

   Overrides the legacy `.portal-onboarding-bar` and `.portal-onb-*`
   selectors from pages.css (~220 rules). portal.css loads after
   pages.css so equal-specificity rules win on source order. Where
   pages.css uses `!important` or higher specificity, we match it.

   Goals (Principles 0/2/6/7/10/11/13):
   - Body floor 18, UI floor 14, eyebrow floor 13
   - Mixed-case buttons (no more uppercase BACK/CONTINUE)
   - Real elevation on the sheet (state panel — interrupts the page)
   - Calibrated hover on primary buttons (no muddy olive-deep)
   - Refined option tiles with affordance
   - Cleaner sub-step progress indicator
   ============================================================ */

/* ----- Onboarding bar (sticky chrome under nav).
   TRUE GLASSMORPHISM — frosted dark olive state-panel.
   Translucent bg (`rgba`) WITH a strong `backdrop-filter: blur(24px)
   + saturate(180%)` abstracts whatever scrolls beneath into texture
   rather than letting it bleed through as readable chrome (which is
   what naive rgba bgs do). The blur radius must be high enough to
   defocus content; the saturate boost richens the underlying colour
   so the bar feels like physical glass over a vibrant page, not gauze.
   Inner highlight (inset 1px top) reads as glass-edge refraction.
   Gold 3px top accent + state-panel shadow preserved for identity
   (Principle 11). Fallback to solid olive via @supports for the rare
   browser without backdrop-filter. */
.portal-onboarding-bar {
  background: rgba(28, 38, 14, 0.88);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  backdrop-filter: blur(24px) saturate(180%);
  border-top: 3px solid var(--portal-gold);
  border-bottom: 1px solid rgba(0, 0, 0, 0.28);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    0 10px 32px -12px rgba(10, 16, 4, 0.55);
  /* overflow: visible (override pages.css clipping) so the per-badge
     hover-labels can hang below the bar's bottom edge. The bar is
     full-width with no border-radius so there's nothing to clip. */
  overflow: visible !important;
  transition:
    background var(--portal-dur-base) var(--portal-ease-out),
    backdrop-filter var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onboarding-bar:has(.portal-onboarding-bar__inner:hover) {
  background: rgba(20, 30, 10, 0.92);
  -webkit-backdrop-filter: blur(28px) saturate(190%);
  backdrop-filter: blur(28px) saturate(190%);
}
/* Complete state — Sprint 9d redesign. Richer gold accent (thicker top
   border + subtle glow), no hover state change (the bar is informational
   now, not actionable as a whole). The medal icon + bigger title carry
   the celebration; bg stays calm so the page below remains the focus. */
.portal-onboarding-bar--complete {
  background: rgba(16, 24, 8, 0.92);
  border-top: 4px solid var(--portal-gold);
  border-bottom-color: rgba(0, 0, 0, 0.28);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    inset 0 -1px 0 rgba(201, 162, 74, 0.18),
    0 10px 32px -12px rgba(10, 16, 4, 0.6);
}
.portal-onboarding-bar--complete:has(.portal-onboarding-bar__inner:hover) {
  background: rgba(16, 24, 8, 0.92);
}
/* Fallback — solid opaque deep-olive for browsers without
   backdrop-filter (do NOT let the bar become naively transparent). */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .portal-onboarding-bar {
    background: var(--portal-olive);
    background-image: linear-gradient(180deg, #4d5f30 0%, #3d4d24 100%);
  }
  .portal-onboarding-bar:has(.portal-onboarding-bar__inner:hover) {
    background-image: linear-gradient(180deg, #485a2b 0%, #364520 100%);
  }
  .portal-onboarding-bar--complete {
    background: var(--portal-olive-deep);
    background-image: linear-gradient(180deg, #3a4a1f 0%, #2c3a17 100%);
  }
}

/* Inner — inherits the OUTER bg shift on hover (no separate inner bg).
   Grid collapsed from 3 cols (left | badges | meta) → 2 cols (left | action)
   so the counter-above-badges + adjacent CTA can group as one cluster. */
.portal-onboarding-bar__inner {
  padding: var(--portal-space-7) var(--portal-space-8) !important;
  gap: var(--portal-space-8) !important;
  background: transparent !important;
  grid-template-columns: minmax(0, 1fr) auto !important;
}

/* Right-side action cluster — 2×2 grid so the CTA can align to the
   BADGES row only, not the counter+badges average. The user-visible
   result: icon row and CTA share a vertical centerline; counter sits
   above the icons as a small data-label.
   Grid map:
     [counter centered]    [empty]
     [badges row]          [CTA centered to this row]
   Column gap is restrained (20px) so the icons read as "belonging to"
   the button, not as a separate region. */
.portal-onboarding-bar__action {
  display: grid;
  grid-template-columns: auto auto;
  column-gap: var(--portal-space-5);
  row-gap: var(--portal-space-2);
  align-items: center;
}
.portal-onboarding-bar__action > .portal-onboarding-bar__counter {
  grid-column: 1;
  grid-row: 1;
  justify-self: center;
}
.portal-onboarding-bar__action > .portal-onboarding-bar__badges {
  grid-column: 1;
  grid-row: 2;
}
.portal-onboarding-bar__action > .portal-onboarding-bar__cta {
  grid-column: 2;
  grid-row: 2;
  align-self: center;
}
.portal-onboarding-bar__inner:hover {
  background: transparent !important;
}
.portal-onboarding-bar__inner--complete,
.portal-onboarding-bar__inner--complete:hover {
  background: transparent !important;
}

/* Complete-state inner layout (Sprint 9d):
   [medal] [title + body] [CTA] [×] — single-row, items vertically centered.
   The medal and dismiss are auto-sized; copy stretches; CTA is intrinsic. */
.portal-onboarding-bar__inner--complete {
  display: grid !important;
  grid-template-columns: auto minmax(0, 1fr) auto auto !important;
  align-items: center !important;
  gap: var(--portal-space-6) !important;
  padding: var(--portal-space-6) var(--portal-space-8) !important;
}

/* Medal — circular gold disc with check, suggests "achievement unlocked".
   Sized to feel substantial without overwhelming the copy. */
.portal-onboarding-bar__medal {
  display: inline-grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--portal-gold);
  color: var(--portal-olive-deep);
  flex-shrink: 0;
  box-shadow:
    0 0 0 4px rgba(201, 162, 74, 0.18),
    0 8px 20px -8px rgba(201, 162, 74, 0.55);
}
.portal-onboarding-bar__medal svg {
  display: block;
  color: var(--portal-olive-deep);
}
/* Celebration badge insignia — line-style emblems share the achievement
   icon language; currentColor resolves to the brand olive on the cream field. */
.portal-onb-badge-art { color: var(--portal-olive); }

/* Copy block — title is celebratory (bigger, sentence case, not the
   tracked eyebrow it was before), body is calm supporting text. */
.portal-onboarding-bar__copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.portal-onboarding-bar--complete .portal-onboarding-bar__title {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-card-title);
  letter-spacing: -0.2px;
  text-transform: none;
  color: #ffffff;
  line-height: 1.2;
}
.portal-onboarding-bar--complete .portal-onboarding-bar__body {
  font-size: var(--portal-text-label);
  color: rgba(245, 243, 238, 0.78);
  line-height: 1.4;
  margin: 0;
}
/* Gold emphasis inside the complete-bar body — echoes the gold CTA so
   "free credit monitoring" reads as the reward, not another chore. */
.portal-onboarding-bar__gold {
  color: var(--portal-gold);
  font-weight: 600;
}

/* Complete-state CTA — gold pill, dark text, the celebratory accent
   that pulls the eye to action. Different from the in-progress CTA
   (which is calmer because it's a means to an end, not a reward). */
.portal-onboarding-bar--complete .portal-onboarding-bar__cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--portal-gold);
  color: var(--portal-olive-deep);
  padding: 10px 18px;
  border-radius: 999px;
  font-family: var(--portal-font-body);
  font-weight: 700;
  font-size: var(--portal-text-label);
  text-decoration: none;
  white-space: nowrap;
  transition:
    background var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out),
    box-shadow var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onboarding-bar--complete .portal-onboarding-bar__cta:hover {
  background: #d8b466;
  transform: translateY(-1px);
  box-shadow: 0 10px 24px -10px rgba(201, 162, 74, 0.6);
}
.portal-onboarding-bar--complete .portal-onboarding-bar__cta svg {
  width: 16px;
  height: 16px;
  transition: transform var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onboarding-bar--complete .portal-onboarding-bar__cta:hover svg {
  transform: translateX(2px);
}

/* Dismiss — discoverable but subtle. Square button, ghost on hover. */
.portal-onboarding-bar__dismiss {
  appearance: none;
  background: transparent;
  border: 1px solid rgba(245, 243, 238, 0.22);
  color: rgba(245, 243, 238, 0.7);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: inline-grid;
  place-items: center;
  cursor: pointer;
  transition:
    background var(--portal-dur-base) var(--portal-ease-out),
    color var(--portal-dur-base) var(--portal-ease-out),
    border-color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onboarding-bar__dismiss:hover {
  background: rgba(245, 243, 238, 0.1);
  color: #ffffff;
  border-color: rgba(245, 243, 238, 0.4);
}
.portal-onboarding-bar__dismiss:focus-visible {
  outline: 2px solid var(--portal-gold);
  outline-offset: 2px;
}
.portal-onboarding-bar__dismiss svg { display: block; }

/* Mobile — stack copy under medal, full-width CTA, dismiss top-right. */
@media (max-width: 720px) {
  .portal-onboarding-bar__inner--complete {
    grid-template-columns: auto 1fr auto !important;
    grid-template-rows: auto auto !important;
    row-gap: var(--portal-space-3) !important;
    padding: var(--portal-space-5) var(--portal-space-6) !important;
  }
  .portal-onboarding-bar__medal { grid-row: 1; grid-column: 1; width: 44px; height: 44px; }
  .portal-onboarding-bar__copy { grid-row: 1; grid-column: 2; }
  .portal-onboarding-bar__dismiss { grid-row: 1; grid-column: 3; align-self: start; }
  .portal-onboarding-bar--complete .portal-onboarding-bar__cta {
    grid-row: 2;
    grid-column: 1 / -1;
    justify-content: center;
  }
}

.portal-onboarding-bar__left {
  gap: var(--portal-space-2) !important;
}
.portal-onboarding-bar__title {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-gold);
  margin: 0;
}
.portal-onboarding-bar__body {
  font-size: var(--portal-text-card-body);
  font-weight: 500;
  color: #f5f3ee;
  line-height: 1.45;
  margin: 0;
}

/* Progress meter — substantial enough for senior eyes (10px height
   bar). Gold fill on a darker olive track inside the state panel so
   contrast against the cream content remains high. */
.portal-onboarding-bar__meter {
  gap: var(--portal-space-3) !important;
  margin-top: var(--portal-space-3);
}
.portal-onboarding-bar__meter-track {
  height: 10px !important;
  background: rgba(0, 0, 0, 0.28) !important;
  border: 1px solid rgba(0, 0, 0, 0.32) !important;
  border-radius: 6px !important;
  max-width: 360px !important;
  overflow: hidden;
}
.portal-onboarding-bar__meter-fill {
  background: var(--portal-gold) !important;
  border-radius: 5px !important;
  height: 100%;
}
.portal-onboarding-bar__meter-pct {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-label);
  letter-spacing: 0;
  color: rgba(245, 243, 238, 0.82);
  text-transform: none;
}
/* Counter sits ABOVE the badges as a small data-label — slightly
   smaller than a peer-line counter, slightly tracked so it reads as a
   stat indicator, not as body copy. Floor 13px (eyebrow floor — UI
   floor 14 doesn't apply since this is a contextual data-label, not
   an action). */
.portal-onboarding-bar__counter {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-caption);
  letter-spacing: 0.04em;
  color: rgba(245, 243, 238, 0.78);
  text-transform: none;
}

/* Mini badges — bigger (52px) with bigger icons inside (28px) for
   visual presence and senior readability. Tight grouping (gap 8). */
.portal-onboarding-bar__badges {
  gap: var(--portal-space-3) !important;
}
.portal-onb-mini-badge {
  width: 52px !important;
  height: 52px !important;
}
.portal-onb-mini-badge__inner {
  border-width: 1.5px;
}
.portal-onb-mini-badge__inner svg {
  width: 26px !important;
  height: 26px !important;
}
.portal-onb-mini-badge--empty .portal-onb-mini-badge__inner {
  background: rgba(245, 243, 238, 0.08);
  border-color: rgba(245, 243, 238, 0.32);
  color: rgba(245, 243, 238, 0.7);
}
.portal-onb-mini-badge--done .portal-onb-mini-badge__inner {
  background: var(--portal-gold);
  border-color: var(--portal-gold);
  color: var(--portal-olive-deep);
  box-shadow: 0 4px 12px -4px rgba(201, 162, 74, 0.5);
}
.portal-onb-mini-badge__tick {
  width: 20px !important;
  height: 20px !important;
  right: -2px !important;
  bottom: -2px !important;
}
.portal-onb-mini-badge__tick svg {
  width: 12px !important;
  height: 12px !important;
}

/* Hover label — name of the step appears below each badge. Positioned
   absolute so it doesn't shift layout; hidden by default; fades in +
   slides up subtly when its parent badge is hovered. The label can
   extend past the bar's bottom edge because we removed `overflow:
   hidden` from the bar (see rule below). Pointer-events disabled so
   the label never blocks click on the badge. */
.portal-onb-mini-badge__label {
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(-3px);
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-caption);
  letter-spacing: 0.04em;
  line-height: 1;
  color: var(--portal-gold);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition:
    opacity var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onb-mini-badge:hover .portal-onb-mini-badge__label,
.portal-onb-mini-badge:focus-visible .portal-onb-mini-badge__label {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .portal-onb-mini-badge__label {
    transition: opacity var(--portal-dur-base) linear;
    transform: translateX(-50%) translateY(0);
  }
}

/* Bar CTA — INVERSE treatment for dark state panel. Cream/bone bg with
   olive ink reads as the action against the olive bar (Principle 7 —
   calibrated CTA, not muddy darken). Hover lifts slightly + adds gold
   hairline ring to reinforce the cue. */
.portal-onboarding-bar__cta,
.portal-onboarding-bar__cta:link,
.portal-onboarding-bar__cta:visited {
  background: #f5f3ee;
  color: var(--portal-olive-deep);
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-label);
  letter-spacing: 0;
  text-transform: none;
  border-radius: var(--portal-radius-button);
  padding: 0 var(--portal-space-6);
  height: 48px;
  min-height: 48px;
  border: 0;
  box-shadow: 0 2px 8px -4px rgba(0, 0, 0, 0.32);
  transition:
    background var(--portal-dur-base) var(--portal-ease-out),
    color var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out),
    box-shadow var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onboarding-bar__inner:hover .portal-onboarding-bar__cta,
.portal-onboarding-bar__cta:hover {
  background: #fff;
  color: var(--portal-olive-deep);
  transform: translateY(-1px);
  box-shadow: 0 12px 28px -10px rgba(0, 0, 0, 0.5);
}

/* Dismiss X (complete state) — refined hairline circle on dark bg */
.portal-onboarding-bar__dismiss {
  width: 36px;
  height: 36px;
  border: 1.5px solid rgba(245, 243, 238, 0.32);
  background: rgba(245, 243, 238, 0.08);
  color: rgba(245, 243, 238, 0.82);
}
.portal-onboarding-bar__dismiss:hover {
  background: var(--portal-gold);
  border-color: var(--portal-gold);
  color: var(--portal-olive-deep);
  transform: none;
}


/* ----- Wizard sheet panel ----- */
.portal-onb-sheet__panel {
  background: var(--portal-surface);
  border-radius: 0 0 var(--portal-radius-card-lg) var(--portal-radius-card-lg);
  /* State panel weight (Principle 11) — real elevation + olive accent */
  border-top: 3px solid var(--portal-olive);
  box-shadow:
    0 2px 0 rgba(14, 15, 12, 0.03),
    0 32px 80px -24px rgba(14, 15, 12, 0.32);
}

/* Sheet head — eyebrow ONLY as anchor. Title in head is DOWNGRADED to
   a single caption line that reads as breadcrumb-context, not a title
   (Principle 6 — one h2 per view; the body's sub-step title is THE
   focal point, the head is just context). */
.portal-onb-sheet__head {
  padding: var(--portal-space-6) var(--portal-space-12);
  border-bottom: 1px solid var(--portal-hairline);
  gap: var(--portal-space-6);
  align-items: center;
}
.portal-onb-sheet__head--with-tabs {
  padding: var(--portal-space-6) var(--portal-space-10);
  border-bottom: 1px solid var(--portal-hairline);
}
.portal-onb-sheet__eyebrow {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-olive);
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  margin: 0;
}
/* Downgrade the head title to caption — it's CONTEXT, not THE title.
   The body's sub-step title is the focal point. */
.portal-onb-sheet__title {
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-label);
  letter-spacing: 0;
  line-height: 1.4;
  color: var(--portal-ink-muted);
  margin: var(--portal-space-1) 0 0;
}
.portal-onb-sheet__counter {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-label);
  letter-spacing: 0;
  text-transform: none;
  color: var(--portal-ink-secondary);
}

/* Close X — refined, larger target. top aligns its CENTER with the
   head row's text line (eyebrow / counter / dots all center at ~39px
   from the panel top — review 2026-08-02: the ✕ sat 24px low). */
.portal-onb-sheet__close,
.portal-onb-sheet__close--floating {
  width: 40px;
  height: 40px;
}
.portal-onb-sheet__close--floating {
  top: 19px;
}
.portal-onb-sheet__close,
.portal-onb-sheet__close--floating {
  border: 1.5px solid var(--portal-hairline-strong);
  background: var(--portal-surface);
  color: var(--portal-ink-secondary);
  box-shadow: none;
  transition:
    background var(--portal-dur-base) var(--portal-ease-out),
    border-color var(--portal-dur-base) var(--portal-ease-out),
    color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onb-sheet__close:hover,
.portal-onb-sheet__close--floating:hover {
  background: var(--portal-ink);
  border-color: var(--portal-ink);
  color: #fff;
  transform: none;
}


/* ----- Sub-step content typography -----
   The question owns each screen. Big display type; the lede beneath
   was retired (2026-07) — one question per screen is enough.
   Field labels above inputs also live nearby (see .portal-onb-field__label
   further down) and use the body font at body size so they read cleanly. */
.portal-onb-sub__title {
  font-family: var(--portal-font-display, var(--portal-font-body));
  font-weight: 500;
  font-size: var(--portal-text-display);
  line-height: 1.08;
  letter-spacing: -1px;
  color: var(--portal-ink);
  margin: 0;
}
/* Acknowledgment line — a warm one-liner above the next military question
   that reflects the previous answer ("Thank you for your service." after
   the status pick). Filled by paintAck() in portal-onboarding.js. */
.portal-onb-sub__ack {
  margin: 0 0 var(--portal-space-3);
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-body);
  font-weight: 600;
  color: var(--portal-olive-deep);
}
/* Compact density for the document-upload sub-step (round-3 note): the
   richest wizard screen (accordion + dropzone + QR card + reassure row)
   must fit the sheet WITHOUT internal scrolling, so the footer's
   Submit button is always on screen. Tighter stack gap, hero-size title
   (not display), smaller QR code. */
.portal-onb-sub[data-sub-key="upload"][data-current] {
  gap: var(--portal-space-6);
  padding-top: var(--portal-space-4);
  padding-bottom: var(--portal-space-2);
}
.portal-onb-sub[data-sub-key="upload"] .portal-onb-sub__title {
  font-size: var(--portal-text-hero);
  letter-spacing: -0.5px;
}
.portal-onb-sub[data-sub-key="upload"] .portal-onb-qr-card__code svg {
  width: 104px;
  height: 104px;
}
.portal-onb-sub[data-sub-key="upload"] .portal-onb-qr-card {
  min-height: 0;
  padding: var(--portal-space-4) var(--portal-space-5);
}

/* Inline link-style button (e.g. "Resend code" in the email OTP hint). */
.portal-onb-linkbtn {
  background: none;
  border: 0;
  padding: 0;
  font-family: var(--portal-font-body);
  font-size: inherit;
  font-weight: 600;
  color: var(--portal-olive-deep);
  text-decoration: underline;
  cursor: pointer;
}
.portal-onb-linkbtn:hover { color: var(--portal-ink); }
.portal-onb-linkbtn:disabled { color: var(--portal-ink-muted); text-decoration: none; cursor: default; }
/* Email OTP cells reuse the SSN cell visuals; the group just breathes a
   little more without the SSN group's separators/toggle. The whole field
   (label, cells, hint) centers to match the centered question above. */
.portal-onb-email-cells {
  gap: var(--portal-space-3);
  justify-content: center;
}
.portal-onb-tab-body--email .portal-onb-field {
  align-items: center;
  text-align: center;
}
.portal-onb-tab-body--email .portal-onb-field__label,
.portal-onb-tab-body--email .portal-onb-field__hint {
  text-align: center;
  align-self: center;
}
/* SMFA OTP cells — same cell visuals, centered on the DIT card, with
   breathing room before the hint/actions below. */
.portal-onb-smfa-cells {
  gap: var(--portal-space-3);
  justify-content: center;
  margin: var(--portal-space-2) auto;
}
.portal-onb-sub__lead {
  display: none;
}
/* Exceptions: screens that carry a FORM rather than a single question
   keep their lead — it says why we're asking (Slav, 2 Aug). The
   email-verification screen keeps its own: the member must see WHICH
   address the code went to. */
.portal-onb-sub[data-sub-key="confirm"] .portal-onb-sub__lead,
.portal-onb-sub[data-sub-key="details"] .portal-onb-sub__lead {
  display: block;
  /* One line at this width — a two-line lead costs the row the screen
     needs to stay scroll-free. */
  margin: var(--portal-space-2) auto 0;
  max-width: 70ch;
  font-size: var(--portal-text-body);
  line-height: 1.5;
  color: var(--portal-ink-secondary);
}
.portal-onb-tab-body--email .portal-onb-sub__lead {
  display: block;
  margin: var(--portal-space-3) 0 0;
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-body);
  line-height: 1.5;
  color: var(--portal-ink-secondary);
  max-width: 52ch;
  margin-left: auto;
  margin-right: auto;
}


/* ----- Sub-step progress indicator — segmented bar + explicit label
   (Principle 0 — senior-friendly: concrete > abstract). Dots are
   abstract; segmented bar with width-filled chunks is concrete and
   readable at any age. Label reads "Question X of Y" in plain copy. */
.portal-onb-sub-progress {
  gap: var(--portal-space-3);
}
.portal-onb-sub-progress__dots {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.portal-onb-sub-progress__dot {
  display: inline-block;
  width: 28px !important;
  height: 6px !important;
  border-radius: 3px !important;
  background: var(--portal-hairline-strong) !important;
  transition: background var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onb-sub-progress__dot.is-done,
.portal-onb-sub-progress__dot.is-active {
  background: var(--portal-olive) !important;
}
.portal-onb-sub-progress__label {
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-label);
  letter-spacing: 0;
  text-transform: none;
  color: var(--portal-ink-secondary);
  order: -1;  /* label BEFORE the bar visually */
}


/* ----- Option tiles (Pick / Branch / Status) — unified refined affordance.
   Principle 0 — labels at 18px (body floor for interactive elements).
   Principle 5 — every tile identical padding/center (active declaration).
   Principle 14b — selection changes properties, not structure. Inline
   checkmark is HIDDEN; we use border+bg+text-color signal only. */
.portal-onb-pick__option,
.portal-onb-branch-pick__option,
.portal-onb-status-pick__option {
  border: 1.5px solid var(--portal-hairline-strong);
  border-radius: var(--portal-radius-card);
  background: var(--portal-surface);
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-body);
  letter-spacing: 0;
  color: var(--portal-ink);
  min-height: 64px;
  padding: var(--portal-space-5) var(--portal-space-6);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  transition:
    background var(--portal-dur-base) var(--portal-ease-out),
    border-color var(--portal-dur-base) var(--portal-ease-out),
    box-shadow var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onb-pick__option:hover,
.portal-onb-branch-pick__option:hover,
.portal-onb-status-pick__option:hover {
  background: var(--portal-surface-warm);
  border-color: var(--portal-olive);
  color: var(--portal-ink);
  transform: translateY(-1px);
}
.portal-onb-pick__option:has(input:checked),
.portal-onb-branch-pick__option:has(input:checked),
.portal-onb-status-pick__option:has(input:checked) {
  background: var(--portal-olive-tint);
  border-color: var(--portal-olive);
  color: var(--portal-olive-deep);
  box-shadow: 0 0 0 3px var(--portal-olive-tint);
  font-weight: 600;
  transform: none;
}

/* Branch tile — vertical layout (emblem above label) */
.portal-onb-branch-pick__option {
  flex-direction: column;
  gap: var(--portal-space-3);
  min-height: 132px;
  padding: var(--portal-space-5) var(--portal-space-4);
}
.portal-onb-branch-pick__emblem {
  width: 56px;
  height: 56px;
  background: var(--portal-olive-tint);
  color: var(--portal-olive);
  border-radius: 50%;
  display: inline-grid;
  place-items: center;
}
.portal-onb-branch-pick__emblem svg {
  width: 28px;
  height: 28px;
}
.portal-onb-branch-pick__label {
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-body);
  line-height: 1.2;
  letter-spacing: 0;
  text-align: center;
  color: var(--portal-ink);
}
.portal-onb-branch-pick__option:has(input:checked) .portal-onb-branch-pick__label {
  color: var(--portal-olive-deep);
  font-weight: 600;
}
.portal-onb-branch-pick__option:has(input:checked) .portal-onb-branch-pick__emblem {
  background: var(--portal-olive);
  color: #fff;
}

/* Hide the inline checkmark on selected tiles (Principle 14b — selection
   changes properties, not structure). Border + bg + text-color is enough. */
.portal-onb-branch-pick__check,
.portal-onb-status-pick__check,
.portal-onb-pick__option .portal-onb-pick__check {
  display: none !important;
}


/* ----- Upload zone — solid border, refined affordance, left-aligned
   content (icon column + text block stacked left, not centered) ----- */
.portal-onb-upload {
  border: 1.5px solid var(--portal-hairline-strong);
  background: var(--portal-surface);
  background-image: none;
  border-radius: var(--portal-radius-card);
  padding: var(--portal-space-6) var(--portal-space-8);
  min-height: 88px;
  gap: var(--portal-space-5);
  display: flex;
  align-items: center;
  justify-content: flex-start;
  text-align: left;
}
.portal-onb-upload:hover {
  border-color: var(--portal-olive);
  background: var(--portal-surface-warm);
  transform: translateY(-1px);
}
.portal-onb-upload--filled {
  border-color: var(--portal-olive);
  background: var(--portal-olive-tint);
}
/* Camera capture zone — phone only. On a desktop the document handoff
   is the QR card (scan it with the phone in your pocket); there is no
   rear-facing lens to open here, so the zone never renders. Shown at
   ≤768px in portal-mobile.css, where it REPLACES the QR card. Desktop
   output is unchanged: the element is simply not painted. */
.portal-onb-upload--camera { display: none; }
/* Pointer copy is the desktop default; the touch span is the phone's,
   shown at ≤768px. Both live in the markup so neither breakpoint has to
   rewrite the other's approved string. */
.portal-onb-upload__label--touch { display: none; }
.portal-onb-upload__icon {
  width: 48px;
  height: 48px;
  background: var(--portal-olive-tint);
  color: var(--portal-olive);
  border-radius: 50%;
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
}
.portal-onb-upload__icon svg {
  width: 22px;
  height: 22px;
}
.portal-onb-upload__copy {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-1);
  min-width: 0;
  text-align: left;
}
.portal-onb-upload__label {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-body);
  color: var(--portal-ink);
  letter-spacing: 0;
  text-transform: none;
  text-align: left;
}
.portal-onb-upload__hint {
  font-size: var(--portal-text-label);
  color: var(--portal-ink-muted);
  text-align: left;
}


/* ----- Stepper (−/value/+) precision (Principle 5 + 6).
   3 cells: minus button | value display | plus button. Equal heights,
   equal proportional weight. Center is the display, sides are buttons.
   The frame is the stepper container; cells are separated by hairlines. */
.portal-onb-stepper {
  display: inline-flex;
  align-items: stretch;
  border: 1.5px solid var(--portal-hairline-strong);
  border-radius: var(--portal-radius-card);
  background: var(--portal-surface);
  overflow: hidden;
}
.portal-onb-stepper__btn {
  background: var(--portal-surface);
  border: 0;
  color: var(--portal-ink-secondary);
  cursor: pointer;
  display: inline-grid;
  place-items: center;
  transition: background var(--portal-dur-base) var(--portal-ease-out), color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onb-stepper__btn:hover:not(:disabled) {
  background: var(--portal-surface-warm);
  color: var(--portal-ink);
}
.portal-onb-stepper__btn:disabled {
  color: var(--portal-ink-disabled);
  cursor: not-allowed;
}
.portal-onb-stepper__value {
  border-left: 1.5px solid var(--portal-hairline-strong);
  border-right: 1.5px solid var(--portal-hairline-strong);
  background: var(--portal-surface);
  display: inline-flex;
  align-items: center;     /* vertically center the "4 years" group */
  justify-content: center;
  gap: var(--portal-space-2);
  padding: 0 var(--portal-space-8);
}
.portal-onb-stepper__num {
  font-family: var(--portal-font-display);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--portal-ink);
  letter-spacing: -0.6px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
}
.portal-onb-stepper__unit {
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-body);
  color: var(--portal-ink-muted);
  letter-spacing: 0;
  line-height: 1;
}


/* ----- Value-pitch (credit) screen — clean structure + bigger fonts ----- */
.portal-onb-tab-body__estimate {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-olive);
  margin: 0;
}
.portal-onb-tab-body__estimate-icon {
  color: var(--portal-olive);
}
/* Only the `--centered` intro tab uses the centered flex head. The
   credit COLLECT view (no `--centered` modifier) must fall through to
   the canonical `.portal-onb-tab-body__head--with-progress` grid so
   the title sits on the left and the sub-step progress dots on the
   right — same layout as military / tax. */
.portal-onb-tab-body--centered .portal-onb-tab-body__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--portal-space-3);
  text-align: center;
  padding: var(--portal-space-6) 0 var(--portal-space-7);
}
.portal-onb-tab-body--centered .portal-onb-tab-body__pitch:not(.portal-onb-tab-body__pitch *) {
  font-family: var(--portal-font-body);
  font-weight: 400;
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-secondary);
  text-align: center;
  max-width: 60ch;
  margin: 0 auto;
}

/* ============================================================
   3.10.5 CREDIT COLLECT + DIT FLOW — matches identity-verification.html
   The credit COLLECT view mirrors the standalone identity-verification
   page: left-aligned sub-step head, per-digit SSN cells, and the
   post-collect funnel (submitting spinner, DIT decision picker,
   SMFA-SMS / SMFA-Email cards, KBA, success, retry).
   ============================================================ */

/* The address step (review 2026-07-16) — split composition: form on the
   left, the member welcome-kit photo on the right. Everything left-aligns
   inside the form column (the shared sub-step layout centers), the title
   drops from display to hero size, and vertical spacing tightens so the
   footer buttons stay above the fold with no sheet scroll. The lead is
   restored from the global hide — it carries the WHY (the welcome kit). */
.portal-onb-address-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--portal-space-10);
  align-items: stretch;
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
}
.portal-onb-address-split__form {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-4);
  text-align: left;
}
/* Compact vertical rhythm — the whole step (head, form, footer) must
   clear the fold on a 900px-tall viewport with zero sheet scroll. */
.portal-onb-tab-body--address {
  padding-top: 8px;
  padding-bottom: 12px;
  gap: 12px;
}
.portal-onb-tab-body--address .portal-onb-sub[data-current] {
  padding-top: 4px;
  padding-bottom: 8px;
}
.portal-onb-form[data-onb-form="address"] .portal-onb-fields {
  gap: 12px;
}
.portal-onb-form[data-onb-form="address"] .portal-onb-substep-foot {
  margin-top: 8px;
}
.portal-onb-address-split__media {
  margin: 0;
  border-radius: var(--portal-radius-card);
  border: 1px solid var(--portal-hairline);
  overflow: hidden;
  min-height: 100%;
}
.portal-onb-address-split__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 68% center;
}
.portal-onb-form[data-onb-form="address"] .portal-onb-sub__head {
  align-items: flex-start;
  gap: var(--portal-space-2);
}
.portal-onb-form[data-onb-form="address"] .portal-onb-sub__title {
  font-size: var(--portal-text-hero);
  letter-spacing: -0.6px;
  text-align: left;
}
.portal-onb-form[data-onb-form="address"] .portal-onb-sub__lead {
  display: block;
  font-size: var(--portal-text-body);
  color: var(--portal-ink-secondary);
  line-height: 1.55;
  max-width: 48ch;
  margin: 0;
  text-align: left;
}
.portal-onb-form[data-onb-form="address"] .portal-onb-fields {
  width: 100%;
  text-align: left;
}
.portal-onb-form[data-onb-form="address"] .portal-onb-field__label {
  text-align: left;
}
.portal-onb-form[data-onb-form="address"] .portal-onb-reassure {
  justify-content: flex-start;
  margin-top: 0;
}
@media (max-width: 960px) {
  /* Narrow: the photo yields to the form — height stays short so the
     footer keeps its no-scroll guarantee. */
  .portal-onb-address-split { grid-template-columns: 1fr; }
  .portal-onb-address-split__media { display: none; }
}

/* SSN per-digit cells — 9 numeric inputs + 2 dash separators + eye toggle. */
.portal-onb-ssn-cells {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.portal-onb-ssn-cell {
  appearance: none;
  width: 44px;
  height: 56px;
  padding: 0;
  background: var(--portal-surface);
  border: 1.8px solid #b9b3a3;
  border-radius: 10px;
  font-family: var(--portal-font-display, var(--portal-font-body));
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: var(--portal-text-section);
  text-align: center;
  color: var(--portal-ink);
  transition: border-color 160ms ease, box-shadow 160ms ease, background 160ms ease;
}
.portal-onb-ssn-cell:focus {
  outline: none;
  border-color: var(--portal-olive);
  box-shadow: 0 0 0 3px var(--portal-olive-tint);
}
.portal-onb-ssn-cell[data-filled="true"] {
  border-color: var(--portal-olive);
  background: var(--portal-olive-tint);
}
.portal-onb-ssn-sep {
  font-size: var(--portal-text-section);
  color: var(--portal-ink-muted);
  user-select: none;
  line-height: 1;
  padding: 0 2px;
}
.portal-onb-ssn-toggle {
  margin-left: 8px;
  width: 40px;
  height: 40px;
  background: transparent;
  border: 0;
  border-radius: 999px;
  display: grid;
  place-items: center;
  cursor: pointer;
  color: var(--portal-ink-muted);
  transition: background 160ms ease, color 160ms ease;
}
.portal-onb-ssn-toggle:hover {
  background: var(--portal-olive-tint);
  color: var(--portal-ink);
}

/* Verifying-identity spinner (post-collect). */
.portal-onb-cred-spinner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--portal-space-5);
  padding: var(--portal-space-12) 0;
  text-align: center;
}
.portal-onb-cred-spinner__icon {
  width: 48px;
  height: 48px;
  border: 3px solid var(--portal-hairline);
  border-top-color: var(--portal-olive);
  border-radius: 50%;
  animation: portal-onb-cred-spin 800ms linear infinite;
}
@keyframes portal-onb-cred-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .portal-onb-cred-spinner__icon { animation: none; }
}
.portal-onb-cred-spinner__title {
  margin: 0;
  font-size: var(--portal-text-section);
  font-weight: 600;
  color: var(--portal-ink);
}
.portal-onb-cred-spinner__body {
  margin: 0;
  font-size: var(--portal-text-body);
  color: var(--portal-ink-secondary);
  max-width: 60ch;
}

/* DIT decision picker (mock simulator). */
.portal-onb-cred-dit {
  background: var(--portal-surface-warm);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card);
  padding: var(--portal-space-6);
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-4);
}
.portal-onb-cred-dit__title {
  font-size: var(--portal-text-label);
  font-weight: 600;
  margin: 0;
  color: var(--portal-ink);
}
.portal-onb-cred-dit__sub {
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
  margin: 0;
  font-style: italic;
}
.portal-onb-cred-dit__btns {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--portal-space-3);
}
@media (max-width: 520px) {
  .portal-onb-cred-dit__btns { grid-template-columns: 1fr; }
}
.portal-onb-cred-dit__btn {
  appearance: none;
  background: var(--portal-surface);
  border: 1.5px solid var(--portal-hairline-strong);
  border-radius: 12px;
  padding: 14px;
  cursor: pointer;
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-label);
  font-weight: 500;
  color: var(--portal-ink);
  text-align: left;
  transition: border-color 160ms ease, background 160ms ease;
}
.portal-onb-cred-dit__btn:hover {
  border-color: var(--portal-olive);
  background: var(--portal-olive-tint);
}
.portal-onb-cred-dit__btn-label { display: block; font-weight: 600; }
.portal-onb-cred-dit__btn-sub {
  display: block;
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
  margin-top: 2px;
}

/* Post-collect content card (SMFA / success / retry). Centered
   horizontally so the icon, title, body, and CTA sit on the middle
   axis of the sheet — same visual pattern as the wizard's other
   celebration / completion moments. */
.portal-onb-cred-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--portal-space-5);
  padding: var(--portal-space-6) 0 var(--portal-space-4);
  max-width: 640px;
  margin: 0 auto;
}
.portal-onb-cred-card__icon {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--portal-olive-tint);
  color: var(--portal-olive-deep, var(--portal-olive));
  display: grid;
  place-items: center;
  flex-shrink: 0;
}
.portal-onb-cred-card__icon svg { width: 32px; height: 32px; }
.portal-onb-cred-card__icon--danger {
  background: #fdecec;
  color: #a83232;
}
.portal-onb-cred-card__title {
  margin: 0;
  font-family: var(--portal-font-display, var(--portal-font-body));
  font-weight: 500;
  font-size: var(--portal-text-hero);
  letter-spacing: -0.5px;
  line-height: 1.1;
  color: var(--portal-ink);
}
.portal-onb-cred-card__body {
  margin: 0;
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-secondary);
  max-width: 60ch;
}
.portal-onb-cred-card__body strong { color: var(--portal-ink); font-weight: 600; }
.portal-onb-cred-card__hint {
  margin: 0;
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
  font-style: italic;
}

/* KBA questions. */
.portal-onb-cred-kba__q {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-3);
  margin-bottom: var(--portal-space-6);
}
.portal-onb-cred-kba__q-head {
  font-size: var(--portal-text-label);
  font-weight: 600;
  color: var(--portal-ink);
  margin: 0 0 var(--portal-space-2);
}
.portal-onb-cred-kba__a {
  display: flex;
  align-items: center;
  gap: var(--portal-space-3);
  padding: 12px 16px;
  border: 1.5px solid var(--portal-hairline-strong);
  border-radius: 12px;
  cursor: pointer;
  font-size: var(--portal-text-body);
  color: var(--portal-ink);
  transition: border-color 160ms ease, background 160ms ease;
}
.portal-onb-cred-kba__a:has(input:checked) {
  border-color: var(--portal-olive);
  background: var(--portal-olive-tint);
}
.portal-onb-cred-kba__a input { accent-color: var(--portal-olive); }

/* Error banner (KBA fail). */
.portal-onb-cred-error {
  background: #fdecec;
  border: 1px solid #f3c7c7;
  color: #a83232;
  font-size: var(--portal-text-caption);
  line-height: 1.5;
  padding: var(--portal-space-3) var(--portal-space-4);
  border-radius: var(--portal-radius-button);
  margin: 0 0 var(--portal-space-4);
}
.portal-onb-cred-error[hidden] { display: none !important; }

/* Action group (primary CTA + optional secondary link). */
.portal-onb-cred-actions {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-3);
  align-items: stretch;
  margin-top: var(--portal-space-4);
}
.portal-onb-cred-back {
  align-self: center;
  background: transparent;
  border: 0;
  padding: 0;
  font-size: var(--portal-text-label);
  font-weight: 500;
  color: var(--portal-ink-secondary);
  cursor: pointer;
  transition: color 160ms ease;
}
.portal-onb-cred-back:hover { color: var(--portal-ink); }

/* Hide the value-banner — its content is redundant with the pitch
   paragraph ("worth $240/yr, yours free"). Keeping it added visual
   noise without adding information. */
.portal-onb-value-banner {
  display: none !important;
}

/* Benefit grid — info tiles, NOT interactive. No hover state, no
   "card" affordance. They display value points. Bigger fonts for the
   senior audience. */
.portal-onb-benefit-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--portal-space-5);
  width: 100%;
  max-width: 1000px;
  margin: var(--portal-space-4) auto 0;
}
.portal-onb-benefit-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--portal-space-3);
  padding: var(--portal-space-6) var(--portal-space-4);
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card);
  /* No transition / no hover state — these are display tiles, not
     interactive (Principle 11 — hover affordance reserved for
     actionable elements only). */
  transition: none;
}
.portal-onb-benefit-tile:hover {
  background: var(--portal-surface);
  border-color: var(--portal-hairline);
  transform: none;
}
.portal-onb-benefit-tile__icon {
  width: 52px;
  height: 52px;
  background: var(--portal-olive-tint);
  color: var(--portal-olive);
  border-radius: 50%;
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
}
.portal-onb-benefit-tile__icon svg {
  width: 24px;
  height: 24px;
}
.portal-onb-benefit-tile__title {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-body);
  letter-spacing: 0;
  color: var(--portal-ink);
  margin: 0;
  line-height: 1.3;
}
.portal-onb-benefit-tile__body {
  font-family: var(--portal-font-body);
  font-weight: 400;
  font-size: var(--portal-text-card-body);
  color: var(--portal-ink-muted);
  margin: 0;
  line-height: 1.45;
}

/* Footer area on the credit step — center the big CTA */
.portal-onb-tab-body__foot--centered {
  display: flex;
  justify-content: center;
  margin-top: var(--portal-space-8);
}


/* ----- Trust signals row — token consistency across the system.
   Same olive ✓ prefix, same font-size/weight as ca-trust, benefit-card,
   etc. (Principle 6 — same role = same token). */
.portal-onb-reassure {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--portal-space-6);
  justify-content: center;
}
.portal-onb-reassure-item,
.portal-onb-tab-body__pitch-item {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-label);
  color: var(--portal-ink-secondary);
}
.portal-onb-reassure-item svg,
.portal-onb-tab-body__pitch-item svg {
  width: 16px;
  height: 16px;
  color: var(--portal-olive);
  flex-shrink: 0;
}
.portal-onb-reassure__dot { color: var(--portal-olive); }


/* ----- Buttons (Back / Continue / Finish) — mixed case + calibrated
   hover + optical padding compensation for arrow icons (Principle 11
   — affordance via visible balance, not declaration balance). */
.portal-onb-btn {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-label);
  letter-spacing: 0;
  text-transform: none;
  border-radius: var(--portal-radius-button);
  border: 1.5px solid transparent;
  /* Padding declared per-side: when an arrow sits on one edge, pull
     that side in 4px so the visible center isn't shifted by the icon. */
  padding: 0 var(--portal-space-8);
  min-height: var(--portal-control-h);
  height: var(--portal-control-h);
  gap: var(--portal-space-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition:
    background var(--portal-dur-base) var(--portal-ease-out),
    color var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out),
    box-shadow var(--portal-dur-base) var(--portal-ease-out),
    border-color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onb-btn--xl {
  padding: 0 var(--portal-space-8);
  font-size: var(--portal-text-label);
  min-height: var(--portal-control-h);
  height: var(--portal-control-h);
}
/* Optical alignment: when a button has an arrow on the LEFT (Back ←) or
   RIGHT (Continue →), the side with the arrow gets slightly less padding
   so the content visually centers despite the icon's mass. */
.portal-onb-btn:has(svg:first-child) { padding-left: var(--portal-space-6); }
.portal-onb-btn:has(svg:last-child)  { padding-right: var(--portal-space-6); }
.portal-onb-btn--primary {
  background: var(--portal-olive);
  color: #fff;
  border-color: var(--portal-olive);
  box-shadow: 0 0 0 0 transparent;
}
.portal-onb-btn--primary:hover:not(:disabled) {
  background: var(--portal-olive-hover);
  color: #fff;
  border-color: var(--portal-olive-hover);
  transform: translateY(-1px);
  box-shadow: 0 10px 24px -10px var(--portal-olive-shadow);
}
.portal-onb-btn--primary:disabled {
  background: var(--portal-hairline-strong);
  color: var(--portal-ink-muted);
  border-color: var(--portal-hairline-strong);
  cursor: not-allowed;
}
.portal-onb-btn--ghost {
  background: var(--portal-surface);
  color: var(--portal-ink-secondary);
  border-color: var(--portal-hairline-strong);
}
.portal-onb-btn--ghost:hover:not(:disabled) {
  background: var(--portal-surface-warm);
  color: var(--portal-ink);
  border-color: var(--portal-ink);
}
.portal-onb-btn--ghost:disabled {
  color: var(--portal-ink-disabled);
  border-color: var(--portal-hairline);
  cursor: not-allowed;
}

/* Substep footer (review 2026-08-02) — sticky to the sheet's bottom on
   every screen: Back left, primary CTA always right, optional Skip
   beside the CTA. The sheet body is the scroll container, so
   position: sticky pins the bar while content scrolls beneath. */
.portal-onb-substep-foot {
  position: sticky;
  bottom: 0;
  z-index: 4;
  background: var(--portal-surface);
  border-top: 1px solid var(--portal-hairline);
  padding: var(--portal-space-4) 0 var(--portal-space-4);
  margin-top: var(--portal-space-6);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--portal-space-4);
}
.portal-onb-substep-foot__actions {
  display: flex;
  align-items: center;
  gap: var(--portal-space-4);
  margin-left: auto;
}
/* Buttons opt out of layout when [hidden] — inline-flex on
   .portal-onb-btn would otherwise beat the attribute (the Skip
   button leaked onto every screen, review 2026-08-02). */
.portal-onb-btn[hidden] { display: none; }

/* Phone-help line (Pastel #102) — retired from the wizard footer
   (help is the sheet-top "Need help? Call me" button now) but still
   canonical on the signup wizard (#202). */
.portal-onb-help {
  margin: 0;
  max-width: 52ch;
  text-align: center;
  /* Pastel #263 — "enlarge a bit"; was --portal-text-body (18). */
  font-size: var(--portal-text-card-title);
  color: var(--portal-ink);
  line-height: 1.5;
}
.portal-onb-help__tel {
  color: var(--portal-olive-deep);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.portal-onb-help__tel:hover {
  color: var(--portal-ink);
  text-decoration: underline;
}

/* One-click help (Pastel #206/#207/#208 + review 2026-08-02) — the
   "Need help? We'll call you right away!" control sits directly below
   the main title on every TASK screen (never on celebrations). Resend-
   code lifecycle: idle pill → green calling status with countdown →
   "didn't get our call?" fallback with the direct line + retry. */
.portal-onb-callme {
  display: flex;
  align-items: center;
  /* Follows its screen's alignment: activation wizards are left-aligned,
     the onboarding sheet is centered (rule below). */
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: var(--portal-space-4);
  margin: var(--portal-space-6) 0;
}
.portal-onb-sub__head .portal-onb-callme,
.portal-onb-tab-body--centered .portal-onb-callme {
  justify-content: center;
  margin-bottom: 0;
}
.portal-onb-callme__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-3);
  min-height: 48px;
  padding: 0 var(--portal-space-8);
  border: 1.5px solid var(--portal-hairline-strong);
  border-radius: var(--portal-radius-pill);
  background: var(--portal-surface);
  font-family: inherit;
  font-size: var(--portal-text-body);
  font-weight: 600;
  color: var(--portal-olive-deep);
  cursor: pointer;
  white-space: nowrap;
  transition:
    border-color var(--portal-dur-base) var(--portal-ease-out),
    color var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-fast) var(--portal-ease-out);
}
.portal-onb-callme__btn:hover {
  border-color: var(--portal-olive);
  color: var(--portal-ink);
}
.portal-onb-callme__btn:active { transform: scale(0.98); }
.portal-onb-callme__status {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-3);
  min-height: 48px;
  padding: 0 var(--portal-space-8);
  border-radius: var(--portal-radius-pill);
  background: var(--portal-success-bg);
  font-size: var(--portal-text-body);
  font-weight: 600;
  color: var(--portal-success);
  white-space: nowrap;
}
.portal-onb-callme__timer {
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  opacity: 0.75;
}
.portal-onb-callme__fallback {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  font-size: var(--portal-text-body);
  color: var(--portal-ink);
}
.portal-onb-callme__again {
  min-height: 48px;
  padding: 0 var(--portal-space-6);
  border: 1.5px solid var(--portal-hairline-strong);
  border-radius: var(--portal-radius-pill);
  background: var(--portal-surface);
  font-family: inherit;
  font-size: var(--portal-text-label);
  font-weight: 600;
  color: var(--portal-olive-deep);
  cursor: pointer;
  transition:
    border-color var(--portal-dur-base) var(--portal-ease-out),
    color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onb-callme__again:hover {
  border-color: var(--portal-olive);
  color: var(--portal-ink);
}

/* The VA checking screen's title inherits the credit-activating panel's
   Stratum treatment from pages.css — Stratum is logotype-only (brand
   rule). Match the wizard's Graphik title language instead. */
[data-onb-va-checking] .portal-onb-activating__title {
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-hero);
  letter-spacing: -0.5px;
}

/* Name-on-account card (Pastel #224) — prefilled from signup with an
   in-place Edit unlock. Reads as a confirmation, not a form ask. */
.portal-onb-namecard {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--portal-space-6);
  padding: var(--portal-space-2) var(--portal-space-7);
  background: var(--portal-surface-warm);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card);
  /* The wizard theme centers field labels — this card is a record
     row and reads left-aligned like the #224 mock. */
  text-align: left;
}
.portal-onb-namecard__copy {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-1);
  flex: 1;
  min-width: 0;
}
.portal-onb-namecard__label {
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-ink-muted);
}
.portal-onb-namecard__input {
  border: 0;
  padding: 0;
  background: transparent;
  font-family: inherit;
  font-size: var(--portal-text-card-title);
  font-weight: 600;
  color: var(--portal-ink);
  width: 100%;
}
.portal-onb-namecard__input:focus {
  outline: none;
}
.portal-onb-namecard__input:not([readonly]) {
  border-bottom: 2px solid var(--portal-olive);
}
.portal-onb-namecard__edit {
  border: 0;
  padding: var(--portal-space-2) var(--portal-space-4);
  min-height: 48px;
  background: transparent;
  font-family: inherit;
  font-size: var(--portal-text-label);
  font-weight: 600;
  color: var(--portal-olive-deep);
  text-decoration: underline;
  cursor: pointer;
}
.portal-onb-namecard__edit:hover {
  color: var(--portal-ink);
}

/* Confirm-your-details card (Pastel #224 mock, rebuilt 2026-08-02) —
   one compact left-aligned column, capped at 720px, that fits the
   sheet with zero scrolling. */
.portal-onb-confirm {
  width: min(100%, 880px);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  /* Row gap separates the three bands — name card / the DOB+address
     row / the disclosure. At space-2 they read as one crowded block
     (review 2 Aug). */
  gap: var(--portal-space-8) var(--portal-space-10);
  align-items: start;
  text-align: left;
}
.portal-onb-confirm > .portal-onb-namecard,
.portal-onb-confirm > .portal-onb-confirm__how {
  grid-column: 1 / -1;
}
/* Manual entry (rebuilt 2026-08-02 after review — the earlier
   `display: contents` flattening produced hanging labels and fields off
   a common baseline). The address block claims the full row and holds a
   real two-row form grid: street + unit on top, city + state + ZIP
   beneath. Every label owns one line, every field starts on the same
   baseline as its row-mates. */
/* In manual mode the address block's own children join the outer grid so
   the label row can share the DOB's row instead of leaving that column
   empty, and the two field rows span the full width beneath. (Contents
   is applied to the WRAPPER only — the field rows keep their own grids,
   which is what the earlier flattening got wrong.) The disclosure steps
   aside; it's one "Search instead" away on the default view. */
@media (min-width: 961px) {
  .portal-onb-confirm:has([data-onb-addr-manual]:not([hidden])) {
    grid-template-areas:
      "name name"
      "dob  addrhead"
      "manual manual";
    /* Manual mode carries an extra field row, so the bands sit closer —
       it still reads as three bands because the disclosure has yielded. */
    row-gap: var(--portal-space-4);
  }
  .portal-onb-confirm:has([data-onb-addr-manual]:not([hidden])) > .portal-onb-namecard { grid-area: name; }
  .portal-onb-confirm:has([data-onb-addr-manual]:not([hidden])) > .portal-onb-confirm__dob-group { grid-area: dob; }
  .portal-onb-confirm:has([data-onb-addr-manual]:not([hidden])) > .portal-onb-confirm__how { display: none; }
  .portal-onb-confirm:has([data-onb-addr-manual]:not([hidden])) > .portal-onb-confirm__address { display: contents; }
  .portal-onb-confirm:has([data-onb-addr-manual]:not([hidden])) .portal-onb-confirm__address-head {
    grid-area: addrhead;
    align-self: end;
    padding-bottom: var(--portal-space-4);
    gap: var(--portal-space-4);
  }
  /* Neither the label nor the mode toggle wraps onto a second line —
     they're a single row of two items, not a paragraph. */
  .portal-onb-confirm:has([data-onb-addr-manual]:not([hidden])) .portal-onb-confirm__address-head > * {
    white-space: nowrap;
  }
  .portal-onb-confirm:has([data-onb-addr-manual]:not([hidden])) .portal-onb-confirm__manual {
    grid-area: manual;
    margin-top: 0;
  }
  /* Manual mode carries one more field row than search mode, and the
     member has already read the lead on the way here — so the lead
     yields its line rather than the screen gaining a scrollbar. */
  .portal-onb-sub:has([data-onb-addr-manual]:not([hidden])) .portal-onb-sub__lead {
    display: none;
  }
  .portal-onb-sub:has([data-onb-addr-manual]:not([hidden])) .portal-onb-callme {
    margin-top: var(--portal-space-4);
    margin-bottom: var(--portal-space-2);
  }
}
.portal-onb-confirm:has([data-onb-addr-manual]:not([hidden])) .portal-onb-confirm__address {
  grid-column: 1 / -1;
}
.portal-onb-confirm__manual {
  display: grid;
  grid-template-columns:
    minmax(0, 2fr)    /* street */
    minmax(0, 1fr)    /* unit   */
    minmax(0, 1.4fr)  /* city   */
    minmax(0, 1fr)    /* state  */
    minmax(0, 1fr);   /* zip    */
  gap: var(--portal-space-5) var(--portal-space-5);
  align-items: start;
  margin-top: var(--portal-space-2);
}
/* Each label owns exactly one line, so every input in the row starts on
   the same baseline regardless of label length. */
.portal-onb-confirm__manual .portal-onb-field__label {
  display: flex;
  align-items: baseline;
  min-height: 25px;
  white-space: nowrap;
}
/* (The phone layout for this screen lives at the END of the confirm
   rules — see "Confirm screen on phones". It must come after every base
   rule it overrides; sitting here it lost to later same-specificity
   declarations.) */
/* Fit-the-screen rhythm (review 2026-08-02: no sheet scrolling) — the
   confirm screen is a form, not a single question: its title steps
   down from display to hero size, the head tightens, and the military
   tab trims its outer padding (the sticky footer carries its own). */
.portal-onb-sub[data-sub-key="confirm"] .portal-onb-sub__title {
  font-size: var(--portal-text-hero);
}
.portal-onb-sub[data-sub-key="confirm"] .portal-onb-sub__head {
  margin-bottom: var(--portal-space-2);
}
/* The lead already sits under the title, so the help pill doesn't need
   its full standalone gap on this screen. */
.portal-onb-sub[data-sub-key="confirm"] .portal-onb-callme {
  margin-top: var(--portal-space-2);
}
.portal-onb-tab-body--military {
  padding-top: 0;
  padding-bottom: var(--portal-space-1);
}
/* Optional-details screen: two questions + help + footer must clear a
   900px viewport (review 2026-08-02, no-scroll rule). Like the confirm
   screen, this one holds a FORM rather than a single question, so its
   title takes the hero step and the branch tiles run compact. */
.portal-onb-sub[data-sub-key="details"] .portal-onb-sub__title {
  font-size: var(--portal-text-hero);
}
.portal-onb-sub[data-sub-key="details"] .portal-onb-sub__head {
  margin-bottom: var(--portal-space-4);
}
.portal-onb-sub[data-sub-key="details"] .portal-onb-branch-pick {
  gap: var(--portal-space-2);
}
.portal-onb-sub[data-sub-key="details"] .portal-onb-branch-pick__option {
  /* The canonical tile is 132px — sized for the old full-screen branch
     question. Here it's one of two optional fields, so it runs compact. */
  min-height: 104px;
  padding: var(--portal-space-4) var(--portal-space-2);
  gap: var(--portal-space-2);
}
.portal-onb-sub[data-sub-key="details"] .portal-onb-branch-pick__emblem {
  width: 32px;
  height: 32px;
}
.portal-onb-sub[data-sub-key="details"] .portal-onb-branch-pick__emblem svg {
  width: 32px;
  height: 32px;
}
.portal-onb-sub[data-sub-key="details"] .portal-onb-field + .portal-onb-field {
  margin-top: var(--portal-space-4);
}
/* The rank select doesn't need the full column width beside a 7-tile row. */
@media (min-width: 961px) {
  .portal-onb-sub[data-sub-key="details"] .portal-onb-select {
    max-width: 420px;
  }
}
/* The rank list's helper line duplicates the "optional" label above it. */
.portal-onb-sub[data-sub-key="details"] .portal-onb-field > .portal-onb-field__hint:last-child {
  display: none;
}
.portal-onb-tab-body--military .portal-onb-tab-body__head {
  padding-bottom: var(--portal-space-2);
}
.portal-onb-tab-body--military .portal-onb-substep-foot {
  margin-top: var(--portal-space-1);
}
.portal-onb-confirm .portal-onb-field__label {
  text-align: left;
  justify-content: flex-start;
}
.portal-onb-confirm .portal-onb-field__hint {
  text-align: left;
  margin: 0;
}
.portal-onb-confirm .portal-onb-field {
  display: grid;
  gap: var(--portal-space-2);
  margin: 0;
  padding: 0;
  border: 0;
}

/* DOB — ONE labelled question whose answer is three selects (review
   2 Aug). The group label sits on the same line as the address label
   so both columns share a baseline. */
.portal-onb-confirm__dob-group {
  display: grid;
  gap: var(--portal-space-2);
}
.portal-onb-confirm__dob-group > .portal-onb-field__label {
  display: flex;
  align-items: baseline;
  min-height: 25px;
}
.portal-onb-confirm__dob {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: var(--portal-space-4);
}

/* Address block — search-first with a manual fallback. Both columns
   share the same skeleton (label row, then a 56px field row) so labels
   and fields sit on common lines across the DOB/address split
   (review 2026-08-02 — the hint line was breaking the alignment). */
.portal-onb-confirm__address {
  display: grid;
  gap: var(--portal-space-2);
}
.portal-onb-confirm__address-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--portal-space-6);
  min-height: 25px;
}
/* (Per-part DOB labels are gone — the group carries the single
   "Your date of birth" label; the selects' own placeholders say
   Month / Day / Year.) */
.portal-onb-addr-search {
  position: relative;
}
.portal-onb-addr-search__icon {
  position: absolute;
  left: 18px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--portal-ink-muted);
  pointer-events: none;
}
.portal-onb-addr-search__input {
  width: 100%;
  padding-left: 52px;
}
.portal-onb-addr-search--picked .portal-onb-addr-search__icon {
  color: var(--portal-olive);
}
.portal-onb-addr-suggest {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 10;
  margin: 0;
  padding: var(--portal-space-1);
  list-style: none;
  background: var(--portal-surface);
  border: 1.5px solid var(--portal-hairline-strong);
  border-radius: var(--portal-radius-card);
  box-shadow: var(--portal-shadow-card-hover);
}
.portal-onb-addr-suggest button {
  display: block;
  width: 100%;
  min-height: 48px;
  padding: var(--portal-space-4) var(--portal-space-6);
  border: 0;
  background: transparent;
  border-radius: var(--portal-radius-button);
  font-family: inherit;
  font-size: var(--portal-text-label);
  color: var(--portal-ink);
  text-align: left;
  cursor: pointer;
}
.portal-onb-addr-suggest button:hover {
  background: var(--portal-olive-tint);
}
.portal-onb-addr-manual {
  justify-self: start;
  min-height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  font-family: inherit;
  font-size: var(--portal-text-label);
  font-weight: 600;
  color: var(--portal-olive-deep);
  text-decoration: underline;
  cursor: pointer;
}
.portal-onb-addr-manual:hover { color: var(--portal-ink); }
.portal-onb-confirm__address-head .portal-onb-addr-manual {
  min-height: 0;
  padding: 0;
}
/* (The 5-across manual grid that lived here was the source of the
   crammed/overlapping fields — the real two-row form grid is defined
   with the rest of the manual-entry rules above.) */
.portal-onb-confirm__manual[hidden],
.portal-onb-addr-search[hidden] { display: none; }
.portal-onb-confirm__how summary { text-align: left; }

/* ---- Confirm screen on phones (designed 2026-08-02) --------------------
   The desktop two-column split can't survive a 360px viewport, so the
   screen restructures rather than shrinks: every band stacks full-width,
   the DOB keeps one line (short values belong together) with the Year
   given the widest track so "YYYY" never truncates, the address label
   and its mode-toggle each own a line instead of fighting for one, and
   the manual fields become street / unit / city / state+zip. The sheet
   may scroll here — a phone form is a scrolling form; the no-scroll
   rule is a desktop guarantee.
   Placed last on purpose: these override base rules of equal
   specificity, so they have to win on source order. */
@media (max-width: 960px) {
  .portal-onb-confirm {
    grid-template-columns: 1fr;
    gap: var(--portal-space-7);
  }

  .portal-onb-confirm__dob {
    /* Month needs room for its word, Year for its own — all three
       placeholders are words now, so the tracks even out. */
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--portal-space-3);
  }
  .portal-onb-confirm__dob-group > .portal-onb-field__label { min-height: 0; }
  .portal-onb-confirm__dob .portal-onb-select {
    padding-left: var(--portal-space-4);
    padding-right: var(--portal-space-7);
  }

  .portal-onb-confirm__address-head {
    display: grid;
    justify-items: start;
    gap: var(--portal-space-1);
    min-height: 0;
  }
  .portal-onb-confirm__address-head > * { white-space: normal; }
  .portal-onb-confirm__address-head .portal-onb-addr-manual {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    padding: 0;
  }

  /* The same five fields re-templated into a readable stack. */
  .portal-onb-confirm__manual {
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
    grid-template-areas:
      "street street"
      "unit   unit"
      "city   city"
      "state  zip";
    gap: var(--portal-space-5) var(--portal-space-4);
  }
  .portal-onb-confirm__manual .portal-onb-field--street { grid-area: street; }
  .portal-onb-confirm__manual .portal-onb-field--unit   { grid-area: unit; }
  .portal-onb-confirm__manual .portal-onb-field--city   { grid-area: city; }
  .portal-onb-confirm__manual .portal-onb-field--state  { grid-area: state; }
  .portal-onb-confirm__manual .portal-onb-field--zip    { grid-area: zip; }
  .portal-onb-confirm__manual .portal-onb-field__label { white-space: normal; }
}

/* VA lookup screens. The spinner reuses .portal-onb-activating
   (pages.css) whose display:flex beats [hidden] — pin both panels. */
.portal-onb-activating[hidden],
.portal-onb-va-result[hidden] { display: none; }

/* Dedicated success screen — the positive beat after verification. */
.portal-onb-va-result {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--portal-space-4);
  padding: var(--portal-space-16) var(--portal-space-8);
  text-align: center;
}
.portal-onb-va-result__icon {
  width: 64px;
  height: 64px;
  display: grid;
  place-items: center;
  border-radius: var(--portal-radius-pill);
  background: var(--portal-olive);
  color: #fff;
  box-shadow: 0 0 0 10px rgba(74, 91, 45, 0.14);
  margin-bottom: var(--portal-space-2);
}
.portal-onb-va-result__title {
  margin: 0;
  font-size: var(--portal-text-hero);
  font-weight: 500;
  letter-spacing: -0.5px;
  color: var(--portal-ink);
}
.portal-onb-va-result__body {
  margin: 0;
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-secondary);
}

/* Progress dots — a skipped sub's dot must actually disappear
   (review 2026-08-02: the [hidden] attribute lost to the dot's
   display rule and left a dead gray dot). */
.portal-onb-sub-progress__dot[hidden] { display: none; }

/* Demo-only VA outcome toggle, tucked inside the disclosure. */
.portal-onb-demo-toggle {
  display: flex;
  align-items: center;
  gap: var(--portal-space-2);
  margin-top: var(--portal-space-4);
  cursor: pointer;
}

/* Section heads inside the sheet body — Principle 6: one title per
   view. The tab-body heading is CONTEXT (step name); the sub-step
   title is THE focus (the actual question). Downgrade the heading
   to a small caption so there's no competition with the sub-step. */
.portal-onb-section__head,
.portal-onb-tab-body__head {
  padding: var(--portal-space-6) 0 var(--portal-space-4);
}
/* Clear the floating close button (40px at right: 36px, seated on this
   row's visual line — pages.css) so the sub-step progress bar ends a
   clean 16px before it. Lives here, after the shorthand above, which
   would otherwise reset padding-right to 0 (portal.css loads last). */
/* Reserves room for the floating ✕ only — the help control moved
   below the screen title (review 2026-08-02), so the head row is
   [heading] [counter + dots] [✕] on one line. */
.portal-onb-tab-body__head--with-progress {
  padding-right: 56px;
}
.portal-onb-section__title {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-num-data);
  letter-spacing: -0.3px;
  line-height: 1.15;
  color: var(--portal-ink);
  margin: 0;
}
/* Wizard top heading — reduced to an eyebrow tag (2026-07) so the
   sub-step question can own the screen. Reads as a step name rather
   than a page title; the h2 wrapping is preserved for a11y. */
.portal-onb-tab-body__heading {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-caption);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--portal-accent, var(--portal-ink));
  margin: 0;
}
.portal-onb-section__sub {
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-secondary);
  margin: var(--portal-space-2) 0 0;
}


/* ----- Celebration screen (badge stamp + earned) ----- */
.portal-onb-celebration__stamp {
  position: relative;
  display: inline-grid;
  place-items: center;
}
.portal-onb-celebration__eyebrow {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-olive);
}
.portal-onb-celebration__title {
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-hero);
  letter-spacing: -0.4px;
  line-height: 1.1;
  color: var(--portal-ink);
}
.portal-onb-celebration__body {
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-secondary);
}

/* Progress dots — 3 medal dots, filled for earned. Replaces the
   "Badge X of 3" pill. Tight + visual. */
.portal-onb-celebration__progress {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-3);
  padding: var(--portal-space-2) var(--portal-space-4);
  border-radius: var(--portal-radius-pill);
  background: var(--portal-olive-tint);
}
.portal-onb-celebration__progress-dots {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
}
.portal-onb-celebration__progress-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: transparent;
  border: 1.5px solid var(--portal-olive);
  display: inline-block;
}
.portal-onb-celebration__progress-dot--done {
  background: var(--portal-olive);
}
.portal-onb-celebration__progress-meta {
  font-family: var(--portal-font-body);
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--portal-olive-deep);
}

/* ----- Finale credit handoff (2026-07-16) ----- */
/* After the last badge, the celebration stays a celebration; this card
   below it sells the first benefit — photo left, three concrete value
   points, one primary CTA to the credit page, and a quiet "later" out
   so it reads as an offer, never a demand. */
.portal-onb-credit-offer {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: var(--portal-space-8);
  align-items: center;
  width: 100%;
  max-width: 880px;
  margin-top: var(--portal-space-5);
  padding: var(--portal-space-6);
  background: var(--portal-olive-tint);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card-lg);
  text-align: left;
}
.portal-onb-credit-offer__media {
  margin: 0;
  border-radius: var(--portal-radius-card);
  overflow: hidden;
  align-self: stretch;
  min-height: 220px;
}
.portal-onb-credit-offer__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.portal-onb-credit-offer__copy {
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-3);
}
.portal-onb-credit-offer__eyebrow {
  margin: 0;
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-olive);
}
.portal-onb-credit-offer__title {
  margin: 0;
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-section);
  letter-spacing: -0.3px;
  line-height: 1.15;
  color: var(--portal-ink);
}
.portal-onb-credit-offer__logo {
  align-self: flex-start;
  height: 20px;
  width: auto;
  max-width: 128px;
  object-fit: contain;
  /* White-on-transparent asset (built for dark cards) — darken it so the
     Equifax wordmark reads on this light olive-tint card. */
  filter: brightness(0);
  opacity: 0.82;
  margin: var(--portal-space-1) 0;
}
.portal-onb-credit-offer__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-2);
}
.portal-onb-credit-offer__list li {
  display: flex;
  align-items: center;
  gap: var(--portal-space-3);
  font-size: var(--portal-text-body);
  line-height: 1.4;
  color: var(--portal-ink-secondary);
}
.portal-onb-credit-offer__list svg {
  width: 18px;
  height: 18px;
  color: var(--portal-olive);
  flex-shrink: 0;
}
.portal-onb-credit-offer__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--portal-space-6);
  margin-top: var(--portal-space-2);
}
.portal-onb-credit-offer__later {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  min-height: 48px;
  font-family: var(--portal-font-body);
  font-weight: 500;
  font-size: var(--portal-text-label);
  color: var(--portal-ink-secondary);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  transition: color var(--portal-dur-base) var(--portal-ease-out);
}
.portal-onb-credit-offer__later:hover {
  color: var(--portal-ink);
}
@media (max-width: 840px) {
  .portal-onb-credit-offer {
    grid-template-columns: 1fr;
    gap: var(--portal-space-5);
  }
  .portal-onb-credit-offer__media {
    min-height: 0;
    max-height: 200px;
  }
}

/* Confetti burst — 72 pieces launched in a full 360° radial pattern
   from behind the badge, then pulled down by mock-gravity while
   rotating mid-flight. Two-phase keyframe: 0–35 % is the outward
   burst to (--bx, --by); 35–100 % is the gravity fall + sideways
   drift to (--fx, --fy). All motion is transform + opacity only —
   no layout reflow. Pointer-events disabled, fully decorative. */
.portal-onb-celebration__confetti {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: visible;
}

.portal-onb-celebration__confetti-piece {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--size, 10px);
  height: var(--size, 10px);
  transform: translate(-50%, -50%) rotate(0deg);
  opacity: 0;
  animation: portal-onb-confetti-burst var(--dur, 2200ms) cubic-bezier(0.18, 0.72, 0.32, 1) var(--d, 0ms) 1 forwards;
  will-change: transform, opacity;
}

/* Shape variants —
   s0: soft square (default, tiny corner radius)
   s1: circle (adds visual variety, reads as "dot")
   s2: ribbon (thin, tall rectangle — mimics streamer)
   s3: micro-square (smaller offset, denser look) */
.portal-onb-celebration__confetti-piece--s0 { border-radius: 2px; }
.portal-onb-celebration__confetti-piece--s1 { border-radius: 50%; }
.portal-onb-celebration__confetti-piece--s2 {
  width: calc(var(--size, 10px) * 0.45);
  height: calc(var(--size, 10px) * 1.6);
  border-radius: 1.5px;
}
.portal-onb-celebration__confetti-piece--s3 {
  width: calc(var(--size, 10px) * 0.72);
  height: calc(var(--size, 10px) * 0.72);
  border-radius: 1px;
}

/* Brand-palette rotation — five tones so no two adjacent pieces are
   identical. Gold-soft keeps its outline so it reads against the
   cream sheet background without disappearing. */
.portal-onb-celebration__confetti-piece--c0 { background: var(--portal-olive, #5a6b3f); }
.portal-onb-celebration__confetti-piece--c1 { background: var(--portal-gold, #fac957); }
.portal-onb-celebration__confetti-piece--c2 { background: var(--portal-olive-deep, #3f4828); }
.portal-onb-celebration__confetti-piece--c3 {
  background: var(--portal-gold-soft, #fbe6b1);
  box-shadow: inset 0 0 0 1px var(--portal-gold, #fac957);
}
.portal-onb-celebration__confetti-piece--c4 { background: var(--portal-cream, #f4f0e3); box-shadow: inset 0 0 0 1px var(--portal-olive, #5a6b3f); }

@keyframes portal-onb-confetti-burst {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) rotate(0deg) scale(0.4);
  }
  6% {
    opacity: 1;
    transform: translate(-50%, -50%) rotate(calc(var(--rot, 360deg) * 0.06)) scale(1);
  }
  /* Peak of the outward burst — pieces reach (--bx, --by). */
  35% {
    opacity: 1;
    transform:
      translate(calc(-50% + var(--bx, 0px)), calc(-50% + var(--by, 0px)))
      rotate(calc(var(--rot, 360deg) * 0.35))
      scale(1);
  }
  /* Gravity fall to the final resting spot (--fx, --fy). */
  80% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform:
      translate(calc(-50% + var(--fx, 0px)), calc(-50% + var(--fy, 0px)))
      rotate(var(--rot, 720deg))
      scale(0.9);
  }
}

@media (prefers-reduced-motion: reduce) {
  .portal-onb-celebration__confetti-piece {
    animation: none;
    opacity: 0;
  }
}


/* ============================================================
   4. REDUCED MOTION — respect user preference

   All portal transitions use only transform / opacity / colour so they
   degrade cleanly. We still neutralise transition + transform to keep
   the page calm for users who opt out.
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  .portal-nav__link,
  .portal-stat,
  .portal-history,
  .portal-filter,
  .portal-alert,
  .portal-alert__cta,
  .portal-alert__cta svg,
  .portal-quick-tile,
  .portal-benefit-card,
  .portal-benefit-card__cta,
  .portal-benefit-card__cta svg,
  .portal-spotlight__cta,
  .portal-spotlight__cta svg,
  .portal-section__link,
  .portal-section__link svg {
    transition: none;
  }
  .portal-quick-tile:hover,
  .portal-benefit-card:hover {
    transform: none;
  }
  .portal-alert:hover .portal-alert__cta svg,
  .portal-benefit-card:hover .portal-benefit-card__cta svg,
  .portal-spotlight__cta:hover svg,
  .portal-section__link:hover svg {
    transform: none;
  }
}

/* ─── Activated benefit card ────────────────────────────────────────────
   Activated cards carry .portal-benefit-card--activated:
   - 2px olive border + very light olive body tint
   - The "Activated" pill and a plain text-link CTA share a footer row at
     the bottom of the body (.portal-benefit-card__foot).
*/
.portal-benefit-card--activated {
  border: 2px solid var(--portal-olive, #576038);
  background: #f4f6ec;
}
.portal-benefit-card--activated:hover {
  border-color: var(--portal-olive, #576038);
}


.portal-benefit-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: auto;
}

.portal-benefit-card__active-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 24px;
  padding: 0 10px;
  border-radius: 999px;
  background: var(--portal-olive, #576038);
  color: #ffffff;
  font-family: var(--vs-font-body, "Graphik", system-ui, sans-serif);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.portal-benefit-card__active-pill svg {
  flex: 0 0 auto;
}

/* Text-link CTA variant (used on activated cards). Strips the full-width
   button chrome of the canonical .portal-benefit-card__cta and renders as
   a plain underlined link. Card hover still shifts the arrow. */
.portal-benefit-card__cta--link {
  padding: 0;
  min-height: 0;
  align-self: auto;
  margin-top: 0;
  display: inline-flex;
  gap: var(--portal-space-2);
  background: transparent;
  border: 0;
  border-radius: 0;
  font-size: var(--portal-text-label);
  color: var(--portal-olive-deep, #3f4a26);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}
.portal-benefit-card:hover .portal-benefit-card__cta--link {
  background: transparent;
  border: 0;
  color: var(--portal-olive-deep, #3f4a26);
}
.portal-benefit-card:active .portal-benefit-card__cta--link {
  transform: none;
}
.portal-benefit-card__cta--link svg {
  width: 12px;
  height: 12px;
}



/* ============================================================
   3.11 BENEFIT-PAGE VALUE PERK — "As a member you'll get" card
   Used on every pre-activation partner page inside .bnft-glance.
   Previously inlined only on newday-home-new; hoisted here so all
   partner pages share one canonical component.
   ============================================================ */
.bnft-glance__perks {
  padding-top: var(--portal-space-7);
  border-top: 1px solid var(--portal-hairline);
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-5);
}
.bnft-perk {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--portal-space-6);
  padding: var(--portal-space-7);
  background: #f3f5ec;
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card);
}
.bnft-perk__icon {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  flex: none;
  border-radius: 50%;
  background: var(--portal-surface);
  color: var(--portal-olive-deep);
}
.bnft-perk__copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.bnft-perk__title {
  font-size: var(--portal-text-card-title);
  font-weight: 600;
  color: var(--portal-ink);
}
.bnft-perk__sub {
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
}
.bnft-perk__chip {
  flex: none;
  white-space: nowrap;
  font-size: var(--portal-text-caption);
  font-weight: 700;
  color: var(--portal-olive-deep);
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-pill);
  padding: 6px 12px;
}
@media (max-width: 480px) {
  .bnft-perk { grid-template-columns: auto 1fr; }
  .bnft-perk__chip { grid-column: 2; justify-self: start; }
}


/* ============================================================
   3.12 BENEFIT-PAGE VIDEO HERO — explainer walkthrough
   Uses the canonical .portal-video-hero--split component (same
   layout as portal.html home). .bnft-video is only a section-level
   spacing wrapper — no overrides on the inner component.
   ============================================================ */
.bnft-video {
  padding: var(--portal-space-4) 0 var(--portal-space-12);
}
@media (max-width: 900px) {
  .bnft-video { padding: var(--portal-space-3) 0 var(--portal-space-10); }
}

/* ============================================================
   3.13 STATUS TRACK — horizontal step progress (Pastel #166/#167)
   Compact filed-return / application tracker: Submitted →
   Accepted → Refund. Steps are --done, --current, or upcoming
   (default). Used inside the taxhawk-new-active launch card
   (--on-dark) and the dashboard offer-status box.
   ============================================================ */
.portal-track {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
}
.portal-track__step {
  flex: 1;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--portal-space-1);
  min-width: 0;
}
.portal-track__step::before {
  content: "";
  position: absolute;
  top: 11px;
  right: 50%;
  width: 100%;
  height: 2px;
  background: var(--portal-hairline);
}
.portal-track__step:first-child::before { display: none; }
.portal-track__step--done::before,
.portal-track__step--current::before { background: var(--portal-olive); }
.portal-track__dot {
  position: relative;
  z-index: 1;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--portal-surface);
  border: 2px solid var(--portal-hairline);
  color: #ffffff;
  margin-bottom: var(--portal-space-2);
}
.portal-track__step--done .portal-track__dot {
  background: var(--portal-olive);
  border-color: var(--portal-olive);
}
.portal-track__step--current .portal-track__dot { border-color: var(--portal-olive); }
.portal-track__step--current .portal-track__dot::after {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--portal-olive);
}
.portal-track__label {
  font-size: var(--portal-text-caption);
  font-weight: 600;
  color: var(--portal-ink);
}
.portal-track__date {
  font-size: var(--portal-text-caption);
  color: var(--portal-ink-muted);
}

/* On-dark variant — slate panels. Gold replaces olive. */
.portal-track--on-dark .portal-track__step::before { background: rgba(255, 255, 255, 0.24); }
.portal-track--on-dark .portal-track__step--done::before,
.portal-track--on-dark .portal-track__step--current::before { background: var(--portal-gold); }
.portal-track--on-dark .portal-track__dot {
  background: transparent;
  border-color: rgba(255, 255, 255, 0.32);
}
.portal-track--on-dark .portal-track__step--done .portal-track__dot {
  background: var(--portal-gold);
  border-color: var(--portal-gold);
  color: var(--portal-slate);
}
.portal-track--on-dark .portal-track__step--current .portal-track__dot {
  background: transparent;
  border-color: var(--portal-gold);
}
.portal-track--on-dark .portal-track__step--current .portal-track__dot::after { background: var(--portal-gold); }
.portal-track--on-dark .portal-track__label { color: var(--portal-slate-bright); }
.portal-track--on-dark .portal-track__date { color: rgba(255, 255, 255, 0.72); }

/* Large variant — for prominent cards where 65+ readability matters
   (taxhawk-new-active status card). Bumps dot + labels one step up. */
.portal-track--lg .portal-track__dot { width: 28px; height: 28px; }
.portal-track--lg .portal-track__step::before { top: 13px; }
.portal-track--lg .portal-track__label { font-size: var(--portal-text-label); }
.portal-track--lg .portal-track__date { font-size: var(--portal-text-label); }

/* ============================================================
   3.14 OFFER STATUS BOX — compact dashboard glance for an in-flight
   offer/application (Pastel #167). Deliberately small: one row —
   icon · title + one status line · status chip · link into the
   benefit page. NOT a hero. The full tracker + tabs live on the
   benefit's active page; this is only a "you have something in
   flight" pointer.
   ============================================================ */
.portal-offer-status {
  display: flex;
  align-items: center;
  gap: var(--portal-space-6);
  background: var(--portal-surface);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card-lg);
  padding: var(--portal-space-5) var(--portal-space-8);
}
.portal-offer-status__icon {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--portal-olive-tint);
  color: var(--portal-olive-deep);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.portal-offer-status__body { flex: 1; min-width: 0; }
.portal-offer-status__title {
  margin: 0;
  font-size: var(--portal-text-body);
  font-weight: 600;
  color: var(--portal-ink);
}
.portal-offer-status__sub {
  margin: var(--portal-space-1) 0 0;
  font-size: var(--portal-text-label);
  color: var(--portal-ink-muted);
}
.portal-offer-status__chip { flex-shrink: 0; }
.portal-offer-status__link {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
  font-size: var(--portal-text-label);
  font-weight: 600;
  color: var(--portal-olive);
  text-decoration: none;
  white-space: nowrap;
}
.portal-offer-status__link svg { transition: transform var(--portal-dur-base) var(--portal-ease-out); }
.portal-offer-status__link:hover { color: var(--portal-ink); }
.portal-offer-status__link:hover svg { transform: translateX(3px); }
@media (max-width: 680px) {
  .portal-offer-status { flex-wrap: wrap; }
  .portal-offer-status__body { flex-basis: calc(100% - 44px - var(--portal-space-6)); }
  .portal-offer-status__chip { order: 3; }
  .portal-offer-status__link { order: 4; margin-left: auto; }
}

/* ============================================================
   3.13c ACTIVATION-WIZARD FOOTER — Back left, primary right.
   Slav, 2026-08-02: every activation wizard follows the profile
   wizard's rule. The pages' own footers are `display: flex` with an
   (empty, since the "progress is saved" line was removed) meta span
   first, which pushed BOTH buttons to the right edge. Pushing the
   ghost Back button instead splits them to opposite ends. Defined
   once here rather than in eight page-local <style> blocks.
   ============================================================ */
.bnft-wiz__footer-inner > .bnft-wiz__footer-meta:empty {
  margin-right: 0;
}
.bnft-wiz__footer-inner > .portal-btn--ghost {
  margin-right: auto;
}

/* ============================================================
   3.14a CREDIT SNAPSHOT (.portal-snapshot) — Pastel #230, Li Saul's
   attachment (30 Jul). The credit dashboard's opening act: one
   headline, four big-number cards under dark identity bands, then a
   row of compact secondary tiles. Lower information density — each
   card answers ONE question at arm's length.
   ============================================================ */
.portal-snapshot {
  display: grid;
  gap: var(--portal-space-8);
}
.portal-snapshot__head {
  display: grid;
  gap: var(--portal-space-2);
  justify-items: center;
  text-align: center;
}
.portal-snapshot__title {
  margin: 0;
  font-family: var(--portal-font-body);
  font-weight: 600;
  font-size: var(--portal-text-hero);
  line-height: 1.15;
  letter-spacing: -0.5px;
  color: var(--portal-ink);
}
.portal-snapshot__meta {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--portal-space-2);
  font-size: var(--portal-text-body);
  color: var(--portal-ink-muted);
}
/* Pastel #218 — Equifax attribution, inline with the update line. */
.portal-snapshot__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-2);
}
/* The shipped Equifax mark is white-on-transparent (built for dark
   panels) — invert it to sit on the light snapshot header. */
.portal-snapshot__brand img {
  display: block;
  height: 22px;
  width: auto;
  filter: brightness(0) saturate(100%);
  opacity: 0.75;
}

.portal-snapshot__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--portal-space-8);
}

.portal-snap-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card-lg);
  background: var(--portal-surface);
  overflow: hidden;
}
/* Dark identity band — olive-tinted, never neutral slate (Principle 4). */
.portal-snap-card__band {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--portal-space-2);
  padding: var(--portal-space-5) var(--portal-space-8);
  background: var(--portal-slate);
}
.portal-snap-card__band-label {
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--portal-gold);
}
.portal-snap-card__info {
  appearance: none;
  border: 0;
  padding: var(--portal-space-1);
  margin: -4px 0;
  background: transparent;
  display: grid;
  place-items: center;
  border-radius: var(--portal-radius-pill);
  color: var(--portal-slate-text);
  cursor: pointer;
  transition: color var(--portal-dur-fast) var(--portal-ease-out);
}
.portal-snap-card__info:hover { color: var(--portal-slate-bright); }
.portal-snap-card__info[aria-expanded="true"] { color: var(--portal-gold); }
.portal-snap-card__info-pop {
  position: absolute;
  top: calc(100% + 6px);
  left: var(--portal-space-8);
  z-index: 6;
  padding: var(--portal-space-2) var(--portal-space-4);
  background: var(--portal-surface);
  color: var(--portal-ink);
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  border-radius: var(--portal-radius-button);
  box-shadow: var(--portal-shadow-card-hover);
  white-space: nowrap;
}

.portal-snap-card__body {
  display: grid;
  gap: var(--portal-space-2);
  padding: var(--portal-space-7) var(--portal-space-8) var(--portal-space-8);
}
.portal-snap-card__value-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--portal-space-6);
}
.portal-snap-card__value {
  margin: 0;
  font-family: var(--portal-font-display);
  font-weight: 500;
  font-size: var(--portal-num-feature);
  line-height: 1;
  letter-spacing: -2px;
  color: var(--portal-ink);
  font-variant-numeric: tabular-nums;
}
.portal-snap-card__chip {
  flex-shrink: 0;
  padding: var(--portal-space-2) var(--portal-space-6);
  border: 1.5px solid var(--portal-hairline-strong);
  border-radius: var(--portal-radius-pill);
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--portal-ink-secondary);
  white-space: nowrap;
}
.portal-snap-card__chip--dark {
  background: var(--portal-slate);
  border-color: var(--portal-slate);
  color: var(--portal-gold);
}
.portal-snap-card__chip[hidden] { display: none; }
.portal-snap-card__meter {
  height: 8px;
  border-radius: 4px;
  background: var(--portal-surface-soft);
  overflow: hidden;
  margin-top: var(--portal-space-2);
}
.portal-snap-card__meter > span {
  display: block;
  height: 100%;
  border-radius: 4px;
  background: var(--portal-olive);
}
.portal-snap-card__note {
  margin: 0;
  font-size: var(--portal-text-body);
  line-height: 1.5;
  color: var(--portal-ink-muted);
}
.portal-snap-card__note--strong { color: var(--portal-olive-deep); font-weight: 500; }

/* Secondary tiles — one metric each, icon + label + value on a line. */
.portal-snapshot__tiles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--portal-space-6);
}
.portal-snap-tile {
  display: flex;
  align-items: center;
  gap: var(--portal-space-5);
  padding: var(--portal-space-6) var(--portal-space-7);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-card);
  background: var(--portal-surface);
  text-decoration: none;
  transition:
    border-color var(--portal-dur-base) var(--portal-ease-out),
    transform var(--portal-dur-base) var(--portal-ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .portal-snap-tile:hover {
    border-color: var(--portal-hairline-strong);
    transform: translateY(-1px);
  }
}
.portal-snap-tile__icon {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: var(--portal-radius-pill);
  background: var(--portal-olive-tint);
  color: var(--portal-olive);
}
.portal-snap-tile__icon svg { width: 24px; height: 24px; }
.portal-snap-tile__copy {
  display: grid;
  gap: 2px;
  min-width: 0;
}
.portal-snap-tile__label {
  font-size: var(--portal-text-caption);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--portal-ink-muted);
}
.portal-snap-tile__value {
  font-family: var(--portal-font-display);
  font-weight: 500;
  font-size: var(--portal-num-data);
  line-height: 1.1;
  letter-spacing: -0.4px;
  color: var(--portal-ink);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 1080px) {
  .portal-snapshot__tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 720px) {
  .portal-snapshot__grid { grid-template-columns: minmax(0, 1fr); }
  .portal-snap-card__value { font-size: var(--portal-num-headline); }
}
@media (max-width: 480px) {
  .portal-snapshot__tiles { grid-template-columns: minmax(0, 1fr); }
}

/* ============================================================
   3.14b FLOATING CONCIERGE (.portal-cfloat) — Slav, 2026-08-02.
   The concierge left the page flow for a bottom-right dock: the
   launcher pattern support products converged on, adapted for a 65+
   audience (labelled pill instead of a bare bubble, 48px+ targets,
   spelled-out CTA). Collapsed = identity + invitation. Expanded = the
   concierge card, scaling from the launcher's corner.
   ============================================================ */
.portal-cfloat {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--portal-space-4);
  pointer-events: none;
}
.portal-cfloat > * { pointer-events: auto; }

/* Collapsed launcher — photo + name + one-line invitation. */
.portal-cfloat__launcher {
  display: inline-flex;
  align-items: center;
  gap: var(--portal-space-4);
  max-width: 320px;
  padding: var(--portal-space-3) var(--portal-space-7) var(--portal-space-3) var(--portal-space-3);
  border: 1px solid var(--portal-hairline);
  border-radius: var(--portal-radius-pill);
  background: var(--portal-surface);
  box-shadow: 0 2px 0 rgba(14, 15, 12, 0.03), 0 16px 40px -12px rgba(14, 15, 12, 0.28);
  cursor: pointer;
  text-align: left;
  transition:
    transform var(--portal-dur-base) var(--portal-ease-out),
    box-shadow var(--portal-dur-base) var(--portal-ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .portal-cfloat__launcher:hover {
    transform: translateY(-2px);
    box-shadow: 0 2px 0 rgba(14, 15, 12, 0.03), 0 22px 48px -12px rgba(14, 15, 12, 0.34);
  }
}
.portal-cfloat__launcher:active { transform: scale(0.98); }
.portal-cfloat__launcher[hidden] { display: none; }
.portal-cfloat__launcher-avatar {
  position: relative;
  flex-shrink: 0;
  width: 48px;
  height: 48px;
}
.portal-cfloat__launcher-avatar img {
  width: 48px;
  height: 48px;
  border-radius: var(--portal-radius-pill);
  object-fit: cover;
  display: block;
}
.portal-cfloat__launcher-dot {
  position: absolute;
  right: 1px;
  bottom: 1px;
  width: 12px;
  height: 12px;
  border-radius: var(--portal-radius-pill);
  background: var(--portal-olive);
  box-shadow: 0 0 0 2px var(--portal-surface);
}
.portal-cfloat__launcher-copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.portal-cfloat__launcher-name {
  font-size: var(--portal-text-label);
  font-weight: 600;
  color: var(--portal-ink);
  white-space: nowrap;
}
.portal-cfloat__launcher-cta {
  font-size: var(--portal-text-caption);
  font-weight: 500;
  color: var(--portal-olive-deep);
  white-space: nowrap;
}

/* Expanded card — the concierge panel, scaled from the launcher corner. */
.portal-cfloat__panel {
  position: relative;
  width: min(380px, calc(100vw - 48px));
  border-radius: var(--portal-radius-card-lg);
  background: var(--portal-surface);
  box-shadow: 0 2px 0 rgba(14, 15, 12, 0.03), 0 28px 64px -16px rgba(14, 15, 12, 0.34);
  transform-origin: bottom right;
  animation: portal-cfloat-in var(--portal-dur-base) var(--portal-ease-out) both;
}
.portal-cfloat__panel[hidden] { display: none; }
@keyframes portal-cfloat-in {
  from { opacity: 0; transform: scale(0.96) translateY(6px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .portal-cfloat__panel { animation: none; }
  .portal-cfloat__launcher { transition: none; }
}
.portal-cfloat__collapse {
  position: absolute;
  top: var(--portal-space-4);
  right: var(--portal-space-4);
  z-index: 2;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--portal-radius-pill);
  background: transparent;
  color: var(--portal-ink-muted);
  cursor: pointer;
  transition:
    background-color var(--portal-dur-fast) var(--portal-ease-out),
    color var(--portal-dur-fast) var(--portal-ease-out);
}
.portal-cfloat__collapse:hover {
  background: var(--portal-surface-warm);
  color: var(--portal-ink);
}
/* The card reuses the canonical concierge panel — trim its page-level
   framing so it sits flush inside the dock. */
.portal-cfloat__panel .portal-concierge__welcome {
  margin: 0;
  border: 0;
  border-radius: var(--portal-radius-card-lg);
  padding: var(--portal-space-8);
  gap: var(--portal-space-5);
}
.portal-cfloat__panel .portal-concierge__welcome-foot {
  flex-direction: column;
  align-items: stretch;
  gap: var(--portal-space-4);
}
.portal-cfloat__panel .portal-concierge__welcome-foot .portal-btn {
  justify-content: center;
  width: 100%;
}
.portal-cfloat__panel .portal-concierge__welcome-head { padding-right: 32px; }

/* The compact help circle only exists on phones. */
.portal-cfloat__launcher-compact { display: none; }

@media (max-width: 720px) {
  /* Clear the fixed bottom tab bar (72px + safe area, portal-mobile.css). */
  .portal-cfloat {
    right: 16px;
    left: 16px;
    bottom: calc(72px + 16px + env(safe-area-inset-bottom, 0px));
    align-items: flex-end;
  }
  .portal-cfloat__panel { width: 100%; }

  /* Collapsed = a help circle, not the identity pill (Slav, 2 Aug):
     64px target, icon over a "Help" label. The avatar and the two copy
     lines are for the expanded card, which still leads with the person. */
  .portal-cfloat__launcher {
    max-width: none;
    width: 64px;
    height: 64px;
    padding: 0;
    border-radius: var(--portal-radius-pill);
    flex-direction: column;
    gap: 0;
    justify-content: center;
  }
  .portal-cfloat__launcher-avatar,
  .portal-cfloat__launcher-copy { display: none; }
  .portal-cfloat__launcher-compact {
    display: grid;
    justify-items: center;
    gap: 1px;
    color: var(--portal-olive-deep);
  }
  .portal-cfloat__launcher-compact-label {
    font-size: var(--portal-text-caption);
    font-weight: 600;
    line-height: 1;
  }
}

/* ============================================================
   3.15 ACTIVATION CONGRATS (.portal-congrats) — one-shot success
   banner on the first dashboard landing after credit activation
   (Pastel #220). JS-injected by dashboard.html; dismiss removes it.
   Success-tinted state panel: real presence, but below the hero's
   weight — it congratulates, it doesn't gate.
   ============================================================ */
.portal-congrats {
  display: flex;
  align-items: flex-start;
  gap: var(--portal-space-6);
  padding: var(--portal-space-7) var(--portal-space-8);
  background: var(--portal-success-bg);
  border: 1px solid var(--portal-hairline);
  border-top: 3px solid var(--portal-olive);
  border-radius: var(--portal-radius-card-lg);
}
.portal-congrats__icon {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: var(--portal-radius-pill);
  background: var(--portal-olive);
  color: #fff;
}
.portal-congrats__copy {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--portal-space-1);
}
.portal-congrats__title {
  margin: 0;
  font-size: var(--portal-text-card-title);
  font-weight: 600;
  color: var(--portal-success);
}
.portal-congrats__body {
  margin: 0;
  font-size: var(--portal-text-body);
  line-height: 1.55;
  color: var(--portal-ink-secondary);
}
.portal-congrats__dismiss {
  appearance: none;
  flex-shrink: 0;
  border: 0;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  padding: 0;
  background: transparent;
  border-radius: var(--portal-radius-pill);
  color: var(--portal-ink-muted);
  cursor: pointer;
  transition: color var(--portal-dur-fast) var(--portal-ease-out),
              background-color var(--portal-dur-fast) var(--portal-ease-out);
}
.portal-congrats__dismiss:hover {
  color: var(--portal-ink);
  background: rgba(0, 0, 0, 0.05);
}

/* ============================================================
   4. MOBILE APP SHELL (≤720px) — the portal on a phone is an
   app, not a shrunk site. One fixed bottom tab bar (injected by
   portal-chrome.js on every portal page), a compact top bar,
   and 65+ rules throughout: 48px taps, 18px body floor,
   explicit labels on every icon.
   ============================================================ */

/* ---- 4.1 Bottom tab bar (.portal-tabbar) ---- */
.portal-tabbar {
  display: none; /* desktop: top nav owns navigation */
}
/* 768px, not 720: the top nav's inline links are hidden from 768px down
   (portal-mobile.css), so a 720px cut-off left 721–768px with neither the
   inline links nor the tab bar. */
@media (max-width: 768px) {
  .portal-tabbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    align-items: stretch;
    background: var(--portal-surface);
    border-top: 1px solid var(--portal-hairline);
    box-shadow: 0 -4px 20px rgba(14, 15, 12, 0.08);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .portal-tabbar__item {
    flex: 1;
    min-width: 0;
    min-height: 60px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--portal-space-1);
    padding: var(--portal-space-2) var(--portal-space-1);
    text-decoration: none;
    color: var(--portal-ink-muted);
    transition: color var(--portal-dur-fast) var(--portal-ease-out), transform 160ms var(--portal-ease-out);
  }
  .portal-tabbar__item:active { transform: scale(0.96); }
  .portal-tabbar__icon { display: inline-flex; }
  .portal-tabbar__label {
    font-size: var(--portal-text-caption);
    font-weight: 600;
    line-height: 1;
    white-space: nowrap;
  }
  /* The bar carries three tabs as of 2026-07-27 (Home · Credit · Benefits),
     so the rule that used to hide the last item below 360px is gone — it
     would now delete Benefits. Three labelled tabs fit at every width we
     support; Concierge and Account moved to the menu sheet. */
  .portal-tabbar__item--active { color: var(--portal-olive); }
  @media (prefers-reduced-motion: reduce) {
    .portal-tabbar__item { transition: color var(--portal-dur-fast) var(--portal-ease-out); }
    .portal-tabbar__item:active { transform: none; }
  }

  /* Content never hides behind the bar. */
  body.has-portal-tabbar {
    padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px));
  }
  /* Sticky action bars (wizards, benefit pages) stack above the tab bar. */
  body.has-portal-tabbar .bnft-wiz__footer {
    bottom: calc(64px + env(safe-area-inset-bottom, 0px));
  }
}

/* ---- 4.2 Compact top bar (≤720px) ---- */
@media (max-width: 720px) {
  /* The tab bar owns section navigation — the top bar slims down to
     brand + avatar. The concierge-message icon is redundant with the
     Concierge tab, so it goes too. */
  .portal-nav__links { display: none; }
  .portal-nav__icon-btn { display: none; }
  .portal-nav__inner {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--portal-space-4) var(--portal-space-6);
    gap: var(--portal-space-4);
  }
  .portal-nav__right { flex: 0 0 auto; margin-left: auto; }
  .portal-nav__brand { min-width: 0; }
  .portal-nav__brand-text { min-width: 0; overflow: hidden; }
  .portal-nav__brand-word {
    display: inline-block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    font-size: var(--portal-text-label);
    letter-spacing: 0.16em;
  }
  @media (max-width: 420px) {
    /* Not enough room for wordmark + avatar — the mark alone carries the
       brand (same rule as the marketing nav). */
    .portal-nav__brand-word { display: none; }
  }
  .portal-nav__account-label,
  .portal-nav__account-chev { display: none; }
  .portal-nav__account { padding: var(--portal-space-2); min-width: 48px; min-height: 48px; justify-content: center; }
  /* Credit sub-nav becomes a horizontal swipe rail with visible overflow
     affordance — links keep full labels (no icon-only). */
  .portal-subnav__inner {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-left: var(--portal-space-6);
    padding-right: var(--portal-space-6);
  }
  .portal-subnav__inner::-webkit-scrollbar { display: none; }
  .portal-subnav__link { white-space: nowrap; min-height: 48px; display: inline-flex; align-items: center; }
}

/* ---- 4.3 Onboarding bar — app banner, not a desktop band ----
   On a phone this is a compact setup card: eyebrow, one line of copy,
   a slim meter, one full-width action. The desktop flair (badge trio,
   sticky pinning, hover states) steps aside. */
@media (max-width: 720px) {
  .portal-onboarding-bar {
    position: static; /* never eat viewport on a phone */
    max-height: none;
  }
  .portal-onboarding-bar__inner {
    grid-template-columns: 1fr !important;
    padding: var(--portal-space-5) var(--portal-space-6) var(--portal-space-6) !important;
    gap: var(--portal-space-3) !important;
  }
  .portal-onboarding-bar__eyebrow {
    font-size: var(--portal-text-caption);
    letter-spacing: 0.14em;
  }
  .portal-onboarding-bar__title { font-size: var(--portal-text-body); line-height: 1.35; }
  .portal-onboarding-bar__body { font-size: var(--portal-text-label); line-height: 1.45; }
  /* The badge trio is desktop decoration — the meter + counter carry the
     same information in a fraction of the height. */
  .portal-onboarding-bar__badges { display: none; }
  .portal-onboarding-bar__action {
    display: grid;
    grid-template-columns: 1fr;
    row-gap: var(--portal-space-3);
  }
  .portal-onboarding-bar__action > .portal-onboarding-bar__counter {
    grid-column: 1;
    grid-row: auto;
    justify-self: start;
    font-size: var(--portal-text-caption);
  }
  .portal-onboarding-bar__action > .portal-onboarding-bar__cta,
  .portal-onboarding-bar__cta {
    grid-column: 1;
    grid-row: auto;
    justify-self: stretch;
    width: 100%;
    justify-content: center;
    min-height: 52px;
  }
  /* Complete-state celebration: single column, medal retired, CTA full row. */
  .portal-onboarding-bar__inner--complete { grid-template-columns: 1fr !important; }
  .portal-onboarding-bar__medal { display: none; }
  .portal-onboarding-bar__dismiss { position: absolute; top: var(--portal-space-4); right: var(--portal-space-4); }
  .portal-onboarding-bar__inner--complete { position: relative; }
}

/* ---- 4.4 Onboarding sheet — full-screen on phones ---- */
@media (max-width: 720px) {
  .portal-onb-sheet__panel {
    width: 100% !important;
    max-width: none !important;
    min-height: 100dvh;
    border-radius: 0 !important;
  }
  .portal-onb-sheet__title { font-size: var(--portal-text-section) !important; line-height: 1.2 !important; }
  .portal-onb-sheet__eyebrow { font-size: var(--portal-text-caption) !important; }
}

/* ---- 4.5 Portal home + shared components on phones ----
   One rhythm for the member-home screen: 16px gutters, 24px between
   blocks, cards padded 20/24, full-width primary actions. */
@media (max-width: 720px) {
  .phome-page {
    padding: var(--portal-space-6) var(--portal-space-6) 0;
  }
  .phome-page > * + * { margin-top: var(--portal-space-8); }
  .phome-hero {
    padding: var(--portal-space-8) var(--portal-space-7);
    gap: var(--portal-space-6);
  }
  .phome-hero__title { font-size: var(--portal-text-section); line-height: 1.2; }
  .phome-hero__actions { width: 100%; }
  .phome-hero__cta { width: 100%; justify-content: center; }

  /* Section heads: one size down, tighter lead-in. */
  .portal-section__head { margin-bottom: var(--portal-space-6); }

  /* Canonical stepper (signup / credit-activate / wizards): compact row —
     32px nodes, tight gaps, no sprawling connectors. */
  .portal-stepper { gap: var(--portal-space-2); }
  .portal-stepper__num {
    width: 32px;
    height: 32px;
  }
  .portal-stepper__num-text { font-size: var(--portal-text-caption); }
}

/* ---- 4.6 Mobile type-scale lock — collapse portal type to the same
   6-tier ladder used site-wide (32/24/20/18/16/14). Canonical, so it
   applies everywhere the portal chrome is used. ---- */
@media (max-width: 720px) {
  .portal-video-hero__title { font-size: var(--portal-text-section); line-height: 1.2; }
}
