/* ==========================================================================
   2K JUMPERS · design tokens
   Portable. Drop verbatim into src/app/globals.css (Next) or link as-is.
   Dark is the primary theme. Light is fully specified, not an auto-flip.

   Every data palette below was validated with the dataviz six-checks
   (node scripts/validate_palette.js). Do not hand-edit a hex without
   re-running it. Results recorded at the bottom of this file.
   ========================================================================== */

:root {
  color-scheme: dark;

  /* ---- Surfaces (dark = default) ------------------------------------- */
  --jm-plane:        #0A0C0E;   /* page background                        */
  --jm-surface-1:    #12161A;   /* cards, chart surface (validated vs)    */
  --jm-surface-2:    #191F25;   /* raised: table header, popovers         */
  --jm-surface-3:    #212932;   /* hover / selected row                   */
  --jm-line:         #232B34;   /* hairline rule                          */
  --jm-line-strong:  #303A45;   /* section divider, axis baseline         */

  /* ---- Ink ------------------------------------------------------------ */
  --jm-ink:          #F2F5F8;
  --jm-ink-2:        #A8B4C0;   /* secondary label                        */
  --jm-ink-3:        #6E7C8A;   /* axis ticks, muted meta                 */
  --jm-ink-inv:      #0A0C0E;

  /* ---- Brand ---------------------------------------------------------- */
  /* Sampled from the logo files: the wordmark underbar is #54FD04 and the
     icon meter is #B4FF28. Those two measure 8.2 apart in normal vision, so
     they read as one colour; #54FD04 is canonical.
     The data ramp below now lives in this same hue, so the site is one green
     family rather than a lime logo sitting on spring-green charts.          */
  --jm-brand:        #54FD04;
  --jm-brand-dim:    #43C904;
  --jm-brand-wash:   rgba(84, 253, 4, 0.12);
  --jm-brand-glow:   0 0 18px rgba(84, 253, 4, 0.45);   /* logo lockups only */

  /* ---- Status (fixed, never themed; ALWAYS ships with glyph + label) -- */
  --jm-good:         #0CA30C;
  --jm-warn:         #FAB219;
  --jm-crit:         #D03B3B;
  --jm-neutral:      #6E7C8A;

  /* ---- Series: head-to-head compare. HARD CAP 2. --------------------- */
  /* A 3rd jumper does not get a 3rd hue. It becomes small multiples.     */
  --jm-series-a:     #3987E5;
  --jm-series-b:     #D95926;
  --jm-series-a-wash: rgba(57, 135, 229, 0.16);
  --jm-series-b-wash: rgba(217, 89, 38, 0.16);

  /* ---- Sequential: make rate / green rate (magnitude, one hue) --------
     Steps run 1 (lightest) to 6 (darkest). Magnitude reads as DISTANCE FROM
     THE SURFACE, so the direction flips with the theme: on dark, the highest
     rate takes rate-1; on light, it takes rate-6. Mapping high to dark on a
     dark surface makes the peak cell recede, which is backwards.            */
  --jm-rate-1:       #EDFFD8;
  --jm-rate-2:       #C0F86A;
  --jm-rate-3:       #9BE62E;
  --jm-rate-4:       #74BC18;
  --jm-rate-5:       #55880F;
  --jm-rate-6:       #3A5F0A;
  --jm-grade-ink:    #10190A;   /* dark ink: dark-theme fills are light         */

  /* ---- Ordinal: badge tier ladder (None→Legend, one hue, blue) -------- */
  /* Blue, not metals. Bronze/silver/gold as data colors is a rainbow and
     fails CVD. Metal tint lives on the tiny tier glyph only.             */
  --jm-tier-0:       #CDE2FB;   /* None            */
  --jm-tier-1:       #9EC5F4;   /* Bronze          */
  --jm-tier-2:       #6DA7EC;   /* Silver          */
  --jm-tier-3:       #3987E5;   /* Gold            */
  --jm-tier-4:       #256ABF;   /* Hall of Fame    */
  --jm-tier-5:       #184F95;   /* Legend          */

  /* Badge tier colours. Promoted from decorative glyph tints to real data
     colours (Lance 2026-08-09: players read badge colour instantly).
     Re-tuned from the raw in-game metals, which failed hard: legend vs bronze
     measured ΔE 9.8 in NORMAL vision. These clear the CVD and normal-vision
     floors; see the validator block at the bottom of this file.
     None and Silver stay grey on purpose, so they miss the chroma floor. That
     is semantic, not an error, and the overlay pairs colour with a dash
     pattern and a direct label so hue never carries tier alone.            */
  --jm-metal-none:   #8A94A0;
  --jm-metal-bronze: #A65E2E;
  --jm-metal-silver: #C9D3DC;
  --jm-metal-gold:   #E3B02A;
  --jm-metal-hof:    #A78BFA;
  --jm-metal-legend: #F0456B;

  /* ---- Miss direction: green / too early / too late --------------------
     Matched to what a 2K player already reads on the shot feedback: green is
     good, amber is slightly off, red is badly off. Straight yellow could not
     be used because our brand green is a LIME, so yellow sat on top of it
     (CVD 2.3, normal-vision 15.0). Amber clears both.
     Validated all-pairs: CVD 10.7, normal-vision 17.4, contrast 3:1+.
     The lightness-band check is waived on the green by design, as elsewhere:
     it is the hero outcome, not a peer series.                              */
  --jm-early:        #F5871F;   /* released too soon, amber */
  --jm-late:         #E03131;   /* released too late, red   */

  /* ---- Chart chrome --------------------------------------------------- */
  --jm-grid:         #1D242B;   /* hairline gridline, recessive           */
  --jm-axis:         #303A45;
  --jm-ci-alpha:     0.14;      /* confidence ribbon fill opacity         */
  --jm-ring:         rgba(255, 255, 255, 0.10);

  /* ---- The green window band (the signature mark) --------------------- */
  --jm-core:         #74BC18;   /* confirmed IN ms                        */
  --jm-core-edge:    #9BE62E;
  --jm-soft:         rgba(116, 188, 24, 0.34);  /* unconfirmed / soft     */
  --jm-out:          #2A3138;   /* tested and OUT                          */

  /* ---- Type ----------------------------------------------------------- */
  --jm-font-display: "Archivo", system-ui, sans-serif;
  --jm-font-ui:      "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --jm-font-mono:    "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Scale. Display is condensed + uppercase + tight. UI is plain.        */
  --jm-t-display:    28px;  --jm-t-display-lh: 32px;
  --jm-t-h1:         20px;  --jm-t-h1-lh:      26px;
  --jm-t-h2:         15px;  --jm-t-h2-lh:      20px;
  --jm-t-body:       14px;  --jm-t-body-lh:    21px;
  --jm-t-sm:         13px;  --jm-t-sm-lh:      18px;
  --jm-t-micro:      11px;  --jm-t-micro-lh:   14px;  /* eyebrow, caps    */
  --jm-t-stat:       32px;  --jm-t-stat-lh:    34px;  /* stat tile value  */
  --jm-t-hero:       52px;  --jm-t-hero-lh:    52px;  /* one number/page  */

  /* ---- Space (4px base) ----------------------------------------------- */
  --jm-s-1: 4px;  --jm-s-2: 8px;   --jm-s-3: 12px; --jm-s-4: 16px;
  --jm-s-5: 24px; --jm-s-6: 32px;  --jm-s-7: 48px; --jm-s-8: 64px;

  /* ---- Radius. Minimal: near-square. ---------------------------------- */
  --jm-r-1: 3px;   /* chips, cells       */
  --jm-r-2: 6px;   /* buttons, inputs    */
  --jm-r-3: 10px;  /* cards, panels      */
  --jm-r-full: 999px;

  /* ---- Elevation. Dark UI: use surface + hairline, not big shadows. --- */
  --jm-e-1: 0 1px 0 rgba(255,255,255,0.03) inset, 0 1px 2px rgba(0,0,0,0.4);
  --jm-e-2: 0 8px 24px rgba(0,0,0,0.55);

  /* ---- Motion. Data moves fast or not at all. ------------------------- */
  --jm-dur-1: 90ms;    /* hover, chip                 */
  --jm-dur-2: 160ms;   /* panel, tab, tooltip         */
  --jm-dur-3: 280ms;   /* chart transition, band slide */
  --jm-ease:     cubic-bezier(0.22, 0.61, 0.36, 1);
  --jm-ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* ---- Layout --------------------------------------------------------- */
  --jm-w-page: 1440px;
  --jm-w-read: 720px;
  --jm-h-row:  44px;    /* table row: dense but touchable */
  --jm-h-ctl:  32px;    /* control height */
}

