/* ============================================================
   20 — SAVINGS OPPORTUNITIES
   A dense, scannable ranked list. Every figure is written at
   runtime from window.Pyncht; this file only shapes it.
   ============================================================ */

.opps {
  background: var(--surface-alt);
  border-top: 1px solid var(--border);
  /* Alignment gutter for the category column. The tinted chip itself is
     always fit-content — this only sizes the grid TRACK so titles line up.
     On the 8pt grid like everything else: 96 + 32. */
  --opp-chip: calc(var(--s-12) + var(--s-7));
  --opp-accent: var(--c-brand);
}

.opps__sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ---------------- degraded / no-JS state ----------------
   Nothing below the intro contains an authored number, so until the engine
   has written the figures there is nothing honest to show. `.is-ready` is
   added by the script only after every row is populated. */

.opps:not(.is-ready) .opps__disclaimer,
.opps:not(.is-ready) .opps__summary,
.opps:not(.is-ready) .opps__toolbar,
.opps:not(.is-ready) .opps__panel,
.opps:not(.is-ready) .opps__bar,
.opps:not(.is-ready) .opps__method { display: none; }

.opps:not(.is-degraded) .opps__fallback { display: none; }

.opps__fallback {
  max-width: 62ch;
  margin-top: var(--s-7);
  padding: var(--s-5) var(--s-6);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}
/* <noscript> renders only with scripting off, where `.is-degraded` can
   never be set — so it opts out of the rule above. */
.opps:not(.is-degraded) .opps__fallback--noscript { display: block; }

/* ---------------- head ---------------- */

.opps__title { margin-top: var(--s-3); max-width: 26ch; }
.opps__lead { margin-top: var(--s-5); }

/* The sample-data disclosure is the one line a sceptical reader must be able
   to read, so it takes --text-body (7.38:1 on --surface-alt), not the muted
   ink it used to carry (4.03:1 — under AA at this size). */
.opps__disclaimer {
  margin-top: var(--s-3);
  max-width: 78ch;
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-body);
}

/* ---------------- summary strip ---------------- */

.opps__summary {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin: var(--s-7) 0 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.opps__stat { padding: var(--s-6) var(--s-6) var(--s-6) 0; min-width: 0; }
.opps__stat + .opps__stat {
  border-left: 1px solid var(--border);
  padding-left: var(--s-6);
}

.opps__stat-label {
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-body);
  line-height: var(--lh-normal);
}

.opps__stat-value {
  margin: var(--s-2) 0 0;
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--lh-tight);
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
  min-height: 1em;
}

.opps__stat-note,
.opps__stat-recon {
  margin: var(--s-2) 0 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-body);
  font-variant-numeric: tabular-nums;
  text-wrap: pretty;
}

/* The subtraction that gets the tile from the rows to the figure. It reads as
   a footnote to the number, so it sits a step back on a hairline rule rather
   than competing with the split line above it. */
.opps__stat-recon {
  margin-top: var(--s-1);
  padding-top: var(--s-2);
  border-top: 1px solid var(--border);
  max-width: 34ch;
}
.opps__stat-recon[hidden] { display: none; }

/* ---------------- methodology, under the evidence it qualifies ---------------- */

.opps__method {
  margin-top: var(--s-7);
  border-top: 1px solid var(--border);
  padding-top: var(--s-4);
}
.opps__method-summary {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semi);
  letter-spacing: var(--tracking-snug);
  color: var(--text-strong);
  cursor: pointer;
  list-style: none;
}
.opps__method-summary::-webkit-details-marker { display: none; }
.opps__method-caret {
  width: var(--s-4);
  height: var(--s-4);
  color: var(--text-muted);
  transform: rotate(-90deg);
  transition: transform var(--t-base) var(--ease-out);
}
.opps__method[open] .opps__method-caret { transform: none; }

