/* ============================================================
   EXPLORE PAGE — "Warm Editorial Authority" Design
   GrantCompass /explore — Interactive Grant Database

   Uses design tokens from main.css (:root variables).
   This file handles all explore-specific styles, overriding
   main.css values with a warm, editorial color palette.
   ============================================================ */

/* Fonts are loaded from HTML <link> tags — no @import here */

:root {
    /* Layout */
    --explore-max-width: 1320px;
    --explore-navbar-height: 56px;
    --explore-row-expand-speed: 350ms;

    /* Typography */
    --explore-font-display: 'Fraunces', 'Georgia', serif;
    --explore-font-body: 'Plus Jakarta Sans', sans-serif;
    --explore-font-data: 'IBM Plex Mono', 'SF Mono', monospace;

    /* Warm palette */
    /* ★★ ROUND 14 — THE PALETTE WAS THE DEFECT, NOT THE THIRTEEN SELECTORS THAT
       REPORTED IT. A whole-surface sweep (probe-r14-contrast.js: every element with
       its own text node, measured against its own composited opaque backdrop, five
       user/width states) found 123 failing selector-states — and almost all of them
       were one arithmetic fact restated: `--ink-muted` #9a9085 is 2.91:1 on `--cream`
       and 3.06:1 on `--paper`, so EVERY quiet label on the page failed AA wherever it
       landed. Fixing thirteen named rules would have left the other ninety in place
       and reported itself complete.
       ⚠ THE HIERARCHY IS PRESERVED BY ARITHMETIC, NOT BY EYE. The inks keep their
       order and roughly their spacing on `--cream` after the change:
           ink 15.7 : 1  ·  secondary 6.84  ·  muted 5.24  ·  faint (non-text only)
       Round 13 rebuilt the row's type scale deliberately (the name leads on size,
       weight AND darkness); probe-r14 re-reads all three channels after every colour
       change and the harness asserts them, so raising contrast cannot invert it.
       ★ `--ink-faint` is NOT raised: after this round it paints only rules, dots and
       disc fills. Its two TEXT users (`.explore-rail__title`, `.row-chevron`) are
       repointed at `--ink-muted` instead, which is what a name like "faint" should
       have meant all along. */
    --cream: #faf6f0;
    --cream-dark: #f2ebe0;
    --paper: #fffcf7;
    --ink: #1a1714;
    --ink-secondary: #5c5549;
    --ink-muted: #6f665a;        /* was #9a9085 (2.91:1 on cream) — now 5.24:1 */
    --ink-faint: #c7bfb4;        /* NON-TEXT ONLY (1.69:1) — rules, dots, disc fills */
    /* ⚠ `--ink-3` was NEVER DEFINED and ~30 rules referenced it through a fallback —
       with TWO different fallbacks for the same name (#78716c in most places, #a8a29e
       on `.exp-rank`), so one token was silently rendering as two colours, neither of
       which cleared AA. Defining it makes every one of those sites resolve to one
       compliant ink and the drifting fallbacks become dead text. */
    --ink-3: #6f665a;

    /* Accent colors */
    /* ★ Sienna was 4.22:1 as body-link ink on cream and 3.84:1 on `--cream-dark`
       (the toolkit strip). Darkened to clear AA on BOTH; every place it is used as a
       BACKGROUND under white text gains contrast from the same move. */
    --accent-sienna: #a83f1a;    /* was #c4532b (4.22:1 on cream) — now 5.75:1 */
    --accent-sienna-light: rgba(168, 63, 26, 0.08);
    --accent-forest: #2d6a4f;
    --accent-forest-light: rgba(45, 106, 79, 0.08);
    --accent-forest-deep: #1a4731;
    --accent-navy: #264673;
    --accent-navy-light: rgba(38, 70, 115, 0.08);
    --accent-plum: #7b2d8e;
    --accent-plum-light: rgba(123, 45, 142, 0.08);
    --accent-gold: #b8860b;
    --accent-gold-light: rgba(184, 134, 11, 0.06);
    --accent-peach: #e8a87c;
    /* ★ ROUND 14 — the Win Layer's three amber INKS, named rather than retyped.
       `#8a6d3b` appeared as a literal in 14 rules (3.92:1 on the band header),
       `#a86a1f` in 4 (3.88:1) and `#8a6508` in 3 (3.04:1). They are the amber that
       carries WORDS; the same hexes still paint boxes, fills, borders and focus
       rings, where the ratio that matters is against what sits ON them and is
       unaffected. Splitting ink from fill is what lets the text clear AA without
       repainting the band. */
    --amber-ink: #7a6029;        /* was #8a6d3b — 4.80:1 on the #efe6d6 band header */
    --amber-strong: #8a5410;     /* was #a86a1f — 5.49:1 on the chip's own tint */
    --gold-ink: #7a5c10;         /* was #8a6508 — 5.64:1 on the receipt's tint */

    /* Hero */
    --hero-bg: #1a4731;

    /* Premium theming (forest green) */
    --explore-premium-accent: #2d6a4f;
    --explore-premium-accent-light: rgba(45, 106, 79, 0.08);
    --explore-premium-accent-dark: #1a4731;
    --explore-premium-glow: rgba(45, 106, 79, 0.06);

    /* Shadows */
    --explore-shadow-sm: 0 1px 2px rgba(26, 23, 20, 0.04);
    --explore-shadow-md: 0 4px 12px rgba(26, 23, 20, 0.06);
    --explore-shadow-lg: 0 12px 40px rgba(26, 23, 20, 0.08);

    /* Radii */
    --explore-radius: 10px;
    --explore-radius-lg: 16px;
}

/* ============================================================
   0. BODY OVERRIDES & DECORATIVE ELEMENTS
   ============================================================ */

body:has(.explore-hero) {
    background: var(--cream);
}

/* Override main.css overflow-x: hidden with clip to fix position: sticky on filter bar.
   'clip' prevents horizontal overflow without creating a scroll container. */
html:has(.explore-hero),
body:has(.explore-hero) {
    overflow-x: clip;
}

/* Subtle grain texture overlay */
body:has(.explore-hero)::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    opacity: 0.025;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-repeat: repeat;
    background-size: 256px 256px;
}

/* Decorative background circles */
.explore-deco-circle {
    position: fixed;
    border-radius: 50%;
    border: 1px solid rgba(26, 23, 20, 0.06);
    pointer-events: none;
    z-index: 0;
}

.explore-deco-circle--one {
    width: 500px;
    height: 500px;
    top: -120px;
    right: -120px;
}

.explore-deco-circle--two {
    width: 300px;
    height: 300px;
    bottom: -60px;
    left: -60px;
}

/* ============================================================
   1. HERO SECTION
   ============================================================ */

/* ⚠ THE TOOL'S OUTPUT WAS ENTIRELY BELOW THE FOLD (2026-07-28).
   Measured on 1440x900: the hero was 578px (64% of the viewport) and the first
   result row started at 865px — 96% down the first screen. A visitor landing on
   the catalogue browse page saw a marketing hero and ZERO programs, on the one
   surface whose entire job is showing programs. Mobile was already fine (23% /
   40%), so this is a desktop-only correction: trimming the vertical padding on
   >=1024px lifts roughly two result rows above the fold without touching the
   hero's content, type scale, or the mobile layout. */
.explore-hero {
    padding: calc(var(--explore-navbar-height) + 48px) var(--space-xl) 80px;
    background: var(--hero-bg);
    text-align: center;
    overflow: hidden;
    position: relative;
    z-index: 1;
    animation: exploreSlideUp 0.6s ease-out;
}

/* Grain texture inside hero */
.explore-hero__grain {
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: 0.03;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-repeat: repeat;
    background-size: 256px 256px;
    z-index: 1;
}

/* Subtle radial glow behind heading */
.explore-hero::after {
    content: '';
    position: absolute;
    top: 45%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 600px;
    height: 400px;
    background: radial-gradient(ellipse, rgba(232, 168, 124, 0.10) 0%, transparent 70%);
    pointer-events: none;
    z-index: 1;
}

.explore-hero__inner {
    max-width: 800px;
    margin: 0 auto;
    position: relative;
    z-index: 2;
}

/* Eyebrow badge */
.explore-hero__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 16px;
    border-radius: 100px;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.12);
    color: rgba(255, 255, 255, 0.7);
    font-family: var(--explore-font-body);
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-bottom: 24px;
}

.explore-hero h1 {
    font-family: var(--explore-font-display);
    font-size: 3.4rem;
    font-weight: 400;
    color: #fff;
    margin-bottom: 12px;
    letter-spacing: -0.03em;
    line-height: 1.1;
}

.explore-hero h1 span {
    color: var(--accent-peach);
}

/* Italic accent within hero heading */
.explore-hero__accent {
    font-style: italic;
    font-weight: 300;
    color: var(--accent-peach);
}

.explore-hero__subtitle {
    font-family: var(--explore-font-body);
    font-size: 1.1rem;
    color: rgba(255, 255, 255, 0.7);
    max-width: 520px;
    margin: 0 auto 32px;
    line-height: 1.65;
}

.explore-hero__premium-cue {
    color: var(--accent-peach);
    font-weight: 600;
}

/* Floating stats card — overlaps into content area */
.explore-hero__stats-card {
    background: var(--paper);
    border-radius: var(--explore-radius-lg);
    box-shadow: var(--explore-shadow-lg);
    max-width: 720px;
    margin: 0 auto -36px;
    padding: 20px 32px;
    position: relative;
    z-index: 3;
    animation: exploreSlideUp 0.6s ease-out 0.15s both;
}

/* Legacy stat pills — hidden if stat-strip is used, but kept for fallback */
.explore-hero__stats {
    display: flex;
    justify-content: center;
    gap: var(--space-sm);
    flex-wrap: wrap;
}

/* --- Stat strip (inside stats card) --- */

.stat-strip {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0;
}

.stat-strip__item {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0 24px;
    gap: 4px;
}

.stat-strip__dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-bottom: 2px;
}

.stat-strip__dot--grant       { background: var(--accent-forest); }
.stat-strip__dot--program     { background: var(--accent-navy); }
.stat-strip__dot--loan        { background: var(--accent-gold); }
.stat-strip__dot--tax-credit  { background: var(--accent-plum); }
.stat-strip__dot--award       { background: var(--accent-sienna); }
.stat-strip__dot--forgivable-loan { background: var(--accent-gold); }

.stat-strip__number {
    font-family: var(--explore-font-display);
    font-size: 1.6rem;
    font-weight: 600;
    color: var(--ink);
    line-height: 1;
}

.stat-strip__label {
    font-family: var(--explore-font-body);
    font-size: 0.68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-muted);
}

.stat-strip__divider {
    width: 1px;
    height: 36px;
    background: var(--ink-faint);
    opacity: 0.4;
    flex-shrink: 0;
}

/* --- Premium Preview Ticker (below stats card) --- */

.explore-hero__preview {
    margin: 14px -32px -20px;
    padding: 0;
    border-top: 1px solid rgba(26, 23, 20, 0.06);
}

.premium-preview__inner {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 32px;
    background: rgba(45, 106, 79, 0.04);
    border-radius: 0 0 var(--explore-radius-lg) var(--explore-radius-lg);
    font-family: var(--explore-font-body);
    font-size: 0.8rem;
    color: var(--ink-secondary);
}

.premium-preview__badge {
    flex-shrink: 0;
    padding: 2px 8px;
    background: var(--accent-forest);
    color: #fff;
    border-radius: 4px;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.premium-preview__text {
    flex: 1;
    min-width: 0;
    transition: opacity 0.3s ease, transform 0.3s ease;
    line-height: 1.4;
}

.premium-preview__text strong {
    color: var(--ink);
    font-weight: 600;
}

.premium-preview__link {
    flex-shrink: 0;
    color: var(--accent-forest);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    font-size: 0.8rem;
}

.premium-preview__link:hover {
    text-decoration: underline;
}

@media (max-width: 600px) {
    .explore-hero__preview {
        margin: 10px -16px -16px;
    }
    .premium-preview__inner {
        flex-wrap: wrap;
        gap: 6px;
        padding: 8px 16px;
        font-size: 0.75rem;
    }
    .premium-preview__link {
        width: 100%;
        text-align: center;
        padding-top: 4px;
        border-top: 1px solid rgba(45, 106, 79, 0.1);
    }
}

@media (prefers-reduced-motion: reduce) {
    .premium-preview__text {
        transition: none;
    }
}

/* Legacy stat pills (kept for fallback) */
.stat-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border-radius: var(--radius-full);
    font-family: var(--explore-font-body);
    font-size: var(--text-sm);
    font-weight: 600;
    background: var(--paper);
    border: 1px solid rgba(26, 23, 20, 0.06);
    color: var(--ink-secondary);
    transition: transform var(--transition-fast);
}

.stat-pill:hover {
    transform: translateY(-1px);
}

.stat-pill::before {
    content: '';
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

.stat-pill--grant::before     { background: var(--accent-forest); }
.stat-pill--program::before   { background: var(--accent-navy); }
.stat-pill--loan::before      { background: var(--accent-gold); }
.stat-pill--tax-credit::before { background: var(--accent-plum); }
.stat-pill--other::before     { background: var(--ink-faint); }

/* ============================================================
   2. FILTER BAR
   ============================================================ */

/* Apr 25 2026 (filter-bar redesign) — compact density, honest active state.
   Previously the filter bar took ~330px vertical at desktop because
   selects were 44px tall + the (broken) `select:not([value=""])` selector
   was making EVERY filter render in sienna by default (selectors don't
   match `value=""` reliably on <select> elements; needs JS class toggle).
   New design: single-row at desktop ~52px tall, neutral default state,
   sienna only when JS sets .has-value, sticky-shrink on scroll. */

.explore-filters {
    position: sticky;
    top: var(--explore-navbar-height);
    z-index: 100;
    background: rgba(250, 246, 240, 0.92);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border-bottom: 1px solid rgba(26, 23, 20, 0.06);
    padding: 12px var(--space-xl);
    /* ★★ 2026-09-18 — VERTICAL PADDING ONLY (CLS). This transition exists for the
       sticky-shrink (`.is-stuck` changes padding-top/-bottom). As `padding` it also
       animated the HORIZONTAL padding, which changes exactly once: when the catalogue
       lands and `_initRail()` adds `body.has-rail` (padding-left/right → 0) in the same
       frame `_mountWorkbench()` moves the five view chips into this bar. For the first
       ~110ms of the 180ms tween the chips' track was 520–573px, under their ~574px
       natural width, so they wrapped (they wrap by design, never ellipsise): the bar
       went 36 → 78px, the table dropped 42px and then jumped back up — two shifts
       worth ~0.45 CLS at 1440 on every fresh load. A layout-mode switch must land in
       its final geometry in one frame; only the stuck/unstuck shrink animates. */
    transition: padding-top 180ms ease, padding-bottom 180ms ease, box-shadow var(--transition-base);
    animation: exploreSlideUp 0.5s ease-out 0.25s both;
}

.explore-filters.is-stuck {
    padding-top: 8px;
    padding-bottom: 8px;
    box-shadow: 0 4px 20px rgba(26, 23, 20, 0.06);
}

.explore-filters__inner {
    max-width: var(--explore-max-width);
    margin: 0 auto;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    row-gap: 8px;
}

.explore-filters__search {
    position: relative;
    flex: 0 1 240px;
    min-width: 180px;
}

.explore-filters__search i,
.explore-filters__search svg {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    width: 14px;
    height: 14px;
    color: var(--ink-faint);
    pointer-events: none;
}

.explore-filters__search input {
    width: 100%;
    padding: 7px 28px 7px 32px;
    border: 1px solid rgba(26, 23, 20, 0.14);
    border-radius: 8px;
    font-size: 13px;
    font-family: var(--explore-font-body);
    color: var(--ink);
    background: var(--paper);
    height: 36px;
    line-height: 1;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
    outline: none;
}

.explore-filters__search input:focus {
    border-color: var(--accent-sienna);
    box-shadow: 0 0 0 3px rgba(196, 83, 43, 0.10);
}

.explore-filters__search input::placeholder {
    color: var(--ink-faint);
    font-style: normal;
}

.explore-search-clear {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    width: 20px;
    height: 20px;
    border: none;
    background: none;
    color: var(--ink-faint);
    cursor: pointer;
    display: none;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-full);
    padding: 0;
    font-size: 0;
    transition: color 0.15s ease, background 0.15s ease;
}

.explore-search-clear.is-visible {
    display: flex;
}

.explore-search-clear:hover {
    color: var(--ink-secondary);
    background: rgba(26, 23, 20, 0.06);
}

.explore-search-clear svg {
    width: 14px;
    height: 14px;
    pointer-events: none;
}

/* ★★ ROUND 14 — NOTHING RENDERS AS AN INTERACTIVE CONTROL THAT IS ABOUT TO BE
   REPLACED. Verified by delaying grants.json 5s: on every fresh load nine `<select>`s
   and a Reset button painted, live and pressable, and were then `display:none`'d and
   replaced by the rail. `display: none` is the whole fix and it belongs HERE, in the
   base rule, rather than in the `body.has-rail` override that used to carry it — an
   override cannot apply before the class it depends on exists, which is exactly the
   window the flash lived in.
   ⚠ The elements STAY IN THE DOM. They are the page's option registry: every facet's
   values, labels and order are read from `sel.options`, and `sel.value` is kept in
   step so URLs and shared links round-trip. See the note in explore.html.
   ⚠ `<noscript>` in explore.html re-reveals them with `!important` — with JS off there
   is no rail and no sheet, and a page with no filter UI at all is worse than the row
   this replaces. */
.explore-filters__dropdowns {
    display: none;
    gap: 6px;
    flex-wrap: wrap;
    align-items: center;
}

.explore-filters__dropdowns select {
    padding: 6px 26px 6px 10px;
    border: 1px solid rgba(26, 23, 20, 0.14);
    border-radius: 8px;
    font-size: 13px;
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    background: var(--paper) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%239a9085' stroke-width='2.5'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E") no-repeat right 8px center;
    background-size: 10px 10px;
    appearance: none;
    cursor: pointer;
    transition: border-color var(--transition-fast), color var(--transition-fast), background-color var(--transition-fast);
    outline: none;
    height: 36px;
    line-height: 1;
    max-width: 160px;
}

.explore-filters__dropdowns select:hover {
    border-color: rgba(26, 23, 20, 0.28);
}

.explore-filters__dropdowns select:focus {
    border-color: var(--accent-sienna);
    box-shadow: 0 0 0 3px rgba(196, 83, 43, 0.10);
}

/* HONEST active state — only when JS toggles .has-value */
.explore-filters__dropdowns select.has-value {
    border-color: var(--accent-sienna);
    color: var(--accent-sienna);
    font-weight: 500;
    background-color: rgba(196, 83, 43, 0.05);
}

.explore-filters__meta {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-left: auto;
    font-size: 12px;
}

#results-count {
    font-size: 12px;
    font-family: var(--explore-font-data);
    color: var(--ink-muted);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.explore-filters__reset {
    padding: 6px 12px;
    border: 1px solid rgba(26, 23, 20, 0.14);
    border-radius: 8px;
    background: transparent;
    color: var(--ink-secondary);
    font-size: 12px;
    font-family: var(--explore-font-body);
    font-weight: 500;
    cursor: pointer;
    transition: all var(--transition-fast);
    height: 28px;
    line-height: 1;
    /* ★ ROUND 14 — HIDDEN, not dimmed, until there is something to reset. A live
       Reset button on a page that has not loaded its data yet is the second half of
       the "the page changes its mind" flash (the rail's own version of this rule has
       said so since round 4: "a Reset that cannot reset anything is a permanent 69px
       of dead control"). `_syncFilterActiveStates` adds `.is-active` the moment a
       filter exists, at every width. */
    display: none;
    opacity: 1;
}

.explore-filters__reset.is-active {
    display: inline-flex;
    align-items: center;
    opacity: 1;
    color: var(--accent-sienna);
    border-color: rgba(196, 83, 43, 0.35);
}

.explore-filters__reset:hover {
    opacity: 1;
    background: var(--accent-sienna-light);
    border-color: var(--accent-sienna);
    color: var(--accent-sienna);
}

/* ★★ ROUND 9 — A DIMMED-BUT-ENABLED RESET IN A 390px STICKY BAR. Measured at 390 on
   a fresh load: `opacity: 0.55`, `disabled` false, `pointer-events: auto`, no
   `aria-disabled` — so it LOOKED unavailable and WAS pressable, and it occupied the
   top-right of the one row a phone can never scroll away from, permanently, with
   nothing to reset. Three ways of being wrong at once: a visual state that lies
   about the interactive state, a control with no work to do, and 59px of a bar whose
   whole problem is width (round 8 measured this same row overflowing by 27px).
   ⚠ The `is-active` class ALREADY carried the truth — nothing needed to be computed,
   only honoured. The rail's own copy of this rule (>=1280px, "a Reset that cannot
   reset anything is a permanent 69px of dead control") made exactly this call for
   the desktop workbench; this is the same decision for the width where space is
   scarcest. Between 1024 and 1280 the classic bar keeps the dimmed affordance: there
   is room for it there, and it teaches that Reset exists. */
@media (max-width: 1023px) {
    .explore-filters__reset:not(.is-active) { display: none; }
}

.explore-filters__hidden-toggle {
    padding: 4px 10px;
    border: 1px solid rgba(26, 23, 20, 0.14);
    border-radius: 8px;
    background: transparent;
    color: var(--ink-muted);
    font-size: 12px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 28px;
    line-height: 1;
    transition: all 0.15s ease;
}

.explore-filters__hidden-toggle:hover {
    background: var(--accent-sienna-light);
    border-color: var(--accent-sienna);
    color: var(--accent-sienna);
}

.explore-filters__hidden-toggle.is-active {
    background: var(--accent-sienna);
    color: var(--paper);
    border-color: var(--accent-sienna);
}

/* Tablet — slightly tighter wrap behavior */
@media (max-width: 920px) {
    .explore-filters {
        padding: 10px var(--space-lg);
    }
    .explore-filters__dropdowns select {
        max-width: 140px;
    }
}

/* Mobile — keep WCAG 2.5.8 tap targets at touch-input size */
@media (max-width: 640px) {
    .explore-filters__dropdowns select,
    .explore-filters__search input {
        height: 40px;
        font-size: 14px;
    }
    .explore-filters__meta {
        margin-left: 0;
        width: 100%;
        justify-content: space-between;
        /* ⚠ ROUND 8 — MEASURED, and it is a defect this round INTRODUCED. Putting the
           Sort control on phones added 84px to a row that had 43px of slack: the row
           ran 401px of content in a 358px box with `flex-wrap: nowrap`, and Reset
           rendered from x358 to x417 — 27px past a 390px viewport, cut mid-word. It
           produced NO page-level horizontal scroll, because the body clips it, which
           is exactly why "no page h-scroll" is not "no overflow". */
        flex-wrap: wrap;
        row-gap: 8px;
        /* ★ 2026-09-18 — RESERVE THE SORT ROW (CLS). On a phone this row holds only
           "Loading..." (19px) until the catalogue lands; then `_initSortPicker()` mounts
           the Sort button, a 44px touch target (the ≤1023 rule below), and the row — and
           the whole grid under it — moved down 25px at 390 (0.02 CLS). The button is
           always there once settled, so 44px is the row's floor either way; this only
           makes the pre-data row start at it. (At ≤~340px the settled row wraps to two
           lines and still grows; that remainder is noted, not reserved.) */
        min-height: 44px;
    }
}

@media (max-width: 480px) {
    .explore-filters__dropdowns select,
    .explore-filters__search input {
        height: 44px; /* WCAG 2.5.5 strict on phone */
    }
}

/* ── The program-status banner ────────────────────────────────────────────────
   ⚠ ROUND 5C — IT WAS IMPERSONATING A FORM FIELD. Rendered in the drawer at ~460px
   with only "Between Intakes" inside it (`programStatusNote` is absent on most
   records), a pale-blue fill inside a 1px blue rounded border with blue text is,
   pixel for pixel, a focused text input on macOS Chrome. Khalid read it as exactly
   that and filed it as the drawer's focus target.
   ★ The fix is the VOCABULARY, not the palette: a status is a LABEL, so it gets a
   left accent rule and an uppercase status word, and it loses the enclosing border
   that made it a box you could type in. Nothing about the fill, and therefore
   nothing about the status semantics the four modifiers carry, changed. */
.detail-status-banner {
    padding: 8px 14px;
    border-radius: 0 var(--explore-radius) var(--explore-radius) 0;
    font-size: 13px;
    line-height: 1.4;
    margin-bottom: 12px;
    border: 0;
    border-left: 3px solid currentColor;
}
.detail-status-banner strong {
    display: inline-block;
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: 0.07em;
    text-transform: uppercase;
}

.detail-status-discontinued {
    background: #fef2f2;
    color: #991b1b;
}

.detail-status-paused {
    background: #fffbeb;
    color: #92400e;
}

.detail-status-between-intakes {
    background: #eff6ff;
    color: #1e40af;
}

.detail-status-winding-down {
    background: #fef2f2;
    color: #991b1b;
}

.btn--hide {
    padding: 4px 10px;
    border: 1px solid rgba(26, 23, 20, 0.12);
    border-radius: var(--explore-radius);
    background: var(--paper);
    color: var(--ink-muted);
    font-size: 12px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    transition: all 0.15s ease;
}

.btn--hide:hover {
    border-color: #e74c3c;
    background: #fef2f2;
    color: #e74c3c;
}

/* Mobile: collapsed filters */
.explore-filters__toggle {
    display: none;
    align-items: center;
    gap: 6px;
    padding: 10px 16px;
    border: 1px solid rgba(26, 23, 20, 0.12);
    border-radius: var(--explore-radius);
    background: var(--paper);
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    font-weight: 500;
    color: var(--ink-secondary);
    cursor: pointer;
}

/* ★ ROUND 14 — THE TOGGLE IS REVEALED BY THE VIEWPORT, NOT BY JAVASCRIPT. It used to
   be shown at ≤1023 and again by `.is-stuck`, and hidden at ≥1280 by `body.has-rail`
   — a class JS only adds once grants.json has landed — so at 1024-1279 the button
   arrived late and at ≥1280 it painted and then vanished, which is the same flash the
   dropdown row had. A media query is true from the first paint. 1279 is the rail's own
   boundary, stated here and in the rail's own query and nowhere else. */
@media (max-width: 1279px) {
    .explore-filters__toggle { display: inline-flex; }
}
.explore-filters__toggle .filter-count {
    background: var(--accent-sienna);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    padding: 1px 7px;
    border-radius: var(--radius-full);
    min-width: 20px;
    text-align: center;
}

/* ── Premium Filter Lock (Explore bar) ───────────────────── */
.explore-filters__premium-group {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    position: relative;
    cursor: pointer;
}
.explore-filters__premium-group:hover .explore-filters__premium-select {
    opacity: 0.55;
}
.explore-filters__premium-group:hover .explore-filters__lock-badge {
    background: var(--accent-forest, #2D6A4F);
    color: #fff;
}
.explore-filters__premium-group:hover .explore-filters__lock-badge svg {
    stroke: #fff;
}
.explore-filters__premium-select {
    opacity: 0.4;
    pointer-events: none;
    min-width: 120px;
}
.explore-filters__lock-badge {
    display: flex;
    align-items: center;
    gap: 0.2rem;
    font-size: 0.6rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--accent-forest, #2D6A4F);
    background: var(--bg-paper, #FFFCF7);
    padding: 0.2rem 0.5rem;
    border-radius: 100px;
    border: 1px solid var(--accent-forest, #2D6A4F);
    white-space: nowrap;
    flex-shrink: 0;
}
/* Unlock for premium users */
body.is-premium .explore-filters__premium-select {
    opacity: 1;
    cursor: pointer;
    pointer-events: auto;
}
body.is-premium .explore-filters__lock-badge {
    display: none;
}

.filter-premium-tooltip {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    background: var(--forest-deep, #1B4332);
    color: #fff;
    font-size: 0.72rem;
    padding: 0.5rem 0.65rem;
    border-radius: 8px;
    z-index: 10;
    animation: filterTipIn 0.2s ease;
    white-space: nowrap;
    line-height: 1.4;
}
.filter-premium-tooltip a {
    color: #A8D5BA;
    text-decoration: underline;
    pointer-events: auto;
    font-weight: 600;
}
@keyframes filterTipIn {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: translateY(0); }
}

/* Mobile: premium filters on own row */
@media (max-width: 1023px) {
    .explore-filters__premium-group {
        flex-wrap: wrap;
        width: 100%;
    }
    .explore-filters__premium-select {
        min-width: 0;
        flex: 1;
    }
}

/* ============================================================
   3. MAIN TABLE
   ============================================================ */

.explore-main {
    max-width: var(--explore-max-width);
    margin: 0 auto;
    padding: var(--space-lg) var(--space-xl);
    padding-top: calc(var(--space-lg) + 36px); /* account for overlapping stats card */
    min-height: 60vh;
}

.explore-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 400px;
    /* ★★ 2026-09-18 — THE PLACEHOLDER RESERVES THE FOLD (CLS). At 400px this box was
       far shorter than what replaces it (a 676px+ table pane at 1440, twelve cards
       ~6,500px tall at 390), so the $39 pricing section painted INSIDE the first
       viewport for the whole catalogue download (bottom 121px at 390x844 for ~9s on
       a slow phone; bottom 170px at 1440x900) and was then thrown ~6,000px down when
       the grid rendered — a shift scored at the maximum distance, 0.87 on mobile.
       The margin makes box + margin exactly one viewport tall, so everything below
       the grid starts under the fold before the data lands, exactly as it does
       after. It is a MARGIN, not min-height, so the spinner stays centred where it
       always was; and it leaves with the box (display:none once the catalogue is
       in), so the settled page is untouched. */
    margin-bottom: max(0px, calc(100vh - 400px));
    gap: var(--space-md);
    /* Apr 20, 2026: kill premature clicks during the load window (56 dead clicks/30d in Clarity). */
    pointer-events: none;
    cursor: wait;
}
/* The load-failure state is terminal: loadGrants() writes an .explore-empty message
   into this box and leaves it displayed, and there is no grid coming to fill the
   reserved space, so the error must not sit above a viewport of blank. */
.explore-loading:has(> .explore-empty) { margin-bottom: 0; }

.explore-loading .loading-spinner {
    width: 36px;
    height: 36px;
    border: 3px solid var(--cream-dark);
    border-top-color: var(--accent-forest);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}
/* ★ ROUND 14 — a continuously spinning ring is exactly the vestibular trigger
   `prefers-reduced-motion` exists for, and this one runs for as long as 2.86 MB
   takes to arrive. It does not stop being a progress indicator when it stops
   turning: the ring stays, the arc becomes a full quiet circle, and the words
   "Loading programs…" beneath it were always the real message.
   ⚠ `.checkout-loading-spinner` had the same omission and is fixed with it — that
   one runs during a PAYMENT, where a reader is least able to look away. */
@media (prefers-reduced-motion: reduce) {
    .explore-loading .loading-spinner,
    .checkout-loading-spinner {
        animation: none;
        border-color: var(--cream-dark);
        border-top-color: var(--cream-dark);
        opacity: 0.75;
    }
}

.explore-loading p {
    color: var(--ink-muted);
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
}

.explore-table-wrap {
    /* ★★ ROUND 5A — this is a two-axis scroll PANE, not a table in a box.
       `overflow: auto` on both axes makes the wrap the scroll container, which is
       precisely what lets `position: sticky` on <thead> pin the header to the top
       of the PANE. (The repo's documented "overflow kills sticky" trap is about
       sticking to the VIEWPORT from inside a scroll box — a different geometry.
       Asserted by measurement in dev-tools/explore-pro-qa.js, not by this comment.)
       Height + width live in the >=1024 block further down. */
    overflow: auto;
    /* ★ A BOUNDED PANE MUST NOT HAND ITS LEFTOVER SCROLL TO THE PAGE. Without this the
       wheel/trackpad delta that runs past the pane's own limit CHAINS to the document, which
       rubber-bands on macOS and exposes a band of white under the workbench — measured at the
       top edge as a 394px document jump (dev-tools/explore-overscroll-test.js). `contain` also
       refuses the HORIZONTAL chain, which on macOS is swipe-to-go-back: a sideways flick in a
       1655px-wide table would have navigated the reader off the page. (Khalid, 2026-09-04:
       "feels kind of unprofessional and like i shouldn't be able to scroll past the limits".) */
    /* ★★ `none`, NOT `contain` — and the difference is the whole defect. `contain` stops the
       pane handing leftover scroll to the PAGE, but it explicitly KEEPS the element's own
       overscroll affordance: the pane's contents still slide past its own top/left/right edges
       and expose the pane's background as a white strip. That is what Khalid actually reported
       (2026-09-04, second pass): "i had to rapidly screenshot to capture it, while i was
       scrolling inside the table and tried to go up, higher than the table should have allowed
       … the same applies to the left and right". `none` suppresses the local bounce as well,
       on both axes, and still refuses the chain — including the horizontal one, which on macOS
       is swipe-to-go-back. */
    overscroll-behavior: none;
    border: 1px solid rgba(26, 23, 20, 0.06);
    border-radius: var(--explore-radius-lg);
    background: var(--paper);
    box-shadow: var(--explore-shadow-sm);
    animation: exploreSlideUp 0.5s ease-out 0.3s both;
}

.explore-table {
    /* ⚠ NOT `width: 100%`. The table's width is the SUM of its columns, written
       inline by renderTable from the same registry the <colgroup> comes from. A
       percentage width here would re-introduce exactly the model round 5A killed:
       columns divided into the canvas instead of summing to their own width. */
    border-collapse: collapse;
    font-size: var(--text-sm);
    table-layout: fixed;
}

/* Column widths come from the <colgroup> the column REGISTRY emits
   (explore.src.js `_columns()`), so the width map can never again disagree with
   the rendered column set. The old nth-child percentage block that lived here
   described a 6-column table while anonymous visitors rendered 5 — the widths
   summed to 88% and `table-layout: fixed` parked the missing 12% as a 150px
   dead gutter after the last column (2026-08-04 bug, Khalid's screenshot). */

/* Difficulty label in table */
.explore-table__cell--difficulty { text-align: center; }
.difficulty-label {
    font-size: 11px;
    font-weight: 600;
    padding: 3px 10px;
    border-radius: 12px;
    display: inline-block;
}
/* ★ 2026-07-28 (composition round 2). These three were the ONLY difficulty rules,
   against a label set that has said "Minimal / Light / Moderate / Heavy effort /
   Intensive" for some time. The class was built as `diffLabel.toLowerCase()`, so
   "Moderate effort" emitted `difficulty-label--moderate effort` — TWO class tokens,
   one of which happened to match `--moderate` below. Every other value emitted a
   class with no rule at all and rendered as a plain grey pill. Measured against the
   catalogue: 256 of 691 rows (level 3) were coloured and 435 were not, on the column
   a scanner uses to triage effort — and nothing failed, because every label was
   correct. The class now comes from the SCORE, never from the prose. */
/* ★★ ROUND 9 — A FIVE-STEP SCALE THAT READ AS THREE. The 2026-07-28 fix above made
   every step reach a rule; it did not check that the five rules were DISTINGUISHABLE.
   Measured (CIE76 ΔE between adjacent steps, on the rendered text colours):

       Minimal -> Light   17.9      Light -> Moderate  69.7
       Moderate -> Heavy  26.0      Heavy -> Intensive 13.0        spread 56.7

   The two smallest gaps are the two ENDS and the largest is the middle, so the ramp
   clustered into {Minimal, Light} green · {Moderate} amber · {Heavy, Intensive}
   orange — three steps wearing five labels, on the column a scanner uses to triage.
   ⚠ AND the amber failed contrast: #d97706 is 3.08:1 on paper, under the 4.5:1 floor
   for 11px text. That was never a finding because nothing measured it.

   The replacement was chosen by measurement, not by eye — candidates were scored on
   adjacent-ΔE spread AND contrast, and this one won both:

       Minimal -> Light   32.4      Light -> Moderate  39.6
       Moderate -> Heavy  37.1      Heavy -> Major     41.8        spread 9.4
       contrast on paper: 5.17 · 4.93 · 5.03 · 4.82 · 8.96  (all >= AA)

   One hue journey, green -> olive -> bronze -> sienna -> deep wine, monotonic in
   severity, so a reader can order two pills without reading either word.
   ⚠ `--intensive` KEEPS ITS TOKEN NAME while its label becomes "Major effort". The
   token is derived from the SCORE (`getDifficultyClass`) and renaming it in sympathy
   with a copy edit is precisely the coupling the note above exists to prevent. */
/* ★ ROUND 14 — measured on their OWN tinted grounds, not on the page's: light 4.38,
   moderate 4.46, heavy 4.31. Each pill sits on a wash of its own hue, which lifts the
   background and eats the margin the raw ink appeared to have. One step darker each;
   the five-rung ladder keeps its hues and its order. */
.difficulty-label--easy,
.difficulty-label--minimal { background: rgba(17,122,82,0.09); color: #0E6544; }
.difficulty-label--light { background: rgba(78,122,22,0.10); color: #426612; }
.difficulty-label--moderate { background: rgba(141,102,6,0.10); color: #785705; }
.difficulty-label--heavy,
.difficulty-label--hard { background: rgba(191,74,30,0.09); color: #A33D18; }
.difficulty-label--intensive { background: rgba(131,35,58,0.10); color: #83233A; }

/* Deadline cell */
.explore-table__cell--deadline { font-size: 0.82rem; color: var(--ink-secondary); }

/* Table header
   ★ 2026-07-29 — these headers were declared sticky and never stuck, because the wrap
   was a horizontal scroll container and the header pinned to the top of a box that
   itself scrolled out of the viewport (measured: y = -858px after a 1600px scroll).
   Round 3 answered that by removing the scroll container at desktop and, when insider
   columns forced it back, by painting a JS-synced CLONE of the labels under the filter
   bar.

   ★★ ROUND 5A — both of those are gone. The pane is now bounded to the viewport and
   owns vertical scroll, so `top: 0` here pins the header to the top of the PANE, which
   is where the rows are. That is the native pattern, it needs no clone, and it holds in
   every column configuration instead of only the ones that happened to fit. */
.explore-table thead {
    position: sticky;
    top: 0;
    z-index: 10;
}

.explore-table th {
    background: var(--cream-dark);
    color: var(--ink-muted);
    font-family: var(--explore-font-body);
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 12px 16px;
    text-align: left;
    border-bottom: 2px solid rgba(26, 23, 20, 0.06);
    white-space: nowrap;
    cursor: pointer;
    user-select: none;
    transition: color var(--transition-fast);
    overflow: hidden;
    /* `resize: horizontal` lived here — a half-working affordance that painted native
       grippies on every header and let users wedge columns into broken px widths.
       Column sizing is the registry's job now (2026-08-04). */
}

.explore-table th:hover {
    color: var(--ink-secondary);
}

.explore-table th.is-sorted {
    color: var(--accent-sienna);
}

.explore-table th .sort-arrow {
    display: inline-flex;
    align-items: center;
    margin-left: 4px;
    opacity: 0.5;
    vertical-align: middle;
    transition: opacity 0.15s ease, transform 0.15s ease;
}

.explore-table th .sort-arrow svg {
    width: 10px;
    height: 10px;
    stroke-width: 2.5;
    transition: transform 0.15s ease;
}

.explore-table th.is-sorted .sort-arrow {
    opacity: 1;
    color: var(--accent-sienna);
}

.explore-table th:hover .sort-arrow {
    opacity: 0.8;
}

/* Premium column headers */
.explore-table__th--premium {
    position: relative;
    background: linear-gradient(135deg, var(--cream-dark) 0%, var(--explore-premium-glow) 100%);
}

.explore-table__th--premium::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 2px;
    background: linear-gradient(90deg, var(--explore-premium-accent), var(--explore-premium-accent-dark));
}

/* Table cells */
.explore-table td {
    padding: 14px 16px;
    border-bottom: 1px solid rgba(26, 23, 20, 0.04);
    color: var(--ink-secondary);
    vertical-align: middle;
    transition: background var(--transition-fast);
}

/* Program name cell
   ⚠ `max-width: 280px` lived here and is removed in round 5A: Program is now the one
   FLEXIBLE column (300-480px, set from the registry via the colgroup). A stale cap
   describing the old fixed width is the kind of second width map this rebuild exists
   to delete, even though `table-layout: fixed` currently ignores it. */
.explore-table__cell--program {
    font-weight: 600;
    color: var(--ink);
}

.explore-table__cell--program .row-chevron {
    display: inline-block;
    width: 16px;
    font-size: 10px;
    /* ★ ROUND 14 — was `--ink-faint` (1.78:1). This glyph is the row's only cue that
       the record opens, so it is a UI control's affordance, not decoration, and it
       renders 25 times a screen. */
    color: var(--ink-muted);
    transition: transform var(--transition-fast), color var(--transition-fast);
    margin-right: 6px;
}

/* ⚠ THIS SELECTOR IS DECLARED TWICE IN THIS FILE — here, and again in the round-4
   uniform-row block near the bottom, which wins on order. Round 13 changes the type, so
   both copies state the same weight: a reader who finds this one first must not be told
   500 while the page renders 600. Size, colour and the two-line clamp live in the later
   block, beside the arithmetic that reserves the row's title height. */
.explore-table__cell--program .program-title {
    display: inline;
    font-family: var(--explore-font-display);
    font-weight: 600;
}

/* Row states */
.explore-table__row {
    cursor: pointer;
    transition: background var(--transition-fast);
}

.explore-table__row:hover td {
    background: rgba(196, 83, 43, 0.02);
}

/* ★ ROUND 5B — SELECTED, not expanded. The row whose record is open in the drawer
   has to stay findable in a grid the reader can still see, so it carries a left
   marker and a tinted body rather than the old "this row has grown a panel"
   treatment. The frozen Program cell paints its own opaque background (content
   scrolls under it), so it needs the tint stated separately or the row reads as
   broken in half — the same hazard as the hover rule. */
.explore-table__row.is-selected > td {
    background: var(--accent-forest-light);
    border-bottom-color: var(--accent-forest-light);
}
.explore-table tbody > tr.explore-table__row.is-selected > td:first-child {
    background: var(--accent-forest-light);
    /* ⚠ The fallback is load-bearing: `--exp-freeze-shadow` is declared inside the
       >=1024px frozen-column block, and an UNDEFINED var() invalidates the whole
       declaration (documented gotcha) — which would drop the selection marker
       entirely rather than just the freeze shadow. `none` inside a comma list does
       the same, so the fallback is a transparent shadow. */
    box-shadow: inset 3px 0 0 0 var(--accent-forest),
                var(--exp-freeze-shadow, 0 0 0 0 rgba(26, 23, 20, 0));
}
.explore-table__row.is-selected .row-chevron {
    color: var(--accent-forest);
    opacity: 1;
}

/* ============================================================
   4. TYPE BADGES
   ============================================================ */

.type-badge {
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border-radius: var(--radius-full);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.02em;
    white-space: nowrap;
    text-transform: capitalize;
}

.type-badge--grant           { background: var(--accent-forest-light); color: var(--accent-forest); }
.type-badge--tax-credit      { background: var(--accent-plum-light); color: var(--accent-plum); }
.type-badge--award           { background: var(--accent-sienna-light); color: var(--accent-sienna); }
.type-badge--program         { background: var(--accent-navy-light); color: var(--accent-navy); }
.type-badge--forgivable-loan { background: var(--accent-gold-light); color: var(--accent-gold); }
.type-badge--loan            { background: var(--accent-gold-light); color: var(--accent-gold); }

/* (Premium cell blur removed — table columns are no longer gated) */

/* Locked placeholder text styling */
.locked-placeholder {
    color: #94a3b8;
    font-family: inherit;
    letter-spacing: 0.5px;
}

/* Star / dot rating display */
.star-rating {
    font-family: var(--explore-font-data);
    font-size: 13px;
    letter-spacing: 2px;
    white-space: nowrap;
}

.star-rating .filled {
    color: var(--accent-forest);
}

.star-rating .empty {
    color: var(--ink-faint);
}

/* ============================================================
   6. SAMPLE ROW ANNOTATION
   ============================================================ */

.sample-row-note-tr td {
    padding: 0 16px 8px;
    border-bottom: 2px dashed rgba(26, 23, 20, 0.08);
    background: rgba(45, 106, 79, 0.04);
}

.sample-row-note {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: var(--accent-forest);
    font-weight: 500;
    font-style: italic;
    line-height: 1.5;
}

.sample-row-note::before {
    content: '\2605';
    font-size: 10px;
    color: var(--accent-forest);
    flex-shrink: 0;
}

@media (max-width: 767px) {
    .sample-row-note-tr td {
        padding: 6px 12px 10px;
        text-align: center;
    }

    .sample-row-note {
        justify-content: center;
        text-align: center;
        font-size: 10.5px;
        gap: 5px;
    }

    /* On cards, sample-row-note is already centered via .explore-card .sample-row-note */
    .explore-card .sample-row-note {
        font-size: 10.5px;
        padding: 6px 0 0;
        line-height: 1.5;
    }
}

/* ============================================================
   7. DETAIL PANEL — the record body
   ⚠ ROUND 5B: this panel is no longer an inline `<tr class="explore-detail-row">`
   under the row. `renderDetailPanel()` output now renders in the RIGHT-SIDE
   DRAWER on desktop and in the bottom sheet on mobile; the `.explore-detail-row`
   selector and its `--exp-wrap-w` pinning are deleted (the pin existed only
   because a full-width `<td>` inside a horizontally scrolling pane cannot be as
   wide as the visible box). Drawer rules live in the round-5B block at the end
   of this file; everything below still styles the panel itself, unchanged.
   ============================================================ */

.explore-detail {
    display: flex;
    flex-direction: column;
    padding: 0;
    animation: slideDown var(--explore-row-expand-speed) cubic-bezier(0.34, 1.56, 0.64, 1);
    /* Apr 25 (post-QA Lens 2) — clear the stacked sticky bars (nav + filter).
       Without this, scroll-into-view on row expand hides the banner + title
       + toolbar behind the filter bar. 140px covers nav (~64px) + filter bar
       (~52px desktop, ~110px when wrapped to 2 rows on tablet) + buffer. */
    scroll-margin-top: 140px;
}

@keyframes slideDown {
    from {
        opacity: 0;
        max-height: 0;
        transform: translateY(-8px);
    }
    to {
        opacity: 1;
        max-height: 3000px;
        transform: translateY(0);
    }
}

/* --- Zone 1: Header Bar --- */

/* Option D (Apr 25 2026) — Restructured detail header.
   Status banner at top, title row below, action toolbar separate.
   Anchor for the absolute-positioned close button. */
.explore-detail {
    position: relative;
}

.explore-detail__close {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: 1px solid rgba(26, 23, 20, 0.10);
    background: rgba(255, 255, 255, 0.85);
    color: var(--ink-muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    z-index: 5;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
    font-family: inherit;
}
.explore-detail__close:hover {
    background: var(--paper, #fff);
    color: var(--ink);
    border-color: rgba(26, 23, 20, 0.18);
}
.explore-detail__close:focus-visible {
    outline: 2px solid var(--accent-forest);
    outline-offset: 2px;
}

/* --- Status banner above title (Option D) --- */
.exp-banner {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 10px var(--space-xl);
    background: rgba(26, 23, 20, 0.03);
    border-bottom: 1px solid rgba(26, 23, 20, 0.06);
    font-size: 13px;
    line-height: 1.45;
    font-family: var(--explore-font-body);
    /* Apr 25 2026 (post-QA Lens 4) — was --ink-muted (#9a9085) which yielded
       2.74:1 contrast on the banner background, failing WCAG 1.4.3 AA. Bumped
       to --ink-secondary (#5c5549) → 8.06:1, comfortably AAA. */
    color: var(--ink-secondary, #5c5549);
}
.exp-banner--pursued {
    background: var(--accent-forest-light, #E8EFEA);
    color: var(--accent-forest, #1F4A37);
}
.exp-banner__chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: inherit;
    white-space: nowrap;
}
.exp-banner__chip strong {
    color: var(--ink);
    font-weight: 600;
}
.exp-banner--pursued .exp-banner__chip strong {
    color: var(--accent-forest, #1F4A37);
}
.exp-banner__chip--good {
    color: var(--accent-forest, #1F4A37);
    font-weight: 600;
}
.exp-banner__chip--warn {
    /* Apr 25 (post-QA Lens 4) — was #B85C2E sienna (3.97:1 fails AA at 13px).
       Switched to a darker brand-consistent sienna (~5.3:1) for AA pass. */
    color: var(--sienna-dk, #A3401F);
    font-weight: 600;
}
.exp-banner__check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--accent-forest, #1F4A37);
    color: #fff;
    font-size: 9px;
    font-weight: 700;
    margin-right: 2px;
}
.exp-banner__match-dot {
    color: var(--accent-forest, #1F4A37);
    font-size: 14px;
    line-height: 1;
}
.exp-banner__dot {
    color: rgba(26, 23, 20, 0.25);
    user-select: none;
}

@media (max-width: 640px) {
    .exp-banner {
        gap: 6px 8px;
        padding: 9px var(--space-lg);
        font-size: 12.5px;
    }
    /* Apr 25 (post-QA Lens 3 D14) — long amount strings (e.g. ranges with
       per-unit qualifiers) were pushing the banner past the bottom of the
       mobile viewport, hiding the title. Cap chip width + ellipsis on phone. */
    .exp-banner__chip {
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
    }
}

/* --- Title row (simplified — no more action toolbar inline) --- */
.detail-header {
    display: flex;
    align-items: flex-start;
    gap: var(--space-lg);
    padding: var(--space-lg) var(--space-xl) calc(var(--space-md) + 2px);
    background: var(--cream);
    /* Removed bottom border — toolbar continues the visual row */
}

.detail-header__info {
    flex: 1;
    min-width: 0;
    /* Reserve space for the absolute-positioned × close (32px + 12px gap) */
    padding-right: 48px;
}

.detail-header__pills {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 6px;
}

.detail-header__info h3 {
    font-family: var(--explore-font-display);
    font-size: var(--text-xl);
    font-weight: 500;
    color: var(--ink);
    margin: 0 0 2px;
    line-height: 1.3;
}

.detail-header__org {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-muted);
    margin: 0;
}

.detail-meta-chip {
    display: inline-flex;
    align-items: center;
    padding: 4px 12px;
    border-radius: var(--radius-full);
    font-size: 11px;
    font-weight: 700;
    font-family: var(--explore-font-body);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    background: var(--accent-forest-light);
    color: var(--accent-forest);
    white-space: nowrap;
}

.detail-meta-chip--muted {
    background: rgba(26, 23, 20, 0.05);
    color: var(--ink-muted);
}

/* --- Action toolbar (Option D) --- */
.detail-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-lg);
    flex-wrap: wrap;
    padding: 0 var(--space-xl) var(--space-lg);
    background: var(--cream);
    border-bottom: 1px solid rgba(26, 23, 20, 0.06);
}
.detail-toolbar__primary {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    min-height: 36px;
}
.detail-toolbar__secondary {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
}
.detail-toolbar__icon {
    width: 36px;
    height: 36px;
    border-radius: 8px;
    border: 1px solid rgba(26, 23, 20, 0.12);
    background: var(--paper, #fff);
    color: var(--ink-muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
    font-family: inherit;
}
.detail-toolbar__icon:hover {
    border-color: var(--accent-forest);
    color: var(--accent-forest);
}
.detail-toolbar__icon.is-active {
    background: var(--accent-forest);
    border-color: var(--accent-forest);
    color: #fff;
}
.detail-toolbar__icon:focus-visible {
    outline: 2px solid var(--accent-forest);
    outline-offset: 2px;
}
.detail-toolbar__link {
    color: var(--accent-forest);
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: border-color 120ms ease;
    white-space: nowrap;
    font-family: var(--explore-font-body);
}
.detail-toolbar__link:hover {
    border-bottom-color: var(--accent-forest);
}

/* Apr 25 (post-QA Lens 3 C12) — anonymous-user Pursue stand-in.
   Captures conversion intent at peak interest (user just opened the
   detail panel) by routing them into signup. */
.exp-pursue-signin {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 16px;
    background: var(--accent-forest, #1F4A37);
    color: #fff;
    border: none;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    font-family: var(--explore-font-body);
    transition: background 120ms ease;
    height: 36px;
    line-height: 1;
    white-space: nowrap;
}
.exp-pursue-signin:hover {
    background: var(--accent-forest-dk, #163828);
}
.exp-pursue-signin:focus-visible {
    outline: 2px solid var(--accent-sienna, #B85C2E);
    outline-offset: 2px;
}

@media (max-width: 640px) {
    .detail-header {
        padding: var(--space-md) var(--space-lg) var(--space-sm);
    }
    .detail-toolbar {
        padding: 0 var(--space-lg) var(--space-md);
        gap: 12px;
    }
    .detail-toolbar__secondary {
        gap: 12px;
        width: 100%;
    }
    .detail-toolbar__link {
        font-size: 12.5px;
    }
    /* Apr 25 (post-QA Lens 4) — bump touch targets to WCAG 2.5.8 minimums on phone.
       Desktop precision-pointer users are exempt; mobile users hit 44px. */
    .detail-toolbar__icon {
        width: 44px;
        height: 44px;
    }
    .explore-detail__close {
        width: 40px;
        height: 40px;
    }
}

/* --- Zone 2: Quick Facts Strip --- */

.detail-facts {
    display: flex;
    align-items: stretch;
    gap: 0;
    padding: 0;
    background: var(--paper);
    border-bottom: 1px solid rgba(26, 23, 20, 0.06);
}

.detail-fact {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: var(--space-md) var(--space-lg);
    border-right: 1px solid rgba(26, 23, 20, 0.06);
    min-width: 0;
}

.detail-fact:last-child {
    border-right: none;
}

/* ══════════════════════════════════════════════════════════════════════════
   ROUND 16 — THE RECORD'S WIN-LAYER STRIP (`_recordWinStrip`)
   ══════════════════════════════════════════════════════════════════════════
   The drawer covers five of the eight columns (measured: a 490px panel over
   x=950..1440 at 1440 wide), and all five of them are the paid band. This strip
   is what the reader keeps when the grid goes under the panel. It sits directly
   under the free facts strip, so the record reads free-then-paid top to bottom,
   the same left-to-right order the grid uses — one mental model, two axes.

   It is a DATA strip carrying lock cues, never an offer: the record already makes
   its one offer further down, and two offer blocks inside ~120px is the measured
   paywall-not-a-page defect. Tinted with the band's own colour so a reader who has
   seen the grid recognises it without being told. */
.rws {
    background: linear-gradient(180deg, rgba(232, 168, 124, 0.055), rgba(232, 168, 124, 0.02));
    border-bottom: 1px solid rgba(26, 23, 20, 0.06);
    padding: 11px var(--space-lg) 13px;
}
.rws__head {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 10px;
    font-family: var(--explore-font-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: #8a5a34;
    margin-bottom: 8px;
}
.rws--open .rws__head { color: var(--accent-forest, #2D6A4F); }
.rws__lock { display: inline-flex; align-items: center; opacity: 0.72; }
/* `minmax(0, 1fr)`, never `1fr`: a `1fr` track floors at min-content, so one long
   value ("Not applicable", "$100K-$350K") would push the strip past the drawer and
   the panel would scroll sideways. Documented trap on this surface. */
.rws__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));
    gap: 9px 14px;
}
.rws__cell { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rws__label {
    font-size: 9.5px;
    font-family: var(--explore-font-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--ink-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.rws__value {
    display: flex;
    align-items: center;
    gap: 5px;
    font-family: var(--explore-font-data);
    font-size: 13px;
    font-weight: 600;
    color: var(--ink, #1C1917);
    min-width: 0;
}
.rws__value > span { flex-shrink: 0; }
/* A locked SCALAR blurs — there is one number and it is the product. A locked
   SHAPE does not — its count differentiates records and the list is the product,
   so a free reader gets something true and still cannot read the answer. Same
   division the grid makes, made here by the same registry flag. */
.rws__cell.is-locked .rws__value {
    filter: blur(4.5px);
    user-select: none;
    pointer-events: none;
}
.rws__cell--absent .rws__value { color: var(--ink-muted); font-weight: 500; }
.rws__cell--absent.is-veiled .rws__value { opacity: 0.42; }
@media (prefers-reduced-motion: reduce) { .rws { transition: none; } }

.detail-fact__label {
    font-size: 10px;
    font-family: var(--explore-font-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-muted);
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.detail-fact__value {
    font-family: var(--explore-font-body);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink);
    line-height: 1.4;
}

.detail-fact__value--highlight {
    color: var(--accent-forest);
    font-family: var(--explore-font-data);
    font-size: var(--text-base);
}

/* ⚠ ROUND 16 — `.detail-fact--premium` deleted with the cell it tinted. `Realistic`
   left this strip when the record's Win Layer strip landed directly beneath it (the
   two printed the same figure twice, 200px apart, in two visual languages); nothing
   emits this class now, and a rule for an element that never renders is the "component
   we load and never call" shape this round was auditing for. Its companion
   `.detail-fact__premium-tag` goes with it, below. */
.detail-fact--wide {
    min-width: 220px;
    flex: 1.5;
}
.detail-fact__value--formatted {
    font-size: 0.82rem;
    line-height: 1.5;
}
.detail-fact__value--formatted strong {
    font-weight: 600;
    color: var(--accent-forest);
}

/* --- Zone 3: Body (Description + Intel side-by-side) --- */

.detail-body {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0;
    min-height: 0;
}

.detail-body__main {
    padding: var(--space-lg) var(--space-xl);
    border-right: 1px solid rgba(26, 23, 20, 0.06);
}

.detail-body__desc {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    line-height: 1.7;
    margin-bottom: var(--space-lg);
}

.detail-eligibility {
    margin-bottom: var(--space-lg);
}

.detail-section-label {
    font-size: 11px;
    font-family: var(--explore-font-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-muted);
    margin-bottom: var(--space-xs);
}

.detail-eligibility ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

.detail-eligibility li {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    padding: 3px 0 3px 20px;
    position: relative;
    line-height: 1.5;
}

.detail-eligibility li::before {
    content: '\2192';
    position: absolute;
    left: 0;
    color: var(--accent-forest);
    font-weight: 600;
}

.detail-body__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
}

.tag {
    display: inline-flex;
    padding: 3px 10px;
    border-radius: var(--radius-full);
    font-size: 11px;
    font-weight: 600;
    font-family: var(--explore-font-body);
    background: var(--cream-dark);
    color: var(--ink-secondary);
    white-space: nowrap;
}

.tag--province {
    background: var(--accent-navy-light);
    color: var(--accent-navy);
}

.tag--stage {
    background: var(--accent-forest-light);
    color: var(--accent-forest);
}

/* --- Full-width premium sections (below the two-column body) --- */

.detail-fullwidth {
    padding: var(--space-md) var(--space-xl);
    border-top: 1px solid rgba(26, 23, 20, 0.06);
}

/* --- Buttons --- */

.btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 20px;
    border-radius: var(--explore-radius);
    font-size: var(--text-sm);
    font-weight: 600;
    font-family: var(--explore-font-body);
    cursor: pointer;
    transition: all var(--transition-fast);
    text-decoration: none;
    border: none;
    white-space: nowrap;
}

.btn i, .btn svg {
    width: 14px;
    height: 14px;
}

.btn--sm {
    padding: 8px 16px;
    font-size: 12px;
}

.btn--primary {
    background: var(--accent-forest);
    color: #fff;
}

.btn--primary:hover {
    background: var(--accent-forest-deep);
    transform: translateY(-1px);
    box-shadow: var(--explore-shadow-md);
}

.btn--outline {
    background: var(--paper);
    color: var(--ink-secondary);
    border: 1px solid rgba(26, 23, 20, 0.12);
}

.btn--outline:hover {
    border-color: rgba(26, 23, 20, 0.20);
    background: var(--cream);
}

.btn--accent {
    background: var(--accent-forest);
    color: #fff;
}

.btn--accent:hover {
    background: var(--accent-forest-deep);
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(45, 106, 79, 0.3);
}

/* --- Zone 4: Premium Intel Panel --- */

.detail-intel {
    position: relative;
    padding: var(--space-lg) var(--space-xl);
    background: rgba(45, 106, 79, 0.02);
}

.detail-intel.is-locked {
    overflow: hidden;
    min-height: 420px;
}

.detail-intel.is-locked > *:not(.premium-overlay) {
    filter: blur(8px);
    user-select: none;
    pointer-events: none;
}

/* ★ THE OVERLAY WAS SILENTLY CLIPPING ITS OWN CTA (measured 2026-08-04: 500px of
   content inside a 472px box with overflow:hidden, centred, so ~14px came off each
   end — and 138px off both on the nine records where every Win Layer field is
   absent, which took the pricing buttons with it). The cause is structural, not a
   content overflow: the card is `position:absolute; inset:0`, so it is sized by the
   OTHER grid column and can never make room for itself.

   It only needs to be absolute when there is blurred content behind it to cover.
   In the free-user detail panel it is the sidebar's ONLY child — nothing is behind
   it — so there it flows, and the box grows to fit instead of cutting the offer in
   half. `min-height` still holds the floor. */
.detail-intel.is-locked > .premium-overlay:only-child {
    position: relative;
    inset: auto;
    background: none;
    backdrop-filter: none;
}

/* Premium overlay for free users */
.premium-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 252, 247, 0.65);
    backdrop-filter: blur(2px);
    z-index: 5;
    border-radius: 0;
}

.premium-overlay__content {
    text-align: center;
    padding: var(--space-lg) var(--space-xl);
    max-width: 300px;
}

.premium-overlay__content > svg {
    width: 28px;
    height: 28px;
    color: var(--explore-premium-accent);
    margin-bottom: var(--space-xs);
}

.premium-overlay__content h4 {
    font-family: var(--explore-font-display);
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--ink);
    margin-bottom: var(--space-xs);
}

.premium-overlay__content p {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    line-height: 1.6;
    margin-bottom: var(--space-md);
}

.premium-overlay__teaser {
    font-size: 11px;
    font-family: var(--explore-font-data);
    font-weight: 600;
    color: var(--accent-forest);
    margin-bottom: var(--space-md);
    letter-spacing: 0.02em;
}

.premium-overlay__footnote {
    font-size: 11px;
    color: var(--ink-muted);
    margin-top: var(--space-sm);
}

/* --- Intel: Insider Tip callout --- */

.intel-tip {
    display: flex;
    gap: var(--space-md);
    padding: var(--space-md);
    background: rgba(45, 106, 79, 0.05);
    border-left: 3px solid var(--accent-forest);
    border-radius: 0 var(--explore-radius) var(--explore-radius) 0;
    margin-bottom: var(--space-lg);
}

.intel-tip__icon {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--accent-forest);
    color: #fff;
    border-radius: 6px;
}

.intel-tip__icon svg {
    width: 14px;
    height: 14px;
    stroke: #fff;
}

.intel-tip__body {
    flex: 1;
    min-width: 0;
}

.intel-tip__label {
    display: block;
    font-size: 10px;
    font-family: var(--explore-font-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--accent-forest);
    margin-bottom: 4px;
}

.intel-tip__body p {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    line-height: 1.6;
    font-style: italic;
    margin: 0;
}

/* --- Intel: Scorecard (rating bars) --- */

.intel-scorecard {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    margin-bottom: var(--space-lg);
}

.rating-bar {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}

.rating-bar__label {
    font-size: 11px;
    font-weight: 600;
    font-family: var(--explore-font-body);
    color: var(--ink-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    width: 80px;
    flex-shrink: 0;
}

.rating-bar__track {
    flex: 1;
    height: 6px;
    background: var(--cream-dark);
    border-radius: var(--radius-full);
    overflow: hidden;
}

.rating-bar__fill {
    height: 100%;
    border-radius: var(--radius-full);
    background: var(--accent-forest);
    transition: width var(--transition-base);
}

.rating-bar__fill[data-level="high"] {
    background: var(--accent-forest);
}

.rating-bar__fill[data-level="medium"] {
    background: var(--accent-gold);
}

.rating-bar__fill[data-level="low"] {
    background: var(--accent-sienna);
}

.rating-bar__value {
    font-family: var(--explore-font-data);
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-secondary);
    width: 28px;
    text-align: right;
}

/* --- Intel: Key-Value Row (funding details) --- */

.intel-kv-row {
    display: flex;
    gap: var(--space-lg);
    margin-bottom: var(--space-lg);
    padding: var(--space-sm) 0;
    border-top: 1px solid rgba(26, 23, 20, 0.06);
    border-bottom: 1px solid rgba(26, 23, 20, 0.06);
}

.intel-kv {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.intel-kv__label {
    font-size: 10px;
    font-family: var(--explore-font-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-muted);
}

.intel-kv__value {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    font-weight: 600;
    color: var(--ink);
}

/* --- Intel: Sections with cards --- */

.intel-section {
    margin-bottom: var(--space-lg);
}

.intel-section__title {
    font-size: 11px;
    font-family: var(--explore-font-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-muted);
    margin-bottom: var(--space-sm);
    padding-bottom: var(--space-xs);
    border-bottom: 1px solid rgba(26, 23, 20, 0.06);
}

.intel-section__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-sm);
}

.intel-card {
    padding: var(--space-md);
    border-radius: var(--explore-radius);
    background: var(--paper);
    border: 1px solid rgba(26, 23, 20, 0.06);
}

.intel-card__label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-family: var(--explore-font-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ink-muted);
    margin-bottom: var(--space-xs);
}

.intel-card__label svg {
    width: 12px;
    height: 12px;
    flex-shrink: 0;
}

.intel-card p {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    line-height: 1.6;
    margin: 0;
}

.intel-card ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

.intel-card li {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    padding: 3px 0 3px 18px;
    position: relative;
    line-height: 1.5;
}

.intel-card li.more {
    color: var(--ink-muted);
    font-style: italic;
    padding-left: 0;
}

/* Document card checkboxes */
.intel-card--docs li::before {
    content: '\2610';
    position: absolute;
    left: 0;
    color: var(--ink-faint);
    font-size: 12px;
}

/* Pitfalls card warnings */
.intel-card--pitfalls {
    border-color: rgba(196, 83, 43, 0.12);
    background: rgba(196, 83, 43, 0.02);
}

.intel-card--pitfalls .intel-card__label {
    color: var(--accent-sienna);
}

.intel-card--pitfalls li::before {
    content: '\26A0';
    position: absolute;
    left: 0;
    font-size: 11px;
    color: var(--accent-sienna);
}

/* Success profile — full width */
.intel-card--success {
    grid-column: 1 / -1;
    border-color: rgba(45, 106, 79, 0.12);
    background: rgba(45, 106, 79, 0.02);
}

/* Stacking partners */
.intel-card--stacking {
    grid-column: 1 / -1;
}

.intel-stacking-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.intel-stack-item {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 4px 0 4px 20px;
    position: relative;
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    border-bottom: 1px solid rgba(26, 23, 20, 0.04);
}

.intel-stack-item:last-child {
    border-bottom: none;
}

.intel-stack-item strong {
    color: var(--ink);
    font-weight: 600;
    flex-shrink: 0;
}

.intel-stack-item span {
    font-size: 12px;
    color: var(--ink-muted);
}

.intel-stack-item.is-compatible::before {
    content: '\2713';
    position: absolute;
    left: 0;
    color: var(--accent-forest);
    font-weight: 700;
}

.intel-stack-item.is-incompatible::before {
    content: '\26A0';
    position: absolute;
    left: 0;
    color: var(--accent-gold);
}

/* Deadline card — full width */
.intel-card--deadline {
    grid-column: 1 / -1;
}

/* ============================================================
   8. CONTINUOUS LOAD  (round 4 — replaced PAGINATION)
   ============================================================
   The 25-per-page control set is gone. What remains is one element that is both
   the observer sentinel and the explicit gate: after a bounded run of automatic
   chunks the button appears and NOTHING loads until it is pressed, which is what
   keeps the pricing block, the geo links and the footer reachable. */

.explore-more {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: var(--space-xl) 0;
    /* No min-height: an empty sentinel must not reserve a band of dead page
       between the last row and the footer. */
}

/* ★★ ROUND 16 — THE IN-PANE COPY OF THE CONTROL, and the ONE that desktop reads.
   A scroll pane does not move the page, so a reader who scrolls the rows to their
   end has not scrolled the document at all and never meets a control that lives
   below the pane. Measured at 1440x900 with the page at rest: pane bottom 838,
   control 1009-1067, fold 900. The list's own continuation belongs to the list.

   ⚠ `sticky; left: 0; width: var(--exp-wrap-w)` is the SAME pin the ghost row and
   the recognition banner use, and for the same reason: a block inside a
   horizontally scrolling pane is laid out against the pane's content box, so
   without it the control drifts off the visible strip the moment the reader scrolls
   sideways to read the Win Layer. `--exp-wrap-w` is published by
   `_syncWrapWidthVar()` on every render and resize. */
.explore-more--pane {
    position: sticky;
    left: 0;
    width: var(--exp-wrap-w, 100%);
    max-width: 100%;
    box-sizing: border-box;
    padding: 18px 0 22px;
}
/* The page-level host stays in the DOM at every width — it is the window observer's
   target and `_maybeAutoAppend` measures its rect in window mode — but only ONE of
   the two is ever on screen. Desktop reads the pane copy; mobile (no pane) reads the
   page copy. Two visible copies of one control is the "same fact in two voices"
   defect this surface has fixed twice already. */
@media (min-width: 1024px) {
    #explore-more { display: none; }
}

/* ★★ 2026-08-06 — TWO LINES, because the control is the only thing in view when it is
   pressed. The action is line 1 and keeps the button's whole previous treatment; line 2
   states position, which used to live only in the sticky meta at the top of the page —
   thousands of pixels away, and on a phone never on screen at the same time. Column
   direction, not a wider single line: the block is centred in an empty band, so height is
   free here and width is not (`.explore-more__btn` is capped to 340px on touch layouts). */
.explore-more__btn {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    min-height: 44px;
    padding: 10px 26px;
    border: 1px solid rgba(26, 23, 20, 0.14);
    border-radius: var(--explore-radius);
    background: var(--paper);
    color: var(--ink-secondary);
    font-size: var(--text-sm);
    font-weight: 600;
    font-family: var(--explore-font-data);
    letter-spacing: 0.01em;
    cursor: pointer;
    transition: border-color var(--transition-fast), color var(--transition-fast), background var(--transition-fast);
}

.explore-more__do { display: block; }

/* Quieter than the action by a full step, so the control still reads as a button first
   and the position as its caption. `--ink-muted` is 5.24:1 on cream (AA at any size);
   `--ink-faint` is a NON-TEXT token (1.69:1) and must never carry a sentence. */
.explore-more__pos {
    display: block;
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: var(--ink-muted);
    font-variant-numeric: tabular-nums;
}

.explore-more__btn:hover {
    border-color: var(--accent-sienna);
    color: var(--accent-sienna);
    background: var(--accent-sienna-light);
}
/* The caption follows the button into its hover state — left on `--ink-muted` it read as
   a disabled line inside a live control. */
.explore-more__btn:hover .explore-more__pos { color: var(--accent-sienna); }

.explore-more__btn:focus-visible {
    outline: 2px solid var(--accent-forest);
    outline-offset: 2px;
}

/* ★★ THE ARRIVAL MARK. The press appends 25 rows above the control and, without this,
   nothing on screen changes at the moment of the press. The first row of the new block
   is marked and focused (see `_markChunkArrival`), so the reader is carried to where the
   new material starts and can see the boundary. Non-gaudy on purpose: a left rule in the
   surface's own forest, and a tint that is barely a tint — this is a landmark, not an
   alert, and it competes with `.is-selected` if it shouts.
   ⚠ `box-shadow`, not `border-left`: a border on a <tr> child would shift the row's cells
   by its own width and the whole block would jog sideways for two seconds. */
.explore-table__row.exp-arrived > td,
.explore-card.exp-arrived {
    background: rgba(45, 106, 79, 0.055);
    animation: expArrived 2.2s ease-out 1 forwards;
}
.explore-table__row.exp-arrived > td:first-child,
.explore-card.exp-arrived {
    box-shadow: inset 3px 0 0 0 var(--accent-forest, #2d6a4f);
}
.explore-table__row.exp-arrived:focus-visible > td,
.explore-card.exp-arrived:focus-visible { outline: none; }
@keyframes expArrived {
    0%   { background-color: rgba(45, 106, 79, 0.13); }
    100% { background-color: rgba(45, 106, 79, 0.00); }
}
/* ⚠ The mark still has to EXIST without motion — it is the only visible confirmation the
   press produced anything. Reduced motion removes the fade, never the landmark; the
   class is cleared on a timer rather than on `animationend`, so this path still ends. */
@media (prefers-reduced-motion: reduce) {
    .explore-table__row.exp-arrived > td,
    .explore-card.exp-arrived { animation: none; background: rgba(45, 106, 79, 0.07); }
}

/* End of list. Closure, not decoration: without it the last row and the footer
   meet with no signal, which reads as a page that stopped loading. */
.explore-more__end {
    margin: 0;
    font-family: var(--explore-font-data);
    font-size: 12.5px;
    font-weight: 500;
    letter-spacing: 0.02em;
    /* ⚠ WAS `--ink-faint`, which this file's own token block labels NON-TEXT ONLY
       (1.69:1 on cream — rules, dots, disc fills). It was carrying a whole sentence.
       Found 2026-08-06 while measuring the two named AA failures; same defect class,
       fixed in passing. `--ink-muted` is 5.24:1. */
    color: var(--ink-muted);
}

/* ============================================================
   9. CARD LAYOUT (Mobile < 1024px)
   ============================================================ */

.explore-cards {
    display: grid;
    gap: var(--space-md);
    animation: exploreSlideUp 0.5s ease-out 0.3s both;
}

.explore-card {
    background: var(--paper);
    border: 1px solid rgba(26, 23, 20, 0.06);
    border-radius: var(--explore-radius-lg);
    padding: var(--space-lg);
    cursor: pointer;
    transition: box-shadow var(--transition-fast), border-color var(--transition-fast);
}

.explore-card:hover {
    border-color: rgba(26, 23, 20, 0.12);
    box-shadow: var(--explore-shadow-md);
}

.explore-card__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: var(--space-sm);
    gap: 8px;
}

/* ★★ ROUND 16 — the card's position number. Same argument as the desktop row's, only
   stronger: at 390 a card is close to a screenful, so a phone reader is further from
   any landmark than a grid reader ever is. It leads the header line, in the machine
   face, quiet — it is a coordinate, not a rating, and it must never read as a rank
   the list was scored by. */
.explore-card__rank {
    flex: 0 0 auto;
    font-family: var(--explore-font-data);
    font-size: 11px;
    font-weight: 600;
    color: var(--ink-muted);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
}
/* The amount takes the middle and may wrap; the number and the save keep their track. */
.explore-card__header .explore-card__amount { flex: 1 1 auto; min-width: 0; text-align: right; }

/* ★★ ROUND 13 — THE PHONE HAD THE SAME INVERSION AS THE DESKTOP ROW, BY A DIFFERENT
   MECHANISM. Measured at 390 on the round-12 build: this figure rendered in IBM Plex
   Mono — the face reserved sitewide for machine readouts, so it reads as the one
   authoritative number on the card — at w600 in full ink, in the price-tag slot at the
   top right, above a program name in a LIGHTER weight. The loudest thing on a phone card
   was the advertised ceiling, which is the number this page exists to qualify.
   It is the body face now, quiet, and it NAMES itself (see `.eca__lbl`). */
.explore-card__amount {
    font-family: var(--explore-font-body);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--ink-3, #78716c);
}

.explore-card__title {
    font-family: var(--explore-font-display);
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--ink);
    margin-bottom: 4px;
    line-height: 1.3;
}

.explore-card__org {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-muted);
    margin-bottom: 4px;                /* the tagline follows; the pair owns the gap below */
}

/* What the program IS, on the phone card. The card carried 6–7 pills and no sentence,
   so every card looked alike and none of them said what it funded. This is the one line
   that distinguishes them, so it sits directly under the identity and above the pills. */
.explore-card__tagline {
    font-size: 13px;
    line-height: 1.45;
    color: var(--ink-2, #57534E);
    margin: 0 0 var(--space-md);
}
/* When the funder line is suppressed (title === organization) the tagline inherits the
   gap the org line would have owned. */
.explore-card__title + .explore-card__tagline { margin-top: 5px; }

.explore-card__premium-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    padding: var(--space-sm);
    background: var(--cream);
    border-radius: var(--explore-radius);
    margin-bottom: var(--space-md);
    position: relative;
}

.explore-card__premium-grid .cell-content {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: var(--text-sm);
}

.explore-card__premium-grid .premium-label {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-muted);
    font-family: var(--explore-font-body);
}

/* ─── Mobile Card Locked State ─── */
.explore-card__premium-grid.is-locked .cell-content {
    filter: blur(5px);
    opacity: 0.7;
    user-select: none;
}

.explore-card__premium-grid.is-locked .locked-placeholder {
    color: #94a3b8;
}

/* Premium lock badge for mobile cards */
.card-locked-badge {
    display: none;
}

.explore-card__premium-grid.is-locked .card-locked-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 2;
    padding: 4px 12px;
    background: var(--paper);
    border: 1px solid rgba(45, 106, 79, 0.2);
    border-radius: var(--radius-full);
    font-size: 11px;
    font-weight: 600;
    font-family: var(--explore-font-body);
    color: var(--accent-forest);
    white-space: nowrap;
    box-shadow: var(--explore-shadow-sm);
}

.card-locked-badge svg {
    flex-shrink: 0;
    opacity: 0.8;
}

.explore-card__premium-grid.is-locked::after {
    content: '';
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 252, 247, 0.5);
    border-radius: var(--explore-radius);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%232d6a4f' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='11' width='18' height='11' rx='2' ry='2'%3E%3C/rect%3E%3Cpath d='M7 11V7a5 5 0 0 1 10 0v4'%3E%3C/path%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
}

.explore-card__premium-more {
    font-size: 11px;
    color: var(--accent-forest);
    text-align: center;
    padding: 4px 0 0;
    font-family: var(--explore-font-body);
    font-weight: 500;
}

/* On small screens, hide secondary premium fields and show only 2 */
@media (max-width: 480px) {
    .explore-card__premium-grid {
        grid-template-columns: 1fr;
        gap: 6px;
    }

    .explore-card__premium-secondary {
        display: none;
    }

    .explore-card__premium-grid.is-locked .explore-card__premium-secondary {
        display: none;
    }
}

/* Deadline / status on the mobile card (2026-07-28). The card previously showed
   every attribute EXCEPT whether the program is open — the one fact that decides
   whether a member can act on it. Sits between the badges and the tags so it reads
   right after "how hard is this" and before "where does it apply". */
.explore-card__deadline {
  font-family: var(--explore-font-mono, 'IBM Plex Mono', monospace);
  font-size: 11.5px;
  font-weight: 600;
  color: #6B6458;
  letter-spacing: 0.01em;
  margin: 8px 0 2px;
}
.explore-card__deadline--urgent { color: #B3261E; }

.explore-card__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: var(--space-md);
}

.explore-card__tags .tag {
    font-size: 10px;
    padding: 2px 8px;
}

/* ── The Win Layer, on a card (round 8) ───────────────────────────────────────
   ★★ The phone had NO expression of the paid product at all — measured at 390px,
   anonymous: no locked cell, no blurred cell, the words "Win Layer" nowhere on the
   page, and a 1,427px pricing block underneath selling research the reader had not
   been shown one value of. This block is that evidence, built from the SAME
   `_winLayerManifest` the record drawer uses.
   ⚠ It is a LOCK CUE, not an offer: no price, no button, no verb. The one offer on
   this screen stays `.explore-pricing`, and ~25 cards each carrying an offer is the
   CTA-density defect this repo has already measured once. */
.explore-card__win {
    margin: 0 0 var(--space-md);
    padding: 10px 12px 4px;
    border: 1px solid rgba(184, 134, 11, 0.3);
    border-radius: var(--explore-radius);
    background: rgba(184, 134, 11, 0.06);
}
.explore-card__win.is-open {
    border-color: rgba(45, 106, 79, 0.32);
    background: rgba(45, 106, 79, 0.05);
}
.ecw__head {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-bottom: 8px;
}
.ecw__name {
    font: 700 9.5px/1.2 var(--explore-font-body);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #6b4a12;
}
.explore-card__win.is-open .ecw__name { color: var(--accent-forest, #2d6a4f); }
.ecw__lock { display: inline-flex; color: var(--amber-ink); opacity: 0.85; }
.ecw__tag {
    font: 700 9px/1 var(--explore-font-body);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--accent-forest, #2d6a4f);
    background: rgba(45, 106, 79, 0.12);
    border-radius: 3px;
    padding: 3px 6px;
    cursor: help;
}
/* Two across at 390px is ~150px a cell, which seats a short uppercase label with the
   value under it. The QUESTION is not printed here — see `_winLayerManifest`: five
   sentences × ~25 cards is wallpaper, so on a card it lives on the label's own hover
   card, which is also the only `data-tip` layer a phone had. */
.wl-manifest--card {
    gap: 9px 12px;
    margin-bottom: 8px;
}
.wl-manifest--card .wlm { gap: 2px; min-width: 0; }
/* ⚠ `align-items: stretch`, NOT `flex-start`, and it was found by measuring rather
   than by reading. In a COLUMN flex container `flex-start` shrink-to-fits every item
   to its own max-content, so the honest-absence sentence ("Not published by this
   program.") refused to wrap and the row spilled 234px out of a 141px cell — while
   the page-level horizontal-scroll assertion stayed green, because the overflow was
   inside a box the page never had to grow for. No page h-scroll is not no overflow. */
/* ⚠ NOT A FLEX COLUMN, and this was decided by LOOKING at a render. Two attempts
   failed first: as a flex ROW the label and value sat side by side and there is no
   room for that at ~150px; as a flex COLUMN every child became its own line, so the
   padlock dropped BELOW the value it belongs to and read as a sixth field rather than
   as that value's own state. It is a block: the label owns line one, and the value
   and its lock share line two because they are inline.
   (The flex column had a second fault worth recording — `flex-wrap: wrap` inherited
   from the row rule means "start a SECOND COLUMN if the items do not fit", so the
   box's max-content width was label + value side by side, 234px against a 141px cell,
   while every child still rendered inside its bounds. No page h-scroll is not no
   overflow.) */
.wl-manifest--card .wlm__f { display: block; min-width: 0; }
/* `block` + `fit-content`: it owns its own line (so the value sits under it) but the
   box — and therefore the dotted "there is an explanation here" rule and the press
   area — is only as wide as the words. A full-width label read as a form field's
   caption with a rule under it. */
.wl-manifest--card .wlm__label { display: block; width: fit-content; max-width: 100%; margin-bottom: 1px; }
.wl-manifest--card .wlm__val {
    display: inline-block;
    max-width: 100%;
    vertical-align: middle;
    overflow-wrap: anywhere;
}
.wl-manifest--card .wlm__lock { vertical-align: middle; margin-left: 4px; }
.wl-manifest--card .wlm__absent { display: block; max-width: 100%; overflow-wrap: anywhere; }
.wl-manifest--card .wlm__label {
    /* A tap target that opens the question. Underlined the way every other help
       affordance on this surface is, so it reads as pressable rather than as a
       label that happens to be focusable. */
    border-bottom: 1px dotted rgba(26, 23, 20, 0.26);
    cursor: help;
}
.wl-manifest--card .wlm__label:focus-visible { outline: 2px solid #a86a1f; outline-offset: 2px; }
.wl-manifest--card .wlm__val { font-size: 12px; }
.wl-manifest--card .wlm__absent { font-size: 9.5px; }
.wl-manifest--card .wlm__sub { display: none; }
.explore-card__win .wl-manifest__note {
    margin: 0 0 8px;
    padding-top: 6px;
}

.explore-card__details-btn {
    display: block;
    width: 100%;
    padding: 10px;
    background: var(--cream);
    border: 1px solid rgba(26, 23, 20, 0.08);
    border-radius: var(--explore-radius);
    color: var(--accent-forest);
    font-size: var(--text-sm);
    font-weight: 600;
    font-family: var(--explore-font-body);
    cursor: pointer;
    transition: all var(--transition-fast);
    text-align: center;
    min-height: 44px; /* V1.1 Fix #8 (WCAG 2.5.5 — was 18px) */
    /* ★ ROUND 8: this is an <a> now (so a phone can open a program in a new tab,
       copy its address, or share it). An anchor brings the sitewide `a` rules with
       it — underline, `a:hover { color: var(--sienna) }` — and the documented trap
       on this repo is that those silently repaint a card control. Both are pinned
       here, across every state, at a specificity the sitewide single-class rules
       cannot beat. */
    text-decoration: none;
    box-sizing: border-box;
    line-height: 24px;
}

.explore-card__details-btn,
.explore-card__details-btn:hover,
.explore-card__details-btn:focus,
.explore-card__details-btn:visited,
.explore-card__details-btn:active { color: var(--accent-forest); text-decoration: none; }

.explore-card__details-btn:hover {
    background: var(--accent-forest-light);
    border-color: var(--accent-forest);
}

/* Card sample note */
.explore-card .sample-row-note {
    display: block;
    margin-top: var(--space-sm);
    text-align: center;
}

/* ============================================================
   10. MOBILE BOTTOM SHEET (detail panel on mobile)
   ============================================================ */

.explore-bottom-sheet {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    /* ★★ THE SITE NAV WAS PAINTING OVER THIS SHEET (measured 2026-09-04, 390px).
       nav-v7 is `position:fixed; z-index:1300; height:56px`, so at 1100 this sheet sat
       UNDER it: `elementFromPoint(195, 25)` returned a <span> inside `<a href="/">`.
       The natural "tap the dimmed area above the sheet to dismiss" gesture therefore
       NAVIGATED TO THE HOMEPAGE and discarded the reader's place in an 885-program list.
       It also clipped 8px off the 44px close button.
       9995 is not an arbitrary number: it is what `.exp-fsheet` — the FILTER sheet in
       this same file — already uses. This sheet was the divergence, not the convention. */
    z-index: 9995;
    max-height: 80vh;
    background: var(--paper);
    border-radius: var(--explore-radius-lg) var(--explore-radius-lg) 0 0;
    box-shadow: 0 -8px 40px rgba(26, 23, 20, 0.15);
    overflow-y: auto;
    overscroll-behavior: contain;   /* a sheet over the page must not scroll the page beneath it */
    -webkit-overflow-scrolling: touch;
    animation: sheetUp 400ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

.explore-bottom-sheet.is-open {
    display: block;
}

@keyframes sheetUp {
    from {
        transform: translateY(100%);
        opacity: 0;
    }
    to {
        transform: translateY(0);
        opacity: 1;
    }
}

.explore-bottom-sheet__handle {
    display: flex;
    justify-content: center;
    padding: 12px 0 8px;
    position: sticky;
    top: 0;
    background: var(--paper);
    z-index: 5;
}

.explore-bottom-sheet__handle::after {
    content: '';
    width: 40px;
    height: 4px;
    border-radius: var(--radius-full);
    background: var(--ink-faint);
}

.explore-bottom-sheet__close {
    position: absolute;
    top: 12px;
    right: 16px;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-full);
    border: none;
    background: var(--cream-dark);
    color: var(--ink-secondary);
    font-size: 18px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.2s ease, color 0.2s ease, transform 0.2s ease;
}

.explore-bottom-sheet__close:hover {
    background: rgba(26, 23, 20, 0.12);
    color: var(--ink);
    transform: scale(1.1);
}

.explore-bottom-sheet__close:focus {
    outline: 2px solid var(--accent-forest);
    outline-offset: 2px;
}

.explore-bottom-sheet__close:active {
    transform: scale(0.95);
}

.explore-bottom-sheet__content {
    padding: var(--space-md) var(--space-lg) var(--space-2xl);
}

/* In mobile bottom sheet, make detail panel single-column */
/* Apr 25 (post-QA Lens 1) — bottom sheet has its own .explore-bottom-sheet__close
   in the upper-right; hide the new Option D close inside the sheet to avoid two
   stacked × buttons that target the same conceptual action. */
.explore-bottom-sheet .explore-detail__close {
    display: none;
}

.explore-bottom-sheet .explore-detail {
    animation: none;
}

.explore-bottom-sheet .detail-header {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-sm);
    padding: var(--space-md);
}

.explore-bottom-sheet .detail-header__meta {
    flex-wrap: wrap;
}

.explore-bottom-sheet .detail-facts {
    flex-wrap: wrap;
}

.explore-bottom-sheet .detail-fact {
    flex: 1 1 40%;
    min-width: 120px;
}

.explore-bottom-sheet .detail-body {
    grid-template-columns: 1fr;
}

.explore-bottom-sheet .detail-body__main {
    border-right: none;
    border-bottom: 1px solid rgba(26, 23, 20, 0.06);
    padding: var(--space-md);
}

.explore-bottom-sheet .detail-intel {
    padding: var(--space-md);
}

.explore-bottom-sheet .intel-section__grid {
    grid-template-columns: 1fr;
}

.explore-bottom-sheet .detail-fullwidth {
    padding: var(--space-md);
}

.explore-sheet-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(26, 23, 20, 0.3);
    /* one below the sheet, and both above the nav — a scrim that the nav paints over is
       not a scrim, it is a decoration with a hole in it. */
    z-index: 9994;
}

.explore-sheet-backdrop.is-open {
    display: block;
}

/* ============================================================
   11. GEO CONTENT SECTION
   ============================================================ */

.explore-geo {
    max-width: 960px;
    margin: var(--space-3xl) auto 0;
    padding: 0 var(--space-xl) var(--space-3xl);
}

.explore-geo h2 {
    font-family: var(--explore-font-display);
    font-size: var(--text-2xl);
    font-weight: 400;
    color: var(--ink);
    margin-bottom: var(--space-xl);
    letter-spacing: -0.01em;
}

.explore-geo__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-xl);
    margin-bottom: var(--space-2xl);
}

.explore-geo__block {
    padding: 0;
}

.explore-geo__block:first-child {
    grid-column: 1 / -1;
}

.explore-geo__block h3 {
    font-family: var(--explore-font-display);
    font-size: var(--text-lg);
    font-weight: 500;
    color: var(--ink);
    margin-bottom: var(--space-sm);
}

.explore-geo__block p {
    font-size: var(--text-base);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    line-height: 1.7;
    margin-bottom: var(--space-sm);
}

/* ★★ ROUND 9 — THREE LINKS HAD NO CSS RULE AT ALL. `IRAP`, `CanExport` and
   `Starter Company Plus` computed to `rgb(0, 0, 238)` with a browser-default
   underline: the User-Agent stylesheet, on an ivory/forest/sienna page where they
   were the only pure-blue elements. Not a wrong colour — an ABSENT rule, which is
   the composition class that passes every correctness check ever written about this
   page. Nothing in `.explore-geo` styled prose anchors; the FAQ block below styles
   `summary` and `details`, and the link row below that has its own class, so an
   inline link inside a `<p>` fell through both.
   Scoped to prose containers ONLY: the documented trap on this repo is a broad
   descendant rule repainting a classless anchor that is really a BUTTON. Everything
   here is a sentence. */
.explore-geo__block p a,
.explore-geo__block dd a,
.explore-geo__faq details p a {
    color: var(--accent-sienna);
    text-decoration: underline;
    text-underline-offset: 2px;
    text-decoration-thickness: 1px;
    text-decoration-color: rgba(196, 83, 43, 0.34);
}
.explore-geo__block p a:hover,
.explore-geo__block dd a:hover,
.explore-geo__faq details p a:hover {
    color: #a3401f;
    text-decoration-color: currentColor;
}
.explore-geo__block p a:focus-visible,
.explore-geo__block dd a:focus-visible,
.explore-geo__faq details p a:focus-visible {
    outline: 2px solid var(--accent-sienna);
    outline-offset: 2px;
    border-radius: 2px;
}

.explore-geo__block dl {
    margin: 0;
}

.explore-geo__block dt {
    font-weight: 700;
    font-family: var(--explore-font-body);
    color: var(--ink);
    font-size: var(--text-sm);
    margin-top: var(--space-md);
}

.explore-geo__block dd {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    line-height: 1.7;
    margin-left: 0;
    padding-left: var(--space-md);
    border-left: 2px solid var(--cream-dark);
    margin-top: 4px;
}

/* FAQ section */
.explore-geo__faq {
    margin-bottom: var(--space-2xl);
}

.explore-geo__faq h3 {
    font-family: var(--explore-font-display);
    font-size: var(--text-lg);
    font-weight: 500;
    margin-bottom: var(--space-md);
}

.explore-geo__faq details {
    border: 1px solid rgba(26, 23, 20, 0.08);
    border-radius: var(--explore-radius);
    margin-bottom: var(--space-sm);
    overflow: hidden;
}

.explore-geo__faq summary {
    padding: var(--space-md) var(--space-lg);
    font-weight: 600;
    font-size: var(--text-base);
    font-family: var(--explore-font-body);
    color: var(--ink);
    cursor: pointer;
    list-style: none;
    display: flex;
    justify-content: space-between;
    align-items: center;
    transition: background var(--transition-fast);
}

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

.explore-geo__faq summary::after {
    content: '+';
    font-size: var(--text-xl);
    font-weight: 300;
    color: var(--ink-faint);
    transition: transform var(--transition-fast);
}

.explore-geo__faq details[open] summary::after {
    content: '\2212';
    color: var(--accent-forest);
}

.explore-geo__faq summary:hover {
    background: var(--cream);
}

.explore-geo__faq details p {
    padding: 0 var(--space-lg) var(--space-lg);
    font-size: var(--text-base);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    line-height: 1.7;
}

/* Internal links nav */
.explore-geo__links {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-xl);
}

.explore-geo__links h3 {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-muted);
    margin-bottom: var(--space-sm);
}

.explore-geo__links ul {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.explore-geo__links a {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--accent-forest);
    text-decoration: none;
    transition: color var(--transition-fast);
}

.explore-geo__links a:hover {
    color: var(--accent-sienna);
    text-decoration: underline;
}

/* ============================================================
   12. UPGRADE POPOVER (shown on blurred cell click)
   ============================================================ */

.explore-popover {
    position: fixed;
    z-index: 200;
    background: var(--paper);
    border: 1px solid rgba(26, 23, 20, 0.08);
    border-radius: var(--explore-radius-lg);
    padding: var(--space-md);
    box-shadow: var(--explore-shadow-lg);
    max-width: 320px;
    animation: popIn 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
    pointer-events: auto;
}

.explore-popover::before {
    content: '';
    position: absolute;
    top: -6px;
    left: 50%;
    transform: translateX(-50%);
    width: 12px;
    height: 12px;
    background: var(--paper);
    border-top: 1px solid rgba(26, 23, 20, 0.08);
    border-left: 1px solid rgba(26, 23, 20, 0.08);
    transform: translateX(-50%) rotate(45deg);
}

@keyframes popIn {
    from { opacity: 0; transform: scale(0.95) translateY(4px); }
    to { opacity: 1; transform: scale(1) translateY(0); }
}

.explore-popover__text {
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    margin-bottom: var(--space-sm);
    line-height: 1.5;
}

.explore-popover__link {
    font-size: var(--text-sm);
    font-weight: 600;
    font-family: var(--explore-font-body);
    color: var(--accent-forest);
    text-decoration: none;
}

.explore-popover__link:hover {
    text-decoration: underline;
    color: var(--accent-forest-deep);
}

/* ============================================================
   13. EMPTY / NO RESULTS STATE
   ============================================================ */

.explore-empty {
    text-align: center;
    padding: var(--space-3xl) var(--space-xl);
    color: var(--ink-muted);
}

.explore-empty__icon {
    font-size: 48px;
    margin-bottom: var(--space-md);
    opacity: 0.5;
}

.explore-empty h3 {
    font-family: var(--explore-font-display);
    font-size: var(--text-xl);
    font-weight: 500;
    color: var(--ink);
    margin-bottom: var(--space-sm);
}

.explore-empty p {
    font-size: var(--text-base);
    font-family: var(--explore-font-body);
    max-width: 400px;
    margin: 0 auto var(--space-lg);
    line-height: 1.6;
}

/* ============================================================
   14. ANIMATIONS
   ============================================================ */

@keyframes exploreSlideUp {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .explore-hero,
    .explore-hero__stats-card,
    .explore-filters,
    .explore-table-wrap,
    .explore-cards {
        animation: none;
    }
}

/* ============================================================
   15. RESPONSIVE BREAKPOINTS
   ============================================================ */

/* ⚠ 2026-09-04, second pass — A DOCUMENT-LEVEL RULE USED TO LIVE HERE AND IT WAS AIMED AT THE
   WRONG CAUSE. I read Khalid's "white space past the limits" as the DOCUMENT rubber-banding and
   suppressed it with `html { overscroll-behavior-y: none }` at >=1024. It is not the document:
   it is the PANE's own overscroll affordance, which `overscroll-behavior: contain` deliberately
   preserves and only `none` removes. The pane rule above is the fix; this one changed page-level
   behaviour for every desktop visitor and fixed nothing, so it is gone. Do not reintroduce it
   without evidence that the DOCUMENT is what is bouncing. */

/* Tablet: 768-1023px — 2-column card grid */
@media (max-width: 1023px) {
    .explore-table-wrap {
        display: none !important;
    }

    .explore-cards {
        /* ★ minmax(0,·) IS LOAD-BEARING, not tidiness. `1fr` is `minmax(auto, 1fr)` and that
           `auto` floors at MIN-CONTENT, so once the cards' own text stops shrinking the first
           track pins at 484.562px and the second absorbs whatever is left: two identical cards
           rendering 485px and 267px, from 800px all the way to 1023px. At 800 the two tracks
           plus the gap total 768.03px inside a 736px grid box, so the grid overflows its own
           container by exactly the 32px that was supposed to be the right gutter — the right
           card runs flush to the screen edge while the left keeps its margin. 810/820/834 are
           the three commonest iPad-portrait widths and all three sit inside the broken band.
           Measured 2026-09-04; with minmax(0,1fr) the same widths give 360/360 and 400/400 with
           the gutter restored, and nothing at or below 768 changes. */
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }

    .explore-hero h1 {
        font-size: 2.4rem;
    }

    /* Round 14: `.explore-filters__dropdowns` is display:none in the base rule now;
       this query only has to put the toggle up. */
    .explore-filters__toggle {
        display: flex;
    }

    .explore-filters__meta {
        margin-left: auto;
    }

    .explore-geo__grid {
        grid-template-columns: 1fr;
    }
}

/* Filters expanded state on mobile */
/* ★ ROUND 14 — `.is-expanded` is RETIRED, not repurposed. It was the mechanism by
   which "Filters" showed the legacy select row; the class is no longer written by
   anything and the rule is left as its own record so a future round does not
   reintroduce the row by re-adding it. The sheet (`.exp-fsheet`) is the control. */

/* Phone: < 768px */
@media (max-width: 767px) {
    .explore-hero {
        padding-top: calc(var(--explore-navbar-height) + 24px);
        padding-left: var(--space-md);
        padding-right: var(--space-md);
        padding-bottom: 60px;
    }

    .explore-hero h1 {
        font-size: 1.8rem;
    }

    .explore-hero__subtitle {
        font-size: var(--text-base);
    }

    .explore-hero__eyebrow {
        font-size: 0.65rem;
        padding: 5px 12px;
        margin-bottom: 16px;
    }

    .explore-hero__stats-card {
        padding: 16px 20px;
        margin-bottom: -28px;
    }

    /* Stat strip wraps on mobile */
    .stat-strip {
        flex-wrap: wrap;
        gap: 12px;
        justify-content: center;
    }

    .stat-strip__item {
        padding: 0 16px;
    }

    .stat-strip__divider {
        display: none;
    }

    .stat-strip__number {
        font-size: 1.3rem;
    }

    .stat-strip__label {
        font-size: 0.62rem;
    }

    .explore-hero__stats {
        gap: 6px;
    }

    .stat-pill {
        font-size: 12px;
        padding: 4px 10px;
    }

    .explore-filters {
        padding: var(--space-sm) var(--space-md);
    }

    .explore-filters__search {
        max-width: none;
    }

    .explore-main {
        padding: var(--space-md);
        padding-top: calc(var(--space-md) + 28px);
    }

    .explore-cards {
        grid-template-columns: 1fr;
    }

    .explore-card {
        padding: var(--space-md);
    }

    .explore-geo {
        padding-left: var(--space-md);
        padding-right: var(--space-md);
    }

    .explore-geo h2 {
        font-size: var(--text-xl);
    }

    .explore-geo__links {
        grid-template-columns: 1fr;
    }

    /* Decorative circles smaller on mobile */
    .explore-deco-circle--one {
        width: 300px;
        height: 300px;
    }

    .explore-deco-circle--two {
        width: 180px;
        height: 180px;
    }

    /* Detail panel responsive — phone */
    .detail-header {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-sm);
        padding: var(--space-md);
    }

    .detail-header__meta {
        flex-wrap: wrap;
        width: 100%;
    }

    .detail-header__meta .btn--sm {
        width: 100%;
        justify-content: center;
    }

    .detail-facts {
        flex-wrap: wrap;
    }

    .detail-fact {
        flex: 1 1 45%;
        min-width: 120px;
        padding: var(--space-sm) var(--space-md);
    }

    .detail-body {
        grid-template-columns: 1fr;
    }

    .detail-body__main {
        border-right: none;
        border-bottom: 1px solid rgba(26, 23, 20, 0.06);
        padding: var(--space-md);
    }

    .detail-intel {
        padding: var(--space-md);
    }

    .intel-section__grid {
        grid-template-columns: 1fr;
    }

    .intel-kv-row {
        flex-direction: column;
        gap: var(--space-sm);
    }

    .detail-fullwidth {
        padding: var(--space-md);
    }
}

/* ============================================================
   16. PRINT STYLES
   ============================================================ */

/* ============================================================
   13. PREMIUM BADGE
   ============================================================ */

#premium-badge {
    text-align: center;
}

#premium-badge:empty {
    display: none;
}

.premium-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    font-weight: 600;
    padding: 6px 14px;
    border-radius: var(--radius-full);
    margin-top: var(--space-sm);
}

.premium-badge--active {
    background: rgba(45, 106, 79, 0.12);
    color: var(--accent-forest);
}

.premium-badge--free {
    background: rgba(45, 106, 79, 0.08);
    color: var(--ink-secondary);
}

.premium-badge--free a {
    color: var(--accent-forest);
    text-decoration: underline;
    text-underline-offset: 2px;
    font-weight: 600;
}

.premium-badge--free a:hover {
    color: var(--ink);
}

/* Badge activation pulse */
.premium-badge--just-activated {
    animation: premiumPulse 1.5s ease-out;
}

@keyframes premiumPulse {
    0%   { transform: scale(1); box-shadow: 0 0 0 0 rgba(45, 106, 79, 0.4); }
    30%  { transform: scale(1.08); box-shadow: 0 0 0 8px rgba(45, 106, 79, 0); }
    60%  { transform: scale(1); box-shadow: 0 0 0 0 rgba(45, 106, 79, 0); }
    80%  { transform: scale(1.03); }
    100% { transform: scale(1); }
}

/* Premium watermark in column headers (after activation) */
.th-premium-watermark {
    display: inline-block;
    margin-left: 4px;
    font-size: 9px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--accent-forest);
    opacity: 0;
    animation: fadeInWatermark 0.6s ease-out 0.3s forwards;
    vertical-align: middle;
}

@keyframes fadeInWatermark {
    from { opacity: 0; transform: translateY(2px); }
    to   { opacity: 0.6; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .premium-badge--just-activated { animation: none; }
    .th-premium-watermark { animation: none; opacity: 0.6; }
}

/* ============================================================
   14. PREMIUM OVERLAY — Pricing Buttons
   ============================================================ */

.premium-overlay__pricing {
    display: flex;
    flex-direction: column;
    gap: 8px;
    width: 100%;
}

.premium-overlay__pricing .btn--upgrade {
    width: 100%;
    font-size: var(--text-sm);
    padding: 10px 16px;
    border-radius: var(--explore-radius);
    font-weight: 600;
    font-family: var(--explore-font-body);
    cursor: pointer;
    transition: all var(--transition-fast);
    border: none;
    text-align: center;
}

.premium-overlay__pricing .btn--accent {
    background: var(--accent-forest);
    color: #fff;
    box-shadow: 0 2px 8px rgba(45, 106, 79, 0.3);
}

.premium-overlay__pricing .btn--accent:hover {
    background: var(--accent-forest-deep);
    box-shadow: 0 4px 12px rgba(45, 106, 79, 0.4);
}

.premium-overlay__pricing .btn--outline {
    background: var(--paper);
    color: var(--accent-forest-deep);
    border: 1.5px solid var(--accent-forest);
    position: relative;
}

.premium-overlay__pricing .btn--outline:hover {
    background: var(--accent-forest-light);
    color: var(--accent-forest-deep);
}

.save-badge {
    display: inline-block;
    font-size: 10px;
    font-weight: 700;
    background: var(--accent-forest-light);
    color: var(--accent-forest);
    padding: 2px 6px;
    border-radius: 4px;
    margin-left: 4px;
    vertical-align: middle;
}

/* ============================================================
   15. AUTH MODAL
   ============================================================ */

.explore-auth-modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 2000;
    align-items: center;
    justify-content: center;
}

.explore-auth-modal.is-open {
    display: flex;
}

.explore-auth-modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(26, 23, 20, 0.5);
    backdrop-filter: blur(4px);
}

.explore-auth-modal__content {
    position: relative;
    background: var(--paper);
    border-radius: var(--explore-radius-lg);
    padding: var(--space-2xl);
    width: 420px;
    max-width: 90vw;
    max-height: 90vh;
    overflow-y: auto;
    overscroll-behavior: contain;   /* a modal must never scroll the page underneath it */
    box-shadow: var(--explore-shadow-lg);
    animation: authFadeIn 300ms ease-out;
}

@keyframes authFadeIn {
    from { opacity: 0; transform: translateY(16px); }
    to { opacity: 1; transform: translateY(0); }
}

.explore-auth-modal__close {
    position: absolute;
    top: 16px;
    right: 16px;
    background: none;
    border: none;
    font-size: 24px;
    color: var(--ink-muted);
    cursor: pointer;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--explore-radius);
    transition: all var(--transition-fast);
}

.explore-auth-modal__close:hover {
    background: rgba(26, 23, 20, 0.06);
    color: var(--ink);
}

.auth-modal__title {
    font-family: var(--explore-font-display);
    font-size: var(--text-xl);
    font-weight: 500;
    color: var(--ink);
    margin-bottom: 4px;
}

.auth-modal__subtitle {
    font-size: var(--text-sm);
    color: var(--ink-muted);
    margin-bottom: var(--space-lg);
    line-height: 1.5;
}

.auth-modal__google-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    padding: 12px;
    border: 1px solid rgba(26, 23, 20, 0.15);
    border-radius: var(--explore-radius);
    background: #fff;
    color: var(--ink);
    font-size: var(--text-sm);
    font-weight: 600;
    font-family: var(--explore-font-body);
    cursor: pointer;
    transition: all var(--transition-fast);
}

.auth-modal__google-btn:hover {
    border-color: rgba(26, 23, 20, 0.3);
    box-shadow: var(--explore-shadow-sm);
}

.auth-modal__divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: var(--space-md) 0;
    color: var(--ink-faint);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.auth-modal__divider::before,
.auth-modal__divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: rgba(26, 23, 20, 0.08);
}

.auth-modal__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
}

.auth-modal__field {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.auth-modal__field label {
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--ink-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.auth-modal__field input {
    padding: 10px 14px;
    border: 1px solid rgba(26, 23, 20, 0.12);
    border-radius: var(--explore-radius);
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink);
    background: #fff;
    transition: border-color var(--transition-fast);
}

.auth-modal__field input:focus {
    outline: none;
    border-color: var(--accent-forest);
    box-shadow: 0 0 0 3px rgba(45, 106, 79, 0.1);
}

/* Forgot-password row (2026-08-30, auth audit) */
.auth-modal__forgot { margin: -4px 0 10px; font-size: 12.5px; text-align: right; }
.auth-modal__forgot a { color: #4F4A40; text-decoration: underline; text-underline-offset: 2px; }
.auth-modal__forgot a:hover { color: #14532D; }

.auth-modal__error {
    font-size: var(--text-sm);
    color: var(--accent-sienna);
    background: rgba(196, 83, 43, 0.06);
    padding: 10px 14px;
    border-radius: var(--explore-radius);
    border-left: 3px solid var(--accent-sienna);
}

.auth-modal__submit-btn {
    padding: 12px;
    background: var(--accent-forest);
    color: #fff;
    border: none;
    border-radius: var(--explore-radius);
    font-size: var(--text-sm);
    font-weight: 600;
    font-family: var(--explore-font-body);
    cursor: pointer;
    transition: background var(--transition-fast);
    margin-top: 4px;
}

.auth-modal__submit-btn:hover {
    background: var(--accent-forest-deep);
}

.auth-modal__switch {
    font-size: var(--text-sm);
    color: var(--ink-muted);
    text-align: center;
    margin-top: var(--space-md);
}

.auth-modal__switch a {
    color: var(--accent-forest);
    font-weight: 600;
    text-decoration: none;
}

.auth-modal__switch a:hover {
    text-decoration: underline;
}

/* ============================================================
   16. TOAST NOTIFICATIONS
   ============================================================ */

.explore-toast-container {
    position: fixed;
    bottom: 24px;
    right: 24px;
    z-index: 3000;
    display: flex;
    flex-direction: column;
    gap: 8px;
    pointer-events: none;
}

.explore-toast {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 18px;
    background: var(--ink);
    color: #fff;
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    border-radius: var(--explore-radius);
    box-shadow: 0 8px 32px rgba(26, 23, 20, 0.2);
    pointer-events: auto;
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 300ms ease, transform 300ms ease;
    max-width: 380px;
}

.explore-toast.is-visible {
    opacity: 1;
    transform: translateY(0);
}

.explore-toast__message {
    flex: 1;
    line-height: 1.4;
}

.explore-toast__close {
    background: none;
    border: none;
    color: rgba(255, 255, 255, 0.6);
    font-size: 18px;
    cursor: pointer;
    padding: 0;
    line-height: 1;
    flex-shrink: 0;
}

.explore-toast__close:hover {
    color: #fff;
}

/* ============================================================
   17. ACCOUNT MENU
   ============================================================ */

.explore-account-menu {
    position: fixed;
    z-index: 1500;
    background: var(--paper);
    border: 1px solid rgba(26, 23, 20, 0.08);
    border-radius: var(--explore-radius-lg);
    box-shadow: var(--explore-shadow-lg);
    width: 260px;
    padding: var(--space-md);
    animation: authFadeIn 200ms ease-out;
}

.explore-account-menu__header {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-bottom: var(--space-sm);
    border-bottom: 1px solid rgba(26, 23, 20, 0.06);
    margin-bottom: var(--space-sm);
}

.explore-account-menu__header strong {
    font-size: var(--text-sm);
    color: var(--ink);
}

.explore-account-menu__header span {
    font-size: var(--text-xs);
    color: var(--ink-muted);
}

.explore-account-menu__plan {
    margin-top: 6px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.explore-account-menu__badge {
    display: inline-block;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    background: var(--accent-forest-light);
    color: var(--accent-forest);
    padding: 2px 8px;
    border-radius: 4px;
    width: fit-content;
}

.explore-account-menu__badge--free {
    background: rgba(26, 23, 20, 0.05);
    color: var(--ink-muted);
}

.explore-account-menu__renewal {
    font-size: 10px;
    color: var(--ink-muted);
}

.explore-account-menu__upgrade {
    display: block;
    width: 100%;
    text-align: center;
    padding: 8px 10px;
    border: none;
    background: var(--accent-forest);
    border-radius: var(--explore-radius);
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    font-weight: 600;
    color: #fff;
    cursor: pointer;
    text-decoration: none;
    transition: all var(--transition-fast);
}

.explore-account-menu__upgrade:hover {
    background: var(--accent-forest-deep);
}

.explore-account-menu__actions {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.explore-account-menu__actions button {
    display: block;
    width: 100%;
    text-align: left;
    padding: 8px 10px;
    border: none;
    background: none;
    border-radius: var(--explore-radius);
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    cursor: pointer;
    transition: all var(--transition-fast);
}

.explore-account-menu__actions button:hover {
    background: rgba(26, 23, 20, 0.04);
    color: var(--ink);
}

/* ============================================================
   18. MOBILE RESPONSIVE — Auth & Premium
   ============================================================ */

@media (max-width: 768px) {
    .explore-auth-modal__content {
        width: 100%;
        max-width: 100%;
        border-radius: var(--explore-radius-lg) var(--explore-radius-lg) 0 0;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        max-height: 85vh;
        animation: sheetUp 400ms cubic-bezier(0.34, 1.56, 0.64, 1);
    }

    .explore-toast-container {
        bottom: 16px;
        right: 16px;
        left: 16px;
    }

    .explore-toast {
        max-width: 100%;
    }

    .explore-account-menu {
        top: auto !important;
        bottom: 0;
        left: 0;
        right: 0;
        width: 100%;
        border-radius: var(--explore-radius-lg) var(--explore-radius-lg) 0 0;
        animation: sheetUp 400ms cubic-bezier(0.34, 1.56, 0.64, 1);
    }

    .premium-overlay__pricing {
        flex-direction: column;
    }
}

/* ============================================================
   19. INLINE PRICING SECTION
   ============================================================ */

.explore-pricing {
    max-width: var(--explore-max-width);
    margin: 0 auto;
    padding: var(--space-3xl) var(--space-xl);
    /* ★ ROUND 12 — a locked Win Layer control scrolls here (`_revealPlans`) and the
       site nav is fixed, so without this the block's heading lands underneath it. */
    scroll-margin-top: 84px;
}

.explore-pricing__inner {
    background: var(--paper);
    border-radius: var(--explore-radius-lg);
    border: 1px solid rgba(26, 23, 20, 0.06);
    box-shadow: var(--explore-shadow-md);
    padding: var(--space-2xl) var(--space-xl);
    text-align: center;
}

.explore-pricing h2 {
    font-family: var(--explore-font-display);
    font-size: clamp(1.5rem, 3vw, 2rem);
    color: var(--ink);
    margin-bottom: var(--space-xs);
    font-weight: 600;
}

.explore-pricing__subtitle {
    font-family: var(--explore-font-body);
    font-size: var(--text-md);
    color: var(--ink-secondary);
    margin-bottom: var(--space-xl);
    max-width: 560px;
    margin-left: auto;
    margin-right: auto;
}

/* ── The reach line + the arrival cue (round 12) ───────────────────────────
   Written by `_revealPlans` when a locked Win Layer control is pressed, so the
   offer names the thing that was actually reached for. ⚠ The `[hidden]` pairing is
   load-bearing: the attribute is silently overridden by ANY rule that sets
   `display`, and an invisible-but-laid-out paragraph here would be a click blocker
   over the pricing cards. */
.explore-pricing__reach {
    max-width: 560px;
    margin: 0 auto var(--space-lg);
    padding: 9px 14px;
    border-radius: 999px;
    background: rgba(31, 92, 61, 0.07);
    border: 1px solid rgba(31, 92, 61, 0.16);
    color: #1f5c3d;
    font: 600 13.5px/1.45 var(--explore-font-body);
}
.explore-pricing__reach[hidden] { display: none; }
/* The cue is a RING, not a movement: the reader has just been scrolled here, and a
   block that also jumps is two motions for one event. Static under reduced motion. */
.explore-pricing__inner.is-reached {
    box-shadow: var(--explore-shadow-md), 0 0 0 3px rgba(31, 92, 61, 0.28);
    transition: box-shadow 240ms ease;
}
@media (prefers-reduced-motion: reduce) {
    .explore-pricing__inner.is-reached { transition: none; }
}

.explore-pricing__cards {
    display: flex;
    gap: var(--space-lg);
    justify-content: center;
    margin-bottom: var(--space-xl);
}

.explore-pricing__card {
    background: var(--cream);
    border: 1px solid rgba(26, 23, 20, 0.08);
    border-radius: var(--explore-radius-lg);
    padding: var(--space-xl) var(--space-lg);
    min-width: 220px;
    position: relative;
    transition: transform 200ms ease, box-shadow 200ms ease;
}

.explore-pricing__card:hover {
    transform: translateY(-2px);
    box-shadow: var(--explore-shadow-lg);
}

.explore-pricing__card--popular {
    border-color: var(--accent-forest);
    border-width: 2px;
    background: var(--paper);
}

.explore-pricing__badge {
    position: absolute;
    top: -12px;
    left: 50%;
    transform: translateX(-50%);
    background: var(--accent-forest);
    color: var(--cream);
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 4px 14px;
    border-radius: 20px;
    white-space: nowrap;
}

.explore-pricing__label {
    font-family: var(--explore-font-body);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-secondary);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin-bottom: var(--space-xs);
}

.explore-pricing__price {
    font-family: var(--explore-font-display);
    font-size: 2.5rem;
    font-weight: 700;
    color: var(--ink);
    line-height: 1.1;
    margin-bottom: 4px;
}

.explore-pricing__price span {
    font-size: 1rem;
    font-weight: 400;
    color: var(--ink-muted);
}

/* ★★ ROUND 9 — ONE CARD LEADS. Both prices used to render at 40px/700, so the
   `MOST POPULAR` badge sat over `$39` while `$24.92` — five glyphs against three,
   and a *smaller* figure — was the loudest thing in the block. A badge that names
   the recommendation and typography that contradicts it is the same fact stated
   twice, in opposite directions. The recommended card keeps 2.5rem; the
   alternative steps down to 1.9rem, which is still unmissable and no longer
   competes. ⚠ Both prices stay on screen together — the binding rule is about the
   PAIR being visible, and demoting one is not hiding it. */
.explore-pricing__card--popular .explore-pricing__price { font-size: 2.75rem; }
.explore-pricing__price--alt { font-size: 1.9rem; color: var(--ink-soft, var(--ink)); }
/* The unit phrase runs longer than "/mo" and must read as a qualifier on the
   number, never as a second line of copy: it wraps under the figure at its own
   size rather than stretching the card. */
.explore-pricing__price--alt span {
    display: block;
    font-size: 0.8rem;
    line-height: 1.3;
    margin-top: 2px;
}

.explore-pricing__note {
    font-size: var(--text-sm);
    color: var(--ink-muted);
    margin-bottom: var(--space-md);
}

.btn--pricing {
    width: 100%;
    padding: 12px 24px;
    font-size: var(--text-sm);
    font-weight: 600;
    border-radius: var(--explore-radius);
    cursor: pointer;
    transition: all var(--transition-fast);
}

.explore-pricing__features {
    list-style: none;
    padding: 0;
    margin: 0 auto var(--space-lg);
    max-width: 420px;
    text-align: left;
}

.explore-pricing__features li {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-family: var(--explore-font-body);
    font-size: var(--text-sm);
    color: var(--ink-secondary);
    padding: 6px 0;
    line-height: 1.5;
}

.explore-pricing__features li svg {
    flex-shrink: 0;
    margin-top: 2px;
}

.explore-pricing__guarantee {
    font-size: var(--text-xs);
    color: var(--ink-muted);
}

.explore-pricing__guarantee a {
    color: var(--accent-forest);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* Hide pricing section for premium users (JS adds class) */
body.is-premium .explore-pricing {
    display: none;
}

/* ============================================================
   20. PREMIUM OVERLAY ENHANCEMENTS
   ============================================================ */

.premium-overlay__teaser {
    font-family: var(--explore-font-data);
    font-size: 12px;
    color: var(--accent-forest);
    background: var(--accent-forest-light);
    padding: 6px 12px;
    border-radius: 6px;
    margin-bottom: var(--space-sm);
    letter-spacing: 0.02em;
}

.premium-overlay__footnote {
    font-size: 11px;
    color: var(--ink-muted);
    margin-top: var(--space-xs);
    margin-bottom: 0;
}

/* ============================================================
   21. POPOVER ENHANCEMENTS (Progressive Upsell)
   ============================================================ */

.explore-popover--strong {
    border-left: 3px solid var(--accent-forest);
    background: var(--paper);
}

.explore-popover__dismiss {
    display: block;
    margin-top: 6px;
    font-size: 11px;
    color: var(--ink-muted);
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.explore-popover__dismiss:hover {
    color: var(--ink-secondary);
}

/* Sample row note link styling */
.sample-row-note a {
    color: var(--accent-forest);
    text-decoration: underline;
    text-underline-offset: 2px;
    font-weight: 600;
}

.sample-row-note svg {
    vertical-align: -1px;
    margin-right: 2px;
    opacity: 0.7;
}

/* ============================================================
   22. MOBILE — Pricing Section
   ============================================================ */

@media (max-width: 768px) {
    .explore-pricing__cards {
        flex-direction: column;
        align-items: center;
    }

    .explore-pricing__card {
        min-width: unset;
        width: 100%;
        max-width: 320px;
    }

    .explore-pricing__card--popular {
        order: -1;
    }

    .explore-pricing__inner {
        padding: var(--space-xl) var(--space-md);
    }
}

/* ── Section 23: Premium Onboarding Modal ──────────────── */

/* ── Onboarding Carousel (3-step premium welcome) ── */

.onboarding {
    text-align: center;
    padding: 8px 0;
}

.onboarding--carousel {
    position: relative;
    overflow: hidden;
}

/* Step indicator dots */
.onboarding__steps {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-bottom: 24px;
}

.onboarding__step-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--ink-faint);
    transition: background 300ms ease, transform 300ms ease;
}

.onboarding__step-dot.active {
    background: var(--accent-forest);
    transform: scale(1.25);
}

/* Slide visibility */
.onboarding__slide {
    display: none;
    animation: onboardingSlideIn 350ms ease;
}

.onboarding__slide.active {
    display: block;
}

@keyframes onboardingSlideIn {
    from { opacity: 0; transform: translateX(20px); }
    to { opacity: 1; transform: translateX(0); }
}

/* Celebration icon */
.onboarding__celebration {
    width: 72px;
    height: 72px;
    border-radius: 50%;
    background: var(--accent-forest-light);
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto 20px;
    animation: onboardingPop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes onboardingPop {
    0% { transform: scale(0); opacity: 0; }
    50% { transform: scale(1.15); }
    100% { transform: scale(1); opacity: 1; }
}

.onboarding__title {
    font-family: var(--explore-font-display);
    font-size: 1.5rem;
    color: var(--ink);
    margin-bottom: 8px;
}

.onboarding__subtitle {
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    font-size: 0.95rem;
    line-height: 1.5;
    margin-bottom: 24px;
}

.onboarding__subtitle strong {
    color: var(--accent-forest);
    font-weight: 600;
}

/* Feature items (Step 2) */
.onboarding__features {
    text-align: left;
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin-bottom: 24px;
}

.onboarding__feature {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding: 14px 16px;
    background: var(--cream);
    border-radius: 10px;
    border-left: 3px solid var(--accent-forest);
}

.onboarding__feature-icon {
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    border-radius: 8px;
    background: var(--accent-forest-light);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 2px;
}

.onboarding__feature div:last-child {
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.onboarding__feature strong {
    font-family: var(--explore-font-body);
    font-size: 0.9rem;
    color: var(--ink);
}

.onboarding__feature span {
    font-size: 0.82rem;
    color: var(--ink-secondary);
    line-height: 1.45;
}

/* Checklist items (Step 3) */
.onboarding__checklist {
    text-align: left;
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 20px;
}

.onboarding__check-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    background: var(--cream);
    border-radius: 8px;
    font-family: var(--explore-font-body);
    font-size: 0.85rem;
    color: var(--ink);
    line-height: 1.4;
    cursor: default;
}

.onboarding__check-box {
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    border-radius: 4px;
    border: 2px solid var(--ink-faint);
    background: var(--paper);
}

.onboarding__tip {
    font-family: var(--explore-font-body);
    font-size: 0.82rem;
    color: var(--ink-muted);
    margin-bottom: 16px;
    font-style: italic;
}

/* Navigation buttons */
.onboarding__nav-row {
    display: flex;
    gap: 12px;
    justify-content: center;
}

.onboarding__next-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 12px 28px;
    background: var(--accent-forest);
    color: #fff;
    border: none;
    border-radius: 8px;
    font-family: var(--explore-font-body);
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 200ms ease, transform 150ms ease;
}

.onboarding__next-btn:hover {
    background: var(--accent-forest-deep);
    transform: translateY(-1px);
}

.onboarding__next-btn--primary {
    padding: 14px 36px;
    font-size: 0.95rem;
}

.onboarding__back-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 12px 24px;
    background: transparent;
    color: var(--ink-muted);
    border: 1px solid var(--ink-faint);
    border-radius: 8px;
    font-family: var(--explore-font-body);
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
    transition: border-color 200ms ease, color 200ms ease;
}

.onboarding__back-btn:hover {
    border-color: var(--ink-secondary);
    color: var(--ink);
}

@media (prefers-reduced-motion: reduce) {
    .onboarding__celebration {
        animation: none;
    }
    .onboarding__slide {
        animation: none;
    }
}

/* ── Getting Started Checklist Banner ── */

#checklist-banner {
    max-width: var(--explore-max-width);
    margin: 0 auto;
    padding: 0 24px;
}

.checklist-banner {
    background: var(--paper);
    border: 1px solid var(--cream-dark);
    border-radius: var(--explore-radius);
    margin-bottom: 16px;
    box-shadow: var(--explore-shadow-sm);
    overflow: hidden;
}

.checklist-banner--complete {
    border-color: var(--accent-forest);
    background: linear-gradient(135deg, rgba(45, 106, 79, 0.03), var(--paper));
}

.checklist-banner__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    border-bottom: 1px solid var(--cream-dark);
}

.checklist-banner__title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--explore-font-body);
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--accent-forest);
}

.checklist-banner__progress {
    font-family: var(--explore-font-data);
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--ink-muted);
    background: var(--cream);
    padding: 2px 8px;
    border-radius: 10px;
}

.checklist-banner__toggle {
    background: none;
    border: none;
    padding: 4px;
    cursor: pointer;
    color: var(--ink-muted);
    transition: transform 200ms ease;
}

.checklist-banner__toggle.is-collapsed {
    transform: rotate(-90deg);
}

.checklist-banner__body {
    padding: 8px 16px 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.checklist-banner__item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 6px;
    font-family: var(--explore-font-body);
    font-size: 0.82rem;
    color: var(--ink);
    line-height: 1.4;
    transition: background 200ms ease;
    cursor: default;
}

.checklist-banner__item:hover {
    background: var(--cream);
}

.checklist-banner__item.is-done {
    color: var(--ink-muted);
    text-decoration: line-through;
    text-decoration-color: var(--ink-faint);
}

.checklist-banner__check {
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    border-radius: 4px;
    border: 2px solid var(--ink-faint);
    background: var(--paper);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 200ms ease;
}

.checklist-banner__check.is-checked {
    background: var(--accent-forest);
    border-color: var(--accent-forest);
}

@media (max-width: 768px) {
    #checklist-banner {
        padding: 0 12px;
    }
    .checklist-banner__body {
        padding: 6px 12px 10px;
    }
    .checklist-banner__item {
        font-size: 0.78rem;
        padding: 6px 8px;
    }
}

/* ─── Inline Education Card (Premium) ─────────────────── */

.intel-education {
    margin: 8px 0 4px;
    border: 1px dashed var(--cream-dark, #d5cec0);
    border-radius: 6px;
    font-size: 0.82rem;
    background: rgba(45, 106, 79, 0.02);
}

.intel-education__summary {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 8px 12px;
    cursor: pointer;
    color: var(--accent-forest, #2d6a4f);
    font-weight: 500;
    font-size: 0.78rem;
    letter-spacing: 0.01em;
    list-style: none;
}

.intel-education__summary::-webkit-details-marker {
    display: none;
}

.intel-education__summary::after {
    content: '';
    margin-left: auto;
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid currentColor;
    transition: transform 0.2s ease;
}

.intel-education[open] .intel-education__summary::after {
    transform: rotate(180deg);
}

.intel-education__body {
    padding: 0 12px 10px;
    color: var(--ink-secondary, #5a544a);
    line-height: 1.5;
}

.intel-education__body ul {
    margin: 0;
    padding: 0 0 0 16px;
    list-style: disc;
}

.intel-education__body li {
    margin-bottom: 4px;
}

.intel-education__tip {
    margin: 8px 0 0;
    padding: 6px 10px;
    background: rgba(45, 106, 79, 0.06);
    border-radius: 4px;
    font-weight: 600;
    color: var(--accent-forest, #2d6a4f);
    font-size: 0.8rem;
}

/* ─── Field Tooltips (Premium Education) ──────────────── */

.field-tooltip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    margin-left: 4px;
    color: var(--accent-forest, #2d6a4f);
    opacity: 0.6;
    cursor: pointer;
    vertical-align: middle;
    transition: opacity 0.2s ease, transform 0.2s ease;
    border-radius: 50%;
}

.field-tooltip:hover {
    opacity: 1;
    transform: scale(1.2);
    background: rgba(45, 106, 79, 0.08);
}

.field-tooltip svg {
    display: block;
}

/* ============================================================
   BOOKMARK BUTTON (Detail Panel)
   ============================================================ */

.btn--bookmark {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 6px 14px;
    border: 1.5px solid var(--ink-faint);
    border-radius: var(--radius-full, 100px);
    background: transparent;
    color: var(--ink-secondary);
    font-family: var(--explore-font-body);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
    white-space: nowrap;
}

.btn--bookmark:hover {
    border-color: var(--accent-forest);
    color: var(--accent-forest);
    background: var(--accent-forest-light);
}

.btn--bookmark.is-active {
    border-color: var(--accent-forest);
    background: var(--accent-forest);
    color: #fff;
}

.btn--bookmark.is-active:hover {
    background: var(--accent-forest-deep);
    border-color: var(--accent-forest-deep);
}

.btn--bookmark svg {
    flex-shrink: 0;
}

/* Bookmark loading state */
.btn--bookmark.is-loading {
    opacity: 0.6;
    pointer-events: none;
    position: relative;
}

.btn--bookmark.is-loading svg {
    animation: bookmarkPulse 0.8s ease-in-out infinite;
}

@keyframes bookmarkPulse {
    0%, 100% { opacity: 0.4; }
    50% { opacity: 1; }
}

/* Dashboard link in detail panel */
.detail-dashboard-link {
    font-family: var(--explore-font-body);
    font-size: 12px;
    font-weight: 600;
    color: var(--accent-forest);
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.2s ease;
}

.detail-dashboard-link:hover {
    color: var(--accent-forest-deep);
    text-decoration: underline;
}

/* Grant page link in detail panel */
.detail-grantpage-link {
    font-family: var(--explore-font-body);
    font-size: 12px;
    font-weight: 600;
    color: var(--accent-forest);
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.2s ease;
}
.detail-grantpage-link:hover {
    color: var(--accent-forest-deep);
    text-decoration: underline;
}

/* ── Pursue this grant (Dashboard V1 Phase 4) ─────────────── */
/* Scoped to explore detail panel. Sits alongside Save / Hide / Open. */
.exp-pursue-wrap {
    display: inline-flex;
    align-items: center;
}
.exp-pursue-wrap:empty {
    display: none;
}
/* Apr 25 2026 (post-QA strategic decision D) — was outlined; filled
   forest green for visual continuity with the post-pursue Tracking
   pill and to give the primary toolbar slot the confidence it deserves
   on a high-intent surface. */
.exp-pursue-wrap .pursue-btn.pursue-btn--compact {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 7px 14px;
    background: var(--accent-forest, #2D6A4F);
    color: #FFFDFA;
    border: 1.5px solid var(--accent-forest, #2D6A4F);
    border-radius: 6px;
    font-family: var(--explore-font-body);
    font-weight: 600;
    font-size: 12.5px;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
    box-shadow: 0 1px 3px rgba(31, 74, 55, 0.15);
}
.exp-pursue-wrap .pursue-btn.pursue-btn--compact:hover {
    background: var(--accent-forest-deep, #1F4A37);
    transform: translateY(-1px);
    box-shadow: 0 3px 8px rgba(31, 74, 55, 0.22);
}
.exp-pursue-wrap .pursue-btn.pursue-btn--compact:focus-visible {
    outline: 2px solid var(--accent-forest-deep, #1F4A37);
    outline-offset: 2px;
}
.exp-pursue-wrap .pursue-status {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 5px 10px;
    background: #E8EFEA;
    border: 1px solid #C8D9CE;
    border-radius: 6px;
    font-size: 12px;
    line-height: 1.2;
    white-space: nowrap;
}
.exp-pursue-wrap .pursue-status__label {
    font-family: 'IBM Plex Mono', monospace;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--accent-forest-deep, #1F4A37);
}
.exp-pursue-wrap .pursue-status__link {
    color: var(--accent-forest, #2D6A4F);
    text-decoration: none;
    font-weight: 600;
    font-size: 11px;
}
.exp-pursue-wrap .pursue-status__link:hover {
    text-decoration: underline;
}

/* ============================================================
   DASHBOARD LINK IN ACCOUNT MENU
   ============================================================ */

.explore-account-menu__dashboard {
    display: block;
    width: 100%;
    text-align: left;
    padding: 8px 10px;
    border: none;
    background: none;
    border-radius: var(--explore-radius);
    font-size: var(--text-sm);
    font-family: var(--explore-font-body);
    color: var(--accent-forest);
    font-weight: 600;
    cursor: pointer;
    transition: all var(--transition-fast);
    text-decoration: none;
}

.explore-account-menu__dashboard:hover {
    background: var(--accent-forest-light);
    color: var(--accent-forest-deep);
}

/* ============================================================
   COLUMN HEADER TOOLTIPS & INFO ICONS
   ============================================================ */

.th-info-icon {
    display: inline-flex;
    align-items: center;
    margin-left: 3px;
    color: var(--ink-faint);
    opacity: 0.6;
    vertical-align: middle;
    transition: opacity 0.2s ease, color 0.2s ease;
}

.explore-table th:hover .th-info-icon {
    opacity: 1;
    color: var(--ink-muted);
}

.explore-table th.is-sorted .th-info-icon {
    color: var(--accent-sienna);
    opacity: 0.7;
}

.th-info-icon svg {
    display: block;
}

/* ============================================================
   PAYMENT WARNING BANNER
   ============================================================ */

/* 2026-09-27: Premium paused for an unpaid bill — stronger than the grace-period notice; not dismissible. */
.explore-payment-warning.explore-payment-warning--paused { background: #FDECEC; border-color: #F5C2C2; color: #7A1C1C; }
.explore-payment-warning.explore-payment-warning--paused svg,
.explore-payment-warning.explore-payment-warning--paused a { color: #7A1C1C; }

.explore-payment-warning {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 20px;
    background: #FFF3CD;
    border: 1px solid #FFECB5;
    border-radius: 8px;
    margin: 12px 20px;
    font-family: var(--explore-font-body);
    font-size: 14px;
    color: #664D03;
    line-height: 1.5;
    position: relative;
    z-index: 2;
}

.explore-payment-warning svg {
    flex-shrink: 0;
    color: #856404;
}

.explore-payment-warning a {
    color: #664D03;
    font-weight: 600;
    text-decoration: underline;
}

.explore-payment-warning a:hover {
    color: #533B03;
}

.explore-payment-warning__close {
    background: none;
    border: none;
    font-size: 18px;
    color: #664D03;
    cursor: pointer;
    margin-left: auto;
    padding: 4px;
    opacity: 0.7;
    line-height: 1;
    flex-shrink: 0;
}

.explore-payment-warning__close:hover {
    opacity: 1;
}

/* Account menu: cancelled badge */
.explore-account-menu__cancelled {
    font-size: 0.75rem;
    color: #856404;
    font-weight: 500;
}

/* Account menu: past_due warning text */
.explore-account-menu__past-due {
    display: block;
    font-size: 0.72rem;
    color: #856404;
    font-weight: 500;
    margin-top: 2px;
}

/* Account menu: warning border accent for past_due */
.explore-account-menu__plan--warn {
    border-left: 3px solid #FFCA2C;
    padding-left: 10px;
}

@media (max-width: 768px) {
    .explore-payment-warning {
        margin: 8px 12px;
        padding: 10px 14px;
        font-size: 13px;
        gap: 8px;
    }
}

@media print {
    .explore-filters,
    .explore-more,
    .explore-hero__premium-cue,
    .premium-overlay,
    .explore-geo,
    .explore-pricing,
    .navbar,
    .footer,
    .explore-deco-circle,
    .explore-hero__grain,
    .explore-auth-modal,
    .explore-toast-container,
    .explore-account-menu,
    .premium-badge {
        display: none !important;
    }

    .explore-table__cell--premium.is-locked .cell-content {
        filter: none;
    }

    .explore-hero {
        padding-top: 0;
        background: #fff;
        color: #000;
    }

    .explore-hero h1 {
        color: #000;
    }

    body:has(.explore-hero) {
        background: #fff;
    }

    body:has(.explore-hero)::after {
        display: none;
    }
}

/* ─── Related Grants Section (deep link ?grant=) ─────────────────────────── */

#explore-related {
    margin-top: 48px;
    padding: 32px 0 0;
    border-top: 1px solid rgba(26, 23, 20, 0.08);
}

.explore-related__header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 20px;
    padding: 0 4px;
}

.explore-related__title {
    font-family: var(--explore-font-display);
    font-size: 18px;
    font-weight: 600;
    color: var(--ink);
    margin: 0;
}

.explore-related__browse {
    font-family: var(--explore-font-body);
    font-size: 13px;
    font-weight: 500;
    color: var(--accent-green);
    text-decoration: none;
    transition: opacity 0.15s ease;
}

.explore-related__browse:hover {
    opacity: 0.75;
}

.explore-related__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
}

.explore-related-card {
    background: var(--paper);
    border: 1px solid rgba(26, 23, 20, 0.06);
    border-radius: var(--explore-radius-lg);
    padding: 16px 18px;
    cursor: pointer;
    transition: box-shadow 0.15s ease, border-color 0.15s ease;
}

.explore-related-card:hover {
    border-color: rgba(26, 23, 20, 0.14);
    box-shadow: var(--explore-shadow-md);
}

.explore-related-card__top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 6px;
}

.explore-related-card__title {
    font-family: var(--explore-font-display);
    font-size: 14px;
    font-weight: 500;
    color: var(--ink);
    line-height: 1.3;
    margin-bottom: 2px;
}

.explore-related-card__org {
    font-family: var(--explore-font-body);
    font-size: 12px;
    color: var(--ink-muted);
    margin-bottom: 10px;
}

.explore-related-card__bottom {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.explore-related-card__amount {
    font-family: var(--explore-font-data);
    font-size: 13px;
    font-weight: 600;
    color: var(--ink);
}

.explore-related-card__deadline {
    font-family: var(--explore-font-body);
    font-size: 11px;
    color: var(--ink-muted);
}

.explore-related-card__deadline--urgent {
    color: #dc2626;
    font-weight: 600;
}

.explore-related-card__deadline--soon {
    color: #d97706;
    font-weight: 500;
}

/* ★★ The deep-link arrival's action row (2026-09-01).
   Sits under the Win Layer and above the description in the phone bottom sheet, and ONLY
   there — `_panelTopActions` renders it for `?grant=` landings, so no other open on any
   surface gains a control.
   ⚠ ONE ROW AT 375. The three controls share the line with `flex: 1 1 auto` and
   `min-width: 0` so the row can never propagate an intrinsic width up and put the sheet
   into horizontal scroll (the documented nowrap-row class). `min-height: 44px` is the
   thumb floor and is asserted per control at 390 and 375, not assumed from this rule. */
.exp-topact {
    display: flex;
    align-items: stretch;
    gap: 8px;
    margin: 14px 0 4px;
}

.exp-topact > .eda-btn {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 44px;
    /* The labels are short and fixed ("Start application", "☆ Save",
       "Start Premium · $39/mo"), so they are allowed to wrap rather than ellipsise: a
       truncated price is a worse statement than a two-line button. */
    white-space: normal;
    text-align: center;
    padding-left: 10px;
    padding-right: 10px;
}

/* Save is the narrowest of the three and must not take space from the other two. */
.exp-topact > .eda-btn--ghost { flex: 0 1 auto; }

/* The Premium entry wears the overlay's accent, not a fourth button style. */
.exp-topact > .eda-btn--pro {
    background: var(--sienna, #b0552e);
    border: 1px solid var(--sienna, #b0552e);
    color: #fff;
    font-weight: 600;
}

.exp-topact > .eda-btn--pro:hover,
.exp-topact > .eda-btn--pro:focus-visible {
    background: #90441f;
    border-color: #90441f;
    color: #fff;
}

/* ★★ The deep link's other picks, inside the drawer header (2026-09-01, round 3).
   The banner copy is unreachable at >=1024 because the auto-opened drawer is a modal with a
   scrim over the page; this copy lives INSIDE that modal, so nothing the drawer paints can
   cover it. Quiet by design: it is navigation between the reader's own matches, not an offer,
   and the record already carries exactly one offer. */
.detail-header__also {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    margin-top: 6px;
    padding: 4px 10px;
    border: 1px solid rgba(22, 101, 52, 0.35);
    border-radius: 999px;
    background: #f0fdf4;
    color: #166534;
    font-family: var(--explore-font-body);
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    white-space: nowrap;
}

.detail-header__also:hover,
.detail-header__also:focus-visible {
    background: #166534;
    border-color: #166534;
    color: #f0fdf4;
}

/* Deep link banner */
/* ⚠ `flex-wrap: wrap` is load-bearing since the "N more of your matches" control
   joined this row (2026-09-01). A nowrap flex row holding a sentence, a
   "Browse all 886 programs" link AND a pill button propagates its intrinsic width
   upward and puts the PAGE into horizontal scroll at 320-390 — the documented
   nowrap-row-widens-its-ancestors class. Asserted at all nine widths. */
.explore-deeplink-banner {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    padding: 10px 16px;
    margin-bottom: 16px;
    background: #f0fdf4;
    border: 1px solid #bbf7d0;
    border-radius: var(--explore-radius);
    font-family: var(--explore-font-body);
    font-size: 13px;
    color: #166534;
}

.explore-deeplink-banner a {
    color: #166534;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* ★ The email promised three programmes; this control is how the page keeps that
   promise. It is the ACTION in this row, so it reads as a control rather than as a
   third piece of prose — and it is sized for a thumb (the surface it matters on is a
   phone arriving from an inbox), which the 44px min-height enforces at every width.
   Specificity note: `.explore-deeplink-banner a` above styles the sibling link only;
   this is a <button>, so nothing in that rule reaches it. */
.explore-deeplink-also {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 6px 14px;
    border: 1px solid #166534;
    border-radius: 999px;
    background: #166534;
    color: #f0fdf4;
    font-family: var(--explore-font-body);
    font-size: 13px;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    white-space: nowrap;
}

.explore-deeplink-also:hover,
.explore-deeplink-also:focus-visible {
    background: #14532d;
    border-color: #14532d;
    color: #f0fdf4;
}

@media (max-width: 560px) {
    /* Below ~560 the sentence takes the whole line anyway, so the control stops
       hanging off the right edge and becomes a full-width row of its own — a
       nowrap pill pushed right on a 320px line is exactly what forces the
       horizontal scroll the wrap above exists to prevent. */
    .explore-deeplink-also {
        margin-left: 0;
        width: 100%;
    }
}

@media (max-width: 768px) {
    .explore-related__grid {
        grid-template-columns: 1fr;
    }

    .explore-related__header {
        flex-direction: column;
        gap: 6px;
    }
}

/* ─── Checkout Loading Overlay ─────────────────────────── */

.checkout-loading-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(255, 255, 255, 0.95);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 99999;
    animation: checkoutFadeIn 0.2s ease;
}

.checkout-loading-content {
    text-align: center;
    max-width: 360px;
}

.checkout-loading-spinner {
    width: 40px;
    height: 40px;
    border: 3px solid #e5e7eb;
    border-top-color: #2d6a4f;
    border-radius: 50%;
    animation: checkoutSpin 0.8s linear infinite;
    margin: 0 auto 16px;
}

.checkout-loading-content p {
    font-size: 1.1rem;
    color: #1a1a1a;
    margin: 0 0 4px;
}

.checkout-loading-sub {
    font-size: 0.85rem !important;
    color: #6b7280 !important;
}

@keyframes checkoutSpin {
    to { transform: rotate(360deg); }
}

@keyframes checkoutFadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* ─── Quiz Filter Banner ─────────────────────────────── */

/* ★★ ROUND 17 (2026-08-07) — THIS WAS A SUCCESS ALERT ON A PAGE THAT HAS NO ALERTS.
   Khalid, on the shipped build: "this feels like a slightly different visual language,
   doesn't it?" It did, and the cause was literal: SIX hardcoded Tailwind greens (#f0fdf4,
   #bbf7d0, #166534, #16a34a, #15803d, #86efac) and not one page token, dropped into a
   cream/ink/Fraunces surface. Same family as the durable gotcha about a component that
   hardcodes a brand constant and therefore cannot respect a page-scoped one.

   ── But the colour was the symptom. Three composition faults sat under it ──────────────
   · A GREEN CHECK ASSERTS SUCCESS, and nothing succeeded. The visitor did not complete an
     action here; they arrived and found the list already narrowed. A check mark answers a
     question nobody asked and mis-frames the one they did: "why is this list short?"
   · IT WAS THE LOUDEST THING ON THE PAGE. Saturated fill, full-bleed border and a bold
     coloured value list, sitting directly above a deliberately quiet grid whose own band
     label is muted letterspaced caps. The explanation was shouting over the content it
     was explaining.
   · IT SHIPPED A SECOND CLEAR CONTROL. The toolbar already has Reset and the rail already
     carries a × per facet; a third, drawn as a bordered pill, made the escape the most
     button-like object on the row.

   ── What it is now, and why it still exists at all ────────────────────────────────────
   Provenance is the ONE thing no other surface on this page provides. The toolbar states
   the count, the rail states the values — but nothing says WHY a list the visitor did not
   filter is filtered, and an unexplained count reads as "this catalogue is small". So the
   element earns its place; it just had the wrong job description.
   ⚠ IT KEEPS THE VALUES, and that is not redundancy with the rail: `.explore-rail` is
   `display: none` below the rail breakpoint, so on a phone this line is the ONLY statement
   of what is filtering the list. Removing them to dedupe against the rail would have blanked
   the explanation on the surface that needs it most.
   Now: a parchment line in the page's own ink, no icon, values in normal weight, and the
   escape as a text link that NAMES ITS DESTINATION ("Show all 743 programs") instead of a
   pill that competes with Reset. */
.explore-quiz-banner {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    padding: 9px 14px;
    background: var(--cream-dark, #f2ebe0);
    border: 1px solid var(--ink-faint, #c7bfb4);
    border-radius: var(--explore-radius, 8px);
    margin-bottom: 12px;
    font-family: var(--explore-font-body, inherit);
    font-size: 0.82rem;
    line-height: 1.45;
    color: var(--ink-secondary, #5c5549);
    animation: quizBannerFadeIn 0.3s ease;
}

.explore-quiz-banner__content {
    display: block;          /* a sentence, not an icon row */
    min-width: 0;
}

/* The values are the OBJECT of the sentence, not a badge. Ink, not colour; medium, not
   bold — enough to separate them from the clause without out-shouting the grid. */
.explore-quiz-banner__detail {
    color: var(--ink, #1a1714);
    font-weight: 500;
}

/* ⚠ A TEXT LINK, DELIBERATELY. Drawn as a button it became the third clear control on the
   screen and the loudest; the 44px touch target is met with padding rather than a border so
   it stays quiet on desktop and still pressable on a phone. */
.explore-quiz-banner__clear {
    background: none;
    border: 0;
    color: var(--ink-secondary, #5c5549);
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-color: var(--ink-faint, #c7bfb4);
    padding: 6px 2px;
    min-height: 32px;
    font-family: var(--explore-font-body, inherit);
    font-size: 0.8rem;
    cursor: pointer;
    white-space: nowrap;
    flex-shrink: 0;
    transition: color .15s ease, text-decoration-color .15s ease;
}

.explore-quiz-banner__clear:hover,
.explore-quiz-banner__clear:focus-visible {
    color: var(--ink, #1a1714);
    text-decoration-color: var(--ink, #1a1714);
}

@keyframes quizBannerFadeIn {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: translateY(0); }
}

@media (max-width: 640px) {
    .explore-quiz-banner {
        flex-direction: column;
        align-items: flex-start;
        gap: 4px;
    }

    /* ★ ROUND 17 — 44px TOUCH FLOOR, and it is load-bearing HERE specifically: below the rail
       breakpoint `.explore-rail` is `display: none`, so this line is the only statement of
       what is filtering the list AND its only escape. At 32px (the desktop height, which is
       fine for a pointer) it was the one control a phone reader needs most and can hit least.
       Grown INWARD with padding, never with negative margins — the row above it is a grid. */
    .explore-quiz-banner__clear {
        align-self: flex-start;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        padding: 10px 2px;
    }
}

/* ============================================================
   TOP QUIZ MATCH PREVIEW — #1 match gets premium data free
   ============================================================ */

/* Row highlight (desktop table) */
.explore-table__row.is-top-match {
    background: linear-gradient(135deg, rgba(251, 191, 36, 0.06), rgba(245, 158, 11, 0.04));
    border-left: 3px solid #f59e0b;
}

.explore-table__row.is-top-match:hover {
    background: linear-gradient(135deg, rgba(251, 191, 36, 0.1), rgba(245, 158, 11, 0.07));
}

/* Card highlight (mobile) */
.explore-card.is-top-match {
    border-color: #fbbf24;
    box-shadow: 0 0 0 1px rgba(251, 191, 36, 0.3), 0 4px 16px rgba(245, 158, 11, 0.1);
    background: linear-gradient(135deg, rgba(254, 243, 199, 0.25), rgba(254, 249, 195, 0.15));
}

/* #1 Match badge (inline in program name cell or card header) */
.top-match-badge {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 7px;
    background: linear-gradient(135deg, #fbbf24, #f59e0b);
    color: #78350f;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.02em;
    border-radius: 4px;
    margin-left: 6px;
    vertical-align: middle;
    line-height: 1.4;
    white-space: nowrap;
}

.top-match-badge svg {
    flex-shrink: 0;
}

/* Row annotation (below the table row, like sample-row-note) */
.top-match-note-tr td {
    padding: 0 16px 8px;
    border-bottom: 2px dashed rgba(251, 191, 36, 0.25);
    background: rgba(251, 191, 36, 0.04);
}

.top-match-note {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: #92400e;
    font-weight: 500;
    font-style: italic;
    line-height: 1.5;
}

.top-match-note svg {
    flex-shrink: 0;
    color: #f59e0b;
}

.top-match-note a {
    color: #92400e;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* Detail panel with top-match treatment */
.explore-detail.is-top-match-detail {
    border: 1px solid rgba(251, 191, 36, 0.3);
    border-radius: 8px;
    background: linear-gradient(180deg, rgba(254, 243, 199, 0.08) 0%, transparent 40%);
}

/* Preview banner inside the premium intel sidebar */
.top-match-preview-banner {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    background: linear-gradient(135deg, #fef3c7, #fef9c3);
    border: 1px solid #fde68a;
    border-radius: 8px;
    font-size: 0.8rem;
    color: #92400e;
    margin-bottom: 12px;
    line-height: 1.4;
}

.top-match-preview-banner svg {
    flex-shrink: 0;
    color: #f59e0b;
}

.top-match-preview-banner a {
    color: #92400e;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.top-match-preview-banner a:hover {
    color: #78350f;
}

/* Mobile adjustments */
@media (max-width: 767px) {
    .top-match-note-tr td {
        padding: 6px 12px 10px;
        text-align: center;
    }

    .top-match-note {
        justify-content: center;
        text-align: center;
        font-size: 10.5px;
        gap: 5px;
    }

    .explore-card .top-match-note {
        display: block;
        margin-top: var(--space-sm);
        text-align: center;
        font-size: 10.5px;
        padding: 6px 0 0;
        line-height: 1.5;
    }

    .top-match-badge {
        font-size: 9px;
        padding: 2px 5px;
        margin-left: 4px;
    }

    .top-match-preview-banner {
        font-size: 0.75rem;
        padding: 6px 10px;
        gap: 6px;
    }
}

/* ── Top Match Preview Section (limited premium preview) ── */
.top-match-preview-section {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.preview-field {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 8px 12px;
    background: rgba(254, 243, 199, 0.15);
    border-radius: 6px;
    border-left: 3px solid #fbbf24;
}

.preview-label {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #92400e;
}

.preview-value {
    font-size: 0.85rem;
    color: #374151;
    line-height: 1.45;
}

.preview-upgrade-cta {
    margin-top: 6px;
    padding: 10px 14px;
    background: linear-gradient(135deg, #f0fdf4, #ecfdf5);
    border: 1px solid #bbf7d0;
    border-radius: 8px;
    text-align: center;
}

.preview-upgrade-cta a {
    color: #166534;
    font-size: 0.82rem;
    font-weight: 600;
    text-decoration: none;
}

.preview-upgrade-cta a:hover {
    text-decoration: underline;
    color: #15803d;
}

@media (max-width: 767px) {
    .preview-field {
        padding: 6px 10px;
    }
    .preview-value {
        font-size: 0.8rem;
    }
    .preview-upgrade-cta {
        padding: 8px 10px;
    }
    .preview-upgrade-cta a {
        font-size: 0.78rem;
    }
}

/* ============================================================
   SEARCH HIGHLIGHTING
   ============================================================ */

mark {
    background-color: #fef3c7;
    padding: 1px 3px;
    border-radius: 2px;
    color: inherit;
}

/* ============================================================
   RICH EMPTY STATE
   ============================================================ */

/* ★ ROUND 9 — this max-width was the SECOND half of the collapsed-empty-state
   defect. The frame is `fit-content` at >=1024px, so a 560px content box pulled the
   whole 1138px region down to 562px and the empty state rendered as a narrow column
   with half the canvas blank beside it. `.explore-grid.is-empty` restores the
   frame's width; this restores the CONTENT's presence inside it. 680px keeps the
   prose at a readable measure (a 1138px line is worse than a short one) while the
   recovery rows — program · amount · View, which are tabular, not prose — get room
   to sit as rows rather than as wrapped stacks. */
.explore-empty--rich {
    text-align: left;
    max-width: 680px;
    margin: 0 auto;
    padding: var(--space-3xl) var(--space-xl);
}

.explore-empty--rich .explore-empty__icon {
    text-align: center;
    margin-bottom: var(--space-md);
    opacity: 0.6;
}

.explore-empty--rich h3 {
    text-align: center;
    font-family: var(--explore-font-display);
    font-size: var(--text-xl);
    font-weight: 500;
    color: var(--ink);
    margin-bottom: var(--space-sm);
}

.explore-empty__search-echo {
    text-align: center;
    font-size: var(--text-base);
    color: var(--ink-secondary);
    margin-bottom: var(--space-lg);
    font-family: var(--explore-font-body);
}

.explore-empty__active-filters {
    margin-bottom: var(--space-lg);
}

.explore-empty__filters-label,
.explore-empty__suggestions-label,
.explore-empty__browse-label {
    display: block;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-muted);
    margin-bottom: var(--space-sm);
    font-family: var(--explore-font-body);
}

.explore-empty__filter-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.explore-empty__filter-tag {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    font-size: 0.82rem;
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    background: var(--cream);
    border: 1px solid rgba(26, 23, 20, 0.1);
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
}

.explore-empty__filter-tag:hover {
    background: var(--cream-dark);
    border-color: rgba(26, 23, 20, 0.18);
}

.explore-empty__filter-tag svg {
    opacity: 0.5;
    flex-shrink: 0;
}

.explore-empty__filter-tag:hover svg {
    opacity: 0.8;
}

.explore-empty__filter-tag--search {
    font-style: italic;
}

.explore-empty__suggestions {
    margin-bottom: var(--space-lg);
}

.explore-empty__suggestion-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.explore-empty__suggestion-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    text-align: left;
    padding: 10px 14px;
    font-size: 0.85rem;
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    background: var(--paper);
    border: 1px solid rgba(26, 23, 20, 0.06);
    border-radius: 8px;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.explore-empty__suggestion-btn:hover {
    background: var(--accent-forest-light);
    border-color: rgba(45, 106, 79, 0.2);
    color: var(--accent-forest);
}

.explore-empty__suggestion-count {
    margin-left: auto;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--accent-forest);
    background: rgba(45, 106, 79, 0.08);
    padding: 2px 8px;
    border-radius: 4px;
    white-space: nowrap;
}

.explore-empty__browse {
    margin-bottom: var(--space-xl);
}

.explore-empty__browse-links {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.explore-empty__browse-link {
    padding: 6px 12px;
    font-size: 0.82rem;
    font-family: var(--explore-font-body);
    font-weight: 500;
    color: var(--accent-forest);
    background: var(--accent-forest-light);
    border: 1px solid rgba(45, 106, 79, 0.12);
    border-radius: 20px;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.explore-empty__browse-link:hover {
    background: var(--accent-forest);
    color: #fff;
}

.explore-empty__reset {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: var(--space-lg) auto 0;
}

/* ============================================================
   SEARCH AUTOCOMPLETE DROPDOWN
   ============================================================ */

.explore-autocomplete {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 100;
    background: var(--paper);
    border: 1px solid rgba(26, 23, 20, 0.08);
    border-radius: 10px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.12);
    max-height: 320px;
    overflow-y: auto;
    overscroll-behavior: contain;   /* suggestion list must not scroll the page behind it */
    display: none;
    margin-top: 4px;
    min-width: 400px;
    max-width: 100%;
}

.explore-autocomplete.is-open {
    display: block;
}

.explore-autocomplete__section {
    padding: 6px 0;
}

.explore-autocomplete__section + .explore-autocomplete__section {
    border-top: 1px solid rgba(26, 23, 20, 0.06);
}

.explore-autocomplete__section-label {
    display: block;
    padding: 6px 14px 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-muted);
    font-family: var(--explore-font-body);
}

.explore-autocomplete__item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    cursor: pointer;
    transition: background 0.1s;
}

.explore-autocomplete__item:hover {
    background: var(--cream);
}

.explore-autocomplete__item.is-selected,
.explore-autocomplete__item.is-active {
    background: var(--cream-dark, #f2ebe0);
}

.explore-autocomplete__icon {
    flex-shrink: 0;
    width: 14px;
    height: 14px;
    color: var(--ink-faint);
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ★ ROUND 14 — THE SUGGESTIONS WERE CLIPPED MID-WORD ("Export Development Canada
   (EDC", "Alberta Export Expansion Program"). The clamp was doing its job; the flex
   item was not. A flex child defaults to `min-width: auto` = its MIN-CONTENT width,
   so a long unbroken program name pushed this span wider than the row and the
   dropdown's own `overflow: hidden` cut it off — with no ellipsis, because nothing
   had overflowed as far as this element was concerned. The documented `min-width: 0`
   trap, on a control whose entire job is to let a reader recognise a name.
   `overflow-wrap: anywhere` covers the one name long enough to beat even that. */
.explore-autocomplete__text {
    min-width: 0;
    overflow-wrap: anywhere;
    font-size: 0.85rem;
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    line-height: 1.4;
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.explore-autocomplete__text strong {
    color: var(--ink);
    font-weight: 600;
}

.explore-autocomplete__clear {
    padding: 8px 14px;
    font-size: 0.78rem;
    font-family: var(--explore-font-body);
    color: var(--ink-muted);
    cursor: pointer;
    text-align: center;
    border-top: 1px solid rgba(26, 23, 20, 0.04);
    transition: color 0.15s, background 0.15s;
}

.explore-autocomplete__clear:hover {
    color: var(--accent-sienna);
    background: rgba(196, 83, 43, 0.04);
}

/* Mobile: full width of filter bar */
@media (max-width: 1023px) {
    .explore-filters__search {
        position: relative;
    }

    .explore-autocomplete {
        left: 0;
        right: 0;
    }

    .explore-empty--rich {
        padding: var(--space-xl) var(--space-md);
    }
}

/* ============================================================
   30. IMPROVED PREMIUM POPOVER
   ============================================================ */

.explore-popover__close {
    position: absolute;
    top: 6px;
    right: 8px;
    background: none;
    border: none;
    font-size: 18px;
    line-height: 1;
    color: var(--ink-muted);
    cursor: pointer;
    padding: 0;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    transition: color 0.15s, background 0.15s;
}

.explore-popover__close:hover {
    color: var(--ink);
    background: rgba(26, 23, 20, 0.06);
}

.explore-popover__teaser {
    font-size: 11.5px;
    font-family: var(--explore-font-body);
    color: var(--accent-forest);
    margin-bottom: var(--space-sm);
    font-weight: 500;
    line-height: 1.4;
}

.explore-popover__buttons {
    display: flex;
    gap: 8px;
    margin-bottom: 4px;
}

.explore-popover__btn {
    flex: 1;
    display: inline-block;
    text-align: center;
    padding: 7px 10px;
    border-radius: 8px;
    font-size: 12.5px;
    font-weight: 600;
    font-family: var(--explore-font-body);
    text-decoration: none;
    transition: transform 0.15s, box-shadow 0.15s, background 0.15s;
    line-height: 1.3;
}

.explore-popover__btn:hover {
    transform: translateY(-1px);
    box-shadow: 0 2px 8px rgba(26, 23, 20, 0.1);
}

.explore-popover__btn--monthly {
    background: var(--cream-dark);
    color: var(--ink);
    border: 1px solid rgba(26, 23, 20, 0.1);
}

.explore-popover__btn--annual {
    background: var(--accent-forest);
    color: #fff;
    border: 1px solid var(--accent-forest);
}

.explore-popover__btn--annual span {
    display: block;
    font-size: 10px;
    font-weight: 500;
    opacity: 0.85;
}

/* ============================================================
   31. HERO STATS FILTERED STATE
   ============================================================ */

.stat-strip--filtered {
    position: relative;
}

.stat-strip__filtered {
    display: block;
    text-align: center;
    font-size: 11px;
    font-family: var(--explore-font-body);
    color: var(--accent-forest);
    font-weight: 500;
    margin-top: 4px;
    letter-spacing: 0.02em;
}

.stat-strip__filtered-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: rgba(45, 106, 79, 0.1);
    color: var(--accent-forest);
    padding: 3px 10px;
    border-radius: 12px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.03em;
    margin-left: 8px;
    white-space: nowrap;
}

#total-count.is-filtered {
    color: var(--accent-forest);
    font-weight: 700;
}

/* ============================================================
   32. SOCIAL PROOF — PRICING SECTION
   ============================================================ */

.explore-pricing__proof {
    display: flex;
    flex-direction: column;
    gap: 8px;
    align-items: center;
    margin-bottom: 28px;
}

.explore-pricing__proof-item {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    font-family: var(--explore-font-body);
    color: var(--ink);
}

.explore-pricing__proof-item svg {
    flex-shrink: 0;
}

/* ============================================================
   33. MOBILE STICKY PREMIUM CTA BAR
   ============================================================ */

.explore-mobile-cta {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 150;
    display: none;
    align-items: center;
    justify-content: center;
    gap: 10px;
    height: 52px;
    padding: 0 12px;
    background: var(--paper);
    border-top: 1px solid rgba(26, 23, 20, 0.1);
    box-shadow: 0 -4px 16px rgba(26, 23, 20, 0.08);
    transform: translateY(100%);
    transition: transform 0.3s ease;
}

.explore-mobile-cta--visible {
    transform: translateY(0);
}

@media (max-width: 1023px) {
    .explore-mobile-cta {
        display: flex;
    }
}

.explore-mobile-cta__text {
    font-size: 12.5px;
    font-family: var(--explore-font-body);
    color: var(--ink-secondary);
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex: 1;
    min-width: 0;
}

.explore-mobile-cta__btn {
    flex-shrink: 0;
    padding: 7px 14px;
    background: var(--accent-forest);
    color: #fff;
    border: none;
    border-radius: 8px;
    font-size: 12.5px;
    font-weight: 600;
    font-family: var(--explore-font-body);
    text-decoration: none;
    cursor: pointer;
    transition: background 0.15s;
}

.explore-mobile-cta__btn:hover {
    background: var(--accent-forest-deep);
}

.explore-mobile-cta__close {
    flex-shrink: 0;
    background: none;
    border: none;
    font-size: 20px;
    line-height: 1;
    color: var(--ink-muted);
    cursor: pointer;
    padding: 4px;
}

.explore-mobile-cta__close:hover {
    color: var(--ink);
}

/* Hide mobile CTA for premium users */
body.is-premium .explore-mobile-cta {
    display: none !important;
}

/* ============================================================
   34. MONTHLY PRICING BUTTON VISIBILITY
   ============================================================ */

.explore-pricing__card .btn--outline {
    border: 2px solid var(--accent-forest);
    color: var(--accent-forest);
    font-weight: 600;
    background: transparent;
}
.explore-pricing__card .btn--outline:hover {
    background: var(--accent-forest);
    color: white;
}

/* ============================================================
   35. MOBILE STRUCTURAL UX FIXES
   ============================================================ */

/* ─── Fix 1: Mobile Hero Collapse ─────────────────────── */
@media (max-width: 768px) {
    .explore-hero {
        padding: calc(var(--explore-navbar-height) + 12px) var(--space-md) 12px;
    }
    .explore-hero h1 {
        font-size: 1.5rem;
        margin-bottom: 4px;
    }
    .explore-hero__subtitle {
        font-size: 0.85rem;
        margin-bottom: 8px;
    }
    .explore-hero__eyebrow {
        display: none;
    }
    .explore-hero__stats-card {
        margin: 8px auto -12px;
        padding: 10px 16px;
    }
    .stat-strip {
        display: none;
    }
    .explore-hero__preview {
        display: none;
    }
}

/* ─── Fix 3: Bottom Sheet Full-Screen on Mobile ───────── */
@media (max-width: 768px) {
    .explore-bottom-sheet {
        max-height: 95vh;
        border-radius: 16px 16px 0 0;
    }
}

/* Bottom sheet scroll fade indicator — uses the sheet itself, not content ::after */
.explore-bottom-sheet::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 48px;
    background: linear-gradient(transparent, var(--paper, #fffcf7));
    pointer-events: none;
    z-index: 2;
    border-radius: 0 0 0 0;
}

/* ─── Fix 4: Sticky CTA Close Button — proper 44x44 tap target ─── */
.explore-mobile-cta__close {
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    color: var(--ink-muted);
    cursor: pointer;
    flex-shrink: 0;
    padding: 0;
    -webkit-tap-highlight-color: transparent;
}
.explore-mobile-cta__close:hover {
    color: var(--ink);
}
.explore-mobile-cta__close svg {
    width: 18px;
    height: 18px;
}

/* ─── Fix 6: Back to Top Button (mobile only) ─────────── */
.explore-back-to-top {
    display: none;
}
@media (max-width: 1023px) {
    .explore-back-to-top {
        display: flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        bottom: 70px;
        right: 16px;
        width: 44px;
        height: 44px;
        border-radius: 50%;
        background: var(--accent-forest);
        color: white;
        border: none;
        box-shadow: 0 2px 12px rgba(0,0,0,0.15);
        cursor: pointer;
        opacity: 0;
        transform: translateY(10px);
        transition: opacity 0.2s, transform 0.2s;
        z-index: 80;
        -webkit-tap-highlight-color: transparent;
    }
    .explore-back-to-top.is-visible {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Trial expired banner */
/* ============================================================
   GHOST ROW (premium conversion surface after row 3)
   ============================================================ */
.ghost-row {
    display: flex;
    align-items: center;
    padding: 1rem 1.5rem;
    background: linear-gradient(135deg, rgba(45,106,79,.03), rgba(232,168,124,.04));
    border-left: 3px solid #2D6A4F;
    gap: 1.25rem;
    position: relative;
}

.ghost-row__icon {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: linear-gradient(135deg, #2D6A4F, #1B4332);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    flex-shrink: 0;
}

.ghost-row__content {
    flex: 1;
    min-width: 0;
}

.ghost-row__label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #2D6A4F;
    margin-bottom: 0.2rem;
}

.ghost-row__title {
    font-size: 0.9rem;
    font-weight: 700;
    color: #1C1917;
    margin-bottom: 0.15rem;
}

.ghost-row__meta {
    font-size: 0.78rem;
    color: #57534E;
}

.ghost-row__meta strong {
    color: #2D6A4F;
}

.ghost-row__meta .ghost-row__locked {
    color: var(--ink-muted);
    font-style: italic;
}

/* $19 Playbook alternative — subtle secondary link below primary trial CTA
   (Apr 22 2026). Low visual weight, sienna-tinted to differentiate. */
.ghost-row__alt {
    font-size: 0.72rem;
    color: var(--ink-muted);
    margin-top: 4px;
}

.ghost-row__alt-link {
    color: #C4532B;
    text-decoration: underline;
    text-underline-offset: 2px;
    font-weight: 500;
}

.ghost-row__alt-link:hover {
    color: #A3401F;
}

.ghost-row__cta {
    padding: 0.5rem 1.1rem;
    border-radius: 9999px;
    font-weight: 600;
    font-size: 0.78rem;
    color: #fff;
    background: #2D6A4F;
    border: none;
    text-decoration: none;
    white-space: nowrap;
    transition: all 0.2s;
    flex-shrink: 0;
    cursor: pointer;
}

.ghost-row__cta:hover {
    background: #1B4332;
    transform: translateY(-1px);
    color: #fff;
    text-decoration: none;
}

/* ★★ ROUND 9 — THE × WAS ANCHORED TO THE BAND, NOT TO THE CARD. The ghost row is
   pinned to the full visible pane (`width: var(--exp-wrap-w)` above), so an
   absolutely-positioned dismiss at `right: .75rem` sat in the TOP-RIGHT CORNER of a
   1,136px strip: measured at 1440, a lone `×` roughly 1,030px to the right of the
   `PERSONALIZE YOUR RESULTS` eyebrow and 43px above the only other control in the
   row. A dismiss is a statement about the thing it dismisses, and this one belonged
   to the viewport. It is a flow item now — the last child of the row's own flex
   line, beside the CTA it shares a purpose with. */
.ghost-row__dismiss {
    flex-shrink: 0;
    margin-left: 0.25rem;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: transparent;
    border: none;
    color: var(--ink-muted);
    cursor: pointer;
    font-size: 0.7rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

.ghost-row__dismiss:hover {
    background: rgba(0,0,0,.05);
}

@media (max-width: 768px) {
    .ghost-row {
        flex-wrap: wrap;
        padding: 0.75rem 1rem;
        gap: 0.75rem;
    }
    .ghost-row__cta {
        width: 100%;
        text-align: center;
    }
}

/* ============================================================ */

.trial-expired-banner {
    background: #fffbeb; border: 1px solid #fde68a; border-radius: 10px;
    padding: 14px 20px; margin: 16px auto; max-width: 1200px;
}
.trial-expired-banner__content {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.trial-expired-banner__content strong { color: #92400e; font-size: 14px; }
.trial-expired-banner__content span { color: #a16207; font-size: 13px; }
.trial-expired-banner__cta {
    padding: 6px 14px; background: #15803d; color: #fff !important;
    border-radius: 6px; font-size: 13px; font-weight: 600; text-decoration: none;
    margin-left: auto;
}
.trial-expired-banner__close {
    background: none; border: none; cursor: pointer; color: #a16207;
    font-size: 20px; padding: 0 4px; line-height: 1;
}

/* /grants/ detail link in table rows */
.explore-grant-link {
    display: inline-block;
    margin-left: 8px;
    font-size: 11px;
    font-weight: 500;
    color: var(--explore-accent, #2d6a4f);
    text-decoration: none;
    /* ★★ ROUND 14 — `opacity: 0.5` put this link at 2.21:1 AT REST and only brought it
       to 6.2:1 on row hover. That is a hover-gated affordance, which this file has a
       documented lesson about: invisible on touch, invisible in a screenshot, and here
       also invisible to anyone who cannot chase it with a pointer. It rests legible
       now; hover still answers, with the underline it always added. */
    transition: opacity 0.2s;
}
.explore-table__row:hover .explore-grant-link {
    opacity: 1;
}
.explore-grant-link:hover {
    text-decoration: underline;
    opacity: 1;
}
@media (max-width: 768px) {
    .explore-grant-link {
        opacity: 1;
        display: block;
        margin-left: 22px;
        margin-top: 2px;
    }
}

/* ============================================================================
   TOOLKIT STORE — footer-strip callout (Phase 2D — tasteful distribution)
   Single-line, footer-adjacent, quiet. Brand discipline: parchment-warm
   strip, sienna eyebrow + CTA. NOT above-fold. NOT modal. NOT competing
   with the table or the Premium funnel. Stacks on <700px.
   ============================================================================ */
.ts-footer-callout {
    background: var(--cream-dark);
    border-top: 1px solid rgba(28, 23, 20, 0.06);
    border-bottom: 1px solid rgba(28, 23, 20, 0.06);
}
.ts-footer-callout__inner {
    max-width: var(--explore-max-width);
    margin: 0 auto;
    padding: clamp(20px, 3vw, 32px) 24px;
    display: flex;
    align-items: center;
    gap: clamp(14px, 2vw, 24px);
    flex-wrap: wrap;
}
.ts-footer-callout__eyebrow {
    font-family: var(--explore-font-body);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.12em;
    color: var(--accent-sienna);
    text-transform: uppercase;
    flex-shrink: 0;
}
.ts-footer-callout__copy {
    margin: 0;
    font-family: var(--explore-font-body);
    font-size: 14px;
    line-height: 1.55;
    color: var(--ink-secondary);
    max-width: 60ch;
    flex: 1 1 auto;
}
.ts-footer-callout__cta {
    font-family: var(--explore-font-body);
    font-size: 14px;
    font-weight: 600;
    color: var(--accent-sienna);
    text-decoration: none;
    border-bottom: 1px solid var(--accent-sienna-light);
    padding-bottom: 1px;
    transition: border-color 0.18s ease;
    flex-shrink: 0;
    white-space: nowrap;
}
.ts-footer-callout__cta:hover,
.ts-footer-callout__cta:focus-visible {
    border-bottom-color: var(--accent-sienna);
}
.ts-footer-callout__cta:focus-visible {
    outline: 2px solid var(--accent-sienna);
    outline-offset: 3px;
    border-radius: 2px;
}
@media (max-width: 700px) {
    .ts-footer-callout__inner {
        flex-direction: column;
        align-items: flex-start;
        gap: 10px;
    }
    .ts-footer-callout__copy { font-size: 14px; }
}

/* ============================================================
   ZERO-RESULTS CONVERSION HOOKS  (2026-06-01)
   Quiz CTA + notify-me demand capture in the empty state.
   ============================================================ */
.explore-empty__cta {
    margin-top: var(--space-md, 16px);
}
.explore-empty__cta-quiz {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--accent-forest, #2D6A4F);
    color: #fff;
    text-decoration: none;
    border-radius: 8px;
    padding: 11px 22px;
    font-family: var(--explore-font-body);
    font-size: 0.9rem;
    font-weight: 600;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    transition: background 0.15s, transform 0.15s;
}
.explore-empty__cta-quiz:hover {
    background: var(--forest-deep, #1B4332);
    transform: translateY(-1px);
}
.explore-empty__notify {
    margin: var(--space-lg, 24px) auto 0;
    padding-top: 16px;
    max-width: 360px;
    border-top: 1px solid rgba(26, 23, 20, 0.1);
}
.explore-empty__notify-label {
    font-size: 0.82rem;
    color: var(--ink-faint);
    line-height: 1.45;
    margin: 0 0 8px;
}
.explore-empty__notify-form {
    display: flex;
    gap: 6px;
}
.explore-empty__notify-input {
    flex: 1;
    min-width: 0;
    padding: 9px 11px;
    border: 1px solid rgba(26, 23, 20, 0.18);
    border-radius: 7px;
    font-family: inherit;
    font-size: 0.85rem;
}
.explore-empty__notify-btn {
    background: var(--forest-deep, #1B4332);
    color: #fff;
    border: none;
    border-radius: 7px;
    padding: 9px 16px;
    font-family: var(--explore-font-body);
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s;
}
.explore-empty__notify-btn:hover {
    background: #143728;
}
.explore-empty__notify-btn:disabled {
    opacity: 0.7;
    cursor: default;
}
.explore-empty__notify-success {
    font-size: 0.85rem;
    color: var(--accent-forest, #2D6A4F);
    font-weight: 600;
    text-align: center;
    line-height: 1.45;
    margin: 0;
}
.explore-empty__hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    opacity: 0;
}


/* ── Receipts v1 (2026-07-16) — gate-verified chips on cards + table rows ──
   Mirrors the .cb .rcpt family in homepage-v7.css (explore doesn't load it).
   [hidden] guard: display:inline-flex would defeat the hidden attribute. */
.rcpt {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 600; line-height: 1.4;
  padding: 3px 10px; border-radius: 999px; border: 1px solid transparent;
  letter-spacing: -.003em; position: relative; transition: background .12s ease;
  vertical-align: middle;
}
.rcpt[hidden] { display: none; }
.rcpt--pass { background: rgba(45,106,79,.09); border-color: rgba(45,106,79,.22); color: #1a4731; cursor: help; }
.rcpt--pass::before { content: '✓'; font-weight: 800; color: #2d6a4f; }
.rcpt--open { background: rgba(184,134,11,.08); border-color: rgba(184,134,11,.28); color: var(--gold-ink); cursor: pointer; }
.rcpt--open::before { content: '?'; font-weight: 800; }
.rcpt--open:hover, .rcpt--open:focus-visible { background: rgba(184,134,11,.15); outline: none; }
.rcpt--denied { background: rgba(28,25,23,.05); border-color: rgba(28,25,23,.12); color: #57534e; cursor: help; }
.explore-table__cell--program .rcpt { margin-left: 8px; max-width: 100%; }

/* A title that carries a hover card gets a restrained affordance — dotted underline
   on hover only. Permanent decoration would read as 25 links per page. */
.explore-table__cell--program .program-title[data-tip-card] { cursor: help; }
.explore-table__cell--program .program-title[data-tip-card]:hover,
.explore-table__cell--program .program-title[data-tip-card]:focus-visible {
    text-decoration: underline dotted rgba(31,92,61,.55);
    text-underline-offset: 3px;
}
/* The info glyph on a sortable header is an AFFORDANCE, not a target. It used to be
   the click target for a corner toast; now the whole <th> is the hover trigger, so the
   glyph must never swallow a pointer event of its own. */
.explore-table th .th-info-icon { pointer-events: none; }

.explore-card__badges { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; contain: inline-size; max-width: 100%; }
.rcpt.rcpt-ask--expanded {
  flex-basis: 100%; display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center;
  background: rgba(184,134,11,.07); border: 1px solid rgba(184,134,11,.25);
  border-radius: 10px; padding: 10px 14px; cursor: default; white-space: normal;
}
.rcpt.rcpt-ask--expanded::before { display: none; }
.explore-table__cell--program .rcpt.rcpt-ask--expanded { display: flex; margin: 8px 0 0; }
.rcpt-ask__q { font-size: 12px; font-weight: 500; color: #1c1917; line-height: 1.5; flex: 1 1 220px; min-width: 0; white-space: normal; }
.rcpt-ask__btns { display: flex; gap: 6px; flex-shrink: 0; }
.rcpt-ask__btns button {
  font-family: inherit; font-size: 12px; font-weight: 700; color: var(--gold-ink);
  background: #fff; border: 1px solid rgba(184,134,11,.4); border-radius: 999px;
  padding: 8px 16px; min-height: 36px; cursor: pointer; transition: background .12s ease;
}
.rcpt-ask__btns button:hover, .rcpt-ask__btns button:focus-visible { background: rgba(184,134,11,.12); outline: none; }
.rcpt--open { max-width: 100%; min-width: 0; overflow: hidden; }
.rcpt--open .rcpt-t { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ★ ROUND 14 — `opacity: .8` here and `.75` on the arrow below took a 700-weight
   10.5px mark to 3.36:1 and its hint to 3.71:1, on the one control that invites the
   reader to answer a condition. Both are quiet enough on size and hue alone. */
.rcpt--open .rcpt-hint { flex: none; }
.rcpt--open .rcpt-hint::before { content: '·'; margin: 0 5px; opacity: .7; }
/* Card metadata coherence (2026-07-18): .card-badge (payment/trend) rendered
   as bare unstyled text beside the styled difficulty pill — one pill family
   now. Amount no longer collides with the type pill when it wraps. */
.card-badge {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 11px; font-weight: 600; line-height: 1.4;
  padding: 3px 9px; border-radius: 999px;
  border: 1px solid rgba(28,25,23,.12); background: #fff; color: #57534e;
}
.explore-card__header { align-items: flex-start; gap: 10px; }
.explore-card__amount { text-align: right; max-width: 58%; line-height: 1.35; font-size: 12px; }
/* ★ ROUND 13 — IT NAMES ITSELF, in the same word the desktop column header uses. Round 8
   renamed that column "Advertised" rather than "Amount" so that `Realistic award` beside
   it would read as a DIFFERENT fact rather than a repeat; the card carried the same
   figure with no name at all, which is how it came to read as the price. Its own line,
   so the label never competes with the figure for the 58% the header allows it. */
.eca__lbl {
    display: block;
    font: 700 9px/1 var(--explore-font-body);
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-muted);
    margin-bottom: 2px;
}
/* ★★ AND ON THE CARD THE SENIOR MONEY FIGURE IS `Realistic award`, in the Win Layer
   block below — the phone's half of the same correction the desktop row's
   `.td-insider--money` makes. `data-k` addresses the ONE field: a positional rule would
   have silently re-broken when `TEASER_INSIDER` changed order, which it did in round 10. */
.wl-manifest--card .wlm[data-k="realistic"] .wlm__val {
    font-family: var(--explore-font-display, inherit);
    font-size: 13.5px;
    font-weight: 600;
    color: var(--ink, #1c1917);
    font-variant-numeric: tabular-nums lining-nums;
}
.explore-grant-link { white-space: nowrap; display: inline-block; }
.rcpt-ask__note { flex-basis: 100%; font-size: 10.5px; font-weight: 500; color: var(--ink-muted); }


/* Desktop: bring the results above the fold. See the note on .explore-hero. */
@media (min-width: 1024px) {
  .explore-hero { padding-top: calc(var(--explore-navbar-height) + 24px); padding-bottom: 36px; }
  .explore-hero__subtitle { margin-bottom: 20px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   Explore sticky-bar condense (2026-07-29)

   The stylesheet has said since it was written that this bar is "single-row at
   desktop ~52px tall … sticky-shrink on scroll". It measures 183px and has never
   shrunk, because `.is-stuck` was declared in CSS and NOTHING EVER ADDED IT — no
   observer, no scroll handler, no class toggle anywhere in the codebase. A stated
   design with no implementation reads, from the outside, exactly like a design
   decision to occupy 27% of the viewport permanently.

   Condensing reuses the "Filters (N)" toggle this page already ships and only ever
   showed on mobile — it already opens the dropdown row and already renders a count
   of active filters. Nothing new was built.
   ═══════════════════════════════════════════════════════════════════════ */
:root { --explore-filters-stuck-h: 58px; }

@media (min-width: 1024px) {
    /* ★★ ROUND 5A — THE GRID PANE. This rule used to read `overflow: visible`, i.e.
       "the table always fits, so it never needs to scroll", which is the assumption
       that starved three of seven columns below their own headers at 1440.

       HEIGHT is bounded so the pane is fully visible AT REST. Measured: the pane's
       top is y=204 at every desktop width, so 100vh - 224px puts its bottom 20px
       above the fold at 800, 900 and 1080. ⚠ This is load-bearing, not tidiness: an
       inner scroller that extends past the fold traps the reader — a wheel over it
       scrolls its rows while its own bottom stays off screen, reachable by neither
       gesture. `min-height` keeps it usable on a short window.

       WIDTH is `fit-content` so the bordered pane is never wider than the table it
       contains. fit-content resolves to min(max-content, available): when the table
       is wider than the canvas the pane fills the canvas and scrolls; when it is
       narrower the pane shrinks to it. Either way there is no dead gutter inside the
       border — the defect class this rebuild started from.

       `scrollbar-gutter: stable` reserves the vertical scrollbar ALWAYS, so
       appending a chunk cannot change the canvas width and re-fit every column
       under the reader mid-scroll. _paneCanvas() subtracts exactly this. */
    .explore-table-wrap {
        overflow: auto;
        width: fit-content;
        max-width: 100%;
        max-height: calc(100vh - 224px);
        min-height: 380px;
        scrollbar-gutter: stable;
        /* ⚠ This pre-existing rule was overriding the shorthand above on the X axis alone,
           so the pane still bounced LEFT and RIGHT after the Y axis was fixed — exactly the
           edges Khalid reported alongside the top. `contain` refuses the chain (macOS
           swipe-to-go-back) but keeps the local bounce; `none` does both. */
        overscroll-behavior-x: none;
    }

    /* At rest: everything on show. Once stuck: one row. */
    .explore-filters.is-stuck { padding-top: 9px; padding-bottom: 9px; }
    .explore-filters.is-stuck .explore-filters__inner { row-gap: 0; flex-wrap: nowrap; }
    /* Round 14: the stuck bar's expanded-dropdown panel is gone with the row it
       expanded. At 1024-1279 the stuck bar's Filters button opens the sheet, which
       is a dialog and does not need the bar to make room for it. */
    .explore-filters.is-stuck { position: sticky; }
    /* The toggle is display:none by default and only shown in the mobile query.
       Stuck desktop is the third state that needs it. */
    .explore-filters.is-stuck .explore-filters__toggle { display: inline-flex; }
    .explore-filters.is-stuck .explore-filters__search { flex: 1 1 auto; min-width: 0; }
}

/* The sentinel the observer watches. Zero-height, no layout effect — it exists so the
   bar can know it has left its resting place without a scroll listener firing on every
   frame. */
.explore-filters-sentinel { height: 1px; margin-bottom: -1px; pointer-events: none; }

/* ══════════════════════════════════════════════════════════════════════════
   EXPLORE USES WHAT WE KNOW (2026-07-29)
   ══════════════════════════════════════════════════════════════════════════ */

/* Seven columns fit only because the two low-variance ones give width back.
   Measured on the live page BEFORE this change: deadline ran 52px SHORT while type
   carried 114px of slack and difficulty 49px. Type keeps a column — 61% grant /
   39% other is real variance — but not 150px for one word. */
.explore-table.has-clear { table-layout: fixed; width: 100%; }
.explore-table.has-clear th:nth-child(1), .explore-table.has-clear td:nth-child(1) { width: 25%; }
.explore-table.has-clear th:nth-child(2), .explore-table.has-clear td:nth-child(2) { width: 13.5%; }
.explore-table.has-clear th:nth-child(3), .explore-table.has-clear td:nth-child(3) { width: 96px; }
.explore-table.has-clear th:nth-child(4), .explore-table.has-clear td:nth-child(4) { width: 13%; }
.explore-table.has-clear th:nth-child(5), .explore-table.has-clear td:nth-child(5) { width: 86px; }
.explore-table.has-clear th:nth-child(6), .explore-table.has-clear td:nth-child(6) { width: 17%; }
.explore-table.has-clear th:nth-child(7), .explore-table.has-clear td:nth-child(7) { width: 104px; }
.explore-table.has-clear td { overflow: hidden; }
/* The deadline cell must never clip — its whole defect was being cut off. */
.explore-table.has-clear .explore-table__cell--deadline { overflow: visible; white-space: normal; }

/* ── The ratio cell ──────────────────────────────────────────────────────── */
.explore-table__cell--clear { white-space: nowrap; }
.exp-clear { font-variant-numeric: tabular-nums; font-weight: 700; font-size: 15px; line-height: 1.2; }
.exp-clear small { font-weight: 600; font-size: 12px; color: var(--ink-3, #78716c); }
.exp-clear--all  { color: #1f5c3d; }
.exp-clear--part { color: var(--amber-strong); }
.exp-clear--no   { color: var(--ink-3, #78716c); }
.exp-clear--none { color: #b9b1a4; font-weight: 500; }
/* ★★ ROUND 6 — THE FOURTH TONE, AND IT IS DELIBERATELY NOT GREEN. `gap` is a row
   whose program sets an identity, institution or location condition that no
   question on this site captures. Nothing we checked stands in the founder's way,
   and that is exactly why it must not wear the colour that says "you clear this":
   a women-only program rendering green and ticked for a founder who never
   disclosed ownership is the false-claim class at its most personal. Neutral ink,
   same weight — a state, not a warning. */
.exp-clear--gap  { color: #6b6255; }
/* ★★ ROUND 6b — ONE SEGMENT, AND THE REASON IS A MEASUREMENT.
   Round 6's second line held TWO counts in two colours ("1 to confirm · 2 not
   asked"), which fitted only because the unchecked list was hardGates-only and so
   almost every row printed one segment or none. Once the count reads the record's
   OWN conditions, two segments are the common case — and two segments need 152px
   inside a 120px content box (a 152px column less 2×16px padding). Measured: it
   clipped on 28 of 50 rendered rows, invisibly, because the cell clips and the page
   never scrolls.
   The line now carries the DENOMINATOR, which is the fact the cell was actually
   missing; the split between "we can still ask" and "nothing we ask settles it"
   lives in the tone, in the expansion (named condition by condition) and in the
   cell's accessible name. Muted on purpose: it QUALIFIES the count above it, it is
   not a second finding. */
.exp-clear__open { display: block; margin-top: 1px; line-height: 1.25; }
.exp-clear__of { font-size: 11px; font-weight: 600; color: var(--ink-muted); }

/* ── "ceiling" marker on an inflated maximum ──────────────────────────────
   A CUE, not an offer: no price, no button. It appears only where the gap is
   material (>=2x), so its presence carries information. */
/* Round 3 (O14): a suffix chip on the figure's line, not a second statement under
   it. Smaller — it qualifies the number, it is not a finding of its own. */
.exp-cap {
  display: inline-block; margin-left: 5px; vertical-align: 1px;
  font-size: 8.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--amber-strong); background: rgba(168,106,31,.09);
  border: 1px solid rgba(168,106,31,.22); border-radius: 4px; padding: 1px 4px;
  cursor: help; white-space: nowrap;
}
.exp-cap:focus-visible { outline: 2px solid #a86a1f; outline-offset: 1px; }
/* ── "per unit" / "fund total" basis marker (round 12) ─────────────────────
   Same anatomy as the ceiling chip, deliberately NOT the same colour. The amber
   is a caution — "this number is not what you will get". A basis marker makes no
   such claim: the figure is exactly right, it is simply measured per youth / per
   project / per fund, and the Realistic award beside it is not. So it takes the
   surface's neutral ink and reads as a unit, not a warning. It replaces the quiet
   `.exp-amt__sub` on those cells (see `_amountCell`), so the row gains no width. */
.exp-cap--basis {
  color: var(--ink-3, #78716c); background: rgba(26, 23, 20, .05);
  border-color: rgba(26, 23, 20, .14); text-transform: none;
  font-size: 9px; letter-spacing: .01em; font-weight: 600;
}
.exp-cap--basis:focus-visible { outline: 2px solid var(--ink-3, #78716c); }

/* ── Honest count ─────────────────────────────────────────────────────────── */
.exp-count__note { color: var(--ink-3, #78716c); font-weight: 400; margin-left: 8px; font-size: 13px; }
/* Round 3: a real (small) control, not an underlined word inside a sentence. */
.exp-count__show {
  font: 600 11.5px/1 var(--explore-font-body); color: #1f5c3d;
  background: rgba(31, 92, 61, 0.07); border: 1px solid rgba(31, 92, 61, 0.16);
  border-radius: 999px; padding: 5px 10px; margin-left: 8px; cursor: pointer;
  white-space: nowrap; vertical-align: 1px;
}
/* ★ ROUND 14 — 84x24 measured. On a POINTER device that is a comfortable pill; on a
   phone it is half a touch target, and this control is how a reader reaches the 88
   closed programs the count above it has just told them about. Scoped to
   `pointer: coarse` so the desktop bar keeps its density — the floor is a property
   of the finger, not of the viewport width, and a narrow window on a laptop does not
   need a 44px pill. `inline-flex` + `align-items` keeps the label optically centred
   once the box grows. */
@media (pointer: coarse) {
  .exp-count__show {
    display: inline-flex; align-items: center;
    min-height: 44px; padding: 0 14px; vertical-align: middle;
  }
}
.exp-count__show:hover { color: #17482f; background: rgba(31, 92, 61, 0.13); }
/* ★ ROUND 6 — a toggle has to LOOK pressed. Until this round the control was dead
   (tooltip.js's capture-phase click handler swallowed every press), so it never
   needed a second state; now it has one, and the label changes with it so the state
   is legible without relying on colour alone. */
.exp-count__show.is-on {
  color: #fff; background: #1f5c3d; border-color: #1f5c3d;
}
.exp-count__show.is-on:hover { color: #fff; background: #17482f; }
.exp-count__show:focus-visible { outline: 2px solid #1f5c3d; outline-offset: 2px; }

/* ── Structured deadline ──────────────────────────────────────────────────── */
.exp-dl__lead   { display: block; font-weight: 600; color: var(--ink, #1c1917); }
.exp-dl__detail { display: block; font-size: 12.5px; line-height: 1.35; color: var(--ink-3, #78716c); margin-top: 1px; }

/* ── The recognition row ───────────────────────────────────────────────────
   Sits as the first row of the tbody: above the data, below the sticky header.
   A banner BETWEEN a sticky header and its rows severs the two on every scroll. */
.exp-recog-row > td { padding: 0 !important; background: none !important; }
/* ★ The card-view twin of the row above (2026-09-04). `.explore-cards` is a CSS grid —
   one column at 390, two at 768 — so a plain div dropped into it would take a single
   cell and sit beside a program card as if it were one. `1 / -1` spans every column at
   both widths. Rounded to match the cards it heads, and given the same bottom gap the
   grid uses so it reads as a heading for the list rather than a member of it. */
.exp-recog-row--cards { grid-column: 1 / -1; margin-bottom: 2px; }
.exp-recog-row--cards .exp-recog { border-radius: 10px; }
.exp-recog {
  display: flex; align-items: center; gap: 13px;
  padding: 12px 18px; background: #eaf2ec; border-left: 3px solid #1f5c3d;
}
.exp-recog__i {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%;
  background: #1f5c3d; color: #fff; font-size: 12px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.exp-recog__t { font-size: 14px; font-weight: 600; color: var(--ink, #1c1917); line-height: 1.4; min-width: 0; }
.exp-recog__p { font-weight: 400; color: var(--ink-2, #57534E); }
.exp-recog__seg {
  margin-left: auto; flex: 0 0 auto; display: flex;
  border: 1px solid rgba(28,25,23,.16); border-radius: 8px; overflow: hidden; background: #fff;
}
.exp-recog__seg button {
  font: 600 13px/1 inherit; padding: 8px 14px; border: none; background: #fff;
  color: var(--ink-3, #78716c); cursor: pointer; white-space: nowrap;
  transition: background .16s, color .16s;
}
.exp-recog__seg button.on { background: #1f5c3d; color: #fff; }
.exp-recog__seg button:not(.on):hover { background: #f4f1ec; color: var(--ink, #1c1917); }
.exp-recog__seg button:focus-visible { outline: 2px solid #1f5c3d; outline-offset: -2px; }

@media (max-width: 900px) {
  .exp-recog { flex-wrap: wrap; }
  .exp-recog__seg { margin-left: 0; width: 100%; }
  .exp-recog__seg button { flex: 1 1 0; }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE PROGRAM CELL, THE AMOUNT, AND THE PILLS (2026-07-29, second pass)

   Khalid, on the deployed table: the program column is too tight so titles wrap
   constantly, a wrapped second line is not indented to match the first, the amount
   is "just another nondescript column of typical text", the funding type holds a
   whole column to print one word, and the green eligibility pills "totally disrupt
   the layout".
   ══════════════════════════════════════════════════════════════════════════ */

/* Six columns now (Type folded into the program cell), so the two that were cramped
   get the width back. Program gains ~7 points, Deadline ~2. */
/* ⚠ These MUST sum to 100%. Mixing percentages with fixed px left 79% + 210px against a
   1254px table — 1200px of columns and 54px of nothing after the last one, which is the
   visual gap Khalid reported. `table-layout: fixed` honours every explicit width and simply
   does not assign the remainder. Percentages only, totalling exactly 100. */
.explore-table.has-clear th:nth-child(1), .explore-table.has-clear td:nth-child(1) { width: 32%; }
.explore-table.has-clear th:nth-child(2), .explore-table.has-clear td:nth-child(2) { width: 13%; }
.explore-table.has-clear th:nth-child(3), .explore-table.has-clear td:nth-child(3) { width: 8.5%; }
.explore-table.has-clear th:nth-child(4), .explore-table.has-clear td:nth-child(4) { width: 15%; }
.explore-table.has-clear th:nth-child(5), .explore-table.has-clear td:nth-child(5) { width: 19%; }
/* 12.5% ≈ 157px at 1440, against 104px before. The effort pill needs ~100px of CONTENT and
   had 72px inside a 104px column with 16px padding each side, so ALL 25 wrapped to two lines
   in a column that had space to spare beside it. */
.explore-table.has-clear th:nth-child(6), .explore-table.has-clear td:nth-child(6) { width: 12.5%; }

/* ── The chevron leaves the text flow, so a wrapped title ALIGNS with line 1 ────
   It used to be an inline sibling of the title, so text flowed around it and line 2
   began under the glyph.

   ⚠ NOT `display:flex` on the <td>. That was the first fix and it collapsed the column
   to 128px: a flex container is no longer a table-cell, so the table layout algorithm
   stops sizing it and every width rule above is ignored. The cell must remain a
   table-cell. Absolute-positioning the chevron and padding the content gives the same
   hanging indent while leaving the table intact. */
.explore-table__cell--program { position: relative; }
.explore-table__cell--program .row-chevron {
    position: absolute; left: 8px; top: 15px;
    width: 12px; margin-right: 0;
}

/* ── The amount reads as a figure ───────────────────────────────────────────────
   Same principle as the Game Plan's award cell — the number carries weight and its
   qualifier is demoted — expressed in Explore's own type rather than borrowed. */
/* ★ Round 3: one primary figure, right-aligned, tabular-lining. Money compares by
   magnitude, and a left-aligned money column makes the reader compare string
   lengths instead. Rates and unqualified values get their own quieter treatment so
   "25% credit" and "Varies" never read as small awards. */
.explore-table__cell--amount { white-space: normal; text-align: right; }
.explore-table th[data-sort="amount"] { text-align: right; }
.explore-table th[data-sort="amount"] .sort-arrow,
.explore-table th[data-sort="amount"] .th-info-icon { margin-left: 3px; }
/* ★★ ROUND 13 — THE ADVERTISED CEILING IS THE QUIETER HALF OF ITS OWN PAIR. It was the
   loudest thing on the row (15px / 600 / full ink, against a 14px / 500 program name and
   a 12.5px realistic award) — this page's whole argument is that the advertised figure
   is not what a business gets, and the type was making the opposite case in every row.
   It keeps the tabular figures and the right alignment, because comparing money by
   magnitude is what the column is for; what it gives up is the row's top weight. */
.exp-amt {
    font-family: var(--explore-font-display, inherit);
    font-size: 13px; font-weight: 500; line-height: 1.25;
    color: #6b6255;
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
    white-space: nowrap;
}
.exp-amt--rate { font-size: 12.5px; color: #6b6255; font-weight: 500; }
.exp-amt--soft { font-size: 12.5px; color: var(--ink-3, #78716c); font-weight: 500; white-space: normal; }
.exp-amt--none { color: #b9b1a4; font-weight: 500; }
.exp-amt__sub {
    margin-left: 4px;
    font-size: 11.5px; line-height: 1.35; color: var(--ink-3, #78716c);
    white-space: nowrap;
}

/* ── The pills stop being paragraphs ───────────────────────────────────────────
   An ANSWERED condition is a fact and needs a marker; the sentence lives in the
   hover. Fixed height, one line, never wraps — a row's height must not depend on how
   verbosely a funder worded its eligibility rule. */
.rcpt--terse {
    display: inline-flex; align-items: center;
    margin-top: 5px; max-width: 100%;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    font-size: 11px; font-weight: 600; letter-spacing: .01em;
    padding: 2px 8px; border-radius: 99px; cursor: help;
}
.rcpt--terse::before { margin-right: 4px; font-weight: 800; }
.rcpt--pass.rcpt--terse::before { content: '✓'; }
.rcpt--denied.rcpt--terse::before { content: '✕'; }
.explore-table__cell--program .rcpt { margin-left: 0; }

@media (max-width: 767px) {
    /* Cards, not a table — no chevron gutter to reserve. */
    .explore-table td.explore-table__cell--program { padding-left: 16px; }
}


/* At narrow desktop widths the "You clear" head clipped its own sort chevron. */
/* 'YOU CLEAR' plus its info glyph and sort chevron needs one line, not three. */
.explore-table.has-clear th:nth-child(3) { white-space: nowrap; }
@media (max-width: 1180px) {
    /* Still summing to 100: the two narrow columns give a point each to Program. */
    .explore-table.has-clear th:nth-child(1), .explore-table.has-clear td:nth-child(1) { width: 34%; }
    .explore-table.has-clear th:nth-child(3), .explore-table.has-clear td:nth-child(3) { width: 7.5%; }
    .explore-table.has-clear th:nth-child(6), .explore-table.has-clear td:nth-child(6) { width: 11.5%; }
    .explore-table.has-clear th { font-size: 10.5px; letter-spacing: .05em; }
}

/* The funder name drops government boilerplate and keeps the verbatim string in the hover,
   so the column stops truncating two-thirds of its values without losing anything. */
.exp-funder { cursor: default; }
.exp-funder[data-tip] { cursor: help; }
/* ⚠ On hover ONLY. A permanent dotted underline on every row read as 25 links — decoration
   masquerading as a control, which is the same fault in the opposite direction. */
.exp-funder[data-tip]:hover, .exp-funder[data-tip]:focus-visible {
    border-bottom: 1px dotted rgba(28,25,23,.3);
}
.explore-table__cell--funder { white-space: normal; }

/* ══ Round 3 (2026-07-29) — composition pass on the reworked table ══════════════ */

/* C-6 — a list with no landmarks reads as undifferentiated drift: row 1 and row 6 were
   visually interchangeable. The Game Plan numbers its rows; this does the same.
   ★★ 2026-08-06 — this used to say "ONLY in the fit-ordered view", because a number beside
   a row the user just sorted by award would be claiming a rank that ordering does not
   express. Rewritten rather than deleted: what the element prints is POSITION, not rank,
   and position is true under every ordering, filter and search. Withholding it left the
   default anonymous view with no landmarks at all, which is why a reader deep in a 743-row
   list could not tell that "Show 25 more programs" had done anything. It costs no width
   (the 40px Program gutter already exists for the chevron) and it is not a column, so it
   can never be toggled off and never takes width from one.
   ⚠ Weight dropped 700 -> 600 and the tint lightened for exactly that reason: at 25 rows
   it was an accent, at 743 it is a ruler, and a ruler must not compete with the program
   names beside it. */
.exp-rank {
    /* Aligned with the TITLE's first line, not floating between the title and the meta line
       where it collided with the word "Grant". A number belongs beside the thing it numbers.
       ⚠ GEOMETRY (top / left / width) IS OWNED BY THE ROUND-16 BLOCK NEAR THE END OF THIS FILE,
       which redeclares `.exp-rank` and wins `top`. It is NOT declared here any more: the two
       definitions disagreed (16px here, 11px there), this one lost silently, and the comment
       above went on describing an alignment the page did not have. Everything below is styling
       only, so editing it cannot move the number. `css-debt-audit.js --selector .exp-rank`
       reports the split. */
    position: absolute; text-align: right;
    font-size: 10.5px; font-weight: 600; font-variant-numeric: tabular-nums;
    /* ⚠ NO `opacity` HERE. The first pass of this round quieted the number with
       `opacity: .72`, which composited `--ink-3` down to 3.08:1 and broke AA — caught by
       this file's own whole-surface contrast sweep, not by looking. Quietness comes from
       size and weight, which cost no contrast; ink stays at full strength (5.24:1). */
    color: var(--ink-3, #a8a29e); line-height: 1;
}
/* ⚠ `td.` — NOT a bare class. `.explore-table td { padding: 14px 16px }` is (0,0,1,1) and beat
   the bare `.explore-table__cell--program { padding-left }` at (0,0,1,0), so the gutter never
   existed and the rank, chevron and title all painted over one another ("1ndustrial"). The
   computed value was 16px while the stylesheet said 34px — audit the cascade, do not trust the
   declaration. */
.explore-table td.explore-table__cell--program .row-chevron { left: 24px; top: 17px; }

/* C-5 — emphasis was inverted against the decision. The effort pills were the most
   saturated elements on the page, so the screen answered "how hard is it" loudest, while
   "You clear 3/3" — the personalized fact the whole ranking rests on — was the quietest
   text in the row. Mute the pills to outlines; let the fraction carry the weight. */
.explore-table .difficulty-label {
    background: transparent !important;
    border: 1px solid currentColor;
    opacity: .78;
    font-weight: 600;
    white-space: nowrap;          /* one line, or the column is not wide enough — fix the column */
}
.explore-table__cell--clear .exp-clear { font-size: 16.5px; }
.explore-table__cell--clear .exp-clear--all { color: #1a5236; }

/* C-7 — the fit fraction now survives onto the phone card, where it matters most. */
/* ★ ROUND 6: no trailing label any more — `_clearCell` says "cleared" itself and can
   emit a second line ("1 to confirm · 1 not asked"), which a baseline-aligned flex
   row would have laid out beside the count instead of under it. */
.explore-card__clear { display: block; margin: 0 0 var(--space-md); }
.explore-card__clear-l { font-size: 11px; font-weight: 600; letter-spacing: .04em;
    text-transform: uppercase; color: var(--ink-3, #78716c); }

/* ════════════════════════════════════════════════════════════════════════════
   SERIOUS-SURFACE REBUILD (2026-08-04) — compact band + pro table
   Spec: docs/superpowers/specs/2026-08-04-explore-serious-surface-design.md
   ════════════════════════════════════════════════════════════════════════════ */

/* ── Compact hero band — the catalogue is the hero ── */
.explore-hero--band {
    min-height: 0;
    /* The nav is FIXED: without this clearance the band slides under it and
       renders as a textless green sliver (caught by geometry check 2026-08-04 —
       the harness's height+text assertions both passed while nothing was
       visible; only rect.top vs nav height sees it). */
    margin-top: var(--explore-navbar-height, 76px);
    padding: 22px 24px 20px;
    display: block;
}
.explore-hero--band::after { display: none; }
.explore-hero__band-inner {
    max-width: var(--explore-max-width);
    margin: 0 auto;
    display: flex;
    align-items: baseline;
    gap: 16px;
    flex-wrap: wrap;
    position: relative;
    z-index: 2;
}
.explore-hero--band .explore-hero__band-title {
    font-family: var(--explore-font-display, Georgia, serif);
    font-size: clamp(22px, 2.4vw, 27px);
    line-height: 1.15;
    margin: 0;
    color: #fff;
    letter-spacing: -0.01em;
}
.explore-hero__band-line {
    margin: 0;
    font-family: var(--explore-font-body);
    font-size: 14px;
    color: rgba(255, 255, 255, 0.85);
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    flex-wrap: wrap;
    min-width: 0;
}
.explore-hero__band-line #total-count { color: #fff; font-weight: 700; }
.explore-hero__band-stats { display: inline; }
.explore-hero__band-stats .band-stat { white-space: nowrap; }
.explore-hero__band-stats .band-stat b { color: #fff; font-weight: 600; }
/* ⚠ ROUND 16 — `.band-stat--filtered` deleted with the counts it qualified. The band
   no longer varies with the filter state (the rail and #results-count own every
   number now), so nothing can emit this class and a rule for it would be one more
   piece of styling for an element that never renders. */
.explore-hero__band-verified { opacity: 0.75; white-space: nowrap; }
@media (max-width: 640px) {
    .explore-hero--band { padding: 16px 18px 14px; }
    /* ★★ ROUND 9 — ONE RULE, BOTH HALVES OF THE JOIN. Hiding the phrase without its
       separator left the band ending on a dangling "·" at 390px (measured: the last
       glyph of the page's opening line). The separator exists only to join the stats
       TO this phrase, so it lives or dies with it, in the same declaration — which is
       the only arrangement under which a future change cannot separate them again. */
    .explore-hero__band-verified,
    .band-stat--verified-sep { display: none; }
    /* ★ THE OPENING LINE WAS CUT OFF AT 390 (measured 2026-09-04: the span is 367px
       inside a 354px box, signed in or out). `.band-stat` is `nowrap` so that a real
       stat — "886 programs" — can never break across two lines. But the FIRST band-stat
       is not a stat, it is the sentence "Grants, tax credits, loans, awards and support
       programs", and no amount of nowrap makes 367px fit 354. The parent already wraps;
       only the child forbade it, so the overflow had nowhere to go and the last word was
       clipped at the hero's edge. Phones let the phrase wrap; desktop keeps its stats
       whole. */
    .explore-hero__band-stats .band-stat { white-space: normal; }
    /* ★ …and once it wraps, its alignment stops matching the title above it. The band
       inherits `text-align: center`, which is invisible at >=768 because the sentence fits
       on one line and starts flush left at the same x as the h1 (measured: both at x=24).
       At 390 it wraps and centres — h1 at x=18, the sentence at x=45 — so the same block
       reads in two alignments. Left is the one that agrees with the title AND with how this
       band already renders at 768. */
    .explore-hero__band-line { text-align: left; }
}


/* ══ THE FROZEN PROGRAM COLUMN (round 5A) ═══════════════════════════════════
   ⚠ CONTRACT CHANGE. This block used to be scoped to `.is-hscroll` — the class the
   old two-mode renderer put on the wrap only when an insider column beyond the
   teaser pair was on. There are no modes any more: the pane always scrolls when
   its columns are wider than it, so the freeze is unconditional at desktop.

   The shadow is NOT unconditional: it appears only once there is content behind
   the frozen cell (`.is-scrolled-x`, set from the pane's own scrollLeft), because
   a drop shadow over nothing reads as a rendering artefact.

   ★ The shadow travels through a CUSTOM PROPERTY rather than being declared on
   the cell. `box-shadow` is a single property, so the row cursor's inset ring
   would otherwise REPLACE the freeze shadow on the one cell that needs both. The
   default is a fully transparent shadow, never `none` — `none` inside a
   comma-separated list invalidates the whole declaration (documented gotcha). */
@media (min-width: 1024px) {
    /* ⚠ SCOPED TO THE TWO ROW KINDS THAT HAVE A PROGRAM COLUMN, never to a bare
       `:first-child`. Found by looking at a scrolled render (2026-08-04): a generic
       `.explore-table th:first-child` also matched the insider band row's FREE
       SPACER — a `colspan="6"` cell 1075px wide — which then froze at the pane's
       left edge and painted over the entire visible band, so "THE WIN LAYER" and
       its offer were invisible at every scroll position. Nothing failed; the row
       was simply blank. Same hazard for the detail panel, the ghost row and the
       watch divider, which are all full-width cells. */
    .explore-table .explore-table__labels th:first-child,
    .explore-table tbody > tr.explore-table__row > td:first-child {
        --exp-freeze-shadow: 0 0 0 0 rgba(26, 23, 20, 0);
        position: sticky;
        left: 0;
        z-index: 3;
        background: var(--paper);
        box-shadow: var(--exp-freeze-shadow);
    }
    .explore-grid.is-scrolled-x .explore-table .explore-table__labels th:first-child,
    .explore-grid.is-scrolled-x .explore-table tbody > tr.explore-table__row > td:first-child {
        --exp-freeze-shadow: 8px 0 14px -8px rgba(26, 23, 20, 0.30);
    }
    .explore-table .explore-table__labels th:first-child {
        background: var(--cream-dark);
        z-index: 12;
    }
}

/* ── Insider (premium) columns ── */
.explore-table th.th-insider {
    background: #efe6d6;
    color: var(--amber-ink);
}
.explore-table th.th-insider .th-lock {
    display: inline-flex;
    vertical-align: -1px;
    margin-right: 5px;
    opacity: 0.8;
}

/* ── ROUND 13: the Fit column's confidence qualifier, once, in its own header ──
   ★★ It used to be `.exp-fit__on`, printed under every verdict. It is a fact about the
   READER (round 10's own words), so it is true of all 25 rows at once and printing it
   per row printed it 25 times to say one thing. Here it says it once, it is on screen
   whenever the column is, and it can be the ROUTE — a link per row would have been 25
   links; a link in a header is one.
   ⚠ `white-space: normal` is load-bearing. `.explore-table th` sets `nowrap`, and this
   note in a 152px column with nowrap would push the header's `scrollWidth` past its own
   column and break invariant 6 (no column narrower than its own header). It wraps
   instead, which costs header HEIGHT and never header width.
   ⚠ `text-transform: none` too: the header row is uppercase with 0.08em tracking, which
   is right for a label and unreadable for a sentence. */
.explore-table th .th-note {
    display: block;
    /* ⚠ EVERY NUMBER HERE IS A FOLD BUDGET, not taste. The note makes the Fit header a
       two-line cell, which makes the whole header row taller, which pushes the first data
       row down — measured, it cost the eighth row above the 900px fold for a profiled
       reader (firstTop 329 → 335 at a 71px row). Tightened until the row is back. */
    margin-top: 2px;
    max-width: 100%;
    font-family: var(--explore-font-body);
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    white-space: normal;
    line-height: 1.15;
    color: var(--amber-ink);
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: 2px;
    text-decoration-thickness: 1px;
    cursor: pointer;
}
.explore-table th .th-note:hover,
.explore-table th .th-note:focus-visible {
    color: var(--accent-sienna, #c4532b);
    text-decoration-style: solid;
}
/* ⚠ ONE CELL WITH TWO LINES MISALIGNS EVERY OTHER LABEL IN THE ROW. Middle-aligned, a
   two-line Fit header centres its pair and its first line sits ~6px above the single-line
   labels beside it — a header row that looks broken. Top-aligning the whole label row
   costs nothing while every header is one line (there is no slack to distribute) and
   keeps all thirteen labels on one baseline the moment one of them is not. */
.explore-table__labels th { vertical-align: top; }
/* And the note's own cell gives its bottom padding back, so the taller header buys the
   fewest possible pixels from the fold. */
.explore-table th.th-has-note { padding-bottom: 5px; }
.explore-table td.td-insider {
    background: rgba(239, 230, 214, 0.26);
    font-family: var(--explore-font-body);
    font-weight: 600;
    font-size: 12.5px;
    color: #4a4238;
    white-space: nowrap;
}
/* A prose-derived insider value ("Invitation-only") is longer than its column and
   was the ONLY thing on this page overflowing the scroll box's right edge at 1366
   (measured 2026-08-04). nowrap + ellipsis inside the cell, not past it. */
.explore-table td.td-insider > span {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}
.explore-table td.td-insider .ins-sub {
    display: block;
    font-weight: 500;
    font-size: 11px;
    color: var(--ink-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Locked values: REAL data blurred (grants.json is public client-side; CSS blur
   is the accepted sitewide gating model — never fake values). aria-hidden set in
   markup; nothing focusable inside. */
/* ⚠ ROUND 10 — `:not(.exp-fit__on)`. The Fit cell's confidence line is a direct-child
   <span> of the td, so it was swept into this blur and the one line on the cell a free
   reader can ACT on went to fog. Caught by asserting COMPUTED filter rather than the
   presence of a class. The verdict itself is still the only thing gated. */
.explore-table td.td-insider.is-locked > span:not(.exp-fit__on) {
    filter: blur(4.5px);
    user-select: none;
    opacity: 0.85;
    pointer-events: none;
}
.explore-table tr.row-insider-open td.td-insider {
    background: rgba(239, 230, 214, 0.5);
}
/* ── ROUND 13: `Realistic award` is the row's SENIOR money figure ────────────
   ★★ THE PAIR HAS TO COMPARE AS MONEY, WHICH MEANS ONE FACE. Round 8 put `Advertised`
   and `Realistic award` either side of the seam because that contrast is the strongest
   argument on the page and carries no CTA. Measured, it was made in two typefaces at two
   sizes with the WRONG one senior: Advertised in Fraunces at 15/600/full-ink, Realistic
   in the body face at 12.5/600/#4a4238. Two faces read as two unrelated facts; the
   contrast only argues when the eye can see it is the same quantity, twice.
   ⚠ The width budget is untouched: 190px, set from the header's measured natural width
   ("REALISTIC AWARD" uppercase + lock + info glyph), which is far wider than any value
   this rule can produce — the header is the binding constraint on this column, so
   nothing here can push it past invariant 6.
   ★ It stays senior WHEN BLURRED, which is the point. For a free reader the composition
   then says the figure worth reading is the one they cannot: that is the offer, made in
   type rather than in another CTA. */
.explore-table td.td-insider--money > span {
    font-family: var(--explore-font-display, inherit);
    font-size: 14.5px;
    font-weight: 600;
    color: var(--ink, #1c1917);
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}

/* ── Shape cells (round 4): the count is open, the list is not ───────────────
   `traps` and `docs` are LIST fields, so the product is the list and the count is
   only its shape. Blurring a two-character count would hide nothing and say
   nothing — every locked cell in the column would render identically and the
   column would stop telling one row from another. These carry NO blur and no
   `is-locked`: a real number, and a lock glyph naming what Premium adds. */
.explore-table td.td-insider--shape > span { color: #4a4238; }
.ins-shape-lock {
    display: inline-flex;
    vertical-align: -1px;
    margin-left: 3px;
    color: var(--amber-ink);
    opacity: 0.75;
}
/* ── ODDS: one ladder, five rungs (round 6) ─────────────────────────────────
   ★ THE RUNGS ARE READ BY POSITION, NOT BY HUE. The words already carry the
   order; the tick is a second, non-colour encoding so the scale is legible in
   greyscale, at a glance, and to a reader who cannot separate red from green.
   Deliberately NOT a red→green ramp: low odds are not an error and high odds are
   not a recommendation — this column reports, it does not advise.
   ⚠ A locked cell blurs its span, so the tick must live on the SPAN and not on the
   td, or the rung would stay sharp while the word behind it went to fog. */
.explore-table td.td-insider--odds > span { position: relative; padding-left: 15px; }
.explore-table td.td-insider--odds > span::before {
    content: "";
    position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    width: 10px; height: 10px; border-radius: 2px;
    background: linear-gradient(to top, #6b6255 var(--odds-fill, 20%), rgba(107, 98, 85, 0.16) var(--odds-fill, 20%));
}
.explore-table td.odds-step-1 > span { --odds-fill: 20%; }
.explore-table td.odds-step-2 > span { --odds-fill: 40%; }
.explore-table td.odds-step-3 > span { --odds-fill: 60%; }
.explore-table td.odds-step-4 > span { --odds-fill: 80%; }
.explore-table td.odds-step-5 > span { --odds-fill: 100%; }
/* "No contest" is NOT the bottom rung — it is a different answer. Muted, no tick,
   so a scanning reader cannot mistake it for "very low". */
.explore-table td.td-insider--nocontest > span { color: var(--ink-3, #78716c); font-style: italic; }

/* ── FIT: one ordered verdict, five rungs (round 10) ─────────────────────────
   ★ READ BY WEIGHT AND BY POSITION, NOT BY HUE, for the same reason the odds ladder
   is: this column reports a judgement, and a red-to-green ramp would turn "Long shot"
   into an error state. The top rung gets the page's own confirmed green and real
   weight; the middle rung is plain ink; "Long shot" and "Not a match" recede. The
   ladder is legible in greyscale because the WEIGHT falls monotonically with it.
   ⚠ The verdict lives in the `> span`, which a locked cell blurs. Anything that must
   stay readable while the value is fogged has to sit OUTSIDE it — see `.exp-fit__on`. */
.explore-table td.td-insider--fit { line-height: 1.2; }
.explore-table td.td-insider--fit > span { font-size: 13.5px; }
.explore-table td.fit-band-4 > span { color: #1a5236; font-weight: 800; }
.explore-table td.fit-band-3 > span { color: #4a4238; font-weight: 700; }
.explore-table td.fit-band-2 > span { color: #6b6255; font-weight: 600; }
/* Bands 1 and 0 also carry `td-insider--nocontest` (muted + italic): neither is a
   rung on the ladder. "Unrated" is a stated non-value and "Not a match" is a closed
   door, and a reader must not be able to scan either as "the bottom of the scale". */
.explore-table td.fit-band-1 > span,
.explore-table td.fit-band-0 > span { font-weight: 600; }
/* ★★ ROUND 13 — `.exp-fit__on` IS RETIRED, and the rule is kept as its own record.
   It was the per-row confidence line ("on 3 of 5 answers"), and round 10's own comment
   named the reason it had to go: it is "a fact about the READER". A fact about the
   reader is true of every row at once, so it printed 25 identical lines under 25
   identical verdicts. It moved to the column header (`.th-note`), which is on screen
   whenever the column is, states it once, and can carry the route — which a per-row
   footnote could not without becoming 25 links. Nothing about round 10's blur ruling
   changed: the qualifier is still outside anything the paywall fogs.
   ⚠ The `:not(.exp-fit__on)` in the locked-value blur rule above is left in place: it
   costs nothing, and removing it is the kind of tidy-up that re-blurs the thing the next
   time somebody puts an unblurred span back in a fit cell. */

/* An unrated cell (no profile) KEEPS the help cursor, and the first draft of this rule
   removed it on the reasoning that its hover says something different from the
   catalogue's. That was wrong in the way this file keeps a lens for: the cursor is not
   a description of the message, it is the ONLY cue that a message exists at all, and
   this is the one cell on the row whose message the reader can act on. It inherits the
   `td-insider--absent` treatment unchanged; the class exists so the harness and the
   guard can tell the two absences apart. */
.explore-table td.td-insider--fitnone { cursor: help; }

/* ── Honest absence: the dash means "not published", not "not loaded". The cue
   is the cursor, not decoration — a column of flagged dashes would read as a
   column of errors, which is the impression this exists to prevent. */
.explore-table td.td-insider--absent {
    color: var(--ink-muted);
    cursor: help;
}
/* ★★ ROUND 13 — AND INSIDE A LOCKED BAND IT RECEDES BEHIND ITS BLURRED NEIGHBOURS.
   Measured on the round-12 build, anonymous: 58 insider cells carried a real value under
   `blur(4.5px)` and 36 carried a crisp dash, so the sharpest, most legible content in
   the paid band was the places with nothing in them — and a sharp mark pulls the eye
   past a soft one regardless of what either says. Blurring the dash is NOT the fix: fog
   is the page's claim that something is being withheld, and here nothing is.
   ⚠ Scoped to `is-veiled`, which `_cellFor` sets only while that row's band is locked.
   For a member every absence is at full contrast, because for them it IS the answer.

   ★★ ROUND 14 — THE RECEDE IS KEPT AND THE MECHANISM IS REPLACED, because `opacity`
   was buying salience with LEGIBILITY. Measured on the round-13 build, anonymous: the
   dash rendered at **1.49:1** — the single worst contrast on the surface, worse than
   the "FILTERS" title, on a mark that carries a real fact ("this program does not
   publish it"). Round 13's argument was RELATIVE ("a sharp mark pulls the eye past a
   soft one"), so it is satisfied by a relative step, and a relative step does not have
   to start below the floor. The veiled dash is now a compliant ink that is still
   visibly quieter than the same dash for a member — and the step is taken on TWO
   channels, because colour alone cannot buy much recede once both ends must clear 4.5:
       member (full contrast)  var(--ink-muted) w600   5.4:1
       veiled                  #776e60          w400   4.7:1
   Weight does most of the work now, and it costs no legibility at all. The harness
   contract was rewritten, never deleted: it asserts the STEP (veiled is both lighter
   and lighter-weight than unveiled) AND the floor, so neither half can be lost. */
.explore-table td.td-insider--absent.is-veiled > span {
    color: #776e60;
    font-weight: 400;
}
.row-preview-tag {
    display: inline-block;
    font: 700 9px/1 var(--explore-font-body);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #2c5c3f;
    background: rgba(44, 92, 63, 0.1);
    border-radius: 999px;
    padding: 3px 8px;
    margin-top: 5px;
}

/* ── Insider band header (round 3) ──────────────────────────────────────────
   The floating unlock pill is GONE. It was fixed-position and had already been
   moved twice to stop it covering something interactive (insider cells, then
   three live rail filter options). This is the same offer, in flow, spanning
   exactly the columns it sells — so it labels the band, scrolls with it, and
   cannot cover a control at any scroll position. */
.ins-band-row th {
    background: var(--cream-dark);
    border-bottom: 0;
    padding: 0;
    cursor: default;
}
.ins-band-row .ins-band-spacer { background: var(--cream-dark); position: relative; }
/* Round 4: the free half is NAMED, not blank — "public record" is both true and the
   thing that makes the Win Layer legible as the part nobody else publishes. Absolute
   so it can never add height to the band (the one surface whose job is rows above
   the fold), and suppressed under 1200px where the offer needs the whole strip. */
.ins-band-free {
    position: absolute;
    left: 14px;
    top: 50%;
    transform: translateY(-50%);
    font: 600 10px/1 var(--explore-font-body);
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--ink-muted);
    white-space: nowrap;
    pointer-events: none;
}
@media (max-width: 1199px) { .ins-band-free { display: none; } }
/* ⚠ [hidden] is silently beaten by ANY rule that sets display (documented gotcha),
   and the band line styles its spans. State it, or the emptied element becomes an
   invisible inline box holding the composition stats off their left edge. */
#total-count[hidden] { display: none; }
.ins-band-row .ins-band-head {
    background: #efe6d6;
    padding: 0;
    height: 30px;
    position: relative;
    overflow: visible;
    /* O13: the band's left edge is a real seam between free and premium data. */
    border-left: 1px solid rgba(138, 109, 59, 0.34);
}
.ins-band-in {
    /* ★ Round 4 STRUCTURAL GUARD: this box is absolutely positioned and was free to
       run left out of its own cell — measured 63px of "THE WIN LAYER" printed over
       the free columns the band exists to be distinguished from. Constrained to the
       band, it now degrades by truncating its own label instead of trespassing, and
       the harness asserts the label's left edge never precedes the band's. */
    /* ★★ ROUND 5A: anchored LEFT, not right. The band spans the insider columns,
       and the pane now scrolls, so at 1440 with the rail open the right-hand ~113px
       of the table is off the visible box at rest — which is exactly where a
       right-anchored band put its NAME and its one offer control. Measured: the
       "Unlock N more" button was clipped by the pane's right edge on first paint.
       Anchoring at the seam puts both at the boundary they are about, keeps them
       visible at scrollLeft 0 in every configuration, and makes the round-4 defect
       (the label printing over the FREE half) structurally impossible rather than
       merely constrained — the box now grows away from the free columns. */
    max-width: calc(100% - 20px);
    overflow: hidden;
    position: absolute;
    left: 14px;
    top: 50%;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    gap: 10px;
    white-space: nowrap;
}
.ins-band-head__label {
    font: 600 10px/1 var(--explore-font-body);
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--amber-ink);
    /* ★ ROUND 14 — `opacity: 0.85` was here and it took the band's own name to
       3.62:1. Quietness belongs in the weight and the size, both of which this rule
       already sets; opacity is the one lever that cannot be spent without spending
       legibility with it. */
}
/* ★ ROUND 9 — a member's band says what they own. It sits where the "Unlock N more"
   button sits for everyone else and is deliberately NOT a button: the same slot, a
   receipt instead of an offer. Quiet by construction — it must never out-weigh the
   band's own name, and it is the last thing that should compete with a row. */
.ins-band-owned {
    font: 500 10px/1 var(--explore-font-body);
    letter-spacing: 0.04em;
    color: var(--amber-ink);
    /* ★ ROUND 14 — was `opacity: 0.72` (2.65:1). The receipt still loses to the band's
       name on WEIGHT (500 against 600) and on letter-spacing, which is the difference
       the round-9 note actually asked for. */
    white-space: nowrap;
}
/* Same reasoning as `.ins-band-free`: under 1200px the band cannot seat a name and
   a second phrase without one of them printing over the free columns (the round-4
   overhang defect). The name wins; the receipt is not load-bearing. */
@media (max-width: 1199px) { .ins-band-owned { display: none; } }
.ins-band-cta {
    font: 600 11px/1 var(--explore-font-body);
    letter-spacing: 0;
    text-transform: none;
    color: #6b4f1d;
    background: rgba(255, 255, 255, 0.72);
    border: 1px solid rgba(138, 109, 59, 0.3);
    border-radius: 999px;
    padding: 5px 11px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
}
/* ★ 2026-10-09 — the band's contents degrade to fit the strip that is actually on
   screen (`_fitInsiderBand` in explore.src.js picks ONE of these). Content shrinks;
   the box never moves left of the seam. */
.ins-band-in--short .ins-band-more,
.ins-band-in--solo .ins-band-head__label,
.ins-band-in--bare .ins-band-owned { display: none; }
.ins-band-cta:hover { background: #fff; border-color: rgba(138, 109, 59, 0.6); color: #4a3610; }
.ins-band-cta:focus-visible { outline: 2px solid #a86a1f; outline-offset: 2px; }
/* ★★ The FETCH-FAILED band (2026-08-07). Not a sales strip and not a receipt: when the
   premium payload could not be loaded we do not know whether this reader is entitled, so
   the band states the failure instead of asserting either. Deliberately NOT the gold of the
   offer band — a warm sales colour on an error reads as another pitch. It borrows the
   page's muted ink so it reads as a status, which is what it is. */
/* ★★ A STICKY HEADER MAY NOT BE SEMI-TRANSPARENT — the rows scroll THROUGH it (Khalid,
   2026-09-04). These two states are the only ones on the band that carried an alpha, and
   `--owned` is the SUBSCRIBER state, so the one reader who sees it is the one who paid: their
   header read "THE WIN LAYER · UNLOCKED ON YOUR SUBSCRIPTION" with row 1's values printed
   through it. ⚠ Neither an anonymous nor a free-tier probe can reach this — the default state
   is fully opaque, which is why every check passed.
   Both are now the SAME COLOUR composited over the pane behind them (rgb(255,252,247)), so the
   band looks identical at rest and simply stops being see-through:
     rgba(239,230,214,.55) over rgb(255,252,247) -> rgb(246,240,229)
     rgba(233,229,222,.70) over rgb(255,252,247) -> rgb(240,236,229)  */
.ins-band-row--stalled .ins-band-head { background: rgb(240, 236, 229); }
.ins-band-retry {
    font: 600 11px/1 var(--explore-font-body);
    letter-spacing: 0;
    text-transform: none;
    color: #4a4640;
    background: rgba(255, 255, 255, 0.85);
    border: 1px solid rgba(74, 70, 64, 0.35);
    border-radius: 999px;
    /* 5px/11px matches .ins-band-cta so the band's vertical rhythm is unchanged between
       states — a taller control here would shift every row on a failure. */
    padding: 5px 11px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
}
.ins-band-retry:hover { background: #fff; border-color: rgba(74, 70, 64, 0.6); color: #2b2926; }
.ins-band-retry:focus-visible { outline: 2px solid #4a4640; outline-offset: 2px; }
.ins-band-retry[disabled] { opacity: 0.65; cursor: default; }

/* Premium members: the band is a section label, not a sales strip. */
.ins-band-row--owned .ins-band-head { background: rgb(246, 240, 229); }

/* ── Sticky label strip — DELETED in round 5A ────────────────────────────────
   `.explore-stl` / `#explore-sticky-labels` was a JS-synced CLONE of the header
   labels, fixed under the filter bar and translated by the pane's scrollLeft. It
   existed for exactly one reason: in h-scroll mode the real <thead> could not
   stick, because the wrap was a scroll container whose own top scrolled off the
   page. Round 5A bounds the pane to the viewport and gives it vertical scroll, so
   the real header pins to the top of the pane in every configuration. A second
   copy of the header is a second surface to keep true; it is gone, not hidden. */

/* ── Columns picker ── */
.explore-cols {
    position: relative;
    display: inline-block;
}
.explore-cols__btn {
    font: 600 12.5px/1 var(--explore-font-body);
    padding: 9px 14px;
    border: 1.5px solid rgba(26, 23, 20, 0.16);
    border-radius: 9px;
    background: var(--paper);
    color: var(--ink-secondary);
    display: inline-flex;
    gap: 7px;
    align-items: center;
    cursor: pointer;
}
.explore-cols__btn:hover { border-color: rgba(26, 23, 20, 0.32); }
.explore-cols__count {
    background: var(--ink, #1a1714);
    color: #fff;
    border-radius: 99px;
    padding: 2px 7px;
    font-size: 10.5px;
}
/* ★★ ROUND 6b — A MENU IS BOUNDED BY THE VIEWPORT, BY CONSTRUCTION.
   Measured before the fix, at 1440x900: this popover computed `overflow-y: visible`
   and `max-height: none`, rendered 886px tall from y205, and put its bottom edge at
   y1091 — 191px below the fold, with the last four column rows and the whole Reset
   control unreachable. Identical at 1280x800 and at 1440x700, because the height was
   a property of the CONTENT, not of the screen. And the wheel chained: a box that
   cannot scroll hands the event to its ancestor, so trying to scroll the menu
   scrolled the PAGE — which is what the reader reported.
   `max-height` is computed in JS (`_fitMenu`) from the room below the anchor, because
   an absolutely-positioned box does not know where it starts; `overscroll-behavior`
   is the half that stops the chaining and it belongs here. Both declarations are
   shared with `.explore-sort__pop`, which is why the Sort control added in the same
   round was not born with the same defect. */
.explore-cols__pop,
.explore-sort__pop {
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
}
.explore-cols__pop {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 60;
    width: 252px;
    background: #fff;
    border: 1px solid rgba(26, 23, 20, 0.1);
    border-radius: 12px;
    box-shadow: 0 12px 34px rgba(26, 23, 20, 0.16);
    padding: 8px 6px;
    font: 13px/1.4 var(--explore-font-body);
    color: var(--ink-secondary);
}
.explore-cols__pop[hidden] { display: none; }

/* The scroll-range spacer that makes the clipped pane reachable — see
   `_reserveGridForMenu`. Height 1px and no ink: it exists to extend `scrollWidth`,
   nothing else, and it is removed the moment the menu closes. */
.exp-reserve-pad { height: 1px; pointer-events: none; }

/* ★★ 2026-08-06 — THE COLUMN THE READER JUST SWITCHED ON MARKS ITSELF.
   The pane is clipped out from under the open panel (`_reserveGridForMenu`) and the
   toggled column is scrolled into what is left (`_revealColumn`); this is the third
   part — without it the column arrives correctly and silently, in a grid of eleven
   near-identical columns, which is the same "did that do anything" the whole item is
   about. Brief and low: a tint plus a rule on the header, never a border (a border on
   a <td> shifts every cell in the column by its own width). */
.explore-table th.exp-col-lit,
.explore-table td.exp-col-lit {
    background-color: rgba(45, 106, 79, 0.075);
    animation: expColLit 1.8s ease-out 1 forwards;
}
.explore-table th.exp-col-lit { box-shadow: inset 0 -2px 0 0 var(--accent-forest, #2d6a4f); }
@keyframes expColLit {
    0%   { background-color: rgba(45, 106, 79, 0.16); }
    100% { background-color: rgba(45, 106, 79, 0.00); }
}
@media (prefers-reduced-motion: reduce) {
    .explore-table th.exp-col-lit,
    .explore-table td.exp-col-lit { animation: none; background-color: rgba(45, 106, 79, 0.09); }
}
.explore-cols__row:focus-visible,
.explore-cols__resetbtn:focus-visible { outline: 2px solid var(--accent-sienna, #a8562d); outline-offset: -2px; }

/* ── Sort control (round 6b) ─────────────────────────────────────────────────
   Same component family as Columns, deliberately: they sit side by side in the meta
   row and answer the two questions a grid reader has about the grid itself ("in what
   order?" then "showing what?"). Sharing the shell is also what makes the bounded-menu
   fix above cover both. */
.explore-sort { position: relative; display: inline-block; }
/* ⚠ 94px IS A HARD CEILING AT 1440, MEASURED: past it the view-chip row wraps to a
   second line and costs two result rows of fold. 80px leaves ~15% of that budget in
   hand, which is the standing clearance rule on this surface. Do not put the current
   ordering back into this button without re-measuring the chip row. */
.explore-sort__btn {
    font: 600 12.5px/1 var(--explore-font-body);
    padding: 9px 11px;
    border: 1.5px solid rgba(26, 23, 20, 0.16);
    border-radius: 9px;
    background: var(--paper);
    color: var(--ink-secondary);
    display: inline-flex;
    gap: 5px;
    align-items: center;
    cursor: pointer;
    white-space: nowrap;
}
.explore-sort__btn:hover { border-color: rgba(26, 23, 20, 0.32); }
.explore-sort__btn:focus-visible { outline: 2px solid var(--accent-sienna, #a8562d); outline-offset: 2px; }
.explore-sort__lbl { color: var(--ink-secondary); font-weight: 600; }
/* A column sort has taken over from the smart views: no chip is lit, so the control
   that DID it has to look active. The header's own caret says which column. */
.explore-sort__btn.is-on {
    border-color: var(--ink, #1a1714);
    background: var(--ink, #1a1714);
    color: #fff;
}
.explore-sort__btn.is-on .explore-sort__lbl { color: #fff; }
.explore-sort__pop {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 60;
    width: 292px;
    background: #fff;
    border: 1px solid rgba(26, 23, 20, 0.1);
    border-radius: 12px;
    box-shadow: 0 12px 34px rgba(26, 23, 20, 0.16);
    padding: 8px 6px;
    font: 13px/1.4 var(--explore-font-body);
    color: var(--ink-secondary);
}
.explore-sort__pop[hidden] { display: none; }
.explore-sort__head,
.explore-sort__foot {
    margin: 2px 10px 8px;
    font-size: 11.5px;
    line-height: 1.4;
    color: var(--ink-muted, #8a8178);
}
.explore-sort__foot { margin: 8px 10px 4px; padding-top: 9px; border-top: 1px solid rgba(26, 23, 20, 0.08); }
.explore-sort__head strong { color: var(--ink, #1a1714); font-weight: 700; }
.explore-sort__row {
    display: flex;
    align-items: baseline;
    gap: 9px;
    width: 100%;
    padding: 7px 10px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
}
button.explore-sort__row:hover { background: rgba(26, 23, 20, 0.04); }
.explore-sort__row--on { flex-direction: column; align-items: stretch; gap: 6px; background: rgba(239, 230, 214, 0.45); cursor: default; }
.explore-sort__name { flex: 1; min-width: 0; color: var(--ink, #1a1714); font-weight: 600; }
.explore-sort__dir { font-size: 11px; color: var(--ink-muted, #8a8178); white-space: nowrap; }
/* ★★ ROUND 13 — the Fit row while the view is ALREADY the fit ordering. Measured: it
   returned the identical first 60 rows. The row stays live (pressing it does something
   visible — the button goes `is-on`, the header takes the caret, the descending arm
   opens), so this is a demotion, never a disable: a greyed-out control that still fires
   is the worst of both, and a removed one reports as "nobody wanted it". */
.explore-sort__row--inert .explore-sort__name { color: var(--ink-secondary, #57534e); font-weight: 500; }
.explore-sort__row--inert .explore-sort__dir { font-style: italic; }
.explore-sort__pick {
    display: flex; width: 100%; padding: 0; border: 0; background: transparent;
    font: inherit; color: inherit; text-align: left; cursor: default;
}
/* The direction pair is the "exposes direction" half of the control: two real
   buttons, each stating what it does, with `aria-pressed` carrying the state. */
.explore-sort__seg { display: flex; gap: 5px; }
.explore-sort__dirbtn {
    flex: 1 1 0;
    min-width: 0;
    appearance: none;
    font: 600 10.5px/1.25 var(--explore-font-body);
    padding: 6px 7px;
    border: 1px solid rgba(26, 23, 20, 0.16);
    border-radius: 7px;
    background: var(--paper, #fdfbf7);
    color: var(--ink-secondary);
    cursor: pointer;
}
.explore-sort__dirbtn.is-on { background: var(--ink, #1a1714); border-color: var(--ink, #1a1714); color: #fff; }
.explore-sort__dirbtn:hover { border-color: rgba(26, 23, 20, 0.34); }
.explore-sort__row:focus-visible,
.explore-sort__pick:focus-visible,
.explore-sort__dirbtn:focus-visible,
.explore-sort__clear:focus-visible { outline: 2px solid var(--accent-sienna, #a8562d); outline-offset: 1px; }
.explore-sort__clear {
    appearance: none;
    font: 600 11.5px/1 var(--explore-font-body);
    padding: 7px 10px;
    border: 1px solid rgba(26, 23, 20, 0.18);
    border-radius: 7px;
    background: var(--paper, #fdfbf7);
    color: var(--ink-secondary);
    cursor: pointer;
}
.explore-sort__clear:hover { border-color: rgba(26, 23, 20, 0.36); color: var(--ink); }
/* ★ The sorted header is a keyboard stop (one roving `tabindex="0"` across the whole
   thead — see `_syncHeaderCursor`). It had no focus style at all, so a keyboard user
   walking the headers had no idea where they were. */
#explore-table-wrap thead th[data-sort]:focus-visible {
    outline: 2px solid var(--accent-sienna, #a8562d);
    outline-offset: -2px;
}
/* ★★ ROUND 8 — the `display: none` that used to be here is GONE. It, plus the two
   `isMobile` bails in explore.src.js, were the whole reason a phone reader could not
   order 698 programs at all. The control is a popover menu in a row that renders at
   every width, so nothing about it was desktop-specific except this rule. What it
   gets on a phone: room for a touch target, and a popover that cannot outgrow the
   viewport it opens in (`_fitMenu` already clamps the height; this clamps the width,
   which only ever mattered below ~420px). */
@media (max-width: 1023px) {
    .explore-sort__btn { min-height: 44px; }
    .explore-sort__pop { max-width: calc(100vw - 28px); }
    .explore-sort__row,
    .explore-sort__pick { min-height: 44px; }
}
.explore-cols__row {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 7px 10px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
}
.explore-cols__row:hover { background: rgba(26, 23, 20, 0.04); }
.explore-cols__row--insider { background: rgba(239, 230, 214, 0.35); }
.explore-cols__row--insider:hover { background: rgba(239, 230, 214, 0.6); }
/* ★ ROUND 9 — the menu's two section heads, in the table's own vocabulary. Sized and
   coloured as a LABEL, never as a row: the reader must never try to click one, and
   at 10px/uppercase/faint it cannot be mistaken for a togglable line. The Win Layer
   head carries the divider (a rule ABOVE it), because the boundary and its name are
   one statement and a separate `<hr>` would be the same fact drawn twice. */
.explore-cols__group {
    font: 700 9.5px/1 var(--explore-font-body);
    letter-spacing: 0.10em;
    text-transform: uppercase;
    color: var(--ink-muted);
    padding: 12px 12px 6px;
    user-select: none;
}
.explore-cols__group:first-child { padding-top: 8px; }
.explore-cols__group--insider {
    margin-top: 6px;
    border-top: 1px solid rgba(26, 23, 20, 0.10);
    color: var(--amber-ink);
}
.explore-cols__row[disabled] { opacity: 0.5; cursor: default; }
.explore-cols__check {
    width: 16px;
    height: 16px;
    flex: 0 0 16px;
    border-radius: 5px;
    border: 1.5px solid #c9c2b4;
    background: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.explore-cols__row.is-on .explore-cols__check {
    border-color: var(--ink, #1a1714);
    background: var(--ink, #1a1714);
}
.explore-cols__label { flex: 1; min-width: 0; }
.explore-cols__pro {
    font: 700 9.5px/1 var(--explore-font-body);
    letter-spacing: 0.06em;
    color: var(--amber-ink);
    background: #efe6d6;
    padding: 3px 7px;
    border-radius: 999px;
    display: inline-flex;
    gap: 4px;
    align-items: center;
}
.explore-cols__foot {
    margin: 8px 10px 4px;
    padding-top: 10px;
    border-top: 1px solid rgba(26, 23, 20, 0.08);
    font-size: 12px;
    color: var(--ink-muted);
}

/* ── "Worth watching" divider — non-actionable programs sink below it ── */
.explore-watch-divider > td {
    background: var(--cream, #f6f1e7);
    color: var(--ink-muted);
    font: 600 11.5px/1 var(--explore-font-body);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 9px 16px;
    border-top: 1px solid rgba(26, 23, 20, 0.08);
    border-bottom: 1px solid rgba(26, 23, 20, 0.06);
}
.explore-watch-divider .watch-sub {
    text-transform: none;
    letter-spacing: 0;
    font-weight: 500;
    font-size: 12px;
    margin-left: 8px;
}

/* ── Detail-panel springboard action bar ── */
.explore-detail__actions {
    display: flex;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
    padding: 14px 22px;
    margin: 18px -22px -18px;
    background: var(--ink, #1a1714);
    border-radius: 0 0 14px 14px;
}
.explore-detail__actions .eda-btn {
    font: 600 13.5px/1 var(--explore-font-body);
    padding: 11px 18px;
    border-radius: 9px;
    border: 1.5px solid transparent;
    cursor: pointer;
    min-height: 44px;
}
.explore-detail__actions .eda-btn--primary {
    background: var(--accent-sienna, #C4532B);
    color: #fff;
    border-color: var(--accent-sienna, #C4532B);
}
.explore-detail__actions .eda-btn--ghost {
    background: transparent;
    color: #fdfbf7;
    border-color: rgba(253, 251, 247, 0.35);
}
.explore-detail__actions .eda-btn--ghost:hover { border-color: rgba(253, 251, 247, 0.7); }
.explore-detail__actions .eda-spacer { flex: 1; }
.explore-detail__actions .eda-official {
    font: 500 13px/1 var(--explore-font-body);
    color: rgba(253, 251, 247, 0.78);
    text-decoration: none;
    padding: 10px 0;
}
.explore-detail__actions .eda-official:hover { color: #fff; }
@media (max-width: 640px) {
    .explore-detail__actions { margin: 18px -16px -16px; padding: 12px 16px; }
}

/* ── Zero-results recovery ── */
.explore-zero-recovery { padding: 26px 0 10px; }
.explore-zero-recovery__head {
    font: 600 17px/1.35 var(--explore-font-body);
    color: var(--ink);
    margin: 0 0 4px;
}
.explore-zero-recovery__sub {
    font: 13.5px/1.5 var(--explore-font-body);
    color: var(--ink-muted);
    margin: 0 0 16px;
}
.explore-zero-recovery__sub a { color: var(--accent-sienna); cursor: pointer; }
.explore-zero-recovery__row {
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 15px 18px;
    border: 1px solid rgba(26, 23, 20, 0.08);
    border-radius: 12px;
    margin-bottom: 10px;
    background: var(--paper);
}
.explore-zero-recovery__main { flex: 1; min-width: 0; }
.explore-zero-recovery__title {
    font: 600 14.5px/1.35 var(--explore-font-display, Georgia, serif);
    color: var(--ink);
}
.explore-zero-recovery__tag {
    font: 13px/1.45 var(--explore-font-body);
    color: var(--ink-muted);
    margin-top: 3px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.explore-zero-recovery__amt {
    font: 700 14px/1.3 var(--explore-font-display, Georgia, serif);
    color: var(--ink);
    white-space: nowrap;
}
.explore-zero-recovery__view {
    font: 600 12.5px/1 var(--explore-font-body);
    padding: 9px 14px;
    border-radius: 8px;
    border: 1.5px solid var(--ink);
    background: transparent;
    color: var(--ink);
    cursor: pointer;
    min-height: 36px;
}
.explore-zero-recovery__foot {
    font: 13px/1.5 var(--explore-font-body);
    color: var(--ink-muted);
    margin-top: 14px;
}
.explore-zero-recovery__foot a { color: var(--accent-sienna); font-weight: 600; }
@media (max-width: 640px) {
    .explore-zero-recovery__row { flex-wrap: wrap; gap: 10px; }
}

.explore-table__cell--status {
    font-size: 12.5px;
    color: var(--ink-secondary);
    white-space: nowrap;
}

/* ── Phase 2 (2026-08-04): the elements the phase-1 classes did not yet cover.
   Both blocks render ANCHORS inside button-shaped classes, and an <a> brings its
   own display, underline and inherited link colour — the sitewide
   `a:hover { color: var(--sienna) }` rule reaches into both. State every property
   on the element that owns it rather than relying on what a <button> defaults to. */
.explore-detail__actions .eda-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    text-decoration: none;
    white-space: nowrap;
}
.explore-detail__actions a.eda-btn--primary,
.explore-detail__actions a.eda-btn--primary:hover,
.explore-detail__actions a.eda-btn--primary:focus,
.explore-detail__actions a.eda-btn--primary:visited,
.explore-detail__actions a.eda-btn--primary:active { color: #fff; }
.explore-detail__actions a.eda-btn--ghost,
.explore-detail__actions a.eda-btn--ghost:hover,
.explore-detail__actions a.eda-btn--ghost:focus,
.explore-detail__actions a.eda-btn--ghost:visited,
.explore-detail__actions a.eda-btn--ghost:active { color: #fdfbf7; }
.explore-detail__actions a.eda-official,
.explore-detail__actions a.eda-official:visited { color: rgba(253, 251, 247, 0.78); }
.explore-detail__actions .eda-btn:focus-visible,
.explore-detail__actions .eda-official:focus-visible {
    outline: 2px solid #fdfbf7;
    outline-offset: 2px;
}
/* The bar is the last element inside the panel; it must not inherit the panel's
   bottom padding gap when the panel is the mobile sheet's only child. */
.explore-detail__actions:last-child { margin-bottom: -18px; }

.explore-zero-recovery__main { display: block; }
.explore-zero-recovery__title,
.explore-zero-recovery__tag { display: block; }
.explore-zero-recovery__row[role="link"] { cursor: pointer; }
.explore-zero-recovery__row[role="link"]:hover { border-color: rgba(26, 23, 20, 0.22); }
a.explore-zero-recovery__view {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    flex: 0 0 auto;
}
a.explore-zero-recovery__view,
a.explore-zero-recovery__view:hover,
a.explore-zero-recovery__view:focus,
a.explore-zero-recovery__view:visited,
a.explore-zero-recovery__view:active { color: var(--ink); }
a.explore-zero-recovery__view:hover { background: var(--ink); color: var(--paper); }
.explore-zero-recovery__foot a { cursor: pointer; text-decoration: none; }
.explore-zero-recovery__foot a:hover { text-decoration: underline; }
.explore-detail__actions .eda-official {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
}

/* ── `--exp-wrap-w`: the width of the pane's VISIBLE box, published by
   _syncWrapWidthVar() on every render + resize.
   ⚠ ROUND 5B DELETION. Two rules lived here that pinned the inline detail panel
   (`.explore-table-wrap .explore-detail-row > td > .explore-detail`, plus a
   contained-instead-of-full-bleed override for its action bar). Both existed for
   ONE reason: a `colspan` `<td>` inside a horizontally scrolling pane is as wide
   as the TABLE, not as wide as what the reader can see, so the panel's right-hand
   controls rendered past the visible edge. The record is a right-side drawer now
   and the problem is gone with the geometry that caused it — the pin is not
   "kept for safety", it addressed a `<tr>` that no longer exists.
   The ghost row and the recognition banner are NOT expansions; they are genuine
   full-width table rows, they have the same problem, and they keep the fix. */
@media (min-width: 1024px) {
/* Same class of defect as the deleted panel rules (caught by ui-stress-test
   2026-08-04): the ghost row and the recognition banner span the FULL table td
   in h-scroll mode, so their right-anchored controls (quiz CTA, dismiss ×,
   order toggle) rendered past the visible edge at x≈1458-1666 against a
   1348px box — the page's flagship conversion CTA was invisible until the
   user happened to scroll sideways. Pin their CONTENT to the visible box. */
.explore-table-wrap .ghost-row,
.explore-table-wrap .exp-recog {
    position: sticky;
    left: 0;
    width: var(--exp-wrap-w, 100%);
    max-width: 100%;
    box-sizing: border-box;
}
/* ★ ROUND 9 — the promo moved OUT of the scroll pane (see `_injectGhostRow`), so it
   no longer needs the sticky pin above: it is a page-level block in the main column
   and is already exactly as wide as the canvas. Rounded, because inside the table it
   inherited the pane's edges and out here it is its own object. */
.ghost-row--below {
    width: 100%;
    box-sizing: border-box;
    margin: 14px 0 4px;
    border-radius: var(--explore-radius, 10px);
    border: 1px solid rgba(26, 23, 20, 0.08);
    border-left: 3px solid #2D6A4F;
}
}   /* /min-width: 1024px */

/* Touch-target floor on touch layouts (ui-stress-test 2026-08-04): the ghost
   row's dismiss was 20px and its playbook alt-link a 13px-tall line — both
   pre-existing, both on the rebuilt surface's hit paths. (The pagination
   numbers that used to head this list are gone with pagination itself; the
   Show-more control is 44px tall at every width by its own rule.) */
@media (max-width: 1023px) {
    .explore-more__btn { width: 100%; max-width: 340px; }
    .ghost-row__cta { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
    .ghost-row__dismiss {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    .ghost-row__alt-link { display: inline-block; padding: 12px 4px; margin: -8px 0; }
}

/* ── Workbench shell: left facet rail + canvas (round 2, 2026-08-04) ──
   The rail is an alternate UI over the SAME filter selects — dropdowns hide the
   active slice, a rail keeps it visible and frames the table as a workbench
   (the Crunchbase-Pro pattern the reference names). <1280px: rail hides, the
   classic filter bar carries on unchanged. */
@media (min-width: 1280px) {
    .explore-shell {
        max-width: 1440px;
        margin: 0 auto;
        display: grid;
        grid-template-columns: 232px minmax(0, 1fr);
        gap: 22px;
        padding: 0 24px;
        align-items: start;
    }
    /* ★ min-width:0 is LOAD-BEARING, not tidiness. minmax(0,1fr) sizes the TRACK;
       the grid ITEM still defaults to min-width:auto = its min-content, and the
       insider table carries an inline min-width of ~1585px. Without this the main
       column rendered 1587px wide inside a 1138px track, overflow-x:auto never
       engaged (scrollWidth === clientWidth), body{overflow-x:clip} swallowed the
       remainder, and 4 of the 6 PREMIUM columns at 1440px (all 6 at 1280px) were
       painted off-screen with no scrollbar to reach them. Caught 2026-08-04 by
       explore-pro-qa's rail-mode reachability check. */
    .explore-shell .explore-main { max-width: none; min-width: 0; width: 100%; padding-left: 0; padding-right: 0; }
    .explore-rail {
        overscroll-behavior: contain;   /* the filter rail is a pane too — no chaining to the page */
        position: sticky;
        top: calc(var(--explore-navbar-height) + var(--explore-filters-stuck-h, 58px) + 14px);
        max-height: calc(100vh - var(--explore-navbar-height) - 90px);
        overflow-y: auto;
        padding: 4px 4px 24px 0;
        scrollbar-width: thin;
    }
    /* rail replaces the dropdown row */
    body.has-rail .explore-filters__dropdowns,
    body.has-rail .explore-filters__premium-group { display: none; }

    /* ── Grid alignment (round 3) ────────────────────────────────────────────
       One vertical grid for the whole workbench. The sticky bar's content box is
       made identical to the table's: same 1440 max-width, same 24px gutters, and
       a left pad of exactly the rail track + its gutter. So the views chips start
       on the table's left edge and the meta group ends on its right edge, with no
       arithmetic to keep in sync — both derive from the same three numbers as
       .explore-shell above. */
    body.has-rail .explore-filters { padding-left: 0; padding-right: 0; }
    body.has-rail .explore-filters__inner {
        max-width: 1440px;
        margin: 0 auto;
        box-sizing: border-box;
        padding-left: calc(24px + 232px + 22px);
        padding-right: 24px;
        flex-wrap: nowrap;
        gap: 14px;
    }
    body.has-rail #explore-views { flex: 1 1 auto; min-width: 0; margin: 0; }
    body.has-rail .explore-filters__meta { flex: 0 0 auto; margin-left: 0; }
    /* Chips share the bar with the meta group now — measured, the round-2 padding
       tipped the row onto a second line at 1280 and 1366 (a 44px tax on the fold
       at the two commonest laptop widths). */
    /* Chips wrap rather than ellipsise if a width ever runs out: a truncated
       control label ("Biggest awa…") is worse than a second line. Measured, none of
       1280/1366/1440 wraps anonymous, and only 1280 wraps with a profile. */
    body.has-rail .explore-views { gap: 6px; }
    body.has-rail .explore-views__chip { padding: 8px 8px; font-size: 12px; white-space: nowrap; }
    body.has-rail .explore-views__chip--watch { margin-left: 9px; }
    body.has-rail .explore-views__chip--watch::before { left: -9px; }
    /* A Reset that cannot reset anything is a permanent 69px of dead control. It
       already knew it was inactive (.is-active is toggled on it); now it says so by
       not being there. */
    body.has-rail .explore-filters__reset:not(.is-active) { display: none; }

    /* The rail column: search on top (outside the scroll area, so the
       autocomplete dropdown is not clipped by it), facets below. */
    body.has-rail .explore-rail {
        display: flex;
        flex-direction: column;
        overflow: visible;
    }
    /* flex column + max-height: every direct child needs flex-shrink:0 or the
       children compress instead of the body scrolling. */
    body.has-rail .explore-rail__head { flex: 0 0 auto; padding: 0 4px 14px 0; }
    body.has-rail .explore-rail__body {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        overscroll-behavior: contain;
        scrollbar-width: thin;
        padding-right: 4px;
    }
    body.has-rail .explore-filters__search { flex: none; width: 100%; min-width: 0; }
    /* The dropdown is wider than the rail on purpose — it overhangs the canvas
       rather than squeezing suggestions into 232px. */
    body.has-rail .explore-filters__search .explore-autocomplete { right: auto; width: 400px; }

    /* ⚠ DEAD GAP: .explore-main still reserved 36px for the overlapping stats
       card this rebuild deleted, on top of --space-lg. Both go — the first data
       row is what this page is for. */
    body.has-rail .explore-shell .explore-main { padding-top: 0; padding-bottom: var(--space-lg); }
    body.has-rail .explore-shell { padding-top: 14px; }
}
@media (max-width: 1279px) {
    .explore-rail { display: none; }
}

/* ══ Facet rail (round 4, item 3) ═══════════════════════════════════════════
   A facet rail on a data surface is INFORMATION, not navigation: the reader
   should be able to learn the catalogue's shape by looking at it. Counts on every
   option, proportional bars on the two facets whose distribution is actually
   lopsided (status 176x spread, type 26x — measured; the other four are 1.4-1.9x
   and would be ink asserting "no pattern here"), collapsible groups, checkbox
   multi-select.

   The column has to stay CALM while carrying ~30 numbers, so the hierarchy is
   deliberate: the label holds the weight, the count is muted and tabular so a
   column of them reads as one right edge rather than as text, and the bar is 3px
   under it. Row height ~27px, which is Linear/Crunchbase facet density. These are
   pointer targets on a >=1280px surface only — the rail is display:none below
   that, where the touch-sized <select> controls take over — which is why they are
   not sized to the 44px touch minimum. */
.explore-rail__group { margin-bottom: 14px; }
.rail-fx__headrow { display: flex; align-items: center; gap: 4px; }
.rail-fx__head {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1 1 auto;
    min-width: 0;
    padding: 5px 6px 5px 4px;
    border: 0;
    border-radius: 7px;
    background: transparent;
    text-align: left;
    cursor: pointer;
}
.rail-fx__head--static { cursor: default; }
.rail-fx__head:hover { background: rgba(26, 23, 20, 0.04); }
.rail-fx__head--static:hover { background: transparent; }
.rail-fx__head:focus-visible { outline: 2px solid var(--accent-sienna); outline-offset: 1px; }
.rail-fx__chev {
    flex: 0 0 9px;
    color: var(--ink-muted);
    transition: transform 140ms ease;
}
.rail-fx__head[aria-expanded="true"] .rail-fx__chev { transform: rotate(90deg); }
.explore-rail__label {
    font: 700 10.5px/1 var(--explore-font-body);
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-muted);
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* The active-count badge doubles as this facet's clear control, so a facet can be
   emptied without hunting down the values that are set. */
.rail-fx__n {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 2px;
    font: 700 10.5px/1 var(--explore-font-body);
    color: #1e4234;
    background: rgba(30, 66, 52, 0.11);
    border: 0;
    border-radius: 99px;
    padding: 4px 6px;
    cursor: pointer;
}
.rail-fx__n:hover { background: rgba(30, 66, 52, 0.2); }
.rail-fx__n:focus-visible { outline: 2px solid var(--accent-sienna); outline-offset: 1px; }
.rail-fx__x { font-weight: 500; opacity: 0.7; }
/* ⚠ The [hidden] attribute is silently overridden by ANY rule that sets display —
   documented class. State it explicitly, or a collapsed group becomes an invisible
   click-blocker instead of a collapsed group. */
.rail-fx__opts[hidden] { display: none; }
.rail-fx__opts { padding-top: 2px; }

.explore-rail__opt {
    display: flex;
    align-items: center;
    gap: 7px;
    width: 100%;
    padding: 5px 7px 5px 6px;
    border: 0;
    border-radius: 7px;
    background: transparent;
    font: 12.5px/1.3 var(--explore-font-body);
    color: var(--ink-secondary);
    text-align: left;
    cursor: pointer;
}
.explore-rail__opt:hover:not(:disabled) { background: rgba(26, 23, 20, 0.05); }
.explore-rail__opt:focus-visible { outline: 2px solid var(--accent-sienna); outline-offset: -1px; }
.explore-rail__opt.is-active { color: var(--ink); font-weight: 600; }
.rail-opt__box {
    flex: 0 0 13px;
    width: 13px; height: 13px;
    border: 1.5px solid rgba(26, 23, 20, 0.28);
    border-radius: 4px;
    background: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #fff;
}
.explore-rail__opt:hover:not(:disabled) .rail-opt__box { border-color: rgba(26, 23, 20, 0.5); }
.explore-rail__opt.is-active .rail-opt__box { background: #1e4234; border-color: #1e4234; }
.explore-rail__opt.is-active .rail-opt__box::after {
    content: '';
    width: 3.5px; height: 7px;
    margin-top: -1.5px;
    border: solid #fff;
    border-width: 0 1.8px 1.8px 0;
    transform: rotate(43deg);
}
.rail-opt__t {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rail-opt__bar {
    flex: 0 0 26px;
    height: 3px;
    border-radius: 2px;
    background: rgba(26, 23, 20, 0.08);
    overflow: hidden;
}
.rail-opt__bar > i {
    display: block;
    height: 100%;
    min-width: 2px;
    border-radius: 2px;
    background: rgba(30, 66, 52, 0.42);
}
.explore-rail__opt.is-active .rail-opt__bar > i { background: #1e4234; }
.rail-opt__n {
    flex: 0 0 auto;
    min-width: 26px;
    text-align: right;
    font: 11.5px/1 var(--explore-font-body);
    font-variant-numeric: tabular-nums lining-nums;
    color: var(--ink-muted);
}
.explore-rail__opt.is-active .rail-opt__n { color: #1e4234; }
/* An option that would return nothing stays VISIBLE and goes quiet: an empty facet
   is a true fact about the catalogue, and hiding it would make the rail's contents
   change under the reader for reasons they cannot see. */
.explore-rail__opt.is-empty { opacity: 0.4; cursor: default; }
.explore-rail__opt.is-empty .rail-opt__bar { visibility: hidden; }

.explore-rail__more {
    font: 600 11.5px/1 var(--explore-font-body);
    color: var(--accent-sienna);
    background: none; border: 0;
    padding: 6px 7px;
    cursor: pointer;
}
.explore-rail__more:hover { text-decoration: underline; }
.explore-rail__clear {
    font: 600 12.5px/1 var(--explore-font-body);
    color: var(--accent-sienna);
    background: none; border: 0; padding: 8px 6px; cursor: pointer;
}
.explore-rail__clear:hover { text-decoration: underline; }

/* ── The Win Layer group (round 4, item 5) ─────────────────────────────────
   Rail FURNITURE, not an offer block: no button, no price. Same palette as the
   table's Win Layer band, so a reader recognises one product in two places rather
   than meeting two pitches. */
.rail-fx--win {
    margin-top: 20px;
    padding-top: 12px;
    border-top: 1px solid rgba(138, 109, 59, 0.28);
}
.rail-fx--win .explore-rail__label { color: var(--amber-ink); }
.rail-fx--win .explore-rail__opt { align-items: flex-start; }
.rail-fx--win .rail-opt__box { margin-top: 1px; }
.rail-fx--win .rail-opt__t { white-space: normal; overflow: visible; line-height: 1.32; }
.rail-cap.is-locked .rail-opt__box {
    border-color: rgba(138, 109, 59, 0.4);
    background: rgba(239, 230, 214, 0.75);
    color: var(--amber-ink);
}
.rail-cap:hover:not(:disabled) { background: rgba(239, 230, 214, 0.5); }
.rail-cap.is-active .rail-opt__box { background: #8a6d3b; border-color: var(--amber-ink); }
.rail-cap.is-active .rail-opt__n { color: #6b4f1d; }
/* ── The locked capability is NOT a checkbox (round 12) ────────────────────
   It used to render the same 13px square `rail-opt__box` as the province and
   industry facets six rows above it, with the same right-aligned count — and
   pressing it went straight to Stripe. A control cannot look like a filter and
   behave like a purchase. The square is gone for free readers (the lock stands on
   its own) and a chevron says the row OPENS something. Members keep the box,
   because for them it really is a checkbox. */
.rail-cap__lock {
    flex: 0 0 auto; width: 12px; margin-top: 2px;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--amber-ink); opacity: .85;
}
/* ⚠ MEASURED, not tidied. "Approval odds above 20%" needs 158px and the row gave it
   exactly 158 before this round; the bare lock and the chevron's padding took it to
   151 and the label wrapped, adding 16px to a group that sits at the bottom of an
   already long rail. These three reclaim 9px (tighter gap, a 12px lock instead of
   13, and a count column sized for three digits rather than four) so the round is
   height-neutral. The counts here are 3-digit at most (max 397 over 748 records) and
   the figures are tabular, so 22px cannot clip one. */
.rail-cap.is-locked { gap: 5px; }
.rail-cap.is-locked .rail-opt__n { min-width: 22px; }
/* ⚠ ABSOLUTE, not a flex child, and that is a measurement rather than a taste. In
   flow the chevron took ~20px out of the label's line box and pushed THREE of the
   six labels onto a second line ("Approval odds above 20%", "Low or no clawback
   risk", "Five or fewer documents" all wrapped) — 3 extra lines of a rail whose
   whole job is fitting the facets above the fold. It now sits in the row's own
   right padding, which grew by 5px, so the group is the height it always was.
   Top-aligned because the group is `align-items: flex-start` and a vertically
   centred chevron on a two-line row points at nothing. */
.rail-cap.is-locked { position: relative; padding-right: 12px; }
.rail-cap__go {
    position: absolute; right: 1px; top: 7px; color: var(--amber-ink);
    display: inline-flex; align-items: center; transition: transform .12s ease, color .12s ease;
}
.rail-cap:hover .rail-cap__go, .rail-cap:focus-visible .rail-cap__go { color: var(--amber-ink); transform: translateX(2px); }
@media (prefers-reduced-motion: reduce) {
    .rail-cap__go { transition: none; }
    .rail-cap:hover .rail-cap__go, .rail-cap:focus-visible .rail-cap__go { transform: none; }
}
.rail-fx__foot {
    margin: 8px 6px 0;
    font: 11.5px/1.45 var(--explore-font-body);
    color: var(--ink-muted);
}

/* ── Smart views: one-click presets — founders shouldn't need to know what to
   filter for. Chips, not tabs: they compose with rail filters. ── */
.explore-views {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin: 2px 0 14px;
}
.explore-views__chip {
    font: 600 12.5px/1 var(--explore-font-body);
    padding: 9px 14px;
    border: 1.5px solid rgba(26, 23, 20, 0.14);
    border-radius: 999px;
    background: var(--paper);
    color: var(--ink-secondary);
    cursor: pointer;
    min-height: 36px;
}
.explore-views__chip:hover { border-color: rgba(26, 23, 20, 0.32); }
.explore-views__chip.is-active {
    background: var(--ink, #1a1714);
    border-color: var(--ink, #1a1714);
    color: #fdfbf7;
}
/* O15: the watch chip is a FILTER sitting in a row of ORDERINGS. It used to be
   pushed to the far edge with margin-left:auto, which (a) read as part of the
   results-meta cluster once the views moved into the sticky bar and (b) forced the
   chip row to wrap to two lines at 1366. A separator says "different kind of
   control" without spending a whole line to say it. */
/* ★★ ROUND 9 — THREE SIGNALS FOR ONE FACT. The chip carried a dashed border, a
   separator rule and a count, all saying "this one is not like the other four". The
   dashed border is the one that costs the most and carries the least: it makes the
   chip look provisional or disabled inside a segmented control whose other five are
   solid, and it says nothing the separator does not already say. Deleted.
   What stays, and why each earns its place: the `::before` rule is a DELIMITER
   between two kinds of control, not a variation of the chip; the count is
   INFORMATION (this chip filters to a subset, the others reorder the whole set, and
   the number is the only place that difference is legible). One deviation of
   appearance, one of content, each doing separate work. */
.explore-views__chip--watch {
    margin-left: 11px;
    position: relative;
}
.explore-views__chip--watch::before {
    content: '';
    position: absolute;
    left: -11px; top: 7px; bottom: 7px;
    width: 1px;
    background: rgba(26, 23, 20, 0.16);
}
.explore-views__chip--watch.is-active { background: #6b5d3f; border-color: #6b5d3f; }
@media (max-width: 1023px) { .explore-views { display: none; } }

/* ── Density pass (round 2) — its padding is SUPERSEDED by round 4B's 9px and the
   duplicate declaration is removed rather than left to lose the cascade silently:
   two rules setting the same property on the same selector is the exact debt class
   that makes a later edit to "the" padding rule change nothing. The clamp block
   stays here (round 4B narrows it to 2 lines further down). */
.explore-table__cell--deadline .dl-clamp {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Opus r2 #4: the expanded panel rendered every action twice — the springboard
   bar carries all four actions, so the legacy toolbar row hides (DOM stays: the
   bookmark-state sync + pursue hydration no-op safely against hidden nodes).
   Full panel recomposition (one endpoint, premium tiles) is the round-3 brief. */
.explore-detail .detail-toolbar { display: none; }

/* Opus r2 #10: once the filter bar sticks, its mobile "Filters" toggle was
   flipping visible NEXT TO the rail — two filter UIs at once. */
body.has-rail .explore-filters__toggle { display: none !important; }

/* ── Watch view header strip (round 3, O5) ─────────────────────────────────
   The view listed programs that are not open and stopped there. This says what
   we can do about it and carries the one action — wired to the LIVE notify-me →
   reopening-alerts path (see _syncWatchStrip's trace), with an email required,
   because an intent we cannot deliver on is worse than no button. */
.exp-watch {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    flex-wrap: wrap;
    margin: 0 0 12px;
    padding: 12px 16px;
    background: rgba(107, 93, 63, 0.07);
    border: 1px solid rgba(107, 93, 63, 0.2);
    border-radius: 10px;
}
.exp-watch__line {
    margin: 0;
    font: 13.5px/1.45 var(--explore-font-body);
    color: var(--ink-secondary);
    min-width: 0;
}
.exp-watch__line strong { color: var(--ink); font-weight: 700; }
.exp-watch__tick { color: #2c5c3f; font-weight: 700; margin-right: 6px; }
.exp-watch__act { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.exp-watch__btn {
    font: 600 12.5px/1 var(--explore-font-body);
    padding: 10px 16px;
    min-height: 40px;
    border: 0;
    border-radius: 8px;
    background: #6b5d3f;
    color: #fdfbf7;
    cursor: pointer;
    white-space: nowrap;
}
.exp-watch__btn:hover { background: #56492f; }
.exp-watch__btn:focus-visible { outline: 2px solid #1a1714; outline-offset: 2px; }
.exp-watch__form { display: flex; align-items: center; gap: 8px; }
.exp-watch__form[hidden] { display: none; }
.exp-watch__lbl {
    font: 600 11px/1 var(--explore-font-body);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-muted);
}
.exp-watch__input {
    font: 13px/1 var(--explore-font-body);
    padding: 10px 12px;
    min-height: 40px;
    width: 200px;
    border: 1px solid rgba(26, 23, 20, 0.2);
    border-radius: 8px;
    background: var(--paper);
    color: var(--ink);
}
.exp-watch__note { font: 12px/1.4 var(--explore-font-body); color: var(--ink-muted); }
@media (max-width: 640px) {
    .exp-watch { flex-direction: column; align-items: stretch; }
    .exp-watch__input { width: 100%; }
    .exp-watch__form { flex-wrap: wrap; }
}

/* ── The Win Layer manifest (round 4, items 8 + 9) ──────────────────────────
   Supersedes the round-3 stat tiles AND the paragraph above them. The tiles
   showed 2-3 real values and left the other five fields unnamed; the paragraph
   restated three of the same questions as prose. Both were arguing for the layer
   without ever naming what it holds.

   The card was capped at 300px inside a 479-568px sidebar — 268px of unused
   width while the content overflowed the box VERTICALLY by 28px and was being
   silently clipped at both ends (measured 2026-08-04, content 500px in a 472px
   box with overflow:hidden). Widening it is what buys the room for eight rows. */
.premium-overlay__content--wl {
    max-width: 520px;
    padding: var(--space-md) var(--space-lg);
}
.premium-overlay__content--wl .premium-overlay__lead {
    font-size: var(--text-sm);
    margin-bottom: var(--space-sm);
}
/* Two across: at ~200px per column each question is 2-3 short lines, which is
   the densest arrangement that still reads as sentences rather than as chips. */
.wl-manifest {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px 16px;
    width: 100%;
    margin: 0 0 var(--space-sm);
    padding: 0;
    list-style: none;
    text-align: left;
}
.wlm {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 3px;
}
/* The QUESTION leads and carries the reading weight — it is the part that
   argues. The field name under it is the anchor back to the table column. */
.wlm__q {
    font: 400 11px/1.36 var(--explore-font-body);
    color: var(--ink-secondary);
}
.wlm__f {
    display: flex;
    align-items: baseline;
    gap: 6px;
    flex-wrap: wrap;
    min-width: 0;
}
.wlm__label {
    font: 700 9.5px/1.2 var(--explore-font-body);
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--ink-muted);
}
.wlm__lock {
    display: inline-flex;
    color: var(--amber-ink);
    opacity: 0.8;
}
/* The open taste: a real value, on the two secondary fields only. */
.wlm__val {
    font: 600 11.5px/1.2 var(--explore-font-body);
    color: var(--accent-forest, #2d6a4f);
    overflow-wrap: anywhere;
}
/* Absence takes the whole line under the label rather than trailing it — it is a
   sentence, not a value, and squeezing it beside the label would hyphen-shred it. */
.wlm__absent {
    flex: 1 1 100%;
    font: 400 10px/1.34 var(--explore-font-body);
    color: var(--ink-muted);
}
/* ★ ROUND 13 — the Fit row's invitation is a real link now (it named an action and gave
   no way to take it). ⚠ Pinned across all five states: the sitewide `a:hover { color:
   var(--sienna) }` + underline rules repaint any control built from an anchor, which is
   the documented trap this file has hit before. */
.wlm__absent a,
.wlm__absent a:hover,
.wlm__absent a:focus,
.wlm__absent a:visited,
.wlm__absent a:active {
    color: #2c5c3f;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.wlm__absent a:focus-visible { outline: 2px solid var(--accent-sienna, #a8562d); outline-offset: 2px; }
.wlm--absent .wlm__q { color: var(--ink-muted); }
/* The doctrine half of the absence line, stated once per card. Set apart by a rule
   so it reads as the surface speaking, not as an eighth field. */
.wl-manifest__note {
    margin: -2px 0 var(--space-sm);
    padding-top: 7px;
    border-top: 1px solid rgba(26, 23, 20, 0.08);
    font: 500 10px/1.35 var(--explore-font-body);
    letter-spacing: 0.01em;
    color: var(--ink-muted);
    text-align: left;
}
/* ⚠ The card's own buttons were 38px tall — under the 44px touch minimum, on the
   mobile bottom sheet where they are the only way to buy. Pre-existing; caught by
   asserting the RENDERED geometry of the sheet rather than trusting that the phone
   reuses the desktop component. */
.premium-overlay__content--wl .premium-overlay__pricing .btn--upgrade { min-height: 44px; }
/* The two buttons sit side by side once the card is this wide; stacked they cost
   42px of the height the manifest needs. Below that they stack (base rule). */
@media (min-width: 640px) {
    .premium-overlay__content--wl .premium-overlay__pricing { flex-direction: row; }
    .premium-overlay__content--wl .premium-overlay__footnote { margin-bottom: 0; }
}


/* ── Round 3 polish batch ──────────────────────────────────────────────────
   O10: the sort caret was painted on every header at rest, so eleven columns
   each advertised an affordance and none of them stood out. It appears on hover
   and stays on the sorted column — which is the only one whose caret carries
   information. */
.explore-table th .sort-arrow { opacity: 0; transition: opacity 120ms ease; }
.explore-table th:hover .sort-arrow,
.explore-table th:focus-within .sort-arrow { opacity: 0.45; }
.explore-table th.is-sorted .sort-arrow { opacity: 1; }

/* O17: two fixed decorative circles sat BEHIND the table region — an arc
   crossing the rail and the first data rows on every desktop screenshot. They
   were background for a marketing hero; this page is a workbench now. */
@media (min-width: 1024px) {
    .explore-deco-circle { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   ROUND 4, ITEM 7 — UNIFORM, DENSER ROWS
   ══════════════════════════════════════════════════════════════════════════
   Measured before: rows 85-131px at 1440 anonymous, mean 100, VARIABLE, and only
   6 clearing the 900px fold (4 when a profile is present, because the Program
   column narrows and everything in it wraps one line further). Variable height is
   what made this read as a stack of cards rather than a grid.

   The fix is structural, not a padding trim. The Program cell now has exactly two
   slots and RESERVES the taller one:

     .exp-prog__head   the program name, clamped to two lines and given the height
                       of two lines whether it uses them or not
     .exp-prog__meta   one line: what kind of money it is + what it is for, then a
                       fixed tail (chips, the Details link)

   Reserving line 2 is the whole trick: a short title now costs exactly what a long
   one costs, so every scan row is the same height and the eye can lock a rhythm.
   It is affordable because most titles already need both lines — measured over all
   785 records at the rendered column width, only ~24% fit on one. */

/* ⚠ ROUND 13 PAYS FOR THE TITLE OUT OF THE ROW'S OWN PADDING (9px → 8px). The name went
   from 14px to 15px, which is +2.56px of reserved two-line height per row — measured as
   71.4px → 73.9px, and at 73.9 the eighth row falls below the 900px fold. "Rows above
   the fold" is the metric this whole rebuild has been buying, so the round gives the
   2px back here rather than spending it: 15px of name at 8px of padding lands the row
   back at ~71px, and the emphasis moves without the density moving. */
.explore-table td { padding-top: 8px; padding-bottom: 8px; }

.explore-table__cell--program .exp-prog { display: block; min-width: 0; }

/* Head: the title owns the full column width. `Details` deliberately does NOT sit
   here — it lives in the meta tail, where a fixed-basis flex item cannot push the
   title into a third line (round 3's finding, kept). */
.explore-table__cell--program .exp-prog__head {
    display: block;
    min-width: 0;
}
/* ★★ ROUND 13 — THE PROGRAM'S NAME LEADS ITS OWN ROW. Measured on the round-12 build,
   first row, every user state and every width:

     advertised ceiling   Fraunces 15px w600 rgb(26,23,20)
     the program's NAME   Fraunces 14px w500 rgb(92,85,73)
     realistic award      Plus Jakarta 12.5px w600 rgb(74,66,56)

   Nothing there is inaccurate; the type argued against the product 25 times a screen.
   The name of the thing was the third-loudest item in its own row, behind a number this
   page exists to qualify — and round 8 placed `Advertised | Realistic award` adjacent
   precisely so the CONTRAST would make the case for the subscription. Now: name first
   (15/600/ink), realistic award second (see `.td-insider--money`), advertised ceiling
   third (see `.exp-amt`). Nothing was hidden and nothing moved; only the emphasis. */
.explore-table__cell--program .program-title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: 15px;
    /* ⚠ 1.28 → 1.25 buys back the last of the height the size increase cost. At 15px
       that is 18.75px a line, which is still open for a display serif; the pair (size up,
       leading in) is what keeps the row at the round-12 height while the name reads
       first. ★ The `.exp-prog` reservation below restates BOTH numbers — change them
       together or the second title line stops being reserved and the row starts flexing
       under its own content again. */
    line-height: 1.25;
    color: var(--ink, #1c1917);
}
/* ★ RESERVE the two-line height on the BLOCK, not on the title. Without a
   reservation the row height tracks the title length and the grid flexes under its
   own content — the defect this round removes. Putting it on the title instead
   pushed the meta line to the bottom of an empty second line, so a one-line program
   showed a visible gap between its name and its description; on the block, the td's
   `vertical-align: middle` centres the pair and the slack goes to the margins where
   it reads as breathing room. Written as the same arithmetic the type uses, so a
   size change moves both together instead of silently unpinning them. */
/* ⚠ ROUND 13: the `14px` here IS the title's font-size, restated. It moved to 15px
   above, and this arithmetic has to move with it or the block under-reserves the second
   title line and the row height starts tracking the title again — the exact defect the
   reservation exists to remove, re-entering silently through a number nobody linked. */
.explore-table__cell--program .exp-prog {
    min-height: calc(2 * 1.25 * 15px + 1px + 1.35 * 11.5px);
}

/* One meta line, never two: `what` is the only flexible track and it ellipsizes,
   so nothing in the tail can ever wrap the row taller. */
.explore-table__cell--program .exp-prog__meta {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    margin-top: 1px;
    font-size: 11.5px;
    line-height: 1.35;
}
.explore-table__cell--program .exp-prog__what {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 500;
    color: var(--ink-3, #78716c);
}
/* A clipped description is a help cursor, not a decoration — the hover is added by
   `_syncTruncationTips()` only where the browser actually clipped. */
.explore-table__cell--program .exp-prog__what[data-tip] { cursor: help; }
.explore-table__cell--program .exp-prog__tail {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    max-width: 62%;
}
.explore-table__cell--program .exp-prog__tail:empty { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   ROUND 16 — SAVE, ON THE ROW ITSELF (`_rowSaveHtml`)
   ══════════════════════════════════════════════════════════════════════════
   Until now a row carried exactly one control ("Details") and saving a program
   meant opening its record and pressing Save in the drawer's footer: one modal
   round trip per mark, on a 743-row database whose native gesture is "skim, mark
   a handful, come back". Quiet at rest so it does not compete with the program
   name, and it fills in when it is on — a saved row should be legible from across
   the list, an unsaved one should be almost silent.
   ⚠ NOT hover-only. A hover-revealed control is invisible to a touch reader, and
   this layout is reachable by tablet at 1024-1279. */
.exp-rowsave {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    color: rgba(26, 23, 20, 0.30);
    cursor: pointer;
    border-radius: 5px;
    transition: color 120ms ease, background 120ms ease;
    /* ★★ THE 22px BOX MUST NOT SET THE META LINE'S HEIGHT. Measured by ablation on the
       first build of this control: the tail line went 15px -> 22px, which took the row
       set from {71 x 24, 72 x 1} to {71 x 11, 78 x 14} — a 7px spread across the grid,
       failing `comfortable rows stay UNIFORM`. Uniform row height is this surface's
       density contract, and it is what makes it read as a grid rather than a list of
       cards (round 4). The negative block margins let the control keep its 22px mark
       and its 22px hit area while contributing 14px to the line box, which is under
       the 15px that was already there — so it costs the layout nothing.
       ⚠ It overflows the LINE, not the cell: the Program cell's own padding absorbs
       3.5px on each side, asserted rather than assumed. */
    margin-top: -4px;
    margin-bottom: -4px;
}
.exp-rowsave:hover { color: var(--accent-forest, #2D6A4F); background: rgba(45, 106, 79, 0.09); }
.exp-rowsave.is-on { color: var(--accent-forest, #2D6A4F); }
.exp-rowsave.is-on:hover { color: #1B4332; }
.exp-rowsave:focus-visible { outline: 2px solid var(--accent-forest, #2D6A4F); outline-offset: 1px; }
@media (prefers-reduced-motion: reduce) { .exp-rowsave { transition: none; } }
/* Touch layouts get the 44px floor. The grid is a pointer surface at >=1024, where
   22px beside a 13px text link is the right visual weight; anywhere a finger is the
   input, the hit area grows without the mark growing with it.
   ⚠ NO NEGATIVE MARGINS. The first version pulled the box 11px past the card header's
   right edge to keep the glyph flush, and measured `scrollWidth - clientWidth = 11` on
   every card: a control spilling its own container, which the page-level h-scroll
   assertion cannot see (documented — "no page h-scroll is not no overflow"). The
   target grows INWARD instead. */
@media (hover: none) {
    /* ⚠ AND the block margins go back to zero here. They exist to protect the desktop
       grid's uniform row height; on a card there is no line box to protect, and a
       negative margin on a 44px target inside a centred header only makes the header
       shorter than the thing in it. */
    .exp-rowsave { width: 44px; height: 44px; margin-top: 0; margin-bottom: 0; }
    /* ⚠ The card header is `align-items: flex-start` on a phone and the amount runs to
       two lines, so a 44px box that centres its glyph parks the mark ~22px below the
       badge it sits beside and reads as orphaned under the price. The BOX keeps its 44
       (that is the touch floor); only the glyph moves back up to the line it belongs to. */
    .explore-card__header .exp-rowsave { align-items: flex-start; padding-top: 3px; }
}
.explore-table__cell--program .exp-prog__tail .explore-grant-link { margin-left: 0; }
/* Terse chips in the tail sit ON the line, never above it. */
.explore-table__cell--program .exp-prog__tail .rcpt {
    margin: 0;
    max-width: 100%;
}

/* ⚠ NOT a float. The tag was floated right inside the head so it would ride the
   title line; a float cannot sit beside a `display:-webkit-box` title (that box is
   block-level), so it dropped and overlapped the meta line, squeezing the first two
   rows' description to "Gran…" — on the two rows a visitor reads first. It lives in
   the meta tail instead, where the flex row gives it a fixed track. */
.explore-table__cell--program .exp-prog__tail .row-preview-tag {
    margin-top: 0;
    padding: 2px 7px;
    letter-spacing: 0.06em;
    /* ⚠ Without this the two-word version wrapped INSIDE its own pill ("OPEN" over
       "PREVIEW"), which made the pill two lines tall in a one-line flex row. */
    white-space: nowrap;
    cursor: help;
}

/* The amber question keeps a line of its own — see the note at its injection site. */
.explore-table__cell--program .exp-prog > .rcpt--open { margin: 5px 0 0; max-width: 100%; }

/* Chevron and rank re-align to the shorter row. Both were positioned against a
   14px/1.4 title in a 108px row; at 9px padding the first line's optical centre
   moved up 6px and they would otherwise float beside the second line. */
.explore-table td.explore-table__cell--program .row-chevron { top: 12px; }

/* ★★ ROUND 17 (2026-08-07) — THE POSITION NUMBER, MEASURED. Khalid, on the shipped build:
   "the row number column is not properly positioned / aligned, and looks like a UI bug" and
   "with higher numbers, the width for this row is too little". Both were real, and this block
   is now the SINGLE owner of the number's geometry (the styling half stays at `.exp-rank`
   above; `top` used to be declared in both and this one won, so the other's comment described
   an alignment the page never had).

   Measured on production at 1440 before the change:
     · BASELINE — rank baseline sat at y=22.0 against the title's y=27.25. Five and a quarter
       pixels high, on every row, which is exactly far enough to read as a superscript rather
       than as a label. `top: 11px` + 5.25 = 16.25 -> 16px puts the two baselines within a
       quarter of a pixel. (16px is what the ORIGINAL rule said before the round-13 override
       moved it to 11 for a shorter row; the override overshot.)
     · WIDTH — the box was 18px and right-aligned. At one and two digits the ink measured 7px
       and 14px and fitted. At THREE it measured 21px: it overflowed the box by 3px and its
       right edge landed at x=25 against a chevron whose left edge is x=24. The number
       physically overlapped the chevron on every row from 100 onward — 644 of the 743
       browsable programs, i.e. 87% of the catalogue, reachable by pressing "Show 25 more"
       four times. One and two digits were never the case that mattered.

   The new numbers, with the file's own >=20% clearance rule applied to the binding case:
   3-digit ink is 21px, the box is 26px (24% clear), the chevron starts at 32 so the ink's right
   edge (26) clears it by 6px — up from MINUS one — and the title starts at 48 so the chevron's
   box clears it by 4px, which is what it had before this round. The gutter grows 40 -> 48px and
   that is the whole cost of the fix: it comes out of a 300px Program column whose title
   truncates on a 62-CHARACTER budget, not a pixel one.
   ⚠ THE FIRST PASS OF THIS FIX TOOK THE CHEVRON-TO-TITLE CLEARANCE TO ZERO — it bought the
   number's 8px by spending the chevron's 4px, and the harness caught it. These three values are
   ONE SYSTEM (rank width + chevron left + td gutter): move one and something on either side of
   it closes up. Assert all three computed gaps together, never these declarations — a split
   declaration is exactly how the 11px/16px disagreement above survived a round unnoticed. */
.exp-rank { top: 16px; left: 0; width: 26px; }
.explore-table td.explore-table__cell--program .row-chevron { left: 32px; }
.explore-table td.explore-table__cell--program { padding-left: 48px; }

/* ── The funder cell carries the level ───────────────────────────────────────
   ★ ONE LINE, enforced by the browser. At two lines this cell — not the Program
   cell — was what set the row height: measured 54.6px of wrapped name against the
   Program cell's 52.7px of title + meta, and 3 lines at the narrower profiled
   width, which is where the 91px rows came from. It also rendered at 14px, the
   same size as the program NAME, so the second column argued as loudly as the
   first. Truncation is CSS here, never a character count (a count is blind to the
   column's real width); `_syncFunderTips()` then hovers exactly the ones the
   browser clipped. */
.exp-funder {
    display: block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
    line-height: 1.35;
}
.exp-funder__lvl {
    display: block;
    margin-top: 1px;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--ink-muted);
}

/* ── Orphans from the old three-line cell ──────────────────────────────────────
   `.exp-prog__kind` and the in-table `.program-tagline` no longer render; their
   rules are removed rather than left to rot as selectors that match nothing.
   (`.explore-card__tagline` is a DIFFERENT class and still drives the mobile card.)
   The deadline clamp drops 3 lines -> 2: at 9px padding a three-line deadline is
   the only cell that could out-measure the Program cell and re-introduce a
   variable row. Two lines is 36px against the program cell's 53px, with room. */
.explore-table__cell--deadline .dl-clamp { -webkit-line-clamp: 2; }


/* ══════════════════════════════════════════════════════════════════════════════
   ROUND 5A — THE GRID: SIZING, SCROLL, DENSITY, COLLAPSIBLE RAIL
   ("it is still an editorial table with a fixed width, not an interactive
    database grid" — Khalid, 2026-08-04)

   The sizing model and the frozen column live above, next to the rules they
   replace. This block is everything the grid needed that did not exist before.
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── The pane's FRAME ──────────────────────────────────────────────────────────
   A positioned box that is exactly the pane. The edge cue is painted here rather
   than inside the pane because anything inside a scroll container scrolls with the
   content — which is the one place an "there is more over here" cue must not be. */
.explore-grid {
    position: relative;
    width: 100%;
    /* The frame is what _paneCanvas() measures, so it must be the honest available
       width — never fit-content, or the measurement would chase the table it is
       supposed to be sizing. */
}
@media (max-width: 1023px) { .explore-grid { display: none !important; } }

.explore-grid__edge {
    position: absolute;
    top: 1px;
    /* ⚠ Follows the reserve (2026-08-06). While a menu is open the pane is clipped at
       the panel's left edge, and an edge cue left at the pane's own right edge would
       be painted UNDER the panel — so the one moment the grid visibly ends mid-table
       is the one moment nothing says there is more of it. `--exp-reserve` is written
       by `_reserveGridForMenu` and is 0 the rest of the time. */
    right: calc(1px + var(--exp-reserve, 0px));
    bottom: 1px;
    /* ★★ ROUND 16 — MEASURED INVISIBLE, SO IT WAS NOT DOING ITS JOB. Shipped as a
       44px ramp to `rgba(26,23,20,0.10)` over cream: about four points of lightness
       at its darkest column, spread over 44px. On a retina capture of production I
       could not find it while looking for it, and 307px of grid — 21% of the table,
       including Documents and Clawback risk, two of the six columns the page is
       selling — was off-pane with nothing on screen saying so.
       Now: narrower (so the falloff is steep enough to read as an edge rather than a
       tint), darker at its darkest, and finished with a hard 1px line. A shadow reads
       as depth; a wide pale wash reads as a rendering artefact. */
    width: 30px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 160ms ease;
    border-radius: 0 var(--explore-radius-lg) var(--explore-radius-lg) 0;
    background:
        linear-gradient(to right, rgba(26, 23, 20, 0) 0%, rgba(26, 23, 20, 0.055) 55%, rgba(26, 23, 20, 0.17) 100%),
        linear-gradient(to right, rgba(26, 23, 20, 0) calc(100% - 1px), rgba(26, 23, 20, 0.22) calc(100% - 1px));
    z-index: 4;
}

/* ── The explicit move, for a reader who does not know a pane can be dragged ──────
   Parked over the insider BAND row's right end. That strip is empty at every scroll
   position and at every column configuration (the band's label and its one offer are
   pinned to the band's LEFT by `_syncEdgeState`), so this control covers no value —
   the constraint that ruled out the two obvious placements, the pane's vertical
   middle and the header label row. Direction flips at the end of the scroll: one
   control, one place, so a reader never hunts for the second one.
   ⚠ 2026-10-09 — "empty at every position" was FALSE at 1024/1100/1280/1366: the
   visible strip of the band was narrower than its name + offer, so the offer ran under
   this button and off the pane ("Unlo"). `_fitInsiderBand` now sizes the band's
   contents to end 8px short of this button, and hides the button only when even the
   compact "Unlock" cannot sit beside it. Probe: dev-tools/explore-unlock-occlusion-probe.js. */
.explore-grid__xbtn {
    position: absolute;
    top: 7px;
    right: calc(9px + var(--exp-reserve, 0px));
    width: 30px;
    height: 30px;
    border-radius: 50%;
    border: 1px solid rgba(26, 23, 20, 0.16);
    background: rgba(255, 253, 250, 0.94);
    color: var(--ink-secondary, #57534E);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 6;
    box-shadow: 0 1px 4px rgba(26, 23, 20, 0.10);
    transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.explore-grid__xbtn[hidden] { display: none; }
.explore-grid__xbtn:hover {
    background: #fff;
    color: var(--ink, #1C1917);
    border-color: rgba(26, 23, 20, 0.32);
}
.explore-grid__xbtn:focus-visible {
    outline: 2px solid var(--accent-forest, #2D6A4F);
    outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) { .explore-grid__xbtn { transition: none; } }
/* ★ Shown ONLY while there is unread grid to the right, and it goes away at the
   end of the scroll — a cue that is always on is decoration, not information. */
.explore-grid.has-more-x .explore-grid__edge { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .explore-grid__edge { transition: none; } }

/* ⚠ The frame is only as wide as the pane, so at a wide viewport with few columns
   the cue sits on the pane's own right edge rather than out in the page margin. */
@media (min-width: 1024px) {
    .explore-grid { width: fit-content; max-width: 100%; }
    /* ★★ ROUND 9 — `fit-content` fits WHATEVER IS INSIDE, and in the zero-results
       state that is no longer a table. Measured at 1440: the frame collapsed to
       562px inside a 1138px content region, so the empty state — which names the
       failing term, says what it dropped and offers three real programs — rendered
       in a narrow column with half the usable width blank beside it. The rule above
       exists to keep the right-edge cue on the pane's own edge; with no pane to
       point at, it has nothing to do. `.is-empty` is written by `_setGridEmpty()`
       from BOTH of renderTable's exits.
       ⚠ BOTH BOXES, and the second one was found by tracing the rendered chain
       rather than by reading the rule. Widening only the frame moved the frame:
       `.explore-table-wrap` inside it still sized to its own content (measured
       682px), so `margin: 0 auto` on the empty state centred it inside 682 and left
       its centre 228px off the canvas's. A container whose width is content-derived
       has to be told about the empty state too, or fixing the outer box just moves
       the collapse one level in. */
    .explore-grid.is-empty,
    .explore-grid.is-empty > .explore-table-wrap { width: 100%; }
}

/* ── An ALWAYS-VISIBLE horizontal scrollbar ────────────────────────────────────
   macOS hides overlay scrollbars until you scroll, so a grid that scrolls sideways
   is indistinguishable from one that does not — the reader never learns the other
   columns exist. These are styled, permanent, and slim. (Firefox gets
   `scrollbar-width: thin`; it has no equivalent of "always show".) */
.explore-table-wrap { scrollbar-width: thin; scrollbar-color: rgba(26,23,20,0.34) rgba(26,23,20,0.06); }
.explore-table-wrap::-webkit-scrollbar { width: 10px; height: 12px; }
.explore-table-wrap::-webkit-scrollbar-track {
    background: rgba(26, 23, 20, 0.05);
    border-radius: 999px;
}
.explore-table-wrap::-webkit-scrollbar-thumb {
    background: rgba(26, 23, 20, 0.30);
    border: 3px solid transparent;
    background-clip: content-box;
    border-radius: 999px;
}
.explore-table-wrap::-webkit-scrollbar-thumb:hover { background: rgba(26, 23, 20, 0.52); background-clip: content-box; }
.explore-table-wrap::-webkit-scrollbar-corner { background: transparent; }

/* The in-pane load sentinel: zero height, no layout effect. It exists so the pane
   can tell the app "the end of the list is approaching" without a scroll listener
   doing arithmetic on every frame. */
.exp-sentinel { height: 1px; width: 1px; }

/* ── Grid interaction: full-row hover incl. the frozen column, and a row cursor ──
   ⚠ The frozen first cell paints its own opaque background (it has to — content
   scrolls under it), so a hover rule that only targets `td` leaves the Program
   cell unhighlighted and the row reads as broken in half. Both cells, always. */
.explore-table tbody > tr.explore-table__row:hover > td:first-child { background: #fbf7f1; }
.explore-table__row.is-cursor > td { background: rgba(30, 66, 52, 0.055); }
.explore-table tbody > tr.explore-table__row.is-cursor > td:first-child {
    background: #f2f5f1;
    /* The freeze shadow travels in a custom property precisely so the cursor ring
       can compose with it instead of replacing it. */
    box-shadow: inset 3px 0 0 0 #1e4234, var(--exp-freeze-shadow);
}
.explore-table__row.is-cursor > td:last-child { box-shadow: inset -1px 0 0 0 rgba(30, 66, 52, 0.25); }
.explore-table__row:focus { outline: none; }
.explore-table__row:focus-visible > td { background: rgba(30, 66, 52, 0.08); }
/* ⚠ Clears the sticky header when the cursor moves UP into it. Without this,
   `scrollIntoView({block:'nearest'})` parks the row exactly under the thead. */
.explore-table__row { scroll-margin-top: 84px; scroll-margin-bottom: 8px; }

/* ── Row density ───────────────────────────────────────────────────────────────
   Comfortable is the round-4 geometry (9px vertical cell padding, 71px rows).
   Compact is a real step: 3px padding and a one-line title clamp. */
.explore-density { display: inline-flex; }
.exp-dense {
    display: inline-flex;
    border: 1px solid rgba(26, 23, 20, 0.16);
    border-radius: 8px;
    overflow: hidden;
    background: var(--paper);
}
.exp-dense__opt {
    appearance: none;
    border: 0;
    background: transparent;
    padding: 8px 9px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    line-height: 0;
    color: var(--ink-muted);
}
.exp-dense__opt + .exp-dense__opt { border-left: 1px solid rgba(26, 23, 20, 0.12); }
.exp-dense__opt:hover { background: rgba(26, 23, 20, 0.05); color: var(--ink-secondary); }
.exp-dense__opt.is-on { background: #1e4234; color: #fff; }
.exp-dense__opt:focus-visible { outline: 2px solid var(--accent-sienna); outline-offset: -2px; }
.exp-dense__glyph { display: inline-flex; flex-direction: column; justify-content: center; width: 14px; height: 12px; }
.exp-dense__glyph > i { display: block; height: 2px; background: currentColor; border-radius: 1px; }
.exp-dense__glyph--comfortable > i + i { margin-top: 3px; }
.exp-dense__glyph--compact > i + i { margin-top: 1.5px; }
.exp-sr {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
@media (max-width: 1023px) { .explore-density { display: none; } }

body.exp-compact .explore-table td { padding-top: 5px; padding-bottom: 5px; }
body.exp-compact .explore-table th { padding-top: 8px; padding-bottom: 8px; }
/* ★ Compact has to compress the CELL, not just its padding. The Program cell
   RESERVES two title lines (`.exp-prog { min-height: calc(2 * 1.28 * 14px …) }`),
   which floors the row at ~53px of content whatever the padding says — cutting
   padding alone would have moved 71px to 63px and called it compact. One title
   line + the same reservation arithmetic recomputed for one line is what actually
   takes a row to ~45px. ⚠ The clamp lives on `.program-title`, not on a
   `__title` class: a compact rule written against the wrong selector is silent. */
body.exp-compact .explore-table__cell--program .program-title { -webkit-line-clamp: 1; }
body.exp-compact .explore-table__cell--program .exp-prog {
    min-height: calc(1 * 1.28 * 14px + 1px + 1.35 * 11.5px);
}
body.exp-compact .explore-table__cell--deadline .dl-clamp { -webkit-line-clamp: 1; }
/* ⚠ The amount cell is figure + qualifier + an optional CEILING chip, three inline
   spans that wrap independently — it is the only free cell that can be 1, 2 or 3
   lines, and at compact padding it OUT-MEASURED the program cell and made rows
   46.3px and 55.8px side by side. Nothing is hidden: two lines still hold the
   figure, its qualifier and the chip; the third line is what is refused. */
body.exp-compact .explore-table__cell--amount .exp-amt-box {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    line-height: 1.15;
}
/* ★ RESERVE the compact row, exactly as comfortable reserves it. A density that
   only shrinks padding lets whichever cell happens to be tallest set each row's
   height — which is the defect, not the density. `height` on a table cell is a
   MINIMUM, so this is a floor every row meets and no row needs to exceed. */
/* ⚠ ROUND 5C — THE SHAPE CELL'S SECOND LINE, on the PREVIEW rows only. With five
   Win Layer columns on by default, three of them are shape-mode (traps, docs,
   stacks-with), and a shape cell whose values render OPEN adds `.ins-sub` — the
   leading item's name — on a line of its own. That happens on the two
   PREVIEW_OPEN_ROWS and for premium members, which is exactly what makes it easy
   to miss: rows 1 and 2 measured 49.1 and 48.6 against 46.3 for every row beneath
   them, a 3.1px spread only the density control exposes. Same doctrine as the
   amount cell three rules up — compact REFUSES the extra line rather than letting
   whichever cell happens to be tallest set the row. */
body.exp-compact .explore-table td.td-insider .ins-sub { display: none; }
body.exp-compact .explore-table__row > td { height: 46px; }
body.exp-compact .explore-table__row { scroll-margin-top: 76px; }

/* ── Collapsible facet rail ────────────────────────────────────────────────────
   Collapsed, the rail is a 46px strip carrying two REAL controls. The count badge
   is not decoration: `body.has-rail .explore-filters__toggle` is display:none
   !important, so without it a collapsed rail would leave a >=1280px viewport with
   an invisible active filter and no affordance to reach it. */
.explore-rail__strip { display: none; }
.explore-rail__headbar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 0 8px 2px;
}
.explore-rail__title {
    flex: 1 1 auto;
    min-width: 0;
    font: 700 11px/1 var(--explore-font-body);
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-muted);
}
.explore-rail__title em {
    font-style: normal;
    color: #1e4234;
    background: rgba(30, 66, 52, 0.10);
    border-radius: 999px;
    padding: 1px 6px;
    margin-left: 4px;
}
.explore-rail__collapse {
    flex: 0 0 auto;
    appearance: none;
    border: 1px solid rgba(26, 23, 20, 0.14);
    background: var(--paper);
    color: var(--ink-muted);
    border-radius: 7px;
    width: 26px; height: 26px;
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    line-height: 0;
}
.explore-rail__collapse:hover { color: var(--ink); border-color: rgba(26, 23, 20, 0.34); }
.explore-rail__collapse:focus-visible { outline: 2px solid var(--accent-sienna); outline-offset: 2px; }

/* ★★ ROUND 9 — COLLAPSING THE RAIL LEFT TWO ORPHANS, NOT ONE FOLDED RAIL. Measured
   at 1440: a 38x92 bordered box carrying vertical "FILTERS" and, 8px below it, a
   detached 38x38 bordered square holding a magnifier — two objects with two borders
   and no relationship, where a moment earlier there had been one panel. Nothing else
   on the surface is drawn as a lone bordered square, and the reader has to infer
   that these two stubs ARE the rail.
   The controls are unchanged (both still real, both still reopen the rail, the count
   badge still rides Filters). What changed is that they are now ONE object: the
   strip carries the border, the radius and the paper fill — the rail's own panel
   language at 46px — and the two controls are flush SEGMENTS inside it, parted by a
   hairline. One border, one shape, one thing that folded.
   ⚠ REFUTED while measuring, and recorded so it is not "fixed" later: the reported
   68px dead gutter is 22px, which is the shell's ordinary column gap (identical
   expanded), and the "bordered-box language used nowhere else" is shared with
   `.explore-sort__btn` / `.explore-cols__btn` — same radius, same fill, 1px solid at
   0.16 vs 0.14 alpha. The orphaning was real; those two were not. */
.explore-rail__strip {
    border: 1px solid rgba(26, 23, 20, 0.14);
    border-radius: 10px;
    background: var(--paper);
    overflow: hidden;
    width: 40px;
}
.rail-strip__btn {
    appearance: none;
    width: 100%;
    min-height: 92px;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--ink-secondary);
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 10px 0;
}
.rail-strip__btn--search {
    min-height: 38px;
    margin-top: 0;
    border-top: 1px solid rgba(26, 23, 20, 0.10);
}
.rail-strip__btn:hover { background: rgba(26, 23, 20, 0.04); color: var(--ink); }
.rail-strip__btn:focus-visible { outline: 2px solid var(--accent-sienna); outline-offset: -2px; }
.rail-strip__word {
    writing-mode: vertical-rl;
    font: 700 10.5px/1 var(--explore-font-body);
    letter-spacing: 0.10em;
    text-transform: uppercase;
}
.rail-strip__badge {
    font: 700 10.5px/1 var(--explore-font-body);
    background: #1e4234;
    color: #fff;
    border-radius: 999px;
    padding: 3px 6px;
    min-width: 18px;
    text-align: center;
}

@media (min-width: 1280px) {
    body.has-rail.rail-collapsed .explore-shell { grid-template-columns: 46px minmax(0, 1fr); }
    body.has-rail.rail-collapsed .explore-rail__head,
    body.has-rail.rail-collapsed .explore-rail__body { display: none; }
    body.has-rail.rail-collapsed .explore-rail__strip { display: block; }
    /* The sticky filter bar's left pad mirrors the rail track + its gutter so the
       views chips keep starting on the table's left edge (round-3 grid alignment). */
    body.has-rail.rail-collapsed .explore-filters__inner { padding-left: calc(24px + 46px + 22px); }
}

/* ── Full bleed above 1440 ─────────────────────────────────────────────────────
   ★ The shell capped at max-width:1440px, so a 1920 viewport rendered a 1136px
   grid and threw away ~480px. Above 1440 the workbench uses the viewport with a
   32px gutter. The sticky filter bar has to follow, or the chips and the results
   meta stay boxed at 1440 while the table they label is wider. */
@media (min-width: 1441px) {
    .explore-shell { max-width: none; padding-left: 32px; padding-right: 32px; }
    body.has-rail .explore-filters__inner {
        max-width: none;
        padding-left: calc(32px + 232px + 22px);
        padding-right: 32px;
    }
    body.has-rail.rail-collapsed .explore-filters__inner { padding-left: calc(32px + 46px + 22px); }
}


/* ══════════════════════════════════════════════════════════════════════════════
   ROUND 5B — THE RECORD DRAWER + HAND-SIZED COLUMNS
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── The record drawer ─────────────────────────────────────────────────────────
   ★ OVERLAY, NOT PUSH, decided by measurement (probe-drawer-geometry-r5b.js). The
   default table's MINIMUM width is 1249px; every Win Layer column on is 2215px.
   Canvas left after a 520px drawer + a 16px gap: 430 at 1280, 590 at 1440, 1054 at
   1920 (rail open), 1240 collapsed at 1920 — short at every width a customer
   actually has, and the first viewport where a push genuinely fits is 2560. A push
   would therefore leave the grid scrolling sideways on the surface the reader
   opened the drawer FROM. Overlay everywhere, with the source row kept selected
   and scrolled into view.

   ⚠ WIDTH is clamped, not a percentage: below ~460px the record's own two-column
   sections stop being readable, and above ~560px the drawer starts eating the
   frozen Program column at 1280. 34vw sits inside that band at every desktop width
   (1280 -> 460, 1440 -> 490, 1600 -> 544, 1920 -> 560). */
.exp-drawer {
    position: fixed;
    top: var(--explore-navbar-height, 56px);
    right: 0;
    bottom: 0;
    width: clamp(460px, 34vw, 560px);
    max-width: 100vw;
    z-index: 9200;
    display: flex;
    flex-direction: column;
    background: var(--paper);
    border-left: 1px solid rgba(26, 23, 20, 0.14);
    box-shadow: -18px 0 44px -22px rgba(26, 23, 20, 0.42);
    animation: expdIn 180ms cubic-bezier(0.22, 0.9, 0.3, 1);
}
.exp-drawer[hidden] { display: none; }
@keyframes expdIn { from { transform: translateX(14px); opacity: 0.6; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .exp-drawer { animation: none; } }
/* ── The drawer's focus treatment (round 5C) ───────────────────────────────────
   ⚠ WHAT THIS REPLACED. `outline: 2px solid sienna; outline-offset: -3px` drew a
   ring around a 490px-wide, full-height panel — ~2,000px of border whose only
   readable end was wherever the reader happened to be looking. Khalid, previewing
   the drawer, read the focus state as belonging to the pale-blue box near the top
   ("Between Intakes") and filed it as a focused text input. Both halves of that are
   fixed: the status banner stopped looking like a field (see `.detail-status-banner`)
   and the drawer now says it has focus with a 3px rule across the TOP of its own
   header — one short, deliberate mark, in the one place that reads as "this panel"
   rather than "this field". */
.exp-drawer:focus { outline: none; }
.exp-drawer:focus-visible { outline: none; }
.exp-drawer:focus-visible .expd__head { box-shadow: inset 0 3px 0 0 var(--accent-sienna); }

/* ★ A 10% scrim, not a 50% one. It has three jobs — say the page behind is not
   interactive, give click-outside-to-close somewhere to land, and STAY OUT OF THE
   WAY of the selected row, which is the only thing connecting the drawer to the
   grid. A heavy scrim would do the first two and destroy the third. */
.exp-drawer-scrim {
    position: fixed;
    inset: 0;
    z-index: 9190;
    background: rgba(26, 23, 20, 0.10);
    animation: expdFade 180ms ease;
}
.exp-drawer-scrim[hidden] { display: none; }
@keyframes expdFade { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .exp-drawer-scrim { animation: none; } }
/* Below the table breakpoint the record is the bottom sheet, never this. */
@media (max-width: 1023px) {
    .exp-drawer, .exp-drawer-scrim { display: none !important; }
}

.expd__head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px 10px 16px;
    border-bottom: 1px solid rgba(26, 23, 20, 0.10);
    background: var(--cream-dark);
}
.expd__pos {
    flex: 1 1 auto;
    min-width: 0;
    font: 700 11px/1 var(--explore-font-body);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-muted);
    font-variant-numeric: tabular-nums;
}
.expd__pos .expd-of { font-weight: 500; opacity: 0.7; }
.expd__nav { display: inline-flex; gap: 4px; }
.expd__navbtn,
.expd__close {
    appearance: none;
    border: 1px solid rgba(26, 23, 20, 0.14);
    background: var(--paper);
    color: var(--ink-secondary);
    border-radius: 7px;
    /* 30px box with a 44px hit area via the ::before below — the head is a 46px
       strip and three 44px squares in it would be the whole bar. */
    width: 30px; height: 30px;
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    position: relative;
    line-height: 0;
}
.expd__navbtn::before,
.expd__close::before {
    content: '';
    position: absolute;
    top: 50%; left: 50%;
    width: 44px; height: 44px;
    transform: translate(-50%, -50%);
}
.expd__navbtn:hover:not(:disabled),
.expd__close:hover { border-color: rgba(26, 23, 20, 0.36); color: var(--ink); }
.expd__navbtn:disabled { opacity: 0.34; cursor: default; }
.expd__navbtn:focus-visible,
.expd__close:focus-visible { outline: 2px solid var(--accent-sienna); outline-offset: 2px; }
.expd__close { margin-left: 2px; }

.expd__body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
}

/* ── The record inside a 460-560px column ──────────────────────────────────────
   The panel's own layout is a two-column body (description | intel) sized for a
   full-width table row. At drawer width both columns would be ~230px, which is
   narrower than the Win Layer manifest's own question lines. One column, and the
   intel block keeps its tint as a section marker rather than a sidebar. */
.exp-drawer .explore-detail { animation: none; }
/* The drawer head already carries a close control; the panel's own round × would
   be a second button for the same action, 12px away. */
.exp-drawer .explore-detail__close { display: none; }
.exp-drawer .detail-body { grid-template-columns: minmax(0, 1fr); }
.exp-drawer .detail-body__main {
    border-right: 0;
    border-bottom: 1px solid rgba(26, 23, 20, 0.06);
    padding: var(--space-md) var(--space-lg);
}
.exp-drawer .detail-intel { padding: var(--space-md) var(--space-lg); }
.exp-drawer .detail-header { padding: var(--space-md) var(--space-lg) 0; }
/* ⚠ No `.detail-toolbar` rule here on purpose: that legacy row is already
   `display: none` sitewide (Opus r2 #4 — the springboard carries all four
   actions), and padding on a hidden element is the dead CSS that makes a later
   reader believe the toolbar renders. */
.exp-drawer .detail-facts { flex-wrap: wrap; }
.exp-drawer .detail-fact { flex: 1 1 44%; min-width: 130px; }
.exp-drawer .intel-section__grid { grid-template-columns: minmax(0, 1fr); }
.exp-drawer .detail-fullwidth { padding-left: var(--space-lg); padding-right: var(--space-lg); }
/* The springboard is the record's ending and must sit at the bottom of the
   scrolled content, full width of the drawer, never full-bleed past its edge. */
.exp-drawer .explore-detail__actions {
    margin: 16px var(--space-lg) var(--space-lg);
    border-radius: 12px;
    flex-wrap: wrap;
}
.exp-drawer .explore-detail__actions:last-child { margin-bottom: var(--space-lg); }

/* ── Column resize handles ─────────────────────────────────────────────────────
   ★ A CURSOR CHANGE IS NOT AN AFFORDANCE. Round 1 removed a native
   `resize: horizontal` partly because nothing on the header said it was there.
   The hit area is 9px and invisible at rest; on hover of the HEADER (not only of
   the 9px strip, which nobody would find first) a 2px rule appears on the column's
   right edge, so hovering any header teaches the whole row.
   ⚠ `.explore-table th` sets `overflow: hidden`, so the handle sits INSIDE the
   cell's right edge rather than straddling the border — a handle at `right: -4px`
   would be clipped away and the feature would silently not exist. */
.explore-table__labels th { position: relative; }
.exp-rz {
    position: absolute;
    top: 0;
    right: 0;
    width: 9px;
    height: 100%;
    cursor: col-resize;
    z-index: 2;
    touch-action: none;
}
.exp-rz::after {
    content: '';
    position: absolute;
    top: 5px;
    bottom: 5px;
    right: 3px;
    width: 2px;
    border-radius: 2px;
    background: var(--accent-forest);
    opacity: 0;
    transition: opacity 120ms ease;
}
.explore-table__labels th:hover .exp-rz::after { opacity: 0.34; }
.exp-rz:hover::after,
.exp-rz.is-dragging::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .exp-rz::after { transition: none; } }
/* While a drag is live the whole document shows the resize cursor and stops
   selecting text — otherwise dragging left across the header selects the labels. */
body.exp-resizing { cursor: col-resize !important; user-select: none; }
body.exp-resizing .explore-table th { cursor: col-resize; }

/* ── Reset columns (in the Columns picker) ─────────────────────────────────── */
.explore-cols__reset {
    border-top: 1px solid rgba(26, 23, 20, 0.08);
    margin-top: 6px;
    padding: 8px 10px 4px;
}
.explore-cols__resetbtn {
    appearance: none;
    border: 1px solid rgba(26, 23, 20, 0.16);
    background: var(--paper);
    color: var(--ink-secondary);
    border-radius: 7px;
    font: 600 11.5px/1 var(--explore-font-body);
    padding: 8px 10px;
    min-height: 34px;
    cursor: pointer;
    width: 100%;
    text-align: center;
}
.explore-cols__resetbtn:hover { border-color: rgba(26, 23, 20, 0.36); color: var(--ink); }
.explore-cols__resetbtn:focus-visible { outline: 2px solid var(--accent-sienna); outline-offset: 2px; }
.explore-cols__resethint {
    display: block;
    margin-top: 5px;
    font: 500 10.5px/1.35 var(--explore-font-body);
    color: var(--ink-muted);
}

/* ══════════════════════════════════════════════════════════════════════════════
   ROUND 5C — the Eligibility column, the four free qualifiers, the ask popover,
   the seam cue, and the docked shortlist.
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── The Eligibility column ────────────────────────────────────────────────────
   ★ It owns the count AND the ask, so it has two slots and reserves neither: the
   ratio is one line, the affordance under it is one line, and the row's height is
   set by the Program cell's two reserved title lines — which is 20px more than
   both of these together. That is what makes it safe to put a question here and
   was never true of the Program cell it came out of. */
.exp-clear--checks small { font-weight: 600; }
/* ⚠ THE CELL CONTAINS ITS OWN CONTROL. Once `_dedupeEligCell` removed the passive
   "1 to confirm", the inline-flex ask button rose onto the RATIO's line — measured,
   "2 of 3 clear" + the button is ~230px against a 152px column, and it spilled 38px
   into Where. (A page-level h-scroll assertion cannot see that: the page does not
   scroll, the control just leaves its box.) Two stacked lines, each inside the
   column, and the row height is unchanged because the Program cell still reserves
   more than both. */
.explore-table__cell--elig { line-height: 1.25; overflow: hidden; }
.explore-table__cell--elig .exp-clear { display: block; }
.explore-table__cell--elig .rcpt { margin-top: 2px; }
.explore-table__cell--elig .rcpt--ask-btn { display: inline-flex; max-width: 100%; }

/* The ask affordance. FIXED-WIDTH BY CONSTRUCTION — it carries a count, never the
   phrase; the phrase is 60 characters of eligibility predicate and lives in the
   hover and in the popover. */
.rcpt--ask-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    appearance: none;
    font: 700 10.5px/1 var(--explore-font-body);
    letter-spacing: 0.01em;
    padding: 4px 7px;
    border-radius: 999px;
    cursor: pointer;
    white-space: nowrap;
    max-width: 100%;
}
.rcpt--ask-btn::before { content: '?'; font-weight: 800; }
.rcpt--ask-btn .rcpt-arrow { /* opacity removed, round 14 — see .rcpt-hint */ }
.rcpt--ask-btn:hover { background: rgba(184, 134, 11, 0.18); }
.rcpt--ask-btn:focus-visible { outline: 2px solid #a86a1f; outline-offset: 1px; }

/* ── The ask popover ───────────────────────────────────────────────────────────
   Body-level + fixed: the pane clips on both axes, so a popover inside a cell
   would be cut off and would scroll away from its own anchor. */
.exp-askpop {
    position: fixed;
    z-index: 9600;
    width: 320px;
    max-width: calc(100vw - 20px);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    padding: 12px 13px;
    background: var(--paper, #fdfbf7);
    border: 1px solid rgba(184, 134, 11, 0.34);
    border-radius: 12px;
    box-shadow: 0 12px 34px rgba(26, 23, 20, 0.18);
}
.exp-askpop .rcpt-ask__q { flex: 1 1 100%; }
.exp-askpop .rcpt-ask__btns { flex: 0 0 auto; }
.exp-askpop .rcpt-ask__btns button {
    appearance: none;
    font: 700 11.5px/1 var(--explore-font-body);
    color: var(--gold-ink);
    background: rgba(184, 134, 11, 0.08);
    border: 1px solid rgba(184, 134, 11, 0.28);
    border-radius: 7px;
    padding: 8px 12px;
    min-height: 32px;
    cursor: pointer;
}
.exp-askpop .rcpt-ask__btns button:hover,
.exp-askpop .rcpt-ask__btns button:focus-visible { background: rgba(184, 134, 11, 0.18); outline: none; }
.exp-askpop .rcpt-ask__btns button:focus-visible { outline: 2px solid #a86a1f; outline-offset: 1px; }

/* ── Where / the free qualifiers ───────────────────────────────────────────── */
.exp-where { font-size: 13px; color: var(--ink, #1c1917); }
.exp-where--all { font-weight: 600; }
.exp-where--none,
.exp-flag--none,
.exp-how--none,
.exp-checked--none { color: #b9b1a4; }
.exp-where[data-tip] { cursor: help; border-bottom: 1px dotted rgba(26, 23, 20, 0.24); }
.exp-flag { font-size: 12.5px; font-weight: 600; cursor: help; }
.exp-flag--yes { color: var(--amber-strong); }
.exp-flag--no { color: #1f5c3d; }
.exp-how { font-size: 12.5px; color: var(--ink-secondary, #57534e); cursor: help; }
/* ⚠ NO freshness colour here, deliberately. A green dot would turn "somebody
   looked on this date" into "this is current", which is the claim the date cannot
   make. The words in the hover are the whole feature. */
.exp-checked { font-size: 12.5px; color: var(--ink-3, #78716c); font-variant-numeric: tabular-nums; cursor: help; }

/* ── The seam cue (round 5C; the floating pill RETIRED in round 8) ────────────
   ★★ The `.exp-seam` pill that used to live here was `position: absolute` over the
   scroll pane and 202px wide against a 44px edge strip, so it protruded 113px into
   the table body: hit-tested at 1440 it covered a Difficulty pill and the first
   peeking locked value, and it survived into the zero-results state, painted across
   the empty-state sentence. Round 8's column budget puts the band on canvas at every
   width ≥1280 by default, so the pill's own condition is now false in the shipped
   configuration — and for the state a reader BUILDS by switching free columns back
   on, the affordance moved to `#explore-views` (`.explore-views__chip--seam` below),
   the row where every other view control on this surface lives. It covers no cell at
   any width, and it cannot render with no rows.
   The gradient below is unchanged: decoration, `pointer-events: none`, the "there is
   more grid over here" signal that macOS overlay scrollbars hide. */
.explore-views__chip--seam {
    /* The band's own accent, so the chip reads as a pointer AT the Win Layer rather
       than as one more view. It is a scroll control, never an offer — no price, no
       verb, no lock glyph (a lock CUE beside the band's own offer would be the
       second-offer defect the pill was careful to avoid, and this one has to be
       careful in a place where the band's offer CAN be on screen with it). */
    color: #6b4a12;
    border-color: rgba(184, 134, 11, 0.45);
    background: rgba(184, 134, 11, 0.1);
    letter-spacing: 0.04em;
}
.explore-views__chip--seam:hover { background: rgba(184, 134, 11, 0.18); }

/* ── The shortlist, docked (round 5C) ─────────────────────────────────────────
   ⚠ PAGE-SCOPED ON PURPOSE. explore.css is loaded by explore.html and nothing
   else, so none of the ~290 other pages that load shortlist.js can be reached by
   these rules whatever class the module puts on its root.
   The floating TRIGGER is what collided with the grid, so the floating trigger is
   what goes: the basket keeps every control inside it and appears only when the
   "Saved (N)" chip in the view row asks for it. */
.sl-root.sl-docked .sl-bbar { display: none !important; }
.sl-root.sl-docked .sl-basket {
    /* Not bottom-docked: anchored under the workbench header, the same place the
       Columns popover opens, so it reads as this page's own panel. */
    top: calc(var(--explore-navbar-height, 56px) + 96px);
    bottom: auto;
    right: 24px;
    max-height: min(66vh, 560px);
    /* Closed = not there at all. The floating bar was the only reason a collapsed
       basket had to stay on screen. */
    transform: translateY(-10px) scale(0.98);
    opacity: 0;
    pointer-events: none;
}
.sl-root.sl-docked .sl-basket.sl-show.sl-expanded {
    transform: none;
    opacity: 1;
    pointer-events: auto;
}
/* The companion page inset would re-fit every grid column mid-read. */
body.sl-docked-host.sl-shift { padding-right: 0; }

/* The chip itself — same component as every other view chip, with the saved
   bookmark's own accent so it reads as a place rather than an ordering. */
.explore-views__chip--saved { margin-left: 9px; position: relative; }
.explore-views__chip--saved::before {
    content: '';
    position: absolute;
    left: -9px;
    top: 20%;
    bottom: 20%;
    width: 1px;
    background: rgba(26, 23, 20, 0.14);
}

/* ══ THE VALUE-REACH LAYER (round 5D) ══════════════════════════════════════════
   Every value the grid renders has to be READABLE. The cell answers in a fixed
   width, so a value that will not fit is either clipped or summarised into a count
   or a rating — and both are dead ends nothing on the page admits to. `explore.js`
   attaches ONE expansion (`data-tip-card="expcell"`) to every cell that needs one;
   these rules are its affordance and its list typography.

   ⚠ These style the BODY-LEVEL `.gc-tip` element, which is shared. They live in
   explore.css — a file no other page loads — precisely so the list block cannot
   leak into the shared program card on the Game Plan or a grant page. */

/* The affordance. A hover-only cue is invisible on touch AND in a screenshot
   (documented lesson), so the cue is a persistent dotted underline on the value,
   the same one `.exp-where[data-tip]` already uses — not a hover-revealed one. */
.explore-table .exp-reach { cursor: help; }
.explore-table td > .exp-reach:not(td) {
    text-decoration: underline dotted rgba(26, 23, 20, 0.26);
    text-underline-offset: 3px;
    text-decoration-thickness: 1px;
}
.explore-table td.exp-reach { cursor: help; }
/* ★ The cell cursor's focus ring, on an element that is `tabindex="-1"` and
   therefore never reached by Tab. `:focus-visible` alone does not fire reliably for
   programmatic focus on a non-tabbable element, so this uses `:focus` — which is
   correct here BECAUSE the only way to focus these is the deliberate ←/→ walk. */
.explore-table .exp-reach:focus {
    outline: 2px solid var(--forest, #1B4332);
    outline-offset: -2px;
    border-radius: 3px;
}
.explore-table .exp-reach:focus:not(:focus-visible) { outline-color: rgba(27, 67, 50, 0.55); }

/* The card's list block. Same document typography as `.gc-tipc__sum`; a marker
   column so the eye can run down the items, and a tone per eligibility verdict. */
.gc-tipc__list {
    display: block;
    margin: 0;
    padding: 10px 15px 11px;
    list-style: none;
}
.gc-tipc__li {
    display: flex;
    gap: 8px;
    align-items: baseline;
    padding: 3px 0;
    font-size: 12px;
    line-height: 1.45;
    color: var(--ink-2, #57534E);
}
.gc-tipc__li + .gc-tipc__li { border-top: 1px dotted rgba(28, 25, 23, 0.10); }
.gc-tipc__mark {
    flex: 0 0 auto;
    width: 11px;
    text-align: center;
    font-weight: 700;
    color: var(--ink-3, #78716c);
}
.gc-tipc__litext { flex: 1 1 auto; min-width: 0; }
.gc-tipc__li--yes .gc-tipc__mark { color: #1f5c3d; }
.gc-tipc__li--ask .gc-tipc__mark { color: var(--amber-strong); }
.gc-tipc__li--no  .gc-tipc__mark { color: #9F4121; }
/* Round 6 — a condition NOBODY checked. Neutral: it is not a pass, not a fail, and
   not a question we can put to the reader. Its whole content is "we do not know". */
.gc-tipc__li--gap .gc-tipc__mark { color: #a8a096; }
.gc-tipc__li--gap .gc-tipc__litext { color: #6b6255; }
/* A second prose block under the first is context, not a restatement — set it
   apart so the card does not read as two paragraphs of equal weight. */
.gc-tipc__sum--note,
.gc-tipc__sum--meaning {
    padding-top: 0;
    color: var(--ink-3, #78716c);
    font-size: 11.5px;
}
.gc-tipc__list + .gc-tipc__sum { padding-top: 0; }

/* ⚠⚠ A FLIPPED CARD LOSES ITS OWN STYLING — sitewide bug, worked around here.
   `tooltip.js positionTip()` rebuilds the element's className when a tooltip does not
   fit in its requested placement and has to flip:

       t.className = 'gc-tip gc-tip--' + finalPlacement
                   + (t.classList.contains('gc-tip--popover') ? ' gc-tip--popover' : '');

   It preserves `--popover` and DROPS `--card`. The round-5D expansions are tall (a
   six-item list is ~520px), so they flip constantly — and a flipped card rendered as
   the base tooltip: 260px wide instead of 372, forest-dark background under
   `var(--ink-2)` body text and a `var(--forest)` title, i.e. dark green on dark
   green, with the little arrow back. Screenshot evidence: the first capture of
   `r5d-open-traps.png`. It hits the SHARED program card on the Game Plan too,
   whenever that one flips.

   ★ THE CORRECT FIX IS ONE LINE IN `tooltip.js` — carry `--card` across the flip the
   same way `--popover` is carried. It is NOT made here: tooltip.js is loaded by ~290
   pages (every grant page, the Game Plan, the arts vertical), and retiming or
   re-classing a shared hover layer to fix one grid is not a trade this block gets to
   make on its own. Reported instead.

   The workaround keys off content rather than class: a card ALWAYS contains
   `.gc-tipc__head`, whatever `positionTip` did to the className. These rules live in
   explore.css, which no other page loads, so nothing leaks sideways. Delete this
   block the day tooltip.js carries the class. */
.gc-tip:has(.gc-tipc__head) {
    background: #fdfaf3;
    color: var(--ink-2, #57534E);
    border: 1px solid rgba(28, 25, 23, 0.13);
    border-radius: 12px;
    max-width: 372px;
    padding: 0;
    font-size: 12.5px;
    line-height: 1.5;
    box-shadow: 0 18px 44px -14px rgba(28, 25, 23, 0.34), 0 2px 6px rgba(28, 25, 23, 0.06);
    pointer-events: auto;
    overflow: hidden;
}
.gc-tip:has(.gc-tipc__head)::before { display: none; }

/* ★★ ROUND 6b — THE EXPANSION CARD IS BOUNDED TOO, AND THIS ROUND IS WHY.
   The card is `position: fixed` with `overflow: hidden` and no `max-height`: it fit
   at 219px while the eligibility expansion held three or four conditions. Round 6b
   makes that list the record's OWN conditions (up to eight rows plus a lead
   sentence), so the same card can now want more room than a short viewport has —
   and `overflow: hidden` would CLIP it silently, with no scrollbar and no cue, which
   is a worse failure than the Columns popover's because nothing about it looks
   wrong. Bounded to the viewport and scrollable inside that bound, with the same
   `overscroll-behavior: contain` the menus use so the wheel does not chain.
   ⚠ Keyed off `:has(.gc-tipc__head)` for the same reason the block above it is:
   `positionTip` rebuilds the className each time and drops `--card`. Page-scoped —
   explore.css is loaded by explore.html and nothing else. */
.gc-tip:has(.gc-tipc__head) {
    max-height: min(72vh, 520px);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
}

/* ============================================================
   ROUND 7 — THE RECORD DRAWER, HARDENED
   ------------------------------------------------------------
   Everything below is scoped to the drawer or to blocks that only render inside a
   record. Written as ONE block at the end on purpose: every rule here is an
   OVERRIDE of an earlier declaration in this file, and the cascade discipline for
   these two stylesheets says to state the winner where a later reader will find it
   rather than to edit a rule six thousand lines up that four other surfaces share.
   ============================================================ */

/* ── The drawer is a three-part flex column: head, scrolling body, pinned foot ──
   ★ The action bar is `flex: 0 0 auto` at the bottom, NOT the last thing in the
   scroll. Records run 1,400 to 4,800px in this column, so the panel's ending used
   to be three to six screens below the fold — and on a free record it landed within
   ~100px of the offer block, which is what made two filled buttons read as one
   paywall. Pinning it fixes the reach and the adjacency together. */
.exp-drawer .expd__foot {
    flex: 0 0 auto;
    border-top: 1px solid rgba(26, 23, 20, 0.10);
    background: var(--cream-dark);
}
.exp-drawer .expd__foot .explore-detail__actions {
    margin: 0;
    padding: 10px 14px;
    border-radius: 0;
    background: transparent;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
}
/* ★ ONE PRIMARY. The bar used to lead with a FILLED sienna "Open full grant page →"
   — the only control that leaves the surface the reader is triaging in. The two
   exits are quiet links now and the pursue action is the single filled control. */
.exp-drawer .expd__foot .eda-row { display: flex; gap: 8px; align-items: stretch; }
.exp-drawer .expd__foot .eda-btn { flex: 1 1 auto; min-height: 42px; }
/* ⚠ SIENNA, NOT FOREST — and the difference is the whole point. The offer block
   inside the body uses forest (`.btn--accent`), so a forest footer button put TWO
   filled green buttons on one screen and rebuilt the competing-primaries defect one
   layer down. Sienna is this surface's ACT-ON-THIS colour and forest is its BUY
   colour; with one of each, a reader can tell what kind of thing each button is
   without reading it. Caught by measuring background colours on a rendered drawer,
   not by reading the rule. */
.exp-drawer .expd__foot .eda-btn--primary {
    background: var(--accent-sienna, #C4532B);
    border-color: var(--accent-sienna, #C4532B);
    color: #fff;
    flex: 2 1 auto;
}
.exp-drawer .expd__foot .eda-btn--ghost {
    background: var(--paper);
    color: var(--ink);
    border-color: rgba(26, 23, 20, 0.20);
    flex: 0 0 auto;
    min-width: 96px;
}
.exp-drawer .expd__foot .eda-btn--ghost:hover { border-color: rgba(26, 23, 20, 0.42); }
.exp-drawer .expd__foot .eda-links {
    display: flex;
    gap: 14px;
    align-items: baseline;
    flex-wrap: wrap;
}
.exp-drawer .expd__foot .eda-link,
.exp-drawer .expd__foot .eda-link:visited {
    font: 500 12px/1.2 var(--explore-font-body);
    color: var(--ink-secondary);
    text-decoration: none;
    border-bottom: 1px solid rgba(26, 23, 20, 0.18);
    padding: 2px 0;
}
.exp-drawer .expd__foot .eda-link:hover { color: var(--ink); border-bottom-color: var(--ink); }
.exp-drawer .expd__foot .eda-link:focus-visible {
    outline: 2px solid var(--accent-sienna);
    outline-offset: 3px;
}
/* The in-body copy is gone (the drawer passes `springboard: false`), so nothing
   here can double up; the mobile sheet keeps the dark in-flow bar it always had. */

/* ── The resize grip (round 7, approved) ──────────────────────────────────────
   ★ A CURSOR CHANGE IS NOT AN AFFORDANCE — the round-5B ruling. The strip is 10px
   and quiet at rest; on hover or focus a 2px rule appears down the drawer's leading
   edge, and a short centre bar says which axis it moves on. */
.expd__grip {
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 10px;
    margin: 0; padding: 0;
    border: 0;
    background: transparent;
    cursor: col-resize;
    z-index: 3;
    touch-action: none;
    -webkit-appearance: none;
    appearance: none;
}
.expd__grip > span {
    position: absolute;
    left: 4px; top: 50%;
    width: 2px; height: 34px;
    margin-top: -17px;
    border-radius: 2px;
    background: rgba(26, 23, 20, 0.16);
    transition: background 120ms ease;
}
.expd__grip::after {
    content: '';
    position: absolute;
    left: 4px; top: 0; bottom: 0;
    width: 2px;
    background: transparent;
    transition: background 120ms ease;
}
.expd__grip:hover > span,
.expd__grip.is-dragging > span { background: var(--accent-sienna); }
.expd__grip:hover::after,
.expd__grip.is-dragging::after { background: var(--accent-sienna); }
.expd__grip:focus { outline: none; }
.expd__grip:focus-visible::after { background: var(--accent-sienna); }
.expd__grip:focus-visible > span {
    background: var(--accent-sienna);
    height: 60px; margin-top: -30px;
}
/* The body must not sit under the grip's 10px strip. */
.exp-drawer .expd__head { padding-left: 20px; }
.exp-drawer .expd__body { padding-left: 4px; }
/* ⚠ Mid-drag the whole page must stop selecting text and stop reacting to hover —
   the same body class the column resize uses, so there is one drag state, not two. */
body.exp-resizing .exp-drawer { user-select: none; }

/* ── Typography: ONE system inside the record ────────────────────────────────
   ★ The drawer was mixing three families with no rule behind the mix: Fraunces for
   the title AND for the offer heading, IBM Plex Mono for the Amount value but not
   for the Deadline value beside it, sans for everything else. Three typefaces in
   one 490px column, two of them alternating INSIDE a four-cell strip.
   The system now: Fraunces is the record's NAME and nothing else (it is the one
   h1-equivalent on the surface); mono is reserved for figures that need to line up
   in a column, which in a stacked drawer is none of them; everything else is the
   body sans, differentiated by size and weight. */
.exp-drawer .detail-fact__value--highlight {
    font-family: var(--explore-font-body);
    font-size: var(--text-sm);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--accent-forest);
}
.exp-drawer .premium-overlay__content h4 {
    font-family: var(--explore-font-body);
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

/* ── Quick facts: four scannable cells, never an essay ───────────────────────── */
.exp-drawer .detail-facts { padding: 0; row-gap: 0; }
.exp-drawer .detail-fact {
    flex: 1 1 44%;
    min-width: 130px;
    padding: 11px var(--space-lg);
    border-bottom: 1px solid rgba(26, 23, 20, 0.05);
}
/* ★ Every fact value is clamped, with the full string on hover. `deadline` is free
   prose in the catalogue (longest values run past 200 characters), so without this
   a cell whose whole job is to be scanned rendered a five-line paragraph beside an
   AMOUNT cell holding "$25K" and stretched to match it. A prose value also takes
   the FULL width of the strip rather than half of it (`--wide`).
   ⚠ Clamping is a RENDER decision only: the complete string stays in the DOM and in
   the `title`, and the record's Deadline Intelligence card carries the long form. */
.exp-drawer .detail-fact__value {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.exp-drawer .detail-fact--wide { flex: 1 1 100%; }
/* A program that is not open still has a deadline in the record, and the banner
   above already says why it does not apply. The cell goes quiet rather than
   repeating the status word: one owner per fact (round 5C). */
.detail-fact--stale .detail-fact__value { color: var(--ink-muted); }
.detail-fact--stale .detail-fact__label { opacity: 0.75; }

/* ── The status notice, in this surface's palette ─────────────────────────────
   ★★ TWO OF THE SIX STATES HAD NO RULE AT ALL. `closed` (71 records) and
   `upcoming` (9) fell through to a class with no declaration: an unstyled strip
   with the browser's default colour, carrying the single most consequential fact a
   record can state. The four that did have rules were raw Tailwind blues and reds
   that appear nowhere else on this page — which is why a reviewer read the
   pale-blue "Between Intakes" box as a focused text input. */
.exp-drawer .detail-status-banner,
.explore-bottom-sheet .detail-status-banner {
    margin: 0 var(--space-lg) 0;
    padding: 9px 12px;
    border-radius: 8px;
    border-left-width: 3px;
    font: 500 12.5px/1.45 var(--explore-font-body);
}
.detail-status-closed,
.detail-status-discontinued,
.detail-status-winding-down {
    background: #fbf1ec;
    color: #8a3d1d;
}
.detail-status-paused,
.detail-status-upcoming {
    background: #fbf5e8;
    color: #7a5a1c;
}
.detail-status-between-intakes {
    background: #f1f4ef;
    color: #3f5c46;
}
.detail-status-banner strong { margin-right: 5px; }

/* ── Rating rows: one polarity, no orphaned "?" ──────────────────────────────
   ★ `.field-tooltip` is `display: inline-flex`, and `.intel-scorecard` is a COLUMN
   flex, so a "?" rendered as a SIBLING of a bar became its own flex ITEM and
   dropped to a line of its own — visually attaching to the row below and reading as
   a stray bullet. It lives inside the label now, which is the only element a hint
   about a label can belong to. */
.intel-scorecard__axis {
    font: 500 10px/1.3 var(--explore-font-body);
    letter-spacing: 0.04em;
    color: var(--ink-muted);
    margin-bottom: 2px;
}
.intel-scorecard .rating-bar__label {
    width: 88px;
    display: inline-flex;
    align-items: center;
    gap: 2px;
}
.intel-scorecard .rating-bar__label .field-tooltip {
    width: 13px; height: 13px; margin-left: 2px;
}
.rating-bar__value--word {
    width: auto;
    min-width: 74px;
    text-align: right;
    font-family: var(--explore-font-body);
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}
.intel-tip__label .field-tooltip { width: 13px; height: 13px; }

/* ── Prose that used to be crammed into a fact cell ──────────────────────────── */
.intel-note {
    margin: 0 0 var(--space-md);
    padding: 10px 12px;
    background: rgba(45, 106, 79, 0.04);
    border-radius: 9px;
}
.intel-note__label {
    display: block;
    font: 700 9.5px/1.2 var(--explore-font-body);
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--ink-muted);
    margin-bottom: 5px;
}
.intel-note__body {
    font: 400 12.5px/1.55 var(--explore-font-body);
    color: var(--ink-secondary);
}
.intel-note__body strong { font-weight: 600; color: var(--accent-forest); }

/* ── Co-funding / budget: stacked, not two flex cells fighting for one line ──── */
.exp-drawer .intel-kv-row { flex-direction: column; gap: var(--space-sm); }
.exp-drawer .intel-kv { flex-direction: row; align-items: baseline; gap: 8px; }
.exp-drawer .intel-kv__label { flex: 0 0 92px; }
.exp-drawer .intel-kv__value { flex: 1 1 auto; min-width: 0; }

/* ── Can Stack With: the worst-composed block in the record ───────────────────
   ★★ `.intel-stack-item` is a flex ROW and `strong` carried `flex-shrink: 0`, so a
   long program name refused to give ground: the notes were squeezed into whatever
   was left and rendered ONE WORD PER LINE for twenty lines, while the row itself
   spilled 148px past the drawer's right edge. Measured: the drawer body scrolled
   sideways on 5 of 8 sampled premium records, and every existing assertion was
   green because they measure the DOCUMENT's scrollWidth, not this box's.
   Name and note are BLOCKS now, and `min-width: 0` is stated so a long unbroken
   program name cannot re-inflate the row. */
.intel-stack-item {
    display: block;
    min-width: 0;
    padding: 6px 0 6px 20px;
}
.intel-stack-item .intel-stack-item__name {
    display: block;
    flex-shrink: 1;
    font: 600 12.5px/1.4 var(--explore-font-body);
    color: var(--ink);
    overflow-wrap: anywhere;
}
.intel-stack-item .intel-stack-item__note {
    display: block;
    margin-top: 2px;
    font: 400 11.5px/1.45 var(--explore-font-body);
    color: var(--ink-muted);
    overflow-wrap: anywhere;
}
.intel-stack-item.is-conditional::before {
    content: '\223C';
    position: absolute;
    left: 0;
    color: var(--accent-gold);
    font-weight: 700;
}
.intel-card__n {
    display: inline-block;
    margin-left: 5px;
    padding: 1px 6px;
    border-radius: 8px;
    background: rgba(26, 23, 20, 0.07);
    font: 700 9.5px/1.5 var(--explore-font-body);
    color: var(--ink-secondary);
    letter-spacing: 0.02em;
}
/* Nothing inside a record may push its own box sideways. The drawer body is the
   only viewport these blocks have. */
.exp-drawer .expd__body,
.exp-drawer .intel-card,
.exp-drawer .intel-section__grid,
.exp-drawer .detail-body__main { min-width: 0; overflow-x: clip; }

/* ── The locked Win Layer: the same gate the grid uses, not an emptier one ─────
   ★★ The manifest rendered TEN labels and zero values: a menu of absence, with
   nothing on it a reader could weigh. Measured, it contained no blurred element at
   all — while the grid two hundred pixels to its left renders every locked insider
   cell as the REAL value under `filter: blur(4.5px)`. The drawer now renders what
   the grid renders: never legible, never in the accessible tree, never selectable,
   but unmistakably a real answer of a real shape. */
.wlm__val--blur {
    filter: blur(4.2px);
    -webkit-filter: blur(4.2px);
    user-select: none;
    -webkit-user-select: none;
    pointer-events: none;
    max-width: 132px;
    overflow: hidden;
    white-space: nowrap;
    color: var(--ink-secondary);
}
/* The shape fields keep their COUNT open — it is already public in the grid. */
.wlm--shape .wlm__val { color: var(--ink); }
.wlm__sub {
    flex: 1 1 100%;
    font: 400 10px/1.34 var(--explore-font-body);
    color: var(--ink-muted);
    overflow-wrap: anywhere;
}
/* An OPEN manifest (a preview row, or a member) is not an offer table — it is the
   record's own intel, so it drops the sell tint and reads as data. */
.wl-manifest--open .wlm__val { color: var(--ink); font-weight: 600; }

/* ── The offer: one primary, with the second plan stated beside it ───────────── */
.premium-overlay__alt {
    display: block;
    width: 100%;
    background: none;
    border: 0;
    padding: 4px 0 0;
    cursor: pointer;
    font: 500 11.5px/1.4 var(--explore-font-body);
    color: var(--ink-secondary);
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-color: rgba(26, 23, 20, 0.28);
}
.premium-overlay__alt:hover { color: var(--accent-forest-deep); }
.premium-overlay__alt:focus-visible {
    outline: 2px solid var(--accent-sienna);
    outline-offset: 2px;
    border-radius: 4px;
}

/* ── Eligibility list: a list, not a wall ────────────────────────────────────── */
.exp-drawer .detail-eligibility ul { margin: 0; }
.exp-drawer .detail-eligibility li { margin-bottom: 5px; }

/* ── A collapsed rail group states what it is still filtering by ──────────────
   ★ Replaces the refusal that used to stand in `_railIsOpen`. A bare count would
   satisfy the letter of "never hide an active filter" and not its point: "Province
   2" tells a reader two things are on, not which two, so they still have to open
   the group — the state we just freed them from. */
.rail-fx__sel {
    margin: -2px 0 6px 17px;
    font: 500 10.5px/1.35 var(--explore-font-body);
    color: var(--accent-forest-deep, #1f4b38);
    overflow-wrap: anywhere;
}
.rail-fx--shut-active .rail-fx__head .explore-rail__label { color: var(--ink); }

/* ── The offer block reads as a SECTION, not a poster ─────────────────────────
   ★ Caught by LOOKING: the heading, the lead and the footnote were centred while
   the manifest under them was left-aligned, so one block used two alignments and
   the eye had to reset three times inside 300px. Left throughout — the manifest is
   the argument and it sets the axis. */
.exp-drawer .premium-overlay__content { text-align: left; }
.exp-drawer .premium-overlay__lead { text-align: left; }
.exp-drawer .premium-overlay__footnote { text-align: left; }
.exp-drawer .premium-overlay__pricing {
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 14px;
}
.exp-drawer .premium-overlay__pricing .btn--upgrade { width: auto; flex: 0 1 auto; }
.exp-drawer .premium-overlay__alt { width: auto; padding: 0; }

/* ══════════════════════════════════════════════════════════════════════════════
   ROUND 14 — THE FILTER SHEET (what "Filters" opens below 1280)
   ══════════════════════════════════════════════════════════════════════════════
   ★★ IT HOSTS THE RAIL'S OWN MARKUP, UNCHANGED. `_facetPanelHtml()` writes the same
   `.explore-rail__group` / `.explore-rail__opt` / `.rail-fx--win` tree into both
   hosts, so every facet style above applies here with no duplication and the two
   surfaces cannot drift apart visually any more than they can drift in their counts.
   Only the CHROME is new: a dialog frame, a scrim, and a footer that states the
   result set the grid behind the sheet is already showing.

   ⚠ TOUCH SIZING IS THE ONE REAL DIFFERENCE. The rail's 27px option rows are a
   pointer density; on a phone every option is a tap target, so rows are floored at
   44px here and the checkbox grows with them. That is a `@media (pointer: coarse)`
   decision as much as a width one, so it is keyed on the sheet, which only ever
   renders below 1280. */
.exp-fsheet { position: fixed; inset: 0; z-index: 9995; }
.exp-fsheet[hidden] { display: none; }
.exp-fsheet__scrim {
    position: absolute; inset: 0;
    background: rgba(26, 23, 20, 0.42);
    animation: expFsheetFade .16s ease-out;
}
.exp-fsheet__panel {
    position: absolute; left: 0; right: 0; bottom: 0;
    /* Never taller than the viewport, and never so short that two groups cannot be
       compared. `dvh` so an iOS URL bar appearing cannot push the footer off. */
    max-height: 88dvh;
    display: flex;
    flex-direction: column;
    background: var(--paper);
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -10px 40px rgba(26, 23, 20, 0.18);
    animation: expFsheetUp .2s cubic-bezier(.2,.8,.3,1);
}
@keyframes expFsheetFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes expFsheetUp { from { transform: translateY(14px); opacity: .6; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .exp-fsheet__scrim, .exp-fsheet__panel { animation: none; }
}
.exp-fsheet__head {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: 10px;
    padding: 16px 16px 12px;
    border-bottom: 1px solid rgba(26, 23, 20, 0.10);
}
.exp-fsheet__title {
    flex: 1 1 auto; min-width: 0; margin: 0;
    font: 600 17px/1.2 var(--explore-font-display);
    color: var(--ink);
}
.exp-fsheet__title:focus { outline: none; }
.exp-fsheet__title:focus-visible { outline: 2px solid var(--accent-sienna); outline-offset: 3px; border-radius: 4px; }
.exp-fsheet__n {
    flex: 0 0 auto;
    font: 700 11.5px/1 var(--explore-font-body);
    color: #fff; background: var(--accent-sienna);
    border-radius: 999px; padding: 4px 9px;
}
.exp-fsheet__n[hidden] { display: none; }
.exp-fsheet__close {
    flex: 0 0 auto;
    width: 44px; height: 44px; margin: -10px -8px -10px 0;
    display: inline-flex; align-items: center; justify-content: center;
    border: 0; background: transparent; color: var(--ink-secondary);
    border-radius: 10px; cursor: pointer;
}
.exp-fsheet__close:hover { background: rgba(26, 23, 20, 0.06); }
/* ⚠ flex column + max-height: every direct child needs an explicit flex-basis or
   the children compress instead of the body scrolling (documented class). */
.exp-fsheet__body {
    flex: 1 1 auto; min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding: 8px 12px 12px;
}
.exp-fsheet__foot {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: 10px;
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid rgba(26, 23, 20, 0.10);
    background: var(--cream);
}
.exp-fsheet__clear {
    flex: 0 0 auto;
    min-height: 44px; padding: 0 14px;
    border: 1px solid rgba(26, 23, 20, 0.16); border-radius: 10px;
    background: transparent; color: var(--ink-secondary);
    font: 600 13px/1 var(--explore-font-body); cursor: pointer;
}
.exp-fsheet__clear:disabled { opacity: .45; cursor: default; }
.exp-fsheet__done {
    flex: 1 1 auto;
    min-height: 44px; padding: 0 16px;
    border: 0; border-radius: 10px;
    background: var(--accent-forest); color: #fff;
    font: 600 14px/1 var(--explore-font-body); cursor: pointer;
}
.exp-fsheet__done:hover { background: var(--accent-forest-deep); }
.exp-fsheet__clear:focus-visible,
.exp-fsheet__done:focus-visible,
.exp-fsheet__close:focus-visible { outline: 2px solid var(--accent-sienna); outline-offset: 2px; }

/* The rail's own furniture, re-tuned for a hand. Scoped to the sheet so the 232px
   column at ≥1280 is untouched. */
.exp-fsheet .explore-rail__group { margin-bottom: 4px; }
.exp-fsheet .rail-fx__head { min-height: 44px; }
.exp-fsheet .explore-rail__label { font-size: 11.5px; }
.exp-fsheet .explore-rail__opt {
    min-height: 44px;
    padding: 6px 8px;
    font-size: 14px;
    gap: 10px;
}
.exp-fsheet .rail-opt__box { flex-basis: 18px; width: 18px; height: 18px; }
.exp-fsheet .rail-opt__n { font-size: 12.5px; min-width: 30px; }
.exp-fsheet .rail-fx__n { min-height: 32px; min-width: 32px; }
.exp-fsheet .explore-rail__more { min-height: 44px; }
.exp-fsheet .rail-fx__foot { font-size: 12px; margin: 8px 8px 4px; }
/* ★★ FOUND BY LOOKING AT THE RENDER, after every assertion was green: the panel's
   own "Clear all filters" and the sheet footer's "Clear all" were both on screen,
   60px apart, doing the identical thing under two different labels. One action, two
   controls, is the composition defect this project keeps a lens for. The FOOTER wins
   because it is pinned and always reachable; the panel's copy is suppressed inside
   the sheet and untouched in the rail, where it is the only one. */
.exp-fsheet .explore-rail__clear { display: none; }
/* The page must not scroll behind an open sheet. */
body.exp-fsheet-open { overflow: hidden; }
/* The sheet is a below-1280 control; if a viewport somehow grows under an open
   sheet the JS closes it, and this is the belt for the frame between the two. */
@media (min-width: 1280px) { .exp-fsheet { display: none; } }

/* ══════════════════════════════════════════════════════════════════════════════
   ROUND 14 — ONE FOCUS RING FOR THE WHOLE SURFACE
   ══════════════════════════════════════════════════════════════════════════════
   ★★ THE REVIEW'S CLAIM WAS HALF TRUE, AND THE HALF THAT WAS TRUE IS THE PROBLEM.
   This file already carries 56 bespoke `:focus-visible` rules — the rail options,
   the sort and columns menus, the headers, the cell cursor, the drawer, the density
   control. What it never had was a DEFAULT, so everything nobody remembered — the
   search input, the rows, every link in the article, the pricing buttons, the ghost
   row's CTA, the toolkit strip, the footer — fell through to Chrome's
   `1px auto rgb(0, 95, 204)`: a system blue, one pixel wide, on a cream-green-rust
   page. Not invisible, but the thinnest and most foreign mark on the surface, on the
   one state a keyboard reader navigates entirely by.
   ⚠ IT IS A FALLBACK, NOT AN OVERRIDE. Specificity (0,0,1,0) and it sits above
   nothing: every rule that already names a colour still wins, because those were
   chosen against the thing they sit on (amber inside the Win Layer, sienna in the
   menus, forest inside the grid). This only reaches what had nothing.
   ⚠ `:focus-visible`, never `:focus` — a mouse press on a button must not paint a
   ring. The one deliberate exception on this surface is the round-5D cell cursor,
   which uses `:focus` BECAUSE the only way to focus those is a keyboard walk.
   Contrast: #1a4731 on `--paper` is 9.7:1 and on `--cream` 9.2:1, so the ring clears
   the 3:1 non-text floor with a wide margin on every ground this page uses. */
/* ⚠ `body :focus-visible`, deliberately, rather than a list of this page's regions.
   A list is a list of the places somebody REMEMBERED, which is the failure mode being
   fixed. explore.css is loaded by explore.html and by nothing else, so the reach is
   this page and the shared nav/footer AS THEY RENDER HERE. At (0,0,1,1) any rule in
   main.css that names its own ring still wins. */
body :focus-visible {
    outline: 2px solid var(--accent-forest-deep, #1a4731);
    outline-offset: 2px;
    border-radius: 3px;
}
/* An input already draws its own box; an offset ring around it reads as a second
   border. It sits on the edge instead. */
.explore-filters input:focus-visible,
.exp-fsheet input:focus-visible {
    outline-offset: 0;
}

/* ★ ROUND 14 — see `_syncHeaderOcclusion`. A header whose label has scrolled under
   the frozen Program column stops showing its ornaments, so the visible sliver is
   never a lone ⓘ with nothing to attach it to. `visibility` (not `display`) because
   the column widths are a measured contract and an inline box leaving a `<th>`
   mid-scroll would re-fit the grid under the reader. The label itself is untouched:
   a fragment of a word is a true signal that there is more to the left. */
.explore-table__labels th.th-occluded .th-info-icon,
.explore-table__labels th.th-occluded .sort-arrow,
.explore-table__labels th.th-occluded .th-note { visibility: hidden; }

/* ════ WIN LAYER, ROUND 14 — one section, an action, and the whole offer ════════════
   Khalid, 2026-08-11, from the B2 mockup (dev-assets/win-layer-mockups-2026-08-10).
   Three faults being fixed: the open/locked split read as two competing lists rather
   than one offer; the panel sold one RECORD when the subscription is the product; and
   on a phone it ran ~1,200px, which is three screens of paywall. */

.premium-overlay__head {
    display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
    border-bottom: 2px solid var(--accent-forest); padding: 0 0 9px; margin: 4px 0 2px;
    font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
}
.premium-overlay__n { font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--ink-3); }

/* The Fit row, promoted out of the locked list into something a reader can act on. */
.premium-overlay__fit {
    display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
    flex-wrap: wrap; background: #fff; border: 1px solid var(--cream-dark);
    border-radius: 9px; padding: 12px 15px; margin: 14px 0 0;
    font-size: 13px; color: var(--ink-3);
}
.premium-overlay__fit a { color: var(--accent-sienna); font-weight: 600; }

.premium-overlay__and {
    font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
    color: var(--ink-3); margin: 26px 0 10px;
}
.premium-overlay__perks { list-style: none; margin: 0 0 22px; padding: 0; display: grid; gap: 11px; }
.premium-overlay__perks .pop { display: grid; grid-template-columns: 18px 1fr; gap: 11px; align-items: start; }
.premium-overlay__perks .pop__i svg { width: 14px; height: 14px; margin-top: 3px; color: var(--accent-forest); }
.premium-overlay__perks .pop__t b { display: block; font-size: 13.5px; font-weight: 600; }
/* ⚠ The DESCRIPTION is its own element. In the mockup title+description shared one span
   and the mobile rule below hid the pair — four bare ticks, the four best selling points
   invisible on every phone. Hide `.pop__d`, never `.pop__t`. */
.premium-overlay__perks .pop__d { display: block; font-size: 12px; line-height: 1.45; color: var(--ink-3); }

/* The definitions, collapsed on a phone. ⚠ NOT tooltips: a hover-designed popover is a
   documented loop on touch here (the tap opens a card under the thumb, any scroll kills
   it). Native <details> is press-to-toggle, keyboard-reachable and needs no JS. */
.wl-defs { display: none; margin: 12px 0 0; border: 1px solid var(--cream-dark); border-radius: 9px; background: #fff; }
.wl-defs > summary { cursor: pointer; padding: 11px 14px; font-size: 12.5px; font-weight: 600; list-style: none; }
.wl-defs > summary::-webkit-details-marker { display: none; }
.wl-defs > summary::after { content: "+"; float: right; font-weight: 400; color: var(--ink-3); }
.wl-defs[open] > summary::after { content: "\2013"; }
.wl-defs dl { margin: 0; padding: 0 14px 13px; }
.wl-defs dt { font-size: 12.5px; font-weight: 600; margin: 9px 0 1px; }
.wl-defs dd { margin: 0; font-size: 12px; line-height: 1.45; color: var(--ink-3); }

@media (max-width: 520px) {
    /* Measured: the section ran 1,197px before this block and 1,012px after. */
    .premium-overlay__content--wl .wlm__q { display: none; }
    .wl-defs { display: block; }
    .premium-overlay__perks .pop__d { display: none; }
    .premium-overlay__perks { gap: 6px; margin-bottom: 15px; }
    .premium-overlay__perks .pop { grid-template-columns: 16px 1fr; gap: 9px; }
    .premium-overlay__perks .pop__t b { font-size: 13px; }
    .premium-overlay__and { margin: 20px 0 9px; }
    .premium-overlay__fit { padding: 9px 12px; margin-top: 10px; font-size: 12.5px; }
    .premium-overlay__head { padding-bottom: 7px; }
}

/* The arrival cue for a reader who clicked a locked value: a brief tint, no blink.
   `prefers-reduced-motion` drops the transition and keeps the state change. */
.premium-overlay__content--wl.is-arrived,
.detail-intel.is-arrived {
    animation: wl-arrive 1.4s ease-out 1;
}
@keyframes wl-arrive {
    0%   { background: rgba(45, 106, 79, 0.10); }
    100% { background: transparent; }
}
@media (prefers-reduced-motion: reduce) {
    .premium-overlay__content--wl.is-arrived,
    .detail-intel.is-arrived { animation: none; background: rgba(45, 106, 79, 0.07); }
}

/* Round 14 phone compression: five rows, then a disclosure. Desktop never sees the
   control — the element ships `open` and its summary is hidden above 520px, so a
   no-JS reader still gets all ten rows rather than five and a dead affordance. */
.wl-rest > summary { display: none; }
.wl-manifest--rest { margin-top: 0; }
@media (max-width: 520px) {
    .wl-rest > summary {
        display: block; cursor: pointer; list-style: none; padding: 11px 2px;
        font-size: 13px; font-weight: 600; color: var(--accent-forest);
    }
    .wl-rest > summary::-webkit-details-marker { display: none; }
    .wl-rest > summary::after { content: " \25BE"; }
    .wl-rest[open] > summary::after { content: " \25B4"; }
}

/* ★ The bottom sheet's close button was a 32×32 target on a touch surface — under the
   44px WCAG 2.5.5 minimum this file already enforces elsewhere (see the 44px rules above).
   Found by the Codex review round of 2026-08-11, which described it as "half outside the
   sheet"; that part was wrong (measured: fully inside), but the element it pointed at had
   a real defect. The visible disc stays 32px so the design is unchanged; the HIT AREA grows
   to 44px via padding + a negative margin, which is the standard way to hit the floor
   without moving anything. */
@media (pointer: coarse) {
    .explore-bottom-sheet__close {
        width: 44px;
        height: 44px;
        /* keep the painted disc visually 32px */
        background-clip: content-box;
        padding: 6px;
        margin: -6px;
    }
}

/* ═══ ROUND 15 — THE B2 ROW LAYOUT ═══════════════════════════════════════════
   ★★ WHY THIS BLOCK EXISTS: round 14 shipped B2's CONTENT (one section, buyer
   field order, `fit` as an action, the four account-wide benefits, the new lede,
   the phone collapse) and NOT its row layout — the reason Khalid picked B2 over
   A and C. He found it by reading the deployed panel; Kimi's review found the
   same thing independently ("reading order is inverted: gray descriptive prose
   comes first, then the label, then the blurred value"). The rows below are
   `mock-b2.html` in dev-assets/win-layer-mockups-2026-08-10, transposed onto the
   production class names.

   What changes, and why each part:
   · ONE COLUMN, not two. Two 200px columns of question-first text was the "wall"
     — at 890px the drawer has room to give each answer its own line, and a list
     a reader can run an eye DOWN beats a grid they have to zigzag through.
   · LABEL first at reading size, ANSWER right-aligned on the same line. A buyer
     scans field → answer; the explanation is for the fields they do not know.
   · THE EXPLANATION IS DEMOTED, not deleted — 12px muted under the pair.
   · A HAIRLINE per row so ten answers read as a table of contents for what is
     bought, rather than as ten paragraphs.
   · THE LEAD ROW gets the headline figure at display size. It is the one number
     the whole panel is selling.

   ⚠ EVERY RULE IS SCOPED TO `.premium-overlay__content--wl`. `.wlm` has exactly
   two consumers — this drawer panel and the PHONE CARD list (`.wl-manifest--card`,
   ~25 records, its own round-8 hover-card treatment and a 150px cell that cannot
   seat a label and a value side by side). An unscoped rule here silently rebuilds
   that surface too. The member strip is `.rws`, a third and separate renderer. */
.premium-overlay__content--wl .wl-manifest {
    display: block;
    margin-bottom: 0;
}
.premium-overlay__content--wl .wlm {
    display: block;
    padding: 11px 2px;
    border-bottom: 1px solid rgba(26, 23, 20, 0.09);
}
/* Label left, answer right, on one baseline. `space-between` right-aligns every
   answer to the same edge, which is what B2's fixed 132px column bought without
   needing to guess a width that the longest blurred value must still fit. */
.premium-overlay__content--wl .wlm__f {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 18px;
    flex-wrap: wrap;
}
.premium-overlay__content--wl .wlm__label {
    font: 600 13.5px/1.3 var(--explore-font-body);
    letter-spacing: normal;
    text-transform: none;
    color: var(--ink);
}
/* The padlock rides the label now (see `_winLayerManifest`) — small and quiet, a
   state marker on the field rather than a second glyph competing with the value. */
.premium-overlay__content--wl .wlm__label .wlm__lock {
    margin-left: 5px;
    opacity: 0.5;
}
.premium-overlay__content--wl .wlm__val {
    font: 500 13.5px/1.25 var(--explore-font-data);
    text-align: right;
    color: var(--ink);
}
/* ⚠ GREEN MEANS OPEN, AND A SHAPE COUNT IS NOT OPEN. The first cut of this block
   painted the shape counts forest-green — the same green as the ticks on the
   account-wide benefits, which mark things you GET. Three independent reviewers read
   "8 traps" as an already-unlocked field sitting beside locked ones, and one put the
   sharper version of it: the free teaser ended up looking more valuable than the paid
   answer beside it. Green is reserved for a genuinely OPEN manifest (a preview record,
   or a member). A shape count stays legible but neutral, and the free/paid boundary is
   STATED in words under the head rather than implied by a colour nobody was taught. */
.premium-overlay__content--wl .wlm--shape .wlm__val {
    color: var(--ink);
    font-weight: 600;
}
.premium-overlay__content--wl .wl-manifest--open .wlm__val {
    color: var(--accent-forest, #2d6a4f);
    font-weight: 600;
}
/* The boundary, said once, in words — and only where a count actually rendered. */
.premium-overlay__content--wl .wl-manifest__gate {
    margin: 7px 0 0;
    font: 400 11.5px/1.4 var(--explore-font-body);
    color: var(--ink-muted);
}
.premium-overlay__content--wl .wlm__q {
    display: block;
    margin-top: 3px;
    font: 400 12px/1.45 var(--explore-font-body);
    color: var(--ink-muted);
    max-width: 60ch;
}
/* An honest absence is the ANSWER to that row, so it belongs in the answer
   column, not on a line of its own. It stays muted: the emptiest row must never
   be the most legible thing in a locked band (the round-13 correction). */
.premium-overlay__content--wl .wlm__absent {
    flex: 0 1 auto;
    max-width: 58%;
    text-align: right;
    font: 400 11.5px/1.36 var(--explore-font-body);
}
.premium-overlay__content--wl .wlm__sub {
    flex: 1 1 100%;
    text-align: right;
}
/* The lead row: the headline figure at display size. */
.premium-overlay__content--wl .wlm--lead {
    padding: 14px 2px 13px;
}
.premium-overlay__content--wl .wlm--lead .wlm__label { font-size: 15px; }
.premium-overlay__content--wl .wlm--lead .wlm__val {
    font: 600 22px/1.15 var(--explore-font-display);
}
/* The blur cap has to grow with the type or a 22px display figure is clipped
   mid-value; it is a veil, not a truncation. */
.premium-overlay__content--wl .wlm--lead .wlm__val--blur { max-width: 190px; }

@media (max-width: 520px) {
    /* ⚠ THE HIDE IS RESTATED HERE ON PURPOSE. `.wlm__q { display: none }` already
       exists in the phone block above, at the same specificity — and this block's
       `display: block` is LATER in the file, so it would win inside the media query
       and put every explanation back on the phone, which is the 1,197px section
       round 14 measured down to 1,012px. Source order decides ties; keep this. */
    .premium-overlay__content--wl .wlm__q { display: none; }
    .premium-overlay__content--wl .wlm { padding: 7px 2px; }
    .premium-overlay__content--wl .wlm__label { font-size: 13px; }
    .premium-overlay__content--wl .wlm__val { font-size: 13px; }
    /* At 390px a label and a display figure cannot share a line, and forcing them
       wraps the figure to a ragged right edge under its own label. The lead row
       goes stacked and left-aligned instead. */
    .premium-overlay__content--wl .wlm--lead { padding: 11px 2px 10px; }
    .premium-overlay__content--wl .wlm--lead .wlm__f { display: block; }
    .premium-overlay__content--wl .wlm--lead .wlm__val {
        display: block;
        font-size: 18px;
        text-align: left;
        margin-top: 2px;
    }
    .premium-overlay__content--wl .wlm--lead .wlm__absent { text-align: left; max-width: 100%; }
}

/* ★★★ THE PANEL WAS CENTRE-ALIGNED ON EVERY PHONE, AND THE ROUND-14 SHOT COULD NOT
   SHOW IT. Round 14 left-aligned this block with `.exp-drawer .premium-overlay__content`
   — an ANCESTOR-scoped rule, and `.exp-drawer` is `display: none !important` below
   1023px ("the record is the bottom sheet, never this"). So on a phone the sheet
   inherited `.premium-overlay__content { text-align: center }` and the whole Win Layer
   rendered centred: a two-line centred section header, centred perk titles with their
   ticks stranded at the left margin, and the exact "one block, two alignments, the eye
   resets three times" defect round 14 wrote the rule to fix — fixed on the one width
   where the rule could not reach. The same shape as the `.gcx-*` ancestor-scoped CTA
   trap already in the gotcha index.
   ⚠ AND THE VERIFICATION MISSED IT TWICE: round 14's `05-FIXED-phone-sheet.png` is a
   photograph of the RECORD HEADER — the harness scrolled `#sheet-content`, which does
   not scroll, so the section it claimed to verify was never in the frame. A screenshot
   that does not contain its subject is not evidence.
   → The alignment belongs to the COMPONENT, not to one of its two hosts. */
.premium-overlay__content--wl,
.premium-overlay__content--wl .premium-overlay__lead,
.premium-overlay__content--wl .premium-overlay__footnote,
.premium-overlay__content--wl h4 {
    text-align: left;
}
@media (max-width: 520px) {
    /* At 310px "WHAT PREMIUM UNLOCKS" and "10 answers on this program" cannot share a
       line, and wrapping them produced four centred fragments. The count also restates
       what the lede says two lines above it ("The same 10 questions…") — the same fact
       twice in two visual languages, which is the composition defect this file keeps
       finding. On a phone the head keeps the title and drops the count. */
    .premium-overlay__content--wl .premium-overlay__n { display: none; }
}

/* ★★ THE REST OF THE ANCESTOR-SCOPED SWEEP. Having found `text-align` fixed only on
   the width where `.exp-drawer` renders, I grepped every `.exp-drawer .premium-overlay*`
   rule rather than fixing the one I had tripped over — guard the output, not the one
   producer you noticed. Three more never reached the phone sheet:
     · the h4 treatment, so the phone printed "The Win Layer" in the big display serif
       while the desktop printed it as a small uppercase section label. Unintentional,
       and it competed with the record title, which is also serif.
     · the annual-plan control's width and padding.
   ⓘ `.exp-drawer .premium-overlay__pricing { flex-direction: row }` is DELIBERATELY
   left drawer-only: a row seats two controls at 437px and would crowd them at 310px,
   where the stacked default is correct. Scoping is only a bug when the narrow host
   wanted the same answer. */
.premium-overlay__content--wl h4 {
    font-family: var(--explore-font-body);
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
.premium-overlay__content--wl .premium-overlay__alt { width: auto; padding: 0; }
/* The footnote's clauses each own their separator and never break inside it. */
.premium-overlay__content--wl .pof__c { white-space: nowrap; }

/* ── Winner evidence in the record panel (2026-08-25) ─────────────────────
   One host serves BOTH the desktop drawer and the mobile bottom sheet (the
   panel markup is shared). Free/anon: a quiet inset fact line, deliberately
   NOT styled as an offer — no border-accent, no button, nothing pressable.
   Premium: GCWinnerPanel renders inside and brings its own styles. */
.expw { display: block; margin: 18px 0 4px; }
.expw[hidden] { display: none; }   /* display rule above would defeat [hidden] */
.expw .expw__ico { margin-right: 8px; font-size: 15px; vertical-align: 1px; }
.expw .expw__txt {
  display: inline;
  font-size: 13.5px; line-height: 1.55; color: #4a4438;
}
.expw--line {
  background: #F7F5F0; border: 1px solid #E7E0D4; border-radius: 8px;
  padding: 11px 14px;
}
.expw .expw__scope { opacity: .72; font-size: .94em; }
.expw .expw__attr {
  display: block; margin-top: 6px;
  font-size: 11px; color: #8a8272; letter-spacing: .01em;
}
@media (max-width: 700px) {
  .expw { margin: 14px 0 2px; }
  .expw--line { padding: 10px 12px; }
}

/* ★ 2026-09-03 tap targets on the inline pricing card (phone): the primary measured 42px
   and the two guarantee-line links 15px. 44px floor. */
@media (max-width: 768px) {
  .explore-pricing__card .btn--pricing { min-height: 44px; }
  .explore-pricing__guarantee a { display: inline-block; padding: 8px 0; margin: -8px 0; }
}

/* ★ 2026-09-03 tap targets (phone): Filters 40px, Reset 28px — the two most-pressed
   controls on the mobile toolbar. 44px floor. */
@media (max-width: 768px) {
  .explore-filters__toggle, .explore-filters__reset { min-height: 44px; }
}