/* ==========================================================================
   LIGHT THEME. Selected steps, not an inversion.
   ========================================================================== */
:root[data-theme="light"] {
  color-scheme: light;

  --jm-plane:        #F5F6F7;
  --jm-surface-1:    #FFFFFF;
  --jm-surface-2:    #F7F8F9;
  --jm-surface-3:    #EDF0F2;
  --jm-line:         #E3E7EA;
  --jm-line-strong:  #CDD4D9;

  --jm-ink:          #0B0F13;
  --jm-ink-2:        #4C5761;
  --jm-ink-3:        #78838D;
  --jm-ink-inv:      #FFFFFF;

  /* the neon does not survive a white page, so light mode takes the same
     hue several steps down where it still clears text contrast            */
  --jm-brand:        #4A7A10;
  --jm-brand-dim:    #3A5F0A;
  --jm-brand-wash:   rgba(74, 122, 16, 0.10);
  --jm-brand-glow:   none;

  --jm-series-a:     #2A78D6;
  --jm-series-b:     #EB6834;
  --jm-series-a-wash: rgba(42, 120, 214, 0.14);
  --jm-series-b-wash: rgba(235, 104, 52, 0.14);

  --jm-rate-1:       #74BC18;
  --jm-rate-2:       #55880F;
  --jm-rate-3:       #3A5F0A;
  --jm-rate-4:       #2A4607;
  --jm-rate-5:       #1B2D05;
  --jm-rate-6:       #101C03;
  --jm-grade-ink:    #F4FFE4;   /* light ink: light-theme fills are dark        */

  --jm-early:        #F59E0B;
  --jm-late:         #B02525;

  /* metals re-stepped for a white surface: the six compress badly on light,
     so bronze goes darker and silver goes cool-blue to clear None            */
  --jm-metal-none:   #5F6874;
  --jm-metal-bronze: #7A3B0E;
  --jm-metal-silver: #6F98C6;
  --jm-metal-gold:   #B08A00;
  --jm-metal-hof:    #6A45D6;
  --jm-metal-legend: #C2185B;

  --jm-tier-0:       #86B6EF;
  --jm-tier-1:       #4A90E4;
  --jm-tier-2:       #2A78D6;
  --jm-tier-3:       #1C5CAB;
  --jm-tier-4:       #154786;
  --jm-tier-5:       #0D366B;

  --jm-grid:         #ECEFF1;
  --jm-axis:         #CDD4D9;
  --jm-ring:         rgba(11, 15, 19, 0.10);

  --jm-core:         #55880F;
  --jm-core-edge:    #3A5F0A;
  --jm-soft:         rgba(85, 136, 15, 0.26);
  --jm-out:          #E3E7EA;

  --jm-e-1: 0 1px 2px rgba(11,15,19,0.06);
  --jm-e-2: 0 8px 24px rgba(11,15,19,0.12);
}