.opps__notes {
  display: grid;
  gap: var(--s-2);
  margin-top: var(--s-2);
  padding-bottom: var(--s-3);
  max-width: 78ch;
}
.opps__notes li {
  position: relative;
  padding-left: var(--s-4);
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-body);
  text-wrap: pretty;
}
.opps__notes li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .55em;
  width: var(--s-2);
  height: 1px;
  background: var(--c-slate-light);
}

/* ---------------- toolbar: filters + status ---------------- */

.opps__toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s-4);
  margin-top: var(--s-6);
}

.opps__toolbar-end {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
}

.opps__tablist {
  display: flex;
  gap: var(--s-1);
  max-width: 100%;
  padding: var(--s-1);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  box-shadow: var(--sh-xs);
  overflow-x: auto;
  scrollbar-width: none;
}
.opps__tablist::-webkit-scrollbar { display: none; }

.opps__tab {
  flex: 0 0 auto;
  height: 44px;
  padding-inline: var(--s-5);
  border-radius: var(--r-pill);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  letter-spacing: var(--tracking-snug);
  color: var(--text-body);
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease-out),
              color var(--t-fast) var(--ease-out);
}
.opps__tab:hover { background: var(--surface-alt); color: var(--text-strong); }
.opps__tab[aria-selected="true"] {
  background: var(--c-navy);
  color: var(--text-invert);
}

.opps__status,
.opps__restore {
  font-size: var(--fs-xs);
  color: var(--text-body);
  font-variant-numeric: tabular-nums;
}

.opps__restore {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
}
.opps__restore[hidden] { display: none; }
/* Undoing a dismissal must be at least as legible as the text explaining it.
   Brand ink on --surface-alt is 4.45:1 at 13px — under AA — so the resting
   state is --text-strong (14.7:1) and the brand is reserved for hover, where
   --c-brand-press clears AA at 5.8:1. The underline carries the affordance,
   so nothing here depends on colour alone. */
.opps__restore-btn {
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  color: var(--text-strong);
  text-decoration: underline;
  text-underline-offset: .2em;
  /* keeps the 44px target without a 44px-tall link in a text row */
  padding-block: var(--s-3);
}
.opps__restore-btn:hover { color: var(--c-brand-press); }

/* ---------------- the list ---------------- */

.opps__list {
  margin-top: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-xs);
}
.opps__panel.is-empty .opps__list { display: none; }

.opps__empty {
  margin-top: var(--s-5);
  padding: var(--s-7) var(--s-6);
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--r-lg);
  font-size: var(--fs-sm);
  color: var(--text-body);
  text-align: center;
}
.opps__empty[hidden] { display: none; }

.opps__row {
  position: relative;
  border-top: 1px solid var(--border);
  transition: background var(--t-base) var(--ease-out);
}
.opps__row[hidden] { display: none; }
.opps__row.is-first { border-top: 0; }

.opps__row--interchange { --opp-accent: var(--c-brand); }
.opps__row--routing     { --opp-accent: var(--c-cyan); }
.opps__row--recovery    { --opp-accent: var(--c-lime); }
.opps__row--risk        { --opp-accent: var(--c-orange); }
.opps__row--pricing     { --opp-accent: var(--c-magenta); }

/* Cyan, lime and orange are near-white in luminance: tinting them straight
   into white gives a chip you cannot see (lime landed at 1.04:1). Pulling
   each accent toward navy FIRST puts all five tints in one visible band —
   1.29–1.53:1 fill on white — and gives the hairline and glyph a shared ink
   that clears 3:1 against the fill on every one of the five. Resolved per
   row, so each modifier's --opp-accent is the one that feeds these. */
.opps__row {
  --opp-tone: color-mix(in srgb, var(--opp-accent) 60%, var(--c-navy));
  --opp-ink:  color-mix(in srgb, var(--opp-accent) 50%, var(--c-navy));
  --opp-solid: color-mix(in srgb, var(--opp-accent) 32%, var(--c-navy));
}

