/* Spring UI (Django + HTMX friendly)
   Load after spring-tokens.css
*/

html { height: 100%; }

body {
  margin: 0;
  font-family: var(--font-sans);
  color: var(--color-text);
  background:
    repeating-linear-gradient(
      135deg,
      rgba(255, 255, 255, 0.04),
      rgba(255, 255, 255, 0.04) 1px,
      transparent 1px,
      transparent 60px
    ),
    var(--color-bg);
  background-attachment: fixed;
  /* Sticky-footer layout (Workstream E3): body is a flex column so the footer
     always flows below content and can never clip it — this replaces the
     fragile fixed-footer + padding-bottom hack. main grows to fill the
     viewport; the footer sits at the bottom. Applies to every standard page. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* line-height: 1.5; */
}

/* Primary content region grows to fill remaining height, pushing the footer to
   the viewport bottom on short pages and after content on long ones. */
main.container {
  flex: 1 0 auto;
}

/* Site chrome */
.site-header {
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.brand {
  display: inline-flex;
  align-items: center;
  color: var(--color-primary);
  text-decoration: none;
}

.brand-logo {
  height: var(--brand-logo-height, 60px);
  width: var(--brand-logo-width, auto);
  max-height: var(--brand-logo-max-height, 60px);
  max-width: var(--brand-logo-max-width, 400px);
  display: block;
  object-fit: contain;
}

/* #296: the wordmark image carries its trademark symbol, and its canvas was
   extended on the right to hold it: 171 of its 1865px, which at any size is
   171/481 of its height. In the header that strip is taken out of the layout
   with a negative margin, so the navigation keeps exactly the room it had
   before the symbol was added. The symbol hangs into the gap between the
   wordmark and the navigation: 12.8px of the 16px gap at the header's 36px.
   The height is the one base.html and errors/bare_base.html give the image. */
.brand .brand-logo {
  margin-right: calc(var(--brand-logo-height, 36px) * -171 / 481);
}

.nav {
  display: flex;
  gap: var(--space-3);
  align-items: center;
}

.nav-link {
  font-weight: 650;
  color: var(--color-primary);
}

.nav-link:hover { color: var(--color-secondary); }

.nav-dropdown {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.nav-dropdown::after {
  content: '';
  position: absolute;
  top: 100%;
  right: 0;
  min-width: 200px;
  height: 6px;
}

.nav-dropdown-toggle {
  cursor: pointer;
  user-select: none;
}

.nav-dropdown-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 200px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  padding: var(--space-2);
  display: none;
  flex-direction: column;
  gap: var(--space-2);
  z-index: 20;
}

.nav-dropdown-menu .nav-link {
  font-weight: 600;
}

.nav-dropdown:hover .nav-dropdown-menu {
  display: flex;
}

.site-footer {
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
  /* Flows at the end of the flex column (see body) — no longer fixed, so it
     can never overlap page content. flex-shrink keeps it at its natural height. */
  flex-shrink: 0;
}

.site-footer .container {
  font-size: 0.9rem;
  color: var(--color-text-muted);
  padding-top: var(--space-3);
  padding-bottom: var(--space-4);
}

/* Layout helpers */
.container {
  /* max-width: 1100px; */
  margin: 0 auto;
  padding: var(--space-5);
}

.stack > * + * { margin-top: var(--space-4); }
.cluster {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.cluster-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mb-3 { margin-bottom: var(--space-3); }

/* Typography */
h1, h2, h3, h4 { margin: 0 0 var(--space-3); color: var(--color-primary); }
p { margin: 0 0 var(--space-3); }
small, .text-muted { color: var(--color-text-muted); }

a { color: var(--color-link); text-decoration: none; }
a:hover { color: var(--color-link-hover); text-decoration: underline; }
a.btn { text-decoration: none; }
a.btn:hover { text-decoration: none; }

/* Surfaces */
.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: var(--space-5);
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-4);
}

.account-layout {
  display: flex;
  gap: 16px;
  align-items: flex-start;
}

.goal-panel {
  flex: 1 1 0;
  min-width: 0;
}

.account-overview-panel {
  flex: 0 0 25%;
  max-width: 25%;
  position: sticky;
  top: 16px;
  align-self: flex-start;
}

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 28, 46, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 40;
}

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 28, 46, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 40;
}

.modal {
  background: var(--color-surface);
  padding: var(--space-5);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  width: min(70%, 92vw);
  box-shadow: var(--shadow-md);
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
}

.modal-content {
  background: var(--color-surface);
  padding: var(--space-5);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  width: min(70%, 92vw);
  box-shadow: var(--shadow-md);
  height: 90vh;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.modal-header {
  margin-bottom: var(--space-3);
}

.modal-body {
  overflow-y: scroll;
  flex: 1 1 auto;
  min-height: 0;
}

.modal > .card.stack {
  overflow-y: auto;
  max-height: 100%;
}

.modal-close {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  width: 20px;
  height: 20px;
  line-height: 18px;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text);
  font-size: 14px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

body.modal-open {
  overflow: hidden;
}

.modal-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  height: 100%;
  flex: 1 1 auto;
}

.modal-footer {
  margin-top: var(--space-4);
}

.range-field {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.range-field input[type="range"] {
  flex: 1 1 auto;
}

.range-value {
  min-width: 3ch;
  text-align: right;
  font-weight: 650;
  color: var(--color-text);
}

/* Editable number input that sits next to a range slider */
.range-value-input {
  width: 5ch;
  padding: 2px 4px;
  font-size: 0.875rem;
  font-weight: 650;
  text-align: right;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  color: var(--color-text);
  background: transparent;
  -moz-appearance: textfield;
}
.range-value-input::-webkit-outer-spin-button,
.range-value-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.range-value-input:focus {
  outline: 2px solid var(--color-primary);
  outline-offset: 1px;
}

/* ── Inline-editable rate chips (narrative sentence in chart column) ──────── */
.rate-chip {
  display: inline-block;
  color: var(--color-secondary);
  background: rgba(62, 92, 118, 0.09);
  border-bottom: 1.5px dotted var(--color-secondary);
  border-radius: 3px;
  padding: 0 3px;
  cursor: pointer;
  font-weight: 600;
  line-height: 1.4;
}

.rate-chip:hover {
  background: rgba(62, 92, 118, 0.16);
}

.rate-chip-input {
  width: 4ch;
  padding: 0 2px;
  font-size: inherit;
  font-weight: 600;
  color: var(--color-secondary);
  text-align: right;
  border: none;
  border-bottom: 1.5px solid var(--color-secondary);
  border-radius: 3px;
  outline: 2px solid var(--color-secondary);
  outline-offset: 1px;
  background: var(--color-surface);
  -moz-appearance: textfield;
}

.rate-chip-input::-webkit-outer-spin-button,
.rate-chip-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.goal-helper-step-c-grid {
  display: grid;
  grid-template-columns: 2fr 3fr; /* left=40%, right=60% */
  gap: var(--space-5);
  align-items: start;
}

@media (max-width: 700px) {
  .goal-helper-step-c-grid {
    grid-template-columns: 1fr;
  }
}

/* Step D — Details & Funding: two-column layout (form left, summary right) */
.step-d-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-5);
  align-items: start;
}

@media (max-width: 700px) {
  .step-d-grid {
    grid-template-columns: 1fr;
  }
}

/* Recommended monthly contribution — inline badge in the Monthly Contribution label */
.recommended-value {
  display: inline-block;
  font-weight: 700;
  font-size: 1.05em;
  color: var(--color-secondary);
  background: rgba(62, 92, 118, 0.09);
  padding: 1px 7px;
  border-radius: 5px;
  letter-spacing: 0;
}

/* Suppress the OOB span when it renders inside the chart column on initial
   page load (before any HTMX swaps replace it into the label). */
#goal-helper-chart #recommended-monthly-value {
  display: none;
}

.goal-helper-chart {
  display: block;
  width: 100%;
  height: auto;
}

/* ── Forecast disclosure: side-by-side narrative + chart ──────────── */
.forecast-layout {
  display: flex;
  gap: var(--space-4);
  align-items: flex-start;
}
.forecast-narrative {
  flex: 0 0 35%;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-top: 4px;
}
.forecast-chart {
  flex: 1;
  min-width: 0;
  max-height: 320px;
}
.forecast-chart .goal-helper-chart {
  max-height: 320px;
  width: 100%;
  height: auto;
}
.forecast-range {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.forecast-range-value {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--color-primary);
}
.forecast-range-label {
  font-size: 0.72rem;
  font-weight: 400;
  color: var(--color-text-muted);
}
.forecast-range-value--cash {
  color: #9AA0A6;
  font-weight: 500;
}