@media (prefers-reduced-motion: reduce) {
  :root { --jm-dur-1: 0ms; --jm-dur-2: 0ms; --jm-dur-3: 0ms; }
}

/* ==========================================================================
   PRIMITIVES
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

/* Any component that sets its own `display` silently defeats the [hidden]
   attribute. This makes hidden actually mean hidden, everywhere. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  background: var(--jm-plane);
  color: var(--jm-ink);
  font-family: var(--jm-font-ui);
  font-size: var(--jm-t-body);
  line-height: var(--jm-t-body-lh);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--jm-brand);
  outline-offset: 2px;
  border-radius: var(--jm-r-1);
}

/* Every number in the product is mono + tabular. No exceptions. */
.jm-num,
.jm-table td.jm-num,
.jm-axis-tick {
  font-family: var(--jm-font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ---- Display / eyebrow --------------------------------------------- */
.jm-display {
  font-family: var(--jm-font-display);
  font-weight: 700;
  font-size: var(--jm-t-display);
  line-height: var(--jm-t-display-lh);
  letter-spacing: -0.02em;
  font-stretch: 88%;
  text-transform: uppercase;
  margin: 0;
}
.jm-h1 { font-family: var(--jm-font-display); font-weight: 700; font-size: var(--jm-t-h1); line-height: var(--jm-t-h1-lh); letter-spacing: -0.01em; margin: 0; }
.jm-h2 { font-size: var(--jm-t-h2); line-height: var(--jm-t-h2-lh); font-weight: 600; margin: 0; }
.jm-eyebrow {
  font-size: var(--jm-t-micro);
  line-height: var(--jm-t-micro-lh);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--jm-ink-3);
}
.jm-muted { color: var(--jm-ink-2); }
.jm-dim   { color: var(--jm-ink-3); }

/* ---- Card ------------------------------------------------------------ */
.jm-card {
  background: var(--jm-surface-1);
  border: 1px solid var(--jm-line);
  border-radius: var(--jm-r-3);
  padding: var(--jm-s-4);
}
.jm-card-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--jm-s-3); margin-bottom: var(--jm-s-3);
}

