/* ============================================================
   30-LEAKS — failed payments and revenue leaks
   ------------------------------------------------------------
   VISUAL CHOICE: ranked rows, not one horizontal stacked bar.
   A single stacked bar can encode ONE dimension — share of lost
   volume — and this section lives or dies on a SECOND dimension:
   how much of each slice is actually winnable. Seven segments in
   one bar would need seven sub-splits inside segments as narrow
   as 4% of the width; expired card at 88% recoverable and
   suspected fraud at 12% would be indistinguishable slivers.
   Ranked rows give every reason its own baseline, bar LENGTH
   carries the ranking and the brand-coloured segment carries the
   winnable dollars. For length to be readable the WHOLE bar has
   to be visible, not just the purple part: the pale "gone" fill
   alone is 1.66:1 on white, so the bar is outlined with a 1px
   navy-50% ring measured at 3.17:1 (WCAG non-text minimum is
   3:1). That ring is drawn on .leaks__bar::after, NOT as an inset
   shadow on .leaks__bar itself: an inset shadow paints above the
   element's background but below its descendants, and the two
   segments cover the padding box edge to edge, so an inset ring
   on the bar is painted and then completely hidden. Verified by
   rendering both. Bar ends are therefore legible even on a row
   that is almost entirely "gone" — do not lighten that ring, and
   do not "simplify" it back onto the bar. The hatch
   inside the gone fill is a second, redundant encoding
   (slate on the fill = 4.71:1); at 360px the shortest row's gone
   sliver is ~1.3px, narrower than the hatch period, which is why
   the split is also printed as text beside every bar.
   The two FILLS against each other measure 2.2–2.8:1 depending
   where the brand gradient is sampled — under the 3:1 floor for
   adjacent graphical objects, and not fixable without abandoning
   the brand ramp. That is precisely why the hatch, the tier chip
   and the printed dollars exist: the colour pair is the one
   encoding here that does NOT carry the split on its own.
   ============================================================ */

.leaks {
  background: var(--surface);
  /* clip, not hidden: `overflow: hidden` makes this a scroll container and
     silently kills position:sticky on the retry panel below. `overflow-x:
     clip` gives the same belt-and-braces at 360px with no scroll container. */
  overflow-x: clip;
}

/* ---------------- degraded state ----------------
   Set by the section script on a missing engine or any throw. Everything
   that would print a number is removed; the lead carries one honest line. */
.leaks.is-degraded .leaks__grid,
.leaks.is-degraded .leaks__cb { display: none; }

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

.leaks__head {
  max-width: 62ch;
  margin-bottom: var(--s-9);
}
/* Same header rhythm as the sibling section (10-costs.css:37): one step of
   air under the eyebrow, one under the title. Without it the eyebrow sits on
   5px of line-box leading and reads as a layout bug on the first thing here. */
.leaks__eyebrow { margin-bottom: var(--s-4); }
.leaks__eyebrow-icon { width: var(--s-4); height: var(--s-4); flex: none; }
.leaks__title { margin-bottom: var(--s-4); }
.leaks__lead { color: var(--text-body); }

/* ---------------- layout ---------------- */

.leaks__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-6);
  align-items: start;
}
@media (min-width: 960px) {
  .leaks__grid { grid-template-columns: minmax(0, 1.66fr) minmax(0, 1fr); }
}

.leaks__panel {
  border-radius: var(--r-xl);
  padding: clamp(var(--s-5), 3vw, var(--s-7));
  min-width: 0;
}
.leaks__panel--reasons {
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--sh-xs);
}

/* ---------------- reasons panel head ---------------- */

/* Column, not a wrapped row: title-plus-sub and the legend together exceed
   the panel at every width the section supports, so `space-between` never
   resolved and the legend always dropped to its own line anyway. Stating the
   stack directly is the layout that actually renders. */
.leaks__panel-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-3);
  padding-bottom: var(--s-5);
  border-bottom: 1px solid var(--c-hairline-soft);
}
.leaks__panel-title { font-size: var(--fs-h4); }
.leaks__panel-title--invert { color: var(--text-invert); }
.leaks__panel-sub {
  margin-top: var(--s-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--text-body);
}