/* ── Status badges (funding status in Recent Orders) ──────────────── */
.status-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 0.72rem;
  font-weight: 500;
}
.status-badge--queued    { background: var(--color-bg-muted); color: var(--color-text-muted); }
.status-badge--paused    { background: var(--color-bg-muted); color: var(--color-text-muted); }
.status-badge--requested { background: #dbeafe; color: #1e40af; }
.status-badge--pending   { background: #fef3c7; color: #92400e; }
.status-badge--settled   { background: #d1fae5; color: #065f46; }
.status-badge--failed    { background: #fee2e2; color: #991b1b; }

/* Transfer & Order Management — funding status badge variants.
   Colors progress from farthest-from-placement (grey) through cash-in-flight
   (blue) to cash-ready (gold) to done (green). All use style-guide semantic
   tokens from bold_minimal.css / docs/ui/style-guide.md. */
.status-badge--draft              { background: var(--color-bg-muted); color: var(--color-text-muted); }
.status-badge--submitted          {
  background: var(--color-surface, #ffffff);
  color: var(--color-text-muted);
  border: 1px solid var(--color-border, #D0D7DE);
}
.status-badge--pending_funds      {
  background: color-mix(in srgb, var(--color-secondary) 14%, white);
  color: var(--color-secondary);
}
.status-badge--funded             {
  background: var(--color-gold-bg);
  color: var(--color-gold);
  border: 1px solid var(--color-gold-border);
}
.status-badge--executing          {
  background: color-mix(in srgb, var(--color-warning) 18%, white);
  color: var(--color-warning);
}
.status-badge--complete           {
  background: color-mix(in srgb, var(--color-success) 22%, white);
  color: var(--color-success);
}
.status-badge--canceled           { background: var(--color-bg-muted); color: var(--color-text-muted); }
.status-badge--not_started        {
  background: var(--color-surface, #ffffff);
  color: var(--color-text-muted);
  border: 1px solid var(--color-border, #D0D7DE);
}
.status-badge--scheduled          {
  background: color-mix(in srgb, var(--color-success) 12%, white);
  color: var(--color-success);
}
.status-badge--transfer_created   { background: #dbeafe; color: #1e40af; }
.status-badge--transfer_pending_settlement { background: #fef3c7; color: #92400e; }
.status-badge--ready_to_trade     { background: #dbeafe; color: #1e40af; }
.status-badge--trading_submitted  { background: #fef3c7; color: #92400e; }
.status-badge--completed          { background: #d1fae5; color: #065f46; }
.status-badge--cancelled          { background: var(--color-bg-muted); color: var(--color-text-muted); }
.status-badge--new                { background: #dbeafe; color: #1e40af; }
.status-badge--accepted           { background: #dbeafe; color: #1e40af; }
.status-badge--filled             { background: #d1fae5; color: #065f46; }
.status-badge--error              { background: #fee2e2; color: #991b1b; }
.status-badge--deposit            { background: #dbeafe; color: #1e40af; }
.status-badge--withdraw           { background: #fef3c7; color: #92400e; }

/* V5: needs_review badge — amber/gold, matches style guide's
   attention-signal bucket (--color-gold). */
.status-badge--review             { background: #fef3c7; color: #854d0e; margin-left: 4px; }

/* V5: origin pill — compact label that identifies which system
   placed a SpringOrder (funding_run, allocator, manual_staff, unknown).
   Renders on every row in the Transfers dashboard and Account Inspector
   to give staff instant attribution context. */
.pill {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 0.6rem;
  font-weight: 500;
  margin-left: 4px;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  vertical-align: middle;
  white-space: nowrap;
}
.pill--origin-funding_run         { background: #dbeafe; color: #1e40af; }
.pill--origin-allocator           { background: #d1fae5; color: #065f46; }
.pill--origin-manual_staff        { background: var(--color-bg-muted, #e9ecef); color: var(--color-text-muted, #5F6368); }
.pill--origin-unknown             { background: #fef3c7; color: #854d0e; }

/* ── Funding chain rail (split-deposit visual grouping) ──────────────
   Applied to every <tr> whose FundingRun shares a chain_id when >= 2
   rows in that chain are visible on the page. Left-border "rail"
   visually ties the chunks together WITHOUT reordering rows, so the
   default updated_at DESC sort is preserved. */
.funding-chain-rail {
  border-left: 3px solid var(--color-secondary, #3E5C76);
}
.funding-chain-header {
  font-size: 0.7rem;
  color: var(--color-text-muted);
  margin-top: 2px;
}
.funding-chain-jump {
  font-size: 0.7rem;
  text-decoration: none;
  color: var(--color-secondary);
  margin-left: 6px;
}
.funding-chain-jump:hover {
  text-decoration: underline;
}

/* Beat-health banner in base.html */
.beat-health-banner code {
  background: rgba(0,0,0,0.04);
  padding: 1px 4px;
  border-radius: 3px;
  font-size: 0.85em;
}

/* ── Info tooltip ─────────────────────────────────────────────
   Semi-transparent white popover with blue border and black text.
   Use .info-tooltip on any element with a data-tooltip attribute
   to provide contextual definitions or explanations on hover.
   Legacy alias: .tooltip-term maps to the same styles.
   ─────────────────────────────────────────────────────────── */
.info-tooltip,
.tooltip-term {
  border-bottom: 1px dotted var(--color-text-muted);
  cursor: help;
  position: relative;
}
.info-tooltip::after,
.tooltip-term::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  background: rgba(255, 255, 255, 0.95);
  color: var(--color-text, #24292e);
  border: 1px solid var(--color-secondary, #3E5C76);
  padding: 6px 10px;
  border-radius: 6px;
  font-size: 0.72rem;
  line-height: 1.4;
  max-width: 300px;
  width: max-content;
  white-space: normal;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
  z-index: 10;
}
.info-tooltip:hover::after,
.tooltip-term:hover::after {
  opacity: 1;
}

@media (max-width: 600px) {
  .forecast-layout {
    flex-direction: column;
  }
  .forecast-narrative {
    flex: none;
  }
  .forecast-chart {
    max-height: none;
  }
}

/* Side-by-side field pairs within the Step C left-panel form */
.step-c-field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
  align-items: start;
}

@media (max-width: 640px) {
  /* At narrow viewports collapse the 2-column field pairs to a single column */
  .step-c-field-row {
    grid-template-columns: 1fr;
  }
}

/* Cancel confirmation — modal overlay centered in the viewport */
.cancel-confirm {
  position: fixed;
  inset: 0;
  display: flex;
  justify-content: center;
  background: rgba(0, 0, 0, 0.35);
  z-index: 500;
  overflow-y: auto;
  padding: var(--space-5) 0;
}

.cancel-confirm[hidden] {
  display: none;
}

.cancel-confirm-dialog {
  background: var(--color-surface, #fff);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  padding: var(--space-5) var(--space-5);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  max-width: 340px;
  width: 90%;
  text-align: center;
  font-size: 0.95rem;
  flex-shrink: 0;
  margin: auto;
}

.cancel-confirm-actions {
  display: flex;
  gap: var(--space-3);
  justify-content: center;
}

/* Sign-in modal (Flight 2): a wider, left-aligned .cancel-confirm dialog so the
   passkey-first sign-in body reads as a compact form rather than a centered alert. */
.auth-modal-dialog {
  max-width: 380px;
  text-align: left;
  align-items: stretch;
  gap: var(--space-3);
  position: relative;
}
.auth-modal-dialog .auth-title { margin: 0 0 var(--space-2); font-size: 1.15rem; }
.auth-modal-dialog .auth-primary { width: 100%; }
.auth-modal-close {
  position: absolute; top: 8px; right: 12px;
  background: none; border: none; padding: 0;
  font-size: 20px; line-height: 1; cursor: pointer; color: var(--color-text-muted);
}
.auth-hint { font-size: 0.9rem; margin: 0; }

/* Consent appears only after sign-in. Native dialog supplies focus containment. */
.analytics-consent-dialog {
  margin: auto;
  width: min(440px, calc(100vw - 2rem));
  max-height: 85dvh;
  overflow-y: auto;
  padding: var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg, 12px);
  background: var(--color-surface, #fff);
  color: var(--color-text);
  box-shadow: var(--shadow-lg);
}
.analytics-consent-dialog:not([open]) { display: none; }
.analytics-consent-dialog::backdrop { background: rgba(0, 0, 0, 0.45); }
.analytics-consent-dialog h2 { margin: 0 0 var(--space-3); }
.analytics-consent-dialog p { margin: 0 0 var(--space-4); }
.analytics-consent-dialog .form-actions { flex-wrap: wrap; }
.beta-agreement-dialog { width: min(580px, calc(100vw - 2rem)); }
.beta-agreement-copy { text-align: left; }
.beta-agreement-choice { display: flex; align-items: flex-start; gap: var(--space-2); margin-bottom: var(--space-3); }
.beta-agreement-choice input { flex: none; margin-top: 0.3em; }
.beta-agreement-form .form-actions { margin-top: var(--space-4); }
.beta-agreement-nojs { max-width: 580px; margin: var(--space-4) auto; }
/* #349 — the passkey offer's two actions are ONE row: Skip for now (secondary) on the left,
   Add a passkey (primary) rightmost, Spring's small gap between them, centred as a pair. The
   order is the markup's; this rule only lays it out. .form-actions has no layout of its own,
   and each action is its own block-level form, so without the flex container they stacked.
   The pair wraps — each action on its own centred line — only when the dialog genuinely
   cannot hold both, never clipping and never scrolling sideways. */
.auth-passkey-dialog .form-actions {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--space-2);
}
.auth-passkey-dialog .form-actions form { margin: 0; }
.passkey-enrollment-nojs, .goal-summary-nojs { max-width: 580px; margin: var(--space-4) auto; }
.analytics-consent-nojs { max-width: 440px; margin: var(--space-4) auto; }
.analytics-preferences.dashboard-column {
  width: min(100%, 560px);
  box-sizing: border-box;
  gap: var(--space-4);
  padding: var(--space-5);
}
.analytics-preferences h1, .analytics-preferences p { margin: 0; }
main.analytics-report-main { width: 100%; box-sizing: border-box; }
.analytics-report-content { min-width: 0; max-width: 100%; }
.analytics-report-table-scroll { max-width: 100%; min-width: 0; overflow-x: auto; }
.analytics-report-table-scroll table { min-width: 520px; }

/* Screen-reader-only utility (Flight A): hides a label visually while keeping it
   in the accessibility tree — used for the email-code field's accessible name
   after its visible label was removed. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.fund-details {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface-2);
  padding: var(--space-3);
}

.fund-details + .fund-details {
  margin-top: var(--space-3);
}

.fund-details-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  cursor: pointer;
  list-style: none;
  font-weight: 650;
  white-space: nowrap;
}

.fund-details-summary::before {
  content: "→";
  display: inline-block;
  margin-right: var(--space-2);
  transition: transform 0.15s ease;
}

details[open] .fund-details-summary::before {
  transform: rotate(-90deg);
}

.fund-details-summary::-webkit-details-marker {
  display: none;
}

.fund-details-subtotal {
  font-weight: 700;
}

.fund-details-body {
  margin-top: var(--space-3);
}

.goal-total-value {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--color-primary);
}

.hr {
  border: 0;
  height: 1px;
  background: var(--color-border);
  margin: var(--space-5) 0;
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 10px 20px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-size: 14px;
  font-weight: 650;
  cursor: pointer;
  user-select: none;
  transition: transform 80ms ease, background-color 120ms ease, border-color 120ms ease, color 120ms ease, opacity 120ms ease;
}

.input-error {
  border: 1px solid var(--spring-error, #cc0000);
}

.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.btn:active { transform: translateY(1px); }

.btn-primary { background: var(--color-primary); color: #fff; }
.btn-primary:hover { background: #1a2d45; color: #fff; }

/* .btn-secondary, .btn-accent — REMOVED (v0.2 style guide).
   Unused in templates. Use .btn-primary or .btn-outline instead. */

.btn-outline {
  background: transparent;
  color: var(--color-primary);
  border-color: var(--color-border);
}
.btn-outline:hover { border-color: var(--color-secondary); color: var(--color-primary); }

.btn-white {
  background: #fff;
  color: var(--color-text-muted);
  border-color: var(--color-border);
}
.btn-white:hover { border-color: var(--color-secondary); color: var(--color-primary); }

/* Neutral (#307) — a quiet action that is neither forward nor back, and must still read as
   ENABLED: light grey fill, dark text, a border darker than the fill. Never the primary's dark
   blue. Used for the Education Plan Forecast's "Reset assumptions". Hover darkens the fill and
   the border; the text colour never changes (style guide, Buttons → Hover). Focus is .btn's. */
.btn-neutral { background: #EEF0F2; color: var(--color-text); border-color: #AEB6BE; }
.btn-neutral:hover { background: #E1E5E9; border-color: #8C97A2; color: var(--color-text); }

.btn-sm {
  padding: 6px 10px;
  font-size: 0.85rem;
}

.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.6; cursor: not-allowed; }

/* ── Button row (navigation actions) ── */
.btn-row { display: flex; gap: 8px; align-items: center; margin-top: 24px; }

/* Expand/Collapse toggle buttons ONLY */
.btn-toggle {
  padding: 0 2px;
  line-height: 1;
  height: auto;
}

/* Forms */
.field { display: grid; gap: var(--space-2); }
label { font-weight: 650; color: var(--color-primary); }

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
}

@media (max-width: 480px) {
  .form-row { grid-template-columns: 1fr; }
}

.label-inline {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}

.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-secondary);
  font-size: 0.72rem;
  font-weight: 650;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

input[type="text"],
input[type="email"],
input[type="tel"],
input[type="number"],
input[type="date"],
input[type="password"],
input[type="search"],
input[type="url"],
.input,
select,
textarea {
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
}

/* Normalize select height to match text inputs by stripping browser-native
   appearance (which ignores our padding) and supplying a custom caret. */
select {
  appearance: none;
  -webkit-appearance: none;
  background-color: var(--color-surface);
  background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' stroke='%23888' stroke-width='1.5' fill='none' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 12px 8px;
  padding-right: 36px;
  cursor: pointer;
}

input[type="text"]:focus,
input[type="email"]:focus,
input[type="tel"]:focus,
input[type="number"]:focus,
input[type="date"]:focus,
input[type="password"]:focus,
input[type="search"]:focus,
input[type="url"]:focus,
.input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: var(--focus-ring);
}

.field input[type="checkbox"] {
  width: auto;
  margin: 0;
  justify-self: start;
}

.help { font-size: 0.9rem; color: var(--color-text-muted); }

/* Django form errors */
.errorlist {
  margin: 0;
  padding-left: 18px;
  color: var(--color-danger);
}

.field-error .input,
.field-error select,
.field-error textarea {
  border-color: rgba(226, 85, 85, 0.65);
}

/* Alerts (messages framework) */
.alert {
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  border: 1px solid var(--color-border);
  background: var(--color-surface-2);
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
}

.alert-success {
  background: #e8f7f0;
  border-color: #bce7d4;
}

.alert-warning {
  background: #fef3e8;
  border-color: #f5d4a8;
}

.alert-danger,
.alert-error {
  background: #fbeaea;
  border-color: #edb8b8;
}

/* Tables */
.table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: visible;
}
.table thead tr:first-child th:first-child { border-top-left-radius: var(--radius-md); }
.table thead tr:first-child th:last-child  { border-top-right-radius: var(--radius-md); }
.table tbody tr:last-child td:first-child  { border-bottom-left-radius: var(--radius-md); }
.table tbody tr:last-child td:last-child   { border-bottom-right-radius: var(--radius-md); }

.table th, .table td {
  padding: 5px 5px;
  border-bottom: 1px solid var(--color-border);
  text-align: left;
}

.table.compact th, .table.compact td {
  padding: 4px 6px;
}

.text-right { text-align: right; }

.table th {
  background: var(--color-surface-2);
  color: var(--color-primary);
  font-weight: 750;
}

.table tbody tr:last-child td { border-bottom: none; }
.table tr:hover td { background: rgba(62, 92, 118, 0.06); }
.goal-details-row {
  display: none;
}
.goal-details-row:target {
  display: table-row;
}

.th-inline {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  white-space: nowrap;
}

.sort-icons {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  white-space: nowrap;
}

.table-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
}

.form-inline {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.form-inline select { width: auto; }

.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-top: var(--space-3);
}

.pager-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.sort-link {
  margin-left: 0;
  font-size: 0.85em;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
}

.sort-muted { opacity: 0.5; }
.sort-active { font-weight: 700; }

/* Brokerage summary label alignment */
.summary-label {
  display: flex;
  align-items: center;
  gap: 8px;
}

.summary-icon-slot {
  width: 28px;
  display: inline-flex;
  justify-content: center;
  align-items: center;
}

.summary-icon-placeholder {
  display: inline-block;
  width: 1px;
  height: 1px;
}

/* Account Overview dot-leader list */
.overview-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.overview-row {
  display: flex;
  align-items: baseline;
  gap: 4px;
  padding-left: var(--space-2);
}

.overview-label {
  flex: 0 0 auto;
  font-size: 0.9rem;
  color: var(--color-text-muted);
  white-space: nowrap;
}

.overview-dots {
  flex: 1 1 auto;
  border-bottom: 1.5px dotted var(--color-border);
  margin-bottom: 4px;
  min-width: 12px;
}

.overview-value {
  flex: 0 0 auto;
  font-size: 0.9rem;
  color: var(--color-text);
  white-space: nowrap;
  text-align: right;
}

.overview-row--total .overview-label,
.overview-row--total .overview-value {
  color: var(--color-primary);
}

.goals-controls-row {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 6px;
}

.goals-filter-form {
  display: flex;
  align-items: center;
}
.goals-filter-form select {
  font-size: 14px;
  font-weight: 650;
  color: var(--color-text-muted);
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 10px 20px;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%235F6368'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  padding-right: 34px;
}
.goals-filter-form select:hover {
  border-color: var(--color-secondary);
  color: var(--color-primary);
}
.goals-filter-form select:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* ── Dashboard column layout ──────────────────────────────────────────── */
.dashboard-column {
  /* Global standard content-card width (UI Polish Sprint 1 / #232).
     Widened from 760px to better accommodate the larger application typography. */
  max-width: 920px;
  margin: 32px auto;
  padding: 48px 40px;
  display: flex;
  flex-direction: column;
  gap: 48px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 14px);
  box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,0.08));
}
.dashboard-column--wide {
  max-width: 95%;
}

/* Goals dashboard width fix (UI Polish Sprint 1 / #232).
   <main class="container"> is a flex item that shrink-wraps to its content's
   max-content width (body's align-items is not stretch), so .dashboard-column
   never reaches its max-width — it just hugs its content (~480px), making
   max-width a no-op. Opting the goals page's <main> into width:100% lets the
   column fill to its 920px max-width and center, per the Style Guide. Scoped to
   this page (via {% block main_extra_class %}) so other surfaces are untouched in
   this flight; the app-wide shrink-wrap is recorded in the deprecation queue. */
main.goals-dashboard-wide {
  width: 100%;
  box-sizing: border-box;
}

/* ── Life Progress Summary ───────────────────────────────────────────── */
.life-progress-summary {
  text-align: center;
}

.life-progress-metric {
  font-size: 48px;
  font-weight: 600;
  color: var(--color-primary);
  line-height: 1;
}

.life-progress-label {
  font-size: 15px;
  color: var(--color-text-muted);
  margin-top: var(--space-2);
}

/* ── Section labels (GOALS, NEXT ACTIONS, ACCOUNT) ───────────────────── */
.section-label {
  font-family: 'Geist Mono', monospace;
  /* 14px + --color-text-muted (UI Polish Sprint 1 / #232) — the app-wide section
     header standard. Larger and more legible than the former 10px/#d1d1db dim
     treatment; keeps the uppercase mono + wide tracking. */
  font-size: 14px;
  font-weight: 400;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-bottom: var(--space-3);
}

/* ── Portfolio allocation bar ─────────────────────────────────────────── */
.portfolio-bar-section {
  margin-bottom: var(--space-4);
}
.portfolio-bar-wrap {
  position: relative;
  padding: 12px 0;
}
.portfolio-bar {
  display: flex;
  height: 14px;
  border-radius: 7px;
  overflow: visible;
  background: var(--color-border);
}
.portfolio-bar-seg {
  position: relative;
  overflow: visible;
  transition: width 0.5s ease;
}
.portfolio-bar-seg:first-child { border-radius: 7px 0 0 7px; }
.portfolio-bar-seg:last-child  { border-radius: 0 7px 7px 0; }
.portfolio-bar-seg:only-child  { border-radius: 7px; }
.portfolio-bar-seg--current { outline: 2px solid rgba(255,255,255,0.6); outline-offset: -2px; }
.portfolio-bar-seg--disabled { opacity: 0.5; }
.portfolio-bar-seg--disabled::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: repeating-linear-gradient(
    -45deg,
    transparent,
    transparent 4px,
    rgba(255, 255, 255, 0.3) 4px,
    rgba(255, 255, 255, 0.3) 8px
  );
}
.portfolio-bar-seg--disabled .portfolio-bar-tag {
  color: var(--color-text-muted);
}
.portfolio-bar-tag {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #fff;
  padding: 4px 8px;
  border-radius: 6px;
  border: 2px solid;
  font: 500 11px/1.2 'Instrument Sans', sans-serif;
  color: var(--color-text);
  white-space: nowrap;
  pointer-events: none;
  z-index: 1;
}

/* ── Goal Allocation Bar (Plan) ──────────────────────────────────────
   The Plan analogue of the Invest portfolio bar — same visual style, but a
   distinct, reusable abstraction driven by each active goal's tracked value
   (Track Your Progress), not brokerage holdings. */
.goal-allocation-bar-section { margin-bottom: var(--space-4); }
.goal-allocation-bar-wrap { position: relative; padding: 12px 0; }
.goal-allocation-bar {
  display: flex;
  height: 14px;
  border-radius: 7px;
  background: var(--color-border);
}
.goal-allocation-bar-seg {
  position: relative;
  transition: width 0.5s ease;
}
.goal-allocation-bar-seg:first-child { border-radius: 7px 0 0 7px; }
.goal-allocation-bar-seg:last-child  { border-radius: 0 7px 7px 0; }
.goal-allocation-bar-seg:only-child  { border-radius: 7px; }
/* Always-visible goal-type icon: a white circle with a border in the segment's
   color, centered over the segment (a simplified version of the hover pill). */
.goal-allocation-bar-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  border: 2px solid;   /* color set inline per-segment (border-color: seg.color) */
  border-radius: 50%;
  font-size: 12px;
  line-height: 1;
  pointer-events: none;
  transition: opacity 0.15s ease;
  z-index: 1;
}
/* Name + value pill — hidden until the segment is hovered, so the bar stays
   calm as goals multiply. The native title tooltip is the accessible fallback. */
.goal-allocation-bar-tag {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #fff;
  padding: 4px 8px;
  border-radius: 6px;
  border: 2px solid;
  font: 500 11px/1.2 'Instrument Sans', sans-serif;
  color: var(--color-text);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s ease;
  z-index: 2;
}
.goal-allocation-bar-seg:hover .goal-allocation-bar-tag {
  opacity: 1;
  visibility: visible;
  z-index: 3;
}
.goal-allocation-bar-seg:hover .goal-allocation-bar-icon {
  opacity: 0;
}

/* ── Seed prompt (gold attention card on manage-goal) ─────────────── */
.seed-prompt { margin-bottom: var(--space-4); }
.seed-prompt-card {
  background: #fffbeb;
  border-left: 4px solid #E6B800;
  border-radius: var(--radius);
  padding: var(--space-4);
}
.seed-prompt-heading {
  font: 600 15px/1.3 'Instrument Sans', sans-serif;
  color: var(--color-text);
}

/* ── Goal card stack (single column) ─────────────────────────────────── */
.goal-card-stack {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.goal-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  border-radius: 14px;
  padding: 28px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  overflow: hidden; /* clip the full-bleed attention banner to the card radius */
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.goal-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
}

/* Goal card preview — static, no hover effect (used in wizard Screen 2) */
.goal-card--preview { cursor: default; }
.goal-card--preview:hover { transform: none; box-shadow: 0 1px 2px rgba(0,0,0,0.04), 0 6px 16px rgba(0,0,0,0.06); }

/* ── Goal Confidence risk bands (#255) ────────────────────────────────────
   ONE semantic token set, driven by accounts.services.goal_confidence's band →
   presentation mapping. The modifier is supplied by the server; templates never map a
   numeric score to a colour. Yellow and orange stay DISTINCT — "At Risk" and "Needs
   Attention" are different messages.

   Green deliberately has NO modifier: it keeps the card's normal neutral border and gets
   no banner, so Spring never adds success chrome.

   Banner tints are mixed from the same family as the border, at a ratio that keeps the
   white banner text above 4.5:1 (the banner is a solid fill, not a wash). */
.goal-card--risk-warning   { border-left: 4px solid var(--color-gold); }
.goal-card--risk-caution   { border-left: 4px solid var(--color-warning); }
.goal-card--risk-off-track { border-left: 4px solid var(--color-danger); }

/* The banner BACKGROUND modifiers live with the .goal-card-banner base rule further down this
   file, NOT here: the base rule sets `background: var(--color-gold)` at equal specificity, so a
   modifier declared above it loses the cascade and every band rendered gold (#255). Keeping the
   two together is what stops that regression recurring. */

/* Confidence UNAVAILABLE (#255) — the calculation failed. Deliberately OUTSIDE the four band
   colours so it can never be read as a risk level, and deliberately not neutral-silent either:
   green shows no banner, so a failure rendered neutral would have been indistinguishable from a
   healthy goal. Muted grey banner, ordinary neutral border. */
.goal-card--risk-unavailable   { border-left: 4px solid var(--color-border); }

/* Return assumption NEEDED (#297) — an Education Plan goal with no user-confirmed expected
   return. Not a band and not a failure: the forecast was deliberately not run. Steel blue, the
   informational colour, so it reads as "something to do" rather than as a risk level (never
   gold, orange or red) and never as the silent neutral state. */
.goal-card--risk-assumption-required { border-left: 4px solid var(--spring-steel-blue); }

/* `.goal-card--attention` (a fixed-gold border driven by a boolean) was REMOVED here: it was the
   Goal Overview's border source while its banner used the band modifier, producing a red banner
   above a yellow border. Both surfaces now derive border AND banner from one
   ConfidencePresentation. No template references it. */

/* ── Disabled overlay — reusable translucent diagonal stripe pattern ─── */
/* Apply .disabled-overlay to any container, or .goal-card--disabled to a goal card. */
.disabled-overlay,
.goal-card--disabled {
  position: relative;
  opacity: 0.7;
}
.disabled-overlay::after,
.goal-card--disabled::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: repeating-linear-gradient(
    -45deg,
    transparent,
    transparent 6px,
    rgba(0, 0, 0, 0.04) 6px,
    rgba(0, 0, 0, 0.04) 12px
  );
}
.goal-card--disabled:hover {
  transform: none;
}
.goal-card-status--disabled .status-dot {
  background: var(--color-text-muted);
}

/* Goal card header: name + manage link */
.goal-card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
}

.goal-card-name {
  font-family: 'Geist Mono', monospace;
  font-size: 12px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
}

/* Goal-type icon + name cluster, aligned on the header baseline with Update. */
.goal-card-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.goal-card-icon {
  font-size: 16px;
  line-height: 1;
  flex: 0 0 auto;
}

.goal-card-manage {
  font-family: 'Geist Mono', monospace;
  font-size: 12px;
  font-weight: 400;
  color: var(--color-secondary);
  text-decoration: none;
}
.goal-card-manage:hover {
  color: var(--color-primary);
}

/* Funded % (left) + monthly contribution (right) share one baseline-aligned row.
   Wraps on very narrow viewports (mobile) so the row never forces horizontal
   overflow of the card/page. */
.goal-card-metrics {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  column-gap: var(--space-3);
  row-gap: 4px;
}

/* Goal card: large funded metric */
.goal-card-funded {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.goal-card-funded-pct {
  font-size: 38px;
  font-weight: 600;
  line-height: 1;
  color: var(--color-primary);
}

.goal-card-funded-label {
  font-size: 15px;
  color: var(--color-text-muted);
}

/* ── Combo bar (funding + timeline merged) ───────────────────────────── */
@keyframes combo-bar-fill {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.combo-bar {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.combo-bar-year {
  font-family: 'Geist Mono', monospace;
  font-size: 11px;
  font-weight: 400;
  color: var(--color-text-muted);
  flex-shrink: 0;
  /* Vertically center on the 6px track */
  margin-top: -4px;
}

.combo-bar-body {
  flex: 1;
  position: relative;
  padding-bottom: 18px;
}

.combo-bar-track {
  width: 100%;
  height: 6px;
  background: #D0D7DE;
  border-radius: 3px;
  overflow: hidden;
  position: relative;
}

.combo-bar-funded {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background: var(--color-success, #16a34a);
  border-radius: 3px 0 0 3px;
  transform-origin: left center;
  animation: combo-bar-fill 120ms ease-out both;
}

.combo-bar-gap {
  position: absolute;
  top: 0;
  height: 100%;
  background: var(--color-gold, #E6B800);
  opacity: 0.45;
  transform-origin: left center;
  animation: combo-bar-fill 120ms ease-out both;
}

@media (prefers-reduced-motion: reduce) {
  .combo-bar-funded,
  .combo-bar-gap {
    animation: none;
  }
}

/* Milestone tick marks at 20% intervals */
.combo-bar-tick {
  position: absolute;
  top: 0;
  width: 2px;
  height: 100%;
  background: rgba(0, 0, 0, 0.12);
  z-index: 1;
}

.combo-bar-marker {
  position: absolute;
  top: -1px;
  width: 8px;
  height: 8px;
  background: #0F1C2E;
  border-radius: 50%;
  transform: translateX(-50%);
  z-index: 2;
}

.combo-bar-marker-label {
  position: absolute;
  top: 11px;
  left: 50%;
  transform: translateX(-50%);
  font-family: 'Geist Mono', monospace;
  font-size: 10px;
  color: var(--color-text-muted);
  white-space: nowrap;
}

/* Goal card: saved / target text */
.goal-card-saved {
  text-align: center;
  font-size: 13px;
  color: var(--color-text-muted);
}

/* Current monthly contribution — centered on its own full-width line below the
   funded metric (#236 · Flight P1 restored the prior centered alignment; a
   regression had right-aligned it opposite the funded %). */
.goal-card-contribution {
  text-align: center;
  flex-basis: 100%;
  font-size: 16px;
  font-weight: 600;
  color: var(--color-text);
}

/* Attention banner — full-width, flush to the card's top edge, solid warning
   background with bold white text. Shown only for warning goals. Negative margins
   break it out of the card's 28px padding; the card's overflow:hidden + radius
   clip its top corners so it reads as part of the card. */
.goal-card-banner {
  margin: -28px -28px 0 -28px;
  padding: 10px 28px;
  background: var(--color-gold, #E6B800);
  color: #fff;
  font-weight: 700;
  font-size: 14px;
  line-height: 1.3;
}

/* Band-specific banner backgrounds (#255). MUST stay immediately after the base rule: the base
   sets a gold background at equal specificity, so declaring these earlier in the file lost the
   cascade and every band rendered gold. Each pairs with the matching .goal-card--risk-* border,
   both driven by the server-supplied modifier from goal_confidence.presentation_for_band().
   White bold text on all three solid fills clears 4.5:1. */
.goal-card-banner--warning   { background: var(--color-gold, #E6B800); }
.goal-card-banner--caution   { background: var(--color-warning, #F2994A); }
.goal-card-banner--off-track { background: var(--color-danger, #E25555); }
/* Not a band — see .goal-card--risk-unavailable above. */
.goal-card-banner--unavailable { background: var(--color-text-muted, #5F6368); }
/* Not a band — see .goal-card--risk-assumption-required above. */
.goal-card-banner--assumption-required { background: var(--spring-steel-blue, #3E5C76); }

/* ── Footer: status (left) + "Updated <date>" (right) on one row ───────
   Wraps on very narrow viewports so the row never forces page overflow. */
.goal-card-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  column-gap: var(--space-3);
  row-gap: 4px;
}
.goal-card-updated {
  font-size: 12px;
  color: var(--color-text-muted);
  flex: 0 0 auto;
}

/* ── Status message ──────────────────────────────────────────────────── */
.goal-card-status {
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.goal-card-status--on_track .status-dot,
.goal-card-status--ahead .status-dot {
  background: var(--color-success, #16a34a);
}

.goal-card-status--behind .status-dot {
  background: var(--color-gold, #E6B800);
}

.goal-card-status--needs_attention .status-dot {
  background: var(--color-danger, #E25555);
}

.goal-card-status--new .status-dot {
  background: var(--color-text-muted);
}

/* ── Attention cards ─────────────────────────────────────────────────── */
.attention-card {
  background: var(--color-gold-bg, #fffbeb);
  border: 1px solid var(--color-gold-border, #fde68a);
  border-left: 4px solid var(--color-gold, #E6B800);
  border-radius: 12px;
  padding: 16px 20px;
}

/* ── Account metrics row ─────────────────────────────────────────────── */
.account-metrics-row {
  display: flex;
  gap: var(--space-5);
  flex-wrap: wrap;
}

.account-metric {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

.account-metric-label {
  font-family: 'Geist Mono', monospace;
  font-size: 11px;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.account-metric-value {
  font-size: 18px;
  font-weight: 600;
  color: var(--color-primary);
}

/* ── Your Progress (Plan users, no brokerage) ────────────────────────── */
/* Single, centered Total Value beneath the Goal Allocation Bar. */
.plan-total-value {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  margin: 0 0 var(--space-3);
  text-align: center;
}

/* Financial Plan placeholder card (Workstream C): descriptive text + View Plan
   CTA on the left, document icon on the right (per the design reference). */
/* Row layout: document icon (left) · title/description (fills middle) · View
   Plan (right). "View Plan" is a styled dark-blue placeholder — no [disabled],
   so it renders full brand color (the global .btn:disabled rule dims to 0.6). */
.plan-financial-plan {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}
.plan-financial-plan-icon {
  flex: 0 0 auto;
  color: var(--color-text-muted);
  opacity: 0.6;
}
.plan-financial-plan-copy {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}
.plan-financial-plan-cta {
  flex: 0 0 auto;
}

/* The document block that opens the plan — presented as a clickable list item,
   NOT a button. Fills the row; "Speak with a CFP" is the only explicit button. */
.plan-financial-plan-doc {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex: 1 1 auto;
  min-width: 0;
  text-decoration: none;
  color: inherit;
  padding: var(--space-2) var(--space-3);
  margin-left: calc(-1 * var(--space-3));   /* keep the icon aligned with the section edge */
  border-radius: var(--radius-md);
  transition: background 0.15s ease;
}
a.plan-financial-plan-doc { cursor: pointer; }
/* Subtle list-item highlight on hover — no underline (keep it document-like, not link-y). */
a.plan-financial-plan-doc:hover { background: var(--color-surface-2); text-decoration: none; }
a.plan-financial-plan-doc:focus-visible {
  background: var(--color-surface-2);
  text-decoration: none;
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}
.plan-financial-plan-doc--static { cursor: default; }

/* Right-side action stack (stale/create states) + the standalone CFP button. */
.plan-financial-plan-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: stretch;
  flex: 0 0 auto;
}
.plan-financial-plan-cfp { flex: 0 0 auto; }

/* Simulation results: 4-column metrics grid (no borders/shading) */
.sim-metrics-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4);
  margin-top: var(--space-4);
}
.sim-metrics-grid .account-metric-label {
  text-transform: none;
  font-family: inherit;
  font-size: 13px;
  letter-spacing: normal;
  white-space: nowrap;
}
.sim-metrics-grid .account-metric-value {
  font-size: 15px;
}
@media (max-width: 600px) {
  .sim-metrics-grid { grid-template-columns: repeat(2, 1fr); }
}

/* Simulation results: portfolio input params (no uppercase) */
.sim-portfolio-inputs .account-metric-label {
  text-transform: none;
  font-family: inherit;
  font-size: 13px;
  letter-spacing: normal;
}
.sim-portfolio-inputs .account-metric-value {
  font-size: 15px;
}

.pager-row {
  display: flex;
  align-items: center;
  margin-top: var(--space-3);
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.pager-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

.pager-left {
  display: flex;
  align-items: center;
}

.pager-buttons {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.col-savings-goal { min-width: 300px; }

/* .spring-btn-dark — REMOVED (v0.2 style guide).
   Hover inverted bg/text, making buttons unreadable.
   Use .btn-primary everywhere instead. */

/* goal-card-grid breakpoints removed — now using single-column .goal-card-stack */

.manage-goal-columns {
  display: grid;
  grid-template-columns: 1fr 30%;
  gap: var(--space-4);
  align-items: start;
}

@media (max-width: 900px) {
  .account-layout {
    flex-direction: column;
  }

  .goal-panel,
  .account-overview-panel {
    flex: 0 0 100%;
    max-width: 100%;
    position: static;
  }

  /* goal-card breakpoints removed — single-column layout is inherently responsive */

  .manage-goal-columns {
    grid-template-columns: 1fr;
  }

  .dashboard-column {
    margin: 16px;
    padding: 28px 20px;
    border-radius: 12px;
  }

  .goal-card {
    padding: 20px;
  }
  /* Keep the full-bleed banner flush with the tighter mobile card padding. */
  .goal-card-banner {
    margin: -20px -20px 0 -20px;
    padding: 9px 20px;
  }
}

/* HTMX: loading indicators and swaps */
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline-flex; }
.htmx-request.btn .label { display: none; }
.htmx-request.btn::after { content: "Working…"; }

/* Optional: subtle swap animation */
@keyframes springFadeIn {
  from { opacity: 0; transform: translateY(2px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ── Goal Finder ───────────────────────────────────────────────────────── */

/* Textarea — Step 1 */
.goal-finder-textarea {
  width: 100%;
  box-sizing: border-box;
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: rgba(62, 92, 118, 0.04);
  font-size: 1.05rem;
  line-height: 1.6;
  resize: vertical;
  font-family: inherit;
  color: var(--color-text);
}
.goal-finder-textarea:focus {
  outline: 2px solid var(--color-primary);
  outline-offset: 1px;
  background: rgba(62, 92, 118, 0.07);
}

/* Checkbox tiles — Step 2 */
.checkbox-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  border: 2px solid var(--color-border);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
  position: relative;
  user-select: none;
}
.checkbox-card input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.checkbox-card:has(input:checked) {
  border-color: var(--color-primary);
  background: rgba(62, 92, 118, 0.08);
}
.checkbox-card.suggested {
  border-color: var(--color-secondary);
  background: rgba(62, 92, 118, 0.04);
}
.checkbox-card.suggested:has(input:checked) {
  border-color: var(--color-primary);
  background: rgba(62, 92, 118, 0.10);
}
.checkbox-card__label {
  font-weight: 600;
  font-size: 0.95rem;
}
.checkbox-card__hint {
  font-size: 0.78rem;
  color: var(--color-muted, var(--color-secondary));
}

/* Drag list items — Steps 3 + Review */
.goal-finder-drag-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
}
.goal-finder-drag-item--dim {
  opacity: 0.45;
}
.goal-finder-drag-ghost {
  opacity: 0.4;
  background: var(--color-surface-alt, #f0f0f0);
}
.goal-finder-divider-after {
  border-bottom: 3px solid var(--color-primary);
  margin-bottom: var(--space-2);
  padding-bottom: var(--space-3);
}
.goal-finder-drag-handle {
  cursor: grab;
  color: var(--color-muted, #aaa);
  font-size: 1.1rem;
  flex-shrink: 0;
  padding: 2px 4px;
}
.goal-finder-drag-label {
  flex: 1;
  font-weight: 500;
}
.goal-finder-rank-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  background: var(--color-primary);
  color: #fff;
  font-size: 0.8rem;
  font-weight: 700;
  flex-shrink: 0;
}

/* Remove button — Review */
.goal-finder-remove-btn {
  background: none;
  border: none;
  color: var(--color-muted, #aaa);
  cursor: pointer;
  font-size: 0.85rem;
  padding: 2px 4px;
  flex-shrink: 0;
}
.goal-finder-remove-btn:hover {
  color: var(--color-danger);
}

/* Add-from-pool chips — Review */
.goal-finder-chip {
  padding: var(--space-1) var(--space-3);
  border: 1px dashed var(--color-border);
  border-radius: 2rem;
  background: none;
  cursor: pointer;
  font-size: 0.875rem;
  color: var(--color-primary);
  transition: background 0.1s;
}
.goal-finder-chip:hover:not(:disabled) {
  background: rgba(62, 92, 118, 0.08);
}

/* Quote callout — Review */
.goal-finder-quote {
  padding: var(--space-4) var(--space-5) 0;
  background: rgba(62, 92, 118, 0.05);
  border-radius: var(--radius);
}
.goal-finder-quote__text {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 500;
  line-height: 1.6;
  white-space: pre-wrap;
  color: var(--color-text);
  text-align: center;
}
.goal-finder-quote__mark {
  display: block;
  font-size: 2.5rem;
  line-height: 1;
  color: var(--color-primary);
  font-family: Georgia, serif;
  opacity: 0.5;
}
.goal-finder-quote__mark--open {
  text-align: left;
  margin-bottom: -1.8rem;
}
.goal-finder-quote__mark--close {
  text-align: right;
  margin-top: -0.5rem;
}

/* Legacy rank card (kept for backwards compat) */
.goal-rank-card {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  cursor: pointer;
  user-select: none;
}
.goal-rank-card.selected {
  border-color: var(--color-primary);
  background: rgba(62, 92, 118, 0.08);
}
.rank-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  background: var(--color-primary);
  color: #fff;
  font-size: 0.8rem;
  font-weight: 700;
  flex-shrink: 0;
}
/* ── Landing hero ──────────────────────────────────────────────────────── */

.hero-card {
  max-width: 640px;
  margin: 40px auto 32px;
  padding: 44px 48px;
  text-align: center;
}

.hero-primary {
  padding: 18px 40px;
  font-size: 1.25rem;
  border-radius: 14px;
  min-width: 300px;
}

.hero-secondary {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 24px;
}

.login-link {
  font-size: 13px;
  font-weight: 450;
  color: var(--color-text-muted);
  text-decoration: none;
}

.login-link:hover {
  text-decoration: underline;
  color: var(--color-secondary);
}

.htmx-settling { animation: springFadeIn 140ms ease-out; }

/* ── Manage Goal page ─────────────────────────────────────────────────────── */

.back-link {
  font-family: 'Geist Mono', monospace;
  font-size: 12px;
  font-weight: 400;
  color: var(--color-secondary);
  text-decoration: none;
}
.back-link:hover { color: var(--color-primary); }

.manage-goal-header {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 16px;
}
.manage-goal-header-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/* Plan section — label-value rows with live form inputs */
.plan-fields {
  display: flex;
  flex-direction: column;
}
.plan-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 14px 0;
  border-bottom: 1px solid #f0f0f4;
}
.plan-row:last-child { border-bottom: none; }
.plan-label {
  font-size: 14px;
  color: var(--color-text-muted);
}
.field-hint {
  display: block;
  font-size: 12px;
  font-weight: 400;
  color: var(--color-text-muted);
  margin-top: 2px;
  line-height: 1.3;
}
/* Inline-editable text inputs (two-state pattern) */
.plan-row input[type="text"] {
  text-align: right;
  font-weight: 600;
  font-size: 16px;
  color: var(--color-secondary);
  border: none;
  border-bottom: 1.5px dotted var(--color-secondary);
  background: rgba(62, 92, 118, 0.09);
  border-radius: 3px;
  padding: 2px 6px;
  max-width: 160px;
  box-shadow: none;
}
.plan-row input[type="text"]:focus {
  border-bottom: 1.5px solid var(--color-secondary);
  outline: 2px solid var(--color-secondary);
  outline-offset: 1px;
  background: var(--color-surface);
  box-shadow: none;
}
/* Standard textbox override — opt out of inline-editable plan-row styling */
.plan-row input[type="text"].standard-input,
.plan-row .plan-input input[type="text"].standard-input,
.plan-row .input-with-prefix input[type="text"].standard-input {
  text-align: left;
  font-weight: 400;
  font-size: 14px;
  color: var(--color-text);
  border: 1px solid var(--color-border) !important;
  background: var(--color-surface) !important;
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  padding-left: 26px;
  max-width: none;
  box-shadow: none;
}
.plan-row input[type="text"].standard-input:focus,
.plan-row .plan-input input[type="text"].standard-input:focus,
.plan-row .input-with-prefix input[type="text"].standard-input:focus {
  border: 1px solid var(--color-primary) !important;
  outline: none;
  box-shadow: var(--focus-ring);
  background: var(--color-surface) !important;
}
/* Standard drop-list selects */
.plan-row select {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text-muted);
  background: transparent;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 6px 30px 6px 10px;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' stroke='%23888' stroke-width='1.5' fill='none' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 12px 8px;
  box-shadow: none;
}
.plan-row select:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: var(--focus-ring);
}
.schedule-selects {
  display: flex;
  gap: 8px;
  align-items: baseline;
}
.plan-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: var(--space-3);
}

/* Disclosures for Forecast / Portfolio & Orders */
.manage-goal-disclosure {
  border-top: 1px solid #f0f0f4;
}
.manage-goal-disclosure summary {
  font-family: 'Geist Mono', monospace;
  font-size: 12px;
  font-weight: 400;
  color: var(--color-secondary);
  cursor: pointer;
  padding: 14px 0;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
}
.manage-goal-disclosure summary::-webkit-details-marker { display: none; }
.manage-goal-disclosure summary::before {
  content: "\2192";
  font-size: 12px;
  transition: transform 0.15s ease;
}
.manage-goal-disclosure[open] summary::before {
  transform: rotate(90deg);
}

/* Allocation explanation — status indicators */
.alloc-status {
  font-size: 0.75rem;
  font-weight: 500;
  padding: 1px 6px;
  border-radius: 4px;
  white-space: nowrap;
}
.alloc-status--ontarget    { background: rgba(22, 163, 74, 0.10); color: var(--color-success); }
.alloc-status--underweight { background: rgba(242, 153, 74, 0.12); color: var(--color-warning); }
.alloc-status--overweight  { background: rgba(242, 153, 74, 0.12); color: var(--color-warning); }

/* ── Staff tool page tabs ─────────────────────────────────────────────── */
.staff-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  border-bottom: 2px solid var(--color-border);
  margin-bottom: var(--space-3);
}
.staff-tab {
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 500;
  color: var(--color-text-muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  transition: color 0.15s, border-color 0.15s;
}
.staff-tab:hover { color: var(--color-text); }
.staff-tab.active {
  color: var(--color-primary);
  border-bottom-color: var(--color-primary);
}

/* ── Simulator comparison grid ───────────────────────────────────────── */
.sim-compare-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
  gap: var(--space-5);
  align-items: start;
}
.sim-compare-grid.single {
  grid-template-columns: 1fr;
  max-width: 720px;
}
.sim-compare-grid .table.compact th,
.sim-compare-grid .table.compact td {
  font-size: 12px;
}

/* ── Other Tools: period toggle ───────────────────────────────────────── */
.period-toggle {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: var(--space-3);
}
.period-btn {
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 500;
  font-family: 'Geist Mono', monospace;
  color: var(--color-text-muted);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  transition: background 0.15s, color 0.15s;
}
.period-btn:hover { background: var(--color-surface-2); color: var(--color-text); }
.period-btn.active {
  background: var(--color-primary);
  color: #fff;
  border-color: var(--color-primary);
}

/* ── Other Tools: heatmap grid ────────────────────────────────────────── */
.heatmap-wrapper {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-width: 480px;
}
.heatmap-col-headers {
  display: grid;
  grid-template-columns: 60px repeat(3, 1fr);
  gap: 2px;
}
.heatmap-corner { }
.heatmap-col-label {
  text-align: center;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--color-text-muted);
  padding: 4px 0;
}
.heatmap-row {
  display: grid;
  grid-template-columns: 60px repeat(3, 1fr);
  gap: 2px;
}
.heatmap-row-label {
  display: flex;
  align-items: center;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--color-text-muted);
  padding-right: 8px;
}
.heatmap-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 16px 8px;
  border-radius: 6px;
  text-align: center;
  min-height: 72px;
}
.heatmap-cell-return {
  font-size: 18px;
  font-weight: 700;
  font-family: 'Geist Mono', monospace;
  line-height: 1.2;
}
.heatmap-cell-price {
  font-size: 11px;
  font-weight: 500;
  font-family: 'Geist Mono', monospace;
  opacity: 0.85;
  margin-top: 1px;
}
.heatmap-cell-ticker {
  font-size: 12px;
  font-weight: 600;
  opacity: 0.95;
  margin-top: 2px;
  letter-spacing: 0.02em;
}

/* ── Other Tools: world map ───────────────────────────────────────────── */
.world-map-wrapper {
  max-width: 700px;
  margin: 0;
}
.world-map-wrapper:fullscreen,
.world-map-wrapper:-webkit-full-screen {
  max-width: 100%;
  background: #1a1f2e;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.world-map-wrapper:fullscreen svg,
.world-map-wrapper:-webkit-full-screen svg {
  background: transparent;
  max-height: 85vh;
  width: auto;
  height: auto;
}
.world-map-fullscreen-legend {
  display: none;
}
.world-map-wrapper:fullscreen .world-map-fullscreen-legend,
.world-map-wrapper:-webkit-full-screen .world-map-fullscreen-legend {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
  font-size: 12px;
  color: #aaa;
}
.world-map-wrapper svg {
  width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
  background: #e8edf2;
}
.world-map-country {
  cursor: pointer;
  stroke: #fff;
  stroke-width: 0.3;
  transition: opacity 0.15s;
}
.world-map-country:hover {
  opacity: 0.75;
  stroke-width: 0.8;
}
.world-map-legend {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: var(--space-2);
  font-size: 11px;
  color: var(--color-text-muted);
}
.world-map-legend-bar {
  width: 200px;
  height: 10px;
  border-radius: 4px;
  background: linear-gradient(to right, #dc2626, #ffffff, #16a34a);
  border: 1px solid var(--color-border);
}

/* ── Two-state inline editable: Goal Name (display state) ──────────────── */
.goal-name-inline {
  font-weight: 700;
  font-size: inherit;
  color: var(--color-secondary);
  background: rgba(62, 92, 118, 0.09);
  padding: 1px 7px;
  border-radius: 5px;
  border-bottom: 2px dotted var(--color-secondary);
  cursor: pointer;
  min-width: 2em;
  display: inline-block;
}

.goal-name-inline:hover {
  background: rgba(62, 92, 118, 0.16);
}

/* Goal name — injected input (edit state) */
.goal-name-input {
  font-weight: 700;
  font-size: inherit;
  color: var(--color-secondary);
  background: var(--color-surface);
  border: none;
  border-bottom: 2px solid var(--color-secondary);
  border-radius: 3px;
  outline: 2px solid var(--color-secondary);
  outline-offset: 1px;
  padding: 0 2px;
  font-family: inherit;
  width: 100%;
  box-sizing: border-box;
}

/* ── Goal-type picker (inline dropdown in heading) ─────────────────────── */
.goal-type-picker {
  font-weight: 700;
  font-size: inherit;
  font-family: inherit;
  color: var(--color-secondary);
  background: rgba(62, 92, 118, 0.09);
  border: 1px solid var(--color-border);
  border-radius: 5px;
  padding: 1px 28px 1px 7px;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' stroke='%233E5C76' stroke-width='1.5' fill='none' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 12px 8px;
  vertical-align: baseline;
}
.goal-type-picker:focus-visible {
  outline: 2px solid var(--color-secondary);
  outline-offset: 1px;
  box-shadow: none;
}

/* ── Two-state inline editable: Goal Notes (display state) ─────────────── */
.goal-notes-inline {
  width: fit-content;
  max-width: 50%;
  border-bottom: 2px dotted var(--color-secondary);
  background: rgba(62, 92, 118, 0.05);
  padding: var(--space-1) var(--space-2);
  border-radius: 4px 4px 0 0;
  font-family: inherit;
  font-size: 0.9rem;
  color: var(--color-text-muted);
  min-height: 1.4em;
  cursor: pointer;
  white-space: pre-wrap;
  word-break: break-word;
}
/* Expand to full 50% width when editing so textarea has room */
.goal-notes-inline[data-editing] {
  width: 50%;
  border-bottom: none;
  background: none;
}

.goal-notes-inline:hover {
  background: rgba(62, 92, 118, 0.10);
}

.goal-notes-placeholder {
  opacity: 0.6;
  font-style: italic;
}

/* Goal notes — injected textarea (edit state) */
.goal-notes-input {
  display: block;
  width: 100%;
  border: none;
  border-bottom: 2px solid var(--color-secondary);
  background: var(--color-surface);
  padding: var(--space-1) var(--space-2);
  border-radius: 4px 4px 0 0;
  font-family: inherit;
  font-size: 0.9rem;
  color: var(--color-text);
  resize: vertical;
  min-height: 2.5rem;
  outline: 2px solid var(--color-secondary);
  outline-offset: 1px;
  box-sizing: border-box;
}

/* Toggle switch */
.toggle-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  user-select: none;
  font-size: 0.9rem;
}

.toggle-switch input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 38px;
  height: 22px;
  border-radius: 11px;
  background: var(--color-border);
  position: relative;
  cursor: pointer;
  transition: background 0.2s;
  flex-shrink: 0;
}

.toggle-switch input[type="checkbox"]:checked {
  background: var(--color-primary);
}

.toggle-switch input[type="checkbox"]::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: white;
  transition: left 0.2s;
  box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}

.toggle-switch input[type="checkbox"]:checked::after {
  left: 19px;
}

.toggle-label-text {
  color: var(--color-text-muted);
  font-weight: 500;
}

/* Order details link */
.details-link {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--color-primary);
  font-size: inherit;
  text-decoration: underline;
  text-decoration-style: double;
  text-underline-offset: 3px;
}

.details-link:hover {
  color: var(--color-secondary);
}

.attention-card--simulated {
  border-left: 4px solid var(--spring-alloc-orange, #F2994A);
  background: #fef3c7;
}

/* ══════════════════════════════════════════════════════════════════════════
   Goal Wizard (type-specific question flow)
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Transition screen ── */
.transition-screen { text-align: center; padding: 60px 28px; }
.transition-screen .icon { font-size: 48px; margin-bottom: 16px; }
.transition-screen h2 { font-size: 22px; font-weight: 600; color: var(--color-primary); margin-bottom: 8px; }
.transition-screen p { font-size: 15px; color: var(--color-text-muted); margin-bottom: 32px; }

/* ── Goal type selector grid ── */
.goal-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin: 20px 0; }
.goal-tile {
  padding: 16px; border: 1px solid var(--color-border); border-radius: var(--radius-sm);
  text-align: center; cursor: pointer; transition: all 0.15s; background: rgba(62, 92, 118, 0.04);
  appearance: none; -webkit-appearance: none;
  color: var(--color-text); -webkit-text-fill-color: currentColor;
}
.goal-tile:hover { border-color: var(--color-secondary); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.goal-tile:focus-visible { outline: 3px solid var(--color-secondary); outline-offset: 2px; }
.goal-tile:active { background: rgba(62, 92, 118, 0.12); transform: translateY(0); }
.goal-tile:disabled { background: var(--color-surface-2); color: var(--color-text-muted); cursor: not-allowed; }
.goal-tile .emoji { font-size: 28px; margin-bottom: 6px; }
.goal-tile .name { font-size: 14px; font-weight: 500; color: inherit; -webkit-text-fill-color: currentColor; }

/* Reusable goal-type grid (UI Polish Sprint 2 · Flight 1A; hardened #236 · Flight P1):
   equal-size 156px tiles in a centered, wrapping HORIZONTAL row. Each tile is wrapped
   in its own POST <form> — those forms are the flex items (.goal-type-cell), so the
   layout no longer depends on `display:contents` promoting the button across the flex
   container (the fragile path that rendered the tiles stacked vertically). Shared by
   the landing hero and the goal selector; the selector caps the row at three (below). */
.goal-type-grid {
  display: flex; flex-flow: row wrap; justify-content: center; align-items: stretch;
  gap: 12px; margin: 20px 0 0;
}
.goal-type-grid .goal-type-cell { display: flex; flex: 0 0 156px; margin: 0; }
.goal-type-grid .goal-tile { flex: 1 1 auto; box-sizing: border-box; }

/* Goal Selector page (UI Polish Sprint 2 · Flight 5). Uses the standard
   .dashboard-column app surface (one card, no nesting — this also satisfies the
   content-surface visual-QA gate) and opts <main> into full width so the column
   reaches its max-width and centers instead of shrink-wrapping and stacking the
   tiles (see the goals-dashboard-wide note). Heading, description, and the shared
   goal-type grid render borderless inside it, mirroring the landing hero. The grid
   is capped to three tiles per row and centered, so a 4th wraps to a centered
   second row even in the wide column. */
main.goal-select-page { width: 100%; box-sizing: border-box; }
.goal-select__intro { text-align: center; }
.goal-select__title { font-size: 2rem; margin: 0 0 8px; }
.goal-select__desc { margin: 0 auto; max-width: 560px; color: var(--color-text-muted); }
/* width:100% gives the grid a DEFINITE width (#236 · Flight P1). Without it, this
   flex container — which is ALSO a flex item of the flex-column .dashboard-column,
   with auto cross-margins — shrink-wraps in WebKit/Safari to a single narrow column
   (tiles stacked vertically; Chromium instead used max-content, one row — which is
   why a Chromium-only repro missed it). A definite width makes both engines lay out
   three 156px columns; max-width caps at three, margin centers. Confirmed in
   Playwright WebKit. */
.goal-select .goal-type-grid { width: 100%; max-width: 504px; margin: 0 auto; }

/* ── Goal header (emoji + name + counter) ── */
.goal-header {
  display: flex; align-items: center; gap: 10px; margin-bottom: 20px;
  padding-bottom: 16px; border-bottom: 1px solid var(--color-border);
}
.goal-header .emoji { font-size: 28px; }
.goal-header .meta { flex: 1; }
.goal-header .goal-label {
  font-family: var(--font-mono); font-size: 11px;
  color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.5px;
}
.goal-header .goal-name { font-size: 18px; font-weight: 600; color: var(--color-primary); }
.goal-header .goal-counter {
  font-family: var(--font-mono); font-size: 11px;
  color: var(--color-text-muted); text-transform: uppercase;
}

/* ── Step indicator (progress dots) ── */
.step-indicator { display: flex; align-items: center; gap: 6px; margin-bottom: 20px; }
.step-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--color-border); transition: background 0.3s;
}
.step-dot.active { background: var(--color-primary); }
.step-dot.done { background: var(--color-success); }
.step-count { font-size: 12px; color: var(--color-text-muted); margin-left: 8px; font-family: var(--font-mono); }

/* ── Question step spacing (compacted for launch — #236 · Flight P1) ── */
.question-step { margin-bottom: 16px; }

/* ── Question typography ── */
.question-title { font-size: 15px; font-weight: 600; color: var(--color-primary); margin-bottom: 4px; }
.question-hint { font-size: 13px; color: var(--color-text-muted); margin-bottom: 12px; line-height: 1.5; }

/* ── Hide number input spinners globally ── */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; }

/* ── Input layout helpers ── */
.input-row { display: flex; gap: 12px; }
.input-row > * { flex: 1; }
.input-group { margin-bottom: 0; }
/* Dollar input + a secondary action button (e.g. TuitionTracker lookup) on one row: the
   button sits to the right of the input and wraps below it on narrow screens. */
.input-group--with-action { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.input-label { font-size: 13px; font-weight: 500; color: var(--color-text); margin-bottom: 4px; display: block; }

/* ── Contextual input widths ── */
.input-w-dollar { width: 160px !important; }
.input-w-percent { width: 90px !important; }
.input-w-number { width: 100px !important; }
.input-w-year { width: 180px !important; }
/* ── Inline sentence (retirement timeline) ── */
.inline-sentence {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  font-size: 16px; font-weight: 500; color: var(--color-text);
  line-height: 2.4;
}
.inline-sentence .inline-label { white-space: nowrap; }
.inline-sentence .inline-editable-input {
  width: 56px; text-align: center; font-size: 16px;
  padding: 2px 4px;
}

/* ── Currency / Percent input wrappers ── */
.input-with-prefix, .input-with-suffix {
  position: relative; display: flex; align-items: center;
}
.input-with-prefix .input-symbol {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  font-size: 15px; color: var(--color-text-muted); pointer-events: none; z-index: 1;
}
/* border-box so width:100% stays INSIDE the fixed-width wrapper (.input-w-dollar 160px).
   There is no global box-sizing reset, so a content-box input would render ~40px past the
   wrapper (padding+border) and overlap an adjacent action button (TuitionTracker, #236). */
.input-with-prefix input { padding-left: 26px; width: 100%; box-sizing: border-box; }
.input-with-suffix .input-symbol {
  position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
  font-size: 15px; color: var(--color-text-muted); pointer-events: none; z-index: 1;
}
.input-with-suffix input { padding-right: 30px; width: 100%; box-sizing: border-box; }

/* ── Radio / option group ── */
/* Equal-width option pills sized to the WIDEST option in the question (not each
   option's own content width, and not the full card width): width:fit-content
   shrinks the group to its widest child, and align-items:stretch makes every
   pill that width (#236 · Flight P1). Tighter height + smaller indicator so
   radios match the chip controls (Flight 3). */
.option-group { display: flex; flex-direction: column; align-items: stretch; width: fit-content; gap: 8px; margin-bottom: 16px; }
.option-btn {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px; border: 1px solid var(--color-border);
  border-radius: var(--radius-sm); background: rgba(62, 92, 118, 0.04);
  cursor: pointer; font-size: 14px; transition: all 0.15s;
}
.option-btn:hover { border-color: var(--color-secondary); background: rgba(62, 92, 118, 0.07); }
.option-btn.selected { border-color: var(--color-primary); background: rgba(62, 92, 118, 0.10); font-weight: 500; }
.option-btn .dot {
  width: 8px; height: 8px; border-radius: 50%;
  border: 2px solid var(--color-border); flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; transition: all 0.15s;
}
.option-btn.selected .dot { border-color: var(--color-primary); background: var(--color-primary); }
.option-btn.selected .dot::after { content: ''; width: 4px; height: 4px; border-radius: 50%; background: #fff; }

/* ── Suggestion chips ── */
.suggestion-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.chip {
  padding: 6px 14px; border-radius: 20px; font-size: 13px;
  border: 1px solid var(--color-border); background: rgba(62, 92, 118, 0.04);
  cursor: pointer; transition: all 0.15s; color: var(--color-text-muted);
}
.chip:hover { border-color: var(--color-secondary); color: var(--color-text); }
.chip.selected {
  border-color: var(--color-primary); background: rgba(62, 92, 118, 0.10);
  color: var(--color-text); font-weight: 500;
}
/* Preset chips + custom input on one horizontal row (education wizard compound
   controls, Flight 3): presets and a custom value read as two ways of answering
   the same question. */
.chip-input-row { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; }
.input-w-state { width: 220px !important; }

/* ── Info box ── */
.info-box {
  background: #f0f7ff; border: 1px solid #c5ddf5; border-radius: var(--radius-sm);
  padding: 12px 16px; font-size: 13px; color: var(--color-text); line-height: 1.5; margin-bottom: 16px;
}
.info-box a { color: var(--color-secondary); }

/* ── Breakdown table ── */
.breakdown { margin: 16px 0; }
.breakdown-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 0; font-size: 14px; color: var(--color-text);
}
.breakdown-row.total {
  border-top: 2px solid var(--color-primary); margin-top: 8px;
  padding-top: 12px; font-weight: 600; font-size: 16px;
}
.breakdown-row .label { color: var(--color-text-muted); flex: 1; }
.breakdown-row.total .label { color: var(--color-primary); }
.breakdown-row .value { text-align: right; min-width: 100px; }
.breakdown-note { font-size: 12px; color: var(--color-text-muted); margin-top: 8px; font-style: italic; }
.breakdown-pct-edit {
  display: inline-block;
  border-bottom: 1.5px dotted var(--color-secondary);
  background: rgba(62, 92, 118, 0.09);
  font-weight: 600; color: var(--color-secondary);
  padding: 2px 6px; border-radius: 3px;
  cursor: pointer; transition: background 0.15s;
  margin-left: 4px;
}
.breakdown-pct-edit:hover { background: rgba(62, 92, 118, 0.16); }
.breakdown-dollar-display { font-weight: 400; }
.btn-reset {
  font-size: 11px; color: var(--color-text-muted); background: none; border: 1px solid var(--color-border);
  border-radius: 4px; padding: 1px 6px; margin-left: 6px; cursor: pointer; vertical-align: middle;
}
.btn-reset:hover { color: var(--color-primary); border-color: var(--color-secondary); }

/* ── Inline editable (two-state pattern) ── */
.inline-editable-display {
  display: inline-block;
  border-bottom: 1.5px dotted var(--color-secondary);
  background: rgba(62, 92, 118, 0.09);
  font-weight: 600; color: var(--color-secondary);
  padding: 2px 6px; border-radius: 3px;
  cursor: pointer; transition: background 0.15s;
}
.inline-editable-display:hover { background: rgba(62, 92, 118, 0.16); }

/* A DERIVED forecast value: same weight/colour as an editable one so the sentence reads
   consistently, but deliberately WITHOUT the dotted underline, chip background and pointer
   cursor that style-guide §407 reserves for tappable fields — signalling it is not directly
   editable (#255: the Savings target is changed via the questionnaire or Increase/Reduce). */
.forecast-static-value {
  display: inline-block;
  font-weight: 600;
  color: var(--color-secondary);
}
.inline-editable-input {
  border: 1px solid var(--color-border); border-radius: 5px;
  font-size: inherit; font-family: inherit; font-weight: 600;
  color: var(--color-secondary); background: rgba(62, 92, 118, 0.09);
  padding: 2px 6px; max-width: 140px;
}
.inline-editable-input:focus {
  outline: 2px solid var(--color-primary); outline-offset: 1px;
  background: rgba(62, 92, 118, 0.07);
}
/* Enrollment-year field ("Starts college in ____", Step 1) is a permanently-
   visible inline-editable value. The global input[type="number"] rule (0,1,1)
   outranks the .inline-editable-input class (0,1,0) and would repaint it as a
   plain boxed field (10px padding, surface bg, plain text) — the same trap
   `.plan-row input[type="text"]` solves. This context selector (0,2,0) restates
   the inline-edit DISPLAY treatment (dotted underline, tinted bg, secondary
   text, semibold) at a specificity that wins; a 4-digit year → narrow, centered. */
.child-age-start .inline-editable-input {
  border: none;
  border-bottom: 1.5px dotted var(--color-secondary);
  border-radius: 3px;
  background: rgba(62, 92, 118, 0.09);
  color: var(--color-secondary);
  font-weight: 600;
  padding: 2px 6px;
  width: 64px;
  text-align: center;
  box-shadow: none;
}
.child-age-start .inline-editable-input:focus {
  border-bottom: 1.5px solid var(--color-secondary);
  outline: 2px solid var(--color-secondary);
  outline-offset: 1px;
  background: var(--color-surface);
  box-shadow: none;
}

/* ── Forecast layout ── */
.forecast-layout {
  /* margin-bottom tightened 16 -> 4 (#236 · Flight P1): pull the lower section
     (contributions / confidence / adjust) up closer to the chart. */
  display: flex; gap: var(--space-4); align-items: flex-start; margin-bottom: 4px;
}
.forecast-narrative {
  flex: 0 0 35%; display: flex; flex-direction: column; gap: 4px;
}
.forecast-narrative .narrative-text {
  font-size: 0.84rem; color: var(--color-text); line-height: 1.6;
}
.forecast-chart-col { flex: 1; min-width: 0; }
.forecast-chart-col svg { width: 100%; height: auto; }
/* §4 — Education owns one full-width forecast module: a narrative lead (with the single assumption
   note inline) above a chart the scenario values live ON at each curve endpoint. The left duplicate
   value column was removed and its width reclaimed. Non-Education keeps the two-column layout above. */
/* Small vertical tightening (education only): the toggles row above this module — after the "Based
   on your savings target…" intro, or after the Funding plan row on an Education Plan Forecast, whose
   intro sits in the chart's heading row instead (#297) — is empty for a normal Education user (the
   display-mode toggle is hidden for the pool chart; tax toggles are staff-only) but keeps a 12px
   bottom margin. Pull the module up modestly to close that gap without touching the shared row. */
.forecast-layout--education { display: block; margin-top: -8px; }
.forecast-layout--education .narrative-text {
  font-size: 0.84rem; color: var(--color-text); line-height: 1.6; margin: 0 0 6px;
}
.forecast-chart-col--education { width: 100%; }
/* §1 — the renderer sizes the SVG itself (viewBox + horizon-scaled max-width, left-aligned), so the
   chart fills the card on long timelines and stays compact on short ones; drop the 320px cap. */
.forecast-chart-col--education .forecast-chart { max-height: none; }
/* Narrow viewports — READABILITY, not layout. The SVG is width:100% over a ~850–1110-unit viewBox, so
   the column's width IS the chart's type scale: at a 375px viewport (≈303px of column) the whole chart
   renders at ~0.27x and its labels land near 3.5px. Raising font sizes cannot fix that — the ratio is
   the cause, and inflating type would distort the desktop chart, which is already correct.
   So: below the breakpoint, pin the chart to the width it ALREADY renders at on desktop
   (.dashboard-column's 920px max-width − 2 × 40px padding = 840px) and let the column scroll it. Every
   label is then exactly as legible as on desktop, and the timeline keeps its geometry, scales, and
   proportions untouched. 900px matches the breakpoint .dashboard-column already re-pads at, and above
   it NONE of this applies — the desktop chart is byte-identical. Scoped to the Education pool chart
   (education-pool-chart); the non-Education forecast chart is a different renderer.
   Modern browsers make overflow containers keyboard-focusable natively, and the SVG carries its own
   role="img" + aria-label, so the scroll region needs no extra tabindex. */
@media (max-width: 900px) {
  .forecast-chart-col--education {
    overflow-x: auto;
    overscroll-behavior-x: contain;   /* keep horizontal panning from triggering page/history swipe */
  }
  .forecast-chart-col--education .education-pool-chart { min-width: 840px; }
}
/* §2 — Education's two contribution controls share one responsive row (wraps on narrow screens). */
.contribution-row {
  display: flex; flex-wrap: wrap; gap: 10px 32px; align-items: baseline; margin: 14px 0 16px;
}
.contribution-item { display: flex; align-items: baseline; gap: 8px; }
.contribution-item-label { font-size: 14px; font-weight: 500; }
/* §1/§2 — the single Education assumption badge, identical on the Breakdown and Forecast pages. */
.assumption-badge {
  display: flex; gap: 10px; align-items: flex-start; max-width: 760px; margin: 16px 0 0;
  padding: 12px 14px; border-radius: 10px;
  background: rgba(62, 92, 118, 0.06); border: 1px solid rgba(62, 92, 118, 0.16);
}
.assumption-badge-icon {
  flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%;
  background: var(--spring-steel-blue); color: #fff;
  font-family: Georgia, "Times New Roman", serif; font-style: italic; font-weight: 700;
  font-size: 12px; line-height: 18px; text-align: center;
}
.assumption-badge-text { font-size: 12.5px; color: var(--color-text); line-height: 1.5; }
.assumption-badge-text strong { color: var(--color-primary); font-weight: 600; }
/* §2 — tighter lead-in for the Education forecast (the 529 note now lives in the badge below). */
.forecast-intro--education-tight { margin-bottom: 4px; }

/* ── #297: required expected annual return (Education Plan Forecast only) ─────────────────
   Reference above, field below. The reference borrows the assumption-badge's steel-blue wash so
   it reads as context, not as a recommendation or a warning. The field is the style guide's
   STANDARD input with a "%" suffix (.input-with-suffix) — the user must provide the value, so it
   is never an inline-editable chip. The row wraps on narrow screens and the button keeps its
   natural width. The figure is the one number in the reference: bold and a step larger than
   the sentence around it. Its "?" help is a POPOVER placed above the "?" (see the help rules
   below). The box is its frame: the panel spans the box's width and floats, so opening it
   never resizes the box. */
.return-assumption { max-width: 760px; margin: 8px 0 18px; }
.return-assumption-reference {
  position: relative;
  padding: 12px 14px; border-radius: 10px;
  background: rgba(62, 92, 118, 0.06); border: 1px solid rgba(62, 92, 118, 0.16);
  font-size: 13px; line-height: 1.55; color: var(--color-text);
}
.return-assumption-reference p { margin: 0; }
.return-assumption-reference strong { color: var(--color-primary); font-weight: 600; }
.return-assumption-reference .return-reference-figure { font-size: 16px; font-weight: 700; white-space: nowrap; }
.return-assumption-reference .help-pop { vertical-align: middle; margin-left: 4px; }
/* The figure, its full stop and the "?" wrap as one unit (#307): the sentence is long enough
   that the "?" could otherwise be left alone on a line of its own. The panel is inside that
   unit, so it is told to wrap its own text normally. */
.return-assumption-reference .return-reference-end { white-space: nowrap; }
.return-assumption-reference .help-pop-panel { white-space: normal; }
/* The entry row is compact (#307): the label, the field and "Show forecast" on ONE line, sized
   to sit with the page's other controls — the label is the Funding plan row's (13px, the same
   160px column, so the field lines up with that row's buttons), and the field and button are
   32px tall, not the 40px of a form field and a navigation button. The field keeps its border
   and "%" suffix, so it still reads as something to type in. On a narrow screen the row wraps
   cleanly: the label onto its own line, the field and its button together on the next
   (.return-assumption-field keeps the pair in one piece). */
.return-assumption-entry { margin-top: 12px; }
.return-assumption-controls { display: flex; flex-wrap: wrap; gap: 6px 4px; align-items: center; }
.return-assumption-label { min-width: 160px; font-size: 13px; font-weight: 500; color: var(--color-text); }
.return-assumption-field { display: inline-flex; align-items: center; gap: 8px; }
.return-assumption-input { display: inline-flex; width: 76px; }
.return-assumption-input input {
  padding: 5px 24px 5px 9px; font-size: 14px; line-height: 20px; border-radius: 6px;
}
.return-assumption-input .input-symbol { right: 9px; font-size: 13px; }
.return-assumption-field .btn { padding: 5px 12px; font-size: 13px; line-height: 20px; white-space: nowrap; }
.return-assumption-controls input[aria-invalid="true"] { border-color: var(--color-danger, #E25555); }
.return-assumption-status { font-size: 12.5px; color: var(--color-text-muted); line-height: 1.5; margin: 8px 0 0; }
/* Empty while the field matches the confirmed rate: present (it is a live region) but taking no
   space. */
.return-assumption-status:empty { margin: 0; }
.return-assumption-error { font-size: 13px; font-weight: 600; color: var(--color-danger, #E25555); margin: 8px 0 0; }
.return-assumption-pending {
  max-width: 760px; margin: 0 0 8px; padding: 18px 16px; border-radius: 10px;
  border: 1px dashed var(--color-border); color: var(--color-text-muted);
  font-size: 14px; text-align: center;
}
.return-scenario-note { max-width: 760px; margin: 10px 0 0; font-size: 12.5px; color: var(--color-text-muted); line-height: 1.5; }
.return-scenario-note strong { color: var(--color-text); font-weight: 600; }

/* ── Chart heading + help (#297) ─────────────────────────────────────────────────────────
   A "?" button with a short explanation in a POPOVER (.help-pop--hover), drawn over what is
   around it. Opens on hover (where the device really has hover), on keyboard focus and on
   tap; closes on Escape (also when it is open by hover alone), a second tap, or a press
   elsewhere. Positioned against a frame — the nearest positioned ancestor: the chart's
   narrative paragraph, or the historical-context box — not against the button, so it spans
   that frame's width and cannot push the page wider than a phone's viewport. It floats:
   opening it never changes the size of anything.

   Two placements. In a frame marked data-help-frame, goal_wizard.js places the panel each time
   it may open, against the "?" itself rather than the frame's edge:
     (default)          just below the "?" — the chart's, where what it covers is the top of the
                        chart. Without the script it opens below the whole paragraph.
     .help-pop--above   above the "?" — the figure's, which sits directly over the required
                        field; with no room above in the viewport it opens below instead
                        (data-help-placement="below") and covers the field until the pointer or
                        focus moves away. */
/* The Education Plan chart's heading row carries the heading and the savings-target sentence
   (#297), the sentence to the right of the heading where there is room; where there is not, the
   row wraps and the sentence takes its own line (and wraps within it) rather than widening the
   page. The chart's help is not in this row: it ends the narrative sentence below (#307). */
.chart-heading {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px;
  max-width: 760px; margin: 14px 0 6px;
}
.chart-heading .section-label { margin: 0; }
/* The sentence keeps .forecast-intro's type; only its box changes in the row. */
.chart-heading .forecast-intro--in-heading { margin: 0; min-width: 0; }
/* The narrative (#307): the sentence, then the chart's "?" immediately after it. The rate, its
   full stop and the "?" are one no-wrap unit (.chart-narrative-end), so wherever the sentence
   wraps the "?" stays with the rate — never alone on a line, never out in a gutter. The
   paragraph is the help's frame: the panel spans its width (the type's own 760px measure; the
   whole paragraph on a phone) and opens just under the "?", floating over the top of the chart
   while it is open. The paragraph sits outside the column that scrolls the chart sideways on a
   narrow screen, so the "?" stays on screen whatever the chart is scrolled to. The panel is
   inside the no-wrap unit, so it is told to wrap its own text normally. */
.chart-narrative { position: relative; }
.chart-narrative .chart-narrative-end { white-space: nowrap; }
.chart-narrative .help-pop { vertical-align: middle; margin-left: 4px; }
.chart-narrative .help-pop-panel { white-space: normal; }
.help-pop { display: inline-flex; }
.help-pop-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0; border-radius: 50%;
  border: 1px solid var(--color-border); background: #fff; color: var(--spring-steel-blue);
  font: inherit; font-size: 13px; font-weight: 700; line-height: 1; cursor: pointer;
}
.help-pop-btn:hover { border-color: var(--color-secondary); }
.help-pop-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.help-pop-panel {
  display: none; position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 20;
  padding: 10px 12px; border: 1px solid var(--spring-steel-blue); border-radius: var(--radius-sm);
  background: #fff; box-shadow: 0 4px 14px rgba(13, 27, 42, 0.14);
  font-size: 13px; font-weight: 400; line-height: 1.5; color: var(--color-text);
  letter-spacing: normal; text-transform: none; text-align: left;
}
/* An invisible bridge over the gap, so moving the pointer from the button to the text does not
   close it (the text stays readable and selectable). goal_wizard.js opens the panel 6px from
   the "?" itself — below it (the chart's, ending the narrative) or above it (.help-pop--above)
   — so 8px spans the gap. Without the script the chart's opens 6px under its whole paragraph. */
.help-pop--hover .help-pop-panel::before { content: ""; position: absolute; left: 0; right: 0; top: -8px; height: 8px; }
/* Activated (tap, click, Enter). */
.help-pop.is-open .help-pop-panel { display: block; }
/* Keyboard focus shows it… */
.help-pop--hover:focus-within .help-pop-panel { display: block; }
/* …and hover, only where hover is a real capability. A touch browser keeps :hover on the last
   element tapped ("sticky hover" — iOS Safari), which would hold the panel open after the user
   had tapped away. On such a device the panel is opened by the tap alone (.is-open). */
@media (hover: hover) {
  .help-pop--hover:hover .help-pop-panel { display: block; }
}
/* Closed on purpose (second tap, Escape) while the button is still hovered or focused. Kept
   AFTER the rules above: same specificity, so order is what lets it win. */
.help-pop.is-dismissed .help-pop-panel { display: none; }
/* Above the "?". These offsets are the fallback before the script has placed it (and with no
   script: above the whole frame); goal_wizard.js sets the exact one inline, from where the "?"
   sits in the sentence it ends. The bridge moves to the side that faces the button. */
.help-pop--above .help-pop-panel { top: auto; bottom: calc(100% + 6px); }
.help-pop--above .help-pop-panel::before { top: auto; bottom: -8px; }
.help-pop--above[data-help-placement="below"] .help-pop-panel { bottom: auto; top: calc(100% + 6px); }
.help-pop--above[data-help-placement="below"] .help-pop-panel::before { bottom: auto; top: -8px; }
/* Laid out but invisible for one synchronous measurement (goal_wizard.js reads its height before
   choosing a side). Kept AFTER .is-dismissed so it wins at equal specificity. */
.help-pop--above.is-measuring .help-pop-panel { display: block; visibility: hidden; }

/* §3 — the Education breakdown tables + controls share ONE width and are LEFT-aligned (like every
   other wizard page) so the page reads as one chronological flow (target → withdrawals → total). */
.breakdown--edu, .edu-withdrawals { max-width: 680px; margin-left: 0; margin-right: auto; }
.breakdown-total-qual { font-size: 11px; font-weight: 400; color: var(--color-text-muted); }
/* #244 — the Education Breakdown reads as ONE top-down derivation. The assumption controls sit
   directly beneath the cost row they drive, and the Savings Target is the emphasized CONCLUSION of
   that derivation rather than just another row. Education-scoped (.breakdown--edu) so House/Car/etc.
   keep the plain .breakdown-row.total treatment. */
.breakdown--edu .breakdown-edu-controls { margin: 2px 0 12px; }
.breakdown--edu .breakdown-row.total {
  margin-top: var(--space-4); padding: var(--space-4);
  background: rgba(15, 28, 46, 0.04);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  align-items: baseline;
}
.breakdown--edu .breakdown-row.total .label { color: var(--color-primary); font-weight: 600; }
.breakdown--edu .breakdown-row.total .value {
  font-size: 24px; font-weight: 700; color: var(--color-primary);
  font-variant-numeric: tabular-nums;
}
/* The after-inflation schedule is supporting detail — give it air from the conclusion above it. */
#breakdown-education-detail .section-label { margin-top: var(--space-5); }
/* §1 — Education breakdown: chronological withdrawal schedule with aligned columns + a reference
   total. (#244 retired .edu-withdrawals-head — the schedule is now titled by a standard
   .section-label.) */
.edu-withdrawals { margin: 12px 0 4px; }
.edu-withdrawals-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.edu-withdrawals-table th {
  padding: 4px 0; color: var(--color-text-muted); font-weight: 600; font-size: 11px;
  letter-spacing: 0.03em; text-transform: uppercase; border-bottom: 1px solid var(--color-border);
}
.edu-withdrawals-table td { padding: 6px 0; border-bottom: 1px solid rgba(0, 0, 0, 0.05); }
.edu-col-year { width: 22%; text-align: left; }
.edu-col-milestone { width: 48%; text-align: left; color: var(--color-text); }
.edu-col-amount { width: 30%; text-align: right; font-variant-numeric: tabular-nums; }
/* The Funding Target is a REFERENCE total, not a second conclusion — the Savings Target above is the
   page's one answer. So the label carries the meaning while the amount drops to plain body weight,
   removing the headline treatment that let it compete. The 2px rule, the right-aligned numeric
   column, and the surrounding spacing are deliberately unchanged. */
.edu-withdrawals-total {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-top: 10px; padding-top: 10px; border-top: 2px solid var(--color-primary);
  font-weight: 600; font-size: 14px;
}
/* Amount: body weight + tabular figures so it lines up with .edu-col-amount above it. */
.edu-withdrawals-total > span:last-child {
  font-weight: 400; font-variant-numeric: tabular-nums;
}
/* Scoped to this row so the Savings Target's own qualifier keeps its treatment untouched. */
.edu-withdrawals-total .breakdown-total-qual { font-size: 12px; }
/* §1A/§3 — Education assumption controls on the Breakdown: compact stacked rows, left-aligned at the
   same width as the tables so the header → controls → tables read as one left-aligned column. */
.breakdown-edu-controls {
  max-width: 680px; margin: 4px 0 18px; display: flex; flex-direction: column; gap: 10px;
  font-size: 13px;
}
.breakdown-edu-controls-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.breakdown-edu-controls-label { display: inline-block; min-width: 150px; color: var(--color-text); }
.edu-seg { display: inline-flex; flex-wrap: wrap; gap: 4px; }
.edu-seg-btn {
  padding: 4px 11px; border: 1px solid var(--color-border); border-radius: 4px; cursor: pointer;
  font-size: 12px; background: transparent; color: var(--color-text);
}
.edu-seg-btn--active {
  background: var(--spring-steel-blue); color: #fff; border-color: var(--spring-steel-blue);
}
/* In-flight feedback: the chart column dims and ignores pointer events
   while an /forecast/update/ request is pending, so the user knows the
   click registered and cannot stack additional clicks against the same
   pending response. The single-flight gate in goal_wizard.js makes
   stacking impossible anyway, but the visual cue communicates it. */
.forecast-chart-col.forecast-loading {
  opacity: 0.6;
  pointer-events: none;
  transition: opacity 0.1s ease-in;
}

/* Rate chip (inline editable % values) */
.rate-chip {
  display: inline-block; color: var(--color-secondary);
  background: rgba(62, 92, 118, 0.09);
  border-bottom: 1.5px dotted var(--color-secondary);
  font-weight: 600; padding: 1px 5px; border-radius: 3px;
  cursor: pointer; transition: background 0.15s;
}
.rate-chip:hover { background: rgba(62, 92, 118, 0.16); }
.rate-chip-input {
  width: 4ch; padding: 0 2px; font-size: inherit; font-family: inherit;
  font-weight: 600; color: var(--color-secondary);
  background: rgba(62, 92, 118, 0.09); border: 1px solid var(--color-border);
  border-radius: 3px; text-align: center;
}
.rate-chip-input:focus {
  outline: 2px solid var(--color-primary); outline-offset: 1px;
  background: rgba(62, 92, 118, 0.07);
}
.rate-chip-input::-webkit-inner-spin-button,
.rate-chip-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.rate-chip-input { -moz-appearance: textfield; }
/* #300: an entry that is not a number is not sent, and the stored rate stands. The box is
   marked the way the expected-return field is (aria-invalid → the danger border), focused
   or not. */
.rate-chip-input[aria-invalid="true"],
.rate-chip-input[aria-invalid="true"]:focus {
  border-color: var(--color-danger, #E25555);
  outline-color: var(--color-danger, #E25555);
}

/* Reset assumptions — small secondary/outline button (#236 · Flight P1), mirroring
   .btn-outline (transparent, --color-primary text, gray border) at a compact size.
   Behavior unchanged — the template keeps data-action="reset_assumptions". */
.forecast-reset-btn {
  background: transparent;
  color: var(--color-primary);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 4px 12px;
  font-size: 12px;
  font-weight: 650;
  cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease;
}
.forecast-reset-btn:hover { border-color: var(--color-secondary); color: var(--color-secondary); }

/* Forecast range values */
.forecast-range { display: flex; flex-direction: column; gap: 2px; margin: 8px 0; }
.forecast-range-value { font-size: 0.95rem; font-weight: 600; color: var(--color-primary); }
.forecast-range-label { font-size: 0.72rem; font-weight: 400; color: var(--color-text-muted); }
.forecast-range-value--cash { color: #9AA0A6; font-weight: 500; }
.forecast-assumption { font-size: 0.72rem; color: var(--color-text-muted); margin: 0; font-style: italic; }

/* Display-mode toggle: the partial renders BOTH real and nominal text
   variants (narrative dollars + "(today's/future dollars)" hint) inside
   spans tagged data-mode-text. Visibility is keyed off the mode wrapper
   so flipping data-display-mode is an instant client-side swap. */
.forecast-modes[data-display-mode="real"]    [data-mode-text="nominal"],
.forecast-modes[data-display-mode="nominal"] [data-mode-text="real"]    { display: none; }

/* ── Confidence score ── */
.confidence-card {
  text-align: center; padding: 16px; border-radius: var(--radius-sm);
  border: 2px solid var(--color-border); margin: 12px 0;
}
.confidence-score { font-size: 34px; font-weight: 700; margin-bottom: 2px; }
.confidence-label { font-size: 12px; color: var(--color-text-muted); margin-bottom: 8px; }
.confidence-msg { font-size: 13px; color: var(--color-text); line-height: 1.5; }
.confidence-bar-bg {
  height: 6px; background: var(--color-border); border-radius: 3px;
  margin: 10px 0; overflow: hidden;
}
.confidence-bar-fill { height: 100%; border-radius: 3px; transition: width 0.5s ease; }

/* ── Forecast intro line ── */
.forecast-intro { font-size: 14px; color: var(--color-text); margin-bottom: 16px; line-height: 1.6; }

/* ── Confidence + Improve side-by-side ── */
.confidence-improve-row {
  display: flex; gap: 16px; align-items: flex-start; margin-top: 12px;
}
.confidence-improve-row > div:first-child { flex: 1 1 0; min-width: 0; }
.confidence-improve-row .confidence-card { margin: 0; }
.confidence-improve-row .improve-options { flex: 1 1 0; min-width: 0; }

/* ── Plan Status widget (#252) — the Education Plan Forecast only ─────────────────────────
   Takes the Goal Confidence card's place in the row above on that one Forecast; every other
   goal type's Forecast still renders the card, so nothing under "Confidence score" is retired.
   It shares the card's box (.confidence-card: border, padding, radius) and adds:
     * the headline, in the page's text colour — the state is words, never colour alone;
     * an icon whose SHAPE differs per row, so rows that share a colour still differ: On Track
       (a solid check) from Nearly On Track (a dashed outline), and the two "Needs Attention"
       rows from each other without relying on orange against red.
   There is NO bar, segment strip or meter, and none is to be added: a lit segment reads as
   partial progress and a solid bar as a quantity — the misreading this widget replaces. The
   status colour is carried by the border and the icon only.
   The band colours are the tokens the goal card's risk border uses. The widget is the frame
   its "?" help is placed against (position: relative; see .help-pop). */
.plan-status { position: relative; text-align: left; }
.plan-status--risk-strong { border-color: var(--color-success); }
.plan-status--risk-warning { border-color: var(--color-gold); }
.plan-status--risk-caution { border-color: var(--color-warning); }
.plan-status--risk-off-track { border-color: var(--color-danger); }
.plan-status-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.plan-status-label { font-size: 12px; color: var(--color-text-muted); }
.plan-status-headline {
  display: flex; align-items: center; gap: 8px; margin: 6px 0 8px;
  font-size: 20px; font-weight: 700; line-height: 1.2; color: var(--color-text);
}
.plan-status-icon { flex: 0 0 auto; width: 24px; height: 24px; }
/* Filled shape, with the mark drawn on it in a colour that reads against that fill. */
.plan-status-icon-shape { fill: currentColor; stroke: none; }
.plan-status-icon-mark,
.plan-status-icon-outline { fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.plan-status-icon-mark { stroke: #fff; }
.plan-status-icon-outline { stroke: currentColor; stroke-width: 2; }
.plan-status--risk-strong .plan-status-icon { color: var(--color-success); }
.plan-status--risk-warning .plan-status-icon { color: var(--color-gold); }
.plan-status--risk-caution .plan-status-icon { color: var(--color-warning); }
.plan-status--risk-off-track .plan-status-icon { color: var(--color-danger); }
/* White is too faint on gold and on orange: the mark is the text colour there. */
.plan-status--risk-warning .plan-status-icon-mark,
.plan-status--risk-caution .plan-status-icon-mark { stroke: var(--color-text); }
.plan-status-summary { margin: 0; font-size: 13px; line-height: 1.5; color: var(--color-text); }
.plan-status-detail { margin: 4px 0 0; font-size: 13px; line-height: 1.5; color: var(--color-text-muted); }
/* The staff-only diagnostic: present in the page for a staff superuser alone, collapsed until
   opened, and marked as internal. */
.plan-status-diagnostic {
  margin-top: 12px; padding-top: 10px; border-top: 1px dashed var(--color-border);
  font-size: 12px; color: var(--color-text-muted);
}
.plan-status-diagnostic summary { cursor: pointer; }
.plan-status-diagnostic p { margin: 6px 0 0; }
/* A phone: the longest headlines ("Needs Attention", "Nearly On Track") stay on one line. */
@media (max-width: 480px) {
  .plan-status-headline { font-size: 18px; }
}

/* ── Improve options ── */
.improve-options { text-align: left; display: flex; flex-direction: column; gap: 8px; }
.adjust-btn-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.improve-btn {
  width: 100%; text-align: center; font-size: 13px; white-space: normal;
}
/* "Reset assumptions" (#307, Education Plan Forecast only): centred under the six levers. */
.adjust-reset-row { display: flex; justify-content: center; margin-top: 4px; }
/* #352 — the same button while the forecast is withheld (no expected return confirmed). The
   lever panel is not on the page then, so the row sits under the "Your forecast will appear
   here" note, centred over that note's width, in the outline style it has inside the panel.
   The row takes the note's own box (.return-assumption-pending: the same max-width, side
   padding and border width), so the button is centred on the note at every width. */
.adjust-reset-row--withheld {
  max-width: 760px; margin: 12px 0 8px; padding: 0 16px;
  border: solid transparent; border-width: 0 1px;
}
/* The lever panel (#307; restyled in #252 — Education Plan Forecast only): a WHITE panel inside
   a subtle grey border, holding the heading, the six levers and Reset. The panel is the
   .improve-options block itself, so in the side-by-side row its top border sits at the row's
   top — level with the Plan Status widget's, whose margin the row already removes. The heading
   is centred over the buttons.
   The six levers are the panel's actions and must read as controls at rest, not only on
   hover: a flat light grey fill with a border clearly darker than the fill, darker text than
   .btn-white's, and a distinct state for hover (darker fill and border), keyboard focus (the
   .btn focus ring) and pressed (darker still; .btn's 1px nudge). Flat on purpose — no
   gradient, shadow or bevel. Reset is the neutral SECONDARY control and must stay visibly
   different from the levers: the outline counterpart — white fill, grey border, small.
   Scoped to the panel class, which only the Education Plan Forecast's lever block carries, so
   the same buttons on every other goal type's Forecast keep .btn-white exactly as it was. */
.improve-options--panel {
  background: #fff; border: 1px solid var(--color-border); border-radius: var(--radius-sm);
  padding: 14px 16px 16px; gap: 10px;
}
.improve-options--panel > .section-label { margin: 0; text-align: center; }
.improve-options--panel .adjust-reset-row { margin-top: 2px; }
.improve-options--panel .improve-btn {
  background: #EEF0F2; border-color: #AEB6BE; color: var(--color-text);
}
.improve-options--panel .improve-btn:hover {
  background: #E1E5E9; border-color: #8C97A2; color: var(--color-text);
}
.improve-options--panel .improve-btn:active { background: #D3D8DD; border-color: #7C8792; }
.improve-options--panel .adjust-reset-row .btn-neutral,
.adjust-reset-row--withheld .btn-neutral {
  background: #fff; border-color: #AEB6BE; color: var(--color-text);
}
.improve-options--panel .adjust-reset-row .btn-neutral:hover,
.adjust-reset-row--withheld .btn-neutral:hover {
  background: #F3F4F6; border-color: #8C97A2; color: var(--color-text);
}
.improve-options--panel .adjust-reset-row .btn-neutral:active,
.adjust-reset-row--withheld .btn-neutral:active { background: #E6E9EC; }
/* Narrow viewports, Education Plan Forecast only (#297): stack the two halves. Side by side,
   each half is narrower than the two-column button grid's min-content width, so the grid ran
   past the card — and, on a phone, past the viewport (Visual QA no-horizontal-overflow).
   Scoped by [data-return-gate], which only that Forecast carries: the same lever block on every
   other goal type's Forecast keeps the layout it had. */
@media (max-width: 700px) {
  .forecast-modes[data-return-gate] .confidence-improve-row { flex-direction: column; align-items: stretch; }
  .forecast-modes[data-return-gate] .confidence-improve-row > div:first-child,
  .forecast-modes[data-return-gate] .confidence-improve-row .improve-options { flex: 0 0 auto; }
}
@media (max-width: 480px) {
  .forecast-modes[data-return-gate] .adjust-btn-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .forecast-modes[data-return-gate] .improve-btn { padding-left: 8px; padding-right: 8px; overflow-wrap: anywhere; }
  /* On a phone the panel's border and padding take width the two-column grid has none to
     spare — a lever would break "Contribution" mid-word — so inside the panel the levers go
     one to a row: full width, one line each, easy to tap. */
  .forecast-modes[data-return-gate] .improve-options--panel { padding: 12px 12px 14px; }
  .forecast-modes[data-return-gate] .improve-options--panel .adjust-btn-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ── Current goals badge card ── */
.current-goals-card {
  background: #F5F6F7;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 10px;
  margin-left: auto;
  width: fit-content;
}
.current-goals-label {
  font-family: var(--font-mono);
  font-size: 10px; font-weight: 500; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--color-text-muted);
  white-space: nowrap;
}
.goal-badges { display: flex; gap: 8px; flex-wrap: wrap; }
.goal-badge {
  display: flex; align-items: center; gap: 4px;
  background: rgba(62, 92, 118, 0.06); border-radius: 6px;
  padding: 3px 7px; font-size: 11px; color: var(--color-text-muted);
  white-space: nowrap; line-height: 1.2;
}
.goal-badge-emoji { font-size: 13px; }
.goal-badge-details { display: flex; flex-direction: column; gap: 0; }
.goal-badge-target { font-weight: 600; color: var(--color-primary); font-size: 11px; }
.goal-badge-monthly { font-size: 10px; color: var(--color-text-muted); }

/* ── Contribution plan fields ── */
.plan-fields { display: flex; flex-direction: column; gap: 16px; }
.plan-row { display: flex; align-items: center; gap: 12px; }
.plan-row .plan-label { flex: 0 0 160px; font-size: 13px; color: var(--color-text-muted); }
.plan-row .plan-input { flex: 1; }
.plan-row .plan-input input,
.plan-row .plan-input select { width: 100%; }

/* ── Inline fields row (for forecast editing) ── */
.inline-fields { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; }
.inline-field-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 4px 0;
}
.inline-field-row .ifl { font-size: 13px; color: var(--color-text-muted); }

/* ── Manage Goal v2 ──────────────────────────────────────────────────
   Scoped to .manage-goal-v2 so these rules do NOT affect the shared
   Allocation ring usage on the legacy manage page or Staff Account
   Inspector. All tier coloring is driven by inline style from the
   alignment context. */

/* Modestly widen the page vs. the default 760px .dashboard-column so
   the Allocation card can render a proper two-column composition on
   desktop without creating empty space. Compound selector has higher
   specificity than .dashboard-column, scoping the change to this
   page only. */
.manage-goal-v2.dashboard-column { max-width: 920px; }

.manage-goal-v2 .progress-stack { gap: var(--space-2); }
.manage-goal-v2 .milestone-note {
  font-size: 13px;
  color: var(--color-text-muted);
  margin: var(--space-1) 0 0;
}
.manage-goal-v2 .mg2-alloc-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.manage-goal-v2 .mg2-alloc-header h3 { margin: 0; }
.manage-goal-v2 .alignment-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.manage-goal-v2 .alignment-chip .dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
}

/* ── Allocation card top band: ring LEFT, readout RIGHT ──
   The row layout is also declared inline on the template elements so
   that a stale cached CSS can't break the structure (dev static files
   aren't content-hashed). These rules are the maintainability layer;
   the inline styles are authoritative at desktop widths. Ring slot is
   a fixed 220px column, materially smaller than the shared component's
   360px drawing buffer so the ring supports the readout rather than
   dominating the card. Canvas is explicitly capped so Chart.js's
   responsive mode cannot re-expand it — wrapper also has
   overflow:hidden as a belt-and-suspenders clip. */
.manage-goal-v2 .mg2-alloc-top {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  gap: var(--space-5);
  align-items: flex-start;
}
.manage-goal-v2 .mg2-alloc-ring {
  flex: 0 0 220px;
  width: 220px;
  max-width: 220px;
  min-width: 0;
  overflow: hidden;
}
.manage-goal-v2 .mg2-alloc-ring canvas {
  width: 220px !important;
  height: 220px !important;
  max-width: 100% !important;
}
.manage-goal-v2 .mg2-alloc-readout {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  font-size: 14px;
}
.manage-goal-v2 .mg2-alloc-readout .alignment-description {
  font-size: 15px;
  color: var(--color-text);
  margin: 0;
  line-height: 1.45;
}
.manage-goal-v2 .mg2-alloc-readout .alignment-recommendation {
  font-size: 13px;
  color: var(--color-text-muted);
  margin: 0;
  line-height: 1.45;
}
.manage-goal-v2 .mg2-alloc-readout hr {
  width: 100%;
  border: 0;
  border-top: 1px solid var(--color-border);
  margin: 0;
}
.manage-goal-v2 .recurring-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.manage-goal-v2 .recurring-row .schedule-text { font-weight: 600; font-size: 15px; }

/* ── Allocation card bottom band: Adjust allocation actions ──
   Two flex cells side-by-side: One-time deposit | Transfer to goal.
   `.mg2-alloc-actions--single` collapses to a single full-width cell
   when the Transfer slot is not rendered (template-driven). */
.manage-goal-v2 .mg2-alloc-divider {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}
.manage-goal-v2 .mg2-alloc-divider .mg2-alloc-adjust-heading {
  margin: 0 0 var(--space-3);
  font-size: 15px;
  font-weight: 600;
}
.manage-goal-v2 .mg2-alloc-actions {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  gap: var(--space-5);
}
.manage-goal-v2 .mg2-alloc-actions > * {
  flex: 1 1 0;
  min-width: 0;
}
.manage-goal-v2 .mg2-alloc-actions--single > * { flex: 1 1 100%; }
.manage-goal-v2 .mg2-alloc-actions h5 {
  margin: 0 0 var(--space-3);
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
}

/* ── Action column (centered section, inline-label control rail) ──
   Each cell is a centering flex column. The form block below the title
   is a 260px rail — same width regardless of whether the outer cell is
   ~376px (two-col state) or ~792px (single-col fallback when Transfer
   is unavailable). Local-dev and staging render identical form blocks. */
.manage-goal-v2 .mg2-alloc-action-col {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.manage-goal-v2 .mg2-alloc-action-title {
  margin: 0 0 var(--space-3);
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
}
.manage-goal-v2 .mg2-alloc-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 260px;
  max-width: 100%;
}
/* Inline label|control row. Labels match the body copy above the card
   (14px/normal weight, matching the "You can also keep this goal
   aligned…" sentence) so nothing in this block reads as an emphasized
   element except the section title itself. */
.manage-goal-v2 .mg2-alloc-form .mg2-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.manage-goal-v2 .mg2-alloc-form .mg2-row label {
  flex: 0 0 60px;
  text-align: right;
  font-size: 14px;
  font-weight: 400;
  margin: 0;
  color: var(--color-text);
}
.manage-goal-v2 .mg2-alloc-form .mg2-row input[type="text"],
.manage-goal-v2 .mg2-alloc-form .mg2-row select {
  flex: 1 1 auto;
  background: #fff;
  min-width: 0;
  padding: 6px 10px;
  font-size: 14px;
}
/* Radio group sits below Amount. Each option is a `display:block` label
   so the two choices can NEVER share a line. The first option's inline
   "to <select>" segment wraps inside its own label as needed; the
   "Transfer Out / Sell" option always starts on a fresh line. */
.manage-goal-v2 .mg2-alloc-radio-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 4px;
}
.manage-goal-v2 .mg2-alloc-radio-group > label.mg2-alloc-radio {
  display: block;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--color-text);
  cursor: pointer;
  margin: 0;
}
.manage-goal-v2 .mg2-alloc-radio-group input[type="radio"] {
  margin: 0 6px 0 0;
  vertical-align: middle;
  accent-color: var(--color-primary);
}
.manage-goal-v2 .mg2-alloc-radio-group .mg2-alloc-inline-to {
  display: inline-flex;
  align-items: center;
  margin-left: 8px;
  font-size: 13px;
  color: var(--color-text-muted);
}
.manage-goal-v2 .mg2-alloc-inline-select {
  padding: 4px 22px 4px 8px;
  font-size: 13px;
  max-width: 140px;
  background: #fff;
}
/* Submit row: right-aligned within the rail, with explicit breathing
   room above so the button doesn't visually touch the last control. */
.manage-goal-v2 .mg2-alloc-submit {
  display: flex;
  justify-content: flex-end;
  margin-top: 8px;
}

/* Standard stacked form fields (label above input). Replaces the
   .plan-row inline-edit pattern — these are action forms, not settings
   rows. Uses the standard .input / select styling from the global
   form reset so inputs feel like normal white textboxes. */
.manage-goal-v2 .field-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);           /* more breathing room between field groups */
}
.manage-goal-v2 .field-stack label {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: var(--color-text);
  margin-bottom: var(--space-2);
}
.manage-goal-v2 .field-stack input[type="text"],
.manage-goal-v2 .field-stack select {
  width: 100%;
  background: #fff;
}

/* Bottom band action columns: titles are centered (set inline on the
   h5) and all form content aligns against the right edge of the
   column, giving the two actions a symmetrical, deliberate read. Each
   field row becomes its own right-aligned flex column so labels and
   inputs anchor to the right side within a 260px content zone. */
.manage-goal-v2 .mg2-alloc-right-col { align-items: flex-end; }
.manage-goal-v2 .mg2-alloc-field {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  width: 100%;
  max-width: 260px;
}
.manage-goal-v2 .mg2-alloc-field label {
  align-self: flex-end;
  text-align: right;
  width: 100%;
}
.manage-goal-v2 .mg2-alloc-field input[type="text"],
.manage-goal-v2 .mg2-alloc-field select {
  width: 100%;
  max-width: 260px;
}
/* Transfer-mode radio group: each choice is a flex row (radio · label)
   and the group as a whole is anchored to the right edge like the
   other fields. Extra vertical rhythm for clarity. */
.manage-goal-v2 .mg2-alloc-radio-group {
  gap: var(--space-2);
}
.manage-goal-v2 .mg2-alloc-radio-group label {
  text-align: right;
  margin-bottom: 0;
}
.manage-goal-v2 .mg2-alloc-radio-group input[type="radio"] {
  margin: 0;
  accent-color: var(--color-primary);
}

/* ── Card 1: rename affordance + primary-action row (Manage Goal UX) ──
   The pencil reveals a lightweight inline rename field (Save/Cancel); the
   primary action concludes the card, anchored bottom-right behind a subtle
   divider so Card 1 ends on "what should I do next?". */
.manage-goal-v2 .goal-name-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.manage-goal-v2 .goal-rename-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;          /* no off-white fill */
  cursor: pointer;
  color: var(--color-primary);      /* Spring dark blue icon */
  padding: 4px;
  line-height: 0;
  border-radius: var(--radius-sm);
}
.manage-goal-v2 .goal-rename-btn:hover { color: var(--color-secondary); }
.manage-goal-v2 .goal-rename-btn:focus-visible {
  outline: 2px solid var(--color-secondary);
  outline-offset: 2px;
}
.manage-goal-v2 .goal-rename-form {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 6px;
}
.manage-goal-v2 .goal-rename-form .input { max-width: 260px; }
/* The `hidden` attribute must beat the display:flex above so the inline rename
   field is CLOSED on page load — the JS clears `hidden` only when the pencil is
   clicked. Without this, the class rule overrides the UA [hidden] rule and the
   edit field is shown on first load. */
.manage-goal-v2 .goal-rename-form[hidden] { display: none; }
/* Scoped header override removes the .goal-header bottom border on this page
   (the line above progress); also enforced inline on the template for
   stale-cache safety. (Flight 6 moved the primary action out of the card, so the
   former .card1-action-row rule was removed as orphaned.) */
.manage-goal-v2 .goal-header { border-bottom: none; }

/* ── Track Your Progress (planning goals): compact balance card ──
   Single-line value summary, single-row entry form, collapsed history —
   "what is my current balance?" with minimal vertical footprint. */
.manage-goal-v2 .balance-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-5);
  font-size: 14px;
  color: var(--color-text);
}
/* Each readout item groups its own label + value with a small gap so
   "Current Value:", "As of:" and "Held at:" never collide with their values
   or with the adjacent item when the row wraps. */
.manage-goal-v2 .balance-summary .balance-summary-item {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
}
.manage-goal-v2 .balance-summary .balance-summary-label { color: var(--color-text-muted); }
.manage-goal-v2 .balance-summary strong { font-size: 16px; font-weight: 600; }
/* Two-row grid: labels row, inputs row, Update right-justified beneath the
   third column ("Held at"). Inline grid/flex on the template are authoritative;
   these rules are the maintainability layer + drive the ≤720px single-column
   stack (with !important to beat the inline desktop grid). */
.manage-goal-v2 .balance-entry-form {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
  align-items: end;
}
.manage-goal-v2 .balance-entry-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.manage-goal-v2 .balance-entry-field label {
  font-size: 12px;
  color: var(--color-text-muted);
}
.manage-goal-v2 .balance-entry-field input {
  width: 100%;
  min-width: 0;            /* let date/text inputs shrink inside the grid cell */
  box-sizing: border-box; /* padding+border stay inside the column — no overlap */
  background: #fff;
  padding: 6px 10px;
  font-size: 14px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}
.manage-goal-v2 .balance-entry-submit {
  grid-column: 1 / -1;
  justify-self: end;
  margin-top: var(--space-2);
}
.manage-goal-v2 .balance-history > summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--color-primary);
  list-style: none;
}
.manage-goal-v2 .balance-history > summary::-webkit-details-marker { display: none; }
.manage-goal-v2 .balance-history > summary::before {
  content: "\25B8";            /* ▸ — rotates to ▾ when open */
  display: inline-block;
  margin-right: 6px;
  color: var(--color-text-muted);
  transition: transform 0.15s ease;
}
.manage-goal-v2 .balance-history[open] > summary::before { transform: rotate(90deg); }

/* Mobile breakpoint uses !important so it can override the inline
   flex-direction:row declarations on the template. */
@media (max-width: 720px) {
  .manage-goal-v2.dashboard-column { max-width: 100%; }
  .manage-goal-v2 .mg2-alloc-top { flex-direction: column !important; }
  .manage-goal-v2 .mg2-alloc-ring {
    flex: 0 0 auto !important;
    width: 220px !important;
    max-width: 100% !important;
    margin: 0 auto !important;
  }
  .manage-goal-v2 .mg2-alloc-actions { flex-direction: column !important; }
  .manage-goal-v2 .mg2-alloc-actions > * { flex: 1 1 100% !important; }
  .manage-goal-v2 .balance-entry-form { grid-template-columns: 1fr !important; }
}

/* "Coming Soon" overlay — used to soft-disable staff tools that remain
   discoverable but are not validated for decision use. Watermark uses
   pointer-events:none so it never traps clicks on inputs underneath or
   on outer navigation. */
.coming-soon-wrapper {
  position: relative;
}
.coming-soon-watermark {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 5;
}
.coming-soon-watermark > span {
  font-family: var(--font-display, inherit);
  font-weight: 800;
  font-size: clamp(48px, 9vw, 128px);
  letter-spacing: 0.08em;
  color: rgba(15, 28, 46, 0.10);
  text-transform: uppercase;
  transform: rotate(-18deg);
  user-select: none;
  white-space: nowrap;
}
.coming-soon-dim > * { opacity: 0.55; }
.coming-soon-dim .alert { opacity: 1; }
.coming-soon-tab-chip {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 2px 8px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  vertical-align: middle;
}

/* --- Ad slots (internal placeholder; Phase 3) ----------------------------- */
.ad-slot {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  color: var(--color-text-muted);
}
.ad-slot-label {
  font-family: 'Geist Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.ad-slot-placeholder { flex: 1; font-size: 13px; }
.ad-slot-upsell { font-size: 12px; color: var(--color-link); white-space: nowrap; }

.ad-banner {
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
}

.ad-rail {
  position: fixed;
  top: 120px;
  right: 24px;
  width: 160px;
  flex-direction: column;
  align-items: flex-start;
  z-index: 10;
}

/* Desktop-only: the fixed rail needs wide margins so it never overlaps the
   760px content column; both slots hide on mobile (mobile ad strategy deferred). */
@media (max-width: 1180px) { .ad-rail { display: none; } }
@media (max-width: 768px) { .ad-slot { display: none; } }

/* ── Financial Plan document (Workstream C UX polish) ──────────────────────
   The saved Financial Plan reads as a professionally prepared document: an
   off-white paper sheet, dark ink, clean sans-serif type, bold headings,
   horizontal rules, generous whitespace — and almost no UI. The only control
   is a small Copy icon in the sheet's upper-right. Brand palette tokens only
   (the sheet is a deliberate light surface on the dark app frame). */
.financial-plan-doc {
  position: relative;
  /* box-sizing keeps the 56px side padding INSIDE the max-width/100% so the
     paper never overflows its content card at any viewport width (#232 F2). */
  box-sizing: border-box;
  background: var(--spring-near-white);
  color: var(--spring-graphite);
  /* Wide-page document: a readable, centered measure inside the
     dashboard-column--wide wrapper (matches the 920px Manage Goal page). */
  max-width: 920px;
  width: 100%;
  margin: 0 auto var(--space-4);
  padding: 40px 56px;
  border-radius: 4px;
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.35);
  font-size: 15px;
  line-height: 1.6;
}
.financial-plan-doc h1 {
  font-size: 26px;
  margin: 0 0 14px;
  color: var(--spring-midnight-blue);
}
/* Document masthead: Spring wordmark beside the plan title, one clean row
   (a letterhead — deliberately distinct from the app's top navigation). */
.financial-plan-doc .fp-doc-header {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin: 0 0 14px;
}
.financial-plan-doc .fp-doc-logo {
  height: 36px;
  width: auto;
}
.financial-plan-doc .fp-doc-header h1 { margin: 0; }
.financial-plan-doc h2 {
  font-size: 18px;
  font-weight: 700;
  margin: 0 0 8px;
  color: var(--spring-midnight-blue);
}
.financial-plan-doc hr {
  border: none;
  border-top: 1px solid var(--spring-cool-gray);
  margin: 20px 0;
}
.financial-plan-doc p { margin: 0 0 10px; }
.financial-plan-doc .fp-doc-lead { margin-bottom: 4px; }
.financial-plan-doc .fp-doc-label {
  font-weight: 700;
  margin: 12px 0 2px;
}
.financial-plan-doc ul {
  margin: 4px 0 10px;
  padding-left: 22px;
}
.financial-plan-doc li { margin: 1px 0; }
/* Plan Summary as an aligned label/value table — reads like a financial
   statement line-item (#232 F2 refinement). */
.financial-plan-doc .fp-doc-summary {
  width: 100%;
  border-collapse: collapse;
  margin: 4px 0 10px;
}
.financial-plan-doc .fp-doc-summary th,
.financial-plan-doc .fp-doc-summary td {
  padding: 4px 0;
  border-bottom: 1px solid var(--spring-cool-gray);
  font-weight: 400;
  vertical-align: baseline;
}
.financial-plan-doc .fp-doc-summary th { text-align: left; }
.financial-plan-doc .fp-doc-summary td {
  text-align: right;
  padding-left: 16px;
  font-variant-numeric: tabular-nums;
}
.financial-plan-doc .fp-doc-summary tr:last-child th,
.financial-plan-doc .fp-doc-summary tr:last-child td { border-bottom: none; }
/* Footer disclaimer + single-line metadata (moved to the bottom, #232 F2). */
.financial-plan-doc .fp-doc-disclaimer {
  font-size: 12px;
  color: var(--spring-neutral-gray);
  margin: 0 0 8px;
}
.financial-plan-doc .fp-doc-meta-footer {
  font-size: 12px;
  color: var(--spring-neutral-gray);
  margin: 0;
}
/* Document actions row (Copy + Print), floated top-right of the paper. */
.fp-doc-actions {
  position: absolute;
  top: 18px;
  right: 18px;
  display: flex;
  gap: 4px;
}
.fp-doc-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--spring-neutral-gray);
  cursor: pointer;
  padding: 6px;
  border-radius: 4px;
  line-height: 0;
  text-decoration: none;
}
.fp-doc-action:hover { color: var(--spring-steel-blue); }
.fp-doc-action--done { color: var(--spring-steel-blue); }

/* ── Education narrative (Flight #243) ───────────────────────────────────────
   The Education section reads as a CFP-style plan: a status verdict, tinted
   callouts (confidence, advisor), a small modeling note, and a subtotalled
   annual schedule. The plan is a fixed light "paper" sheet, so these use fixed
   light tints (no dark-mode variants) to match .financial-plan-doc. Green =
   on-track, gold = attention (The Gold Rule); steel-blue tint = informational. */
.financial-plan-doc .fp-doc-status {
  font-weight: 700;
  font-size: 16px;
  line-height: 1.5;
  margin: 4px 0 16px;
  padding: 12px 16px;
  border-radius: 6px;
  border-left: 4px solid var(--spring-neutral-gray);
  background: var(--spring-near-white);
  color: var(--spring-graphite);
}
.financial-plan-doc .fp-doc-status--on_track {
  border-left-color: var(--color-success);
  background: #f0faf3;
  color: #15683a;
}
.financial-plan-doc .fp-doc-status--attention {
  border-left-color: var(--color-gold);
  background: var(--color-gold-bg);
  color: #7a5e00;
}
.financial-plan-doc .fp-doc-callout {
  margin: 6px 0 16px;
  padding: 12px 16px;
  border-radius: 6px;
  border: 1px solid var(--spring-cool-gray);
  background: var(--spring-near-white);
}
.financial-plan-doc .fp-doc-callout p { margin: 0; }
.financial-plan-doc .fp-doc-callout p + p { margin-top: 6px; }
.financial-plan-doc .fp-doc-confidence {
  border-color: rgba(62, 92, 118, 0.2);
  background: rgba(62, 92, 118, 0.09);
}
.financial-plan-doc .fp-doc-callout-label {
  font-weight: 700;
  color: var(--spring-midnight-blue);
}
.financial-plan-doc .fp-doc-advisor {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
.financial-plan-doc .fp-doc-advisor-link {
  display: inline-block;
  font-weight: 600;
  color: var(--spring-steel-blue);
  border: 1px solid var(--spring-steel-blue);
  border-radius: 4px;
  padding: 5px 14px;
  text-decoration: none;
}
.financial-plan-doc .fp-doc-advisor-link:hover {
  background: var(--spring-steel-blue);
  color: #fff;
  text-decoration: none;
}
.financial-plan-doc .fp-doc-note {
  font-size: 13px;
  color: var(--spring-neutral-gray);
  margin: 2px 0 14px;
}
/* Annual withdrawal schedule: a subtotalled statement — emphasize the Total. */
.financial-plan-doc .fp-doc-schedule tr:last-child th,
.financial-plan-doc .fp-doc-schedule tr:last-child td {
  font-weight: 700;
  border-top: 1.5px solid var(--spring-cool-gray);
  color: var(--spring-midnight-blue);
}

@media (max-width: 640px) {
  .financial-plan-doc { padding: 24px 20px; }
}

/* ── Auth shell (Workstream E1) ─────────────────────────────────────────
   Readable Spring-styled container for allauth-rendered pages (login,
   signup, confirm-email code entry, logout confirm, password flows).
   Applied to <main> via the main_extra_class block in
   templates/allauth/layouts/base.html; allauth's default inner markup
   (unclassed headings/forms/buttons) is styled generically here. Full
   per-page restyling is E4 scope — this makes the pages usable now. */
/* Auth pages put the card ON <main> itself (allauth/layouts/base.html fills
   main_extra_class), so the card is ALSO the sticky-footer flex item that
   `main.container { flex: 1 0 auto }` tells to grow into the viewport. That is
   why every short auth form rendered as a tall white box with dead space under
   its actions. Opt the card out of growing so its height is content-driven; the
   auto BOTTOM margin then absorbs the leftover column space, so the footer stays
   pinned exactly as the E3 sticky-footer layout intends. Width, padding, and the
   56px top rhythm are unchanged, so mobile behaves as before. */
main.container.auth-shell {
  flex: 0 0 auto;
  margin: 56px auto auto;
}
.auth-shell {
  max-width: 440px;
  margin: 56px auto;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: 36px 32px;
  color: var(--color-text);
}
.auth-shell h1,
.auth-shell h2 {
  color: var(--color-primary);
  font-size: 1.35rem;
  line-height: 1.3;
  margin: 0;
}
.auth-shell p {
  color: var(--color-text);
  line-height: 1.5;
  margin: 0;
}
/* Plain links only — link-styled buttons (a.btn-primary etc.) must keep
   their own button text colors (E2 smoke: light-blue text on dark button). */
.auth-shell a:not(.btn) { color: var(--color-secondary); }
/* Flash banners (e.g. "Confirmation email sent…") get room before the heading. */
.auth-shell .alert { margin-bottom: 8px; }
.auth-shell form { margin: 0; }
.auth-shell label {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--color-text-muted);
  margin-bottom: 6px;
}
.auth-shell input[type="text"],
.auth-shell input[type="email"],
.auth-shell input[type="password"] {
  box-sizing: border-box;   /* 100% width must include padding/border */
  display: block;
  width: 100%;
  margin: 0 0 20px;
}
.auth-shell input::placeholder { color: var(--color-text-muted); }
/* allauth wraps label+field pairs in <p> — normalize their rhythm. */
.auth-shell form p { margin: 0 0 4px; }
/* allauth's default buttons carry no Spring classes — mirror .btn/.btn-primary
   with identical sizing on every auth page; adjacent buttons get a gutter. */
.auth-shell button[type="submit"],
.auth-shell button:not([class*="btn"]),
.auth-shell .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 10px 20px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-size: 14px;
  font-weight: 650;
  cursor: pointer;
  margin: 8px 8px 0 0;
}
.auth-shell button[type="submit"],
.auth-shell button:not([class*="btn"]) {
  background: var(--color-primary);
  color: #fff;
}
.auth-shell button[type="submit"]:hover,
.auth-shell button:not([class*="btn"]):hover {
  background: #1a2d45;
  color: #fff;
}
.auth-shell ul.errorlist {
  color: var(--color-danger, #E25555);
  list-style: none;
  margin: 0 0 8px 0;
  padding: 0;
  font-size: 0.9rem;
}
/* Inline label + field row (signup email): label sits at the start of the
   input line instead of crowding above it, with clear air before the row. */
.auth-shell .auth-field-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 24px 0 20px;
}
.auth-shell .auth-field-row label {
  margin: 0;
  flex-shrink: 0;
}
.auth-shell .auth-field-row input[type="text"],
.auth-shell .auth-field-row input[type="email"],
.auth-shell .auth-field-row input[type="password"] {
  flex: 1;
  width: auto;
  margin: 0;
}
/* Secondary hint line (e.g. "New device? Request a sign-in code instead."):
   reads as recovery guidance under the lead sentence, never as a competing
   primary action. */
.auth-shell .auth-hint {
  font-size: 0.85rem;
  color: var(--color-text-muted);
}
/* Compact modal-like variant for single-decision auth pages (logout
   confirm, passkey prompt/enroll): tighter card, floated higher, centered. */
.auth-shell--compact {
  max-width: 400px;
  margin-top: 96px;
  text-align: center;
}
/* Segmented verification-code input (progressive enhancement over the real
   allauth `code` field — see static/js/code_input.js). */
.code-input-boxes {
  display: flex;
  gap: 6px;
  align-items: center;
  margin: 4px 0 20px;
}
.auth-shell input.code-input-box {
  /* Cap at 42px on wide surfaces; flex-shrink to fit narrow (mobile) viewports so
     eight boxes + the dash never trip the no-horizontal-overflow gate. */
  flex: 1 1 0;
  min-width: 0;
  max-width: 42px;
  height: 52px;
  padding: 0;
  margin: 0;
  text-align: center;
  font-size: 1.25rem;
  font-family: var(--font-mono);
  text-transform: uppercase;
}
.code-input-dash {
  color: var(--color-text-muted);
  font-weight: 650;
}
/* The real input stays in the DOM (it is what submits) but is tucked away
   once the boxes take over. */
.auth-shell input.code-input-real {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

/* ─────────────────────────────────────────────────────────────────
   Account menu + Account Settings (Workstream E3)
   The settings page reuses the standard .dashboard-column white surface and
   .section-label headings — no bespoke page layout. Only the account menu,
   the reusable settings row, and the version footnote are new here.
   ───────────────────────────────────────────────────────────────── */
.account-menu-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.account-menu-icon {
  flex: 0 0 auto;
  color: var(--color-primary);
}
/* Header avatar (Workstream E5): the user's initials when they have a display
   name, else the person icon — always centered inside a circle. */
.avatar-circle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  color: var(--color-primary);
  font-size: 0.78rem;
  font-weight: 650;
  line-height: 1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
/* Account dropdown identity header: display name over muted email. */
.account-menu-header {
  padding: 2px 8px 8px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--color-border);
}
.account-menu-name {
  font-weight: 650;
  color: var(--color-primary);
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.account-menu-email {
  font-size: 0.82rem;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Radio option cards (Workstream E5): a visible native radio with a title and a
   description beneath it. No JS — the native control drives selection and
   :has() highlights the chosen card, with a server-set fallback class. */
.radio-option {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 14px 16px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.radio-option + .radio-option {
  margin-top: 10px;
}
.radio-option:hover {
  border-color: var(--color-secondary);
  background: rgba(62, 92, 118, 0.05);
}
.radio-option--selected,
.radio-option:has(input:checked) {
  border-color: var(--color-primary);
  background: rgba(62, 92, 118, 0.08);
}
.radio-option > input[type="radio"] {
  margin-top: 3px;
  flex: 0 0 auto;
}
.radio-option-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.radio-option-title {
  font-weight: 650;
  color: var(--color-primary);
}
.radio-option-desc {
  font-size: 0.9rem;
}

/* Reusable Account Settings row: title/description on the left, a read-only
   value or an action link on the right. Lives inside a .dashboard-column
   section; later flights (E4/E5) swap read-only rows for editable ones without
   restructuring the page. */
.settings-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--color-border);
}
.settings-row:first-of-type {
  border-top: 1px solid var(--color-border);
}
.settings-row-main {
  min-width: 0;
}
.settings-row-title {
  font-weight: 650;
  color: var(--color-primary);
}
.settings-row-desc {
  font-size: 0.9rem;
  margin-top: 2px;
}
.settings-row-control {
  flex: 0 0 auto;
  text-align: right;
  white-space: nowrap;
}
.settings-row-value {
  color: var(--color-text-muted);
}
.settings-row-link {
  font-weight: 600;
}

.settings-footnote {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-3);
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

/* ── Goal Summary dialog (#258) ──────────────────────────────────────────────
   One-time post-wizard completion summary over /goals/. Native <dialog>, so the
   backdrop, focus containment, Escape and top-layer stacking come from the
   platform.

   CENTERING: `margin:auto` on a top-layer dialog centers it both axes against the
   viewport — no fixed top offset, so a two-line summary sits in the visual center
   and a long one still clears top and bottom. `max-height` uses dvh (dynamic
   viewport height) so mobile browser chrome collapsing/expanding never crops the
   footer; vh is the fallback for engines without dvh.

   SCROLLING: the dialog is a flex column; only .goal-summary-body scrolls, so the
   title and the Done action stay visible no matter how many goals are listed. */
.goal-summary-dialog {
  margin: auto;                       /* both axes — the centering mechanism */
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg, 12px);
  padding: 0;
  width: min(560px, calc(100vw - 2rem));
  max-height: 85vh;
  max-height: 85dvh;
  overflow: hidden;
  background: var(--color-surface, #fff);
  color: inherit;
}

/* DISPLAY IS GATED ON [open] — do not hoist `display:flex` onto the bare class.
   The UA stylesheet closes a dialog with `dialog:not([open]) { display: none }`;
   an unconditional `display:flex` on the class is MORE SPECIFIC and silently
   overrides it, so pressing Done removed the `open` attribute and left the dialog
   rendered as ordinary block content at the bottom of /goals/ (QA, 2026-07-27).
   Gating both states keeps the closed dialog structurally absent from layout. */
.goal-summary-dialog:not([open]) { display: none; }
.goal-summary-dialog[open] { display: flex; flex-direction: column; }
.goal-summary-dialog::backdrop { background: rgba(0, 0, 0, 0.45); }

.goal-summary-header { padding: var(--space-4) var(--space-4) 0; }
.goal-summary-header h2 { margin: 0; }
.goal-summary-intro { margin: var(--space-1) 0 0; }

/* The ONLY scrolling region. */
.goal-summary-body {
  padding: var(--space-3) var(--space-4) var(--space-4);
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;                      /* lets a flex child actually shrink+scroll */
}

.goal-summary-card {
  border-top: 1px solid var(--color-border);
  padding: var(--space-3) 0;
}
.goal-summary-card:first-child { border-top: 0; padding-top: var(--space-1); }

.goal-summary-card-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-3); margin-bottom: var(--space-2);
}
.goal-summary-name { margin: 0; font-size: 1rem; overflow-wrap: anywhere; }
/* Edit never gets displaced by a long name: the name wraps, Edit holds its width. */
.goal-summary-edit { white-space: nowrap; flex: 0 0 auto; }

/* 2x2: two columns, two rows. Collapses to one column only when genuinely narrow,
   so the pairs stay on one row wherever practical. */
.goal-summary-grid {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2) var(--space-4);
}
.goal-summary-cell { min-width: 0; }
.goal-summary-cell dt {
  margin: 0;
  font-size: 13px;
  color: var(--color-text-muted, #666);
}
.goal-summary-cell dd {
  margin: 0;
  font-variant-numeric: tabular-nums;  /* digits align down the columns */
  overflow-wrap: anywhere;
}

/* Visually distinct summary row, outside the scroll region so it stays put. */
.goal-summary-total {
  display: flex; justify-content: space-between; gap: var(--space-3);
  margin: 0; padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-border);
  background: var(--color-surface-2, rgba(0, 0, 0, 0.03));
}
.goal-summary-total strong { font-variant-numeric: tabular-nums; }

.goal-summary-actions {
  margin: 0; padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-border);
  display: flex; justify-content: flex-end;
}

@media (max-width: 480px) {
  .goal-summary-dialog {
    width: calc(100vw - 1rem);
    max-height: 90vh;
    max-height: 90dvh;
  }
  /* Single column only here — prevents any horizontal overflow on small screens. */
  .goal-summary-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ── #321: monthly contributions, the summary shortcut, and saved next steps ──────
   One spelling ("$X/mo") on the Current Goals badges, the goal cards, the Goal Summary
   and the page total. The cards and the summary label it "Monthly contribution"; the
   compact badges show the amount alone, with the words kept for screen readers. Nothing
   here changes a card's bar, timeline, funded figure or proportions. */

/* Current Goals badge. The monthly line is the amount alone, in the badge's own regular
   weight and muted colour (.goal-badge-monthly): no rule here makes it bold. Its
   "Monthly contribution" words are screen-reader-only (.visually-hidden in the fragment). */
.goal-badge-name { font-weight: 600; color: var(--color-primary); font-size: 11px; }
.goal-badge-monthly-amount { font-weight: 400; }

/* Every badge now carries a second line, and an overview badge carries a goal's name, so
   the strip has to be able to give way: it never grows past its container, the badge list
   may shrink and wrap beside the label, and on a phone the label sits above the badges
   instead of beside them. */
.current-goals-card { max-width: 100%; box-sizing: border-box; }
.goal-badges { min-width: 0; }
@media (max-width: 480px) {
  .current-goals-card { flex-wrap: wrap; }
}

/* Goal card: label and amount stay on the card's one centred line, and wrap as a pair. */
.goal-card-contribution-label {
  font-size: 13px;
  font-weight: 400;
  color: var(--color-text-muted);
  margin-right: 6px;
}
.goal-card-contribution-amount { font-variant-numeric: tabular-nums; }

/* Goal card: the next-step count. A quiet text control, not a second primary action
   beside Update; the footer row it sits in already wraps on a narrow card. */
.goal-card-next-steps {
  margin-left: auto;
  padding: 2px 10px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--color-primary);
  background: rgba(62, 92, 118, 0.08);
  border: 1px solid transparent;
  border-radius: 999px;
  cursor: pointer;
}
.goal-card-next-steps:hover,
.goal-card-next-steps:focus-visible { border-color: var(--color-secondary); }

/* Goals heading with the "Your goals at a glance" button beside it. The heading keeps its
   own bottom margin on the row, so the cards sit where they always did. */
.goals-heading-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
}
.goals-heading-row .section-label { margin-bottom: 0; }
.goals-summary-open { font-size: 13px; padding: 6px 14px; }

/* The persistent page total (#321; placed and restyled in #349): the closing lines of "Your
   Progress" — centred, with no panel, border or fill of their own.

   The label and the amount are ONE line wherever that line fits. To make it fit a phone
   without shrinking to nothing, the line's type follows the width it actually has: the block
   is a size container and the line is set at 1/21.5 of its width, never under a readable
   12px and never over the body's 16px. A phone's card leaves the block about 270px of a
   390px window, so there it also takes 8px of the card's 20px side padding on each side —
   13px type, which holds a four-figure total with cents on the line.

   Where it still cannot fit — a narrower window, a longer amount, longer words in another
   language — it WRAPS, and nothing else: the amount is one unbreakable word, so it drops
   whole onto a second centred line under the label (and the label's own words wrap only if
   the label alone is too wide). It never runs outside the card, clips, or scrolls the page
   sideways. The goal count sits under it and scales with it, always the smaller line.
   Measured at 320, 360, 390 and 1280px in visual_qa/tests/test_goals_page_cleanup.py. */
.goals-monthly-total {
  container-type: inline-size;
  margin-top: var(--space-3);
  text-align: center;
}
.goals-monthly-total p { margin: 0; }
.goals-monthly-total-line {
  font-size: clamp(12px, calc(100cqw / 21.5), 16px);
  line-height: 1.35;
}
.goals-monthly-total-label { font-weight: 600; }
.goals-monthly-total-amount {
  font-weight: 700; color: var(--color-primary);
  white-space: nowrap;                    /* "$1,234.56/mo" is never broken — not at "/" either */
}
.goals-monthly-total .goals-monthly-total-scope {
  font-size: clamp(11px, calc(100cqw / 24), 13px);
  margin-top: 2px;
}
/* Where .dashboard-column has its phone padding (20px a side — see the 900px block above). */
@media (max-width: 900px) {
  .goals-monthly-total { margin-inline: -8px; }
}

/* Goal Summary: "Your next steps". Each step is its copy with its one control beside it;
   on a narrow dialog the control drops under the copy rather than squeezing it. */
.goal-summary-steps {
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
}
.goal-summary-steps-title { margin: 0; font-size: 1rem; }
.goal-summary-steps-intro,
.goal-summary-steps-note,
.goal-summary-steps-empty { margin: var(--space-1) 0 0; font-size: 13px; }
.goal-summary-steps-list { list-style: none; margin: var(--space-2) 0 0; padding: 0; }
.goal-summary-step {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-2) 0;
  border-top: 1px solid var(--color-border);
}
.goal-summary-step:first-child { border-top: 0; }
.goal-summary-step-copy {
  display: flex; flex-direction: column; gap: 2px;
  flex: 1 1 240px; min-width: 0;
}
.goal-summary-step-headline { overflow-wrap: anywhere; }
.goal-summary-step-detail { font-size: 13px; overflow-wrap: anywhere; }
.goal-summary-step-form { margin: 0; flex: 0 0 auto; }
.goal-summary-step-done { font-size: 13px; padding: 6px 14px; white-space: nowrap; }
/* The goal list follows the steps: its first card needs the rule the steps end with. */
.goal-summary-steps + .goal-summary-card { border-top: 0; padding-top: var(--space-3); }