/* enabled: the row commits — accent rail, faint accent wash */
.opps__row.is-enabled {
  background: color-mix(in srgb, var(--opp-accent) 5%, var(--c-white));
}
.opps__row.is-enabled::before {
  content: "";
  position: absolute;
  left: var(--s-2);
  top: var(--s-4);
  bottom: var(--s-4);
  width: var(--s-1);
  border-radius: var(--r-pill);
  background: var(--opp-ink);
}

.opps__rowhead { font-size: inherit; font-weight: inherit; letter-spacing: normal; }

/* ---------------- trigger row ---------------- */

.opps__trigger {
  display: grid;
  width: 100%;
  /* the category track holds the alignment; the chip stays fit-content */
  grid-template-columns: minmax(var(--opp-chip), auto) minmax(0, 1fr) auto auto;
  grid-template-areas: "chip main amount caret";
  align-items: center;
  gap: var(--s-5);
  padding: var(--s-5) var(--s-6);
  text-align: left;
  transition: background var(--t-fast) var(--ease-out);
}
.opps__trigger:hover { background: color-mix(in srgb, var(--c-navy) 3%, transparent); }
.opps__row.is-first .opps__trigger {
  border-top-left-radius: var(--r-lg);
  border-top-right-radius: var(--r-lg);
}
.opps__row.is-last:not(.is-open) .opps__trigger {
  border-bottom-left-radius: var(--r-lg);
  border-bottom-right-radius: var(--r-lg);
}

/* category chip */
.opps__chip {
  grid-area: chip;
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  height: var(--s-7);
  padding-inline: var(--s-3);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--opp-tone) 26%, var(--c-white));
  box-shadow: inset 0 0 0 1px var(--opp-ink);
  transition: background var(--t-base) var(--ease-out),
              box-shadow var(--t-base) var(--ease-out);
}
.opps__glyph {
  flex: 0 0 auto;
  width: var(--s-5);
  height: var(--s-5);
  color: var(--opp-ink);
}
.opps__chip-label {
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  letter-spacing: var(--tracking-snug);
  color: var(--text-strong);
  white-space: nowrap;
}
.opps__row.is-enabled .opps__chip {
  background: var(--opp-solid);
  box-shadow: inset 0 0 0 1px var(--opp-solid);
}
.opps__row.is-enabled .opps__glyph,
.opps__row.is-enabled .opps__chip-label { color: var(--text-invert); }

/* title + meta */
.opps__main { grid-area: main; min-width: 0; }

.opps__name {
  display: block;
  font-size: var(--fs-body);
  font-weight: var(--fw-semi);
  line-height: var(--lh-snug);
  letter-spacing: var(--tracking-snug);
  color: var(--text-strong);
  text-wrap: pretty;
}

.opps__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-2);
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-body);
  font-variant-numeric: tabular-nums;
}
.opps__effort { display: inline-flex; align-items: center; gap: var(--s-2); }
/* Decorative and aria-hidden, but still rendered text: --c-slate-light put it
   at 4.26:1, under AA. It takes the same ink as the meta it divides. */
.opps__sep { color: var(--text-body); }

.opps__meter { display: inline-flex; gap: var(--s-1); }
/* A meter only reads if the filled segments separate from the empty ones.
   The track is navy at 26% (1.7:1 on white — visible as a slot, quiet as a
   rule) and the filled segments are full navy, so on-vs-off is 9:1 rather
   than the 1.96:1 that made a 1-of-3 meter look like a rendering artefact. */
.opps__meter i {
  width: var(--s-3);
  height: var(--s-1);
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--c-navy) 26%, var(--c-white));
}
.opps__meter i.is-on { background: var(--c-navy); }

/* annual figure */
.opps__amount {
  grid-area: amount;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  max-width: calc(var(--s-14) + var(--s-4));
}
/* The payoff column: the largest thing in its own row, but a step below the
   strip and the selection bar. A single $1,613 row set at the same size as
   the headline it is one seventh of inverts the hierarchy. */
.opps__value {
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.opps__per {
  margin-inline-start: .12em;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tracking-normal);
  color: var(--text-body);
}
.opps__kind {
  margin-top: var(--s-1);
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-body);
  text-wrap: pretty;
}