.leaks__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-5);
  font-size: var(--fs-xs);
  color: var(--text-body);
}
.leaks__legend-item { display: inline-flex; align-items: center; gap: var(--s-2); }
/* the key draws the bar at bar height, so the legend and the chart agree */
.leaks__swatch {
  width: var(--s-6); height: var(--s-3);
  border-radius: var(--r-xs);
  flex: none;
}
.leaks__swatch--rec { background: linear-gradient(90deg, var(--c-brand), var(--c-brand-hover)); }
.leaks__swatch--gone {
  background-color: color-mix(in srgb, var(--c-slate-light) 34%, var(--c-hairline-soft));
  background-image: repeating-linear-gradient(135deg,
    var(--c-slate) 0 1px, transparent 1px 5px);
  /* same ring as the bar, so the legend key matches what the chart draws */
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-navy) 50%, transparent);
}

/* ---------------- ranked rows ---------------- */

.leaks__rows { margin-top: var(--s-2); }
.leaks__empty { padding-top: var(--s-6); font-size: var(--fs-sm); color: var(--text-body); }

.leaks__row {
  padding-block: var(--s-5);
  border-bottom: 1px solid var(--c-hairline-soft);
}
.leaks__row:last-child { border-bottom: 0; padding-bottom: var(--s-1); }

.leaks__row-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: var(--s-3);
  row-gap: var(--s-1);
  margin-bottom: var(--s-3);
}
.leaks__rank {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  color: var(--text-body);
  font-variant-numeric: tabular-nums;
  /* optical: the mono digits sit a hair low against the label baseline */
  position: relative;
  top: -1px;
}
.leaks__reason {
  font-size: var(--fs-sm);
  font-weight: var(--fw-semi);
  letter-spacing: var(--tracking-snug);
  color: var(--text-strong);
}
.leaks__count {
  grid-column: 2;
  font-size: var(--fs-xs);
  color: var(--text-body);
  font-variant-numeric: tabular-nums;
}
.leaks__vol {
  grid-column: 3;
  grid-row: 1 / span 2;
  align-self: center;
  /* the seven figures happen to be the same width today; alignment must not
     depend on that surviving the next dataset */
  text-align: right;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semi);
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}

/* track = full width; bar = share of the largest reason */
.leaks__track {
  display: block;
  position: relative;
  width: 100%;
  height: var(--s-3);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.leaks__bar {
  display: flex;
  position: relative;
  width: var(--bar, 100%);
  height: 100%;
  border-radius: var(--r-pill);
  overflow: hidden;
}
/* 3.17:1 on --surface — this ring is what makes total bar length readable
   where the fill is pale. It sits on ::after so it paints ABOVE the opaque
   segments; as an inset shadow on .leaks__bar it is drawn under them and
   never seen. Absolutely positioned, so it is not a flex item.
   See the file header before touching it. */
.leaks__bar::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-navy) 50%, transparent);
  pointer-events: none;
}
.leaks__seg { display: block; height: 100%; }
.leaks__seg--rec {
  background: linear-gradient(90deg, var(--c-brand), var(--c-brand-hover));
}
.leaks__seg--gone {
  background-color: color-mix(in srgb, var(--c-slate-light) 34%, var(--c-hairline-soft));
  /* hatch, so recoverable vs gone is never colour-only */
  background-image: repeating-linear-gradient(135deg,
    var(--c-slate) 0 1px, transparent 1px 5px);
}

/* wipe: reveals the bars left-to-right using transform only */
.leaks__wipe {
  position: absolute;
  inset: 0;
  background: var(--surface);
  transform-origin: right center;
  transform: scaleX(1);
  transition: transform var(--t-slower) var(--ease-expo),
              opacity var(--t-fast) linear;
  /* stagger derived from the motion tokens: .4 × --t-fast ≈ 56ms per row.
     The opacity delay is the FULL transform duration, not --t-slow: the
     longhand below overrides the shorthand's delay, and at --t-slow the
     cover faded out 280ms before the wipe it is covering had finished. */
  transition-delay: calc(var(--i, 0) * var(--t-fast) * .4),
                    calc(var(--i, 0) * var(--t-fast) * .4 + var(--t-slower));
}
.leaks__panel--reasons.is-visible .leaks__wipe { transform: scaleX(0); opacity: 0; }