/* ---- Pill / chip ----------------------------------------------------- */
.jm-pill {
  display: inline-flex; align-items: center; gap: 5px;
  height: 20px; padding: 0 7px;
  border-radius: var(--jm-r-1);
  font-size: var(--jm-t-micro); line-height: 1; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  border: 1px solid transparent; white-space: nowrap;
}
/* Status pills carry a glyph. Status is never color alone. */
.jm-pill--core    { color: var(--jm-good);    background: color-mix(in srgb, var(--jm-good) 14%, transparent);    border-color: color-mix(in srgb, var(--jm-good) 34%, transparent); }
.jm-pill--soft    { color: var(--jm-warn);    background: color-mix(in srgb, var(--jm-warn) 14%, transparent);    border-color: color-mix(in srgb, var(--jm-warn) 34%, transparent); }
.jm-pill--draft   { color: var(--jm-warn);    background: transparent; border-color: color-mix(in srgb, var(--jm-warn) 34%, transparent); }
.jm-pill--pending { color: var(--jm-ink-3);   background: transparent; border-color: var(--jm-line-strong); }
.jm-pill--out     { color: var(--jm-crit);    background: color-mix(in srgb, var(--jm-crit) 14%, transparent);    border-color: color-mix(in srgb, var(--jm-crit) 34%, transparent); }
.jm-pill--neutral { color: var(--jm-ink-2);   background: var(--jm-surface-2); border-color: var(--jm-line); }

/* Sample-size chip. Any grade or rate shown without one is a bug. */
.jm-n {
  font-family: var(--jm-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--jm-t-micro); color: var(--jm-ink-3);
  border: 1px solid var(--jm-line); border-radius: var(--jm-r-1);
  padding: 2px 5px; white-space: nowrap;
}

/* Delta chip: sign is in the glyph AND the text, not just the color. */
.jm-delta {
  font-family: var(--jm-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--jm-t-sm); font-weight: 600;
  display: inline-flex; align-items: center; gap: 4px;
}
.jm-delta--up   { color: var(--jm-good); }
.jm-delta--down { color: var(--jm-crit); }
.jm-delta--flat { color: var(--jm-ink-3); }

/* ---- Grade token ----------------------------------------------------- */
.jm-grade {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: var(--jm-r-1);
  font-family: var(--jm-font-display); font-weight: 700; font-size: 14px;
  border: 1px solid var(--jm-ring);
}
/* Grade ink follows the fill, not the theme name: the rate ramp inverts between
   themes, so a fixed dark ink lands on a dark fill in light mode and goes
   unreadable. --jm-grade-ink is redefined with the ramp. */
.jm-grade--a { background: var(--jm-rate-4); color: var(--jm-grade-ink); }
.jm-grade--b { background: var(--jm-rate-2); color: var(--jm-grade-ink); }
.jm-grade--c { background: var(--jm-surface-3); color: var(--jm-ink-2); }
.jm-grade--d { background: transparent; color: var(--jm-ink-3); border-color: var(--jm-line-strong); }

/* ---- Buttons --------------------------------------------------------- */
.jm-btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: var(--jm-h-ctl); padding: 0 12px;
  border-radius: var(--jm-r-2); border: 1px solid var(--jm-line-strong);
  background: var(--jm-surface-2); color: var(--jm-ink);
  font: inherit; font-size: var(--jm-t-sm); font-weight: 500;
  cursor: pointer; transition: background var(--jm-dur-1) var(--jm-ease),
                              border-color var(--jm-dur-1) var(--jm-ease);
}
.jm-btn:hover { background: var(--jm-surface-3); }
.jm-btn:active { transform: translateY(0.5px); }
.jm-btn[disabled] { opacity: 0.45; cursor: not-allowed; }
.jm-btn--primary {
  background: var(--jm-brand); border-color: var(--jm-brand); color: var(--jm-ink-inv); font-weight: 600;
}
.jm-btn--primary:hover { background: var(--jm-brand-dim); border-color: var(--jm-brand-dim); }
.jm-btn--ghost { background: transparent; border-color: transparent; color: var(--jm-ink-2); }
.jm-btn--ghost:hover { background: var(--jm-surface-2); color: var(--jm-ink); }