.opps__caret {
  grid-area: caret;
  width: var(--s-5);
  height: var(--s-5);
  color: var(--text-body);
  transition: transform var(--t-base) var(--ease-out);
}
.opps__row.is-open .opps__caret { transform: rotate(180deg); }

/* ---------------- expanded body ---------------- */

.opps__body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--t-base) var(--ease-out);
}
.opps__row.is-open .opps__body { grid-template-rows: 1fr; }

/* Padding-bottom is NOT clipped by a 0fr track, so it would leave every
   closed row 24px too tall. It belongs to the open state only.
   `visibility: hidden` is what actually takes the collapsed Enable and
   Dismiss buttons out of the tab order and out of the a11y tree —
   a 0fr track plus overflow:hidden only hides them from the eye. */
.opps__body-inner {
  min-height: 0;
  overflow: hidden;
  padding: 0 var(--s-6);
  opacity: 0;
  visibility: hidden;
  transform: translateY(calc(var(--s-1) * -1));
  transition: opacity var(--t-base) var(--ease-out),
              transform var(--t-base) var(--ease-out),
              visibility var(--t-base) var(--ease-out);
}
.opps__row.is-open .opps__body-inner {
  padding-block-end: var(--s-6);
  opacity: 1;
  visibility: visible;
  transform: none;
}

.opps__why {
  max-width: 68ch;
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}

.opps__math {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  margin-top: var(--s-4);
  padding: var(--s-3) var(--s-4);
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.opps__math-label {
  flex: 0 0 auto;
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-body);
}
/* The receipt wraps rather than clipping — a truncated expression is worse
   than a two-line one, and a horizontal scroller is unreachable by keyboard. */
.opps__math code {
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}

.opps__caveat {
  max-width: 68ch;
  margin-top: var(--s-3);
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-body);
  text-wrap: pretty;
}
.opps__caveat[hidden] { display: none; }

.opps__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-top: var(--s-5);
}

/* Both actions are the base .btn — only the colour changes here.
   Enabled state is driven off the row class, since the button carries its
   state in its name rather than in aria-pressed. */
.opps__row.is-enabled .opps__enable,
.opps__row.is-enabled .opps__enable:hover {
  background: var(--opp-solid);
}
.opps__tick { display: none; width: var(--s-4); height: var(--s-4); }
.opps__row.is-enabled .opps__tick { display: block; }

/* --border on white is 1.23:1 — below 1.4.11's 3:1 for the visible boundary
   of a control. --c-slate-light is 4.28:1 and is the ink the row's own meta
   text already uses, so the outline reads without shouting. */
.opps__dismiss {
  --btn-bg: transparent;
  --btn-fg: var(--text-body);
  box-shadow: inset 0 0 0 1px var(--c-slate-light);
}
.opps__dismiss:hover {
  background: transparent;
  color: var(--text-strong);
  box-shadow: inset 0 0 0 1px var(--text-strong);
  transform: none;
}

/* .btn--sm is 36px; touch pointers and narrow viewports get a 44px target. */
@media (pointer: coarse), (max-width: 899px) {
  .opps__enable,
  .opps__dismiss { height: 44px; }
}

/* ---------------- sticky selection bar ---------------- */

.opps__bar {
  position: sticky;
  bottom: calc(var(--s-5) + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-sticky);
  /* Grid, not flex, so the netting note can span the full width underneath
     without pushing the figure or the CTA onto a row of their own. The note
     row collapses to zero when the note is hidden. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  grid-template-areas:
    "lead total cta"
    "note total cta";
  align-items: center;
  column-gap: var(--s-5);
  row-gap: var(--s-1);
  margin-top: var(--s-6);
  padding: var(--s-4) var(--s-4) var(--s-4) var(--s-6);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-lg);
  transition: box-shadow var(--t-base) var(--ease-out);
}

/* Nothing selected yet: sit quietly at the end of the list instead of
   hovering over it. Enabling the first row is what lifts the bar. */
.opps__bar.is-empty {
  position: static;
  box-shadow: var(--sh-xs);
}

.opps__bar-lead { grid-area: lead; min-width: 0; }
.opps__bar-count {
  display: block;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semi);
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}
.opps__bar-hint { display: none; font-size: var(--fs-sm); color: var(--text-body); }
.opps__bar.is-empty .opps__bar-count { display: none; }
.opps__bar.is-empty .opps__bar-hint { display: inline; }
.opps__bar.is-empty .opps__bar-total { display: none; }