.leaks__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2) var(--s-4);
  margin-top: var(--s-3);
}
/* facts stay glued together on the left so the action link owns the right
   edge; at 360px the whole thing just wraps into three stacked lines */
.leaks__facts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  min-width: 0;
}
.leaks__fix {
  font-size: var(--fs-xs);
  /* 13px text is a 20px hit area; pad to 44px and pull the padding back out
     of the flow so the row rhythm is unchanged */
  padding-block: var(--s-3);
  margin-block: calc(var(--s-3) * -1);
}

.leaks__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  height: var(--s-6);
  padding-inline: var(--s-3);
  border-radius: var(--r-pill);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  letter-spacing: var(--tracking-snug);
  font-variant-numeric: tabular-nums;
  /* defaults = low tier; tiers override below */
  background: var(--c-hairline-soft);
  color: var(--text-body);
  box-shadow: inset 0 0 0 1px var(--border);
}
.leaks__chip-icon { width: var(--s-3); height: var(--s-3); flex: none; }
.leaks__chip-money { font-weight: var(--fw-medium); }

.leaks__row--high .leaks__chip {
  background: var(--c-brand-tint);
  color: var(--c-brand-press);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-brand) 24%, transparent);
}
.leaks__row--mid .leaks__chip {
  background: var(--surface-alt);
  color: var(--text-body);
  box-shadow: inset 0 0 0 1px var(--border);
}

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

/* every row above the 75% recoverability tier gets a quiet gutter marker
   and an action link — position, not colour, carries "look here" */
.leaks__row--high {
  position: relative;
}
.leaks__row--high::before {
  content: "";
  position: absolute;
  left: calc(var(--s-4) * -1);
  top: var(--s-5);
  bottom: var(--s-5);
  width: 2px;
  border-radius: var(--r-pill);
  background: var(--c-brand);
  /* .55 measured 2.21:1 on --surface; .75 is 3.08:1, clearing the WCAG
     non-text 3:1 floor. Still reads as a quiet marker at 2px wide. */
  opacity: .75;
}
@media (max-width: 767px) {
  .leaks__row--high::before { left: calc(var(--s-3) * -1); }
}

.leaks__panel-foot {
  margin-top: var(--s-6);
  padding-top: var(--s-5);
  border-top: 1px solid var(--c-hairline-soft);
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-body);
  max-width: 68ch;
}

/* ---------------- retry panel ---------------- */

.leaks__panel--retry {
  background: var(--grad-dark);
  color: var(--text-invert-body);
  box-shadow: var(--sh-lg);
  position: relative;
  overflow: hidden;
}
.leaks__panel--retry::after {
  content: "";
  position: absolute;
  inset: auto -30% -55% auto;
  width: 22rem; height: 22rem;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--c-brand) 38%, transparent), transparent 68%);
  pointer-events: none;
}
.leaks__panel--retry > * { position: relative; z-index: var(--z-base); }

/* The row list runs ~1350px at 1440 and the retry panel ~840px, so a plain
   two-column grid ends the right column half a screen early and leaves a void
   roughly a quarter of the section. Sticky is what the composition already
   wanted: the panel tracks the rows the whole way down and the columns
   terminate together. Offset clears the dashboard's sticky top bar (--s-10 =
   64px, declared on .dash-topbar and so not readable from here) plus a step of
   air. Declared AFTER the position:relative above — equal specificity, source
   order decides. Needs align-items:start on the grid (it has it) and no
   scroll-container ancestor, which is why .leaks uses overflow-x:clip.

   Gated on HEIGHT as well as width. The panel runs ~830px; stuck at
   88px on a 768px-tall viewport it hangs 146px below the fold for the whole
   sticky range, and the 146px that gets clipped is the "Assumptions, stated"
   paragraph — the one thing in this section that must not be the part you
   cannot read. Below the height gate the panel simply scrolls with the page
   and the right column ends early; a void is a cheaper defect than a hidden
   disclosure. 940px = the panel + the 88px offset + a step of air.
   ponytail: fixed threshold; if the panel's content ever grows past ~850px
   (longer assumptions, large text zoom) raise it to match, or the clip is
   back. max-height + overflow-y would survive that automatically but buys a
   nested scroll region and a keyboard tab stop for a case that is not real. */