/* ---- Segmented control (the primary filter affordance) --------------- */
.jm-seg {
  display: inline-flex; padding: 2px; gap: 2px;
  background: var(--jm-surface-2); border: 1px solid var(--jm-line);
  border-radius: var(--jm-r-2);
}
.jm-seg button {
  height: 26px; padding: 0 10px; border: 0; border-radius: var(--jm-r-1);
  background: transparent; color: var(--jm-ink-2);
  font: inherit; font-size: var(--jm-t-sm); font-weight: 500; cursor: pointer;
  transition: background var(--jm-dur-1) var(--jm-ease), color var(--jm-dur-1) var(--jm-ease);
}
.jm-seg button:hover { color: var(--jm-ink); }
.jm-seg button[aria-pressed="true"] { background: var(--jm-surface-3); color: var(--jm-ink); }

/* ---- Table ----------------------------------------------------------- */
.jm-table { width: 100%; border-collapse: collapse; font-size: var(--jm-t-sm); }
.jm-table thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--jm-surface-2);
  font-size: var(--jm-t-micro); letter-spacing: 0.07em; text-transform: uppercase;
  font-weight: 600; color: var(--jm-ink-3);
  text-align: left; padding: 8px 10px; white-space: nowrap;
  border-bottom: 1px solid var(--jm-line-strong);
}
.jm-table tbody td {
  padding: 0 10px; height: var(--jm-h-row);
  border-bottom: 1px solid var(--jm-line);
  vertical-align: middle;
}
.jm-table tbody tr:hover { background: var(--jm-surface-2); }
.jm-table tbody tr[aria-selected="true"] { background: var(--jm-surface-3); }
.jm-table .jm-r { text-align: right; }
.jm-table th.jm-r { text-align: right; }

/* ---- Stat tile ------------------------------------------------------- */
.jm-stat { display: flex; flex-direction: column; gap: 2px; }
.jm-stat .v {
  font-family: var(--jm-font-mono); font-size: var(--jm-t-stat); line-height: var(--jm-t-stat-lh);
  font-weight: 600; letter-spacing: -0.02em;
}
.jm-stat .k { font-size: var(--jm-t-micro); letter-spacing: 0.08em; text-transform: uppercase; color: var(--jm-ink-3); font-weight: 600; }

/* ---- Window strip: the signature mark -------------------------------- */
/* Shared ms domain across every row so widths compare by eye.            */
/* The axis belongs WITH the strip: a strip without its rail labelled is  */
/* just a green smear. Moved here from app.html 2026-08-27 so the static  */
/* /jumpers table gets it too, instead of a second copy that can drift.   */
.msaxis { display: flex; justify-content: space-between;
          font-family: var(--jm-font-mono); font-size: 9px; color: var(--jm-ink-3); }
.jm-strip { position: relative; height: 18px; width: 100%; min-width: 140px; }
.jm-strip .track {
  position: absolute; inset: 6px 0; border-radius: 3px;
  background: var(--jm-out);
}
.jm-strip .soft {
  position: absolute; top: 6px; bottom: 6px; border-radius: 3px;
  background: var(--jm-soft);
}
.jm-strip .core {
  position: absolute; top: 4px; bottom: 4px; border-radius: 3px;
  background: var(--jm-core);
  box-shadow: 0 0 0 1px var(--jm-surface-1);
  min-width: 3px;
}
.jm-strip .peak {
  position: absolute; top: 1px; bottom: 1px; width: 2px;
  background: var(--jm-core-edge); border-radius: 1px;
}

/* ---- Brand mark ------------------------------------------------------ */
/* The brackets take currentColor so the mark themes with the page; the meter
   inside is always brand green, because that green IS the green window.    */
.jm-mark { color: var(--jm-ink); flex: none; }
.jm-mark--glow { filter: drop-shadow(0 0 10px rgba(84, 253, 4, 0.45)); }