.opps__bar-note {
  grid-area: note;
  min-width: 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-body);
  font-variant-numeric: tabular-nums;
  text-wrap: pretty;
}
.opps__bar-note[hidden] { display: none; }

.opps__bar-total {
  grid-area: total;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
}
.opps__bar-label {
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-body);
}
/* The number the reader is actively building. It is the point of the section,
   so it is set at the strip's size — above every individual row. */
.opps__bar-value {
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}
.opps__bar-split {
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-body);
  font-variant-numeric: tabular-nums;
}
.opps__bar.is-bumped .opps__bar-value {
  animation: opps-bump var(--t-base) var(--ease-out);
}
@keyframes opps-bump {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.05); }
  100% { transform: scale(1); }
}

.opps__bar-cta { grid-area: cta; align-self: center; }

/* "Annual impact selected" is already carried by the count beside it, and the
   fee/revenue split is on the tiles and in every row above — on a phone they
   cost two wrapped lines of a bar that is already covering the list. So both
   carry base.css's .sr-only in the markup and are revealed from 640px up:
   the pattern is not re-implemented here, only undone where there is room.
   Anything that would surprise you — the netting, a selection hidden by the
   filter — is in .opps__bar-note, which is visible at every width. */
@media (min-width: 640px) {
  .opps__bar-label.sr-only,
  .opps__bar-split.sr-only {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip: auto;
    clip-path: none;
  }
}

/* ---------------- responsive ---------------- */

@media (min-width: 900px) {
  /* align the expanded body with the title column */
  .opps__body-inner {
    padding-left: calc(var(--s-6) + var(--opp-chip) + var(--s-5));
  }
}

@media (max-width: 899px) {
  .opps__trigger {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "chip   caret"
      "main   main"
      "amount amount";
    align-items: start;
    gap: var(--s-3) var(--s-4);
    padding: var(--s-5);
  }
  .opps__caret { align-self: center; }
  .opps__amount {
    flex-direction: row;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--s-1) var(--s-3);
    max-width: none;
    text-align: left;
  }
  .opps__kind { margin-top: 0; }
  .opps__body-inner { padding-inline: var(--s-5); }
  .opps__row.is-open .opps__body-inner { padding-block-end: var(--s-5); }
}

@media (max-width: 639px) {
  .opps__summary { grid-template-columns: minmax(0, 1fr); }
  .opps__stat { padding: var(--s-5) 0; }
  .opps__stat + .opps__stat {
    border-left: 0;
    border-top: 1px solid var(--border);
    padding-left: 0;
  }
  .opps__toolbar { align-items: flex-start; flex-direction: column; }

  /* Two-up grid rather than hiding the last filter in a scroller. */
  .opps__tablist {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    width: 100%;
    overflow-x: visible;
    border-radius: var(--r-lg);
  }
  .opps__tab { padding-inline: var(--s-3); }

  /* Stack the receipt so the arithmetic gets the full width. */
  .opps__math { display: block; }
  .opps__math-label { display: block; margin-bottom: var(--s-1); }

  /* A sticky bar occludes the list it is summarising, so it earns its height.
     The count and the figure share the top row, the note takes the width it
     needs beneath them, and only the CTA gets a row to itself. */
  .opps__bar {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "lead total"
      "note note"
      "cta  cta";
    align-items: baseline;
    column-gap: var(--s-3);
    padding: var(--s-3) var(--s-4);
  }
  .opps__bar-cta { margin-top: var(--s-2); width: 100%; }

}