@media (min-width: 960px) and (min-height: 940px) {
  .leaks__panel--retry {
    position: sticky;
    top: calc(var(--s-10) + var(--s-6));
    align-self: start;
  }
}

.leaks__retry-head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-bottom: var(--s-7);
}
.leaks__retry-icon { width: var(--s-6); height: var(--s-6); flex: none; color: var(--c-teal); }

.leaks__retry-figure {
  font-size: var(--fs-h2);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-invert);
  font-variant-numeric: tabular-nums;
}
.leaks__retry-caption {
  margin-top: var(--s-3);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--text-invert-body);
  max-width: 32ch;
}

.leaks__retry-list {
  margin-top: var(--s-7);
  border-top: 1px solid color-mix(in srgb, var(--c-white) 14%, transparent);
}
.leaks__retry-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  padding-block: var(--s-3);
  border-bottom: 1px solid color-mix(in srgb, var(--c-white) 9%, transparent);
  font-size: var(--fs-sm);
}
.leaks__retry-row dt { color: var(--text-invert-body); }
.leaks__retry-row dd {
  margin: 0;
  font-weight: var(--fw-semi);
  color: var(--text-invert);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.leaks__retry-row--total {
  border-bottom: 0;
  padding-top: var(--s-4);
}
.leaks__retry-row--total dt { color: var(--c-teal); font-weight: var(--fw-semi); }
.leaks__retry-row--total dd { font-size: var(--fs-h4); }

.leaks__retry-act { margin-top: var(--s-6); }
/* the label carries a dollar figure, so it must be allowed to wrap rather
   than push the dark panel past 360px — .btn is nowrap by default */
.leaks__retry-cta {
  width: 100%;
  height: auto;
  min-height: 44px;
  padding-block: var(--s-3);
  white-space: normal;
  text-align: center;
}

.leaks__assumption {
  margin-top: var(--s-6);
  padding-top: var(--s-5);
  border-top: 1px solid color-mix(in srgb, var(--c-white) 14%, transparent);
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-invert-body);
}

/* ---------------- chargeback strip ---------------- */

.leaks__cb {
  margin-top: var(--s-6);
  padding: clamp(var(--s-5), 3vw, var(--s-7));
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background: var(--surface-alt);
  display: grid;
  gap: var(--s-6);
}
@media (min-width: 1024px) {
  .leaks__cb {
    grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
    align-items: center;
    gap: var(--s-9);
  }
}

.leaks__cb-head { display: flex; align-items: flex-start; gap: var(--s-3); }
.leaks__cb-icon {
  width: var(--s-5); height: var(--s-5); flex: none;
  color: var(--c-brand);
  /* optical nudge, not spacing: drops the 20px glyph onto the h4 cap-height
     instead of its line box. Half a step, expressed off the token rather
     than as a raw px value. */
  margin-top: calc(var(--s-1) / 2);
}
.leaks__cb-title { font-size: var(--fs-h4); }
.leaks__cb-sub {
  margin-top: var(--s-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--text-body);
}

/* pinned counts, never auto-fit: auto-fit resolved to 3 columns at 768 and
   1024 and stranded the fourth stat — the total — alone on its own row.
   2×2 below 1200, 4-up above, nothing in between. The breakpoint is set by
   the NOTES, not the labels: at 1024 the strip's two-column layout leaves the
   stats ~515px, and 4-up gives each note a 113px well that runs the longest
   note to eight lines of two words while its siblings run three. 2×2 there is
   ~245px a column; 4-up only clears 155px a column from 1200 up. */
.leaks__cb-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-5) var(--s-6);
  margin: 0;
}
/* declared after the base rule, not inside the .leaks__cb block above, because
   equal specificity means source order decides which grid wins */
@media (min-width: 1200px) {
  .leaks__cb-stats {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    column-gap: var(--s-5);
  }
}

.leaks__cb-stat { min-width: 0; }
.leaks__cb-label {
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-body);
}
.leaks__cb-value {
  margin: var(--s-2) 0 0;
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-snug);
  color: var(--text-strong);
  font-variant-numeric: tabular-nums;
}
.leaks__cb-note {
  margin: var(--s-1) 0 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
  color: var(--text-body);
}

/* ---------------- reduced motion ---------------- */

@media (prefers-reduced-motion: reduce) {
  .leaks__wipe { display: none; }
}