/* ---- Forgiveness meter ----------------------------------------------- */
/* Window width, read at a glance. Always shipped beside the word, never
   alone: four blocks are a rank, and a rank with no label is a riddle.    */
.fmeter { display: inline-flex; gap: 2px; flex: none; }
.fmeter i {
  width: 6px; height: 14px; border-radius: 1px;
  background: var(--jm-out); display: inline-block;
}
.fmeter i[data-on="true"] { background: var(--jm-core); }

/* ---- Tooltip --------------------------------------------------------- */
.jm-tip {
  position: absolute; pointer-events: none; z-index: 40;
  background: var(--jm-surface-2); border: 1px solid var(--jm-line-strong);
  border-radius: var(--jm-r-2); box-shadow: var(--jm-e-2);
  padding: 8px 10px; font-size: var(--jm-t-sm); min-width: 150px;
  opacity: 0; transition: opacity var(--jm-dur-1) var(--jm-ease);
}
.jm-tip[data-show="true"] { opacity: 1; }
.jm-tip .row { display: flex; justify-content: space-between; gap: 14px; }
.jm-tip .row + .row { margin-top: 3px; }

/* ---- Skeleton -------------------------------------------------------- */
.jm-skel {
  background: linear-gradient(90deg, var(--jm-surface-2) 0%, var(--jm-surface-3) 50%, var(--jm-surface-2) 100%);
  background-size: 200% 100%; border-radius: var(--jm-r-1);
  animation: jm-shimmer 1.4s linear infinite;
}
@keyframes jm-shimmer { to { background-position: -200% 0; } }

/* ---- Motion: the one signature animation ---------------------------- */
/* The window band slides/scales into place. Nothing else animates.      */
@keyframes jm-band-in {
  from { transform: scaleX(0.2); opacity: 0; }
  to   { transform: scaleX(1);   opacity: 1; }
}
.jm-anim-band { transform-origin: left center; animation: jm-band-in var(--jm-dur-3) var(--jm-ease-out) both; }

/* ==========================================================================
   VALIDATOR RESULTS. Re-run before changing any hex above.
   node scripts/validate_palette.js "<hexes>" --mode <m> --surface <s> [--ordinal|--pairs all]

   series dark   #3987E5,#D95926                              surface #12161A  all-pairs  PASS
   series light  #2A78D6,#EB6834                              surface #FFFFFF  all-pairs  PASS
   tier  dark    #CDE2FB,#9EC5F4,#6DA7EC,#3987E5,#256ABF,#184F95  #12161A  ordinal  PASS
   tier  light   #86B6EF,#4A90E4,#2A78D6,#1C5CAB,#154786,#0D366B  #FFFFFF  ordinal  PASS
   tier  dark    #8A94A0,#A65E2E,#C9D3DC,#E3B02A,#A78BFA,#F0456B  #12161A  all-pairs
                 CVD 8.3 / normal-vision 15.1 PASS. Chroma floor waived on the two greys
                 (None and Silver are grey by meaning); dash + direct label carry the rest.
   tier  light   #5F6874,#7A3B0E,#6F98C6,#B08A00,#6A45D6,#C2185B  #FFFFFF  all-pairs
                 normal-vision 15.1 PASS, contrast PASS, CVD 6.8 WARN (legal with the
                 dash + label secondary encoding that the overlay always draws).
   miss  dark    #9BE62E,#F5871F,#E03131   #12161A  all-pairs  CVD 10.7 / vision 17.4  PASS
                 (lightness band waived on the green: hero outcome, not a peer series)
   miss  light   #3A5F0A,#F59E0B,#B02525   #FFFFFF  all-pairs  PASS
   rate  dark    #EDFFD8,#C0F86A,#9BE62E,#74BC18,#55880F,#3A5F0A  #12161A  ordinal  PASS
   rate  light   #74BC18,#55880F,#3A5F0A,#2A4607,#1B2D05,#101C03  #FFFFFF  ordinal  PASS
   (re-run 2026-08-08 when the green family moved to the logo hue #54FD04.)

   Known and accepted: good(#0CA30C) vs crit(#D03B3B) fails CVD separation.
   That is the universal red/green problem. Mitigation is mandatory and is
   already in the primitives: every status pill ships a glyph + a text label,
   so hue never carries the meaning alone. Make-rate uses the one-hue
   sequential ramp, never red-to-green, so the heatmap dodges it entirely.
   ========================================================================== */
